"use strict";
(function($, window, document, undefined) {
var settings;
var systemSettings = {
dataKey : 'cellsSelector',
eventNamespace: 'cellsselector'
};
var isMouseDown = false;//нажата ли левая клавиша мыши
var methods = {
init: function(options){
//console.log('method>init');
var defaultSettings = {
selectableTableClass : 'tcs',//класс добавляемый к таблицам
selectedCellClass : 'tcs-selected',//класс добавляемый к выделенным ячейкам таблицы
selectionEnabled: 'tcs-selection-enabled' //???
};
settings =
(!options) ? defaultSettings
: $.extend(defaultSettings,options);
//todo: set listenets on all document for handle all tables added to html
return this.filter("table").each(initTableIfNeed);
},
destroy: function(){
//console.log('method>destroy');
return this.each(function(){
destroyTable($(this));
//$(window).unbind('.cellsselector');
});
},
selectedCells: function(){
//this - $table
//console.log('selectedCells');
return getSelectedCells(this);
},
/*
getSelectedCells: function(){},
setSelectedCells: function(){},
selectCells: function(){},
selectCell: function(){},
selectAll: function(){},
deselectAll: function(){},
*/
//todo: implement
removeDocumentHtmlChanges: removeDocumentHtmlChanges,
addDocumentHtmlChanges: addDocumentHtmlChanges
};
//----------- INIT
/** Инициализирует таблицу если она неинициализированна */
function initTableIfNeed(index, table)
{
var $table = $(table),
data = $table.data(systemSettings.dataKey);
//плагин еще не проинициализирован
if(!data){
//инициализируем
initTable($table);
$table.data(systemSettings.dataKey,getInitialData());
}
//initTableCellsSelector(el);
//$(this).__proto__.csHandler = new Handler(settings, this, document);
//if(!$(this).csHandler) $(this).__proto__.csHandler = new Handler(settings, this, document);
//else $(this).csHandler.restart();
}
/** Возварщает исходные данные таблицы */
function getInitialData()
{
return {
selFrom: false,
selTo: false,
isHighlighted: undefined,
$currTR: $(),//todo: нужны ли?
$currTH: $(),//todo: нужны ли?
$currCell: $() //todo: нужны ли?
};
}
/** Инициализирует таблицу */
function initTable($table){
//addData()???
addTableHtmlChanges($table);
addEventListeners($table);
function addEventListeners($table)
{
//console.log('addInitialEventListeners');
addInitialSelectionEventListeners($table);
addInitialCopyPasteEventListeners($table);
function addInitialSelectionEventListeners($table)
{
//console.log('addInitialSelectionEventListeners');
$table
.on(getEventNameWithPluginNamespace('mouseover'),onMouseOver)//.mouseover(onMouseOver)
.on(getEventNameWithPluginNamespace('mousedown'),onMouseDown)//.mousedown(onMouseDown)
//.on(getEventNameWithPluginNamespace('dragstart'),onDragStart)
.on(getEventNameWithPluginNamespace('mouseup'),onMouseUp);//.mouseup(onMouseUp);
//клик на документе вне таблицы
$($table[0].ownerDocument).on(getEventNameWithPluginNamespace('click'),onOutTableClick);
//or $table.closest(":root"); - html
//or $('html')
//or $(document)
function onMouseOver(event)
{
//console.log('mouseover table');
var pluginData = getPluginDataByEvent(event);
var $target = pluginData.$target;
//таблица
var $table = pluginData.$table;
//ячейку
var $cell = pluginData.$cell;
if($cell.length==0) return;//событие сработало не для ячейки (самой таблицы или других её элементов)
var data = pluginData.data;
data.$currCell = $cell;
//если клавиша мыши не нажата, значит не выделение ячеек
if (!isMouseDown) return false;
//todo: переделать на глобальный индикатор нажатия кнопки мыши
//скрываем стандартное выделение в таблице
var selectionEnableClass = settings.selectionEnabled;
$table.find('.'+selectionEnableClass).removeClass(selectionEnableClass);
data.selTo = getCoordinates($cell);
setPointCoordinates(data,$cell);
//todo: move to bottom
function coordinateManipulateMagic() {
var i = 1;
while (i > 0) {
i = 0;//wtf???
getTableCells($table).each(function (key, cell) {
data.$elemX = $(cell).offset().left;
data.$elemXwidth = $(cell).offset().left + $(cell).width();
data.$elemY = $(cell).offset().top;
data.$elemYheight = $(cell).offset().top + $(cell).height();
if ((data.$elemX < data.$pointXmin) && (data.$elemXwidth >= data.$pointXmin) && (data.$elemXwidth <= data.$pointXwidth) && (data.$elemYheight <= data.$pointYheight) && (data.$elemYheight > data.$pointYmin)) {
data.$temp = data.$elemX;
if (data.$temp != data.$pointXmin) {
data.$pointXmin = data.$temp;
i = 1;
}
}
if ((data.$elemX >= data.$pointXmin) && (data.$elemX < data.$pointXwidth) && (data.$elemXwidth >= data.$pointXwidth) && (data.$elemYheight <= data.$pointYheight) && (data.$elemYheight > data.$pointYmin)) {
data.$temp = data.$elemXwidth;
if (data.$temp != data.$pointXwidth) {
data.$pointXwidth = data.$temp;
i = 1;
}
}
if ((data.$elemY < data.$pointYmin) && (data.$elemYheight >= data.$pointYmin) && (data.$elemYheight <= data.$pointYheight) && (data.$elemXwidth <= data.$pointXwidth) && (data.$elemXwidth > data.$pointXmin)) {
data.$temp = data.$elemY;
if (data.$temp != data.$pointYmin) {
data.$pointYmin = data.$temp;
i = 1;
}
}
if ((data.$elemY >= data.$pointYmin) && (data.$elemY < data.$pointYheight) && (data.$elemYheight >= data.$pointYheight) && (data.$elemXwidth <= data.$pointXwidth) && (data.$elemXwidth > data.$pointXmin)) {
data.$temp = data.$elemYheight;
if (data.$temp != data.$pointYheight) {
data.$pointYheight = data.$temp;
i = 1;
}
}
if ((data.$elemX < data.$pointXmin) && (data.$elemXwidth >= data.$pointXwidth) && (data.$elemYheight <= data.$pointYheight) && (data.$elemYheight > data.$pointYmin)) {
data.$temp = data.$elemX;
if (data.$temp < data.$pointXmin) {
data.$pointXmin = data.$temp;
i = 1;
}
}
if ((data.$elemX > data.$pointXmin) && (data.$elemX < data.$pointXwidth) && (data.$elemY < data.$pointYheight) && (data.$elemY > data.$pointYmin)) {
data.$temp = data.$elemXwidth;
if (data.$temp > data.$pointXwidth) {
data.$pointXwidth = data.$temp;
i = 1;
}
}
if ((data.$elemY < data.$pointYmin) && (data.$elemYheight >= data.$pointYmin) && (data.$elemYheight <= data.$pointYheight) && (data.$elemXwidth <= data.$pointXwidth) && (data.$elemXwidth > data.$pointXmin)) {
data.$temp = data.$elemY;
if (data.$temp < data.$pointYmin) {
data.$pointYmin = data.$temp;
i = 1;
}
}
if ((data.$elemY > data.$pointYmin) && (data.$elemYheight <= data.$pointYheight) && (data.$elemX <= data.$pointXwidth)) {
data.$temp = data.$elemYheight;
if (data.$temp > data.$pointYheight) {
data.$pointYheight = data.$temp;
i = 1;
}
}
});
}
}
coordinateManipulateMagic();
selectCells($table,data);
$table.data(data);
return true;
function setPointCoordinates(data, $cell) {
data.$pointX2 = $cell.offset().left;
data.$pointX2width = $cell.offset().left + $cell.width();
data.$pointY2 = $cell.offset().top;
data.$pointY2height = $cell.offset().top + $cell.height();
if (data.$pointX1 < data.$pointX2) {
data.$pointXmin = data.$pointX1;
data.$pointXmax = data.$pointX2;
} else {
data.$pointXmin = data.$pointX2;
data.$pointXmax = data.$pointX1;
}
if (data.$pointX1width > data.$pointX2width) {
data.$pointXwidth = data.$pointX1width;
} else {
data.$pointXwidth = data.$pointX2width;
}
if (data.$pointY1 < data.$pointY2) {
data.$pointYmin = data.$pointY1;
data.$pointYmax = data.$pointY2;
} else {
data.$pointYmin = data.$pointY2;
data.$pointYmax = data.$pointY1;
}
if (data.$pointY1height > data.$pointY2height) {
data.$pointYheight = data.$pointY1height;
} else {
data.$pointYheight = data.$pointY2height;
}
}
}
function onMouseDown(event){
//console.log('mousedown table');
var pluginData = getPluginDataByEvent(event);
//если клик правой кнопкой мыши - ничего не делаем
if (isRightMouseButton(event)) return true;
//таблица
var $table = pluginData.$table;
//получаем ячейку
var $cell = pluginData.$cell;
//self.$currCell = $cell;
if($cell.length==0) return;//событие сработало не для ячейки (самой таблицы или других её элементов)
//event.stopPropagation();//надо ли?
//event.stopImmediatePropagation();//тем более это?
$cell.addClass(settings.selectionEnabled);
var data = pluginData.data;
isMouseDown = true;
data.selFrom = getCoordinates($cell);
data.selFrom.$el = $cell;
var selectedCellClass = settings.selectedCellClass;
if ($cell.hasClass(selectedCellClass) && deselectAll($table) === 1) {
$cell.removeClass(selectedCellClass);
} else {
deselectAll($table);
$cell.addClass(selectedCellClass);
}
data.$pointX1 = $cell.offset().left;
data.$pointX1width = $cell.offset().left+$cell.width();
data.$pointY1 = $cell.offset().top;
data.$pointY1height = $cell.offset().top+$cell.height();
data.isHighlighted = $cell.hasClass(selectedCellClass);
$table.data(data);
return true;
}
/*function onDragStart(event)
{
//console.log('dragstart table');
//event.preventDefault();//todo: надо ли?
return true;
}*/
function onMouseUp(event){
//console.log('mouseup table');
var pluginData = getPluginDataByEvent(event);
var data = pluginData.data;
isMouseDown = false;
data.selFrom = false;
data.selTo = false;
pluginData.$table.data(data);
}
//клик на документе вне таблицы
function onOutTableClick(event){
//console.log('click (out of table)');
isMouseDown = false;
if($(event.target).closest($table).length==0) deselectAll($table);
}
}
function addInitialCopyPasteEventListeners($table)
{
//console.log('addInitialCopyPasteEventListeners');
//this.$table.off('copy');
$table.on('copy',onCopy);
function onCopy(e){
e.originalEvent.clipboardData.setData('text/plain', handler.getSelectedDataAsText());
e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard
//var handler = $(e.target).csHandler;
//e.originalEvent.clipboardData.setData('text/csv', handler.getSelectedDataAsText());
//e.originalEvent.clipboardData.setData('Text', handler.getSelectedDataAsText());
//e.originalEvent.clipboardData.setData('text/html', 'Hello, world!text/html');
//e.originalEvent.clipboardData.setData('text/html', handler.getSelectedDataAsHtml());
//console.log(e.originalEvent.clipboardData.types);
//console.log('copy:'+ e.target.innerHTML);
//console.log('copy');
//console.log(e);
}
//-------- Copy/Paste event-listeners
/*$table.on('beforecopy', function(e){
//console.log('beforecopy');
/*if(weHaveDataToCopy()){ // use your web app's internal logic to determine if something can be copied
e.preventDefault(); // enable copy UI and events
}
});*/
/* table.addEventListener('copy', function(e){
//var handler = e.target.csHandler;
//var handler = $(e.target).csHandler;
//e.clipboardData.setData('text/plain', handler.getSelectedDataAsText());
//e.clipboardData.setData('text/html', handler.getSelectedDataAsHtml());
//e.clipboardData.setData('text/plain', 'getSelectedDataAsText');
//e.clipboardData.setData('text/html', 'getSelectedDataAsHtml');
//console.log('copy:'+ e.target.innerHTML);
console.log('copy');
console.log(e);
//e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard
});
*/
//this.$table.on('paste',onPaste);
/*$table.on('paste', function(e){
//console.log('paste-Listener');
//console.log(e);
/*if(e.clipboardData.types.indexOf('text/html') > -1){
processDataFromClipboard(e.clipboardData.getData('text/html'));
e.preventDefault(); // We are already handling the data from the clipboard, we do not want it inserted into the document
}
});*/
/*function onPaste(e){
//console.log('paste-onPaste');
//console.log(e);
}*/
}
}
}
//----------- DESTROY
function destroyTable($table)
{
//console.log('destroyTable');
removeEventListeners($table);
removeData($table);
removeTableHtmlChanges($table);
function removeEventListeners($table)
{
$table.unbind('.'+systemSettings.eventNamespace);
}
function removeData($table){
$table.removeData(systemSettings.dataKey);
}
}
$.fn.tableCellsSelection = function (method) {
if(methods[method]) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Метод с именем ' + method + ' не существует для jQuery.tooltip' );
}
};
//**************** Table/Cells funcs
function removeDocumentHtmlChanges($document){
var $tables = $document.find('table');
removeTableHtmlChanges($tables);
}
function addDocumentHtmlChanges($document){
var $tables = $document.find('table');
addTableHtmlChanges($tables);
}
/**
* Удаляет внесенные изменения в html-коде (в данный момент удалет служебные классы)
* @param $table - отдельная таблица или весь документ
*/
//todo: разделить на ф-цию чистки всего документа и таблиц.
//todo: В документе удалять используемые классы только у таблиц.
function removeTableHtmlChanges($table){
$table.removeClass(settings.selectableTableClass);
removeChildrensClass($table,settings.selectedCellClass);
removeChildrensClass($table,settings.selectionEnabled);
function removeChildrensClass($table,className){
$table.find('.'+className).removeClass(className);
}
}
/**
* Добавляет изменения в html-код
* в соответствии с текущим состоянием таблицы.
* На данный момент добаляет служебные классы к таблице и её ячейкам.
* @param $table
*/
function addTableHtmlChanges($table){
$table.addClass(settings.selectableTableClass);
//todo: добавить подсветку выделенных ячеек
/*
$selectedCells = getSelectedCells();
$selectedCells.adddClass(settings.selectedCellClass);
$selectedCells.adddClass(settings.selectionEnabled);
*/
}
function getSelectedDataAsText($table) {
//console.log('getSelectedDataAsText');
var selectedCells = getSelectedCells($table);//выделенные ячейки
var selectedRows = selectedCells.closest('tr');//строки с выделенными ячейками
var ret='';
//для каждой строки получаем значения ячеек
for(var i=0;igetSelectedDataAsHtml';
//if (allTables) return this.$doc.find('.cell-active');
//return this.$table.find('.cell-active');
};*/
//todo: нужно ли?
/*
function restart($table) {
this.$table.addClass(settings.selectableTableClass);
this.allCells() = this.$table.find('th, td');
this.init();
};*/
/*function getCell($table, x, y) {
if (y == 1) return $table.find('tr[data_y="1"] > th[data_x="'+x+'"]');
return this.$table.find('tr[data_y="'+y+'"] > td[data_x="'+x+'"]');
}*/
function isSelectedCell($cell) {
return $cell.hasClass(settings.selectedCellClass);
}
/*function selectCell($table,$cell) {
//todo: implement
if (isSelectedCell($cell)) return deselectCell($cell);
deselectAll($table);
if ($cell.is('td, th')) $cell.addClass(settings.selectedCellClass);
}*/
function selectCells($table, data) {
deselectAll($table);
getTableCells($table).each(function (key, cell) {
data.$elemX = $(cell).offset().left;
data.$elemXwidth = $(cell).offset().left + $(cell).width();
data.$elemY = $(cell).offset().top;
data.$elemXheight = $(cell).offset().top + $(cell).height();
if ((data.$elemX >= data.$pointXmin) && (data.$elemXwidth <= data.$pointXwidth)
&& (data.$elemXwidth <= data.$pointXwidth) && (data.$elemY >= data.$pointYmin)
&& (data.$elemY <= data.$pointYheight) && (data.$elemYheight >= data.$pointYheight)) {
$(cell).addClass(settings.selectedCellClass);
}
});
$table.trigger('selectionchange.'+ systemSettings.eventNamespace);
}
//todo: удалить параметр allTables
function getSelectedCells($table) {
return $table.find('.'+settings.selectedCellClass);
}
function getCoordinates($cell){
return {
x: parseInt($cell.attr('data_x')),//todo: заменить на DOM-свойства вроде colNumber
y: parseInt($cell.parent('tr').attr('data_y')),
colspan: $cell.attr('colspan') ? parseInt($cell.attr('colspan')) : 0,
rowspan: $cell.attr('rowspan') ? parseInt($cell.attr('rowspan')) : 0
};
}
function getTableCells($table){ return $table.find('th, td'); }
function deselectAll($table) {
var selectedCells = getSelectedCells($table);//TODO:исправить на локальные изменения только
var length = 0;
/*selectedCells.each(function(i, cell) {
length++;
$(cell).removeClass(settings.selectedCellClass);
});*/
//or
selectedCells.removeClass(settings.selectedCellClass);
return length;
}
//**************** PLUGIN
function getEventNameWithPluginNamespace(event){return event + '.' + systemSettings.eventNamespace}
function isRightMouseButton(event) {
var isRightMB;
event = event || window.event;
if ("which" in event)
isRightMB = event.which == 3;
else if ("button" in event)
isRightMB = event.button == 2;
return isRightMB;
}
function getPluginDataByEvent(event){
var $target = $(event.target);
var $table = $target.closest('table');
return {
$target: $target,
$table: $table,
$cell: $target.closest('td,th'),
data: $table.data(systemSettings.dataKey)
}
}
})(jQuery, window, document);