1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-17 22:47:59 +00:00
seahub/media/aloha-0.22.7/plugins/common/table/lib/table-selection.js
llj 720ac28c22 [aloha] upgraded to 0.22.7 and added textcolor plugin
* textcolor: fixed plugin bugs, added translation for zh
* image: fixed default.jpg src bug
* added 'ru' support for seaf edit
* rm aloha-0.22.3 and ununsed files in aloha-0.22.7
2013-01-15 14:48:04 +08:00

439 lines
12 KiB
JavaScript

define(
['aloha',
'jquery',
'table/table-plugin-utils',
'table/table-cell',
'ui/dialog',
'i18n!table/nls/i18n'],
function (Aloha, $, Utils, TableCell, Dialog, i18n) {
/**
* The TableSelection object is a helper-object
*/
var TableSelection = function (table) {
this.table = table;
};
/**
* Gives the type of the cell-selection
* possible values are "row" or "col"
* also possible value is 'cell', which defines custom cell selections
*/
TableSelection.prototype.selectionType = undefined;
/**
* Holds all currently selected table cells as an array of DOM "td" representations
*/
TableSelection.prototype.selectedCells = new Array();
/**
* Holds all table columnIdx if selectiontype is column
*/
TableSelection.prototype.selectedColumnIdxs = new Array();
/**
* Holds all table rowIds if selectiontype is column
*/
TableSelection.prototype.selectedRowIdxs = new Array();
/**
* Holds the active/disabled state of cell selection mode
*/
TableSelection.prototype.cellSelectionMode = false;
/**
* Gives the position of the base cell of a selection - [row, column]
*/
TableSelection.prototype.baseCellPosition = null;
/**
* Gives the range of last cell selection - [row, column]
*/
TableSelection.prototype.lastSelectionRange = null;
/**
* Marks all cells of the specified column or columns as selected
*
* @return void
*/
TableSelection.prototype.selectColumns = function ( columnsToSelect ) {
this.unselectCells();
var rows = this.table.getRows();
// first row is the selection row (dump it, it's not needed)
rows.shift();
var grid = Utils.makeGrid(rows);
for (var j = 0; j < columnsToSelect.length; j++) {
// check if this column is already selected.
if ( -1 !== $.inArray(columnsToSelect[j], this.selectedColumnIdxs) ) {
continue;
}
this.selectedColumnIdxs.push( columnsToSelect[j] );
for (var i = 0; i < grid.length; i++) {
var cellInfo = grid[i][columnsToSelect[j]];
if ( Utils.containsDomCell(cellInfo) ) {
$(cellInfo.cell).addClass(this.table.get('classCellSelected'));
this.selectedCells.push( cellInfo.cell );
}
}
}
this.selectionType = 'column';
};
/**
* Marks all cells of the specified row or rows as selected
*
* @return void
*/
TableSelection.prototype.selectRows = function ( rowsToSelect ) {
this.unselectCells();
var rows = this.table.getRows();
rowsToSelect.sort( function ( a, b ) { return a - b; } );
for (var i = 0; i < rowsToSelect.length; i++) {
if ( rows[ rowsToSelect[i] ] ) {
// check if this row is already selected.
for ( var z = 0; z < this.selectedRowIdxs.length; z++ ) {
if ( rowsToSelect[i] == this.selectedRowIdxs[z] ) {
return;
}
}
this.selectedRowIdxs.push( rowsToSelect[i] );
// to not select first cell, which is a control cell
for ( var j = 1; j < rows[ rowsToSelect[i] ].cells.length; j++ ) {
this.selectedCells.push( rows[ rowsToSelect[i] ].cells[j] );
// TODO make proper cell selection method
$( rows[ rowsToSelect[i] ].cells[j] ).addClass( this.table.get('classCellSelected') );
}
}
}
this.selectionType = 'row';
};
TableSelection.prototype.selectAll = function () {
var rowIndices = $.map( this.table.getRows(), function ( item, i ) {
return i;
});
//getRows() returns all rows, even the header row which we must not select
rowIndices.shift();
this.selectRows( rowIndices );
};
/**
* To be called when cells of the table were selected
* @see selectRows, selectColumns, selectCellRange
* TODO this should be private
*/
TableSelection.prototype.notifyCellsSelected = function () {
Aloha.trigger( 'aloha-table-selection-changed' );
// the UI feels more consisten when we remove the non-table
// selection when cells are selected
// TODO this code doesn't work right in IE as it causes the table
// scope of the floating menu to be lost. Maybe this can be
// handled by testing for an empty selection in the
// aloha-selection-changed event.
//Aloha.getSelection().removeAllRanges();
};
/**
* To be called when a cell-selection is entirely removed
* @see unselectCells
*/
TableSelection.prototype._notifyCellsUnselected = function () {
Aloha.trigger( 'aloha-table-selection-changed' );
};
/**
* This method return true if all sellected cells are TH cells.
*
* @return boolean
*/
TableSelection.prototype.isHeader = function ( ) {
if ( this.selectedCells.length == 0 ) {
return false;
}
// take 1 column to detect if the header button is pressd
for (var i = 0; i < this.selectedCells.length; i++) {
if ( !this.selectedCells[i] || this.selectedCells[i].nodeName.toLowerCase() != 'th' ) {
return false;
}
}
return true;
}
/**
* This method removes the "selected" class from all selected cells
*
* @return void
*/
TableSelection.prototype.unselectCells = function(){
var rows;
//don't unselect cells if cellSelectionMode is active
if ( this.cellSelectionMode ) {
return;
}
if (this.selectedCells.length > 0) {
rows = this.table.getRows();
for (var i = 0; i < rows.length; i++) {
for ( var j = 1; j < rows[i].cells.length; j++ ) {
// TODO make proper cell selection method
$( rows[i].cells[j] ).removeClass( this.table.get('classCellSelected') );
}
}
this.selectedCells = new Array();
this.selectedColumnIdxs = new Array();
this.selectedRowIdxs = new Array();
//we keep 'cell' as the default selection type instead of
//unsetting the selectionType to avoid an edge-case where a
//click into a cell doesn't trigger a call to
//TableCell.editableFocs (which would set the 'cell'
//selection type) which would result in the FloatingMenu
//losing the table scope.
this.selectionType = 'cell';
this._notifyCellsUnselected();
}
};
/**
* Returns the index of a given cell, in selectedCells
* returns -1 if the given cell is not in selectedCells
* @params cell
* DOMElement
*
* @return integer
*/
TableSelection.prototype.selectionIndex = function(cell){
for(var i = 0; i < this.selectedCells.length; i++){
if(this.selectedCells[i] === cell){
return i;
}
}
return -1;
};
/**
* Given a contour creates a object representing a rectangle.
* This function only gives a useful return value if the given
* contour rectangular.
*
* @param {object} contour
* a rectangular contour
* @return {object}
* an object with the properties top, right, bottom, left,
* representing the rectangular contour.
*/
function getRectFromContour( contour ) {
return {
'top' : contour.top[0],
'right' : contour.right[0] + 1,
'bottom': contour.bottom[0] + 1,
'left' : contour.left[0]
};
}
/**
* Given a grid and contour, determines whether the contour is
* rectangular, and each cell in the rectangle is selected.
*
* @param {array} grid
* a two-dimensional array representing a grid see Utils.makeGrid
* @param {object} contour
* an object reprensenting a contour see Utils.makeContour
* @param {function} isSelected
* a function that determines whether a cell in the given grid
* is selected for merging.
* @return {boolean}
* true if all cells inside the contour are selected and can
* be merged.
*/
function isMergeable(grid, contour, isSelected) {
var mergeable = true;
if ( -1 !== Utils.indexOfAnyBut( contour.top , contour.top[0] )
|| -1 !== Utils.indexOfAnyBut( contour.right , contour.right[0] )
|| -1 !== Utils.indexOfAnyBut( contour.bottom, contour.bottom[0] )
|| -1 !== Utils.indexOfAnyBut( contour.left , contour.left[0] ) ) {
// the outside of the selected area is jagged (not a rectangle)
mergeable = false;
} else {
// the outside of the selected area is a rectangle, but we
// must also ensore that there are no holes in the selection
var rect = getRectFromContour( contour )
Utils.walkGridInsideRect( grid, rect, function ( cellInfo ) {
if ( ! isSelected( cellInfo ) ) {
mergeable = false;
return false;
}
});
}
return mergeable;
}
/**
* This method merges all selected cells
*
* @return void
*/
TableSelection.prototype.mergeCells = function(){
var selectedCells = this.selectedCells;
if ( 0 === selectedCells.length ) {
return;
}
var isSelected = function ( cellInfo ) {
return -1 != $.inArray( cellInfo.cell, selectedCells );
};
var grid = Utils.makeGrid( this.table.getRows() );
var contour = Utils.makeContour( grid, isSelected );
if ( ! isMergeable( grid, contour, isSelected ) ) {
Dialog.alert({
title : i18n.t('Table'),
text : i18n.t('table.mergeCells.notRectangular')
});
return;
}
var selectedRect = getRectFromContour( contour );
var $firstCell = $( grid[ selectedRect.top ][ selectedRect.left ].cell );
var $firstContainer = $( TableCell.getContainer( $firstCell.get( 0 ) ) );
Utils.walkGridInsideRect( grid, selectedRect, function ( cellInfo, x, y ) {
if ( x - cellInfo.spannedX === selectedRect.left
&& y - cellInfo.spannedY === selectedRect.top ) {
return;
}
var cell = cellInfo.cell;
var contents = $( TableCell.getContainer( cell ) ).contents();
// only append the delimiting space if there is some non-whitespace
for ( var i = 0; i < contents.length; i++ ) {
if ( "string" !== typeof contents[i]
|| "" !== $.trim( contents[i] ) ) {
$firstContainer.append( " " );
$firstContainer.append( contents );
break;
}
}
$( cell ).remove();
});
$firstCell.attr({ 'rowspan': selectedRect.bottom - selectedRect.top,
'colspan': selectedRect.right - selectedRect.left });
//select the merged cell
this.selectedCells = [ $firstCell.get( 0 ) ];
//reset flags
this.cellSelectionMode = false;
this.baseCellPosition = null;
this.lastSelectionRange = null;
this.selectionType = 'cell';
Aloha.trigger( 'aloha-table-selection-changed' );
};
/**
* This method splits all selected cells (if they are already have row or column spans)
*
* @return void
*/
TableSelection.prototype.splitCells = function(){
var selection = this;
// split the selected cells or currently active cell
var cells_to_split = this.selectedCells;
if (cells_to_split.length > 0) {
$(cells_to_split).each(function(){
Utils.splitCell(this, function () {
return selection.table.newActiveCell().obj;
});
});
//reset flags
this.cellSelectionMode = false;
this.baseCellPosition = null;
this.lastSelectionRange = null;
this.selectionType = 'cell';
Aloha.trigger( 'aloha-table-selection-changed' );
}
};
/**
* This method checks if the current selection of cells is merge able
*
* @return {boolean}
* true if more than one cell is selected.
*/
TableSelection.prototype.cellsAreMergeable = function() {
var selectedCells = this.selectedCells;
if ( selectedCells.length < 2 ) {
return false;
}
var isSelected = function ( cellInfo ) {
return -1 != $.inArray( cellInfo.cell, selectedCells );
};
var grid = Utils.makeGrid( this.table.getRows() );
var contour = Utils.makeContour( grid, isSelected );
if ( ! isMergeable( grid, contour, isSelected ) ) {
return false;
} else {
return true;
}
};
/**
* This method checks if the current selection of cells is split able
*
* @return {boolean}
* true if more than one cell is selected.
*/
TableSelection.prototype.cellsAreSplitable = function() {
var splitable = 0;
if ( this.selectedCells.length > 0 ) {
$(this.selectedCells).each(function(){
var cell = this;
var colspan = Utils.colspan( cell );
var rowspan = Utils.rowspan( cell );
if ( colspan > 1 || rowspan > 1 ) {
splitable++;
}
});
if ( splitable > 0 ) {
return true;
} else {
return false;
}
} else {
return false;
}
};
return TableSelection;
});