HTable = HControl.extend componentName: 'table' markupElemNames: [ 'header', 'subview', 'grid' ] defaultEvents: resize: true controlDefaults: HControlDefaults.extend tableType: 'rows' # 'cols' not supported yet constructor: (_view)-> @headerCols = false unless @headerCols? # array of strings @sortDescending = [] unless @sortDescending? # column sorting by index; true for descending; false for ascending @colWidths = [] unless @colWidths? # widths by column index; default 'auto' @colClasses = {} unless @colClasses? # class by column index @defaultColOptions = {} unless @defaultColOptions? # fallback default column options @colOptions = {} unless @colOptions? # column class constructor options by column index @defaultColClass = HStringView unless @defaultColClass? # fallback default column class sortCol: 0 # the default sort column useCellGrid: false # whether to use colored cells or not cellBgColor: '#eee' # the average bg color cellBgColorDiff: '#080808' # color to add/subtract for even/odd col/row cellBorderWidth: 1 # cell border width in pixels cellBorderColor: '#fff' # cell border color _destroyHeader: -> if @headerCols? _table = @ for _elemId, i in @headerCols Event.stopObserving(ELEM.get(_elemId),'click',@sortFns[i]) ELEM.del(_elemId) delete @headerCols delete @headerSizes delete @sortFns sortByCol: (_col)-> if @options.sortCol == _col if @options.sortDescending[_col]? @options.sortDescending[_col] = !@options.sortDescending[_col] else @options.sortDescending[_col] = false else @options.sortCol = _col @drawHeader() @refreshTable() # if @colViews? # for _size, i in @headerSizes # _colView = @colViews[i] # _colView.rect.setLeft(_size[0]) # _colView.rect.setWidth(_size[1]) # _colView.drawRect() _initCellBgColors: -> _color1 = @options.cellBgColor _colorDiff = @options.cellBgColorDiff if _color1 == 'transparent' @_cellBgColors = [ 'transparent', 'transparent', 'transparent' ] return else if not _colorDiff @_cellBgColors = [ _color1, _color1, _color1 ] return _color0 = @hexColorSubtract( _color1, _colorDiff ) _color2 = @hexColorAdd( _color1, _colorDiff ) @_cellBgColors = [ _color0, _color1, _color2 ] _cellBgColorOf: (_row, _col)-> _colorIndex = (_row%2) + (1-_col%2) @_cellBgColors[_colorIndex] _drawCellStyles: -> if @_bgCells? for _bgCellId in @_bgCells ELEM.del( _bgCellId ) delete @_bgCells return unless @colViews? # the border width offset calculations are fubar; refactor when time available @_bgCells = [] _parent = @markupElemIds.grid _leftOffset = @headerSizes[0][0] @_initCellBgColors() _borderSize = @options.cellBorderWidth _borderColor = @options.cellBorderColor _borderStyle = _borderSize+'px solid '+_borderColor _heightOffset = _borderSize _height = 24-_heightOffset _topAdd = 24 _halfBorderWidth = Math.floor(_borderSize*0.5) _widthOffset = 6-_borderSize-_halfBorderWidth _borderLeftOffset = 0-_halfBorderWidth for _colView, _colNum in @colViews [ _left, _width ] = @headerSizes[_colNum] if _colNum == 0 _left = _borderLeftOffset _width += _leftOffset+_borderLeftOffset _width -= _halfBorderWidth if _borderSize % 2 == 1 else _left += _borderLeftOffset-_widthOffset _left -= _borderSize if _borderSize > 1 _width += _widthOffset _width += _halfBorderWidth _top = _borderLeftOffset for _row, _rowNum in @_rows @_bgCells << ELEM.make( _parent, 'div', styles: backgroundColor: @_cellBgColorOf(_rowNum,_colNum) border: _borderStyle top: _top+'px' left: _left+'px' width: _width+'px' height: _height+'px' ) _top += _topAdd drawHeader: -> _elemIds = [] _sizes = [] _autoWidths = [] _sortFns = [] _left = 6 _table = @ for _headerCol, i in @options.headerCols _left += 6 _elemId = ELEM.make( @markupElemIds.header, 'div' ) @options.sortDescending[i] = false unless @options.sortDescending[i]? ELEM.addClassName(_elemId,'table_header_column') ELEM.flush() if @options.sortCol == i _headerCol += '
' if @options.sortDescending[i] _headerCol += '▼' else _headerCol += '▲' _headerCol += '
' _width = -6 #26 else _width = -6 #26 # ELEM.setStyle(_elemId,'font-weight','normal',true) if @options.colWidths[i]? if @options.colWidths[i] == 'auto' _autoWidths.push(i) else _width += @options.colWidths[i] else _width += @stringWidth(_headerCol, null, _elemId) _sizes.push([_left,_width]) ELEM.setAttr(_elemId,'sortcol',i) ELEM.setHTML(_elemId,_headerCol) _sortFns.push((e)-> _table.sortByCol(@sortcol) e.preventDefault() true ) Event.observe(ELEM.get(_elemId),'click',_sortFns[i]) _left += _width _elemIds.push( _elemId ) if _autoWidths.length > 0 _autoWidth = Math.floor(( @rect.width - _left )/_autoWidths.length) _plusLeft = 0 for [_left, _width], i in _sizes _elemId = _elemIds[i] ELEM.setStyle(_elemId,'left',_left+_plusLeft+'px') _sizes[i][0] = _left+_plusLeft if _autoWidths.indexOf(i) != -1 _width = _autoWidth - 6 # _width += 20 if i == @options.sortCol _sizes[i][1] = _width _plusLeft += _autoWidth ELEM.setStyle(_elemId,'width',_width+'px') @_destroyHeader() ELEM.flush() @headerCols = _elemIds @headerSizes = _sizes @sortFns = _sortFns resize: -> @drawHeader() for _colNum in [0..@headerCols.length-1] _left = @headerSizes[_colNum][0] _width = @headerSizes[_colNum][1] @colViews[_colNum].rect.offsetTo( _left, 0 ) @colViews[_colNum].rect.setWidth( _width ) @colViews[_colNum].drawRect() @_drawCellStyles() drawSubviews: -> if @options.headerCols @drawHeader() else @setStyleOfPart('body','top',0) _findClassInNameSpace: (_className)-> if typeof _className == 'function' and _className.hasAncestor? and _className.hasAncestor( HControl ) return _className else if typeof _className == 'string' and window[_className]? return window[_className] # should have more elegant lookup console.warn( 'HTable#'+'_'+'findClassNameInNamespace: No such className => ', _className, ', using default => ',@options.defaultColClass ) return @options.defaultColClass _getClassNameAndValueOfCol: (_col, _colNum)-> if @options.colOptions[_colNum]? _colOption = @options.colOptions[_colNum] else _colOption = @cloneObject( @options.defaultColOptions ) if @options.colClasses[_colNum]? _colClass = @options.colClasses[_colNum] if typeof _colClass == 'function' and _colClass.hasAncestor? and _colClass.hasAncestor( HControl ) return [ _colClass, _colOption ] else if _colClass instanceof Object and not _colClass.hasAncestor? for _className of _colClass return [ @_findClassInNameSpace( _className ), _colClass[_className] ] if @typeChr(_className) == 's' else if @typeChr(_colClass) == 's' return [ @_findClassInNameSpace( _colClass ), _colOption ] return [ @_findClassInNameSpace( @options.defaultColClass ), _colOption ] _destroyRows: -> for _row, _rowNum in @_rows for _col, _colNum in _row _col.die() _row[_colNum] @_rows = [] @_rowsDrawn = false filterRow: (_value)-> return false sortTableRows: -> _rowsVisible = 0 _col = @options.sortCol _sortDescending = @options.sortDescending _desc = _sortDescending[_col] _rowSort = [] for _row, i in @_rows _rowSort.push( [ @value[i], _row ] ) _nextCols = [] if @options.sortOrder? and @options.sortOrder[_col]? if @typeChr( @options.sortOrder[_col] ) == 'a' _nextCols = @cloneObject( @options.sortOrder[_col] ) else _nextCols = [ @options.sortOrder[_col] ] else _nextCols = [] _rowSorter = (_row1, _row2, _col, _nextCols, _desc)-> _r1 = _row1[0][_col] _r2 = _row2[0][_col] while _r1 == _r2 and _nextCols.length > 0 _nextCol = _nextCols.shift() _desc = _sortDescending[_nextCol] _r1 = _row1[0][_nextCol] _r2 = _row2[0][_nextCol] return 0 if _r1 == _r2 if _desc return 1 if _r1 < _r2 else return 1 if _r1 > _r2 return -1 _rowSort.sort( (_row1,_row2)-> _rowSorter(_row1, _row2, _col, HVM.clone(_nextCols), _desc) ) _top = 0 _rowHeight = 24 for [ _value, _row ], _rowNum in _rowSort if @filterRow(_value) for _col in _row _col.hide() else _rowsVisible += 1 for _col, _colNum in _row _col.show() _col.rect.offsetTo( 0, _top ) _col.drawRect() _top += _rowHeight @_rowsVisible = _rowsVisible refreshTableRows: (_newData)-> return unless @headerCols? _top = 0 _rowHeight = 24 if @colViews? _colViews = @colViews else _colViews = [] for _colNum in [0..@headerCols.length-1] _left = @headerSizes[_colNum][0] _width = @headerSizes[_colNum][1] _colViews[_colNum] = HView.nu( [ _left, 0, _width, 1 ], @ ) @colViews = _colViews if @_rowsDrawn and not _newData @sortTableRows() else if @_rowsDrawn and _newData and @_rows.length == @value.length for _row, _rowNum in @value for _col, _colNum in _row _ctrl = @_rows[_rowNum][_colNum] _ctrl.setValue( _col ) @sortTableRows() else if @_rowsDrawn @_destroyRows() _rows = [] for _row, _rowNum in @value _rows[_rowNum] = [] for _col, _colNum in _row [ _colClass, _colOpts ] = @_getClassNameAndValueOfCol(_col, _colNum) _colOpts.value = _col _colOpts.tableRow = _rowNum _colOpts.tableCol = _colNum _ctrl = _colClass.new( [ 0, _top, null, _rowHeight, 0, null ], @colViews[_colNum], _colOpts ) _rows[_rowNum][_colNum] = _ctrl _top += _rowHeight for _colView in _colViews _colView.rect.setHeight(_top) _colView.drawRect() @_rows = _rows @_rowsDrawn = true @sortTableRows() refreshTableCols: (_newData)-> console.warn('HTable#refreshTableCols is not implemented yet!') refreshTable: (_newData)-> if @options.tableType == 'rows' @refreshTableRows(_newData) else if @options.tableType == 'cols' console.log('ERROR; refreshTable: tableType \'cols\' not supported!') @refreshTableCols(_newData) @_drawCellStyles() if @options.useCellGrid and _newData refreshValue: -> if @value instanceof Array @refreshTable( true )