dos2unix
[plewww.git] / plekit / tablesort / paginate.js
index 0dfcc7d..871c100 100644 (file)
-/*\r
-        paginate table object v2.0 by frequency-decoder.com\r
-\r
-        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)\r
-\r
-        Please credit frequency decoder in any derivative work - thanks\r
-\r
-        You are free:\r
-\r
-        * to copy, distribute, display, and perform the work\r
-        * to make derivative works\r
-        * to make commercial use of the work\r
-\r
-        Under the following conditions:\r
-\r
-                by Attribution.\r
-                --------------\r
-                You must attribute the work in the manner specified by the author or licensor.\r
-\r
-                sa\r
-                --\r
-                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.\r
-\r
-        * For any reuse or distribution, you must make clear to others the license terms of this work.\r
-        * Any of these conditions can be waived if you get permission from the copyright holder.\r
-*/\r
-\r
-var tablePaginater = (function() {\r
-        /*\r
-\r
-        Localise the button titles here...\r
-\r
-        %p is replaced with the appropriate page number\r
-        %t is replaced with the total number of pages\r
-        \r
-        */\r
-        var tableInfo = {},\r
-            uniqueID  = 0,\r
-            text      = ["First Page","Previous Page (Page %p)","Next Page (Page %p)","Last Page (Page %t)","Page %p of %t"];\r
-            \r
-        var addClass = function(e,c) {\r
-                if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return;\r
-                e.className += ( e.className ? " " : "" ) + c;\r
-        }; \r
-        \r
-        /*@cc_on\r
-        /*@if (@_win32)\r
-        var removeClass = function(e,c) {\r
-                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');\r
-        };\r
-        @else @*/\r
-        var removeClass = function(e,c) {                 \r
-                e.className = !c ? "" : (e.className || "").replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');\r
-        };\r
-        /*@end\r
-        @*/  \r
-        \r
-        var addEvent = function(obj, type, fn) {\r
-                if( obj.attachEvent ) {\r
-                        obj["e"+type+fn] = fn;\r
-                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );};\r
-                        obj.attachEvent( "on"+type, obj[type+fn] );\r
-                } else {\r
-                        obj.addEventListener( type, fn, true );\r
-                };\r
-        };\r
-        var removeEvent = function(obj, type, fn) {\r
-                try {\r
-                        if( obj.detachEvent ) {\r
-                                obj.detachEvent( "on"+type, obj[type+fn] );\r
-                                obj[type+fn] = null;\r
-                        } else {\r
-                                obj.removeEventListener( type, fn, true );\r
-                        };\r
-                } catch(err) {};\r
-        };\r
-        var stopEvent = function(e) {\r
-                e = e || window.event;\r
-                if(e.stopPropagation) {\r
-                        e.stopPropagation();\r
-                        e.preventDefault();\r
-                };\r
-                \r
-                /*@cc_on@*/\r
-                /*@if(@_win32)\r
-                e.cancelBubble = true;\r
-                e.returnValue  = false;\r
-                /*@end@*/\r
-                return false;\r
-        }; \r
-        \r
-        var init = function(tableId) {\r
-                var tables = tableId && typeof(tableId) == "string" ? [document.getElementById(tableId)] : document.getElementsByTagName('table'),\r
-                    hook, maxPages, visibleRows, numPages, cp, cb, rowList;\r
-                \r
-                for(var t = 0, tbl; tbl = tables[t]; t++) {\r
-                        if(tbl.className.search(/paginate-([0-9]+)/) == -1) { continue; };\r
-\r
-                        if(!tbl.id) { tbl.id = "fdUniqueTableId_" + uniqueID++; };\r
-\r
-                        maxPages = tbl.className.search(/max-pages-([0-9]+)/) == -1 ? null : Number(tbl.className.match(/max-pages-([0-9]+)/)[1]);\r
-                        if(maxPages % 2 == 0 && maxPages > 1) { maxPages--; };\r
-                        \r
-                        hook = tbl.getElementsByTagName('tbody');\r
-                        hook = (hook.length) ? hook[0] : tbl;\r
-\r
-                        visibleRows = calculateVisibleRows(hook);\r
-                        \r
-                        if(maxPages > (visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]))) {\r
-                                maxPages = null;\r
-                        };\r
-                        \r
-                        numPages = Math.ceil(visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]));\r
-                        \r
-                        if(numPages < 2 && !(tbl.id in tableInfo)) {\r
-                                continue;\r
-                        };\r
-                        \r
-                        cp = (tbl.id in tableInfo) ? Math.min(tableInfo[tbl.id].currentPage, numPages) : 1;\r
-                        \r
-                        tableInfo[tbl.id] = {\r
-                                rowsPerPage:Number(tbl.className.match(/paginate-([0-9]+)/)[1]),\r
-                                currentPage:cp,\r
-                                totalRows:hook.getElementsByTagName('tr').length,\r
-                                hook:hook,\r
-                                maxPages:maxPages,\r
-                                numPages:numPages,\r
-                                rowStyle:tbl.className.search(/rowstyle-([\S]+)/) != -1 ? tbl.className.match(/rowstyle-([\S]+)/)[1] : false,\r
-                                callbacks:parseCallback(/^paginationcallback-/i, /paginationcallback-([\S-]+)/ig, tbl.className)\r
-                        };\r
-                        \r
-                        showPage(tbl.id);\r
-                        hook = null;\r
-                };\r
-        };\r
-        \r
-        var parseCallback = function(head, regExp, cname) {\r
-                var cbs    = [],\r
-                    matchs = cname.match(regExp),\r
-                    parts, obj, func;\r
-                \r
-                if(!matchs) { return []; };\r
-                  \r
-                for(var i = 0, mtch; mtch = matchs[i]; i++) {                         \r
-                        mtch = mtch.replace(head, "").replace(/-/g, ".");\r
-                         \r
-                        try {\r
-                                if(mtch.indexOf(".") != -1) {\r
-                                        parts = mtch.split('.');\r
-                                        obj   = window;\r
-                                        for (var x = 0, part; part = obj[parts[x]]; x++) {\r
-                                                if(part instanceof Function) {\r
-                                                        (function() {\r
-                                                                var method = part;\r
-                                                                func = function (data) { method.apply(obj, [data]) };\r
-                                                        })();\r
-                                                } else {\r
-                                                        obj = part;\r
-                                                };\r
-                                        };\r
-                                } else {\r
-                                        func = window[mtch];\r
-                                };\r
-                            \r
-                                if(!(func instanceof Function)) continue;\r
-                                cbs[cbs.length] = func;                              \r
-                        } catch(err) {};\r
-                };\r
-                \r
-                return cbs;                      \r
-        };\r
-                \r
-        var callback = function(tblId, opts) {                \r
-                if(!(tblId in tableInfo) || !(tableInfo[tblId]["callbacks"].length)) return;                \r
-                for(var i = 0, func; func = tableInfo[tblId]["callbacks"][i]; i++) {\r
-                        func(opts || {});\r
-                };\r
-        };\r
-        \r
-        var calculateVisibleRows = function(hook) {\r
-                var trs = hook.rows,\r
-                    cnt = 0,\r
-                    reg = /(^|\s)invisibleRow(\s|$)/;\r
-                \r
-                for(var i = 0, tr; tr = trs[i]; i++) {\r
-                        if(tr.parentNode != hook || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;\r
-                        if(tr.className.search(reg) == -1) { cnt++; };\r
-                };\r
-                \r
-                return cnt;\r
-        };\r
-        \r
-        var createButton = function(details, ul, pseudo) {\r
-                var li   = document.createElement("li"),\r
-                    but  = document.createElement(pseudo ? "div" : "a"),\r
-                    span = document.createElement("span");\r
-\r
-                if(!pseudo) { \r
-                        but.href = "#"; \r
-                        but.title = details.title; \r
-                };\r
-                \r
-                but.className = details.className;\r
-\r
-                ul.appendChild(li);\r
-                li.appendChild(but);\r
-                but.appendChild(span);\r
-                span.appendChild(document.createTextNode(details.text));\r
-\r
-                if(!pseudo) { \r
-                        li.onclick = but.onclick = buttonClick; \r
-                        if(details.id) { but.id = details.id; };\r
-                };                 \r
-                \r
-                li = but = span = null;\r
-        };\r
-        var removePagination = function(tableId) {\r
-                var wrapT = document.getElementById(tableId + "-fdtablePaginaterWrapTop"),\r
-                    wrapB = document.getElementById(tableId + "-fdtablePaginaterWrapBottom");\r
-                if(wrapT) { wrapT.parentNode.removeChild(wrapT); };\r
-                if(wrapB) { wrapB.parentNode.removeChild(wrapB); };\r
-        };\r
-        var buildPagination = function(tblId) {\r
-                if(!(tblId in tableInfo)) { return; };\r
-\r
-                removePagination(tblId);\r
-\r
-                var details = tableInfo[tblId];\r
-                \r
-                if(details.numPages < 2) return;\r
-                \r
-                function resolveText(txt, curr) {\r
-                        curr = curr || details.currentPage;\r
-                        return txt.replace("%p", curr).replace("%t", details.numPages);\r
-                };\r
-\r
-                if(details.maxPages) {\r
-                        findex = Math.max(0, Math.floor(Number(details.currentPage - 1) - (Number(details.maxPages - 1) / 2)));\r
-                        lindex = findex + Number(details.maxPages);\r
-                        if(lindex > details.numPages) {\r
-                                lindex = details.numPages;\r
-                                findex = Math.max(0, details.numPages - Number(details.maxPages));\r
-                        };\r
-                } else {\r
-                        findex = 0;\r
-                        lindex = details.numPages;\r
-                };\r
-                \r
-\r
-                var wrapT = document.createElement("div");\r
-                wrapT.className = "fdtablePaginaterWrap";\r
-                wrapT.id = tblId + "-fdtablePaginaterWrapTop";\r
-\r
-                var wrapB = document.createElement("div");\r
-                wrapB.className = "fdtablePaginaterWrap";\r
-                wrapB.id = tblId + "-fdtablePaginaterWrapBottom";\r
-\r
-                // Create list scaffold\r
-                var ulT = document.createElement("ul");\r
-                ulT.id  = tblId + "-tablePaginater";\r
-\r
-                var ulB = document.createElement("ul");\r
-                ulB.id  = tblId + "-tablePaginaterClone";\r
-                ulT.className = ulB.className = "fdtablePaginater";\r
-\r
-                // Add to the wrapper DIVs\r
-                wrapT.appendChild(ulT);\r
-                wrapB.appendChild(ulB);\r
-\r
-                // FIRST (only created if maxPages set)\r
-                if(details.maxPages) {\r
-                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulT, !findex);\r
-                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulB, !findex);\r
-                };\r
-                \r
-                // PREVIOUS (only created if there are more than two pages)\r
-                if(details.numPages > 2) {\r
-                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPage"}, ulT, details.currentPage == 1);\r
-                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPageC"}, ulB, details.currentPage == 1);\r
-                };\r
-                \r
-                // NUMBERED\r
-                for(var i = findex; i < lindex; i++) {\r
-                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPage" : ""}, ulT);\r
-                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPageC" : ""}, ulB);\r
-                };\r
-\r
-                // NEXT (only created if there are more than two pages)\r
-                if(details.numPages > 2) {\r
-                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPage"}, ulT, details.currentPage == details.numPages);\r
-                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPageC"}, ulB, details.currentPage == details.numPages);\r
-                };\r
-                \r
-                // LAST (only created if maxPages set)\r
-                if(details.maxPages) {\r
-                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulT, lindex == details.numPages);\r
-                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulB, lindex == details.numPages);\r
-                };\r
-                \r
-                // DOM inject wrapper DIVs (FireFox 2.x Bug: this has to be done here if you use display:table)\r
-                if(document.getElementById(tblId+"-paginationListWrapTop")) {\r
-                        document.getElementById(tblId+"-paginationListWrapTop").appendChild(wrapT);\r
-                } else {\r
-                        document.getElementById(tblId).parentNode.insertBefore(wrapT, document.getElementById(tblId));\r
-                };\r
-\r
-                if(document.getElementById(tblId+"-paginationListWrapBottom")) {\r
-                        document.getElementById(tblId+"-paginationListWrapBottom").appendChild(wrapB);\r
-                } else {\r
-                        document.getElementById(tblId).parentNode.insertBefore(wrapB, document.getElementById(tblId).nextSibling);\r
-                };\r
-        };\r
-        \r
-        // The tableSort script uses this function to redraw.\r
-        var tableSortRedraw = function(tableid, identical) {\r
-                if(!tableid || !(tableid in fdTableSort.tableCache) || !(tableid in tableInfo)) { return; };\r
-                \r
-                var dataObj     = fdTableSort.tableCache[tableid],\r
-                    data        = dataObj.data,\r
-                    len1        = data.length,\r
-                    len2        = len1 ? data[0].length - 1 : 0,\r
-                    hook        = dataObj.hook,\r
-                    colStyle    = dataObj.colStyle,\r
-                    rowStyle    = dataObj.rowStyle,\r
-                    colOrder    = dataObj.colOrder,                 \r
-                    page        = tableInfo[tableid].currentPage - 1,\r
-                    d1          = tableInfo[tableid].rowsPerPage * page,\r
-                    d2          = Math.min(tableInfo[tableid].totalRows, d1 + tableInfo[tableid].rowsPerPage), \r
-                    cnt         = 0,\r
-                    rs          = 0,\r
-                    reg         = /(^|\s)invisibleRow(\s|$)/,                \r
-                    tr, tds, cell, pos;\r
-                \r
-                for(var i = 0; i < len1; i++) {\r
-                        tr = data[i][len2];\r
-                        \r
-                        if(colStyle) {\r
-                                tds = tr.cells;\r
-                                for(thPos in colOrder) {\r
-                                        if(!colOrder[thPos]) removeClass(tds[thPos], colStyle);\r
-                                        else addClass(tds[thPos], colStyle);\r
-                                };\r
-                        };\r
-                        \r
-                        if(tr.className.search(reg) != -1) { continue; };\r
-                        \r
-                        if(!identical) {\r
-                                cnt++;\r
-\r
-                                if(cnt > d1 && cnt <= d2) {\r
-                                        if(rowStyle) {\r
-                                                if(rs++ & 1) addClass(tr, rowStyle);\r
-                                                else removeClass(tr, rowStyle);\r
-                                        };\r
-                                        tr.style.display = "";\r
-                                } else {\r
-                                        tr.style.display = "none";\r
-                                };\r
-\r
-                                // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser\r
-                                // hook.removeChild(tr);\r
-                                hook.appendChild(tr);\r
-                        };\r
-                };\r
-\r
-                tr = tds = hook = null;\r
-        };\r
-        \r
-        var showPage = function(tblId, pageNum) {\r
-                if(!(tblId in tableInfo)) { return; };\r
-\r
-                var page = Math.max(0, !pageNum ? tableInfo[tblId].currentPage - 1 : pageNum - 1),\r
-                    d1  = tableInfo[tblId].rowsPerPage * page,\r
-                    d2  = Math.min(tableInfo[tblId].totalRows, d1 + tableInfo[tblId].rowsPerPage),\r
-                    trs = tableInfo[tblId].hook.rows,\r
-                    cnt = 0,\r
-                    rc  = 0,\r
-                    len = trs.length,\r
-                    rs  = tableInfo[tblId].rowStyle,\r
-                    reg = /(^|\s)invisibleRow(\s|$)/,\r
-                    row = [];\r
-                \r
-                for(var i = 0; i < len; i++) {\r
-                        if(trs[i].className.search(reg) != -1 || trs[i].getElementsByTagName("th").length || (trs[i].parentNode && trs[i].parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) { continue; };\r
-                        \r
-                        cnt++;\r
-                        \r
-                        if(cnt > d1 && cnt <= d2) {\r
-                                if(rs) {\r
-                                        if(rc++ & 1) {\r
-                                                addClass(trs[i], rs);\r
-                                        } else {\r
-                                                removeClass(trs[i], rs);\r
-                                        }\r
-                                };\r
-                                trs[i].style.display = "";\r
-                                row[row.length] = trs[i];\r
-                        } else {\r
-                                trs[i].style.display = "none";\r
-                        };\r
-                };\r
-\r
-                buildPagination(tblId);\r
-                callback(tblId, {"totalRows":len, "currentPage":(page+1), "rowsPerPage":tableInfo[tblId].rowsPerPage, "visibleRows":row});\r
-        };\r
-        \r
-        var buttonClick = function(e) {\r
-                e = e || window.event;\r
-\r
-                var a = this.tagName.toLowerCase() == "a" ? this : this.getElementsByTagName("a")[0];\r
-\r
-                if(a.className.search("currentPage") != -1) return false;\r
-\r
-                var ul = this;\r
-                while(ul.tagName.toLowerCase() != "ul") ul = ul.parentNode;\r
-\r
-                var tblId = ul.id.replace("-tablePaginaterClone","").replace("-tablePaginater", "");\r
-\r
-                tableInfo[tblId].lastPage = tableInfo[tblId].currentPage;\r
-                \r
-                var showPrevNext = 0;\r
-                \r
-                if(a.className.search("previous-page") != -1) {\r
-                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage > 1 ? tableInfo[tblId].currentPage - 1 : tableInfo[tblId].numPages;\r
-                        showPrevNext = 1;\r
-                } else if(a.className.search("next-page") != -1) {\r
-                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage < tableInfo[tblId].numPages ? tableInfo[tblId].currentPage + 1 : 1;\r
-                        showPrevNext = 2;\r
-                } else if(a.className.search("first-page") != -1) {\r
-                        tableInfo[tblId].currentPage = 1;\r
-                } else if(a.className.search("last-page") != -1) {\r
-                        tableInfo[tblId].currentPage = tableInfo[tblId].numPages;\r
-                } else {\r
-                        tableInfo[tblId].currentPage = parseInt(a.className.match(/page-([0-9]+)/)[1]) || 1;\r
-                };\r
-\r
-                showPage(tblId);\r
-\r
-                // Focus on the appropriate button (previous, next or the current page)\r
-                // I'm hoping screen readers are savvy enough to indicate the focus event to the user\r
-                if(showPrevNext == 1) {\r
-                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-previousPageC" : tblId + "-previousPage");\r
-                } else if(showPrevNext == 2) {\r
-                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-nextPageC" : tblId + "-nextPage");\r
-                } else {\r
-                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-currentPageC" : tblId + "-currentPage");\r
-                };\r
-                \r
-                if(elem && elem.tagName.toLowerCase() == "a") { elem.focus(); };   \r
-                \r
-                return stopEvent(e);\r
-        };\r
-        \r
-        var onUnLoad = function(e) {\r
-                var tbl, lis, pagination, uls;\r
-                for(tblId in tableInfo) {\r
-                        uls = [tblId + "-tablePaginater", tblId + "-tablePaginaterClone"];\r
-                        for(var z = 0; z < 2; z++) {\r
-                                pagination = document.getElementById(uls[z]);\r
-                                if(!pagination) { continue; };\r
-                                lis = pagination.getElementsByTagName("li");\r
-                                for(var i = 0, li; li = lis[i]; i++) {\r
-                                        li.onclick = null;\r
-                                        if(li.getElementsByTagName("a").length) { li.getElementsByTagName("a")[0].onclick = null; };\r
-                                };\r
-                        };\r
-                };\r
-        };\r
-        \r
-        addEvent(window, "load",   init);\r
-        addEvent(window, "unload", onUnLoad); \r
-        \r
-        return {                  \r
-                init:                   function(tableId) { init(tableId); },                 \r
-                redraw:                 function(tableid, identical) { tableSortRedraw(tableid, identical); },\r
-                tableIsPaginated:       function(tableId) { return (tableId in tableInfo); },\r
-                changeTranslations:     function(translations) { text = translations; }\r
-        };        \r
+/*
+        paginate table object v2.0 by frequency-decoder.com
+
+        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)
+
+        Please credit frequency decoder in any derivative work - thanks
+
+        You are free:
+
+        * to copy, distribute, display, and perform the work
+        * to make derivative works
+        * to make commercial use of the work
+
+        Under the following conditions:
+
+                by Attribution.
+                --------------
+                You must attribute the work in the manner specified by the author or licensor.
+
+                sa
+                --
+                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
+
+        * For any reuse or distribution, you must make clear to others the license terms of this work.
+        * Any of these conditions can be waived if you get permission from the copyright holder.
+*/
+
+var tablePaginater = (function() {
+        /*
+
+        Localise the button titles here...
+
+        %p is replaced with the appropriate page number
+        %t is replaced with the total number of pages
+        
+        */
+        var tableInfo = {},
+            uniqueID  = 0,
+            text      = ["First Page","Previous Page (Page %p)","Next Page (Page %p)","Last Page (Page %t)","Page %p of %t"];
+            
+        var addClass = function(e,c) {
+                if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return;
+                e.className += ( e.className ? " " : "" ) + c;
+        }; 
+        
+        /*@cc_on
+        /*@if (@_win32)
+        var removeClass = function(e,c) {
+                e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
+        };
+        @else @*/
+        var removeClass = function(e,c) {                 
+                e.className = !c ? "" : (e.className || "").replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+        };
+        /*@end
+        @*/  
+        
+        var addEvent = function(obj, type, fn) {
+                if( obj.attachEvent ) {
+                        obj["e"+type+fn] = fn;
+                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
+                        obj.attachEvent( "on"+type, obj[type+fn] );
+                } else {
+                        obj.addEventListener( type, fn, true );
+                };
+        };
+        var removeEvent = function(obj, type, fn) {
+                try {
+                        if( obj.detachEvent ) {
+                                obj.detachEvent( "on"+type, obj[type+fn] );
+                                obj[type+fn] = null;
+                        } else {
+                                obj.removeEventListener( type, fn, true );
+                        };
+                } catch(err) {};
+        };
+        var stopEvent = function(e) {
+                e = e || window.event;
+                if(e.stopPropagation) {
+                        e.stopPropagation();
+                        e.preventDefault();
+                };
+                
+                /*@cc_on@*/
+                /*@if(@_win32)
+                e.cancelBubble = true;
+                e.returnValue  = false;
+                /*@end@*/
+                return false;
+        }; 
+        
+        var init = function(tableId) {
+                var tables = tableId && typeof(tableId) == "string" ? [document.getElementById(tableId)] : document.getElementsByTagName('table'),
+                    hook, maxPages, visibleRows, numPages, cp, cb, rowList;
+                
+                for(var t = 0, tbl; tbl = tables[t]; t++) {
+                        if(tbl.className.search(/paginate-([0-9]+)/) == -1) { continue; };
+
+                        if(!tbl.id) { tbl.id = "fdUniqueTableId_" + uniqueID++; };
+
+                        maxPages = tbl.className.search(/max-pages-([0-9]+)/) == -1 ? null : Number(tbl.className.match(/max-pages-([0-9]+)/)[1]);
+                        if(maxPages % 2 == 0 && maxPages > 1) { maxPages--; };
+                        
+                        hook = tbl.getElementsByTagName('tbody');
+                        hook = (hook.length) ? hook[0] : tbl;
+
+                        visibleRows = calculateVisibleRows(hook);
+                        
+                        if(maxPages > (visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]))) {
+                                maxPages = null;
+                        };
+                        
+                        numPages = Math.ceil(visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]));
+                        
+                        if(numPages < 2 && !(tbl.id in tableInfo)) {
+                                continue;
+                        };
+                        
+                        cp = (tbl.id in tableInfo) ? Math.min(tableInfo[tbl.id].currentPage, numPages) : 1;
+                        
+                        tableInfo[tbl.id] = {
+                                rowsPerPage:Number(tbl.className.match(/paginate-([0-9]+)/)[1]),
+                                currentPage:cp,
+                                totalRows:hook.getElementsByTagName('tr').length,
+                                hook:hook,
+                                maxPages:maxPages,
+                                numPages:numPages,
+                                rowStyle:tbl.className.search(/rowstyle-([\S]+)/) != -1 ? tbl.className.match(/rowstyle-([\S]+)/)[1] : false,
+                                callbacks:parseCallback(/^paginationcallback-/i, /paginationcallback-([\S-]+)/ig, tbl.className)
+                        };
+                        
+                        showPage(tbl.id);
+                        hook = null;
+                };
+        };
+        
+        var parseCallback = function(head, regExp, cname) {
+                var cbs    = [],
+                    matchs = cname.match(regExp),
+                    parts, obj, func;
+                
+                if(!matchs) { return []; };
+                  
+                for(var i = 0, mtch; mtch = matchs[i]; i++) {                         
+                        mtch = mtch.replace(head, "").replace(/-/g, ".");
+                         
+                        try {
+                                if(mtch.indexOf(".") != -1) {
+                                        parts = mtch.split('.');
+                                        obj   = window;
+                                        for (var x = 0, part; part = obj[parts[x]]; x++) {
+                                                if(part instanceof Function) {
+                                                        (function() {
+                                                                var method = part;
+                                                                func = function (data) { method.apply(obj, [data]) };
+                                                        })();
+                                                } else {
+                                                        obj = part;
+                                                };
+                                        };
+                                } else {
+                                        func = window[mtch];
+                                };
+                            
+                                if(!(func instanceof Function)) continue;
+                                cbs[cbs.length] = func;                              
+                        } catch(err) {};
+                };
+                
+                return cbs;                      
+        };
+                
+        var callback = function(tblId, opts) {                
+                if(!(tblId in tableInfo) || !(tableInfo[tblId]["callbacks"].length)) return;                
+                for(var i = 0, func; func = tableInfo[tblId]["callbacks"][i]; i++) {
+                        func(opts || {});
+                };
+        };
+        
+        var calculateVisibleRows = function(hook) {
+                var trs = hook.rows,
+                    cnt = 0,
+                    reg = /(^|\s)invisibleRow(\s|$)/;
+                
+                for(var i = 0, tr; tr = trs[i]; i++) {
+                        if(tr.parentNode != hook || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;
+                        if(tr.className.search(reg) == -1) { cnt++; };
+                };
+                
+                return cnt;
+        };
+        
+        var createButton = function(details, ul, pseudo) {
+                var li   = document.createElement("li"),
+                    but  = document.createElement(pseudo ? "div" : "a"),
+                    span = document.createElement("span");
+
+                if(!pseudo) { 
+                        but.href = "#"; 
+                        but.title = details.title; 
+                };
+                
+                but.className = details.className;
+
+                ul.appendChild(li);
+                li.appendChild(but);
+                but.appendChild(span);
+                span.appendChild(document.createTextNode(details.text));
+
+                if(!pseudo) { 
+                        li.onclick = but.onclick = buttonClick; 
+                        if(details.id) { but.id = details.id; };
+                };                 
+                
+                li = but = span = null;
+        };
+        var removePagination = function(tableId) {
+                var wrapT = document.getElementById(tableId + "-fdtablePaginaterWrapTop"),
+                    wrapB = document.getElementById(tableId + "-fdtablePaginaterWrapBottom");
+                if(wrapT) { wrapT.parentNode.removeChild(wrapT); };
+                if(wrapB) { wrapB.parentNode.removeChild(wrapB); };
+        };
+        var buildPagination = function(tblId) {
+                if(!(tblId in tableInfo)) { return; };
+
+                removePagination(tblId);
+
+                var details = tableInfo[tblId];
+                
+                if(details.numPages < 2) return;
+                
+                function resolveText(txt, curr) {
+                        curr = curr || details.currentPage;
+                        return txt.replace("%p", curr).replace("%t", details.numPages);
+                };
+
+                if(details.maxPages) {
+                        findex = Math.max(0, Math.floor(Number(details.currentPage - 1) - (Number(details.maxPages - 1) / 2)));
+                        lindex = findex + Number(details.maxPages);
+                        if(lindex > details.numPages) {
+                                lindex = details.numPages;
+                                findex = Math.max(0, details.numPages - Number(details.maxPages));
+                        };
+                } else {
+                        findex = 0;
+                        lindex = details.numPages;
+                };
+                
+
+                var wrapT = document.createElement("div");
+                wrapT.className = "fdtablePaginaterWrap";
+                wrapT.id = tblId + "-fdtablePaginaterWrapTop";
+
+                var wrapB = document.createElement("div");
+                wrapB.className = "fdtablePaginaterWrap";
+                wrapB.id = tblId + "-fdtablePaginaterWrapBottom";
+
+                // Create list scaffold
+                var ulT = document.createElement("ul");
+                ulT.id  = tblId + "-tablePaginater";
+
+                var ulB = document.createElement("ul");
+                ulB.id  = tblId + "-tablePaginaterClone";
+                ulT.className = ulB.className = "fdtablePaginater";
+
+                // Add to the wrapper DIVs
+                wrapT.appendChild(ulT);
+                wrapB.appendChild(ulB);
+
+                // FIRST (only created if maxPages set)
+                if(details.maxPages) {
+                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulT, !findex);
+                        createButton({title:text[0], className:"first-page", text:"\u00ab"}, ulB, !findex);
+                };
+                
+                // PREVIOUS (only created if there are more than two pages)
+                if(details.numPages > 2) {
+                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPage"}, ulT, details.currentPage == 1);
+                        createButton({title:resolveText(text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPageC"}, ulB, details.currentPage == 1);
+                };
+                
+                // NUMBERED
+                for(var i = findex; i < lindex; i++) {
+                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPage" : ""}, ulT);
+                        createButton({title:resolveText(text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPageC" : ""}, ulB);
+                };
+
+                // NEXT (only created if there are more than two pages)
+                if(details.numPages > 2) {
+                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPage"}, ulT, details.currentPage == details.numPages);
+                        createButton({title:resolveText(text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPageC"}, ulB, details.currentPage == details.numPages);
+                };
+                
+                // LAST (only created if maxPages set)
+                if(details.maxPages) {
+                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulT, lindex == details.numPages);
+                        createButton({title:resolveText(text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulB, lindex == details.numPages);
+                };
+                
+                // DOM inject wrapper DIVs (FireFox 2.x Bug: this has to be done here if you use display:table)
+                if(document.getElementById(tblId+"-paginationListWrapTop")) {
+                        document.getElementById(tblId+"-paginationListWrapTop").appendChild(wrapT);
+                } else {
+                        document.getElementById(tblId).parentNode.insertBefore(wrapT, document.getElementById(tblId));
+                };
+
+                if(document.getElementById(tblId+"-paginationListWrapBottom")) {
+                        document.getElementById(tblId+"-paginationListWrapBottom").appendChild(wrapB);
+                } else {
+                        document.getElementById(tblId).parentNode.insertBefore(wrapB, document.getElementById(tblId).nextSibling);
+                };
+        };
+        
+        // The tableSort script uses this function to redraw.
+        var tableSortRedraw = function(tableid, identical) {
+                if(!tableid || !(tableid in fdTableSort.tableCache) || !(tableid in tableInfo)) { return; };
+                
+                var dataObj     = fdTableSort.tableCache[tableid],
+                    data        = dataObj.data,
+                    len1        = data.length,
+                    len2        = len1 ? data[0].length - 1 : 0,
+                    hook        = dataObj.hook,
+                    colStyle    = dataObj.colStyle,
+                    rowStyle    = dataObj.rowStyle,
+                    colOrder    = dataObj.colOrder,                 
+                    page        = tableInfo[tableid].currentPage - 1,
+                    d1          = tableInfo[tableid].rowsPerPage * page,
+                    d2          = Math.min(tableInfo[tableid].totalRows, d1 + tableInfo[tableid].rowsPerPage), 
+                    cnt         = 0,
+                    rs          = 0,
+                    reg         = /(^|\s)invisibleRow(\s|$)/,                
+                    tr, tds, cell, pos;
+                
+                for(var i = 0; i < len1; i++) {
+                        tr = data[i][len2];
+                        
+                        if(colStyle) {
+                                tds = tr.cells;
+                                for(thPos in colOrder) {
+                                        if(!colOrder[thPos]) removeClass(tds[thPos], colStyle);
+                                        else addClass(tds[thPos], colStyle);
+                                };
+                        };
+                        
+                        if(tr.className.search(reg) != -1) { continue; };
+                        
+                        if(!identical) {
+                                cnt++;
+
+                                if(cnt > d1 && cnt <= d2) {
+                                        if(rowStyle) {
+                                                if(rs++ & 1) addClass(tr, rowStyle);
+                                                else removeClass(tr, rowStyle);
+                                        };
+                                        tr.style.display = "";
+                                } else {
+                                        tr.style.display = "none";
+                                };
+
+                                // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser
+                                // hook.removeChild(tr);
+                                hook.appendChild(tr);
+                        };
+                };
+
+                tr = tds = hook = null;
+        };
+        
+        var showPage = function(tblId, pageNum) {
+                if(!(tblId in tableInfo)) { return; };
+
+                var page = Math.max(0, !pageNum ? tableInfo[tblId].currentPage - 1 : pageNum - 1),
+                    d1  = tableInfo[tblId].rowsPerPage * page,
+                    d2  = Math.min(tableInfo[tblId].totalRows, d1 + tableInfo[tblId].rowsPerPage),
+                    trs = tableInfo[tblId].hook.rows,
+                    cnt = 0,
+                    rc  = 0,
+                    len = trs.length,
+                    rs  = tableInfo[tblId].rowStyle,
+                    reg = /(^|\s)invisibleRow(\s|$)/,
+                    row = [];
+                
+                for(var i = 0; i < len; i++) {
+                        if(trs[i].className.search(reg) != -1 || trs[i].getElementsByTagName("th").length || (trs[i].parentNode && trs[i].parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) { continue; };
+                        
+                        cnt++;
+                        
+                        if(cnt > d1 && cnt <= d2) {
+                                if(rs) {
+                                        if(rc++ & 1) {
+                                                addClass(trs[i], rs);
+                                        } else {
+                                                removeClass(trs[i], rs);
+                                        }
+                                };
+                                trs[i].style.display = "";
+                                row[row.length] = trs[i];
+                        } else {
+                                trs[i].style.display = "none";
+                        };
+                };
+
+                buildPagination(tblId);
+                callback(tblId, {"totalRows":len, "currentPage":(page+1), "rowsPerPage":tableInfo[tblId].rowsPerPage, "visibleRows":row});
+        };
+        
+        var buttonClick = function(e) {
+                e = e || window.event;
+
+                var a = this.tagName.toLowerCase() == "a" ? this : this.getElementsByTagName("a")[0];
+
+                if(a.className.search("currentPage") != -1) return false;
+
+                var ul = this;
+                while(ul.tagName.toLowerCase() != "ul") ul = ul.parentNode;
+
+                var tblId = ul.id.replace("-tablePaginaterClone","").replace("-tablePaginater", "");
+
+                tableInfo[tblId].lastPage = tableInfo[tblId].currentPage;
+                
+                var showPrevNext = 0;
+                
+                if(a.className.search("previous-page") != -1) {
+                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage > 1 ? tableInfo[tblId].currentPage - 1 : tableInfo[tblId].numPages;
+                        showPrevNext = 1;
+                } else if(a.className.search("next-page") != -1) {
+                        tableInfo[tblId].currentPage = tableInfo[tblId].currentPage < tableInfo[tblId].numPages ? tableInfo[tblId].currentPage + 1 : 1;
+                        showPrevNext = 2;
+                } else if(a.className.search("first-page") != -1) {
+                        tableInfo[tblId].currentPage = 1;
+                } else if(a.className.search("last-page") != -1) {
+                        tableInfo[tblId].currentPage = tableInfo[tblId].numPages;
+                } else {
+                        tableInfo[tblId].currentPage = parseInt(a.className.match(/page-([0-9]+)/)[1]) || 1;
+                };
+
+                showPage(tblId);
+
+                // Focus on the appropriate button (previous, next or the current page)
+                // I'm hoping screen readers are savvy enough to indicate the focus event to the user
+                if(showPrevNext == 1) {
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-previousPageC" : tblId + "-previousPage");
+                } else if(showPrevNext == 2) {
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-nextPageC" : tblId + "-nextPage");
+                } else {
+                        var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-currentPageC" : tblId + "-currentPage");
+                };
+                
+                if(elem && elem.tagName.toLowerCase() == "a") { elem.focus(); };   
+                
+                return stopEvent(e);
+        };
+        
+        var onUnLoad = function(e) {
+                var tbl, lis, pagination, uls;
+                for(tblId in tableInfo) {
+                        uls = [tblId + "-tablePaginater", tblId + "-tablePaginaterClone"];
+                        for(var z = 0; z < 2; z++) {
+                                pagination = document.getElementById(uls[z]);
+                                if(!pagination) { continue; };
+                                lis = pagination.getElementsByTagName("li");
+                                for(var i = 0, li; li = lis[i]; i++) {
+                                        li.onclick = null;
+                                        if(li.getElementsByTagName("a").length) { li.getElementsByTagName("a")[0].onclick = null; };
+                                };
+                        };
+                };
+        };
+        
+        addEvent(window, "load",   init);
+        addEvent(window, "unload", onUnLoad); 
+        
+        return {                  
+                init:                   function(tableId) { init(tableId); },                 
+                redraw:                 function(tableid, identical) { tableSortRedraw(tableid, identical); },
+                tableIsPaginated:       function(tableId) { return (tableId in tableInfo); },
+                changeTranslations:     function(translations) { text = translations; }
+        };        
 })();
\ No newline at end of file