Skip to content

Instantly share code, notes, and snippets.

@iguigova
Created September 6, 2011 17:30
Show Gist options
  • Save iguigova/1198324 to your computer and use it in GitHub Desktop.
Save iguigova/1198324 to your computer and use it in GitHub Desktop.
ExtJS HtmlEditor extension to work with tables
/*!
* Ext JS Library 3.2.1
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
Ext.ns('Ext.ux.form');
/**
* @class Ext.ux.form.HtmlTableEditor
* @extends Ext.form.HtmlTableEditor
* Creates an html editor with table management capability
* @xtype htmltableeditor
* @doc: http://dev.sencha.com/deploy/ext-3.3.1/docs/
* @src: http://dev.sencha.com/deploy/ext-3.3.1/docs/source/HtmlEditor.html#cls-Ext.form.HtmlEditor
*/
Ext.ux.form.HtmlTableEditor = Ext.extend(Ext.ux.form.HtmlLintEditor /*Ext.form.HtmlEditor */, {
initComponent : function(){
Ext.ux.form.HtmlTableEditor.superclass.initComponent.call(this);
this.buttonTips.addtable = {
title: 'Add Table',
text: 'Add a new table.',
cls: 'x-html-editor-tip'
};
this.buttonTips.addrow = {
title: 'Add Row',
text: 'Add a new row to the table.',
cls: 'x-html-editor-tip'
};
this.buttonTips.deleterow = {
title: 'Delete Row',
text: 'Delete a row from the table.',
cls: 'x-html-editor-tip'
};
this.buttonTips.addcol = {
title: 'Add Column',
text: 'Add a new column to the table.',
cls: 'x-html-editor-tip'
};
this.buttonTips.deletecol = {
title: 'Delete Column',
text: 'Delete a column from the table.',
cls: 'x-html-editor-tip'
};
this.on('initialize', function(editor){
var cssLink = document.createElement("link")
cssLink.href = editor.cssHref || '/static/css/table.css';
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
editor.getEditorBody().appendChild(cssLink);
});
},
createToolbar : function(editor){
Ext.ux.form.HtmlTableEditor.superclass.createToolbar.call(this, editor);
var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
function btn(id, toggle, handler){
return {
itemId : id,
cls : 'x-btn-icon',
iconCls: 'x-edit-'+id,
enableToggle: toggle !== false,
scope: editor,
handler:handler||editor.relayBtnCmd,
//clickEvent:'mousedown',
tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
overflowText: editor.buttonTips[id].title || undefined,
tabIndex:-1
};
}
editor.tb.add(
'->',
btn('addtable', false, editor.addTable),
btn('addrow', false, editor.addRow),
btn('deleterow', false, editor.deleteRow),
btn('addcol', false, editor.addCol),
btn('deletecol', false, editor.deleteCol)
);
editor.tb.doLayout();
},
select: function(selector){
if (!editor.iframe) return;
return Ext.fly(editor.getEditorBody()).select(selector);
},
getTableSelection: function(){
var selection = this.getWin().getSelection();
if (!selection){
// TODO: select something
return;
}
var node = Ext.fly(selection.focusNode);
var cell = (node.is('td')) ? node : node.parent('td');
if (cell) {
var row = cell.parent('tr');
var table = row.parent('table');
return {cell: cell.dom, row: row.dom, table: table.dom};
}
},
promptTableSelection: function(title, rowOrCell){
if ((rowOrCell != 'row') && (rowOrCell != 'cell')) return;
var tableSelection = this.getTableSelection();
if (tableSelection){
var selectedIndex = tableSelection[rowOrCell][rowOrCell + 'Index'];
var newIndex = parseInt(prompt(title, selectedIndex));
if (!isNaN(newIndex)){
var maxIndex = (rowOrCell == 'row') ? tableSelection.table.rows.length : tableSelection.row.cells.length;
tableSelection.newIndex = Math.min(newIndex, maxIndex);
return tableSelection;
}
}
},
resetCell: function(row, idx, insertOrDelete){
if ((insertOrDelete != 'insert') && (insertOrDelete != 'delete')) return;
var cell = row[insertOrDelete + 'Cell'](idx);
if (cell){
cell.innerHTML = ' ';
}
},
resetCol: function(insertOrDelete){
if ((insertOrDelete != 'insert') && (insertOrDelete != 'delete')) return;
var tableSelection = this.promptTableSelection('Choose column position: ', 'cell');
if (tableSelection){
var rows = tableSelection.table.rows;
var cellIndex = tableSelection.cell.cellIndex;
for (var i = 0, len = rows.length; i < len; i++){
this.resetCell(rows[i], cellIndex, insertOrDelete);
}
}
},
addCol: function(){
this.resetCol('insert');
},
deleteCol: function(){
this.resetCol('delete');
},
resetRow: function(insertOrDelete){
if ((insertOrDelete != 'insert') && (insertOrDelete != 'delete')) return;
var tableSelection = this.promptTableSelection('Choose row position: ', 'row');
if (tableSelection){
var row = tableSelection.table[insertOrDelete + 'Row'](tableSelection.newIndex);
if (insertOrDelete == 'insert'){
for (var i = 0, len = tableSelection.row.cells.length; i < len; i++){
this.resetCell(row, -1, insertOrDelete);
}
}
}
},
addRow: function(){
this.resetRow('insert');
},
deleteRow: function(){
this.resetRow('delete');
},
addTable : function() {
// src: http://www-archive.mozilla.org/editor/midasdemo/
var doc = this.getDoc();
rowstext = prompt("enter rows");
colstext = prompt("enter cols");
rows = parseInt(rowstext);
cols = parseInt(colstext);
if ((rows > 0) && (cols > 0)) {
var table = doc.createElement("table");
table.className = 'template';
table.setAttribute("border", "0");
table.setAttribute("cellpadding", "0");
table.setAttribute("cellspacing", "0");
var tbody = doc.createElement("tbody");
for (var i=0; i < rows; i++) {
var tr = doc.createElement("tr");
for (var j=0; j < cols; j++) {
var td = doc.createElement("td");
var br = doc.createElement("br");
td.appendChild(br);
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
this.insertNodeAtSelection(this.getWin(), table);
}
},
insertNodeAtSelection: function(win, insertNode)
{
// get current selection
var sel = win.getSelection();
// get the first range of the selection
// (there's almost always only one range)
var range = sel.getRangeAt(0);
// deselect everything
sel.removeAllRanges();
// remove content of current selection from document
range.deleteContents();
// get location of current selection
var container = range.startContainer;
var pos = range.startOffset;
// make a new range for the new selection
range = document.createRange();
if (container.nodeType == 3 && insertNode.nodeType == 3) {
// if we insert text in a textnode, do optimized insertion
container.insertData(pos, insertNode.nodeValue);
// put cursor after inserted text
range.setEnd(container, pos+insertNode.length);
range.setStart(container, pos+insertNode.length);
} else {
var afterNode;
if (container.nodeType==3) {
// when inserting into a textnode
// we create 2 new textnodes
// and put the insertNode in between
var textNode = container;
container = textNode.parentNode;
var text = textNode.nodeValue;
// text before the split
var textBefore = text.substr(0,pos);
// text after the split
var textAfter = text.substr(pos);
var beforeNode = document.createTextNode(textBefore);
afterNode = document.createTextNode(textAfter);
// insert the 3 new nodes before the old one
container.insertBefore(afterNode, textNode);
container.insertBefore(insertNode, afterNode);
container.insertBefore(beforeNode, insertNode);
// remove the old node
container.removeChild(textNode);
} else {
// else simply insert the node
afterNode = container.childNodes[pos];
container.insertBefore(insertNode, afterNode);
}
range.setEnd(afterNode, 0);
range.setStart(afterNode, 0);
}
sel.addRange(range);
}
});
Ext.reg('htmltableeditor', Ext.ux.form.HtmlTableEditor);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment