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-plugin.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

1567 lines
46 KiB
JavaScript
Executable File

/* table-plugin.js is part of Aloha Editor project http://aloha-editor.org
*
* Aloha Editor is a WYSIWYG HTML5 inline editing library and editor.
* Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
* Contributors http://aloha-editor.org/contribution.php
*
* Aloha Editor is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* Aloha Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As an additional permission to the GNU GPL version 2, you may distribute
* non-source (e.g., minimized or compacted) forms of the Aloha-Editor
* source code without the copy of the GNU GPL normally required,
* provided you include this license notice and a URL through which
* recipients can access the Corresponding Source.
*/
define([
'aloha',
'jquery',
'aloha/plugin',
'aloha/pluginmanager',
'ui/ui',
'ui/scopes',
'ui/button',
'ui/toggleButton',
'ui/dialog',
'ui/port-helper-attribute-field',
'ui/port-helper-multi-split',
'i18n!table/nls/i18n',
'i18n!aloha/nls/i18n',
'table/table-create-layer',
'table/table',
'table/table-plugin-utils',
'util/dom',
'aloha/console'
], function(
Aloha,
jQuery,
Plugin,
PluginManager,
Ui,
Scopes,
Button,
ToggleButton,
Dialog,
AttributeField,
MultiSplitButton,
i18n,
i18nCore,
CreateLayer,
Table,
Utils,
Dom,
Console
) {
var $ = jQuery;
var GENTICS = window.GENTICS;
/**
* Register the TablePlugin as Aloha.Plugin
*/
var TablePlugin = new Plugin('table');
/**
* The Create-Layer Object of the TablePlugin
*
* @see Table.CreateLayer
*/
TablePlugin.createLayer = undefined;
/**
* default button configuration
*/
TablePlugin.config = [ 'table' ];
/**
* An Array which holds all newly created tables contains DOM-Nodes of
* table-objects
*/
TablePlugin.TableRegistry = new Array();
/**
* Holds the active table-object
*/
TablePlugin.activeTable = undefined;
/**
* parameters-objects for tables
*
* @param className
* The class of activated tables
*/
TablePlugin.parameters = {
className : 'aloha-table', // class of editable tables
classSelectionRow : 'aloha-table-selectcolumn', // class for the upper table-row to select columns
classSelectionColumn : 'aloha-table-selectrow', // class for the left bound table-cells to select rows
classLeftUpperCorner : 'aloha-table-leftuppercorner', // class for the left upper corner cell
classTableWrapper : 'aloha-table-wrapper', // class of the outest table-wrapping div
classCellSelected : 'aloha-cell-selected', // class of cell which are selected (row/column selection)
waiRed : 'aloha-wai-red', // class that shows wai of div
waiGreen : 'aloha-wai-green', // class that shows wai of div
selectionArea : 10 // width/height of the selection rows (in pixel)
};
/**
* @hide
* {name:'green', text:'Green',tooltip:'Green',iconClass:'GENTICS_table GENTICS_button_green',cssClass:'green'}
*/
TablePlugin.checkConfig = function (c){
if (typeof c == 'object' && c.length) {
var newC = [];
for (var i = 0; i < c.length; i++) {
if (c[i]) {
newC.push({
name : c[i].name,
text : c[i].text ? c[i].text : c[i].name,
tooltip : c[i].tooltip ? c[i].tooltip : c[i].text,
iconClass : c[i].iconClass ? c[i].iconClass : 'aloha-icon-' + c[i].name,
cssClass : c[i].cssClass ? c[i].cssClass : c[i].name
});
}
}
c = newC;
} else {
c = [];
}
return c;
};
/**
* Checks whether the given DOM element is nested within a table.
*
* @param {jQuery.<HTMLElement>} $element
* @return {boolean} True if the given element is nested in a table.
*/
function isWithinTable($element) {
return 0 < $element.parents('.aloha-editable table').length;
}
/**
* Checks whether the given DOM element is nested within an aloha block.
*
* @param {jQuery.<HTMLElement>} $element
* @return {boolean} True if the given element is nested in an aloha block.
*/
function isWithinBlock($element) {
return 0 < $element.closest('.aloha-block').length;
}
/**
* Checks whether the table is an editable element.
*
* @return {boolean} True if the table's parent element is contentEditable;
* false otherwise.
*/
function isEditableTable(table) {
return GENTICS.Utils.Dom.isEditable(table);
}
/**
* Checks for the presence of nested tables in the given element.
*
* @param {jQuery.<HTMLElement>} $element jQuery unit set containing a DOM
* element.
* @return {boolean} True if nested tables were detected; false otherwise.
*/
function checkForNestedTables($element) {
var selector = $element.is('table') ? 'table' : 'table table';
if ($element.find(selector).length) {
Console.warn('Table Plugin',
'Nested tables found. They will not be initialized.');
return true;
}
return false;
}
/**
* Creates a table, if it is allowed, and registers a new Table object for
* the given table DOM element.
*
* If the table's editable parent is activated, the table will also
* automatically be activated.
*
* @param {HTMLElement} element HTML table element.
* @return {Table|null} The created Table object or null if it was not
* allowed to create the table.
*/
function createNewTable(element) {
var $table = $(element);
var create = isEditableTable(element)
&& !isWithinTable($table)
&& !isWithinBlock($table);
if (create) {
var table = new Table(element, TablePlugin);
var $host = $(Dom.getEditingHostOf(element));
table.parentEditable = Aloha.getEditableById($host.attr('id'));
TablePlugin.TableRegistry.push(table);
checkForNestedTables($table);
if (Aloha.activeEditable === table.parentEditable) {
table.activate();
}
return table;
}
return null;
}
/**
* Init method of the Table-plugin transforms all tables in the document
*
* @return void
*/
TablePlugin.init = function() {
var that = this,
isEnabled = {};
// apply settings
this.tableConfig = this.checkConfig(this.tableConfig||this.settings.tableConfig);
this.columnConfig = this.checkConfig(this.columnConfig||this.settings.columnConfig);
this.rowConfig = this.checkConfig(this.rowConfig||this.settings.rowConfig);
this.cellConfig = this.checkConfig(this.cellConfig||this.settings.cellConfig);
// table resize settings
this.tableResize = this.settings.tableResize === undefined ? false : this.settings.tableResize;
this.colResize = this.settings.colResize === undefined ? false : this.settings.colResize;
this.rowResize = this.settings.rowResize === undefined ? false : this.settings.rowResize;
// disable table resize settings on browsers below IE8
if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) < 8) {
this.tableResize = false;
this.colResize = false;
this.rowResize = false;
}
// add reference to the create layer object
this.createLayer = new CreateLayer( this );
// subscribe for the 'editableActivated' event to activate all tables in the editable
Aloha.bind( 'aloha-editable-created', function (event, editable) {
var config = that.getEditableConfig(editable.obj);
isEnabled[editable.getId()] = (-1 !== jQuery.inArray('table', config));
// add a mousedown event to all created editables to check if focus leaves a table
editable.obj.bind( 'mousedown', function ( jqEvent ) {
TablePlugin.setFocusedTable( undefined );
} );
editable.obj.find('table').each(function (__unused__, elem) {
createNewTable(elem);
});
} );
// initialize the table buttons
this.initTableButtons();
Aloha.bind( 'aloha-table-selection-changed', function () {
// check if selected cells are split/merge able and set button status
if ( typeof TablePlugin.activeTable !== 'undefined' &&
TablePlugin.activeTable.selection ) {
TablePlugin.updateFloatingMenuScope();
if ( TablePlugin.activeTable.selection.cellsAreSplitable() ) {
that._splitcellsButton.enable(true);
that._splitcellsRowButton.enable(true);
that._splitcellsColumnButton.enable(true);
} else {
that._splitcellsButton.enable(false);
that._splitcellsRowButton.enable(false);
that._splitcellsColumnButton.enable(false);
}
if ( TablePlugin.activeTable.selection.cellsAreMergeable() ) {
that._mergecellsButton.enable(true);
that._mergecellsRowButton.enable(true);
that._mergecellsColumnButton.enable(true);
} else {
that._mergecellsButton.enable(false);
that._mergecellsRowButton.enable(false);
that._mergecellsColumnButton.enable(false);
}
}
});
Aloha.bind( 'aloha-selection-changed', function (event, rangeObject) {
// this case probably occurs when the selection is empty?
if (!rangeObject.startContainer || !Aloha.activeEditable) {
return;
}
// show hide buttons regarding configuration and DOM position
if (isEnabled[Aloha.activeEditable.getId()] && Aloha.Selection.mayInsertTag('table') ) {
that._createTableButton.show();
} else {
that._createTableButton.hide();
}
if (!that.activeTable) {
return;
}
// check wheater we are inside a table
var table = rangeObject.findMarkup(function() {
return this.nodeName === 'TABLE';
}, Aloha.activeEditable.obj);
if (table) {
TablePlugin.updateFloatingMenuScope();
TablePlugin.setActiveCellStyle();
} else {
that.activeTable.selection.cellSelectionMode = false;
that.activeTable.selection.baseCellPosition = null;
that.activeTable.selection.lastSelectionRange = null;
that.activeTable.focusOut();
}
});
Aloha.bind('aloha-editable-activated', function (__event__, data) {
that._splitcellsButton.enable(false);
that._mergecellsButton.enable(false);
that._splitcellsRowButton.enable(false);
that._mergecellsRowButton.enable(false);
that._splitcellsColumnButton.enable(false);
that._mergecellsColumnButton.enable(false);
data.editable.obj.find('table').each(function () {
var registry = TablePlugin.TableRegistry;
for (var i = 0; i < registry.length; i++) {
if (registry[i].obj.attr('id') === jQuery(this).attr('id')) {
registry[i].activate();
return true;
}
}
// Because table this is a new table that is not yet in the
// registry.
createNewTable(this);
});
});
Aloha.bind('aloha-editable-deactivated', function () {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.unselectCells();
}
TablePlugin.setFocusedTable(undefined);
var registry = TablePlugin.TableRegistry;
for (var i = 0; i < registry.length; i++) {
registry[i].deactivate();
}
});
Aloha.bind('aloha-smart-content-changed', function () {
if (Aloha.activeEditable) {
Aloha.activeEditable.obj.find('table').each(function () {
if (TablePlugin.indexOfTableInRegistry(this) == -1) {
if (createNewTable(this)) {
this.id = GENTICS.Utils.guid();
}
}
});
}
});
if (this.settings.summaryinsidebar) {
Aloha.bind('aloha-plugins-loaded', function () {
that.initSidebar(Aloha.Sidebar.right.show());
});
}
};
//namespace prefix for this plugin
var tableNamespace = 'aloha-table';
function nsSel () {
var stringBuilder = [], prefix = tableNamespace;
jQuery.each(arguments, function () { stringBuilder.push('.' + (this == '' ? prefix : prefix + '-' + this)); });
return jQuery.trim(stringBuilder.join(' '));
};
//Creates string with this component's namepsace prefixed the each classname
function nsClass () {
var stringBuilder = [], prefix = tableNamespace;
jQuery.each(arguments, function () { stringBuilder.push(this == '' ? prefix : prefix + '-' + this); });
return jQuery.trim(stringBuilder.join(' '));
};
TablePlugin.initSidebar = function(sidebar) {
var pl = this;
pl.sidebar = sidebar;
pl.sidebarPanel = sidebar.addPanel({
id : nsClass('sidebar-panel'),
title : i18n.t('table.sidebar.title'),
content : '',
expanded : true,
activeOn : 'table',
onInit : function () {
var that = this,
content = this.setContent(
'<label class="' + nsClass('label') + '" for="' + nsClass('textarea') + '" >' + i18n.t('table.label.target') + '</label>' +
'<textarea id="' + nsClass('textarea') + '" class="' + nsClass('textarea') + '" />').content;
jQuery(nsSel('textarea')).live('keyup', function() {
//The original developer thought that escaping the
//quote characters of the textarea value are
//necessary to work around a bug in IE. I could not
//reproduce the bug, so I commented the following
//out.
//.replace("\"", '&quot;').replace("'", "&#39;")
jQuery(that.effective).attr('summary', jQuery(nsSel('textarea')).val());
var waiDiv = jQuery('div[class*="wai"]', 'table#' + jQuery(that.effective).attr('id'));
waiDiv.removeClass(pl.get('waiGreen'));
waiDiv.removeClass(pl.get('waiRed'));
if (jQuery(nsSel('textarea')).val().trim() != '') {
waiDiv.addClass(pl.get('waiGreen'));
} else {
waiDiv.addClass(pl.get('waiRed'));
}
});
},
onActivate: function (effective) {
var that = this;
that.effective = effective;
jQuery(nsSel('textarea')).val(jQuery(that.effective).attr('summary'));
}
});
sidebar.show();
};
/**
* test if the table is editable
* @return boolean true if the table's parent element is contentEditable, false otherwise
*/
TablePlugin.isEditableTable = function (table) {
return GENTICS.Utils.Dom.isEditable( table );
};
/**
* @param {DOMElement} table
* @return {Number}
*/
TablePlugin.indexOfTableInRegistry = function ( table ) {
var registry = this.TableRegistry;
for ( var i = 0; i < registry.length; i++ ) {
// We need to find exactly the same object from the
// registry since we could also deal with cloned objects
if ( registry[ i ].obj[ 0 ].id == table.id ) {
return i;
}
}
return -1;
};
/**
* @param {DOMElement} table
* @return {Table}
*/
TablePlugin.getTableFromRegistry = function ( table ) {
var i = this.indexOfTableInRegistry( table );
if ( i > -1 ) {
return this.TableRegistry[ i ];
}
return null;
};
/**
* Checks whether the current selection is inside a table within an
* editable
*
* @return {Boolean} true if we are inside a table
*/
TablePlugin.isSelectionInTable = function () {
var range = Aloha.Selection.getRangeObject();
var container = jQuery( range.commonAncestorContainer );
if ( container.length == 0 ) {
return false;
}
if ( container.parents( '.aloha-editable table' ).length ) {
return true;
}
return false;
};
TablePlugin.preventNestedTables = function () {
if ( this.isSelectionInTable() ) {
Dialog.alert({
title : i18n.t( 'Table' ),
text : i18n.t( 'table.createTable.nestedTablesNoSupported' )
});
return true;
}
return false;
};
TablePlugin.initMergeSplitCellsBtns = function(){
// TODO current it is not possible to add the same buttons to
// multiple tabs. To work around this limitation we are
// defining the mergecells and splitcells components
// multiple times, once for each tab.
this._mergecellsButton = Ui.adopt("mergecells", Button, {
tooltip: i18n.t("button.mergecells.tooltip"),
icon: "aloha-icon aloha-icon-mergecells",
scope: this.name + '.cell',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.mergeCells();
}
}
});
this._splitcellsButton = Ui.adopt("splitcells", Button, {
tooltip: i18n.t("button.splitcells.tooltip"),
icon: "aloha-icon aloha-icon-splitcells",
scope: this.name + '.cell',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.splitCells();
}
}
});
this._mergecellsRowButton = Ui.adopt("mergecellsRow", Button, {
tooltip: i18n.t("button.mergecells.tooltip"),
icon: "aloha-icon aloha-icon-mergecells",
scope: this.name + '.row',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.mergeCells();
}
}
});
this._splitcellsRowButton = Ui.adopt("splitcellsRow", Button, {
tooltip: i18n.t("button.splitcells.tooltip"),
icon: "aloha-icon aloha-icon-splitcells",
scope: this.name + '.row',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.splitCells();
}
}
});
this._mergecellsColumnButton = Ui.adopt("mergecellsColumn", Button, {
tooltip: i18n.t("button.mergecells.tooltip"),
icon: "aloha-icon aloha-icon-mergecells",
scope: this.name + '.column',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.mergeCells();
}
}
});
this._splitcellsColumnButton = Ui.adopt("splitcellsColumn", Button, {
tooltip: i18n.t("button.splitcells.tooltip"),
icon: "aloha-icon aloha-icon-splitcells",
scope: this.name + '.column',
click: function() {
if (TablePlugin.activeTable) {
TablePlugin.activeTable.selection.splitCells();
}
}
});
};
TablePlugin.initNaturalFitBtn = function() {
var that = this;
if (this.colResize || this.rowResize) {
this._tableNaturalFitButton = Ui.adopt("naturalFit", Button, {
tooltip: i18n.t("button.naturalfit.tooltip"),
icon: "aloha-icon aloha-icon-table-naturalfit",
scope: this.name + '.cell',
click: function() {
if (that.activeTable) {
var tableObj = that.activeTable.obj;
tableObj.find('td').each(function() {
jQuery(this).find('div').css('width', '');
jQuery(this).css('width', '');
});
tableObj.find('tr').each(function() {
jQuery(this).css('height', '');
});
}
}
});
}
};
/**
* Adds default row buttons, and custom formatting buttons to floating menu
*/
TablePlugin.initRowsBtns = function () {
var that = this;
this._addrowbeforeButton = Ui.adopt("addrowbefore", Button, {
tooltip: i18n.t( "button.addrowbefore.tooltip"),
icon: "aloha-icon aloha-icon-addrowbefore",
scope: this.name + '.row',
click: function() {
if (that.activeTable) {
that.activeTable.addRowBeforeSelection();
}
}
});
this._addrowafterButton = Ui.adopt("addrowafter", Button, {
tooltip: i18n.t("button.addrowafter.tooltip"),
icon: "aloha-icon aloha-icon-addrowafter",
scope: this.name + '.row',
click: function() {
if (that.activeTable) {
that.activeTable.addRowAfterSelection();
}
}
});
this._deleterowsButton = Ui.adopt("deleterows", Button, {
tooltip: i18n.t("button.delrows.tooltip"),
icon: "aloha-icon aloha-icon-deleterows",
scope: this.name + '.row',
click: function() {
if (that.activeTable) {
var aTable = that.activeTable;
Dialog.confirm({
title: i18n.t('Table'),
text: i18n.t('deleterows.confirm'),
yes: function(){
aTable.deleteRows();
}
});
}
}
});
this._rowheaderButton = Ui.adopt("rowheader", ToggleButton, {
tooltip: i18n.t("button.rowheader.tooltip"),
icon: "aloha-icon aloha-icon-rowheader",
scope: this.name + '.row',
click: function() {
if (that.activeTable) {
var selectedRowIdxs = that.activeTable.selection.selectedRowIdxs,
cell,
isHeader = that.activeTable.selection.isHeader(),
allHeaders = true; // flag for header check
// loop through selected cells, determine if any are not already headers
for (var j = 0; j < that.activeTable.selection.selectedCells.length; j++) {
cell = that.activeTable.selection.selectedCells[j];
if ( !isHeader ) {
allHeaders = false;
break;
}
}
// updated selected cells
for (var j = 0; j < that.activeTable.selection.selectedCells.length; j++) {
cell = that.activeTable.selection.selectedCells[j];
if ( allHeaders ) {
cell = Aloha.Markup.transformDomObject( cell, 'td' ).removeAttr( 'scope' ).get(0);
} else {
cell = Aloha.Markup.transformDomObject( cell, 'th' ).attr( 'scope', 'row' ).get(0);
}
jQuery( that.activeTable.selection.selectedCells[j] ).bind( 'mousedown', function ( jqEvent ) {
var wrapper = jQuery(this).children('div').eq(0);
// lovely IE ;-)
window.setTimeout(function () {
wrapper.trigger( 'focus' );
}, 1);
// unselect cells
});
}
// select the row
that.activeTable.refresh();
that.activeTable.selection.unselectCells();
that.activeTable.selection.selectRows( selectedRowIdxs );
}
}
});
// generate formatting buttons
this.rowMSItems = [];
jQuery.each(this.rowConfig, function (j, itemConf) {
that.rowMSItems.push({
name: itemConf.name,
text: i18n.t(itemConf.text),
tooltip: i18n.t(itemConf.tooltip),
iconClass: 'aloha-icon aloha-row-layout ' + itemConf.iconClass,
click: function () {
if (that.activeTable) {
var sc = that.activeTable.selection.selectedCells;
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
if ( jQuery(sc[i]).attr('class').indexOf(itemConf.cssClass) > -1 ) {
jQuery(sc[i]).removeClass(itemConf.cssClass);
} else {
jQuery(sc[i]).addClass(itemConf.cssClass);
// remove all row formattings
for (var f = 0; f < that.rowConfig.length; f++) {
if (that.rowConfig[f].cssClass != itemConf.cssClass) {
jQuery(sc[i]).removeClass(that.rowConfig[f].cssClass);
}
}
}
}
// selection could have changed.
that.activeTable.selectRows();
}
}
});
});
if (this.rowMSItems.length > 0) {
this.rowMSItems.push({
name : 'removeFormat',
text : i18n.t('button.removeFormat.text'),
tooltip : i18n.t('button.removeFormat.tooltip'),
'cls' : 'aloha-ui-multisplit-fullwidth',
wide : true,
click : function () {
if (that.activeTable) {
var sc = that.activeTable.selection.selectedCells;
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
for (var f = 0; f < that.rowConfig.length; f++) {
jQuery(sc[i]).removeClass(that.rowConfig[f].cssClass);
}
}
// selection could have changed.
that.activeTable.selectRows();
}
}
});
}
this.rowMSButton = MultiSplitButton({
items: this.rowMSItems,
name: 'formatRow',
hideIfEmpty: true,
scope: this.name + '.row'
});
};
/**
* Adds default column buttons, and custom formatting buttons to floating menu
*/
TablePlugin.initColumnBtns = function () {
var that = this;
this._addcolumnleftButton = Ui.adopt("addcolumnleft", Button, {
tooltip: i18n.t("button.addcolleft.tooltip"),
icon: "aloha-icon aloha-icon-addcolumnleft",
scope: this.name + '.column',
click: function() {
if (that.activeTable) {
that.activeTable.addColumnsLeft();
}
}
});
this._addcolumnrightButton = Ui.adopt("addcolumnright", Button, {
tooltip: i18n.t("button.addcolright.tooltip"),
icon: "aloha-icon aloha-icon-addcolumnright",
scope: this.name + '.column',
click: function() {
if (that.activeTable) {
that.activeTable.addColumnsRight();
}
}
});
this._deletecolumnsButton = Ui.adopt("deletecolumns", Button, {
tooltip: i18n.t("button.delcols.tooltip"),
icon: "aloha-icon aloha-icon-deletecolumns",
scope: this.name + '.column',
click: function() {
if (that.activeTable) {
var aTable = that.activeTable;
Dialog.confirm({
title: i18n.t('Table'),
text: i18n.t('deletecolumns.confirm'),
yes: function(){
aTable.deleteColumns();
}
});
}
}
});
this._columnheaderButton = Ui.adopt("columnheader", ToggleButton, {
tooltip: i18n.t("button.columnheader.tooltip"),
icon: "aloha-icon aloha-icon-columnheader",
scope: this.name + '.column',
click: function() {
if (that.activeTable) {
var
selectedColumnIdxs = that.activeTable.selection.selectedColumnIdxs,
cell,
isHeader = that.activeTable.selection.isHeader(),
allHeaders = true; // flag for header check
// loop through selected cells, determine if any are not already headers
for (var j = 0; j < that.activeTable.selection.selectedCells.length; j++) {
cell = that.activeTable.selection.selectedCells[j];
if ( !isHeader ) {
allHeaders = false;
break;
}
}
// updated selected cells
for (var j = 0; j < that.activeTable.selection.selectedCells.length; j++) {
cell = that.activeTable.selection.selectedCells[j];
if ( allHeaders ) {
cell = Aloha.Markup.transformDomObject( cell, 'td' ).removeAttr( 'scope' ).get(0);
} else {
cell = Aloha.Markup.transformDomObject( cell, 'th' ).attr( 'scope', 'row' ).get(0);
}
jQuery( that.activeTable.selection.selectedCells[j] ).bind( 'mousedown', function ( jqEvent ) {
var wrapper = jQuery(this).children('div').eq(0);
// lovely IE ;-)
window.setTimeout(function () {
wrapper.trigger( 'focus' );
}, 1);
// unselect cells
});
}
// select the column
that.activeTable.refresh();
that.activeTable.selection.unselectCells();
that.activeTable.selection.selectColumns( selectedColumnIdxs );
}
}
});
// generate formatting buttons
this.columnMSItems = [];
jQuery.each(this.columnConfig, function (j, itemConf) {
var item = {
name : itemConf.name,
text : i18n.t(itemConf.text),
tooltip : i18n.t(itemConf.tooltip),
iconClass : 'aloha-icon aloha-column-layout ' + itemConf.iconClass,
click : function (x,y,z) {
if (that.activeTable) {
var sc = that.activeTable.selection.selectedCells;
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
if ( jQuery(sc[i]).attr('class').indexOf(itemConf.cssClass) > -1 ) {
jQuery(sc[i]).removeClass(itemConf.cssClass);
} else {
jQuery(sc[i]).addClass(itemConf.cssClass);
// remove all column formattings
for (var f = 0; f < that.columnConfig.length; f++) {
if (that.columnConfig[f].cssClass != itemConf.cssClass) {
jQuery(sc[i]).removeClass(that.columnConfig[f].cssClass);
}
}
}
}
// selection could have changed.
that.activeTable.selectColumns();
}
}
};
that.columnMSItems.push(item);
});
if (this.columnMSItems.length > 0) {
this.columnMSItems.push({
name : 'removeFormat',
text : i18n.t('button.removeFormat.text'),
tooltip : i18n.t('button.removeFormat.tooltip'),
'cls' : 'aloha-ui-multisplit-fullwidth',
wide : true,
click : function () {
if (that.activeTable) {
var sc = that.activeTable.selection.selectedCells;
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
for (var f = 0; f < that.columnConfig.length; f++) {
jQuery(sc[i]).removeClass(that.columnConfig[f].cssClass);
}
}
// selection could have changed.
that.activeTable.selectColumns();
}
}
});
}
this.columnMSButton = MultiSplitButton({
items: this.columnMSItems,
name: 'formatColumn',
hideIfEmpty: true,
scope: this.name + '.column'
});
};
/**
* Adds custom formatting buttons for cells to floating menu
*/
TablePlugin.initCellBtns = function () {
var that = this;
// generate formatting buttons
this.cellMSItems = [];
jQuery.each(this.cellConfig, function (j, itemConf) {
var item = {
name : itemConf.name,
text : i18n.t(itemConf.text),
tooltip : i18n.t(itemConf.tooltip),
iconClass : 'aloha-icon aloha-column-layout ' + itemConf.iconClass,
click : function (x,y,z) {
if (that.activeTable) {
var sc = that.selectedOrActiveCells();
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
if ( jQuery(sc[i]).attr('class').indexOf(itemConf.cssClass) > -1 ) {
jQuery(sc[i]).removeClass(itemConf.cssClass);
} else {
jQuery(sc[i]).addClass(itemConf.cssClass);
// remove all column formattings
for (var f = 0; f < that.cellConfig.length; f++) {
if (that.cellConfig[f].cssClass != itemConf.cssClass) {
jQuery(sc[i]).removeClass(that.cellConfig[f].cssClass);
}
}
}
}
that.setActiveCellStyle();
}
}
};
that.cellMSItems.push(item);
});
if (this.cellMSItems.length > 0) {
this.cellMSItems.push({
name : 'removeFormat',
text : i18n.t('button.removeFormat.text'),
tooltip : i18n.t('button.removeFormat.tooltip'),
'cls' : 'aloha-ui-multisplit-fullwidth',
wide : true,
click : function () {
if (that.activeTable) {
var sc = that.selectedOrActiveCells();
// if a selection was made, transform the selected cells
for (var i = 0; i < sc.length; i++) {
for (var f = 0; f < that.cellConfig.length; f++) {
jQuery(sc[i]).removeClass(that.cellConfig[f].cssClass);
}
}
that.setActiveCellStyle();
}
}
});
}
this.cellMSButton = MultiSplitButton({
items: this.cellMSItems,
name: 'formatCell',
hideIfEmpty: true,
scope: this.name + '.cell'
});
};
/**
* initialize the buttons and register them on floating menu
*/
TablePlugin.initTableButtons = function () {
var that = this;
// generate the new scopes
Scopes.createScope(this.name + '.row', 'Aloha.continuoustext');
Scopes.createScope(this.name + '.column', 'Aloha.continuoustext');
Scopes.createScope(this.name + '.cell', 'Aloha.continuoustext');
this._createTableButton = Ui.adopt("createTable", Button, {
tooltip: i18n.t("button.createtable.tooltip"),
icon: "aloha-icon aloha-icon-createTable",
scope: 'Aloha.continuoustext',
click: function() {
TablePlugin.createDialog(this.element);
}
});
// now the specific table buttons
// generate formatting buttons for columns
this.initColumnBtns();
// generate formatting buttons for rows
this.initRowsBtns();
// generate formatting buttons for cells
this.initCellBtns();
this.initMergeSplitCellsBtns();
this.initNaturalFitBtn();
// generate formatting buttons for tables
this.tableMSItems = [];
var tableConfig = this.tableConfig;
jQuery.each(tableConfig, function(j, itemConf){
that.tableMSItems.push({
name: itemConf.name,
text: i18n.t(itemConf.text),
tooltip: i18n.t(itemConf.tooltip),
iconClass: 'aloha-icon aloha-table-layout ' + itemConf.iconClass,
click: function(){
// set table css class
if (that.activeTable) {
for (var f = 0; f < tableConfig.length; f++) {
that.activeTable.obj.removeClass(tableConfig[f].cssClass);
}
that.activeTable.obj.addClass(itemConf.cssClass);
}
}
});
});
if(this.tableMSItems.length > 0) {
this.tableMSItems.push({
name : 'removeFormat',
text : i18n.t('button.removeFormat.text'),
tooltip : i18n.t('button.removeFormat.tooltip'),
'cls' : 'aloha-ui-multisplit-fullwidth',
wide : true,
click : function () {
// remove all table classes
if (that.activeTable) {
for (var f = 0; f < tableConfig.length; f++) {
that.activeTable.obj.removeClass(that.tableConfig[f].cssClass);
}
}
}
});
}
this.tableMSButton = MultiSplitButton({
items : this.tableMSItems,
name : 'formatTable',
hideIfEmpty: true,
scope: this.name + '.cell'
});
this._tableCaptionButton = Ui.adopt("tableCaption", ToggleButton, {
tooltip: i18n.t("button.caption.tooltip"),
icon: "aloha-icon aloha-icon-table-caption",
scope: this.name + '.cell',
click: function() {
if (that.activeTable) {
// look if table object has a child caption
if ( that.activeTable.obj.children("caption").is('caption') ) {
that.activeTable.obj.children("caption").remove();
} else {
var captionText = i18n.t('empty.caption');
var c = jQuery('<caption></caption>');
that.activeTable.obj.prepend(c);
that.makeCaptionEditable(c, captionText);
// get the editable span within the caption and select it
var cDiv = c.find('div').eq(0);
var captionContent = cDiv.contents().eq(0);
if (captionContent.length > 0) {
var newRange = new GENTICS.Utils.RangeObject();
newRange.startContainer = newRange.endContainer = captionContent.get(0);
newRange.startOffset = 0;
newRange.endOffset = captionContent.text().length;
// blur all editables within the table
that.activeTable.obj.find('div.aloha-table-cell-editable').blur();
cDiv.focus();
newRange.select();
Aloha.Selection.updateSelection();
}
}
}
}
});
this.summary = AttributeField( {
width : 275,
name : 'tableSummary',
noTargetHighlight: true,
scope: this.name + '.cell'
} );
this.summary.addListener( 'keyup', function( event ) {
if (that.activeTable) {
that.activeTable.checkWai();
}
} );
};
/**
* Helper method to make the caption editable
* @param caption caption as jQuery object
* @param captionText default text for the caption
*/
TablePlugin.makeCaptionEditable = function(caption, captionText) {
var that = this;
var cSpan = caption.children('div');
if (cSpan.length === 0) {
// generate a new div
cSpan = jQuery('<div></div>');
jQuery(cSpan).addClass('aloha-ui');
jQuery(cSpan).addClass('aloha-editable-caption');
if (caption.contents().length > 0) {
// when the caption has content, we wrap it with the new div
cSpan.append(caption.contents());
caption.append(cSpan);
} else {
// caption has no content, so insert the default caption text
if (captionText) {
cSpan.text(captionText);
}
// and append the div into the caption
caption.append(cSpan);
}
} else if (cSpan.length > 1) {
// merge multiple divs (they are probably created by IE)
caption.children('div:not(:first-child)').each(function () {
$this = jQuery(this);
cSpan.eq(0).append($this.contents());
$this.remove();
});
cSpan = cSpan.eq(0);
}
// make the div editable
cSpan.contentEditable(true);
};
/**
* This function adds the createDialog to the calling element
*
* @param callingElement
* The element, which was clicked. It's needed to set the right
* position to the create-table-dialog.
*/
TablePlugin.createDialog = function(callingElement) {
// set the calling element to the layer the calling element mostly will be
// the element which was clicked on it is used to position the createLayer
this.createLayer.set('target', callingElement);
// show the createLayer
this.createLayer.show();
};
/**
* Creates a normal html-table, "activates" this table and inserts it into the
* active Editable
*
* @param cols
* number of colums for the created table
* @param cols
* number of rows for the created table
* @return void
*/
TablePlugin.createTable = function(cols, rows) {
if ( this.preventNestedTables() ) {
return;
}
// Check if there is an active Editable and that it contains an element (= .obj)
if ( Aloha.activeEditable && typeof Aloha.activeEditable.obj !== 'undefined' ) {
// create a dom-table object
var table = document.createElement( 'table' );
var tableId = table.id = GENTICS.Utils.guid();
var tbody = document.createElement( 'tbody' );
// create "rows"-number of rows
for ( var i = 0; i < rows; i++ ) {
var tr = document.createElement( 'tr' );
// create "cols"-number of columns
for ( var j = 0; j < cols; j++ ) {
var text = document.createTextNode( '\u00a0' );
var td = document.createElement( 'td' );
td.appendChild( text );
tr.appendChild( td );
}
tbody.appendChild( tr );
}
table.appendChild( tbody );
prepareRangeContainersForInsertion(
Aloha.Selection.getRangeObject(), table );
// insert the table at the current selection
GENTICS.Utils.Dom.insertIntoDOM(
jQuery( table ),
Aloha.Selection.getRangeObject(),
Aloha.activeEditable.obj
);
cleanupAfterInsertion();
var tableReloadedFromDOM = document.getElementById( tableId );
var tableObj = createNewTable(tableReloadedFromDOM);
if (tableObj) {
// Because without the 10ms delay, we cannot place the cursor
// automatically into the first cell in IE.
if ($.browser.msie) {
window.setTimeout(function () {
tableObj.cells[0].wrapper.get(0).focus();
}, 20 );
} else {
tableObj.cells[0].wrapper.get(0).focus();
}
}
// The selection starts out in the first cell of the new
// table. The table tab/scope has to be activated
// accordingly.
tableObj.focus();
TablePlugin.activeTable.selection.selectionType = 'cell';
TablePlugin.updateFloatingMenuScope();
} else {
this.error( 'There is no active Editable where the table can be\
inserted!' );
}
};
TablePlugin.setFocusedTable = function(focusTable) {
var that = this;
// clicking outside the table unselects the cells of the table
if ( null == focusTable && null != this.activeTable ) {
this.activeTable.selection.unselectCells();
}
for (var i = 0; i < TablePlugin.TableRegistry.length; i++) {
TablePlugin.TableRegistry[i].hasFocus = false;
}
if (typeof focusTable != 'undefined') {
this.summary.setTargetObject(focusTable.obj, 'summary');
if ( focusTable.obj.children("caption").is('caption') ) {
// set caption button
this._tableCaptionButton.setState(true);
var c = focusTable.obj.children("caption");
that.makeCaptionEditable(c);
}
focusTable.hasFocus = true;
}
TablePlugin.activeTable = focusTable;
// show configured formatting classes
for (var i = 0; i < this.tableMSItems.length; i++) {
this.tableMSButton.showItem(this.tableMSItems[i].name);
}
this.tableMSButton.setActiveItem();
if (this.activeTable) {
for (var i = 0; i < this.tableConfig.length; i++) {
if (this.activeTable.obj.hasClass(this.tableConfig[i].cssClass)) {
this.tableMSButton.setActiveItem(this.tableConfig[i].name);
}
}
}
};
/**
* Calls the Aloha.log function with 'error' level
*
* @see Aloha.log
* @param msg
* The message to display
* @return void
*/
TablePlugin.error = function(msg) {
Aloha.Log.error(this, msg);
};
/**
* Calls the Aloha.log function with 'debug' level
*
* @see Aloha.log
* @param msg
* The message to display
* @return void
*/
TablePlugin.debug = function(msg) {
Aloha.Log.debug(this, msg);
};
/**
* Calls the Aloha.log function with 'info' level
*
* @see Aloha.log
* @param msg
* The message to display
* @return void
*/
TablePlugin.info = function(msg) {
Aloha.Log.info(this, msg);
};
/**
* Calls the Aloha.log function with 'info' level
*
* @see Aloha.log
* @param msg
* The message to display
* @return void
*/
TablePlugin.log = function(msg) {
Aloha.log('log', this, msg);
};
/**
* The "get"-method returns the value of the given key.
* First it searches in the config for the property.
* If there is no property with the given name in the
* "config"-object it returns the entry associated with
* in the parameters-object
*
* @param property
* @return void
*
*/
TablePlugin.get = function (property) {
if (this.config[property]) {
return this.config[property];
}
if (this.parameters[property]) {
return this.parameters[property];
}
return undefined;
};
/**
* The "set"-method takes a key and a value. It checks if there is a
* key-value pair in the config-object. If so it saves the data in the
* config-object. If not it saves the data in the parameters-object.
*
* @param key the key which should be set
* @param value the value which should be set for the associated key
*/
TablePlugin.set = function (key, value) {
if (this.config[key]) {
this.config[key] = value;
}else{
this.parameters[key] = value;
}
};
/**
* Make the given jQuery object (representing an editable) clean for saving
* Find all tables and deactivate them
* @param obj jQuery object to make clean
* @return void
*/
TablePlugin.makeClean = function ( obj ) {
var that = this;
obj.find( 'table' ).each( function () {
// Make sure that we only deactivate tables in obj which have the
// same id as tables which have been activated and registered
if ( that.getTableFromRegistry( this ) ) {
( new Table( this, that ) ).deactivate();
// remove the id attribute
jQuery(this).attr('id', null);
}
} );
};
/**
* String representation of the Table-object
*
* @return The plugins namespace (string)
*/
TablePlugin.toString = function() {
return this.prefix;
};
TablePlugin.updateFloatingMenuScope = function() {
if ( null != TablePlugin.activeTable && null != TablePlugin.activeTable.selection.selectionType ) {
Scopes.setScope(TablePlugin.name + '.' + TablePlugin.activeTable.selection.selectionType);
}
};
TablePlugin.setActiveCellStyle = function() {
var that = this;
// reset any selected cell styles
this.cellMSButton.setActiveItem();
var selectedCells = that.selectedOrActiveCells();
jQuery( selectedCells ).each( function() {
for (var k = 0; k < that.cellConfig.length; k++) {
if ( jQuery(this).hasClass(that.cellConfig[k].cssClass) ) {
that.cellMSButton.setActiveItem(that.cellConfig[k].name);
k = that.cellConfig.length;
}
}
});
};
TablePlugin.selectedOrActiveCells = function() {
var that = this;
var sc = this.activeTable.selection.selectedCells;
// if there are no selected cells,
// set the active cell as the selected cell.
if (!sc || sc.length < 1) {
var activeCell = function() {
var range = Aloha.Selection.getRangeObject();
if (Aloha.activeEditable) {
return range.findMarkup( function() {
return this.nodeName.toLowerCase() === 'td';
}, Aloha.activeEditable.obj );
} else {
return null;
}
}
var active_cell = activeCell();
return (active_cell ? [ active_cell ] : []);
} else {
return sc;
}
};
PluginManager.register(TablePlugin);
/**
* Detects a situation where we are about to insert content into a
* selection that looks like this: <p> [</p>...
* We will assume that the nbsp inside the <p> node was placed there to
* "prop-up" the empty paragraph--that is--to make the empty paragraph
* visible in HTML5 conformant rendering engines, like WebKit. Without the
* white space, such browsers would correctly render an empty <p> as
* invisible.
*
* If we detect this situation, we remove the white space so that when we
* paste new content into the paragraph, it is not be split and leaving an
* empty paragraph on top of the pasted content.
*
* Note that we do not use <br />'s to prop up the paragraphs, as WebKit
* does, because IE, will break from the HTML5 specification and will
* display empty paragraphs if they are content-editable. So a <br />
* inside an empty content-editable paragraph will result in 2 lines to be
* shown instead of 1 in IE.
*
* @param {Object} range
* @param {DOMElement} table
*/
function prepareRangeContainersForInsertion ( range, table ) {
var eNode = range.endContainer,
sNode = range.startContainer,
eNodeLength = ( eNode.nodeType == 3 )
? eNode.length
: eNode.childNodes.length;
if ( sNode.nodeType == 3 &&
sNode.parentNode.tagName == 'P' &&
sNode.parentNode.childNodes.length == 1 &&
/^(\s|%A0)$/.test( escape( sNode.data ) ) ) {
sNode.data = '';
range.startOffset = 0;
// In case ... <p> []</p>
if ( eNode == sNode ) {
range.endOffset = 0;
}
}
// If the table is not allowed to be nested inside the startContainer,
// then it will have to be split in order to insert the table.
// We will therefore check if the selection touches the start and/or
// end of their container nodes.
// If they do, we will mark their container so that after they are
// split we can check whether or not they should be removed
if ( !GENTICS.Utils.Dom.allowsNesting(
sNode.nodeType == 3 ? sNode.parentNode : sNode, table ) ) {
if ( range.startOffset == 0 ) {
jQuery( sNode.nodeType == 3 ? sNode.parentNode : sNode )
.addClass( 'aloha-table-cleanme' );
}
if ( range.endOffset == eNodeLength ) {
jQuery( eNode.nodeType == 3 ? eNode.parentNode : eNode )
.addClass( 'aloha-table-cleanme' );
}
}
};
/**
* Looks for elements marked with "aloha-table-cleanme", and removes them
* if they are absolutely empty.
* Note that this will leave paragraphs which contain empty nested elements
* even though they are also invisible.
* We can consider removing these as well at a later stage, if needed.
*/
function cleanupAfterInsertion () {
var dirty = jQuery( '.aloha-table-cleanme' ).removeClass(
'aloha-table-cleanme' );
for ( var i = 0; i < dirty.length; i++ ) {
if ( jQuery.trim( jQuery( dirty[ i ] ).html() ) == '' &&
!GENTICS.Utils.Dom.isEditingHost( dirty[ i ] ) ) {
jQuery( dirty[ i ] ).remove();
/*
// For debugging: to see what we are deleting
jQuery( dirty[ i ] ).css({
border: '3px solid red',
display: 'block'
});
*/
}
}
};
return TablePlugin;
});