wiki-archive/twiki/pub/TWiki/KupuContrib/_kupu/silva/kupusilvatools.js

2627 lines
100 KiB
JavaScript

/*****************************************************************************
*
* Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
*
* This software is distributed under the terms of the Kupu
* License. See LICENSE.txt for license text. For a list of Kupu
* Contributors see CREDITS.txt.
*
*****************************************************************************/
// $Id: kupusilvatools.js 19933 2005-11-16 14:56:53Z guido $
// a mapping from namespace to field names, here you can configure which
// metadata fields should be editable with the property editor (needs to
// be moved to somewhere in Silva or something?)
EDITABLE_METADATA = {
'http://infrae.com/namespaces/metadata/silva-news':
[['subjects', 'checkbox', 1, 'Subjects'],
['target_audiences', 'checkbox', 1, 'Target audiences'],
['start_datetime', 'datetime', 1, 'Start date/time'],
['end_datetime', 'datetime', 0, 'End date/time'],
['location', 'text', 0, 'Location']
]
}
function SilvaLinkTool() {
/* redefine the contextmenu elements */
};
SilvaLinkTool.prototype = new LinkTool;
SilvaLinkTool.prototype.updateLink = function (linkel, url, type,
name, target, title) {
if (type && type == 'anchor') {
linkel.removeAttribute('href');
linkel.setAttribute('name', name);
} else {
linkel.href = url;
linkel.setAttribute('silva_href', url);
if (linkel.innerHTML == "") {
var doc = this.editor.getInnerDocument();
linkel.appendChild(doc.createTextNode(title || url));
}
if (title) {
linkel.title = title;
} else {
linkel.removeAttribute('title');
}
if (target && target != '') {
linkel.setAttribute('target', target);
}
else {
linkel.removeAttribute('target');
};
linkel.style.color = this.linkcolor;
};
};
SilvaLinkTool.prototype.createContextMenuElements = function(selNode, event) {
/* create the 'Create link' or 'Remove link' menu elements */
var ret = new Array();
var link = this.editor.getNearestParentOfType(selNode, 'a');
if (link) {
ret.push(new ContextMenuElement('Delete link', this.deleteLink, this));
} else {
ret.push(new ContextMenuElement('Create link', getLink, this));
};
return ret;
};
function SilvaLinkToolBox(inputid, targetselectid, targetinputid,
addbuttonid, updatebuttonid, delbuttonid,
toolboxid, plainclass, activeclass) {
/* create and edit links */
this.input = getFromSelector(inputid);
this.targetselect = getFromSelector(targetselectid);
this.targetinput = getFromSelector(targetinputid);
this.addbutton = getFromSelector(addbuttonid);
this.updatebutton = getFromSelector(updatebuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolboxel = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
};
SilvaLinkToolBox.prototype = new LinkToolBox;
SilvaLinkToolBox.prototype.initialize = function(tool, editor) {
this.tool = tool;
this.editor = editor;
addEventHandler(this.targetselect, 'change', this.selectTargetHandler, this);
addEventHandler(this.targetinput, 'change', this.selectTargetHandler, this);
addEventHandler(this.addbutton, 'click', this.createLinkHandler, this);
addEventHandler(this.updatebutton, 'click', this.createLinkHandler, this);
addEventHandler(this.delbutton, 'click', this.tool.deleteLink, this);
this.targetinput.style.display = 'none';
this.editor.logMessage('Link tool initialized');
};
SilvaLinkToolBox.prototype.selectTargetHandler = function(event) {
var select = this.targetselect;
var input = this.targetinput;
var selvalue = select.options[select.selectedIndex].value;
if (selvalue != 'input') {
input.style.display = 'none';
} else {
input.style.display = 'inline';
};
};
SilvaLinkToolBox.prototype.createLinkHandler = function(event) {
var url = this.input.value;
var target = this.targetselect.options[
this.targetselect.selectedIndex].value;
if (target == 'input') {
target = this.targetinput.value;
};
this.tool.createLink(url, 'link', null, target);
};
SilvaLinkToolBox.prototype.updateState = function(selNode, event) {
var currnode = selNode;
var link = false;
var href = '';
while (currnode) {
if (currnode.nodeName.toLowerCase() == 'a') {
href = currnode.getAttribute('silva_href');
if (!href) {
href = currnode.getAttribute('href');
};
if (href) {
if (this.toolboxel) {
this.toolboxel.className = this.activeclass;
if (this.toolboxel.open_handler) {
this.toolboxel.open_handler();
};
};
this.input.value = href;
var target = currnode.getAttribute('target');
if (!target) {
this.targetselect.selectedIndex = 0;
this.targetinput.style.display = 'none';
} else {
var target_found = false;
for (var i=0; i < this.targetselect.options.length; i++) {
var option = this.targetselect.options[i];
if (option.value == target) {
this.targetselect.selectedIndex = i;
target_found = true;
break;
};
};
if (target_found) {
this.targetinput.value = '';
this.targetinput.style.display = 'none';
} else {
// XXX this is pretty hard-coded...
this.targetselect.selectedIndex =
this.targetselect.options.length - 1;
this.targetinput.value = target;
this.targetinput.style.display = 'inline';
};
};
this.addbutton.style.display = 'none';
this.updatebutton.style.display = 'inline';
this.delbutton.style.display = 'inline';
return;
};
};
currnode = currnode.parentNode;
};
this.targetselect.selectedIndex = 0;
this.targetinput.value = '';
this.targetinput.style.display = 'none';
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
this.addbutton.style.display = 'inline';
if (this.toolboxel) {
this.toolboxel.className = this.plainclass;
};
this.input.value = '';
};
function SilvaImageTool(editelid, urlinputid, targetselectid, targetinputid,
hireslinkradioid, linklinkradioid, linkinputid,
alignselectid, titleinputid, toolboxid, plainclass,
activeclass) {
/* Silva specific image tool */
this.editel = getFromSelector(editelid);
this.urlinput = getFromSelector(urlinputid);
this.targetselect = getFromSelector(targetselectid);
this.targetinput = getFromSelector(targetinputid);
this.hireslinkradio = getFromSelector(hireslinkradioid);
this.linklinkradio = getFromSelector(linklinkradioid);
this.linkinput = getFromSelector(linkinputid);
this.alignselect = getFromSelector(alignselectid);
this.titleinput = getFromSelector(titleinputid);
this.toolboxel = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
}
SilvaImageTool.prototype = new ImageTool;
SilvaImageTool.prototype.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.targetselect, 'change', this.setTarget, this);
addEventHandler(this.targetselect, 'change', this.selectTargetHandler, this);
addEventHandler(this.targetinput, 'change', this.setTarget, this);
addEventHandler(this.urlinput, 'change', this.setSrc, this);
addEventHandler(this.hireslinkradio, 'click', this.setHires, this);
addEventHandler(this.linklinkradio, 'click', this.setNoHires, this);
addEventHandler(this.linkinput, 'keypress', this.setLink, this);
addEventHandler(this.linkinput, 'change', this.setLink, this);
addEventHandler(this.alignselect, 'change', this.setAlign, this);
addEventHandler(this.titleinput, 'change', this.setTitle, this);
this.targetinput.style.display = 'none';
this.editor.logMessage('Image tool initialized');
};
SilvaImageTool.prototype.createContextMenuElements = function(selNode, event) {
return new Array(new ContextMenuElement('Create image', getImage, this));
};
SilvaImageTool.prototype.selectTargetHandler = function(event) {
var select = this.targetselect;
var input = this.targetinput;
var selvalue = select.options[select.selectedIndex].value;
if (selvalue != 'input') {
input.style.display = 'none';
} else {
input.style.display = 'inline';
};
};
SilvaImageTool.prototype.updateState = function(selNode, event) {
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (image) {
this.editel.style.display = 'block';
var src = image.getAttribute('silva_src');
if (!src) {
src = image.getAttribute('src');
};
this.urlinput.value = src;
var target = image.getAttribute('target');
if (!target) {
this.targetselect.selectedIndex = 0;
this.targetinput.style.display = 'none';
} else {
var target_found = false;
for (var i=0; i < this.targetselect.options.length; i++) {
var option = this.targetselect.options[i];
if (option.value == target) {
this.targetselect.selectedIndex = i;
target_found = true;
break;
};
};
if (target_found) {
this.targetinput.value = '';
this.targetinput.style.display = 'none';
} else {
this.targetselect.selectedIndex = this.targetselect.options.length - 1;
this.targetinput.value = target;
this.targetinput.style.display = 'inline';
};
};
var hires = image.getAttribute('link_to_hires') == '1';
if (!hires) {
var link = image.getAttribute('link');
this.linklinkradio.checked = 'selected';
this.linkinput.value = link == null ? '' : link;
} else {
this.hireslinkradio.checked = 'checked';
this.linkinput.value = '';
};
if (this.toolboxel) {
if (this.toolboxel.open_handler) {
this.toolboxel.open_handler();
};
this.toolboxel.className = this.activeclass;
};
var align = image.getAttribute('alignment');
if (!align) {
align = 'left';
};
var title = image.getAttribute('title');
if (!title) {
title = '';
};
this.titleinput.value = title;
selectSelectItem(this.alignselect, align);
} else {
this.editel.style.display = 'none';
this.urlinput.value = '';
this.titleinput.value = '';
if (this.toolboxel) {
this.toolboxel.className = this.plainclass;
};
this.targetselect.selectedIndex = 0;
this.targetinput.value = '';
this.targetinput.style.display = 'none';
};
};
SilvaImageTool.prototype.createImage = function(url, alttext, imgclass) {
/* create an image */
var img = this.editor.getInnerDocument().createElement('img');
img.src = url;
img.setAttribute('silva_src', url);
img.removeAttribute('height');
img.removeAttribute('width');
if (alttext) {
img.alt = alttext;
};
if (imgclass) {
img.className = imgclass;
};
img = this.editor.insertNodeAtSelection(img, 1);
this.editor.logMessage(_('Image inserted'));
this.editor.updateState();
return img;
};
SilvaImageTool.prototype.setTarget = function() {
var target = this.targetselect.options[this.targetselect.selectedIndex].value;
if (target == 'input') {
target = this.targetinput.value;
};
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('No image selected!', 1);
};
image.setAttribute('target', target);
};
SilvaImageTool.prototype.setSrc = function() {
var selNode = this.editor.getSelectedNode();
var img = this.editor.getNearestParentOfType(selNode, 'img');
if (!img) {
this.editor.logMessage('Not inside an image!', 1);
};
var src = this.urlinput.value;
img.setAttribute('src', src);
img.setAttribute('silva_src', src);
this.editor.logMessage('Image updated');
};
SilvaImageTool.prototype.setHires = function() {
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('No image selected!', 1);
return;
};
image.setAttribute('link_to_hires', '1');
image.removeAttribute('link');
this.linkinput.value = '';
};
SilvaImageTool.prototype.setNoHires = function() {
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('Not inside an image!', 1);
return;
};
var link = this.linkinput.value;
image.setAttribute('link_to_hires', '0');
image.setAttribute('link', link);
this.linklinkradio.setAttribute('selected', 'selected');
};
SilvaImageTool.prototype.setLink = function() {
var link = this.linkinput.value;
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('No image selected!', 1);
return;
};
image.setAttribute('link', link);
image.setAttribute('link_to_hires', '0');
};
SilvaImageTool.prototype.setTitle = function() {
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('No image selected!', 1);
return;
};
var title = this.titleinput.value;
image.setAttribute('title', title);
};
SilvaImageTool.prototype.setAlign = function() {
var selNode = this.editor.getSelectedNode();
var image = this.editor.getNearestParentOfType(selNode, 'img');
if (!image) {
this.editor.logMessage('Not inside an image', 1);
return;
};
var align = this.alignselect.options[this.alignselect.selectedIndex].value;
image.setAttribute('alignment', align);
};
function SilvaTableTool() {
/* Silva specific table functionality
overrides most of the table functionality, required because Silva requires
a completely different format for tables
*/
this.createTable = function(rows, cols, makeHeader, tableclass) {
/* add a Silvs specific table, with an (optional) header with colspan */
var doc = this.editor.getInnerDocument();
var table = doc.createElement('table');
table.style.width = "100%";
table.className = tableclass;
var tbody = doc.createElement('tbody');
if (makeHeader) {
this._addRowHelper(doc, tbody, 'th', -1, cols);
}
for (var i=0; i < rows; i++) {
this._addRowHelper(doc, tbody, 'td', -1, cols);
}
table.appendChild(tbody);
var iterator = new NodeIterator(table);
var currnode = null;
var contentcell = null;
while (currnode = iterator.next()) {
var nodename = currnode.nodeName.toLowerCase();
if (nodename == 'td' || nodename == 'th') {
contentcell = currnode;
break;
};
};
var selection = this.editor.getSelection();
var docfrag = selection.cloneContents();
var setcursoratend = false;
if (contentcell && docfrag.hasChildNodes()) {
while (contentcell.hasChildNodes()) {
contentcell.removeChild(contentcell.firstChild);
};
while (docfrag.hasChildNodes()) {
contentcell.appendChild(docfrag.firstChild);
setcursoratend = true;
};
};
this.editor.insertNodeAtSelection(table);
this._setTableCellHandlers(table);
this.editor.logMessage('Table added');
};
this.addTableRow = function() {
/* add a table row or header */
var currnode = this.editor.getSelectedNode();
var doc = this.editor.getInnerDocument();
var tbody = this.editor.getNearestParentOfType(currnode, 'tbody');
if (!tbody) {
this.editor.logMessage('No table found!', 1);
return;
}
var cols = this._countCells(tbody);
var currrow = this.editor.getNearestParentOfType(currnode, 'tr');
if (!currrow) {
this.editor.logMessage('Not inside a row!', 1);
return;
};
var index = this._getRowIndex(currrow) + 1;
// should check what to add as well
this._addRowHelper(doc, tbody, 'td', index, cols);
this.editor.logMessage('Table row added');
};
this.delTableRow = function() {
/* delete a table row or header */
var currnode = this.editor.getSelectedNode();
var currtr = this.editor.getNearestParentOfType(currnode, 'tr');
if (!currtr) {
this.editor.logMessage('Not inside a row!', 1);
return;
};
currtr.parentNode.removeChild(currtr);
this.editor.logMessage('Table row removed');
};
this.addTableColumn = function() {
/* add a table column */
var currnode = this.editor.getSelectedNode();
var doc = this.editor.getInnerDocument();
var body = this.editor.getNearestParentOfType(currnode, 'tbody');
if (!body) {
this.editor.logMessage('Not inside a table!');
return;
};
var currcell = this.editor.getNearestParentOfType(currnode, 'td');
if (!currcell) {
var currcell = this.editor.getNearestParentOfType(currnode, 'th');
if (!currcell) {
this.editor.logMessage('Not inside a row!', 1);
return;
} else {
var index = -1;
};
} else {
var index = this._getColIndex(currcell) + 1;
};
var numcells = this._countCells(body);
this._addColHelper(doc, body, index, numcells);
this.editor.logMessage('Column added');
};
this.delTableColumn = function() {
/* delete a column */
var currnode = this.editor.getSelectedNode();
var body = this.editor.getNearestParentOfType(currnode, 'tbody');
if (!body) {
this.editor.logMessage('Not inside a table body!', 1);
return;
}
var currcell = this.editor.getNearestParentOfType(currnode, 'td');
if (!currcell) {
currcell = this.editor.getNearestParentOfType(currnode, 'th');
if (!currcell) {
this.editor.logMessage('Not inside a cell!');
return;
};
var index = -1;
} else {
var index = this._getColIndex(currcell);
};
this._delColHelper(body, index);
this.editor.logMessage('Column deleted');
};
this.setColumnWidths = function(widths) {
/* sets relative column widths */
var selNode = this.editor.getSelectedNode();
var table = this.editor.getNearestParentOfType(selNode, 'table');
// first remove all current width settings from the table
var iterator = new NodeIterator(table);
var currnode = null;
while (currnode = iterator.next()) {
if (currnode.nodeName.toLowerCase() == 'td') {
if (currnode.getAttribute('width')) {
currnode.removeAttribute('width');
} else if (currnode.style.width) {
delete currnode.style.width;
};
};
};
var silva_column_info = new Array();
widths = widths.split(',');
for (var i=0; i < widths.length; i++) {
widths[i] = widths[i].strip();
silva_column_info.push('C:' + widths[i]);
var widthint = parseInt(widths[i]);
if (!isNaN(widthint)) {
widths[i] = widthint;
} else if (widths[i] != '*') {
alert(_('Illegal or missing width entry in column info!'));
return;
};
};
silva_column_info = silva_column_info.join(' ');
table.setAttribute('silva_column_info', silva_column_info);
// find the first cell, use its parent as the row
// XXX note that this might potentially go wrong on nested tables!
var firstrow = table.getElementsByTagName('td')[0].parentNode;
// now convert the relative widths to percentages
// first find the first row containing cells
var totalunits = 0;
for (var i=0; i < widths.length; i++) {
if (widths[i] == '*') {
totalunits += 1;
} else {
totalunits += widths[i];
};
};
var percent_per_unit = 100.0 / totalunits;
var children = firstrow.childNodes;
var currcellindex = 0;
for (var i=0; i < children.length; i++) {
var child = children[i];
if (child.nodeType != 1 || child.nodeName.toLowerCase() != 'td') {
continue;
};
var width = widths[currcellindex];
if (width != '*') {
child.setAttribute('width', '' +
(width * percent_per_unit) + '%');
} else {
child.removeAttribute('width');
};
currcellindex++;
};
};
this.getColumnWidths = function(table) {
var silvacolinfo = table.getAttribute('silva_column_info');
var widths = new Array();
if (!silvacolinfo) {
var body = null;
var iterator = new NodeIterator(table);
var body = iterator.next();
while (body.nodeName.toLowerCase() != 'tbody') {
body = iterator.next();
};
var numcols = this._countCells(body);
for (var i=0; i < numcols; i++) {
widths.push(1);
};
} else {
silvacolinfo = silvacolinfo.split(' ');
for (var i=0; i < silvacolinfo.length; i++) {
var pair = silvacolinfo[i].split(':');
if (pair[1] == '*') {
widths.push('*');
} else {
widths.push(parseInt(pair[1]));
};
};
widths = this._factorWidths(widths);
};
return widths;
};
this._factorWidths = function(widths) {
var highest = 0;
for (var i=0; i < widths.length; i++) {
if (widths[i] > highest) {
highest = widths[i];
};
};
var factor = 1;
for (var i=0; i < highest; i++) {
var testnum = highest - i;
var isfactor = true;
for (var j=0; j < widths.length; j++) {
if (widths[j] % testnum != 0) {
isfactor = false;
break;
};
};
if (isfactor) {
factor = testnum;
break;
};
};
if (factor > 1) {
for (var i=0; i < widths.length; i++) {
widths[i] = widths[i] / factor;
};
};
return widths;
};
this._addRowHelper = function(doc, body, celltype, index, numcells) {
/* actually adds a row to the table */
var row = doc.createElement('tr');
// fill the row with cells
if (celltype == 'td') {
for (var i=0; i < numcells; i++) {
var cell = doc.createElement(celltype);
var nbsp = doc.createTextNode("\u00a0");
cell.appendChild(nbsp);
row.appendChild(cell);
}
} else if (celltype == 'th') {
var cell = doc.createElement(celltype);
cell.setAttribute('colSpan', numcells);
var nbsp = doc.createTextNode("\u00a0");
cell.appendChild(nbsp);
row.appendChild(cell);
}
// now append it to the tbody
var rows = this._getAllRows(body);
if (index == -1 || index >= rows.length) {
body.appendChild(row);
} else {
var nextrow = rows[index];
body.insertBefore(row, nextrow);
}
return row;
};
this._addColHelper = function(doc, body, index, numcells) {
/* actually adds a column to a table */
var rows = this._getAllRows(body);
for (var i=0; i < rows.length; i++) {
var row = rows[i];
var cols = this._getAllColumns(row);
var col = cols[0];
if (col.nodeName.toLowerCase() == 'th') {
var colspan = col.getAttribute('colSpan');
if (colspan) {
colspan = parseInt(colspan);
} else {
colspan = 1;
}
col.setAttribute('colSpan', colspan + 1);
} else {
var cell = doc.createElement('td');
var nbsp = doc.createTextNode('\u00a0');
cell.appendChild(nbsp);
if (index == -1 || index >= rows.length) {
row.appendChild(cell);
} else {
row.insertBefore(cell, cols[index]);
};
};
};
};
this._delColHelper = function(body, index) {
/* actually delete all cells in a column */
var rows = this._getAllRows(body);
for (var i=0; i < rows.length; i++) {
var row = rows[i];
var cols = this._getAllColumns(row);
if (cols[0].nodeName.toLowerCase() == 'th') {
// is a table header, so reduce colspan
var th = cols[0];
var colspan = th.getAttribute('colSpan');
if (!colspan || colspan == '1') {
body.removeChild(row);
} else {
colspan = parseInt(colspan);
th.setAttribute('colSpan', colspan - 1);
};
} else {
// is a table cell row, remove one
if (index > -1) {
row.removeChild(cols[index]);
} else {
row.removeChild(cols[cols.length - 1]);
}
}
};
};
this._getRowIndex = function(row) {
/* get the current rowindex */
var rowindex = 0;
var prevrow = row.previousSibling;
while (prevrow) {
if (prevrow.nodeName.toLowerCase() == 'tr') {
rowindex++;
};
prevrow = prevrow.previousSibling;
};
return rowindex;
};
this._countCells = function(body) {
/* get the current column index */
var numcols = 0;
var cols = this._getAllColumns(this._getAllRows(body)[0]);
for (var i=0; i < cols.length; i++) {
var node = cols[i];
if (node.nodeName.toLowerCase() == 'th') {
var colspan = node.getAttribute('colSpan');
if (colspan) {
colspan = parseInt(colspan);
} else {
colspan = 1;
};
numcols += colspan;
} else {
numcols++;
};
};
return numcols;
};
this._getAllRows = function(body) {
/* returns an Array of all available rows */
var rows = new Array();
for (var i=0; i < body.childNodes.length; i++) {
var node = body.childNodes[i];
if (node.nodeName.toLowerCase() == 'tr') {
rows.push(node);
};
};
return rows;
};
this._getAllColumns = function(row) {
/* returns an Array of all columns in a row */
var cols = new Array();
for (var i=0; i < row.childNodes.length; i++) {
var node = row.childNodes[i];
if (node.nodeName.toLowerCase() == 'td' ||
node.nodeName.toLowerCase() == 'th') {
cols.push(node);
};
};
return cols;
};
}
SilvaTableTool.prototype = new TableTool;
function SilvaTableToolBox(addtabledivid, edittabledivid, newrowsinputid,
newcolsinputid, makeheaderinputid, classselectid, alignselectid, widthinputid,
addtablebuttonid, addrowbuttonid, delrowbuttonid, addcolbuttonid, delcolbuttonid,
fixbuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
/* Silva specific table functionality
overrides most of the table functionality, required because Silva requires
a completely different format for tables
*/
this.addtablediv = getFromSelector(addtabledivid);
this.edittablediv = getFromSelector(edittabledivid);
this.newrowsinput = getFromSelector(newrowsinputid);
this.newcolsinput = getFromSelector(newcolsinputid);
this.makeheaderinput = getFromSelector(makeheaderinputid);
this.classselect = getFromSelector(classselectid);
this.alignselect = getFromSelector(alignselectid);
this.widthinput = getFromSelector(widthinputid);
this.addtablebutton = getFromSelector(addtablebuttonid);
this.addrowbutton = getFromSelector(addrowbuttonid);
this.delrowbutton = getFromSelector(delrowbuttonid);
this.addcolbutton = getFromSelector(addcolbuttonid);
this.delcolbutton = getFromSelector(delcolbuttonid);
this.fixbutton = getFromSelector(fixbuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolboxel = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this.initialize = function(tool, editor) {
/* attach the event handlers */
this.tool = tool;
this.editor = editor;
addEventHandler(this.addtablebutton, "click", this.addTable, this);
addEventHandler(this.addrowbutton, "click", this.tool.addTableRow, this.tool);
addEventHandler(this.delrowbutton, "click", this.tool.delTableRow, this.tool);
addEventHandler(this.addcolbutton, "click", this.tool.addTableColumn, this.tool);
addEventHandler(this.delcolbutton, "click", this.tool.delTableColumn, this.tool);
addEventHandler(this.fixbutton, "click", this.fixTable, this);
addEventHandler(this.delbutton, "click", this.tool.delTable, this);
addEventHandler(this.alignselect, "change", this.setColumnAlign, this);
addEventHandler(this.classselect, "change", this.setTableClass, this);
addEventHandler(this.widthinput, "change", this.setColumnWidths, this);
this.addtablediv.style.display = "block";
this.edittablediv.style.display = "none";
this.editor.logMessage('Table tool initialized');
};
this.updateState = function(selNode) {
/* update the state (add/edit) and update the pulldowns (if required) */
var table = this.editor.getNearestParentOfType(selNode, 'table');
if (table) {
this.addtablediv.style.display = "none";
this.edittablediv.style.display = "block";
var td = this.editor.getNearestParentOfType(selNode, 'td');
if (!td) {
td = this.editor.getNearestParentOfType(selNode, 'th');
this.widthinput.value = '';
} else {
this.widthinput.value = this.tool.getColumnWidths(table);
};
if (td) {
var align = td.getAttribute('align');
if (this.editor.config.use_css) {
align = td.style.textAlign;
};
selectSelectItem(this.alignselect, align);
};
selectSelectItem(this.classselect, table.className);
if (this.toolboxel) {
if (this.toolboxel.open_handler) {
this.toolboxel.open_handler();
};
this.toolboxel.className = this.activeclass;
};
} else {
this.edittablediv.style.display = "none";
this.addtablediv.style.display = "block";
this.alignselect.selectedIndex = 0;
this.classselect.selectedIndex = 0;
if (this.toolboxel) {
this.toolboxel.className = this.plainclass;
};
};
};
this.addTable = function() {
/* add a Silvs specific table, with an (optional) header with colspan */
var rows = parseInt(this.newrowsinput.value);
var cols = parseInt(this.newcolsinput.value);
var makeHeader = this.makeheaderinput.checked;
var classchooser = getFromSelector("kupu-table-classchooser-add");
var tableclass = this.classselect.options[this.classselect.selectedIndex].value;
this.tool.createTable(rows, cols, makeHeader, tableclass);
};
this.setTableClass = function() {
var cls = this.classselect.options[this.classselect.selectedIndex].value;
this.tool.setTableClass(cls);
};
this.setColumnWidths = function() {
var widths = this.widthinput.value;
this.tool.setColumnWidths(widths);
};
this.fixTable = function(event) {
/* fix the table so it is Silva (and this tool) compliant */
// since this can be quite a nasty creature we can't just use the
// helper methods
// first we create a new tbody element
var currnode = this.editor.getSelectedNode();
var table = this.editor.getNearestParentOfType(currnode, 'TABLE');
if (!table) {
this.editor.logMessage('Not inside a table!');
return;
};
var doc = this.editor.getInnerDocument();
var tbody = doc.createElement('tbody');
var allowed_classes = new Array('plain', 'grid', 'list', 'listing', 'data');
if (!allowed_classes.contains(table.getAttribute('class'))) {
table.setAttribute('class', 'plain');
};
table.setAttribute('cellpadding', '0');
table.setAttribute('cellspacing', '0');
// now get all the rows of the table, the rows can either be
// direct descendants of the table or inside a 'tbody', 'thead'
// or 'tfoot' element
var rows = new Array();
var parents = new Array('thead', 'tbody', 'tfoot');
for (var i=0; i < table.childNodes.length; i++) {
var node = table.childNodes[i];
if (node.nodeName.toLowerCase() == 'tr') {
rows.push(node);
} else if (parents.contains(node.nodeName.toLowerCase())) {
for (var j=0; j < node.childNodes.length; j++) {
var inode = node.childNodes[j];
if (inode.nodeName.toLowerCase() == 'tr') {
rows.push(inode);
};
};
};
};
// now find out how many cells our rows should have
var numcols = 0;
for (var i=0; i < rows.length; i++) {
var row = rows[i];
var currnumcols = 0;
for (var j=0; j < row.childNodes.length; j++) {
var node = row.childNodes[j];
if (node.nodeName.toLowerCase() == 'td' ||
node.nodeName.toLowerCase() == 'th') {
var colspan = 1;
if (node.getAttribute('colSpan')) {
colspan = parseInt(node.getAttribute('colSpan'));
};
currnumcols += colspan;
};
};
if (currnumcols > numcols) {
numcols = currnumcols;
};
};
// now walk through all rows to clean them up
for (var i=0; i < rows.length; i++) {
var row = rows[i];
var newrow = doc.createElement('tr');
var currcolnum = 0;
var inhead = -1;
while (row.childNodes.length > 0) {
var node = row.childNodes[0];
if (node.nodeName.toLowerCase() == 'td') {
if (inhead == -1) {
inhead = 0;
node.setAttribute('colSpan', '1');
};
} else if (node.nodeName.toLowerCase() == 'th') {
if (inhead == -1) {
inhead = 1;
newrow.appendChild(node);
node.setAttribute('colSpan', '1');
node.setAttribute('rowSpan', '1');
continue;
} else if (inhead == 0) {
var td = doc.createElement('td');
while (node.childNodes.length) {
td.appendChild(node.childNodes[0]);
};
row.removeChild(node);
node = td;
};
} else {
row.removeChild(node);
continue;
};
node.setAttribute('rowspan', '1');
if (inhead) {
while (node.childNodes.length) {
newrow.childNodes[0].appendChild(node.childNodes[0]);
};
var colspan = node.getAttribute('colSpan');
if (colspan) {
colspan = parseInt(colspan);
} else {
colspan = 1;
}
var current_colspan = parseInt(newrow.childNodes[0].getAttribute('colSpan'));
newrow.childNodes[0].setAttribute('colSpan', (current_colspan + colspan).toString());
row.removeChild(node);
} else {
node.setAttribute('colSpan', 1);
node.setAttribute('rowSpan', 1);
newrow.appendChild(node);
};
};
if (newrow.childNodes.length) {
tbody.appendChild(newrow);
};
};
// now make sure all rows have the correct length
for (var i=0; i < tbody.childNodes.length; i++) {
var row = tbody.childNodes[i];
if (row.childNodes.length && row.childNodes[0].nodeName.toLowerCase() == 'th') {
row.childNodes[0].setAttribute('colSpan', numcols);
} else {
while (row.childNodes.length < numcols) {
var td = doc.createElement('td');
var nbsp = doc.createTextNode('\u00a0');
td.appendChild(nbsp);
row.appendChild(td);
};
};
};
// now remove all the old stuff from the table and add the new tbody
var tlength = table.childNodes.length;
for (var i=0; i < tlength; i++) {
table.removeChild(table.childNodes[0]);
};
table.appendChild(tbody);
this.editor.getDocument().getWindow().focus();
this.editor.logMessage('Table cleaned up');
};
this._fixAllTables = function() {
/* fix all the tables in the document at once */
return;
var tables = this.editor.getInnerDocument().getElementsByTagName('table');
for (var i=0; i < tables.length; i++) {
this.fixTable(tables[i]);
};
};
}
SilvaTableToolBox.prototype = new TableToolBox;
function SilvaIndexTool(inputid, addbuttonid, updatebuttonid, deletebuttonid, toolboxid, plainclass, activeclass) {
/* a tool to manage index items (named anchors) for Silva */
this.input = getFromSelector(inputid);
this.addbutton = getFromSelector(addbuttonid);
this.updatebutton = getFromSelector(updatebuttonid);
this.deletebutton = getFromSelector(deletebuttonid);
this.toolboxel = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this.initialize = function(editor) {
/* attach the event handlers */
this.editor = editor;
addEventHandler(this.input, 'blur', this.updateIndex, this);
addEventHandler(this.addbutton, 'click', this.addIndex, this);
addEventHandler(this.updatebutton, 'click', this.updateIndex, this);
addEventHandler(this.deletebutton, 'click', this.deleteIndex, this);
if (this.editor.getBrowserName() == 'IE') {
// need to catch some additional events for IE
addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnIndex, this);
addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnIndex, this);
};
addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnIndex, this);
this.updatebutton.style.display = 'none';
this.deletebutton.style.display = 'none';
};
this.addIndex = function(event) {
/* create an index */
var name = this.input.value;
var currnode = this.editor.getSelectedNode();
var indexel = this.editor.getNearestParentOfType(currnode, 'A');
if (indexel && indexel.getAttribute('href')) {
this.editor.logMessage('Can not add index items in anchors');
return;
};
if (!indexel) {
var doc = this.editor.getDocument();
if (!name) {
var selection = this.editor.getSelection();
var cloned = selection.cloneContents();
var iterator = new NodeIterator(cloned);
var name = '';
var currnode = null;
while (currnode = iterator.next()) {
if (currnode.nodeValue) {
name += currnode.nodeValue;
};
};
if (name) {
this.input.value = name;
};
};
var docel = doc.getDocument();
indexel = docel.createElement('a');
var text = docel.createTextNode('[' + name + ']');
indexel.appendChild(text);
indexel = this.editor.insertNodeAtSelection(indexel, true);
indexel.className = 'index';
};
indexel.setAttribute('name', name);
var sel = this.editor.getSelection();
sel.collapse(true);
this.editor.logMessage('Index added');
};
this.updateIndex = function(event) {
/* update an existing index */
var currnode = this.editor.getSelectedNode();
var indexel = this.editor.getNearestParentOfType(currnode, 'A');
if (!indexel) {
return;
};
if (indexel && indexel.getAttribute('href')) {
this.editor.logMessage('Can not add an index element inside a link!');
return;
};
var name = this.input.value;
indexel.setAttribute('name', name);
while (indexel.hasChildNodes()) {
indexel.removeChild(indexel.firstChild);
};
var text = this.editor.getInnerDocument().createTextNode('[' + name + ']')
indexel.appendChild(text);
this.editor.logMessage('Index modified');
};
this.deleteIndex = function() {
var selNode = this.editor.getSelectedNode();
var a = this.editor.getNearestParentOfType(selNode, 'a');
if (!a || a.getAttribute('href')) {
this.editor.logMessage('Not inside an index element!');
return;
};
a.parentNode.removeChild(a);
this.editor.logMessage('Index element removed');
};
this.handleKeyPressOnIndex = function(event) {
var selNode = this.editor.getSelectedNode();
var a = this.editor.getNearestParentOfType(selNode, 'a');
if (!a || a.getAttribute('href')) {
return;
};
var keyCode = event.keyCode;
if (keyCode == 8 || keyCode == 46) {
a.parentNode.removeChild(a);
} else if (keyCode == 9 || keyCode == 39) {
var next = a.nextSibling;
while (next && next.nodeName.toLowerCase() == 'br') {
next = next.nextSibling;
};
if (!next) {
var doc = this.editor.getInnerDocument();
next = doc.createTextNode('\xa0');
a.parentNode.appendChild(next);
};
var selection = this.editor.getSelection();
// XXX I fear I'm working around bugs here... because of a bug in
// selection.moveStart() I can't use the same codepath in IE as in Moz
if (this.editor.getBrowserName() == 'IE') {
selection.selectNodeContents(a);
// XXX are we depending on a bug here? shouldn't we move the
// selection one place to get out of the anchor? it works,
// but seems wrong...
selection.collapse(true);
} else {
selection.selectNodeContents(next);
selection.collapse();
var selection = this.editor.getSelection();
};
this.editor.updateState();
};
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
};
return false;
};
this.updateState = function(selNode) {
var indexel = this.editor.getNearestParentOfType(selNode, 'A');
if (indexel && !indexel.getAttribute('href')) {
if (this.toolboxel) {
if (this.toolboxel.open_handler) {
this.toolboxel.open_handler();
};
this.toolboxel.className = this.activeclass;
};
this.input.value = indexel.getAttribute('name');
this.addbutton.style.display = 'none';
this.updatebutton.style.display = 'inline';
this.deletebutton.style.display = 'inline';
} else {
if (this.toolboxel) {
this.toolboxel.className = this.plainclass;
};
this.input.value = '';
this.updatebutton.style.display = 'none';
this.deletebutton.style.display = 'none';
this.addbutton.style.display = 'inline';
};
};
this.createContextMenuElements = function(selNode, event) {
var indexel = this.editor.getNearestParentOfType(selNode, 'A');
if (indexel && !indexel.getAttribute('href')) {
return new Array(new ContextMenuElement('Delete index', this.deleteIndex, this));
} else {
return new Array();
};
};
};
SilvaIndexTool.prototype = new KupuTool;
function SilvaTocTool(depthselectid, addbuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
this.depthselect = getFromSelector(depthselectid);
this.addbutton = getFromSelector(addbuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolbox = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this._inside_toc = false;
this.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.addbutton, 'click', this.addOrUpdateToc, this);
addEventHandler(this.depthselect, 'change', this.updateToc, this);
addEventHandler(this.delbutton, 'click', this.deleteToc, this);
addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnToc, this);
if (this.editor.getBrowserName() == 'IE') {
addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnToc, this);
addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnToc, this);
};
};
this.handleKeyPressOnToc = function(event) {
if (!this._inside_toc) {
return;
};
var keyCode = event.keyCode;
if (keyCode == 8 || keyCode == 46) {
var selNode = this.editor.getSelectedNode();
var toc = this.getNearestToc(selNode);
toc.parentNode.removeChild(toc);
};
if (keyCode == 13 || keyCode == 9 || keyCode == 39) {
var selNode = this.editor.getSelectedNode();
var toc = this.getNearestToc(selNode);
var doc = this.editor.getInnerDocument();
var selection = this.editor.getSelection();
if (toc.nextSibling) {
var sibling = toc.nextSibling;
selection.selectNodeContents(toc.nextSibling);
selection.collapse();
} else {
var parent = toc.parentNode;
var p = doc.createElement('p');
parent.appendChild(p);
var text = doc.createTextNode('\xa0');
p.appendChild(text);
selection.selectNodeContents(p);
};
this._inside_toc = false;
};
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
};
};
this.updateState = function(selNode, event) {
var toc = this.getNearestToc(selNode);
if (toc) {
var depth = toc.getAttribute('toc_depth');
selectSelectItem(this.depthselect, depth);
this.addbutton.style.display = 'none';
this.delbutton.style.display = 'inline';
this._inside_toc = true;
if (this.toolbox) {
if (this.toolbox.open_handler) {
this.toolbox.open_handler();
};
this.toolbox.className = this.activeclass;
};
} else {
this.depthselect.selectedIndex = 0;
this.delbutton.style.display = 'none';
this.addbutton.style.display = 'inline';
this._inside_toc = false;
if (this.toolbox) {
this.toolbox.className = this.plainclass;
};
};
};
this.addOrUpdateToc = function(event, depth) {
var selNode = this.editor.getSelectedNode();
var depth = depth ? depth : this.depthselect.options[this.depthselect.selectedIndex].value;
var toc = this.getNearestToc(selNode);
var doc = this.editor.getInnerDocument();
var toctext = this.getTocText(depth);
if (toc) {
// there's already a toc, just update the depth
toc.setAttribute('toc_depth', depth);
while (toc.hasChildNodes()) {
toc.removeChild(toc.firstChild);
};
toc.appendChild(doc.createTextNode(toctext));
} else {
// create a new toc
var div = doc.createElement('div');
div.setAttribute('toc_depth', depth);
div.setAttribute('is_toc', 1);
div.className = 'toc';
var text = doc.createTextNode(toctext);
div.appendChild(text);
this.editor.insertNodeAtSelection(div);
};
};
this.createDefaultToc = function() {
// XXX nasty workaround, entering null as the event...
this.addOrUpdateToc(null, '-1');
};
this.updateToc = function() {
var selNode = this.editor.getSelectedNode();
var toc = this.getNearestToc(selNode);
if (toc) {
var depth = this.depthselect.options[this.depthselect.selectedIndex].value;
var toctext = this.getTocText(depth);
toc.setAttribute('toc_depth', depth);
while (toc.hasChildNodes()) {
toc.removeChild(toc.firstChild);
};
doc = this.editor.getInnerDocument();
toc.appendChild(doc.createTextNode(toctext));
};
};
this.deleteToc = function() {
var selNode = this.editor.getSelectedNode();
var toc = this.getNearestToc(selNode);
if (!toc) {
this.editor.logMessage('Not inside a toc!', 1);
return;
};
toc.parentNode.removeChild(toc);
};
this.getNearestToc = function(selNode) {
var currnode = selNode;
while (currnode) {
if (currnode.nodeName.toLowerCase() == 'div' &&
currnode.getAttribute('is_toc')) {
return currnode;
};
currnode = currnode.parentNode;
};
return false;
};
this.createContextMenuElements = function(selNode, event) {
/* create the 'Delete TOC' menu elements */
var ret = new Array();
if (this.getNearestToc(selNode)) {
ret.push(new ContextMenuElement('Delete TOC', this.deleteToc, this));
} else {
ret.push(new ContextMenuElement('Create TOC', this.createDefaultToc, this));
};
return ret;
};
this.getTocText = function(depth) {
var toctext = 'Table of Contents ';
switch (depth) {
case '-1':
toctext += '(unlimited levels)';
break;
case '1':
toctext += '(1 level)';
break;
default:
toctext += '(' + depth + ' levels)';
break;
};
return toctext;
};
};
SilvaTocTool.prototype = new KupuTool;
function SilvaAbbrTool(abbrradioid, acronymradioid, radiocontainerid, titleinputid,
addbuttonid, updatebuttonid, delbuttonid,
toolboxid, plainclass, activeclass) {
/* tool to manage citation elements */
this.abbrradio = getFromSelector(abbrradioid);
this.acronymradio = getFromSelector(acronymradioid);
this.radiocontainer = getFromSelector(radiocontainerid);
this.titleinput = getFromSelector(titleinputid);
this.addbutton = getFromSelector(addbuttonid);
this.updatebutton = getFromSelector(updatebuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolbox = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.addbutton, 'click', this.addElement, this);
addEventHandler(this.updatebutton, 'click', this.updateElement, this);
addEventHandler(this.delbutton, 'click', this.deleteElement, this);
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
};
this.updateState = function(selNode, event) {
var element = this.getNearestAbbrAcronym(selNode);
if (element) {
this.addbutton.style.display = 'none';
this.updatebutton.style.display = 'inline';
this.delbutton.style.display = 'inline';
this.titleinput.value = element.getAttribute('title');
this.radiocontainer.style.display = 'none';
if (this.toolbox) {
if (this.toolbox.open_handler) {
this.toolbox.open_handler();
};
this.toolbox.className = this.activeclass;
};
} else {
this.addbutton.style.display = 'inline';
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
this.titleinput.value = '';
if (this.editor.getBrowserName() == 'IE' || this.radiocontainer.nodeName.toLowerCase() != 'tr') {
this.radiocontainer.style.display = 'block';
} else {
this.radiocontainer.style.display = 'table-row';
};
if (this.toolbox) {
this.toolbox.className = this.plainclass;
};
};
};
this.getNearestAbbrAcronym = function(selNode) {
var current = selNode;
while (current && current.nodeType != 9) {
if (current.nodeType == 1) {
var nodeName = current.nodeName.toLowerCase();
if (nodeName == 'abbr' || nodeName == 'acronym') {
return current;
};
};
current = current.parentNode;
};
};
this.addElement = function() {
var type = this.abbrradio.checked ? 'abbr' : 'acronym';
var doc = this.editor.getInnerDocument();
var selNode = this.editor.getSelectedNode();
if (this.getNearestAbbrAcronym(selNode)) {
this.editor.logMessage('Can not nest abbr and acronym elements');
return;
};
var element = doc.createElement(type);
element.setAttribute('title', this.titleinput.value);
var selection = this.editor.getSelection();
var docfrag = selection.cloneContents();
var placecursoratend = false;
if (docfrag.hasChildNodes()) {
for (var i=0; i < docfrag.childNodes.length; i++) {
element.appendChild(docfrag.childNodes[i]);
};
placecursoratend = true;
} else {
var text = doc.createTextNode('\xa0');
element.appendChild(text);
};
this.editor.insertNodeAtSelection(element, 1);
var selection = this.editor.getSelection();
selection.collapse(placecursoratend);
this.editor.getDocument().getWindow().focus();
var selNode = selection.getSelectedNode();
this.editor.updateState(selNode);
this.editor.logMessage('Element ' + type + ' added');
};
this.updateElement = function() {
var selNode = this.editor.getSelectedNode();
var element = this.getNearestAbbrAcronym(selNode);
if (!element) {
this.editor.logMessage('Not inside an abbr or acronym element!', 1);
return;
};
var title = this.titleinput.value;
element.setAttribute('title', title);
this.editor.logMessage('Updated ' + element.nodeName.toLowerCase() + ' element');
};
this.deleteElement = function() {
var selNode = this.editor.getSelectedNode();
var element = this.getNearestAbbrAcronym(selNode);
if (!element) {
this.editor.logMessage('Not inside an abbr or acronym element!', 1);
return;
};
element.parentNode.removeChild(element);
this.editor.logMessage('Deleted ' + element.nodeName.toLowerCase() + ' deleted');
};
};
SilvaAbbrTool.prototype = new KupuTool;
function SilvaCitationTool(authorinputid, sourceinputid, addbuttonid, updatebuttonid, delbuttonid,
toolboxid, plainclass, activeclass) {
/* tool to manage citation elements */
this.authorinput = getFromSelector(authorinputid);
this.sourceinput = getFromSelector(sourceinputid);
this.addbutton = getFromSelector(addbuttonid);
this.updatebutton = getFromSelector(updatebuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolbox = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this._inside_citation = false;
this.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.addbutton, 'click', this.addCitation, this);
addEventHandler(this.updatebutton, 'click', this.updateCitation, this);
addEventHandler(this.delbutton, 'click', this.deleteCitation, this);
if (editor.getBrowserName() == 'IE') {
addEventHandler(editor.getInnerDocument(), 'keyup', this.cancelEnterPress, this);
addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnCitation, this);
} else {
addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnCitation, this);
};
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
};
this.cancelEnterPress = function(event) {
if (!this._inside_citation || (event.keyCode != 13 && event.keyCode != 9)) {
return;
};
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
};
};
this.handleKeyPressOnCitation = function(event) {
if (!this._inside_citation) {
return;
};
var keyCode = event.keyCode;
var citation = this.getNearestCitation(this.editor.getSelectedNode());
var doc = this.editor.getInnerDocument();
var selection = this.editor.getSelection();
if (keyCode == 13 && this.editor.getBrowserName() == 'IE') {
var br = doc.createElement('br');
var currnode = selection.getSelectedNode();
selection.replaceWithNode(br);
selection.selectNodeContents(br);
selection.collapse(true);
event.returnValue = false;
} else if (keyCode == 9) {
var next = citation.nextSibling;
if (!next) {
next = doc.createElement('p');
next.appendChild(doc.createTextNode('\xa0'));
citation.parentNode.appendChild(next);
};
selection.selectNodeContents(next);
selection.collapse();
if (event.preventDefault) {
event.preventDefault();
};
event.returnValue = false;
this._inside_citation = false;
};
};
this.updateState = function(selNode, event) {
var citation = this.getNearestCitation(selNode);
if (citation) {
this.addbutton.style.display = 'none';
this.updatebutton.style.display = 'inline';
this.delbutton.style.display = 'inline';
this.authorinput.value = citation.getAttribute('author');
this.sourceinput.value = citation.getAttribute('source');
this._inside_citation = true;
if (this.toolbox) {
if (this.toolbox.open_handler) {
this.toolbox.open_handler();
};
this.toolbox.className = this.activeclass;
};
} else {
this.addbutton.style.display = 'inline';
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
this.authorinput.value = '';
this.sourceinput.value = '';
this._inside_citation = false;
if (this.toolbox) {
this.toolbox.className = this.plainclass;
};
};
};
this.addCitation = function() {
var selNode = this.editor.getSelectedNode();
var citation = this.getNearestCitation(selNode);
if (citation) {
this.editor.logMessage('Nested citations are not allowed!');
return;
};
var author = this.authorinput.value;
var source = this.sourceinput.value;
var doc = this.editor.getInnerDocument();
var div = doc.createElement('div');
div.className = 'citation';
div.setAttribute('author', author);
div.setAttribute('source', source);
div.setAttribute('is_citation', '1');
var selection = this.editor.getSelection();
var docfrag = selection.cloneContents();
var placecursoratend = false;
if (docfrag.hasChildNodes()) {
for (var i=0; i < docfrag.childNodes.length; i++) {
div.appendChild(docfrag.childNodes[i]);
};
placecursoratend = true;
} else {
var text = doc.createTextNode('\xa0');
div.appendChild(text);
};
this.editor.insertNodeAtSelection(div, 1);
var selection = this.editor.getSelection();
selection.collapse(placecursoratend);
this.editor.getDocument().getWindow().focus();
var selNode = selection.getSelectedNode();
this.editor.updateState(selNode);
};
this.updateCitation = function() {
var selNode = this.editor.getSelectedNode();
var citation = this.getNearestCitation(selNode);
if (!citation) {
this.editor.logMessage('Not inside a citation element!');
return;
};
citation.setAttribute('author', this.authorinput.value);
citation.setAttribute('source', this.sourceinput.value);
};
this.deleteCitation = function() {
var selNode = this.editor.getSelectedNode();
var citation = this.getNearestCitation(selNode);
if (!citation) {
this.editor.logMessage('Not inside citation element!');
return;
};
citation.parentNode.removeChild(citation);
};
this.getNearestCitation = function(selNode) {
var currnode = selNode;
while (currnode) {
if (currnode.nodeName.toLowerCase() == 'div' &&
currnode.getAttribute('is_citation')) {
return currnode;
};
currnode = currnode.parentNode;
};
return false;
};
this.createContextMenuElements = function(selNode, event) {
/* create the 'Delete citation' menu elements */
var ret = new Array();
if (this.getNearestCitation(selNode)) {
ret.push(new ContextMenuElement('Delete cite', this.deleteCitation, this));
};
return ret;
};
};
SilvaCitationTool.prototype = new KupuTool;
function SilvaExternalSourceTool(idselectid, formcontainerid, addbuttonid, cancelbuttonid,
updatebuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
this.idselect = getFromSelector(idselectid);
this.formcontainer = getFromSelector(formcontainerid);
this.addbutton = getFromSelector(addbuttonid);
this.cancelbutton = getFromSelector(cancelbuttonid);
this.updatebutton = getFromSelector(updatebuttonid);
this.delbutton = getFromSelector(delbuttonid);
this.toolbox = getFromSelector(toolboxid);
this.plainclass = plainclass;
this.activeclass = activeclass;
this._editing = false;
this._url = null;
this._id = null;
this._form = null;
this._insideExternalSource = false;
// store the base url, this will be prepended to the id to form the url to
// get the codesource from (Zope's acquisition will make sure it ends up on
// the right object)
var urlparts = document.location.pathname.toString().split('/')
var urlparts_to_use = [];
for (var i=0; i < urlparts.length; i++) {
var part = urlparts[i];
if (part == 'edit') {
break;
};
urlparts_to_use.push(part);
};
this._baseurl = urlparts_to_use.join('/');
this.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.addbutton, 'click', this.startExternalSourceAddEdit, this);
addEventHandler(this.cancelbutton, 'click', this.resetTool, this);
addEventHandler(this.updatebutton, 'click', this.startExternalSourceAddEdit, this);
addEventHandler(this.delbutton, 'click', this.delExternalSource, this);
addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnExternalSource, this);
if (this.editor.getBrowserName() == 'IE') {
addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnExternalSource, this);
addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnExternalSource, this);
};
// search for a special serialized identifier of the current document
// which is used to send to the ExternalSource element when sending
// requests so the ExternalSources know their context
this.docref = null;
var metas = this.editor.getInnerDocument().getElementsByTagName('meta');
for (var i=0; i < metas.length; i++) {
var meta = metas[i];
if (meta.getAttribute('name') == 'docref') {
this.docref = meta.getAttribute('content');
};
};
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
this.cancelbutton.style.display = 'none';
};
this.updateState = function(selNode) {
var extsource = this.getNearestExternalSource(selNode);
if (extsource) {
this._insideExternalSource = true;
selectSelectItem(this.idselect, extsource.getAttribute('source_id'));
this.addbutton.style.display = 'none';
this.cancelbutton.style.display = 'none';
this.updatebutton.style.display = 'inline';
this.delbutton.style.display = 'inline';
this.startExternalSourceUpdate(extsource);
if (this.toolbox) {
this.toolbox.className = this.activeclass;
};
} else {
this._insideExternalSource = false;
this.resetTool();
if (this.toolbox) {
this.toolbox.className = this.plainclass;
};
};
};
this.handleKeyPressOnExternalSource = function(event) {
if (!this._insideExternalSource) {
return;
};
var keyCode = event.keyCode;
var selNode = this.editor.getSelectedNode();
var div = this.getNearestExternalSource(selNode);
var doc = this.editor.getInnerDocument();
if (keyCode == 13 || keyCode == 9 || keyCode == 39) {
if (div.nextSibling) {
var selection = this.editor.getSelection();
selection.selectNodeContents(div.nextSibling);
selection.collapse();
} else {
var p = doc.createElement('p');
var nbsp = doc.createTextNode('\xa0');
p.appendChild(nbsp);
div.parentNode.appendChild(p);
var selection = this.editor.getSelection();
selection.selectNodeContents(p);
selection.collapse();
};
this._insideExternalSource = false;
} else if (keyCode == 8) {
var selectnode = div.nextSibling;
if (!selectnode) {
selectnode = doc.createElement('p');
selectnode.appendChild(doc.createTextNode('\xa0'));
doc.appendChild(selectnode);
};
var selection = this.editor.getSelection();
selection.selectNodeContents(selectnode);
div.parentNode.removeChild(div);
selection.collapse();
};
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
};
};
this.getUrlAndContinue = function(id, handler) {
if (id == this._id) {
// return cached
handler.call(this, this._url);
return;
};
var request = new XMLHttpRequest();
request.open('GET',
this._baseurl + '/edit/get_extsource_url?id=' + id, true);
var callback = new ContextFixer(function() {
if (request.readyState == 4) {
var url = request.responseText;
this._id = id;
this._url = url;
handler.call(this, url);
};
}, this);
request.onreadystatechange = callback.execute;
request.send('');
};
this.startExternalSourceAddEdit = function() {
// get the appropriate form and display it
if (!this._editing) {
var id = this.idselect.options[this.idselect.selectedIndex].value;
this.getUrlAndContinue(id, this._continueStartExternalSourceEdit);
} else {
// validate the data and take further actions
var formdata = this._gatherFormData();
var doc = window.document;
var request = new XMLHttpRequest();
request.open('POST', this._url + '/validate_form_to_request', true);
var callback = new ContextFixer(this._addExternalSourceIfValidated, request, this);
request.onreadystatechange = callback.execute;
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send(formdata);
};
};
this._continueStartExternalSourceEdit = function(url) {
url = url + '/get_rendered_form_for_editor?docref=' + this.docref;
var request = new XMLHttpRequest();
request.open('GET', url, true);
var callback = new ContextFixer(this._addFormToTool, request, this);
request.onreadystatechange = callback.execute;
request.send(null);
while (this.formcontainer.hasChildNodes()) {
this.formcontainer.removeChild(this.formcontainer.firstChild);
};
var text = document.createTextNode('Loading...');
this.formcontainer.appendChild(text);
this.updatebutton.style.display = 'none';
this.cancelbutton.style.display = 'inline';
this.addbutton.style.display = 'inline';
this._editing = true;
};
this.startExternalSourceUpdate = function(extsource) {
var id = extsource.getAttribute('source_id');
this.getUrlAndContinue(id, this._continueStartExternalSourceUpdate);
};
this._continueStartExternalSourceUpdate = function(url) {
url = url + '/get_rendered_form_for_editor';
var formdata = this._gatherFormDataFromElement();
formdata += '&docref=' + this.docref;
var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var callback = new ContextFixer(this._addFormToTool, request, this);
request.onreadystatechange = callback.execute;
request.send(formdata);
this._editing = true;
while (this.formcontainer.hasChildNodes()) {
this.formcontainer.removeChild(this.formcontainer.firstChild);
};
var text = document.createTextNode('Loading...');
this.formcontainer.appendChild(text);
};
this._addFormToTool = function(object) {
if (this.readyState == 4) {
if (this.status != '200') {
// element not found, return without doing anythink
object.resetTool();
return;
};
while (object.formcontainer.hasChildNodes()) {
object.formcontainer.removeChild(object.formcontainer.firstChild);
};
// XXX Somehow appending the XML to the form using DOM doesn't
// work correctly, it looks like the elements aren't HTMLElements
// but XML elements, don't know how to fix now so I'll use string
// insertion for now, needless to say it should be changed to DOM
// manipulation asap...
// XXX why is this.responseXML.documentElement.xml sometimes 'undefined'?
object.formcontainer.innerHTML = this.responseText;
object.idselect.style.display = 'none';
// the formcontainer will contain a table with a form
var form = null;
var iterator = new NodeIterator(object.formcontainer);
while (form == null) {
var next = iterator.next();
if (next.nodeName.toLowerCase() == 'form') {
form = next;
};
};
object._form = form;
};
};
this._addExternalSourceIfValidated = function(object) {
if (this.readyState == 4) {
if (this.status == '200') {
// success, add the external source element to the document
var selNode = object.editor.getSelectedNode();
var currsource = object.getNearestExternalSource(selNode);
var doc = object.editor.getInnerDocument();
var extsource = doc.createElement('div');
extsource.setAttribute('source_id', object._id);
var header = doc.createElement('h4');
extsource.appendChild(header);
extsource.className = 'externalsource';
var metatype = 'Silva Code Source'; // a default just in case
for (var i=0; i < this.responseXML.documentElement.childNodes.length; i++) {
var child = this.responseXML.documentElement.childNodes[i];
if (child.nodeName.toLowerCase() == 'parameter') {
var key = child.getAttribute('key');
var value = '';
for (var j=0; j < child.childNodes.length; j++) {
value += child.childNodes[j].nodeValue;
};
if (key == 'metatype') {
metatype = value;
continue;
};
extsource.setAttribute(key, value);
var textel = doc.createTextNode('Key: ' + key + ', value: ' + value.toString());
extsource.appendChild(textel);
extsource.appendChild(doc.createElement('br'));
};
};
var htext = doc.createTextNode(metatype + ' \xab' + object._id + '\xbb');
header.insertBefore(htext, header.firstChild);
extsource.appendChild(doc.createElement('br'));
if (!currsource) {
object.editor.insertNodeAtSelection(extsource);
} else {
currsource.parentNode.replaceChild(extsource, currsource);
var selection = object.editor.getSelection();
selection.selectNodeContents(extsource);
selection.collapse(true);
};
object.resetTool();
object.editor.updateState();
} else if (this.status == '400') {
// failure, provide some feedback and return to the form
alert('Form could not be validated, error message: ' + this.responseText);
} else {
alert('POST failed with unhandled status ' + this.status);
throw('Error handling POST, server returned ' + this.status + ' HTTP status code');
};
};
};
this.delExternalSource = function() {
var selNode = this.editor.getSelectedNode();
var source = this.getNearestExternalSource(selNode);
if (!source) {
this.editor.logMessage('Not inside external source!', 1);
return;
};
var nextsibling = source.nextSibling;
source.parentNode.removeChild(source);
if (nextsibling) {
var selection = this.editor.getSelection();
selection.selectNodeContents(nextsibling);
selection.collapse();
};
};
this.resetTool = function() {
while (this.formcontainer.hasChildNodes()) {
this.formcontainer.removeChild(this.formcontainer.firstChild);
};
this.idselect.style.display = 'inline';
this.addbutton.style.display = 'inline';
this.cancelbutton.style.display = 'none';
this.updatebutton.style.display = 'none';
this.delbutton.style.display = 'none';
//this.editor.updateState();
this._editing = false;
};
this._gatherFormData = function() {
/* walks through the form and creates a POST body */
// XXX we may want to turn this into a helper function, since it's
// quite useful outside of this object I reckon
var form = this._form;
if (!form) {
this.editor.logMessage('Not currently editing');
return;
};
// first place all data into a dict, convert to a string later on
var data = {};
for (var i=0; i < form.elements.length; i++) {
var child = form.elements[i];
var elname = child.nodeName.toLowerCase();
if (elname == 'input') {
var name = child.getAttribute('name');
var type = child.getAttribute('type');
if (!type || type == 'text' || type == 'hidden' || type == 'password') {
data[name] = child.value;
} else if (type == 'checkbox' || type == 'radio') {
if (child.checked) {
if (data[name]) {
if (typeof data[name] == typeof('')) {
var value = new Array(data[name]);
value.push(child.value);
data[name] = value;
} else {
data[name].push(child.value);
};
} else {
data[name] = child.value;
};
};
};
} else if (elname == 'textarea') {
data[child.getAttribute('name')] = child.value;
} else if (elname == 'select') {
var name = child.getAttribute('name');
var multiple = child.getAttribute('multiple');
if (!multiple) {
data[name] = child.options[child.selectedIndex].value;
} else {
var value = new Array();
for (var i=0; i < child.options.length; i++) {
if (child.options[i].checked) {
value.push(options[i].value);
};
if (value.length > 1) {
data[name] = value;
} else if (value.length) {
data[name] = value[0];
};
};
};
};
};
// now we should turn it into a query string
var ret = new Array();
for (var key in data) {
var value = data[key];
// XXX does IE5 support encodeURIComponent?
ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
};
return ret.join("&");
};
this._gatherFormDataFromElement = function() {
var selNode = this.editor.getSelectedNode();
var source = this.getNearestExternalSource(selNode);
if (!source) {
return '';
};
var ret = new Array();
for (var i=0; i < source.attributes.length; i++) {
var attr = source.attributes[i];
var name = attr.nodeName;
var value = attr.nodeValue;
if (name != 'class' && name != 'source_id' && name != 'id') {
ret.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
};
};
return ret.join('&');
};
this.getNearestExternalSource = function(selNode) {
var currnode = selNode;
while (currnode) {
if (currnode.nodeName.toLowerCase() == 'div' && currnode.className == 'externalsource') {
return currnode;
};
currnode = currnode.parentNode;
};
};
};
SilvaExternalSourceTool.prototype = new KupuTool;
function SilvaKupuUI(textstyleselectid) {
this.tsselect = getFromSelector(textstyleselectid);
this.updateState = function(selNode) {
/* set the text-style pulldown */
// first get the nearest style
var styles = {}; // use an object here so we can use the 'in' operator later on
for (var i=0; i < this.tsselect.options.length; i++) {
// XXX we should cache this
styles[this.tsselect.options[i].value] = i;
}
// search the list of nodes like in the original one, break if we encounter a match,
// this method does some more than the original one since it can handle commands in
// the form of '<style>|<classname>' next to the plain '<style>' commands
var currnode = selNode;
var index = -1;
while (index==-1 && currnode) {
var nodename = currnode.nodeName.toLowerCase();
for (var style in styles) {
if (style.indexOf('|') < 0) {
// simple command
if (nodename == style.toLowerCase() && !currnode.className) {
index = styles[style];
break;
};
} else {
// command + classname
var tuple = style.split('|');
if (nodename == tuple[0].toLowerCase() && currnode.className == tuple[1]) {
index = styles[style];
break;
};
};
};
currnode = currnode.parentNode;
}
this.tsselect.selectedIndex = Math.max(index,0);
};
this.setTextStyle = function(style) {
/* parse the argument into a type and classname part
generate a block element accordingly
*/
// XXX somehow this method always gets called twice... I would
// really like to know why, but can't find it right now and don't
// have time for a full investigation, so fiddle-fixed it this
// way. Needless to say this needs some investigation at some point...
if (this._cancel_update) {
this._cancel_update = false;
return;
};
var classname = "";
var eltype = style;
if (style.indexOf('|') > -1) {
style = style.split('|');
eltype = style[0];
classname = style[1];
};
var command = eltype;
// first create the element, then find it and set the classname
if (this.editor.getBrowserName() == 'IE') {
command = '<' + eltype + '>';
};
this.editor.getDocument().execCommand('formatblock', command);
// now get a reference to the element just added
var selNode = this.editor.getSelectedNode();
var el = this.editor.getNearestParentOfType(selNode, eltype);
// now set the classname
if (classname) {
el.className = classname;
el.setAttribute('silva_type', classname);
};
this._cancel_update = true;
this.editor.updateState();
this.editor.getDocument().getWindow().focus();
};
};
SilvaKupuUI.prototype = new KupuUI;
function SilvaPropertyTool(tablerowid, formid) {
/* a simple tool to edit metadata fields
the fields' contents are stored in Silva's metadata sets
*/
this.tablerow = document.getElementById(tablerowid);
this.form = document.getElementById(formid);
this.table = this.tablerow.parentNode;
while (!this.table.nodeName.toLowerCase() == 'table') {
this.table = this.table.parentNode;
};
// remove current content from the fields
var tds = this.tablerow.getElementsByTagName('td');
for (var i=0; i < tds.length; i++) {
while (tds[i].hasChildNodes()) {
tds[i].removeChild(tds[i].childNodes[0]);
};
};
};
SilvaPropertyTool.prototype = new KupuTool;
SilvaPropertyTool.prototype.initialize = function(editor) {
this.editor = editor;
// walk through all metadata fields and expose them to the user
var metas = this.editor.getInnerDocument().getElementsByTagName('meta');
for (var i=0; i < metas.length; i++) {
var meta = metas[i];
var name = meta.getAttribute('name');
if (!name) {
// http-equiv type
continue;
};
var rowcopy = this.tablerow.cloneNode(true);
this.tablerow.parentNode.appendChild(rowcopy);
// create the form elements, pass in the rowcopy so the row can be
// rendered real-time, this because IE doesn't select checkboxes that
// arent' visible(!!)
this.parseFormElIntoRow(meta, rowcopy);
/*
if (tag) {
this.tablerow.parentNode.appendChild(tag);
};
*/
};
// throw away the original row: we don't need it anymore...
this.tablerow.parentNode.removeChild(this.tablerow);
};
SilvaPropertyTool.prototype.parseFormElIntoRow = function(metatag, tablerow) {
/* render a field in the properties tool according to a metadata tag
returns some false value if the meta tag should not be editable
*/
var scheme = metatag.getAttribute('scheme');
if (!scheme || !(scheme in EDITABLE_METADATA)) {
return;
};
var name = metatag.getAttribute('name');
var namespace = metatag.getAttribute('scheme');
var nametypes = EDITABLE_METADATA[scheme];
var type = 'text';
var mandatory = false;
var namefound = false;
var fieldtitle = '';
for (var i=0; i < nametypes.length; i++) {
var nametype = nametypes[i];
var elname = nametype[0];
var type = nametype[1];
var mandatory = nametype[2];
var fieldtitle = nametype[3];
if (elname == name) {
namefound = true;
break;
};
};
if (!namefound) {
return;
};
var titlefield = document.createElement('span');
var title = document.createTextNode(fieldtitle);
titlefield.appendChild(title);
tablerow.getElementsByTagName('td')[0].appendChild(titlefield);
titlefield.className = 'metadata-field';
var value = metatag.getAttribute('content');
var parentvalue = metatag.getAttribute('parentcontent');
var td = tablerow.getElementsByTagName('td')[1]
if (type == 'text' || type == 'textarea' || type == 'datetime') {
this._createSimpleItemHTML(type, value, name,
namespace, mandatory, td);
} else if (type == 'checkbox') {
var titlecell = tablerow.getElementsByTagName('td')[0];
this._createCheckboxItemHTML(titlecell, value, name, namespace,
mandatory, td);
};
if (parentvalue && parentvalue != '') {
td.appendChild(document.createElement('br'));
td.appendChild(document.createTextNode('acquired value:'));
td.appendChild(document.createElement('br'));
td.appendChild(document.createTextNode(parentvalue));
};
return tablerow;
};
// just to make the above method a bit more readable
SilvaPropertyTool.prototype._createSimpleItemHTML = function(type, value,
name, namespace,
mandatory, td) {
var input = null;
if (type == 'text' || type == 'datetime') {
input = document.createElement('input');
input.setAttribute('type', 'text');
input.value = value;
if (type == 'datetime') {
input.setAttribute('widget:type', 'datetime');
};
} else if (type == 'textarea') {
input = document.createElement('textarea');
var content = document.createTextNode(value);
input.appendChild(content);
};
input.setAttribute('name', name);
input.setAttribute('namespace', namespace);
input.className = 'metadata-input';
if (mandatory) {
input.setAttribute('mandatory', 'true');
};
td.appendChild(input);
};
SilvaPropertyTool.prototype._createCheckboxItemHTML = function(titlecell,
value, name, namespace,
mandatory, td) {
// elements are seperated by ||
var infos = value.split('||');
// messy stuff coming up, that make the checkboxes appear in some
// 'foldable' div
var outerdiv = document.createElement('div');
outerdiv.className = 'kupu-properties-checkbox-outerdiv';
// the arrow and 'items' label
var itemsdiv = document.createElement('div');
outerdiv.appendChild(itemsdiv);
var img = document.createElement('img');
// XXX would be nice if this would be absolute...
img.src = 'kupu_silva/closed_arrow.gif';
outerdiv.image = img; // XXX memory leak!!
itemsdiv.appendChild(img);
itemsdiv.appendChild(document.createTextNode(_('items')));
itemsdiv.setAttribute('title', _('click to unfold'));
// handler for showing/hiding the checkbox divs
var handler = function(evt) {
if (this.lastChild.style.display == 'none') {
this.image.src = 'kupu_silva/opened_arrow.gif';
this.image.setAttribute('title', _('click to fold'));
this.lastChild.style.display = 'block';
} else {
this.image.src = 'kupu_silva/closed_arrow.gif';
this.image.setAttribute('title', _('click to unfold'));
this.lastChild.style.display = 'none';
}
};
addEventHandler(itemsdiv, 'click', handler, outerdiv);
addEventHandler(titlecell, 'click', handler, outerdiv);
// innerdiv is where the actual checkboxes are displayed in, and what
// is collapsed/uncollapsed
var innerdiv = document.createElement('div');
innerdiv.className = 'kupu-properties-checkbox-innerdiv';
outerdiv.appendChild(innerdiv);
td.appendChild(outerdiv);
for (var i=0; i < infos.length; i++) {
// in certain cases the value you want to display is different
// from that you want to store, in that case seperate id from
// value with a |, there should always be a value|checked, but
// in some cases you may want a value|title|checked set...
var info = infos[i].split('|');
var itemvalue = info[0];
var title = info[0];
var checked = (info[1] == 'true' || info[1] == 'yes');
if (info.length == 3) {
title = info[1];
checked = (info[2] == 'true' || info[2] == 'yes');
};
var div = document.createElement('div');
div.className = 'kupu-properties-checkbox-line';
innerdiv.appendChild(div);
var cbdiv = document.createElement('div');
cbdiv.className = 'kupu-properties-checkbox-input';
div.appendChild(cbdiv);
var checkbox = document.createElement('input');
checkbox.setAttribute('name', name);
checkbox.setAttribute('namespace', namespace);
checkbox.type = 'checkbox';
checkbox.value = itemvalue;
cbdiv.appendChild(checkbox);
if (checked) {
checkbox.checked = 'checked';
};
checkbox.className = 'metadata-checkbox';
// XXX a bit awkward to set this on all checkboxes
if (mandatory) {
checkbox.setAttribute('mandatory', 'true');
};
var textdiv = document.createElement('div');
textdiv.className = 'kupu-properties-checkbox-item-title';
textdiv.appendChild(document.createTextNode(title));
div.appendChild(textdiv);
};
// we can not hide the checkboxes earlier because IE requires them
// to be *visible* in order to check them from code :(
innerdiv.style.display = 'none';
};
SilvaPropertyTool.prototype.beforeSave = function() {
/* save the metadata to the document */
if (window.widgeteer) {
widgeteer.widget_registry.prepareForm(this.form);
};
var doc = this.editor.getInnerDocument();
var inputs = this.table.getElementsByTagName('input');
var textareas = this.table.getElementsByTagName('textarea');
var checkboxdata = {}; // name: value for all checkboxes checked
var errors = [];
var okay = [];
for (var i=0; i < inputs.length; i++) {
var input = inputs[i];
if (!input.getAttribute('namespace')) {
continue;
};
var name = input.getAttribute('name');
var scheme = input.getAttribute('namespace');
if (input.getAttribute('type') == 'text') {
var value = input.value;
if (input.getAttribute('mandatory') && value.strip() == '') {
errors.push(name);
continue;
};
okay.push([name, scheme, value]);
} else if (input.getAttribute('type') == 'checkbox') {
if (checkboxdata[name] === undefined) {
checkboxdata[name] = [];
// XXX yuck!!
checkboxdata[name].namespace = scheme;
checkboxdata[name].mandatory =
input.getAttribute('mandatory') ? true : false;
};
if (input.checked) {
checkboxdata[name].push(
input.value.replace('|', '&pipe;', 'g'));
};
};
};
for (var i=0; i < textareas.length; i++) {
var textarea = textareas[i];
var name = textarea.getAttribute('name');
var scheme = textarea.getAttribute('namespace');
var value = textarea.value;
if (textarea.getAttribute('mandatory') && value.strip() == '') {
errors.push(name);
continue;
};
okay.push([name, scheme, value]);
};
for (var name in checkboxdata) {
if (checkboxdata[name].mandatory && checkboxdata[name].length == 0) {
errors.push(name);
} else {
var data = checkboxdata[name];
okay.push([name, data.namespace, data.join('|')]);
};
};
if (errors.length) {
throw('Error in properties: fields ' + errors.join(', ') +
' are required but not filled in');
};
for (var i=0; i < okay.length; i++) {
this._addMetaTag(doc, okay[i][0], okay[i][1], okay[i][2]);
};
};
SilvaPropertyTool.prototype._addMetaTag = function(doc, name, scheme,
value, parentvalue) {
var head = doc.getElementsByTagName('head')[0];
if (!head) {
throw('The editable document *must* have a <head> element!');
};
// first find and delete the old one
// XXX if only we'd have XPath...
var metas = doc.getElementsByTagName('meta');
for (var i=0; i < metas.length; i++) {
var meta = metas[i];
if (meta.getAttribute('name') == name &&
meta.getAttribute('scheme') == scheme) {
meta.parentNode.removeChild(meta);
};
};
var tag = doc.createElement('meta');
tag.setAttribute('name', name);
tag.setAttribute('scheme', scheme);
tag.setAttribute('content', value);
head.appendChild(tag);
};
function SilvaCharactersTool(charselectid) {
/* a tool to add non-standard characters */
this._charselect = document.getElementById(charselectid);
};
SilvaCharactersTool.prototype = new KupuTool;
SilvaCharactersTool.prototype.initialize = function(editor) {
this.editor = editor;
addEventHandler(this._charselect, 'change', this.addCharacter, this);
var chars = this.editor.config.nonstandard_chars.split(' ');
for (var i=0; i < chars.length; i++) {
var option = document.createElement('option');
option.value = chars[i];
var text = document.createTextNode(chars[i]);
option.appendChild(text);
this._charselect.appendChild(option);
};
};
SilvaCharactersTool.prototype.addCharacter = function() {
var select = this._charselect;
var c = select.options[select.selectedIndex].value;
if (!c.strip()) {
return;
};
var selection = this.editor.getSelection();
var textnode = this.editor.getInnerDocument().createTextNode(c);
var span = this.editor.getInnerDocument().createElement('span');
span.appendChild(textnode);
selection.replaceWithNode(span);
var selection = this.editor.getSelection();
selection.selectNodeContents(span);
selection.moveEnd(1);
selection.collapse(true);
this.editor.logMessage('Character ' + c + ' inserted');
this.editor.getDocument().getWindow().focus();
select.selectedIndex = 0;
};