/** * Gentics Aloha Simple Table Plugin * * !!! PROOF OF CONCEPT !!! * */ /** * Register the SimpleTablePlugin as GENTICS.Aloha.Plugin */ GENTICS.Aloha.SimpleTablePlugin = new GENTICS.Aloha.Plugin('com.gentics.aloha.plugins.SimpleTable', 'table'); // // // I M P O R T A N T // // if you want to disable ff table resize handles this has to be executed onload // // document.execCommand( 'enableInlineTableEditing', false, false ); // // the call will fail if used outside of onload, and has to be surrounded by try/catch // but it's proven to work! // // /* -- ATTRIBUTES -- */ /** * The Create-Layer Object of the SimpleTablePlugin * * @see GENTICS.Aloha.Table.CreateLayer */ GENTICS.Aloha.SimpleTablePlugin.createLayer = undefined; /** * Configure the available languages */ GENTICS.Aloha.SimpleTablePlugin.languages = ['en', 'de']; /** * An Array which holds all newly created tables contains DOM-Nodes of * table-objects */ GENTICS.Aloha.SimpleTablePlugin.TableRegistry = new Array(); /** * Holds the active table-object */ GENTICS.Aloha.SimpleTablePlugin.activeTable = undefined; /** * parameters-objects for tables * * @param className * The class of activated tables */ GENTICS.Aloha.SimpleTablePlugin.parameters = { className : 'GENTICS_Aloha_SimpleTable', // class of editable tables classSelectionRow : 'GENTICS_Aloha_SimpleTable_selectColumn', // class for the upper table-row to select columns classSelectionColumn : 'GENTICS_Aloha_SimpleTable_selectRow', // class for the left bound table-cells to select rows classLeftUpperCorner : 'GENTICS_Aloha_SimpleTable_leftUpperCorner', // class for the left upper corner cell classTableWrapper : 'GENTICS_Aloha_SimpleTable_wrapper', // class of the outest table-wrapping div classCellSelected : 'GENTICS_Aloha_Cell_selected', // class of cell which are selected (row/column selection) selectionArea : 10 // width/height of the selection rows (in pixel) }; /** * The configuration-object for the implementer of the plugin. All keys of the * "parameters" object could be overwritten within this object and will simply * be used instead. */ GENTICS.Aloha.SimpleTablePlugin.config = new Object(); /* -- END ATTRIBUTES -- */ /* -- METHODS -- */ /** * Init method of the Table-plugin transforms all tables in the document and * sets the initialized flag to true * * @return void */ GENTICS.Aloha.SimpleTablePlugin.init = function() { // disable this plugin return; // add reference to the create layer object this.createLayer = new GENTICS.Aloha.Table.CreateLayer(); var that = this; // subscribe for the 'editableActivated' event to activate all tables in the editable GENTICS.Aloha.EventRegistry.subscribe(GENTICS.Aloha, 'editableCreated', function(event, editable) { // add a mousedown event to all created editables to check if editable.obj.bind('mousedown', function(jqEvent) { GENTICS.Aloha.SimpleTablePlugin.setFocusedTable(undefined); if (!editable.floatingMenu.isActive) { editable.floatingMenu.call(); } }); editable.obj.find('table').each(function() { // instantiate a new table-object var table = new GENTICS.Aloha.Table(this); table.parentEditable = editable; // activate the table table.activate(); // add the activated table to the TableRegistry GENTICS.Aloha.SimpleTablePlugin.TableRegistry.push(table); }); }); // call initButtons when event enableCreated is triggered GENTICS.Aloha.EventRegistry.subscribe(GENTICS.Aloha, 'editableCreated', this.initCreateTableButton); // disable advanced table handles in firefox // try/catch since opera won't like this $(document).ready(function () { // document.execCommand( 'enableInlineTableEditing', false, false ); }); this.initialized = true; }; /** * initialize the buttons and register them on floating menu * @param event event object * @param editable current editable object */ GENTICS.Aloha.SimpleTablePlugin.initCreateTableButton = function (event, editable) { var buttons = new Array(); var sep = new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'separator', title : 'Separator', cssClass : 'GENTICS_separator', onClick : function(){} }); buttons.push(sep); var table = new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'table', status : 'released', title : 'Insert table', cssClass : 'GENTICS_button_table', onClick : function() { GENTICS.Aloha.SimpleTablePlugin.createDialog(this.html.children("a.GENTICS_button_table").get(0)); } }); buttons.push(table); editable.floatingMenu.registerButtons(GENTICS.Aloha.SimpleTablePlugin, buttons); }; /** * 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. */ GENTICS.Aloha.SimpleTablePlugin.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 */ GENTICS.Aloha.SimpleTablePlugin.createTable = function(cols, rows) { // Check if there is an active Editable and that it contains an element (= .obj) if (GENTICS.Aloha.activeEditable != null && typeof GENTICS.Aloha.activeEditable.obj != 'undefined') { // create a dom-table object var table = document.createElement('table'); var tableId = table.id = GENTICS.Aloha.TableHelper.getNewTableID(); 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); // insert at current cursor position this.insertAtCursorPos(table); // if the table is inserted var tableReloadedFromDOM = document.getElementById(tableId); var tableObj = new GENTICS.Aloha.Table(tableReloadedFromDOM); tableObj.parentEditable = GENTICS.Aloha.activeEditable; // transform the table to be editable // tableObj.activate(); // after creating the table, trigger a click into the first cell to // focus the content // for IE set a timeout of 10ms to focus the first cell, other wise it // won't work // if (jQuery.browser.msie) { // window.setTimeout(function() { tableObj.cells[0].wrapper.get(0).focus(); }, 20); // } else { // tableObj.cells[0].wrapper.get(0).focus(); // } GENTICS.Aloha.SimpleTablePlugin.TableRegistry.push(tableObj); // no active editable => error } else { this.error('There is no active Editable where the table can be inserted!'); } }; /** * Inserts the given element at the cursor position of the current active Editable * * @param elementThe element which should be inserted (a DOM-object) */ GENTICS.Aloha.SimpleTablePlugin.insertAtCursorPos = function(element) { // check if the element-parameter is set if (typeof element != 'undefined') { // check if there is an active editable if (typeof GENTICS.Aloha.activeEditable.obj != 'undefined'){ var range, html; // insertion for IE if (jQuery.browser.msie) { range = GENTICS.Aloha.Editable.range; html = (element.nodeType == 3) ? element.data : element.outerHTML; range.pasteHTML(html); // insertion for other browser } else if (window.getSelection && window.getSelection().getRangeAt){ range = GENTICS.Aloha.Editable.range; range.insertNode(element); // insertion didn't work trigger ERROR to user! } else { this.error("Table couldn't be inserted"); } }else{ this.warn('No active Editable => do nothing.'); } }else{ this.error('This method didn\'t get an element to insert!'); } }; GENTICS.Aloha.SimpleTablePlugin.setFocusedTable = function(focusTable) { for (var i = 0; i < GENTICS.Aloha.SimpleTablePlugin.TableRegistry.length; i++) { GENTICS.Aloha.SimpleTablePlugin.TableRegistry[i].hasFocus = false; } if (typeof focusTable != 'undefined') { focusTable.hasFocus = true; } GENTICS.Aloha.SimpleTablePlugin.activeTable = focusTable; }; /** * Calls the GENTICS.Aloha.log function with 'error' level * * @see GENTICS.Aloha.log * @param msg * The message to display * @return void */ GENTICS.Aloha.SimpleTablePlugin.error = function(msg) { GENTICS.Aloha.Log.error(this, msg); }; /** * Calls the GENTICS.Aloha.log function with 'debug' level * * @see GENTICS.Aloha.log * @param msg * The message to display * @return void */ GENTICS.Aloha.SimpleTablePlugin.debug = function(msg) { GENTICS.Aloha.Log.debug(this, msg); }; /** * Calls the GENTICS.Aloha.log function with 'info' level * * @see GENTICS.Aloha.log * @param msg * The message to display * @return void */ GENTICS.Aloha.SimpleTablePlugin.info = function(msg) { GENTICS.Aloha.Log.info(this, msg); }; /** * Calls the GENTICS.Aloha.log function with 'info' level * * @see GENTICS.Aloha.log * @param msg * The message to display * @return void */ GENTICS.Aloha.SimpleTablePlugin.log = function(msg) { GENTICS.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 * */ GENTICS.Aloha.SimpleTablePlugin.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 */ GENTICS.Aloha.SimpleTablePlugin.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 */ GENTICS.Aloha.SimpleTablePlugin.makeClean = function (obj) { // find all table tags obj.find('table').each(function() { // instantiate a new table-object var table = new GENTICS.Aloha.Table(this); // deactivate the table table.deactivate(); }); }; /** * String representation of the Table-object * * @return The plugins namespace (string) */ GENTICS.Aloha.SimpleTablePlugin.toString = function() { return this.prefix; }; /* -- END METHODS -- */ /************************** +---------------------+ | GENTICS.Aloha.Table | +---------------------+ ***************************/ /** * Constructor of the table object * * @param table * the dom-representation of the held table * @return void */ GENTICS.Aloha.Table = function(table) { // set the table attribut "obj" as a jquery represenation of the dom-table this.obj = jQuery(table); // find the dimensions of the table var rows = this.obj.find("tr"); var firstRow = jQuery(rows.get(0)); this.numCols = firstRow.children("td, th").length; this.numRows = rows.length; // init the cell-attribute with an empty array this.cells = new Array(); // iterate over table cells and create Cell-objects var rows = this.obj.find('tr'); for (var i = 0; i < rows.length; i++) { var row = jQuery(rows[i]); var cols = row.children(); for (var j = 0; j < cols.length; j++) { var col = cols[j]; var Cell = new GENTICS.Aloha.Table.Cell(col, this); this.cells.push(Cell); } } }; /* -- ATTRIBUTES -- */ /** * Attribute holding the jQuery-table-represenation */ GENTICS.Aloha.Table.prototype.obj = undefined; /** * The DOM-element of the outest div-container wrapped around the cell */ GENTICS.Aloha.Table.prototype.tableWrapper = undefined; /** * An array of all Cells contained in the Table * * @see GENTICS.Aloha.Table.Cell */ GENTICS.Aloha.Table.prototype.cells = undefined; /** * Number of rows of the table */ GENTICS.Aloha.Table.prototype.numRows = undefined; /** * Number of rows of the table */ GENTICS.Aloha.Table.prototype.numCols = undefined; /** * This attribute holds the floatingMenu of the table-object */ GENTICS.Aloha.Table.prototype.floatingMenu = undefined; /** * Flag wether the table is active or not */ GENTICS.Aloha.Table.prototype.isActive = false; /** * Flag wether the table is focused or not */ GENTICS.Aloha.Table.prototype.hasFocus = false; /** * The editable which contains the table */ GENTICS.Aloha.Table.prototype.parentEditable = undefined; /** * Flag to check if the mouse was pressed. For row- and column-selection. */ GENTICS.Aloha.Table.prototype.mousedown = false; /** * ID of the column which was pressed when selecting columns */ GENTICS.Aloha.Table.prototype.clickedColumnId = -1; /** * ID of the row which was pressed when selecting rows */ GENTICS.Aloha.Table.prototype.clickedRowId = -1; /** * collection of columnindexes of the columns which should be selected */ GENTICS.Aloha.Table.prototype.columnsToSelect = new Array(); /** * collection of rowindexes of the rows which should be selected */ GENTICS.Aloha.Table.prototype.rowsToSelect = new Array(); /** * contains the plugin id used for interaction with the floating menu */ GENTICS.Aloha.Table.prototype.fmPluginId = undefined; /* -- END ATTRIBUTES -- */ /* -- METHODS -- */ /** * Wrapper-Mehotd to return a property of GENTICS.Aloha.SimpleTablePlugin.get * * @see GENTICS.Aloha.SimpleTablePlugin.get * @param property * the property whichs value should be return * @return the value associated with the property */ GENTICS.Aloha.Table.prototype.get = function(property) { return GENTICS.Aloha.SimpleTablePlugin.get(property); }; /** * Wrapper-Method for GENTICS.Aloha.SimpleTablePlugin.set * * @see GENTICS.Aloha.SimpleTablePlugin.set * @param key * the key whichs value should be set * @param value * the value for the key * @return void */ GENTICS.Aloha.Table.prototype.set = function(key, value) { GENTICS.Aloha.SimpleTablePlugin.set(key, value); }; /** * Transforms the existing dom-table into an editable aloha-table. In fact it * replaces the td-elements with equivalent GENTICS.Aloha.Table.Cell-elements * with attached events. * Furthermore it creates wrapping divs to realize a click-area for row- and * column selection and also attaches events. * * @return void */ GENTICS.Aloha.Table.prototype.activate = function() { if (this.isActive) { return; } var that = this; // alter the table attributes this.obj.addClass(this.get('className')); // set an id to the table if not already set if (this.obj.attr('id') == '') { this.obj.attr('id', GENTICS.Aloha.TableHelper.getNewTableID()); } // add the floating menu to the table this.attachFloatingMenu(); // this.obj.bind('keydown', function(jqEvent){ // if (!jqEvent.ctrlKey && !jqEvent.shiftKey) { // if (GENTICS.Aloha.TableHelper.selectedCells.length > 0 && GENTICS.Aloha.TableHelper.selectedCells[0].length > 0) { // GENTICS.Aloha.TableHelper.selectedCells[0][0].firstChild.focus(); // } // } // }); // handle click event of the table this.obj.bind('click', function(e){ // stop bubbling the event to the outer divs, a click in the table // should only be handled in the table e.stopPropagation(); return false; }); this.obj.bind('mousedown', function(jqEvent) { // focus the table if not already done if (!that.hasFocus) { that.focus(); } // // if a mousedown is done on the table, just focus the first cell of the table // setTimeout(function() { // var firstCell = that.obj.find('tr:nth-child(2) td:nth-child(2)').children('div[contentEditable=true]').get(0); // GENTICS.Aloha.TableHelper.unselectCells(); // jQuery(firstCell).get(0).focus(); // }, 5); // stop bubbling and default-behaviour jqEvent.stopPropagation(); jqEvent.preventDefault(); return false; }); // ### create a wrapper for the table (@see HINT below) // wrapping div for the table to suppress the display of the resize-controls of // the editable divs within the cells // var tableWrapper = jQuery('
'); // wrap the tableWrapper around the table // this.obj.wrap(tableWrapper); // :HINT The outest div (Editable) of the table is still in an editable // div. So IE will surround the the wrapper div with a resize-border // Workaround => just disable the handles so hopefully won't happen any ugly stuff. // Disable resize and selection of the controls (only IE) // Events only can be set to elements which are loaded from the DOM (if they // were created dynamically before) ;) // var htmlTableWrapper = this.obj.parents('.' + this.get('classTableWrapper')); // htmlTableWrapper.get(0).onresizestart = function(e) { return false; }; // htmlTableWrapper.get(0).oncontrolselect = function(e) { return false; }; // // this.tableWrapper = this.obj.parents('.' + this.get('classTableWrapper')).get(0); jQuery(this.cells).each(function () { this.activate(); }); // after the cells where replaced with contentEditables ... add selection cells // first add the additional columns on the left side // this.attachSelectionColumn(); // // then add the additional row at the top // this.attachSelectionRow(); // // // attach events for the last cell // this.attachLastCellEvents(); // set flag, that the table is activated this.isActive = true; // this.lastCellLastCaretPos = 0; // handle cursor moves in first and last table cell // e.keyCode: left = 37, up = 38, right = 39, down = 40 // this.obj.find('tr:nth-child(2) td:nth-child(2)').css('background', 'blue'); // this.obj.find('tr:last td:last div.GENTICS_Table_Cell_editable').bind('keyup', function (e) { // GENTICS.Aloha.Log.debug(that, 'last caret pos: ' + that.lastCellLastCaretPos + ' new: ' // + GENTICS.Aloha.Selection.rangeObject.endOffset); // // // bei den richtigen keys letze pos mit aktueller pos vergleichen // if (that.lastCellLastCaretPos == GENTICS.Aloha.Selection.rangeObject.endOffset // && (e.keyCode == 39 || e.keyCode == 40)) { // alert('jump out'); // } // // that.lastCellLastCaretPos = GENTICS.Aloha.Selection.rangeObject.endOffset; // }); // throw a new event when the table has been activated GENTICS.Aloha.EventRegistry.trigger( new GENTICS.Aloha.Event( 'tableActivated', GENTICS.Aloha, [ this ] ) ); }; /** * Creates a new floatingMenu with the defined buttons and attaches it to the * Table-object * * @return void */ GENTICS.Aloha.Table.prototype.attachFloatingMenu = function() { // create a floating menu for the table and set it as attribute this.floatingMenu = new GENTICS.Aloha.OldFloatingMenu(this.obj); this.fmPluginId = this + this.obj.attr('id'); var that = this; // register the fm buttons var buttons = [ // add column (left) button new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'addColumnLeft', title : 'Add column left', cssClass : 'GENTICS_Aloha_addColumnLeft', onClick : function(){ that.addColumnsLeft(); } }), // add column (right) button new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'addColumnRight', title : 'Add column right', cssClass : 'GENTICS_Aloha_addColumnRight', onClick : function(){ that.addColumnsRight(); } }), // insert row before button new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'addRowBefore', title : 'Insert row before', cssClass : 'GENTICS_Aloha_addRowBefore', onClick : function(){ that.addRowsBefore(true); } }), // insert row after button new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'addRowAfter', title : 'Insert row after', cssClass : 'GENTICS_Aloha_addRowAfter', onClick : function(){ that.addRowsAfter(true); } }), // delete selected rows new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'deleteRows', title : 'Delete selected row(s)', cssClass : 'GENTICS_Aloha_deleteRows', onClick : function(){ var bool = window.confirm('You are going to delete the selected rows?\nContinue?'); if (bool) { that.deleteRows(); } } }), // delete selected columns new GENTICS.Aloha.OldFloatingMenu.Button({ name : 'deleteColumns', title : 'Delete selected column(s)', cssClass : 'GENTICS_Aloha_deleteColumns', onClick : function(){ var bool = window.confirm('You are going to delete the selected columns?\nContinue?'); if (bool) { that.deleteColumns(); } } }) ]; // register the buttons for the floating menu this.floatingMenu.registerButtons(this.fmPluginId, buttons); // hide the buttons for deleting rows and columns }; /** * Add the selection-column to the left side of the table and attach the events * for selection rows * * @return void */ GENTICS.Aloha.Table.prototype.attachSelectionColumn = function() { // create an empty cell var emptyCell = jQuery(''); // set the unicode ' ' code emptyCell.html('\u00a0'); var that = this; var rows = this.obj.context.rows; // add a column before each first cell of each row for (var i = 0; i < rows.length; i++) { var rowObj = jQuery(rows[i]); var columnToInsert = emptyCell.clone(); columnToInsert.addClass(this.get('classSelectionColumn')); columnToInsert.css('width', this.get('selectionArea') + 'px'); rowObj.find('td:first').before(columnToInsert); // rowIndex + 1 because an addtional row is still added var rowIndex = i + 1; // this method sets the selection-events to the cell this.attachRowSelectionEventsToCell(columnToInsert); } }; /** * Binds the needed selection-mouse events to the given cell * * @param cell * The jquery object of the table-data field * @return void */ GENTICS.Aloha.Table.prototype.attachRowSelectionEventsToCell = function(cell){ var that = this; // unbind eventually existing events of this cell cell.unbind('mousedown'); cell.unbind('mouseover'); // prevent ie from selecting the contents of the table cell.get(0).onselectstart = function() { return false; }; cell.bind('mousedown', function(e){ that.rowSelectionMouseDown(e); // focus the table (if not already done) that.focus(); // stop bubble, otherwise the mousedown of the table is called ... e.stopPropagation(); // prevent ff/chrome/safare from selecting the contents of the table //return false; }); cell.bind('mouseover', function(e) { that.rowSelectionMouseOver(e); e.preventDefault(); }); }; /** * Mouse-Down event for the selection-cells on the left side of the table * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.prototype.rowSelectionMouseDown = function (jqEvent) { // set flag that the mouse is pressed this.mousedown = true; // if no cells are selected, reset the selection-array if (GENTICS.Aloha.TableHelper.selectedCells.length == 0) { this.rowsToSelect = new Array(); } // set the origin-rowId of the mouse-click this.clickedRowId = jqEvent.currentTarget.parentNode.rowIndex; // set the array of to-be-selected columns if (jqEvent.ctrlKey) { var arrayIndex = jQuery.inArray(this.clickedRowId, this.rowsToSelect); if (arrayIndex >= 0) { this.rowsToSelect.splice(arrayIndex, 1); }else{ this.rowsToSelect.push(this.clickedRowId); } }else if (jqEvent.shiftKey) { this.rowsToSelect.sort(function(a,b){return a - b;}); var start = this.rowsToSelect[0]; var end = this.clickedRowId; if (start > end) { start = end; end = this.rowsToSelect[0]; } this.rowsToSelect = new Array(); for (var i = start; i <= end; i++) { this.rowsToSelect.push(i); } }else{ this.rowsToSelect = [this.clickedRowId]; } // do the selection this.selectRows(); // prevent browser from selecting the table jqEvent.preventDefault(); }; /** * The mouse-over event for the selection-cells on the left side of the table. * On mouse-over check which column was clicked, calculate the span between * clicked and mouse-overed cell and mark them as selected * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.prototype.rowSelectionMouseOver = function (jqEvent) { var rowIndex = jqEvent.currentTarget.parentNode.rowIndex; // only select the row if the mouse was clicked and the clickedRowId isn't // from the selection-row (row-id = 0) if (this.mousedown && this.clickedRowId >= 0) { var indexInArray = jQuery.inArray(rowIndex, this.rowsToSelect); var start = (rowIndex < this.clickedRowId) ? rowIndex : this.clickedRowId; var end = (rowIndex < this.clickedRowId) ? this.clickedRowId : rowIndex; this.rowsToSelect = new Array(); for (var i = start; i <= end; i++) { this.rowsToSelect.push(i); } // this actually selects the rows this.selectRows(); } }; /** * Binds the needed selection-mouse events to the given cell * * @param cell * The jquery object of the table-data field * @return void */ GENTICS.Aloha.Table.prototype.attachSelectionRow = function() { var that = this; // create an empty td var emptyCell = jQuery(''); emptyCell.html('\u00a0'); // get the number of columns in the table (length of the cells in the first row) var numColumns = this.obj.context.rows[0].cells.length; var selectionRow = jQuery(''); selectionRow.addClass(this.get('classSelectionRow')); selectionRow.css('height', this.get('selectionArea') + 'px'); for (var i = 0; i < numColumns; i++) { var columnToInsert = emptyCell.clone(); // the first cell should have no function, so only attach the events for // the rest if (i > 0) { // bind all mouse-events to the cell this.attachColumnSelectEventsToCell(columnToInsert); } // add the cell to the row selectionRow.append(columnToInsert); } // global mouseup event to reset the selection properties jQuery(document).bind('mouseup', function(e) { that.mousedown = false; that.clickedColumnId = -1; that.clickedRowId = -1; }); selectionRow.find('td:first').addClass(this.get('classLeftUpperCorner')); this.obj.find('tr:first').before(selectionRow); }; /** * Binds the events for the column selection to the given cell. * * @param cell * the jquery object of the td-field * @return void */ GENTICS.Aloha.Table.prototype.attachColumnSelectEventsToCell = function (cell) { var that = this; // unbind eventually existing events of this cell cell.unbind('mousedown'); cell.unbind('mouseover'); // prevent ie from selecting the contents of the table cell.get(0).onselectstart = function() { return false; }; cell.bind('mousedown', function(e) { that.columnSelectionMouseDown(e); // focus the table that.focus(); // stop bubble, otherwise the mousedown of the table is called ... e.stopPropagation(); return false; }); cell.bind('mouseover', function (e) { that.columnSelectionMouseOver(e); }); }; /** * Mouse-down event for a columns-selection cell. It adds the index of the * clicked column to the "columnsToSelect"-Array and calls the method which * selects the column. * * @param jqEvent * the jquery event-object * @return void */ GENTICS.Aloha.Table.prototype.columnSelectionMouseDown = function (jqEvent) { // set the mousedown flag this.mousedown = true; // if no cells are selected, reset the selection-array if (GENTICS.Aloha.TableHelper.selectedCells.length == 0) { this.columnsToSelect = new Array(); } // store the id of the column which has been originally clicked this.clickedColumnId = jqEvent.currentTarget.cellIndex; if (jqEvent.ctrlKey) { var arrayIndex = jQuery.inArray(this.clickedColumnId, this.columnsToSelect); if (arrayIndex >= 0) { this.columnsToSelect.splice(arrayIndex, 1); }else{ this.columnsToSelect.push(this.clickedColumnId); } }else if (jqEvent.shiftKey) { this.columnsToSelect.sort(function(a,b){return a - b;}); var start = this.columnsToSelect[0]; var end = this.clickedColumnId; if (start > end) { start = end; end = this.columnsToSelect[0]; } this.columnsToSelect = new Array(); for (var i = start; i <= end; i++) { this.columnsToSelect.push(i); } }else{ this.columnsToSelect = [this.clickedColumnId]; } // this does actually the column-selection. // it reads the columns which should be selected from "columnsToSelect" this.selectColumns(); // prevent browser from selecting the table jqEvent.preventDefault(); }; /** * Mouseover-event for the column-selection cell. This method calcluates the * span between the clicked column and the mouse-overed cell and selects the * columns inbetween. and mark them as selected * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.prototype.columnSelectionMouseOver = function (jqEvent) { var colIndex = jqEvent.currentTarget.cellIndex; if (this.mousedown && this.clickedColumnId > 0) { var indexInArray = jQuery.inArray(colIndex, this.columnsToSelect); var start = (colIndex < this.clickedColumnId) ? colIndex : this.clickedColumnId; var end = (colIndex < this.clickedColumnId) ? this.clickedColumnId : colIndex; this.columnsToSelect = new Array(); for (var i = start; i <= end; i++) { this.columnsToSelect.push(i); } this.selectColumns(); } }; /** * Unbinds all events of the last cell * * @return void */ GENTICS.Aloha.Table.prototype.releaseLastCellEvents = function() { this.obj.find('tr:last td:last').unbind(); }; /** * Attach a keydown-event for the last cell * * @see GENTICS.Aloha.Table.lastCellKeyDown * @return void */ GENTICS.Aloha.Table.prototype.attachLastCellEvents = function() { var that = this; this.obj.find('tr:last td:last').bind('keydown', function(jqEvent) { that.lastCellKeyDown(jqEvent); }); }; /** * If the tab-key was pressed in the last cell create a new row and jump into * the first cell of the next row. * Only add a new row if no addtional key was pressed (shift, alt, ctrl) * * @param jqEvent * the jquery-event object * @return */ GENTICS.Aloha.Table.prototype.lastCellKeyDown = function(jqEvent) { var KEYCODE_TAB = 9; // only add a row on a single key-press of tab (so check if alt-, shift- or // ctrl-key are NOT pressed) if (KEYCODE_TAB == jqEvent.keyCode && !jqEvent.altKey && !jqEvent.shiftKey && !jqEvent.ctrlKey) { // add a row after the current row (false stands for not highlighting the new row) this.addRowsAfter(false); // stop propagation because this should overwrite all other events jqEvent.stopPropagation(); // for ie make a special case ... focus the first cell of the new row if (jQuery.browser.msie) { this.obj.find('tr:last td:nth-child(1) div.GENTICS_Table_Cell_editable').get(0).focus(); return false; } } }; /** * Deletes the selected rows. If no row are selected, delete the row, where the * cursor is positioned. If all rows of the table should be deleted, the whole * table is deletet and removed from the tableRegistry. * * @return void */ GENTICS.Aloha.Table.prototype.deleteRows = function() { var rowIDs = new Array(); // flag if the table should be deleted var deleteTable = false; // if a selection was made, delete the selected cells if (GENTICS.Aloha.TableHelper.selectedCells.length > 0) { for (var i = 0; i < GENTICS.Aloha.TableHelper.selectedCells.length; i++) { rowIDs.push(GENTICS.Aloha.TableHelper.selectedCells[i][0].parentNode.rowIndex); } // if no rows were selected, delete the row, where the cursor is placed in }else if (typeof GENTICS.Aloha.Table.Cell.lastActiveCell != 'undefined') { rowIDs.push(GENTICS.Aloha.Table.Cell.lastActiveCell.obj.context.parentNode.rowIndex); } // if all rows should be deleted, set flag to remove the WHOLE table if (rowIDs.length == this.numRows) { deleteTable = true; } // delete the whole table if (deleteTable) { if (window.confirm('You have selected all rows to be deleted. This will delete the table.\nContinue?')) { this.deleteTable(); } }else{ rowIDs.sort(function(a,b){return a - b;}); // check which cell should be focused after the deletion var focusRowId = rowIDs[0]; if (focusRowId > (this.numRows - rowIDs.length)) { focusRowId --; } // release the events of the last cell this.releaseLastCellEvents(); // get all rows var rows = this.obj.find('tr'); var rows2delete = new Array(); // build the array with the row-ids of th rows which should be deleted for (var i = 0; i < rowIDs.length; i++) { rows2delete.push(jQuery(rows[rowIDs[i]])); } // delete cells from cells-array for (var i = 0; i < rows2delete.length; i ++) { var cols = rows2delete[i].children("td").toArray(); for (var j = 0; j < cols.length; j++) { for (var m = 0; m < this.cells.length; m ++) { if (cols[j] == this.cells[m].obj.get(0)) { this.cells.splice(m, 1); m = this.cells.length; } } } } // remove the rows for (var i = 0; i < rows2delete.length; i++) { rows2delete[i].remove(); } // reduce the attribute storing the number of rows in the table this.numRows -= rows2delete.length; if (jQuery.browser.msie){ setTimeout(this.obj.find('tr:nth-child(' + (focusRowId + 1) + ') td:nth-child(2) div.GENTICS_Table_Cell_editable').get(0).focus, 5); }else{ this.obj.find('tr:nth-child(' + (focusRowId + 1) + ') td:nth-child(2) div.GENTICS_Table_Cell_editable').get(0).focus(); } // re-attach the events for the last cell this.attachLastCellEvents(); // finally unselect the marked cells GENTICS.Aloha.TableHelper.unselectCells(); } }; /** * Deletes the selected columns. If no columns are selected, delete the column, where the * cursor is positioned. If all columns of the table should be deleted, the whole * table is deleted from the dom and removed from the tableRegistry. * * @return void */ GENTICS.Aloha.Table.prototype.deleteColumns = function() { var colIDs = new Array(); // flag if the table should be deleted var deleteTable = false; // if a selection was made, delete the selected cells if (GENTICS.Aloha.TableHelper.selectedCells.length > 0) { for (var i = 0; i < GENTICS.Aloha.TableHelper.selectedCells[0].length; i++) { colIDs.push(GENTICS.Aloha.TableHelper.selectedCells[0][i].cellIndex); } // if no columns were selected, delete the column, where the cursor is placed in }else if (typeof GENTICS.Aloha.Table.Cell.lastActiveCell != 'undefined') { colIDs.push(GENTICS.Aloha.Table.Cell.lastActiveCell.obj.context.cellIndex); } // if all columns should be deleted, set flag to remove the WHOLE table if (colIDs.length == this.numCols) { deleteTable = true; } // delete the whole table if (deleteTable) { if (window.confirm('You have selected all columns to be deleted. This will delete the table.\nContinue?')) { this.deleteTable(); } }else{ colIDs.sort(function(a,b){return a - b;}); // check which cell should be focused after the deletion var focusColID = colIDs[0]; if (focusColID > (this.numCols - colIDs.length)) { focusColID --; } // release the events of the last cell this.releaseLastCellEvents(); // get all rows to iterate var rows = this.obj.find('tr'); var cols2delete = new Array(); // build the array with the row-ids of th rows which should be deleted for (var i = 0; i < rows.length; i++) { var cells = jQuery(rows[i]).children("td").toArray(); for (var j = 0; j < colIDs.length; j++) { cols2delete.push(cells[colIDs[j]]); } } // delete cells from cells-array for (var i = 0; i < cols2delete.length; i ++) { for (var j = 0; j < this.cells.length; j++) { if (cols2delete[i] == this.cells[j].obj.get(0)) { this.cells.splice(j, 1); j = this.cells.length; } } } // remove the columns for (var i = 0; i < cols2delete.length; i++) { jQuery(cols2delete[i]).remove(); } // reduce the attribute storing the number of rows in the table this.numCols -= colIDs.length; if (jQuery.browser.msie){ setTimeout(this.obj.find('tr:nth-child(2) td:nth-child(' + (focusColID + 1) + ') div.GENTICS_Table_Cell_editable').get(0).focus, 5); }else{ this.obj.find('tr:nth-child(2) td:nth-child(' + (focusColID + 1) + ') div.GENTICS_Table_Cell_editable').get(0).focus(); } // re-attach the events for the last cell this.attachLastCellEvents(); GENTICS.Aloha.TableHelper.unselectCells(); } }; /** * Deletes the table from the dom and remove it from the tableRegistry. * * @return void */ GENTICS.Aloha.Table.prototype.deleteTable = function() { var deleteIndex = -1; for (var i = 0; i < GENTICS.Aloha.SimpleTablePlugin.TableRegistry.length; i++){ if (GENTICS.Aloha.SimpleTablePlugin.TableRegistry[i].obj.attr('id') == this.obj.attr('id')) { deleteIndex = i; break; } } if (deleteIndex >= 0) { GENTICS.Aloha.SimpleTablePlugin.TableRegistry.splice(i, 1); this.obj.remove(); this.parentEditable.obj.focus(); this.parentEditable.floatingMenu.call(); delete this; } }; /** * Wrapper function for this.addRow to add a row before the active row * * @param highlightNewRows flag if the newly created rows should be marked as selected * @see GENTICS.Aloha.Table.prototype.addRow * @return */ GENTICS.Aloha.Table.prototype.addRowsBefore = function(highlightNewRows) { this.addRows('before', highlightNewRows); }; /** * Wrapper function for this.addRow to add a row after the active row * * @param highlightNewRows flag if the newly created rows should be marked as selected * @see GENTICS.Aloha.Table.prototype.addRow * @return */ GENTICS.Aloha.Table.prototype.addRowsAfter = function(highlightNewRows) { this.addRows('after', highlightNewRows); }; /** * Adds new rows to the table. If rows were selected, the new rows will be * inserted before/after the first/last selected row. If no rows are selected, a * new row will be inserted before/after the row of the currently selected cell. * As well the row-selection events have to be bound again. * * @param position * could be 'after' or 'before'. defines the position where the new * rows should be inserted * @param highlightNewRows * flag if the newly created rows should be marked as selected * @return void */ GENTICS.Aloha.Table.prototype.addRows = function(position, highlightNewRows) { if (typeof GENTICS.Aloha.SimpleTablePlugin.activeTable != 'undefined') { // release listening events of the last cell this.releaseLastCellEvents(); var that = this; var numCols = this.numCols; // number of rows to insert var rowsToInsert = 1; // index where new rows should be inserted var rowId = 1; // if rows were selected take the amount of selected cells for the new rows if (GENTICS.Aloha.TableHelper.selectedCells.length > 0) { rowsToInsert = GENTICS.Aloha.TableHelper.selectedCells.length; // get the index where the new rows should be inserted switch (position) { case 'before': if (GENTICS.Aloha.TableHelper.selectedCells[0].length){ rowId = GENTICS.Aloha.TableHelper.selectedCells[0][0].parentNode.rowIndex; } break; case 'after': var lastRow = GENTICS.Aloha.TableHelper.selectedCells.length - 1; if (GENTICS.Aloha.TableHelper.selectedCells[lastRow].length){ rowId = GENTICS.Aloha.TableHelper.selectedCells[lastRow][0].parentNode.rowIndex; } break; } // no rows selected, insert 1 new row before/after the row of the last active cell }else if (typeof GENTICS.Aloha.Table.Cell.lastActiveCell != 'undefined') { rowId = GENTICS.Aloha.Table.Cell.lastActiveCell.obj.context.parentNode.rowIndex; } // the new row index for the created row var newRowIndex = rowId; // if the new rows should be inserted after the last selected row // increase the rowindex will be one more than the actual row if (position == 'after') { newRowIndex += 1; } var rowIdArray = new Array(); for (var j = 0; j < rowsToInsert; j++) { rowIdArray.push(newRowIndex); var insertionRow = jQuery(''); // create the first column, the "select row" column var selectionColumn = jQuery(''); selectionColumn.addClass(this.get('classSelectionColumn')); this.attachRowSelectionEventsToCell(selectionColumn); insertionRow.append(selectionColumn); for (i = 0; i < numCols; i++) { var newCol = jQuery(''); newCol.html('\u00a0'); var cell = new GENTICS.Aloha.Table.Cell(newCol.get(0), GENTICS.Aloha.SimpleTablePlugin.activeTable); cell.activate(); this.cells.push(cell); insertionRow.append(cell.obj); } var currentRow = jQuery(GENTICS.Aloha.SimpleTablePlugin.activeTable.obj.find("tr").get(rowId)); switch (position) { case 'before': currentRow.before(insertionRow); break; case 'after': currentRow.after(insertionRow); break; default: this.warn(this, 'Wrong call of GENTICS.Aloha.Table.prototype.addRow!'); } newRowIndex ++; this.numRows ++; } GENTICS.Aloha.TableHelper.unselectCells(); this.rowsToSelect = rowIdArray; if (highlightNewRows) { this.selectRows(); } // re-attach events of the last cell this.attachLastCellEvents(); } }; /** * Wrapper method to add columns on the right side * * @see GENTICS.Aloha.Table.addColumns * @return void */ GENTICS.Aloha.Table.prototype.addColumnsRight = function () { this.addColumns('right'); }; /** * Wrapper method to add columns on the left side * * @see GENTICS.Aloha.Table.addColumns * @return void */ GENTICS.Aloha.Table.prototype.addColumnsLeft = function() { this.addColumns('left'); }; /** * Inserts new columns into the table. Either on the right or left side. If * columns are selected, the amount of selected columns will be inserted on the * 'right' or 'left' side. If no cells are selected, 1 new column will be * inserted before/after the column of the last active cell. * As well all column-selection events must be bound to the firsts row-cell. * * @param position * could be 'left' or 'right'. defines the position where the new * columns should be inserted * @return void */ GENTICS.Aloha.Table.prototype.addColumns = function (position) { if (typeof GENTICS.Aloha.SimpleTablePlugin.activeTable != 'undefined') { // release listening events of the last cell this.releaseLastCellEvents(); var that = this; // amount of columns to insert var columnsToInsert = 1; // index of the column from where the new columns should be inserted var colId = 1; // if columns are selected, get the column-index of the column on the left/right selected end if (GENTICS.Aloha.TableHelper.selectedCells.length > 0) { columnsToInsert = GENTICS.Aloha.TableHelper.selectedCells[0].length; switch (position) { case 'left': if (GENTICS.Aloha.TableHelper.selectedCells[0].length){ colId = GENTICS.Aloha.TableHelper.selectedCells[0][0].cellIndex; } break; case 'right': var lastColumn = GENTICS.Aloha.TableHelper.selectedCells[0].length - 1; if (GENTICS.Aloha.TableHelper.selectedCells[0].length){ colId = GENTICS.Aloha.TableHelper.selectedCells[0][lastColumn].cellIndex; } break; } // otherwise take the column-index of the last active cell }else if (typeof GENTICS.Aloha.Table.Cell.lastActiveCell != 'undefined') { colId = GENTICS.Aloha.Table.Cell.lastActiveCell.obj.context.cellIndex; } // the new col index for the created column var newColId = colId; var emptyCell = jQuery(''); var rows = this.obj.find('tr'); var colIdArray = new Array(); for (var i = 0; i < rows.length; i++){ var currentColId = newColId; var row = rows[i]; for (var j = 0; j < columnsToInsert; j++) { var cell = emptyCell.clone(); cell.html('\u00a0'); // this is the first row, so make a column-selection cell if (i == 0) { this.attachColumnSelectEventsToCell(cell); }else{ cellObj = new GENTICS.Aloha.Table.Cell(cell.get(0), GENTICS.Aloha.SimpleTablePlugin.activeTable); this.cells.push(cellObj); cellObj.activate(); cell = cellObj.obj; } var insertionColumn = jQuery(jQuery(row).find("td").get(newColId)); switch (position) { case 'left': if (jQuery.inArray(currentColId, colIdArray) < 0) { colIdArray.push(currentColId); } insertionColumn.before(cell); break; case 'right': if (jQuery.inArray((currentColId + 1), colIdArray) < 0) { colIdArray.push(currentColId + 1); } insertionColumn.after(cell); break; } currentColId ++; } } this.numCols += columnsToInsert; GENTICS.Aloha.TableHelper.unselectCells(); this.columnsToSelect = colIdArray; this.selectColumns(); // re-attach events of the last cell this.attachLastCellEvents(); } }; /** * Helper method to set the focus-attribute of the table to true * * @return void */ GENTICS.Aloha.Table.prototype.focus = function() { if (!this.hasFocus) { if (!this.parentEditable.isActive) { this.parentEditable.obj.focus(); } GENTICS.Aloha.SimpleTablePlugin.setFocusedTable(this); this.floatingMenu.call(); } // TODO workaround - fix this. the selection is updated later on by the browser // using setTimeout here is hideous, but a simple execution-time call will fail setTimeout('GENTICS.Aloha.Selection.updateSelection(false, true)', 50); }; /** * Helper method to set the focus-attribute of the table to false * * @return void */ GENTICS.Aloha.Table.prototype.focusOut = function() { if (this.hasFocus) { this.hasFocus = false; GENTICS.Aloha.SimpleTablePlugin.setFocusedTable(undefined); } }; /** * Marks all cells of the specified column as marked (adds a special class) * * @return void */ GENTICS.Aloha.Table.prototype.selectColumns = function() { // get the class which selected cells should have var selectClass = this.get('classCellSelected'); // unselect selected cells GENTICS.Aloha.TableHelper.unselectCells(); GENTICS.Aloha.TableHelper.selectionType = 'col'; this.columnsToSelect.sort(function(a,b){return a - b;}); var rows = this.obj.find("tr").toArray(); // first row is the selection row (dump it => not needed) rows.shift(); var toSelect = new Array(); for (var i = 0; i < rows.length; i++){ var rowCells = rows[i].cells; var selectedCellsInCol = new Array(); for (var j = 0; j < this.columnsToSelect.length; j++) { var colIndex = this.columnsToSelect[j]; var cell = rowCells[colIndex]; toSelect.push(cell); if (i == 0 && j == 0) { if (jQuery.browser.msie) { // setTimeout(jQuery(cell).children('div.GENTICS_Table_Cell_editable').get(0).focus, 5); }else{ jQuery(cell).children('div.GENTICS_Table_Cell_editable').get(0).focus(); } } selectedCellsInCol.push(cell); } GENTICS.Aloha.TableHelper.selectedCells.push(selectedCellsInCol); }; jQuery(toSelect).addClass(selectClass); }; /** * Marks all cells of the specified row as marked (adds a special class) * * @return void */ GENTICS.Aloha.Table.prototype.selectRows = function() { // get the class which selected cells should have var selectClass = this.get('classCellSelected'); // unselect selected cells GENTICS.Aloha.TableHelper.unselectCells(); this.rowsToSelect.sort(function(a,b){return a - b;}); for (var i = 0; i < this.rowsToSelect.length; i++) { var rowId = this.rowsToSelect[i]; var rowCells = jQuery(this.obj.find('tr').get(rowId).cells).toArray(); // shift the first element (which is a selection-helper cell) rowCells.shift(); GENTICS.Aloha.TableHelper.selectedCells.push(rowCells); if (i == 0) { if (jQuery.browser.msie) { // setTimeout(jQuery(rowCells[0]).children('div.GENTICS_Table_Cell_editable').get(0).focus, 5); }else{ jQuery(rowCells[0]).children('div.GENTICS_Table_Cell_editable').get(0).focus(); } } jQuery(rowCells).addClass(this.get('classCellSelected')); } GENTICS.Aloha.TableHelper.selectionType = 'row'; }; /** * Deactivation of a Aloha-Table. Clean up ... remove the wrapping div and the * selection-helper divs * * @return void */ GENTICS.Aloha.Table.prototype.deactivate = function() { this.obj.removeClass(this.get('className')); if (GENTICS.Aloha.trim(this.obj.attr('class')) == '') { this.obj.removeAttr('class'); } this.obj.removeAttr('contenteditable'); this.obj.removeAttr('id'); // unwrap the selectionLeft-div if available if (this.obj.parents('.' + this.get('classTableWrapper')).length){ this.obj.unwrap(); } // remove the selection row this.obj.find('tr.' + this.get('classSelectionRow') + ':first').remove(); // remove the selection column (first column left) var that = this; jQuery.each(this.obj.context.rows, function(){ jQuery(this).children('td.' + that.get('classSelectionColumn')).remove(); }); // remove the "selection class" from all td and th in the table this.obj.find('td, th').removeClass(this.get('classCellSelected')); this.obj.unbind(); // wrap the inner html of the contentEditable div to its outer html for (var i = 0; i < this.cells.length; i++) { var Cell = this.cells[i]; Cell.deactivate(); } }; /** * toString-method for GENTICS.Aloha.Table object * * @return void */ GENTICS.Aloha.Table.prototype.toString = function() { return 'GENTICS.Aloha.Table'; }; /* -- END METHODS -- */ /***************************** +--------------------------+ | GENTICS.Aloha.Table.Cell | +--------------------------+ ******************************/ /** * The constructor for the Cell-Objects takes a DOM td-object, attaches * events, adds an wrapper into the cell and returns the modified td-object as * DOM representation * * @param originalTd * The original td-field which should will be transformed * @param colId * the internal id of the corresponding col (begin with 0) * @param rowId * the internal id of the corresponding row (begin with 0) * @param tableObj * GENTICS.Aloha.Table-Object which contains the cell * * @return the created table-data field as DOM-representation */ GENTICS.Aloha.Table.Cell = function(originalTd, tableObj) { this.obj = jQuery(originalTd); this.tableObj = tableObj; }; /* -- ATTRIBUTES -- */ /** * Reference to the jQuery-representation of the wrapping table * * @see GENTICS.Aloha.Table.Cell.table */ GENTICS.Aloha.Table.Cell.prototype.tableObj = undefined; /** * Reference to the jQuery td-Object of the cell */ GENTICS.Aloha.Table.Cell.prototype.obj = undefined; /** * The jQuery wrapper of the cell */ GENTICS.Aloha.Table.Cell.prototype.wrapper = undefined; /** * Flag if the cell has focus */ GENTICS.Aloha.Table.Cell.prototype.hasFocus = false; /** * The jQuery wrapper of the cell */ GENTICS.Aloha.Table.Cell.activeCell = undefined; /** * The jQuery wrapper of the cell */ GENTICS.Aloha.Table.Cell.lastActiveCell = undefined; /* -- END ATTRIBUTES -- */ /** * Focus method for the contentediable div within a table data-field. The method * requires the event-property Cell as a GENTICS.Aloha.Table.Cell object. If the * Cell wasn't activated yet it does all relevant actions to activate the cell. * * @param e * the jquery event object * @return void */ GENTICS.Aloha.Table.Cell.prototype.editableFocus = function(e) { // only do activation stuff if the cell don't has the focus if (!this.hasFocus) { // set an internal flag to focus the table this.tableObj.focus(); // set the clicked cell active as the active cell GENTICS.Aloha.Table.Cell.activeCell = this; // set the clicked cell active as the last active cell (the difference // to activeCell is that lastActiveCell won't be reset on blur) GENTICS.Aloha.Table.Cell.lastActiveCell = this; // add an active-class this.obj.addClass('GENTICS_Table_Cell_active'); // set the focus flag this.hasFocus = true; // select the whole content in the table-data field this.selectAll(this.wrapper.get(0)); } }; /** * Blur event for the contenteditable div within a table-data field. The method * requires the event-property Cell as a GENTICS.Aloha.Table.Cell object. It * sets the hasFocus flag of the cell to false and removes the "active" * css-class. * * @param jqEvent * the jquery event object * @return void */ GENTICS.Aloha.Table.Cell.prototype.editableBlur = function(jqEvent){ // no active cell GENTICS.Aloha.Table.Cell.activeCell = undefined; // reset the focus of the cell this.hasFocus = false; // remove "active class" this.obj.removeClass('GENTICS_Table_Cell_active'); }; GENTICS.Aloha.Table.Cell.prototype.activate = function() { // create the editable wrapper for the cells // var wrapper = jQuery('
'); // wrapper.attr('contentEditable', 'true'); // wrapper.addClass('GENTICS_Table_Cell_editable'); // // var that = this; // // attach events to the editable div-object // wrapper.bind('focus', function(jqEvent) { that.editableFocus(jqEvent); }); // wrapper.bind('mousedown', function(jqEvent) { that.editableMouseDown(jqEvent); }); // wrapper.bind('blur', function(jqEvent) { that.editableBlur(jqEvent); }); // wrapper.bind('keyup', function(jqEvent) { that.editableKeyUp(jqEvent); }); // wrapper.bind('keydown', function(jqEvent) { that.editableKeyDown(jqEvent); }); // // this.obj.bind('mousedown', function(jqEvent) { // setTimeout(function() { // that.wrapper.trigger('focus'); // }, 1); // // // unselect cells // GENTICS.Aloha.TableHelper.unselectCells(); // // jqEvent.stopPropagation(); // }); // this.obj.get(0).onselectstart = function (jqEvent) { return false; }; // // // wrap the created div into the contents of the cell // this.obj.wrapInner(wrapper); // // // set contenteditable wrapper div // this.wrapper = this.obj.children(); // this.wrapper.get(0).onselectstart = function() { // window.event.cancelBubble = true; // }; return this; }; /** * The deactivate method removes the contenteditable helper div within the * table-data field and wraps the innerHtml to the outerHTML * * @return void */ GENTICS.Aloha.Table.Cell.prototype.deactivate = function() { var wrapper = this.obj.children('.GENTICS_Table_Cell_editable'); if (wrapper.length) { // get the inner html of the contenteditable div var innerHtml = wrapper.html(); // remove the contenteditable div and its attached events wrapper.unbind(); wrapper.remove(); // remove the click event of the this.obj.unbind('click'); if (GENTICS.Aloha.trim(this.obj.attr('class')) == '') { this.obj.removeAttr('class'); } // set the inner html of the contenteditable div as html for the table-data // field this.obj.html(innerHtml); } }; /** * Native toString-method * * @return string name of the namespace */ GENTICS.Aloha.Table.Cell.prototype.toString = function() { return 'GENTICS.Aloha.Table.Cell'; }; /** * Selects all inner-contens of an contentEditable-object * * @param editableNode dom-representation of the editable node (div-element) * @return void */ GENTICS.Aloha.Table.Cell.prototype.selectAll = function(editableNode) { var e = (editableNode.jquery) ? editableNode.get(0) : editableNode; // Not IE if (!jQuery.browser.msie) { var s = window.getSelection(); // Safari if (s.setBaseAndExtent) { s.setBaseAndExtent(e, 0, e, e.innerText.length - 1); } // Firefox and Opera else { // workaround for bug # 42885 if (window.opera && e.innerHTML.substring(e.innerHTML.length - 4) == '
') { e.innerHTML = e.innerHTML + ' '; } var r = document.createRange(); r.selectNodeContents(e); s.removeAllRanges(); s.addRange(r); } } // Some older browsers else if (document.getSelection) { var s = document.getSelection(); var r = document.createRange(); r.selectNodeContents(e); s.removeAllRanges(); s.addRange(r); } // IE else if (document.selection) { var r = document.body.createTextRange(); r.moveToElementText(e); r.select(); } GENTICS.Aloha.Selection.updateSelection(editableNode); }; /** * The mouse-down event for the editable-div in the thd-field. Unselect all * cells when clicking on the editable-div. * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.Cell.prototype.editableMouseDown = function(jqEvent) { // deselect all highlighted cells registred in the TableHelper object GENTICS.Aloha.TableHelper.unselectCells(); if (this.tableObj.hasFocus) { jqEvent.stopPropagation(); } }; /** * The key-up event for the editable-div in the td-field. Just check if the div * is empty and insert an   * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.Cell.prototype.editableKeyUp = function(jqEvent) { this.checkForEmptyEvent(jqEvent); }; /** * The key-down event for the ediable-div in the td-field. Check if the the div * is empty and insert an  . Furthermore if cells are selected, unselect * them. * * @param jqEvent * the jquery-event object * @return void */ GENTICS.Aloha.Table.Cell.prototype.editableKeyDown = function(jqEvent) { this.checkForEmptyEvent(jqEvent); if (!jqEvent.ctrlKey && !jqEvent.shiftKey) { if (GENTICS.Aloha.TableHelper.selectedCells.length > 0 && GENTICS.Aloha.TableHelper.selectedCells[0].length > 0) { GENTICS.Aloha.TableHelper.selectedCells[0][0].firstChild.focus(); GENTICS.Aloha.TableHelper.unselectCells(); jqEvent.stopPropagation(); } }else if(jqEvent.shiftKey && GENTICS.Aloha.TableHelper.selectedCells.length > 0){ var KEYCODE_ARROWLEFT = 37; var KEYCODE_ARROWUP = 38; var KEYCODE_ARROWRIGHT = 39; var KEYCODE_ARROWDOWN = 40; switch (GENTICS.Aloha.TableHelper.selectionType) { case 'row': switch(jqEvent.keyCode) { case KEYCODE_ARROWUP: var firstSelectedRow = GENTICS.Aloha.TableHelper.selectedCells[0][0].parentNode.rowIndex; if (firstSelectedRow > 1) { this.tableObj.rowsToSelect.push(firstSelectedRow - 1); } break; case KEYCODE_ARROWDOWN: var lastRowIndex = GENTICS.Aloha.TableHelper.selectedCells.length - 1; var lastSelectedRow = GENTICS.Aloha.TableHelper.selectedCells[lastRowIndex][0].parentNode.rowIndex; if (lastSelectedRow < this.tableObj.numRows) { this.tableObj.rowsToSelect.push(lastSelectedRow + 1); } break; } this.tableObj.selectRows(); break; case 'col': switch(jqEvent.keyCode) { case KEYCODE_ARROWLEFT: var firstColSelected = GENTICS.Aloha.TableHelper.selectedCells[0][0].cellIndex; if (firstColSelected > 1) { this.tableObj.columnsToSelect.push(firstColSelected - 1); } break; case KEYCODE_ARROWRIGHT: var lastColIndex = GENTICS.Aloha.TableHelper.selectedCells[0].length - 1; var lastColSelected = GENTICS.Aloha.TableHelper.selectedCells[0][lastColIndex].cellIndex; if (lastColSelected < this.tableObj.numCols) { this.tableObj.columnsToSelect.push(lastColSelected + 1); } break; } this.tableObj.selectColumns(); break; } jqEvent.stopPropagation(); jqEvent.preventDefault(); return false; } }; /** * The custom keyup event for a table-cell Checks if the cell is empty and * inserts a space (\u00a0) * * @param e * the event object which is given by jquery * @return void */ GENTICS.Aloha.Table.Cell.prototype.checkForEmptyEvent = function(jqEvent) { // get the editable contents var text = this.wrapper.text(); // if empty insert a blank space and blur and focus the wrapper if (text == ''){ this.wrapper.text('\u00a0'); this.wrapper.get(0).blur(); this.wrapper.get(0).focus(); } }; /* -- END METHODS -- */ /************************************** +---------------------------------+ | GENTICS.Aloha.Table.CreateLayer | +---------------------------------+ ***************************************/ /** * Dummy initialize of the CreateLayer object */ GENTICS.Aloha.Table.CreateLayer = function(){}; /* -- ATTRIBUTES -- */ /** * Internal configuration of the create-table panel */ GENTICS.Aloha.Table.CreateLayer.prototype.parameters = { elemId: 'GENTICS_Aloha_SimpleTable_createLayer', // id of the create-table panel className: 'GENTICS_Table_Createdialog', // class-name of the create-table panel numX: 10, // Number of cols in the create-layer numY: 10, // Number of rows in the create-layer vertically layer: undefined, // Attribute holding the create-layer target: undefined // the clicktarget which was clicked on (mostly the button of the floatingmenu) }; /** * The configuration-object for the implementer of the plugin. All keys of * the "parameters" object could be overwritten within this object and will * simply be used instead. */ GENTICS.Aloha.Table.CreateLayer.prototype.config = new Object(); /** * Flag wether the CreateLayer is currently visble or not */ GENTICS.Aloha.Table.CreateLayer.prototype.visible = false; /* -- END ATTRIBUTES -- */ /* -- METHODS -- */ /** * This function checks if there is an create-table-layer. If no layer exists, it creates one and puts it into the configuration. * If the layer was already created it sets the position of the panel and shows it. * * @return void */ GENTICS.Aloha.Table.CreateLayer.prototype.show = function(){ var layer = this.get('layer'); // create the panel if the layer doesn't exist if (layer == null) { this.create(); }else { // or reposition, cleanup and show the layer this.setPosition(layer); layer.find('td').removeClass('hover'); layer.show(); } this.visible = true; }; /** * Creates the div-layer which holds a table with the given number of rows and cols. * It sets click and mouseover-events to the table data fields * * @return void */ GENTICS.Aloha.Table.CreateLayer.prototype.create = function () { var that = this; var layer = jQuery('
'); layer.id = this.get('elemId'); layer.addClass(this.get('className')); var table = jQuery('
'); table.css('width', (this.get('numX') + 6) * 15); var tr; var td; for (var i = 0; i < this.get('numY'); i++) { tr = jQuery(''); for (var j = 0; j < this.get('numX'); j++) { td = jQuery('\u00a0'); if (i == 0 && j == 0) { td.addClass('hover'); } td.bind('mouseover', {rowId: i, colId: j}, function(e) { that.handleMouseOver(e, table); }); td.bind('click', {rowId: i, colId: j}, function(e){ var rows = e.data.rowId + 1; var cols = e.data.colId + 1; GENTICS.Aloha.SimpleTablePlugin.createTable(cols, rows); that.hide(); }); tr.append(td); } table.append(tr); } layer.append(table); // set attributes this.set('layer', layer); this.setPosition(); // stop bubbling the click on the create-dialog up to the body event layer.bind('click', function(e) { e.stopPropagation(); }); // append layer to body and // hide the create layer if user clicks anywhere in the body jQuery('body').append(layer).bind('click', function(e) { if (e.target != that.get('target') && that.visible) { that.hide(); } }); }; /** * handles the mose over state for a cell * @param e event object * @param table the aeffected table * @return void */ GENTICS.Aloha.Table.CreateLayer.prototype.handleMouseOver = function(e, table) { var rowId = e.data.rowId; var colId = e.data.colId; var innerRows = table.find('tr'); for (var n = 0; n <= innerRows.length; n++) { var innerCells = jQuery(innerRows[n]).find('td'); for (var k = 0; k <= innerCells.length; k++) { if (n <= rowId && k <= colId) { jQuery(innerCells[k]).addClass('hover'); } else { jQuery(innerCells[k]).removeClass('hover'); } } } }; /** * Sets the "left" and "top" style-attributes according to the clicked target-button * * @return void */ GENTICS.Aloha.Table.CreateLayer.prototype.setPosition = function() { var targetObj = jQuery(this.get('target')); var pos = targetObj.offset(); this.get('layer').css('left', pos.left + 'px'); this.get('layer').css('top', (pos.top + targetObj.height()) + 'px'); }; /** * Hides the create-table panel width the jQuery-method hide() * * @see jQuery().hide() * @return void */ GENTICS.Aloha.Table.CreateLayer.prototype.hide = function() { this.get('layer').hide(); this.visible = false; }; /** * 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 */ GENTICS.Aloha.Table.CreateLayer.prototype.get = function(property) { // return param from the config if (this.config[property]) { return this.config[property]; } // if config-param was not found return param from the parameters-object 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 */ GENTICS.Aloha.Table.CreateLayer.prototype.set = function (key, value) { // if the key already exists in the config-object, set it to the config-object if (this.config[key]) { this.config[key] = value; // otherwise "add" it to the parameters-object }else{ this.parameters[key] = value; } }; /* -- END METHODS -- */ /******************************** +---------------------------+ | GENTICS.Aloha.TableHelper | +---------------------------+ *********************************/ /** * The TableHelper object is a helper-object which consists of static/global attributes and functions */ GENTICS.Aloha.TableHelper = function(){}; /* -- ATTRIBUTES -- */ /** * Gives the type of the cell-selection * possible values are "row" or "col" */ GENTICS.Aloha.TableHelper.prototype.selectionType = undefined; /** * Holds all currently selected table cells as an array of DOM "td" representations */ GENTICS.Aloha.TableHelper.prototype.selectedCells = new Array(); /* -- END ATTRIBUTES -- */ /* -- METHODS -- */ /** * This method removes the "selected" class from all selected cells * * @return void */ GENTICS.Aloha.TableHelper.prototype.unselectCells = function(){ if (this.selectedCells.length > 0) { for (var i = 0; i < this.selectedCells.length; i++) { jQuery(this.selectedCells[i]).removeClass(GENTICS.Aloha.SimpleTablePlugin.get('classCellSelected')); } this.selectedCells = new Array(); this.selectionType = undefined; } }; GENTICS.Aloha.TableHelper.prototype.getNewTableID = function() { var idPrefix = 'GENTICS_Table_'; var factor = 1000000; for (this.tableCounter; true; this.tableCounter ++) { var id = idPrefix + (Math.ceil(Math.random() * factor)); // fill up the id with zeros for (var j = id.length; j < idPrefix.length + factor.toString().length; j++) { id += '0'; } if (!jQuery('#' + id).length) { return id; } } }; /* -- END METHODS -- */ /** * Initialize a new Object from the same object to get access to the prototype methods */ GENTICS.Aloha.TableHelper = new GENTICS.Aloha.TableHelper();