1 /* 2 * ==================================================================== 3 * About Sarissa: http://dev.abiss.gr/sarissa 4 * ==================================================================== 5 * Sarissa table utils are dependent on sarissa.js and are used for 6 * stuff like table sorting. 7 * @version ${project.version} 8 * @author: Copyright 2004-2008 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net 9 * ==================================================================== 10 * Licence 11 * ==================================================================== 12 * Sarissa is free software distributed under the GNU GPL version 2 (see <a href="gpl.txt">gpl.txt</a>) or higher, 13 * GNU LGPL version 2.1 (see <a href="lgpl.txt">lgpl.txt</a>) or higher and Apache Software License 2.0 or higher 14 * (see <a href="asl.txt">asl.txt</a>). This means you can choose one of the three and use that if you like. If 15 * you make modifications under the ASL, i would appreciate it if you submitted those. 16 * In case your copy of Sarissa does not include the license texts, you may find 17 * them online in various formats at <a href="http://www.gnu.org">http://www.gnu.org</a> and 18 * <a href="http://www.apache.org">http://www.apache.org</a>. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 21 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 22 * WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 24 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 26 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 */ 29 30 /** 31 * Sort the table data based on the column corresponding to the given TH element (clickedElem). 32 * @memberOf Sarissa 33 * @param {Node} clickedElem the table heading (<code>th</code>) initiating the sort. 34 * @param {Function} iFunc the custom sort function if needed. Default (null) is case-sensitive sort. 35 * You can also use <code>Sarissa.SORT_IGNORE_CASE</code>, <code>Sarissa.SORT_DATE_US</code>, 36 * and <code>Sarissa.SORT_DATE_EU</code> 37 * @param {boolean} bSkipCache whether to skip the data cache and read table data all over again. Setting this 38 * to <code>true</code> means the cache for the table, if it exists, will not be updated either. Defaul is <code>false</code> 39 * @param {Function} oCallbac a callback function to be executed when the table is 40 * sorted and updated. The callback function may be used for effects for example. The parameters 41 * passed to the callback are the table as a DOM node and the sort column index (zero based <code>int</code>) 42 * @requires Sarissa sarissa.js 43 */ 44 Sarissa.sortHtmlTableData = function(clickedElem, iFunc, bSkipCache, oCallbac){ 45 // get the table 46 var oTbl = clickedElem.parentNode.parentNode; 47 while(oTbl.nodeName.toLowerCase() != "table"){ 48 oTbl = oTbl.parentNode; 49 } 50 // we need a table ID for the cache 51 if(!oTbl.id){ 52 oTbl.id = "SarissaTable"+ (Sarissa.tableIdGenCount++); 53 } 54 // the column to sort on 55 var iColIndex = clickedElem.cellIndex; 56 var matrix; 57 // use the cache if available and permitted 58 if(!bSkipCache && Sarissa.tableDataCache[oTbl.id]){ 59 matrix = Sarissa.tableDataCache[oTbl.id]; 60 } 61 else{ 62 // read table, skip any rows containing headings, cache if permitted 63 matrix = this.getArrayFromTableData(oTbl, null, null, "th"); 64 if(!bSkipCache){ 65 Sarissa.tableDataCache[oTbl.id] = matrix; 66 } 67 } 68 // init state persistence as needed 69 if(!Sarissa.tableColumnSortStates[oTbl.id]){ 70 Sarissa.tableColumnSortStates[oTbl.id] = []; 71 } 72 // build a array to sort from the specific column data, adding 73 // original index info as a suffix 74 var sortedColumn = []; 75 for(var i=0; i < matrix.length;i++){ 76 sortedColumn[i] = Sarissa.stripTags(matrix[i][iColIndex]) + "_mbns_" + i; 77 } 78 // sort the array 79 if(iFunc){ 80 sortedColumn.sort(iFunc); 81 } 82 else{ 83 sortedColumn.sort(); 84 } 85 // persist column state 86 var sortOrder = Sarissa.tableColumnSortStates[oTbl.id][iColIndex]; 87 if(sortOrder != "asc"){ 88 Sarissa.tableColumnSortStates[oTbl.id][iColIndex] = "asc"; 89 } 90 else{ 91 sortedColumn.reverse(); 92 Sarissa.tableColumnSortStates[oTbl.id][iColIndex] = "desc"; 93 } 94 // create the sorted matrix based on sortedColumn 95 var sortedMatrix = []; 96 for(var j=0; j < matrix.length; j++){ 97 var indexItem = sortedColumn[j]; 98 var iRow = indexItem.substring(indexItem.indexOf("_mbns_")+6, indexItem.length); 99 sortedMatrix[j] = []; 100 for(var k=0; k < matrix[j].length; k++){ 101 sortedMatrix[j][k] = matrix[iRow][k]; 102 } 103 } 104 // update table data, skipping rows with headings 105 this.updateTableData(oTbl, sortedMatrix, null, null, "th"); 106 if(oCallbac){ 107 oCallbac(oTbl, iColIndex); 108 } 109 }; 110 111 /** 112 * Used for generating table IDs, which are required for the cache and sort state persistance 113 * @memberOf Sarissa 114 * @private 115 */ 116 Sarissa.tableIdGenCount = 0; 117 118 /** 119 * Used for persisting sort state per table column 120 * @memberOf Sarissa 121 * @private 122 */ 123 Sarissa.tableColumnSortStates = []; 124 125 /** 126 * Used for caching table data. 127 * @memberOf Sarissa 128 */ 129 Sarissa.tableDataCache = []; 130 131 /** 132 * Keep track of the cache size. The length property is not for associative arrays 133 * and I really dont want to add 50 lines and implement a PseudoHashMap right now :-) 134 * @memberOf Sarissa 135 * @private 136 */ 137 Sarissa.tableDataCacheSize = 0; 138 139 /** 140 * The table data cache size, used for sorting HTML tables. You can change it, default is 5 (tables). When a 141 * table is cached exceeding the cache size, the oldest entry is disgarded from the cache. 142 * @memberOf Sarissa 143 */ 144 Sarissa.tableDataCacheMaxSize = 5; 145 146 /** 147 * Updates the cache, discards oldest entry if cache size is exceeded. 148 * @memberOf Sarissa 149 * @private 150 */ 151 Sarissa.tableDataCachePut = function(sTableId, oArr){ 152 if(Sarissa.tableDataCacheSize.length >= Sarissa.tableDataCacheMaxSize){ 153 Sarissa.tableDataCache.shift(); 154 Sarissa.tableDataCacheSize--; 155 } 156 Sarissa.tableDataCache[sTableId] = oArr; 157 Sarissa.tableDataCacheSize++; 158 }; 159 /** 160 * Updates the cache of a specific table by reposition a column in the cached data. 161 * This is usefull if you use DHTML to visually reposition columns and need to 162 * synchronize the cache. 163 * @memberOf Sarissa 164 * @private 165 */ 166 Sarissa.tableDataCacheMoveColumn = function(sTableId, oldColumnIndex, newColumnIndex){ 167 var oldMatrix = Sarissa.tableDataCache[sTableId]; 168 var newMatrix = []; 169 // iterate rows 170 var oldRow, movedColumn, newRow; 171 for(var i=0; i<oldMatrix.length; i++){ 172 oldRow = oldMatrix[i]; 173 movedColumn = oldRow.splice(oldColumnIndex, 1); 174 newRow = []; 175 // reposition column value 176 for(var j=0;j<oldArr.length;J++){ 177 if(j == newColumnIndex){ 178 newRow.put(movedColumn); 179 } 180 newRow.put(oldRow[j]); 181 } 182 newMatrix[i] = newRow; 183 } 184 Sarissa.tableDataCache[sTableId] = newMatrix; 185 }; 186 187 /** 188 * Function for case-insensitive sorting or simple comparison. Can be used as 189 * a parameter to <code>Array.sort()</code>. 190 * @memberOf Sarissa 191 * @param a a string 192 * @param b a string 193 * @return -1, 0 or 1 depending on whether <code>a</code> is "less than", equal or "greater than" <code>b</code> 194 */ 195 Sarissa.SORT_IGNORE_CASE = function(a, b){ 196 var strA = a.toLowerCase(), 197 strB = b.toLowerCase(); 198 if(strA < strB) return -1; 199 else if(strA > strB) return 1; 200 else return 0; 201 }; 202 203 /** 204 * Function for comparing US dates. Can be used as 205 * a parameter to <code>Array.sort()</code>. 206 * @memberOf Sarissa 207 * @param a a string 208 * @param b a string 209 * @return -1, 0 or 1 depending on whether <code>a</code> is "less than", equal or "greater than" <code>b</code> 210 */ 211 Sarissa.SORT_DATE_US = function(a, b){ 212 var datA = new Date(a.substring(0, a.lastIndexOf("_mbns_"))), 213 datB = new Date(b.substring(0, b.lastIndexOf("_mbns_"))); 214 if(datA < datB) return -1; 215 else if(datA > datB) return 1; 216 else return 0; 217 218 }; 219 220 /** 221 * Function for comparing EU dates. Can be used as 222 * a parameter to <code>Array.sort()</code>. 223 * @memberOf Sarissa 224 * @param a a string 225 * @param b a string 226 * @return -1, 0 or 1 depending on whether <code>a</code> is "less than", equal or "greater than" <code>b</code> 227 */ 228 Sarissa.SORT_DATE_EU = function(a, b){ 229 var strA = a.substring(0, a.lastIndexOf("_mbns_")).split("/"), 230 strB = b.substring(0, b.lastIndexOf("_mbns_")).split("/"), 231 datA = new Date(strA[2], strA[1], strA[0]), 232 datB = new Date(strB[2], strB[1], strB[0]); 233 if(datA < datB) return -1; 234 else if(datA > datB) return 1; 235 else return 0; 236 }; 237 238 /** 239 * Get the data of the given element as a two-dimensional array. The 240 * given XML or HTML Element must match the structure of an HTML table, 241 * although element names may be different. 242 * @memberOf Sarissa 243 * @param oElem an HTML or XML table. The method works out of the box 244 * for <code>table</code>, <code>tbody</code>, <code>thead</code> 245 * or <code>tfooter</code> elements. For custom XML tables, the 246 * <code>sRowName</code> <code>sCellName</code> must be used. 247 * @param sRowName the row element names. Default is <code>tr</code> 248 * @param sCellName the row element names. Default is <code>td</code> 249 * @param sHeadingName the heading element names. If you use this, rows with 250 * headings will be <strong>skipped</strong>. To skip headings when reading 251 * HTML tables use <code>th</code> 252 * @param bStripTags whether to strip markup from cell contents. Default is <code>false</code> 253 * @return a two-dimensional array with the data found in the given element's rows 254 */ 255 Sarissa.getArrayFromTableData = function(oElem, sRowName, sCellName, sHeadingName, bStripTags){ 256 if(!sRowName){ 257 sRowName = "tr" 258 } 259 if(!sCellName){ 260 sCellName = "td" 261 } 262 if(!sHeadingName){ 263 sHeadingName = "th" 264 } 265 var rows = oElem.getElementsByTagName(sRowName); 266 var matrix = []; 267 for(var i=0, j=0; i < rows.length; i++) { 268 // skip rows with headings 269 var row = rows[i]; 270 if((!sHeadingName) || row.getElementsByTagName(sHeadingName).length == 0){ 271 matrix[j] = []; 272 var cells = row.getElementsByTagName(sCellName); 273 for(var k=0; k < cells.length; k++){ 274 matrix[j][k] = bStripTags ? Sarissa.stripTags(cells[k].innerHTML) : cells[k].innerHTML; 275 } 276 j++; 277 } 278 } 279 return matrix; 280 }; 281 282 /** 283 * Update the data of the given element using the giventwo-dimensional array as a source. The 284 * given XML or HTML Element must match the structure of an HTML table. 285 * @memberOf Sarissa 286 * @param oElem an HTML or XML table. The method works out of the box 287 * for <code>table</code>, <code>tbody</code>, <code>thead</code> 288 * or <code>tfooter</code> elements. For custom XML tables, the 289 * <code>sRowName</code> <code>sCellName</code> must be used. 290 * @param sRowName the row element names. Default is <code>tr</code> 291 * @param sCellName the row element names. Default is <code>td</code> 292 * @param sHeadingName the heading element names. If you use this, rows with 293 * headings will be <strong>skipped</strong>. To skip headings when reading 294 * HTML tables use <code>th</code> 295 */ 296 Sarissa.updateTableData = function(oElem, newData, sRowName, sCellName, sHeadingName){ 297 if(!sRowName){ 298 sRowName = "tr" 299 } 300 if(!sCellName){ 301 sCellName = "td" 302 } 303 var rows = oElem.getElementsByTagName(sRowName); 304 for(var i=0, j=0; i < newData.length && j < rows.length; j++){ 305 // skip rows with headings 306 var row = rows[j]; 307 if((!sHeadingName) || row.getElementsByTagName(sHeadingName).length == 0){ 308 var cells = row.getElementsByTagName(sCellName); 309 for(var k=0; k < cells.length; k++){ 310 cells[k].innerHTML = newData[i][k]; 311 } 312 i++; 313 } 314 } 315 }; 316 317 318