// Description: Sort table // Peter Kahrel // Place the cursor in the column to be sorted. // You can indicate header and footer rows that should be ignored, // or select a range of rows to be sorted. // and whether the sort should be ascending or descending. // The script recognises numerical data and deals with that correctly. params = getParams(); app.dialogs.everyItem().destroy(); // getparams() returns an object with six fields: // tbl: the selected table as object // col: the index of the selected column // sortType: 'ascend' or 'descend' // alphanum: 'alpha' or 'num' // hdrLines: a number // ftrLines: a number tablesort( params.tbl, params.col, params.hdrLines, params.ftrLines, params.sortType, params.alphanum ); //----------------------------------------------------- function tablesort( tbl, col, hdr, ftr, stype, alphanum ) { ftr = tbl.rows.length-ftr-1; // add dummy row, used for the row swapper tbl.rows.add(); sort_table( tbl, col, hdr, ftr, stype, alphanum ); // delete dummy row tbl.rows[-1].remove(); } //----------------------------------------------------- function sort_table( tbl, c, first, last, dir, anum ) { var i = first; var j = last; var pivot = tbl.columns[c].cells[Math.floor( (first+last) / 2)].contents; while( i < j ) { while( larger_than ( pivot, tbl.columns[c].cells[i].contents, dir, anum ) ) i++; while( larger_than ( tbl.columns[c].cells[j].contents, pivot, dir, anum ) ) j--; if( i <= j ) { swap_rows( tbl, i, j ); i++; j--; } } if( first < j ) sort_table( tbl, c, first, j, dir, anum ); if( i < last ) sort_table( tbl, c, i, last, dir, anum ); } /* Swap_rows swaps rows by moving a row x into the dummy row, row y into row x, and the dummy into row y. As we can't move rows, we have to move the contents of the cells. */ function swap_rows(t, x, y) { // This if clause shouldn't be necessary, and it isn't // in plain line sorts, but for some reason is required here. if ( x != y ) { for( var i = 0; i < t.rows[x].cells.length; i++ ) t.rows[x].cells[i].texts[0].move ( LocationOptions.after, t.rows[-1].cells[i].insertionPoints[0] ); for( var i = 0; i < t.rows[y].cells.length; i++ ) t.rows[y].cells[i].texts[0].move ( LocationOptions.after, t.rows[x].cells[i].insertionPoints[0] ); for( var i = 0; i < t.rows[-1].cells.length; i++ ) t.rows[-1].cells[i].texts[0].move ( LocationOptions.after, t.rows[y].cells[i].insertionPoints[0] ); } } //----------------------------------------------------- function larger_than( a, b, dir, num ) { switch ( dir+num ) { case 'ascendalpha' : return a > b ; break; case 'descendalpha' : return a < b ; break; case 'ascendnum' : return Number(a) > Number(b) ; break; case 'descendnum' : return Number(a) < Number(b) ; break; } } //----------------------------------------------------- function getParams() { // sel returns four-element object: tbl (table as object), // col (index of selected column), cont (contents of selected cell), // and hdr. The last one is 0 of no rows are selected. var sel = getTable(); var sortTypeButtons; var dlg = app.dialogs.add( { name : 'Table sort' } ); with( dlg ) { with( dialogColumns.add() ) { with( borderPanels.add() ) { with( sortTypeButtons = radiobuttonGroups.add( ) ) { radiobuttonControls.add( { staticLabel : 'Ascending sort' , checkedState : true} ); radiobuttonControls.add( { staticLabel : 'Descending sort ' } ) } with( alphanumButtons = radiobuttonGroups.add( ) ) { radiobuttonControls.add( { staticLabel : 'Numerical' , checkedState : isNumber(sel.cont) == 'num' } ); radiobuttonControls.add( { staticLabel : 'Alphanumerical', checkedState : isNumber(sel.cont) == 'alpha' } ) } } // -1: see comment in getTable() if( sel.hdr == -1 ) { with( borderPanels.add() ) { with( dialogColumns.add() ) { staticTexts.add( { staticLabel : ' Number of header rows:' } ); staticTexts.add( { staticLabel : 'Number of footer rows:' } ) } with( dialogColumns.add() ) { var hdrLines = integerEditboxes.add( { editValue : sel.tbl.headerRowCount, minimumValue : 0, maximumValue : 10, smallNudge : 1 } ); var ftrLines = integerEditboxes.add( { editValue : sel.tbl.footerRowCount, minimumValue : 0, maximumValue : 10, smallNudge : 1, largeNudge : 5 } ) } } } } } if( dlg.show() == false ) { dlg.destroy() exit() }; return { tbl : sel.tbl, col : sel.col, alphanum : ['num','alpha'][alphanumButtons.selectedButton], sortType : ['ascend','descend'][sortTypeButtons.selectedButton], hdrLines : sel.hdr != -1 ? sel.hdr : hdrLines.editValue, ftrLines : sel.ftr != -1 ? sel.ftr : ftrLines.editValue } } //----------------------------------------------------- function getTable() { try { if( app.selection[0].parent instanceof Cell ) // If an ins. point or part of cell is selected, // return table, col. index, and contents selected cell. return { tbl : app.selection[0].parent.parent, col : app.selection[0].parent.parentColumn.index, cont : app.selection[0].parent.contents, // We set hdr and ftr to -1 here. Zero is no good, // as that's the index returned when the first row // in a selection is the first row of the table. hdr : -1, ftr : -1 } else if( app.selection[0].rows.length > 1 ) // If several rows are selected, then return // hdr and ftr length return { tbl : app.selection[0].parent, col : app.selection[0].parentColumn.index, cont : app.selection[0].cells[0].contents, hdr : app.selection[0].parentRow.index, ftr : app.selection[0].parent.rows.length - app.selection[0].parentRow.index - app.selection[0].rows.length } } catch(_) { errorM( 'Cursor not in a table\ror illegal selection.\r' + '(Select an insertion point\ror some rows.)' ) } } function errorM( m ) { alert( m ); exit(); } function isNumber( x ) { if ( x.search(/^[\d.]+$/) > -1 ) return 'num'; else return 'alpha'; }