0byt3m1n1
Path:
/
data
/
applications
/
aps.bak
/
tikiwiki
/
1.9.7-31
/
htdocs
/
lib
/
sheet
/
[
Home
]
File: grid.js
/** * TikiSheet Client-side grid manipulation. * By Louis-Philippe Huberdeau * 2004 */ // Global Variables {{{1 var COLCHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var globalFrom; var globalGrid; // }}}1 // Generic value processing {{{1 // decrement {{{2 function decrement( val ) { var nChar = COLCHAR.indexOf( val.charAt( val.length - 1 ) ) - 1; if( nChar >= 0 ) { return val.substr( 0, val.length - 1 ) + COLCHAR.charAt( nChar ); } else { return decrement( val.substr( 0, val.length - 1 ) ) + COLCHAR.charAt( COLCHAR.length - 1 ); } } // increment {{{2 function increment( val ) { var nChar = COLCHAR.indexOf( val.charAt( val.length - 1 ) ) + 1; if( nChar < COLCHAR.length ) { return val.substr( 0, val.length - 1 ) + COLCHAR.charAt( nChar ); } else { return increment( val.substr( 0, val.length - 1 ) ) + COLCHAR.charAt(0); } } // processValue {{{2 function processValue( id ) { var cell = globalGrid.getCell( id ); if( cell.calc ) return cell.endValue; var value = cell.value; if( value == null || value.substr( 0, 1 ) != "=" ) { cell.endValue = value; } else { var temp = value.substr( 1 ); temp = temp.replace( /\$?([A-Z]+)\$?([0-9]+):\$?([A-Z]+)\$?([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { var topLeft = globalGrid.getCell( $1 + $2 ); var bottomRight = globalGrid.getCell( $3 + $4 ); var row = topLeft.row; var col; var list = new Array; while( row != bottomRight.row.next ) { col = topLeft.column; while( col != bottomRight.column.next ) { c = row.getCell( col ); if( c.master == null || c.master == c ) list.push( c.getId() ); col = col.next; } row = row.next; } return "[" + list.join(",") + "]"; } ); temp = temp.replace( /\$?([A-Z]+)\$?([0-9]+)/g, "parseFloat(processValue( '$1$2' ))" ); cell.endValue = eval( temp ); } field = globalGrid.target[id]; cell.calc = true; if( field ) field.value = cell.getDisplayValue(); return cell.endValue; } // }}}1 // Extra Array functionalities {{{1 Array.prototype.remove = function( element ) { var key; for( key in this ) if( this[key] == element ) { this.splice( key, 1 ); return true; } return false; } Array.prototype.contains = function( element ) { var key; for( key in this ) if( this[key] == element ) return true; return false; } // }}}1 // Constructors {{{1 // Grid {{{2 function Grid( target ) { globalGrid = this; this.firstRow = null; this.fistColumn = null; this.target = target; this.cells = new Array(); this.nbRow = 0; this.nbCol = 0; this.focus = null; this.selection = new Selection(0,0); } // Cell {{{2 function Cell( grid, row, column ) { this.grid = grid; this.row = row; this.column = column; row.cells.push( this ); column.cells.push( this ); grid.cells.push( this ); this.endValue = ""; this.value = ""; this.format = ''; this.calc = false; this.width = 1; this.height = 1; } // Row {{{2 function Row( grid, previous ) { this.grid = grid; this.grid.nbRow++; var next = ( previous != null ) ? previous.next : null; // Chain list rows {{{3 if( previous == null ) { this.next = grid.firstRow; if( this.next != null ) this.next.previous = this; this.previous = null; grid.firstRow = this; } else { this.next = previous.next; this.previous = previous; previous.next = this; if( this.next != null ) this.next.previous = this; } // Generate cells {{{3 var current = grid.firstColumn, cell; this.cells = new Array(); var masters = new Array(); // Populate a cell fro every column while( current != null ) { cell = new Cell( grid, this, current ); // Sides are automatically excluded if( previous != null && next != null ) { // Get the master (merged) cell of the given locations pMaster = current.getIndexCell( previous.index ).master; nMaster = current.getIndexCell( next.index ).master; // part of the same merged cell if( nMaster != null && nMaster == pMaster ) { cell.height = 0; cell.width = 0; cell.master = nMaster; // Increment master cells only once {{{4 var found = false; for( key in masters ) { // If master cell already found if( masters[key] == nMaster ) { found = true break; } } if( !found ) masters.push( nMaster ); // }}}4 } } current = current.next; } // Fix references {{{3 current = this.grid.firstRow; while( current != null ) { if( previous == null ) current.incrementReference( null ); else current.incrementReference( previous ) current = current.next; } for( key in masters ) { masters[key].height++; } } // Column {{{2 function Column( grid, previous ) { this.grid = grid; this.grid.nbCol++; var next = ( previous != null ) ? previous.next : null; // Chain list columns {{{3 if( previous == null ) { this.next = grid.firstColumn; if( this.next != null ) this.next.previous = this; this.previous = null; grid.firstColumn = this; } else { this.next = previous.next; this.previous = previous; previous.next = this; if( this.next != null ) this.next.previous = this; } // Generate cells {{{3 var current = grid.firstRow; this.cells = new Array(); var masters = new Array(); while( current != null ) { cell = new Cell( grid, current, this ); if( previous != null && next != null ) { pMaster = current.getIndexCell( previous.index ).master; nMaster = current.getIndexCell( next.index ).master; if( pMaster != null && nMaster == pMaster ) { cell.height = 0; cell.width = 0; cell.master = nMaster; // Increment master cells only once {{{4 var found = false; for( key in masters ) { // If master cell already found if( masters[key] == nMaster ) { found = true break; } } if( !found ) masters.push( nMaster ); // }}}4 } } current = current.next; } // Fix references {{{3 current = this.grid.firstColumn; while( current != null ) { if( previous == null ) current.incrementReference( null ); else current.incrementReference( previous ); current = current.next; } for( key in masters ) { masters[key].width++; } } // }}}1 // Remove {{{1 // Cell.remove {{{2 Cell.prototype.remove = function() { this.row.cells.remove( this ); this.column.cells.remove( this ); this.grid.cells.remove( this ); } // Row.remove {{{2 Row.prototype.remove = function() { this.grid.nbRow--; var masters = new Array(); for( var key = 0; this.cells.length > key; key++ ) if( this.cells[key] == this.cells[key].master ) this.cells[key].changeSize(1,1); while( this.cells.length > 0 ) { found = false; for( key in masters ) if( masters[key] == this.cells[0].master ) found = true; if( !found && this.cells[0].master != null ) masters.push( this.cells[0].master ); this.cells[0].remove(); } for( key in masters ) masters[key].height--; if( this.previous != null ) this.previous.next = this.next; else this.grid.firstRow = this.next; if( this.next != null ) this.next.previous = this.previous; var current = this.grid.firstRow; while( current != null ) { current.decrementReference( this ); current = current.next; } } // Column.remove {{{2 Column.prototype.remove = function() { this.grid.nbCol--; var masters = new Array(); for( var key = 0; this.cells.length > key; key++ ) if( this.cells[key] == this.cells[key].master ) this.cells[key].changeSize(1,1); while( this.cells.length > 0 ) { found = false; for( key in masters ) if( masters[key] == this.cells[0].master ) found = true; if( !found && this.cells[0].master != null ) masters.push( this.cells[0].master ); this.cells[0].remove(); } for( key in masters ) masters[key].width--; if( this.previous != null ) this.previous.next = this.next; else this.grid.firstColumn = this.next; if( this.next != null ) this.next.previous = this.previous; var current = this.grid.firstColumn; while( current != null ) { current.decrementReference( this ); current = current.next; } } // }}}1 // Reference Update {{{1 // Column.decrementReference {{{2 Column.prototype.decrementReference = function( from ) { globalFrom = from; for( var key = 0; this.cells.length > key; key++ ) { this.cells[key].value = this.cells[key].value.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $1 != "$" && globalGrid.getColumn( $2 ).isAfter( globalFrom ) ) return $1 + decrement($2) + $3 + $4; else return $0; } ); } } // Row.decrementReference {{{2 Row.prototype.decrementReference = function( from ) { globalFrom = from; for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[key].value == null ) continue; this.cells[key].value = this.cells[key].value.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $3 != "$" && globalGrid.getRow( $4 ).isAfter( globalFrom ) ) { return $1 + $2 + $3 + String( parseInt( $4 ) - 1 ); } else return $0; } ); } } // Column.incrementReference {{{2 Column.prototype.incrementReference = function( from ) { globalFrom = from; for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[key].value == null ) continue; this.cells[key].value = this.cells[key].value.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $1 != "$" && globalGrid.getColumn( $2 ).isAfter( globalFrom ) ) return $1 + increment($2) + $3 + $4; else return $0; } ); } } // Row.incrementReference {{{2 Row.prototype.incrementReference = function( from ) { globalFrom = from; for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[key].value == null ) continue; this.cells[key].value = this.cells[key].value.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $3 != "$" && globalGrid.getRow( $4 ).isAfter( globalFrom ) ) return $1 + $2 + $3 + String( parseInt( $4 ) + 1 ); else return $0; } ); } } // }}}1 // Element Manipulation {{{1 // Column.isAfter {{{2 Column.prototype.isAfter = function( element ) { if( element == null ) return true; current = element.next; while( current != null ) { if( current == this ) return true; current = current.next; } return false; } // Row.isAfter {{{2 Row.prototype.isAfter = function( element ) { if( element == null ) return true; current = element.next; while( current != null ) { if( current == this ) return true; current = current.next; } return false; } // Row.getCell {{{2 Row.prototype.getCell = function( column ) { for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[ key ].column == column ) return this.cells[ key ]; } alert( "An error occured, inexistant column in row.getCell" ); } // Row.getIndexCell {{{2 Row.prototype.getIndexCell = function( index ) { for( var key = 0; this.cells.length > key; key++ ) if( this.cells[key].column.index == index ) return this.cells[key]; return null; } // Column.getIndexCell {{{2 Column.prototype.getIndexCell = function( index ) { for( var key = 0; this.cells.length > key; key++ ) if( this.cells[key].row.index == index ) return this.cells[key]; return null; } // Column.getCell {{{2 Column.prototype.getCell = function( row ) { for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[ key ].row == row ) return this.cells[ key ]; } alert( "An error occured, inexistant row in column.getCell" ); } // Cell.getId {{{2 Cell.prototype.getId = function() { return this.column.id + this.row.id; } // Grid.getRow {{{2 Grid.prototype.getRow = function( id ) { var current = this.firstRow; while( current != null ) { if( current.id == id ) return current; current = current.next; } alert( "An error occured, inexistant row in grid.getRow" ); } // Grid.getColumn {{{2 Grid.prototype.getColumn = function( id ) { var current = this.firstColumn; while( current != null ) { if( current.id == id ) return current; current = current.next; } alert( "An error occured, inexistant column in grid.getColumn" ); } // Grid.getCell {{{2 Grid.prototype.getCell = function( id ) { for( var key = 0; this.cells.length > key; key++ ) { if( this.cells[ key ].getId() == id ) return this.cells[ key ]; } alert( "An error occured, inexistant cell in grid.getCell" ); } // Grid.getIndexCell {{{2 Grid.prototype.getIndexCell = function( row, col ) { for( i = 0; this.cells.length > i; i++ ) { cell = this.cells[i]; if( cell.column.index == col && cell.row.index == row ) return cell; } return null; } // }}}1 // Value Update {{{1 // Grid.update {{{2 Grid.prototype.update = function( input ) { c = this.getCell( input.name ); c.setValue( input.value ); } // Cell.setValue {{{2 Cell.prototype.setValue = function( value ) { this.value = value; this.grid.refresh(); } // Grid.identify {{{2 Grid.prototype.identify = function() { var i = 0; var index = 0; var current = this.firstColumn; while( current != null ) { if( current.previous == null ) current.id = COLCHAR.charAt(0); else current.id = increment( current.previous.id ); current.index = index++; current = current.next; } index = 0; current = this.firstRow; while( current != null ) { current.id = String( ++i ); current.index = index++ current = current.next; } } // getDisplayValue {{{2 Cell.prototype.getDisplayValue = function() { if( this.format == null ) return this.endValue; var fnc; fnc = display[this.format]; if( !fnc ) return this.endValue; return fnc( this.endValue ); } // }}}1 // Draw {{{1 // Grid.draw {{{2 Grid.prototype.draw = function() { var current = this.firstColumn; var total = '<input type="text" id="topbar" name="bar" onChange="g.focus.setValue( this.value );" />\n<table>\n'; this.identify(); total += "<tr>\n<th></th>\n"; while( current != null ) { total += "<th align='center'>" + current.id + "</th>\n"; current = current.next; } total += "</tr>\n"; current = this.firstRow; while( current != null ) { total += current.draw(); current = current.next; } total += '</table>\n'; this.target.innerHTML = total; this.selection = new Selection( this.nbRow, this.nbCol ); } // Row.draw {{{2 Row.prototype.draw = function() { var current = this.grid.firstColumn; var total = '<tr>\n<th>' + this.id + '</th>\n'; while( current != null ) { total += this.getCell( current ).draw(); current = current.next; } total += '</tr>\n'; return total; } // Cell.draw {{{2 Cell.prototype.draw = function() { if( this.width != 0 && this.height != 0 ) return '<td colspan="' + String(this.width) + '" rowspan="' + String(this.height) + '"><input type="text" name="' + this.getId() + '" value="' + this.endValue + '" onChange="g.update( this )" onFocus="g.focus = g.getCell( this.name ); g.target.bar.value = g.getCell( this.name ).value; g.singleSelect( this.name, event );" onmousedown="g.preFocus();" onClick="g.handleClick( this.name, event )" onDblClick="g.target.bar.focus(); g.focus = g.getCell( this.name );" /></td>\n'; else return "<!--" + this.getId() + "-->"; } // Grid.refresh {{{2 Grid.prototype.refresh = function() { var len = this.cells.length; for( var i = 0; len > i; i++ ) this.cells[i].calc = false; for( var i = 0; len > i; i++ ) processValue( this.cells[i].getId() ); } // }}}1 // Submit {{{1 // Grid.prepareSubmit {{{2 Grid.prototype.prepareSubmit = function() { var append; for( var key = 0; this.cells.length > key; key++ ) { var c = this.cells[key]; append = "<<<" + String(c.width) + "," + String(c.height) + ">>>" + String( c.format ); if( this.target[ c.getId() ] == null ) continue; if( c.endValue != c.value ) this.target[ c.getId() ].value = String(c.endValue).replace( /=/, "\\=" ) + String( c.value ) + append; else this.target[ c.getId() ].value = c.value.replace( /=/, "\\=" ) + "=" + append; } } // }}}1 // Selection {{{1 // Grid.preFocus {{{2 Grid.prototype.preFocus = function() { this.md = true; } // Grid.singleSelect {{{2 Grid.prototype.singleSelect = function( cell, event ) { if( this.md ) { this.md = false; return; } this.selection.singleSelect( this.getCell(cell) ); this.updateSelection(); } // Grid.handleClick {{{2 Grid.prototype.handleClick = function( cell, event ) { element = this.getCell( cell ); if( event.shiftKey ) { this.selection.rangeTo( element ); } else if( event.ctrlKey ) { this.selection.toggle( element ); } else { this.selection.singleSelect( element ); } this.updateSelection(); } // Grid.updateSelection {{{2 Grid.prototype.updateSelection = function() { for( var key = 0; this.cells.length > key; key++ ) { curcell = this.cells[key]; if( curcell.value == null ) continue; id = curcell.getId(); input = this.target[id]; if( input == null ) continue; if( curcell.isSelected() ) { if( input.className == '' ) { input.className = 'selected'; } } else { if( input.className == 'selected' ) input.className = ''; } } } // Cell.isSelected {{{2 Cell.prototype.isSelected = function() { return this.grid.selection.grid[this.row.index][this.column.index]; } // }}}1 // Format cells {{{1 Grid.prototype.format = function( type ) { if( type == '' ) type = null; for( var key = 0; this.cells.length > key; key++ ) if( this.cells[key].isSelected() ) this.cells[key].format = type; } // }}}1 // Merge {{{1 // Cell.changeSize {{{2 Cell.prototype.changeSize = function( row, col ) { var i, j, baseRow, baseCol, maxRow, maxCol, cell; // Restore to normal {{{3 baseRow = this.row.index; baseCol = this.column.index; maxRow = this.height; maxCol = this.width; if( row > maxRow ) maxRow = row; if( col > maxCol ) maxCol = col; maxRow += baseRow; maxCol += baseCol; for( i = baseRow; maxRow > i; i++ ) { for( j = baseCol; maxCol > j; j++ ) { cell = this.grid.getIndexCell( i, j ); cell.width = 1; cell.height = 1; cell.master = null; } } // Hide concerned cells {{{3 maxRow = baseRow + row; maxCol = baseCol + col; for( i = baseRow; maxRow > i; i++ ) { for( j = baseCol; maxCol > j; j++ ) { cell = this.grid.getIndexCell( i, j ); cell.width = 0; cell.height = 0; cell.master = this; } } // Set the main cell span {{{3 this.width = col; this.height = row; } // }}}1 // Selection Class {{{1 // Constructor {{{2 function Selection( rowCount, columnCount ) { this.grid = new Array; for( i = 0; rowCount >= i; i++ ) { this.grid.push( new Array ) for( j = 0; columnCount >= j; j++ ) this.grid[i][j] = false; } this.row = rowCount; this.column = columnCount; this.originalSelect = new Object; this.width = 0; this.height = 0; this.topLeft = 0; } // Selection.isBlock {{{2 Selection.prototype.isBlock = function() { var sRow, eRow, sCol, eCol; for( i = 0; this.row > i; i++ ) { for( j = 0; this.column > j; j++ ) { if( sRow == null && this.grid[i][j] ) { sRow = i; for( temp = i; this.row > temp; temp++ ) { if( !this.grid[temp+1][j] ) { eRow = temp; break; } } } if( sRow != null && eRow != null && this.grid[i][j] ) if( i < sRow || i > eRow ) return false; if( sCol == null && this.grid[i][j] ) sCol = j; if( sCol != null && eCol == null && !this.grid[i][j+1] ) eCol = j; if( sCol != null && eCol != null && this.grid[i][j] ) if( j < sCol || j > eCol ) return false; } } this.height = eRow - sRow + 1; this.width = eCol - sCol + 1; this.topLeft = { row: sRow, column: sCol }; return true; } // Selection.rangeTo {{{2 Selection.prototype.rangeTo = function( cell ) { oRow = this.originalSelect.row; oCol = this.originalSelect.column; tRow = cell.row.index; tCol = cell.column.index; iRow = ( tRow < oRow ) ? -1 : 1; iCol = ( tCol < oCol ) ? -1 : 1; var tempRow; while( oCol - iCol != tCol ) { tempRow = oRow; while( tempRow - iRow != tRow ) { this.grid[tempRow][oCol] = true; tempRow += iRow; } oCol += iCol; } } // Selection.set {{{2 Selection.prototype.set = function( cell, value ) { this.grid[cell.row.index][cell.column.index] = value; if( value ) { this.originalSelect.row = cell.row.index; this.originalSelect.column = cell.column.index; } } // Selection.singleSelect {{{2 Selection.prototype.singleSelect = function( cell ) { for( i = 0; this.row > i; i++ ) for( j = 0; this.column > j; j++ ) this.grid[i][j] = false; this.set( cell, true ); } // Selection.toggle {{{2 Selection.prototype.toggle = function( cell ) { this.set( cell, !this.grid[cell.row.index][cell.column.index] ); } // }}}1 // Copy Values {{{1 Grid.prototype.copy = function( direction ) { var begin1, begin2, end1, end2, type, chtype; if( g.selection.isBlock() ) { temp = g.selection.topLeft; topLeft = g.getIndexCell( temp.row, temp.column ); bottomRight = g.getIndexCell( temp.row + g.selection.height - 1, temp.column + g.selection.width - 1 ); switch( direction ) // Parameters {{{2 { case "Left": begin1 = bottomRight.column; end1 = topLeft.column; begin2 = topLeft.row; end2 = bottomRight.row; type = 'previous'; chtype = 'column'; break; case "Right": begin1 = topLeft.column; end1 = bottomRight.column; begin2 = topLeft.row; end2 = bottomRight.row; type = 'next'; chtype = 'column'; break; case "Up": begin1 = bottomRight.row; end1 = topLeft.row; begin2 = topLeft.column; end2 = bottomRight.column; type = 'previous'; chtype = 'row'; break; case "Down": begin1 = topLeft.row; end1 = bottomRight.row; begin2 = topLeft.column; end2 = bottomRight.column; type = 'next'; chtype = 'row'; break; } // }}}2 var current2 = begin2; // For each 'line' to process while( end2.next != current2 ) { var current1 = begin1[type]; var v = begin1.getCell( current2 ).value; while( end1[type] != current1 ) { var cell = current1.getCell( current2 ); // Increment/Decrement references {{{2 if( chtype == 'row' ) { window.diff = ( type == 'previous' ) ? -1 : 1; v = v.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $3 != "$" ) return $1 + $2 + $3 + String( parseInt( $4 ) + window.diff ); else return $0; } ); } else { if( type == 'previous' ) { v = v.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $1 != "$" ) return $1 + decrement($2) + $3 + $4; else return $0; } ); } else { v = v.replace( /(\$?)([A-Z]+)(\$?)([0-9]+)/g, function( $0, $1, $2, $3, $4 ) { if( $1 != "$" ) return $1 + increment($2) + $3 + $4; else return $0; } ); } } // }}}2 cell.value = v; current1 = current1[type]; } current2 = current2.next; } this.refresh(); } } // }}}1