diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2018-06-09 19:25:33 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2018-06-09 19:25:33 +0200 |
commit | aca28de01e7327a45ce025303fc2acc5c3813406 (patch) | |
tree | ad576b08f361675ee0d4a9eae73b400450431980 /static-files/journal/wymeditor/plugins | |
parent | 9d5115e52c4c42af8249d8eb6f0ad3b8030f7c8d (diff) |
Use WYMeditor as the article text editor.
WYMeditor produces much better XHTML than Trumbowyg.
Diffstat (limited to 'static-files/journal/wymeditor/plugins')
-rw-r--r-- | static-files/journal/wymeditor/plugins/fullscreen/icon_fullscreen.gif | bin | 0 -> 509 bytes | |||
-rw-r--r-- | static-files/journal/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js | 126 | ||||
-rw-r--r-- | static-files/journal/wymeditor/plugins/list/jquery.wymeditor.list.js | 44 | ||||
-rw-r--r-- | static-files/journal/wymeditor/plugins/rdfa/jquery.wymeditor.rdfa.js | 192 | ||||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/jquery.wymeditor.table.js | 761 | ||||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/table_delete_column.png | bin | 0 -> 744 bytes | |||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/table_delete_row.png | bin | 0 -> 683 bytes | |||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/table_insert_column.png | bin | 0 -> 720 bytes | |||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/table_insert_row.png | bin | 0 -> 667 bytes | |||
-rw-r--r-- | static-files/journal/wymeditor/plugins/table/table_join_row.png | bin | 0 -> 685 bytes |
10 files changed, 1123 insertions, 0 deletions
diff --git a/static-files/journal/wymeditor/plugins/fullscreen/icon_fullscreen.gif b/static-files/journal/wymeditor/plugins/fullscreen/icon_fullscreen.gif Binary files differnew file mode 100644 index 0000000..d2a8b0a --- /dev/null +++ b/static-files/journal/wymeditor/plugins/fullscreen/icon_fullscreen.gif diff --git a/static-files/journal/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js b/static-files/journal/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js new file mode 100644 index 0000000..faaa693 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js @@ -0,0 +1,126 @@ +/* + * WYMeditor : what you see is What You Mean web-based editor + * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ + * Dual licensed under the MIT (MIT-license.txt) + * and GPL (GPL-license.txt) licenses. + * + * For further information visit: + * http://www.wymeditor.org/ + * + * File Name: + * jquery.wymeditor.fullscreen.js + * Fullscreen plugin for WYMeditor + * + * File Authors: + * Luis Santos (luis.santos a-t openquest dotpt) + * Jonatan Lundin (jonatan.lundin a-t gmail dotcom) + * Gerd Riesselmann (gerd a-t gyro-php dot org) : Fixed issue with new skin layout + * Philipp Cordes (pc a-t irgendware dotnet) + */ + +//Extend WYMeditor +WYMeditor.editor.prototype.fullscreen = function() { + var wym = this, + $box = jQuery(wym._box), + $iframe = jQuery(wym._iframe), + $overlay = null, + $window = jQuery(window), + + editorMargin = 15; // Margin from window (without padding) + + + //construct the button's html + var html = '' + + "<li class='wym_tools_fullscreen'>" + + "<a name='Fullscreen' href='#' " + + "title='Fullscreen' " + + "style='background-image: url(" + + wym._options.basePath + + "plugins/fullscreen/icon_fullscreen.gif)'>" + + "Fullscreen" + + "</a>" + + "</li>"; + //add the button to the tools box + $box.find(wym._options.toolsSelector + wym._options.toolsListSelector) + .append(html); + + function resize () { + // Calculate margins + var uiHeight = $box.outerHeight(true) - $iframe.outerHeight(true); + var editorPadding = $box.outerWidth() - $box.width(); + + // Calculate heights + var screenHeight = $window.height(); + var iframeHeight = (screenHeight - uiHeight - (editorMargin * 2)) + 'px'; + + // Calculate witdths + var screenWidth = $window.width(); + var boxWidth = (screenWidth - editorPadding - (editorMargin * 2)) + 'px'; + + $box.css('width', boxWidth); + $iframe.css('height', iframeHeight); + $overlay.css({ + 'height': screenHeight + 'px', + 'width': screenWidth + 'px' + }); + } + + //handle click event + $box.find('li.wym_tools_fullscreen a').click(function() { + if ($box.css('position') != 'fixed') { + // Store previous inline styles + $box.data('wym-inline-css', $box.attr('style')); + $iframe.data('wym-inline-css', $iframe.attr('style')); + + // Create overlay + $overlay = jQuery('<div id="wym-fullscreen-overlay"></div>') + .appendTo('body').css({ + 'position': 'fixed', + 'background-color': 'rgb(0, 0, 0)', + 'opacity': '0.75', + 'z-index': '98', + 'top': '0px', + 'left': '0px' + }); + + // Possition the editor + $box.css({ + 'position': 'fixed', + 'z-index': '99', + 'top': editorMargin + 'px', + 'left': editorMargin + 'px' + }); + + // Bind event listeners + $window.bind('resize', resize); + $box.find('li.wym_tools_html a').bind('click', resize); + + // Force resize + resize(); + } else { + // Unbind event listeners + $window.unbind('resize', resize); + $box.find('li.wym_tools_html a').unbind('click', resize); + + // Remove inline styles + $box.css({ + 'position': 'static', + 'z-index': '', + 'width': '', + 'top': '', + 'left': '' + }); + $iframe.css('height', ''); + + // Remove overlay + $overlay.remove(); + $overlay = null; + + // Retore previous inline styles + $box.attr('style', $box.data('wym-inline-css')); + $iframe.attr('style', $iframe.data('wym-inline-css')); + } + + return false; + }); +}; diff --git a/static-files/journal/wymeditor/plugins/list/jquery.wymeditor.list.js b/static-files/journal/wymeditor/plugins/list/jquery.wymeditor.list.js new file mode 100644 index 0000000..b009e25 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/list/jquery.wymeditor.list.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2011 PolicyStat LLC. + * MIT licensed (MIT-license.txt) + * + * This plugin adds the ability to use tab and shift+tab to indent/outdent + * lists, mimicking a user's expected behavior when inside an editor. + * + * @author Wes Winham (winhamwr@gmail.com) + */ + +function ListPlugin(options, wym) { + var listPlugin = this; + ListPlugin._options = jQuery.extend({}, options); + listPlugin._wym = wym; + + listPlugin.init(); +} + +ListPlugin.prototype.init = function() { + var listPlugin = this; + listPlugin._wym.listPlugin = listPlugin; + + listPlugin.bindEvents(); +}; + +ListPlugin.prototype.bindEvents = function() { + var listPlugin = this, + wym = listPlugin._wym; + + wym.keyboard.combokeys.bind( + "tab", + function () { + wym.indent(); + return false; + } + ); + wym.keyboard.combokeys.bind( + "shift+tab", + function () { + wym.outdent(); + return false; + } + ); +}; diff --git a/static-files/journal/wymeditor/plugins/rdfa/jquery.wymeditor.rdfa.js b/static-files/journal/wymeditor/plugins/rdfa/jquery.wymeditor.rdfa.js new file mode 100644 index 0000000..2323440 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/rdfa/jquery.wymeditor.rdfa.js @@ -0,0 +1,192 @@ +/* + * WYMeditor : what you see is What You Mean web-based editor + * Copyright (c) 2005 - 2011 Jean-Francois Hovinne, http://www.wymeditor.org/ + * Dual licensed under the MIT (MIT-license.txt) + * and GPL (GPL-license.txt) licenses. + * + * For further information visit: + * http://www.wymeditor.org/ + * + * File Name: + * jquery.wymeditor.rdfa.js + * RDFa plugin for WYMeditor + * + * File Authors: + * Jean-Francois Hovinne (@jfhovinne) + */ + +//Extend WYMeditor +WYMeditor.editor.prototype.rdfa = function (options) { + var wym = this, + rdfa = new WYMeditor.RDFa(options, wym); + return rdfa; +}; + +//RDFa constructor +WYMeditor.RDFa = function (options, wym) { + var rdfa = this; + options = jQuery.extend({ + setStdNameSpaces: true, + extendXHTMLParser: true, + buttons: {} + }, options); + + rdfa._options = options; + rdfa._wym = wym; + rdfa.init(); +}; + +//RDFa plugin init +WYMeditor.RDFa.prototype.init = function () { + var rdfa = this; + if (rdfa._options.setStdNameSpaces) { + rdfa.setStdNameSpaces(); + } + if (rdfa._options.extendXHTMLParser) { + rdfa.extendXHTMLParser(); + } + rdfa.setButtons(); +}; + +//Adding the namespaces to the document +WYMeditor.RDFa.prototype.setStdNameSpaces = function () { + var rdfa = this; + rdfa.addNameSpace('xmlns', 'http://www.w3.org/1999/xhtml'); + rdfa.addNameSpace('version', 'XHTML+RDFa 1.0'); +}; + +WYMeditor.RDFa.prototype.addNameSpace = function (attr, value) { + var rdfa = this; + jQuery('html', rdfa._wym._doc) + .attr(attr, value); +}; + +WYMeditor.RDFa.prototype.extendXHTMLParser = function () { + var rdfa = this; + rdfa.extendAttributes(); + rdfa.setStdVocabularies(); + rdfa.extendLinkAttributes(); +}; + +WYMeditor.RDFa.prototype.extendAttributes = function () { + //Add the RDFa attributes + WYMeditor.XhtmlValidator._attributes.core.attributes.push( + 'rel', + 'rev', + 'content', + 'href', + 'src', + 'about', + 'property', + 'resource', + 'datatype', + 'typeof' + ); +}; + +WYMeditor.RDFa.prototype.setStdVocabularies = function () { + var rdfa = this; + //Add the 'standard' vocabularies + vocabularies = [ + 'xmlns:biblio', + 'xmlns:cc', + 'xmlns:dbp', + 'xmlns:dbr', + 'xmlns:dc', + 'xmlns:ex', + 'xmlns:foaf', + 'xmlns:rdf', + 'xmlns:rdfs', + 'xmlns:taxo', + 'xmlns:xhv', + 'xmlns:xsd' + ]; + jQuery.each(vocabularies, function (index, vocabulary) { + rdfa.addVocabulary(vocabulary); + }); +}; + +WYMeditor.RDFa.prototype.addVocabulary = function (vocabulary) { + WYMeditor.XhtmlValidator._attributes.core.attributes.push(vocabulary); +}; + +WYMeditor.RDFa.prototype.extendLinkAttributes = function () { + //Overwrite the <a> attributes 'rel' and 'rev' + WYMeditor.XhtmlValidator._tags.a = { + "attributes": { + "0": "charset", + "1": "coords", + "2": "href", + "3": "hreflang", + "4": "name", + "5": "rel", + "6": "rev", + "shape": /^(rect|rectangle|circ|circle|poly|polygon)$/, + "7": "type" + } + }; +}; + +WYMeditor.RDFa.prototype.setButtons = function () { + var rdfa = this, + list = jQuery(rdfa._wym._box).find('div.wym_classes ul'); + jQuery.each(rdfa._options.buttons, function (index, button) { + list + .append('<li></li>') + .children(':last') + .append('<a></a>') + .children(':last') + .attr('href', '#') + .text(button.title) + .bind('click', + { + instance: rdfa._wym, + button: button, + ns: button.ns, + attr: button.attr, + value: button.value + }, + rdfa.clickButtonHandler); + }); +}; + +WYMeditor.RDFa.prototype.clickButtonHandler = function (evt) { + var wym = evt.data.instance, + selected = wym.selectedContainer(); + + //the attribute already exists, remove it + if (typeof jQuery(selected).attr(evt.data.attr) !== 'undefined' && + jQuery(selected).attr(evt.data.attr) !== '') { + WYMeditor.console.log( + 'attribute already exists, remove it:', + evt.data.attr, + jQuery(selected).attr(evt.data.attr) + ); + jQuery(selected) + .removeAttr(evt.data.attr) + .removeClass(evt.data.ns) + .removeClass(evt.data.attr) + .removeClass(evt.data.value); + + //else, add it + } else { + WYMeditor.console.log('attribute does not exist, add it:', evt.data.attr, evt.data.value); + if (evt.data.value) { //value available + jQuery(selected) + .attr(evt.data.attr, evt.data.ns + ':' + evt.data.value) + .addClass(evt.data.ns) + .addClass(evt.data.attr) + .addClass(evt.data.value); + } else { //value not available + evt.data.value = prompt('Value', ''); + if (evt.data.value !== null) { + jQuery(selected) + .attr(evt.data.attr, evt.data.value) + .addClass(evt.data.ns) + .addClass(evt.data.attr) + .addClass(evt.data.value); + } + } + } + return false; +}; diff --git a/static-files/journal/wymeditor/plugins/table/jquery.wymeditor.table.js b/static-files/journal/wymeditor/plugins/table/jquery.wymeditor.table.js new file mode 100644 index 0000000..d258cb5 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/jquery.wymeditor.table.js @@ -0,0 +1,761 @@ +/* global rangy */ +"use strict"; + +// Fugue icons by Yusuke Kamiyamane http://p.yusukekamiyamane.com/ +// and licensed under Creative Commons Attribution + +/** + * A Table editing plugin that gives the user ability to add and remove + * rows and columns as well as merge rows and columns. + * + * @param options A configuration object. + * @param wym The WYMeditor instance to which the TableEditor should attach. + * @class + */ +function TableEditor(options, wym) { + var tableEditor = this; + options = jQuery.extend({ + sMergeRowButtonHtml: String() + + '<li class="wym_tools_merge_row">' + + '<a name="merge_row" href="#" title="Merge Cells" ' + + 'style="background-image: ' + + "url('" + wym._options.basePath + + "plugins/table/table_join_row.png')" + '">' + + 'Merge Table Row' + + '</a>' + + '</li>', + + sMergeRowButtonSelector: "li.wym_tools_merge_row a", + + sAddRowButtonHtml: String() + + "<li class='wym_tools_add_row'>" + + "<a name='add_row' href='#' " + + "title='Add Row' " + + "style='background-image:" + + " url(" + wym._options.basePath + + "plugins/table/table_insert_row.png)'>" + + "Add Table Row" + + "</a>" + + "</li>", + sAddRowButtonSelector: "li.wym_tools_add_row a", + + sRemoveRowButtonHtml: String() + + "<li class='wym_tools_remove_row'>" + + "<a name='remove_row' href='#' " + + "title='Remove Row' " + + "style='background-image: " + + "url(" + wym._options.basePath + + "plugins/table/table_delete_row.png)'>" + + "Remove Table Row" + + "</a>" + + "</li>", + sRemoveRowButtonSelector: "li.wym_tools_remove_row a", + + sAddColumnButtonHtml: String() + + "<li class='wym_tools_add_column'>" + + "<a name='add_column' href='#' " + + "title='Add Column' " + + "style='background-image: " + + "url(" + wym._options.basePath + + "plugins/table/table_insert_column.png)'>" + + "Add Table Column" + + "</a>" + + "</li>", + sAddColumnButtonSelector: "li.wym_tools_add_column a", + + sRemoveColumnButtonHtml: String() + + "<li class='wym_tools_remove_column'>" + + "<a name='remove_column' href='#' " + + "title='Remove Column' " + + "style='background-image: " + + "url(" + wym._options.basePath + + "plugins/table/table_delete_column.png)'>" + + "Remove Table Column" + + "</a>" + + "</li>", + sRemoveColumnButtonSelector: "li.wym_tools_remove_column a", + + enableCellTabbing: true + + }, options); + + tableEditor._options = options; + tableEditor._wym = wym; + + tableEditor.init(); +} + +/** + * Construct and return a table objects using the given options object. + * + * @param options The configuration object. + */ +WYMeditor.editor.prototype.table = function (options) { + var wym = this, + tableEditor = new TableEditor(options, wym); + wym.tableEditor = tableEditor; + + return tableEditor; +}; + +/** + * Initialize the TableEditor object by adding appropriate toolbar buttons and + * binding any required event listeners. + */ +TableEditor.prototype.init = function () { + var tableEditor = this, + wym = tableEditor._wym, + // Add the tool panel buttons + tools = jQuery(wym._box).find( + wym._options.toolsSelector + wym._options.toolsListSelector + ); + + tools.append(tableEditor._options.sMergeRowButtonHtml); + tools.append(tableEditor._options.sAddRowButtonHtml); + tools.append(tableEditor._options.sRemoveRowButtonHtml); + tools.append(tableEditor._options.sAddColumnButtonHtml); + tools.append(tableEditor._options.sRemoveColumnButtonHtml); + + tableEditor.bindEvents(); + rangy.init(); +}; + +/** + * Bind all required event listeners, including button listeners and support + * for tabbing through table cells if enableCellTabbing is true. + */ +TableEditor.prototype.bindEvents = function () { + var tableEditor = this, + wym = tableEditor._wym; + + // Handle tool button click + jQuery(wym._box).find( + tableEditor._options.sMergeRowButtonSelector + ).click(function () { + tableEditor.mergeRow(); + return false; + }); + jQuery(wym._box).find( + tableEditor._options.sAddRowButtonSelector + ).click(function () { + return tableEditor.addRow(wym.selectedContainer()); + }); + jQuery(wym._box).find( + tableEditor._options.sRemoveRowButtonSelector + ).click(function () { + return tableEditor.removeRow(wym.selectedContainer()); + }); + jQuery(wym._box).find( + tableEditor._options.sAddColumnButtonSelector + ).click(function () { + return tableEditor.addColumn(wym.selectedContainer()); + }); + jQuery(wym._box).find( + tableEditor._options.sRemoveColumnButtonSelector + ).click(function () { + return tableEditor.removeColumn(wym.selectedContainer()); + }); + + // Handle tab clicks + if (tableEditor._options.enableCellTabbing) { + jQuery(wym._doc).bind('keydown', tableEditor.keyDown); + } +}; + +/** + * Get the number of columns in a given tr element, accounting for colspan and + * rowspan. This function assumes that the table structure is valid, and will + * return incorrect results for uneven tables. + * + * @param tr The <tr> node whose number of columns we need to count. + * + * @returns {Number} The number of columns in the given tr, accounting for + * colspan and rowspan. + */ +TableEditor.prototype.getNumColumns = function (tr) { + var tableEditor = this, + wym = tableEditor._wym, + numColumns = 0, + table, + firstTr; + + table = wym.findUp(tr, 'table'); + firstTr = jQuery(table).find('tr:eq(0)'); + + // Count the tds and ths in the FIRST ROW of this table, accounting for + // colspan. We count the first td because it won't have any rowspan's + // before it to complicate things + jQuery(firstTr).children('td,th').each(function (index, elmnt) { + numColumns += TableEditor.GET_COLSPAN_PROP(elmnt); + }); + + return numColumns; +}; + +/** + TableEditor.GET_COLSPAN_PROP + ============================ + + Get the integer value of the inferred colspan property on the given cell in + a cross-browser compatible way that's also compatible across jquery + versions. + + jquery 1.6 changed the way .attr works, which affected certain browsers + differently with regard to colspan and rowspan for cells that didn't + explicitly have that attribute set. +*/ +TableEditor.GET_COLSPAN_PROP = function (cell) { + var colspan = jQuery(cell).attr('colspan'); + if (typeof colspan === 'undefined') { + colspan = 1; + } + return parseInt(colspan, 10); +}; + +/** + TableEditor.GET_ROWSPAN_PROP + ============================ + + Get the integer value of the inferred rowspan property on the given cell in + a cross-browser compatible way that's also compatible across jquery + versions. + + See GET_COLSPAN_PROP for details +*/ +TableEditor.GET_ROWSPAN_PROP = function (cell) { + var rowspan = jQuery(cell).attr('rowspan'); + if (typeof rowspan === 'undefined') { + rowspan = 1; + } + return parseInt(rowspan, 10); +}; +/** + * Get the X grid index of the given td or th table cell (0-indexed). This + * takes in to account all colspans and rowspans. + * + * @param cell The td or th node whose X index we're returning. + */ +TableEditor.prototype.getCellXIndex = function (cell) { + var tableEditor = this, + i, + parentTr, + baseRowColumns, + rowColCount, + missingCells, + rowspanIndexes, + checkTr, + rowOffset, + trChildren, + elmnt, + colspan, + indexCounter, + cellIndex; + parentTr = jQuery(cell).parent('tr')[0]; + + baseRowColumns = tableEditor.getNumColumns(parentTr); + + // Figure out how many explicit cells are missing which is how many + // rowspans we're affected by + rowColCount = 0; + jQuery(parentTr).children('td,th').each(function (index, elmnt) { + rowColCount += TableEditor.GET_COLSPAN_PROP(elmnt); + }); + + missingCells = baseRowColumns - rowColCount; + rowspanIndexes = []; + checkTr = parentTr; + rowOffset = 1; + + // If this cell is affected by a rowspan from farther up the table, + // we need to take in to account any possible colspan attributes on that + // cell. Store the real X index of the cells to the left of our cell to use + // in the colspan calculation. + while (missingCells > 0) { + checkTr = jQuery(checkTr).prev('tr'); + rowOffset += 1; + trChildren = jQuery(checkTr).children('td,th'); + for (i = 0; i < trChildren.length; i++) { + elmnt = trChildren[i]; + if (TableEditor.GET_ROWSPAN_PROP(elmnt) >= rowOffset) { + // Actually affects our source row + missingCells -= 1; + colspan = TableEditor.GET_COLSPAN_PROP(elmnt); + rowspanIndexes[tableEditor.getCellXIndex(elmnt)] = colspan; + } + } + } + + indexCounter = 0; + cellIndex = null; + // Taking in to account the real X indexes of all of the columns to the + // left of this cell, determine the real X index. + jQuery(parentTr).children('td,th').each(function (index, elmnt) { + if (cellIndex !== null) { + // We've already iterated to the cell we're checking + return; + } + // Account for an inferred colspan created by a rowspan from above + while (typeof rowspanIndexes[indexCounter] !== 'undefined') { + indexCounter += parseInt(rowspanIndexes[indexCounter], 10); + } + if (elmnt === cell) { + // We're at our cell, no need to keep moving to the right. + // Signal this by setting the cellIndex + cellIndex = indexCounter; + return; + } + // Account for an explicit colspan on this cell + indexCounter += TableEditor.GET_COLSPAN_PROP(elmnt); + }); + + if (cellIndex === null) { + // Somehow, we never found the cell when iterating over its row. + throw "Cell index not found"; + } + return cellIndex; +}; + +/** + * Get the number of columns represented by the given array of contiguous cell + * (td/th) nodes. + * Accounts for colspan and rowspan attributes. + * + * @param cells An array of td/th nodes whose total column span we're checking. + * + * @return {Number} The number of columns represented by the "cells" + */ +TableEditor.prototype.getTotalColumns = function (cells) { + var tableEditor = this, + rootTr = tableEditor.getCommonParentTr(cells), + baseRowColumns, + colspanCount, + rowColCount, + lastCell, + firstCell; + + if (rootTr === null) { + // Non-contiguous columns + throw "getTotalColumns only allowed for contiguous cells"; + } + + baseRowColumns = tableEditor.getNumColumns(rootTr); + + // Count the number of simple columns, not accounting for rowspans + colspanCount = 0; + jQuery(cells).each(function (index, elmnt) { + colspanCount += TableEditor.GET_COLSPAN_PROP(elmnt); + }); + + // Determine if we're affected by rowspans. If the number of simple columns + // in the row equals the number of columns in the first row, we don't have + // any rowspans + rowColCount = 0; + jQuery(rootTr).children('td,th').each(function (index, elmnt) { + rowColCount += TableEditor.GET_COLSPAN_PROP(elmnt); + }); + + if (rowColCount === baseRowColumns) { + // Easy case. No rowspans to deal with + return colspanCount; + } else { + if (cells.length === 1) { + // Easy. Just the colspan + return TableEditor.GET_COLSPAN_PROP(cells[0]); + } else { + lastCell = jQuery(cells).eq(cells.length - 1)[0]; + firstCell = jQuery(cells).eq(0)[0]; + // On jQuery 1.4 upgrade, jQuery(cells).eq(-1) + return 1 + tableEditor.getCellXIndex(lastCell) - + tableEditor.getCellXIndex(firstCell); + } + } +}; + +/** + * Merge the table cells in the given selection using a colspan. + * + * @return {Boolean} true if changes are made, false otherwise + */ +TableEditor.prototype.mergeRow = function () { + var tableEditor = this, + wym = tableEditor._wym, + // Get all of the affected nodes in the range + nodes = wym._getSelectedNodes(), + cells, + rootTr, + mergeCell, + $elmnt, + rowspanProp, + newContent, + combinedColspan; + + wym.deselect(); + + // Just use the td and th nodes + cells = jQuery(nodes).filter('td,th'); + if (cells.length === 0) { + return false; + } + + // If the selection is across multiple tables, don't merge + rootTr = tableEditor.getCommonParentTr(cells); + if (rootTr === null) { + return false; + } + + mergeCell = cells[0]; + // If any of the cells have a rowspan, create the inferred cells + jQuery(cells).each(function (i, elmnt) { + $elmnt = jQuery(elmnt); + rowspanProp = TableEditor.GET_ROWSPAN_PROP(elmnt); + if (rowspanProp <= 1) { + // We don't care about cells without a rowspan + return; + } + + // This cell has an actual rowspan, we need to account for it + // Figure out the x index for this cell in the table grid + var index = tableEditor.getCellXIndex(elmnt), + // Create the previously-inferred cell in the appropriate index + // with one less rowspan + newRowspan = rowspanProp - 1, + newTd, + insertionIndex, + insertionCells, + cellInserted, + xIndex; + if (newRowspan === 1) { + newTd = '<td>' + $elmnt.html() + '</td>'; + } else { + newTd = String() + + '<td rowspan="' + newRowspan + '">' + + $elmnt.html() + + '</td>'; + } + if (index === 0) { + $elmnt.parent('tr') + .next('tr') + .prepend(newTd); + } else { + // TODO: account for colspan/rowspan with insertion + // Account for colspan/rowspan by walking from right to left + // looking for the cell closest to the desired index to APPEND to + insertionIndex = index - 1; + insertionCells = $elmnt.parent('tr').next('tr').find('td,th'); + cellInserted = false; + for (i = insertionCells.length - 1; i >= 0; i--) { + xIndex = tableEditor.getCellXIndex(insertionCells[i]); + if (xIndex <= insertionIndex) { + jQuery(insertionCells[i]).after(newTd); + cellInserted = true; + break; + } + } + if (!cellInserted) { + // Bail out now before we clear HTML and break things + throw "Cell rowspan invalid"; + } + } + + // Clear the cell's html, since we just moved it down + $elmnt.html(''); + }); + + // Remove any rowspan from the mergecell now that we've shifted rowspans + // down + // ie fails when we try to remove a rowspan for some reason + try { + jQuery(mergeCell).removeAttr('rowspan'); + } catch (err) { + jQuery(mergeCell).attr('rowspan', 1); + } + + // Build the content of the new combined cell from all of the included + // cells + newContent = ''; + jQuery(cells).each(function (index, elmnt) { + newContent += jQuery(elmnt).html(); + }); + + // Add a colspan to the farthest-left cell + combinedColspan = tableEditor.getTotalColumns(cells); + if (jQuery.browser.msie) { + // jQuery.attr doesn't work for colspan in ie + mergeCell.colSpan = combinedColspan; + } else { + jQuery(mergeCell).attr('colspan', combinedColspan); + } + + // Delete the rest of the cells + jQuery(cells).each(function (index, elmnt) { + if (index !== 0) { + jQuery(elmnt).remove(); + } + }); + + // Change the content in our newly-merged cell + jQuery(mergeCell).html(newContent); + + tableEditor.selectElement(mergeCell); + + wym.registerModification(); + return true; +}; + +/** + * Add a row to the given elmnt (representing a <tr> or a child of a <tr>). + * + * @param The node which will have a row appended after its parent row. + */ +TableEditor.prototype.addRow = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + tr = tableEditor._wym.findUp(elmnt, 'tr'), + numColumns, + tdHtml, + i; + + if (tr === null) { + return false; + } + + numColumns = tableEditor.getNumColumns(tr); + + tdHtml = ''; + for (i = 0; i < numColumns; i++) { + tdHtml += '<td> </td>'; + } + jQuery(tr).after('<tr>' + tdHtml + '</tr>'); + + wym.registerModification(); + return false; +}; + +/** + * Remove the given table if it doesn't have any rows/columns. + * + * @param table The table to delete if it is empty. + */ +TableEditor.prototype.removeEmptyTable = function (table) { + var tableEditor = this, + wym = tableEditor._wym, + cells = jQuery(table).find('td,th'), + $table; + if (cells.length === 0) { + $table = jQuery(table); + $table.prev('br.' + WYMeditor.BLOCKING_ELEMENT_SPACER_CLASS).remove(); + $table.next('br.' + WYMeditor.BLOCKING_ELEMENT_SPACER_CLASS).remove(); + $table.remove(); + wym.prepareDocForEditing(); + } +}; + +/** + * Remove the row for the given element (representing a <tr> or a child + * of a <tr>). + * + * @param elmnt The node whose parent tr will be removed. + */ +TableEditor.prototype.removeRow = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + tr = wym.findUp(elmnt, 'tr'), + table; + + if (tr === null) { + return false; + } + table = wym.findUp(elmnt, 'table'); + if ( + wym.hasSelection() === true && + wym.doesElementContainSelection(elmnt) === true + ) { + wym.deselect(); + } + jQuery(tr).remove(); + tableEditor.removeEmptyTable(table); + + wym.registerModification(); + return false; +}; + +/** + * Add a column to the given elmnt (representing a <td> or a child of a <td>). + * + * @param elmnt The node which will have a column appended afterward. + */ +TableEditor.prototype.addColumn = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + td = wym.findUp(elmnt, ['td', 'th']), + prevTds, + tdIndex, + tr, + newTd = '<td> </td>', + newTh = '<th> </th>', + insertionElement; + + if (td === null) { + return false; + } + prevTds = jQuery(td).prevAll(); + tdIndex = prevTds.length; + + tr = wym.findUp(td, 'tr'); + jQuery(tr).siblings('tr').andSelf().each(function (index, element) { + insertionElement = newTd; + if (jQuery(element).find('th').length > 0) { + // The row has a TH, so insert a th + insertionElement = newTh; + } + + jQuery(element).find('td,th').eq(tdIndex).after(insertionElement); + }); + + wym.registerModification(); + return false; +}; + +/** + * Remove the column to the right of the given elmnt (representing a <td> or a + * child of a <td>). + */ +TableEditor.prototype.removeColumn = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + td = wym.findUp(elmnt, ['td', 'th']), + table, + prevTds, + tdIndex, + tr; + if (td === null) { + return false; + } + table = wym.findUp(elmnt, 'table'); + prevTds = jQuery(td).prevAll(); + tdIndex = prevTds.length; + + tr = wym.findUp(td, 'tr'); + jQuery(tr).siblings('tr').addBack().each(function (index, element) { + var $cell = jQuery(element).find("td, th").eq(tdIndex); + if ( + wym.hasSelection() === true && + wym.doesElementContainSelection($cell[0]) === true + ) { + wym.deselect(); + } + $cell.remove(); + }); + tableEditor.removeEmptyTable(table); + + wym.registerModification(); + return false; +}; + +/** + * keyDown event handler used for consistent tab key cell movement. + */ +TableEditor.prototype.keyDown = function (evt) { + var doc = this, + wym = WYMeditor.INSTANCES[doc.title], + tableEditor = wym.tableEditor; + + if (evt.which === WYMeditor.KEY_CODE.TAB) { + return tableEditor.selectNextCell(wym.selectedContainer()); + } + + return null; +}; + +/** + * Move the focus to the next cell. + */ +TableEditor.prototype.selectNextCell = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + cell = wym.findUp(elmnt, ['td', 'th']), + nextCells, + tr, + nextRows; + + if (cell === null) { + return null; + } + + // Try moving to the next cell to the right + nextCells = jQuery(cell).next('td,th'); + if (nextCells.length > 0) { + tableEditor.selectElement(nextCells[0]); + return false; + } + + // There was no cell to the right, use the first cell in the next row + tr = wym.findUp(cell, 'tr'); + nextRows = jQuery(tr).next('tr'); + if (nextRows.length !== 0) { + nextCells = jQuery(nextRows).children('td,th'); + if (nextCells.length > 0) { + tableEditor.selectElement(nextCells[0]); + return false; + } + } + + // There is no next row. Do a normal tab + return null; +}; + +/** + * Select the given element using rangy selectors. + */ +TableEditor.prototype.selectElement = function (elmnt) { + var tableEditor = this, + wym = tableEditor._wym, + sel = wym.selection(), + range = rangy.createRange(wym._doc); + + range.setStart(elmnt, 0); + range.setEnd(elmnt, 0); + range.collapse(false); + + try { + sel.setSingleRange(range); + } catch (err) { + // ie8 can raise an "unkown runtime error" trying to empty the range + } + // Old IE selection hack + if (WYMeditor.isInternetExplorerPre11()) { + wym._saveCaret(); + } +}; + +/** + * Get the common parent tr for the given table cell nodes. If the closest + * parent tr for each cell isn't the same, returns null. + */ +TableEditor.prototype.getCommonParentTr = function (cells) { + var firstCell, + parentTrList, + rootTr; + + cells = jQuery(cells).filter('td,th'); + if (cells.length === 0) { + return null; + } + firstCell = cells[0]; + parentTrList = jQuery(firstCell).parent('tr'); + + if (parentTrList.length === 0) { + return null; + } + rootTr = parentTrList[0]; + + // Ensure that all of the cells have the same parent tr + jQuery(cells).each(function (index, elmnt) { + parentTrList = jQuery(elmnt).parent('tr'); + if (parentTrList.length === 0 || parentTrList[0] !== rootTr) { + return null; + } + }); + + return rootTr; +}; diff --git a/static-files/journal/wymeditor/plugins/table/table_delete_column.png b/static-files/journal/wymeditor/plugins/table/table_delete_column.png Binary files differnew file mode 100644 index 0000000..9022d68 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/table_delete_column.png diff --git a/static-files/journal/wymeditor/plugins/table/table_delete_row.png b/static-files/journal/wymeditor/plugins/table/table_delete_row.png Binary files differnew file mode 100644 index 0000000..f9d956f --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/table_delete_row.png diff --git a/static-files/journal/wymeditor/plugins/table/table_insert_column.png b/static-files/journal/wymeditor/plugins/table/table_insert_column.png Binary files differnew file mode 100644 index 0000000..49226ec --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/table_insert_column.png diff --git a/static-files/journal/wymeditor/plugins/table/table_insert_row.png b/static-files/journal/wymeditor/plugins/table/table_insert_row.png Binary files differnew file mode 100644 index 0000000..da435c3 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/table_insert_row.png diff --git a/static-files/journal/wymeditor/plugins/table/table_join_row.png b/static-files/journal/wymeditor/plugins/table/table_join_row.png Binary files differnew file mode 100644 index 0000000..70e5983 --- /dev/null +++ b/static-files/journal/wymeditor/plugins/table/table_join_row.png |