@@ -2377,12 +2402,12 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
if (!node.parentNode) {
return;
}
-
+
if (!node.firstChild) {
node.parentNode.removeChild(node);
return;
}
-
+
var fragment = node.ownerDocument.createDocumentFragment();
while (node.firstChild) {
fragment.appendChild(node.firstChild);
@@ -2416,21 +2441,21 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
function _isBlockElement(node) {
return dom.getStyle("display").from(node) === "block";
}
-
+
function _isLineBreak(node) {
return node.nodeName === "BR";
}
-
+
function _appendLineBreak(element) {
var lineBreak = element.ownerDocument.createElement("br");
element.appendChild(lineBreak);
}
-
+
function resolveList(list, useLineBreaks) {
if (!list.nodeName.match(/^(MENU|UL|OL)$/)) {
return;
}
-
+
var doc = list.ownerDocument,
fragment = doc.createDocumentFragment(),
previousSibling = list.previousElementSibling || list.previousSibling,
@@ -2440,7 +2465,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
shouldAppendLineBreak,
paragraph,
listItem;
-
+
if (useLineBreaks) {
// Insert line break if list is after a non-block element
if (previousSibling && !_isBlockElement(previousSibling) && !_isLineBreak(previousSibling)) {
@@ -2458,7 +2483,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
_appendLineBreak(fragment);
}
}
-
+
listItem.parentNode.removeChild(listItem);
}
} else {
@@ -2480,9 +2505,10 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
list.parentNode.replaceChild(fragment, list);
}
-
+
dom.resolveList = resolveList;
-})(wysihtml5.dom);/**
+})(wysihtml5.dom);
+/**
* Sandbox for executing javascript, parsing css styles and doing dom operations in a secure way
*
* Browser Compatibility:
@@ -2532,7 +2558,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
"referrer",
"write", "open", "close"
];
-
+
wysihtml5.dom.Sandbox = Base.extend(
/** @scope wysihtml5.dom.Sandbox.prototype */ {
@@ -2541,12 +2567,12 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
this.config = wysihtml5.lang.object({}).merge(config).get();
this.editableArea = this._createIframe();
},
-
+
insertInto: function(element) {
if (typeof(element) === "string") {
element = doc.getElementById(element);
}
-
+
element.appendChild(this.editableArea);
},
@@ -2582,7 +2608,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
* In order to make this happen we need to set the "allow-scripts" flag.
* A combination of allow-scripts and allow-same-origin is almost the same as setting no sandbox attribute at all.
* - Chrome & Safari, doesn't seem to support sandboxing correctly when the iframe's html is inlined (no physical document)
- * - IE needs to have the security="restricted" attribute set before the iframe is
+ * - IE needs to have the security="restricted" attribute set before the iframe is
* inserted into the dom tree
* - Believe it or not but in IE "security" in document.createElement("iframe") is false, even
* though it supports it
@@ -2659,7 +2685,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
if (!wysihtml5.browser.supportsSandboxedIframes()) {
// Unset a bunch of sensitive variables
- // Please note: This isn't hack safe!
+ // Please note: This isn't hack safe!
// It more or less just takes care of basic attacks and prevents accidental theft of sensitive information
// IE is secure though, which is the most important thing, since IE is the only browser, who
// takes over scripts & styles into contentEditable elements when copied from external websites
@@ -2674,7 +2700,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
for (i=0, length=documentProperties.length; i
hasAttribute = outerHTML.indexOf(" " + attributeName + "=") != -1;
-
+
return hasAttribute ? node.getAttribute(attributeName) : null;
} else{
return node.getAttribute(attributeName);
}
-};(function(wysihtml5) {
-
+};
+(function(wysihtml5) {
+
var api = wysihtml5.dom;
var MapCell = function(cell) {
@@ -2961,7 +2990,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
this.cell = this.table.querySelectorAll('th, td')[0];
}
};
-
+
function queryInList(list, query) {
var ret = [],
q;
@@ -2973,15 +3002,15 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return ret;
}
-
+
function removeElement(el) {
el.parentNode.removeChild(el);
}
-
+
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
-
+
function nextNode(node, tag) {
var element = node.nextSibling;
while (element.nodeType !=1) {
@@ -2994,12 +3023,12 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
TableModifyerByCell.prototype = {
-
+
addSpannedCellToMap: function(cell, map, r, c, cspan, rspan) {
var spanCollect = [],
rmax = r + ((rspan) ? parseInt(rspan, 10) - 1 : 0),
cmax = c + ((cspan) ? parseInt(cspan, 10) - 1 : 0);
-
+
for (var rr = r; rr <= rmax; rr++) {
if (typeof map[rr] == "undefined") { map[rr] = []; }
for (var cc = c; cc <= cmax; cc++) {
@@ -3012,12 +3041,12 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
map[rr][cc].lastRow = rr == rmax;
map[rr][cc].isReal = cc == c && rr == r;
map[rr][cc].spanCollection = spanCollect;
-
+
spanCollect.push(map[rr][cc]);
}
}
},
-
+
setCellAsModified: function(cell) {
cell.modified = true;
if (cell.spanCollection.length > 0) {
@@ -3028,7 +3057,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
},
setTableMap: function() {
- var map = [];
+ var map = [];
var tableRows = this.getTableRows(),
ridx, row, cells, cidx, cell,
c,
@@ -3043,7 +3072,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
cell = cells[cidx];
// If cell allready set means it is set by col or rowspan,
- // so increase cols index until free col is found
+ // so increase cols index until free col is found
while (typeof map[ridx][c] != "undefined") { c++; }
cspan = api.getAttribute(cell, 'colspan');
@@ -3057,33 +3086,33 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
c++;
}
}
- }
+ }
this.map = map;
return map;
},
-
+
getRowCells: function(row) {
var inlineTables = this.table.querySelectorAll('table'),
inlineCells = (inlineTables) ? queryInList(inlineTables, 'th, td') : [],
allCells = row.querySelectorAll('th, td'),
tableCells = (inlineCells.length > 0) ? wysihtml5.lang.array(allCells).without(inlineCells) : allCells;
-
+
return tableCells;
},
-
+
getTableRows: function() {
var inlineTables = this.table.querySelectorAll('table'),
inlineRows = (inlineTables) ? queryInList(inlineTables, 'tr') : [],
allRows = this.table.querySelectorAll('tr'),
tableRows = (inlineRows.length > 0) ? wysihtml5.lang.array(allRows).without(inlineRows) : allRows;
-
+
return tableRows;
},
-
+
getMapIndex: function(cell) {
var r_length = this.map.length,
c_length = (this.map && this.map[0]) ? this.map[0].length : 0;
-
+
for (var r_idx = 0;r_idx < r_length; r_idx++) {
for (var c_idx = 0;c_idx < c_length; c_idx++) {
if (this.map[r_idx][c_idx].el === cell) {
@@ -3093,7 +3122,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return false;
},
-
+
getElementAtIndex: function(idx) {
this.setTableMap();
if (this.map[idx.row] && this.map[idx.row][idx.col] && this.map[idx.row][idx.col].el) {
@@ -3101,13 +3130,13 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return null;
},
-
+
getMapElsTo: function(to_cell) {
var els = [];
this.setTableMap();
this.idx_start = this.getMapIndex(this.cell);
this.idx_end = this.getMapIndex(to_cell);
-
+
// switch indexes if start is bigger than end
if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
var temp_idx = this.idx_start;
@@ -3119,7 +3148,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
this.idx_start.col = this.idx_end.col;
this.idx_end.col = temp_cidx;
}
-
+
if (this.idx_start != null && this.idx_end != null) {
for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
@@ -3129,12 +3158,12 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return els;
},
-
+
orderSelectionEnds: function(secondcell) {
this.setTableMap();
this.idx_start = this.getMapIndex(this.cell);
this.idx_end = this.getMapIndex(secondcell);
-
+
// switch indexes if start is bigger than end
if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
var temp_idx = this.idx_start;
@@ -3146,20 +3175,20 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
this.idx_start.col = this.idx_end.col;
this.idx_end.col = temp_cidx;
}
-
+
return {
"start": this.map[this.idx_start.row][this.idx_start.col].el,
"end": this.map[this.idx_end.row][this.idx_end.col].el
};
},
-
+
createCells: function(tag, nr, attrs) {
var doc = this.table.ownerDocument,
frag = doc.createDocumentFragment(),
cell;
for (var i = 0; i < nr; i++) {
cell = doc.createElement(tag);
-
+
if (attrs) {
for (var attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
@@ -3167,15 +3196,15 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
}
-
+
// add non breaking space
cell.appendChild(document.createTextNode("\u00a0"));
-
+
frag.appendChild(cell);
}
return frag;
},
-
+
// Returns next real cell (not part of spanned cell unless first) on row if selected index is not real. I no real cells -1 will be returned
correctColIndexForUnreals: function(col, row) {
var r = this.map[row],
@@ -3187,11 +3216,11 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return corrIdx;
},
-
+
getLastNewCellOnRow: function(row, rowLimit) {
var cells = this.getRowCells(row),
cell, idx;
-
+
for (var cidx = 0, cmax = cells.length; cidx < cmax; cidx++) {
cell = cells[cidx];
idx = this.getMapIndex(cell);
@@ -3201,7 +3230,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return null;
},
-
+
removeEmptyTable: function() {
var cells = this.table.querySelectorAll('td, th');
if (!cells || cells.length == 0) {
@@ -3211,7 +3240,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
return false;
}
},
-
+
// Splits merged cell on row to unique cells
splitRowToCells: function(cell) {
if (cell.isColspan) {
@@ -3224,13 +3253,13 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
cell.el.removeAttribute('colspan');
}
},
-
+
getRealRowEl: function(force, idx) {
var r = null,
c = null;
-
+
idx = idx || this.idx;
-
+
for (var cidx = 0, cmax = this.map[idx.row].length; cidx < cmax; cidx++) {
c = this.map[idx.row][cidx];
if (c.isReal) {
@@ -3240,33 +3269,50 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
}
-
+
if (r === null && force) {
r = api.getParentElement(this.map[idx.row][idx.col].el, { nodeName: ["TR"] }) || null;
}
-
+
return r;
},
-
+
injectRowAt: function(row, col, colspan, cType, c) {
var r = this.getRealRowEl(false, {'row': row, 'col': col}),
new_cells = this.createCells(cType, colspan);
-
+
if (r) {
var n_cidx = this.correctColIndexForUnreals(col, row);
if (n_cidx >= 0) {
insertAfter(this.getRowCells(r)[n_cidx], new_cells);
} else {
r.insertBefore(new_cells, r.firstChild);
- }
+ }
} else {
var rr = this.table.ownerDocument.createElement('tr');
rr.appendChild(new_cells);
insertAfter(api.getParentElement(c.el, { nodeName: ["TR"] }), rr);
}
},
-
- canMerge: function() {
+
+ canMerge: function(to) {
+ this.to = to;
+ this.setTableMap();
+ this.idx_start = this.getMapIndex(this.cell);
+ this.idx_end = this.getMapIndex(this.to);
+
+ // switch indexes if start is bigger than end
+ if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
+ var temp_idx = this.idx_start;
+ this.idx_start = this.idx_end;
+ this.idx_end = temp_idx;
+ }
+ if (this.idx_start.col > this.idx_end.col) {
+ var temp_cidx = this.idx_start.col;
+ this.idx_start.col = this.idx_end.col;
+ this.idx_end.col = temp_cidx;
+ }
+
for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
if (this.map[row][col].isColspan || this.map[row][col].isRowspan) {
@@ -3276,10 +3322,10 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
return true;
},
-
+
decreaseCellSpan: function(cell, span) {
var nr = parseInt(api.getAttribute(cell.el, span), 10) - 1;
- if (nr >= 1) {
+ if (nr >= 1) {
cell.el.setAttribute(span, nr);
} else {
cell.el.removeAttribute(span);
@@ -3288,18 +3334,18 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
if (span == 'rowspan') {
cell.isRowspan = false;
- }
+ }
cell.firstCol = true;
cell.lastCol = true;
cell.firstRow = true;
cell.lastRow = true;
- cell.isReal = true;
+ cell.isReal = true;
}
},
-
+
removeSurplusLines: function() {
var row, cell, ridx, rmax, cidx, cmax, allRowspan;
-
+
this.setTableMap();
if (this.map) {
ridx = 0;
@@ -3323,7 +3369,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
}
-
+
// remove rows without cells
var tableRows = this.getTableRows();
ridx = 0;
@@ -3336,21 +3382,21 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
fillMissingCells: function() {
var r_max = 0,
c_max = 0,
prevcell = null;
-
+
this.setTableMap();
if (this.map) {
-
+
// find maximal dimensions of broken table
r_max = this.map.length;
for (var ridx = 0; ridx < r_max; ridx++) {
if (this.map[ridx].length > c_max) { c_max = this.map[ridx].length; }
}
-
+
for (var row = 0; row < r_max; row++) {
for (var col = 0; col < c_max; col++) {
if (this.map[row] && !this.map[row][col]) {
@@ -3363,10 +3409,10 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
}
- }
+ }
}
},
-
+
rectify: function() {
if (!this.removeEmptyTable()) {
this.removeSurplusLines();
@@ -3376,17 +3422,17 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
return false;
}
},
-
+
unmerge: function() {
if (this.rectify()) {
this.setTableMap();
this.idx = this.getMapIndex(this.cell);
-
+
if (this.idx) {
var thisCell = this.map[this.idx.row][this.idx.col],
colspan = (api.getAttribute(thisCell.el, "colspan")) ? parseInt(api.getAttribute(thisCell.el, "colspan"), 10) : 1,
cType = thisCell.el.tagName.toLowerCase();
-
+
if (thisCell.isRowspan) {
var rowspan = parseInt(api.getAttribute(thisCell.el, "rowspan"), 10);
if (rowspan > 1) {
@@ -3400,38 +3446,22 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
// merges cells from start cell (defined in creating obj) to "to" cell
merge: function(to) {
if (this.rectify()) {
- this.to = to;
- this.setTableMap();
- this.idx_start = this.getMapIndex(this.cell);
- this.idx_end = this.getMapIndex(this.to);
-
- // switch indexes if start is bigger than end
- if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
- var temp_idx = this.idx_start;
- this.idx_start = this.idx_end;
- this.idx_end = temp_idx;
- }
- if (this.idx_start.col > this.idx_end.col) {
- var temp_cidx = this.idx_start.col;
- this.idx_start.col = this.idx_end.col;
- this.idx_end.col = temp_cidx;
- }
- if (this.canMerge()) {
+ if (this.canMerge(to)) {
var rowspan = this.idx_end.row - this.idx_start.row + 1,
colspan = this.idx_end.col - this.idx_start.col + 1;
-
+
for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
-
+
if (row == this.idx_start.row && col == this.idx_start.col) {
- if (rowspan > 1) {
+ if (rowspan > 1) {
this.map[row][col].el.setAttribute('rowspan', rowspan);
}
- if (colspan > 1) {
+ if (colspan > 1) {
this.map[row][col].el.setAttribute('colspan', colspan);
}
} else {
@@ -3451,16 +3481,16 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
// Decreases rowspan of a cell if it is done on first cell of rowspan row (real cell)
// Cell is moved to next row (if it is real)
collapseCellToNextRow: function(cell) {
var cellIdx = this.getMapIndex(cell.el),
newRowIdx = cellIdx.row + 1,
newIdx = {'row': newRowIdx, 'col': cellIdx.col};
-
+
if (newRowIdx < this.map.length) {
-
+
var row = this.getRealRowEl(false, newIdx);
if (row !== null) {
var n_cidx = this.correctColIndexForUnreals(newIdx.col, newIdx.row);
@@ -3482,7 +3512,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
// Removes a cell when removing a row
// If is rowspan cell then decreases the rowspan
// and moves cell to next row if needed (is first cell of rowspan)
@@ -3501,7 +3531,36 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
+ getRowElementsByCell: function() {
+ var cells = [];
+ this.setTableMap();
+ this.idx = this.getMapIndex(this.cell);
+ if (this.idx !== false) {
+ var modRow = this.map[this.idx.row];
+ for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
+ if (modRow[cidx].isReal) {
+ cells.push(modRow[cidx].el);
+ }
+ }
+ }
+ return cells;
+ },
+
+ getColumnElementsByCell: function() {
+ var cells = [];
+ this.setTableMap();
+ this.idx = this.getMapIndex(this.cell);
+ if (this.idx !== false) {
+ for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
+ if (this.map[ridx][this.idx.col] && this.map[ridx][this.idx.col].isReal) {
+ cells.push(this.map[ridx][this.idx.col].el);
+ }
+ }
+ }
+ return cells;
+ },
+
// Removes the row of selected cell
removeRow: function() {
var oldRow = api.getParentElement(this.cell, { nodeName: ["TR"] });
@@ -3520,7 +3579,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
removeElement(oldRow);
}
},
-
+
removeColCell: function(cell) {
if (cell.isColspan) {
if (parseInt(api.getAttribute(cell.el, 'colspan'), 10) > 2) {
@@ -3532,7 +3591,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
removeElement(cell.el);
}
},
-
+
removeColumn: function() {
this.setTableMap();
this.idx = this.getMapIndex(this.cell);
@@ -3545,7 +3604,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
// removes row or column by selected cell element
remove: function(what) {
if (this.rectify()) {
@@ -3560,32 +3619,32 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
this.rectify();
}
},
-
+
addRow: function(where) {
var doc = this.table.ownerDocument;
-
+
this.setTableMap();
this.idx = this.getMapIndex(this.cell);
if (where == "below" && api.getAttribute(this.cell, 'rowspan')) {
this.idx.row = this.idx.row + parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1;
}
-
+
if (this.idx !== false) {
var modRow = this.map[this.idx.row],
- newRow = doc.createElement('tr');
-
+ newRow = doc.createElement('tr');
+
for (var ridx = 0, rmax = modRow.length; ridx < rmax; ridx++) {
if (!modRow[ridx].modified) {
this.setCellAsModified(modRow[ridx]);
this.addRowCell(modRow[ridx], newRow, where);
}
}
-
+
switch (where) {
- case 'below':
+ case 'below':
insertAfter(this.getRealRowEl(true), newRow);
break;
- case 'above':
+ case 'above':
var cr = api.getParentElement(this.map[this.idx.row][this.idx.col].el, { nodeName: ["TR"] });
if (cr) {
cr.parentNode.insertBefore(newRow, cr);
@@ -3594,7 +3653,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
addRowCell: function(cell, row, where) {
var colSpanAttr = (cell.isColspan) ? {"colspan" : api.getAttribute(cell.el, 'colspan')} : null;
if (cell.isReal) {
@@ -3608,10 +3667,10 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
row.appendChild(this.createCells('td', 1, colSpanAttr));
} else if (c.isRowspan) {
cell.el.attr('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) + 1);
- }
+ }
}
},
-
+
add: function(where) {
if (this.rectify()) {
if (where == 'below' || where == 'above') {
@@ -3622,24 +3681,24 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
addColCell: function (cell, ridx, where) {
var doAdd,
cType = cell.el.tagName.toLowerCase();
-
+
// defines add cell vs expand cell conditions
// true means add
switch (where) {
- case "before":
+ case "before":
doAdd = (!cell.isColspan || cell.firstCol);
break;
case "after":
doAdd = (!cell.isColspan || cell.lastCol || (cell.isColspan && c.el == this.cell));
break;
}
-
+
if (doAdd){
- // adds a cell before or after current cell element
+ // adds a cell before or after current cell element
switch (where) {
case "before":
cell.el.parentNode.insertBefore(this.createCells(cType, 1), cell.el);
@@ -3648,27 +3707,27 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
insertAfter(cell.el, this.createCells(cType, 1));
break;
}
-
- // handles if cell has rowspan
+
+ // handles if cell has rowspan
if (cell.isRowspan) {
this.handleCellAddWithRowspan(cell, ridx+1, where);
}
-
+
} else {
// expands cell
cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) + 1);
}
},
-
+
addColumn: function(where) {
var row, modCell;
-
+
this.setTableMap();
this.idx = this.getMapIndex(this.cell);
if (where == "after" && api.getAttribute(this.cell, 'colspan')) {
this.idx.col = this.idx.col + parseInt(api.getAttribute(this.cell, 'colspan'), 10) - 1;
}
-
+
if (this.idx !== false) {
for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++ ) {
row = this.map[ridx];
@@ -3682,7 +3741,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
},
-
+
handleCellAddWithRowspan: function (cell, ridx, where) {
var addRowsNr = parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1,
crow = api.getParentElement(cell.el, { nodeName: ["TR"] }),
@@ -3690,21 +3749,21 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
cidx, temp_r_cells,
doc = this.table.ownerDocument,
nrow;
-
+
for (var i = 0; i < addRowsNr; i++) {
cidx = this.correctColIndexForUnreals(this.idx.col, (ridx + i));
crow = nextNode(crow, 'tr');
if (crow) {
if (cidx > 0) {
switch (where) {
- case "before":
+ case "before":
temp_r_cells = this.getRowCells(crow);
if (cidx > 0 && this.map[ridx + i][this.idx.col].el != temp_r_cells[cidx] && cidx == temp_r_cells.length - 1) {
insertAfter(temp_r_cells[cidx], this.createCells(cType, 1));
} else {
temp_r_cells[cidx].parentNode.insertBefore(this.createCells(cType, 1), temp_r_cells[cidx]);
}
-
+
break;
case "after":
insertAfter(this.getRowCells(crow)[cidx], this.createCells(cType, 1));
@@ -3721,71 +3780,87 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
}
}
};
-
+
api.table = {
getCellsBetween: function(cell1, cell2) {
var c1 = new TableModifyerByCell(cell1);
return c1.getMapElsTo(cell2);
},
-
+
addCells: function(cell, where) {
var c = new TableModifyerByCell(cell);
c.add(where);
},
-
+
removeCells: function(cell, what) {
var c = new TableModifyerByCell(cell);
c.remove(what);
},
-
+
mergeCellsBetween: function(cell1, cell2) {
var c1 = new TableModifyerByCell(cell1);
c1.merge(cell2);
},
-
+
unmergeCell: function(cell) {
var c = new TableModifyerByCell(cell);
c.unmerge();
},
-
+
orderSelectionEnds: function(cell, cell2) {
var c = new TableModifyerByCell(cell);
return c.orderSelectionEnds(cell2);
},
-
+
indexOf: function(cell) {
var c = new TableModifyerByCell(cell);
c.setTableMap();
return c.getMapIndex(cell);
},
-
+
findCell: function(table, idx) {
var c = new TableModifyerByCell(null, table);
return c.getElementAtIndex(idx);
+ },
+
+ findRowByCell: function(cell) {
+ var c = new TableModifyerByCell(cell);
+ return c.getRowElementsByCell();
+ },
+
+ findColumnByCell: function(cell) {
+ var c = new TableModifyerByCell(cell);
+ return c.getColumnElementsByCell();
+ },
+
+ canMerge: function(cell1, cell2) {
+ var c = new TableModifyerByCell(cell1);
+ return c.canMerge(cell2);
}
};
-
-
-
+
+
+
})(wysihtml5);
// does a selector query on element or array of elements
wysihtml5.dom.query = function(elements, query) {
var ret = [],
q;
-
+
if (elements.nodeType) {
elements = [elements];
}
-
+
for (var e = 0, len = elements.length; e < len; e++) {
q = elements[e].querySelectorAll(query);
if (q) {
for(var i = q.length; i--; ret.unshift(q[i]));
}
}
- return ret;
-};/**
+ return ret;
+};
+/**
* Fix most common html formatting misbehaviors of browsers implementation when inserting
* content via copy & paste contentEditable
*
@@ -3797,11 +3872,11 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
// When pasting underlined links into a contentEditable, IE thinks, it has to insert to keep the styling
"a u": wysihtml5.dom.replaceWithChildNodes
};
-
+
function cleanPastedHTML(elementOrHtml, rules, context) {
rules = rules || defaultRules;
context = context || elementOrHtml.ownerDocument || document;
-
+
var element,
isString = typeof(elementOrHtml) === "string",
method,
@@ -3814,7 +3889,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
} else {
element = elementOrHtml;
}
-
+
for (i in rules) {
matches = element.querySelectorAll(i);
method = rules[i];
@@ -3823,14 +3898,15 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
method(matches[j]);
}
}
-
+
matches = elementOrHtml = rules = null;
-
+
return isString ? element.innerHTML : element;
}
-
+
return cleanPastedHTML;
-})();/**
+})();
+/**
* IE and Opera leave an empty paragraph in the contentEditable element after clearing it
*
* @param {Object} contentEditableElement The contentEditable element to observe for clearing events
@@ -3869,7 +3945,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
if (innerHTML.indexOf(TILDE_ESCAPED) === -1) {
return innerHTML;
}
-
+
var elementsWithTilde = element.querySelectorAll("[href*='~'], [src*='~']"),
url,
urlToSearch,
@@ -3882,7 +3958,8 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return innerHTML;
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* Force rerendering of a given element
* Needed to fix display misbehaviors of IE
*
@@ -3892,11 +3969,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
*/
(function(wysihtml5) {
var CLASS_NAME = "wysihtml5-quirks-redraw";
-
+
wysihtml5.quirks.redraw = function(element) {
wysihtml5.dom.addClass(element, CLASS_NAME);
wysihtml5.dom.removeClass(element, CLASS_NAME);
-
+
// Following hack is needed for firefox to make sure that image resize handles are properly removed
try {
var doc = element.ownerDocument;
@@ -3904,144 +3981,142 @@ wysihtml5.quirks.ensureProperClearing = (function() {
doc.execCommand("italic", false, null);
} catch(e) {}
};
-})(wysihtml5);wysihtml5.quirks.tableCellsSelection = (function() {
-
- var dom = wysihtml5.dom,
- select = {
- table: null,
- start: null,
- end: null,
- select: selectCells
- },
- editable = null,
- selection_class = "wysiwyg-tmp-selected-cell",
- moveHandler = null,
- upHandler = null,
- editor = null;
-
- function init (element, edit) {
- editable = element;
- editor = edit;
-
- dom.observe(editable, "mousedown", function(event) {
- var target = event.target,
- nodeName = target.nodeName;
- if (nodeName == "TD" || nodeName == "TH") {
- handleSelectionMousedown(target);
- }
-
- });
-
- return select;
- }
-
- function handleSelectionMousedown (target) {
- select.start = target;
- select.end = target;
- select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
-
- if (select.table) {
- removeCellSelections();
- dom.addClass(target, selection_class);
- moveHandler = dom.observe(editable, "mousemove", handleMouseMove);
- upHandler = dom.observe(editable, "mouseup", handleMouseUp);
- }
- }
-
- // remove all selection classes
- function removeCellSelections () {
- if (editable) {
- var selectedCells = editable.querySelectorAll('.' + selection_class);
- if (selectedCells.length > 0) {
- for (var i = 0; i < selectedCells.length; i++) {
- dom.removeClass(selectedCells[i], selection_class);
- }
+})(wysihtml5);
+wysihtml5.quirks.tableCellsSelection = function(editable, editor) {
+
+ var dom = wysihtml5.dom,
+ select = {
+ table: null,
+ start: null,
+ end: null,
+ cells: null,
+ select: selectCells
+ },
+ selection_class = "wysiwyg-tmp-selected-cell",
+ moveHandler = null,
+ upHandler = null;
+
+ function init () {
+
+ dom.observe(editable, "mousedown", function(event) {
+ var target = wysihtml5.dom.getParentElement(event.target, { nodeName: ["TD", "TH"] });
+ if (target) {
+ handleSelectionMousedown(target);
}
- }
- }
-
- function addSelections (cells) {
- for (var i = 0; i < cells.length; i++) {
- dom.addClass(cells[i], selection_class);
+ });
+
+ return select;
}
- }
-
- function handleMouseMove (event) {
- var curTable = null,
- cell = dom.getParentElement(event.target, { nodeName: ["TD","TH"] }),
- selectedCells;
-
- if (cell && select.table && select.start) {
- curTable = dom.getParentElement(cell, { nodeName: ["TABLE"] });
- if (curTable && curTable === select.table) {
+
+ function handleSelectionMousedown (target) {
+ select.start = target;
+ select.end = target;
+ select.cells = [target];
+ select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
+
+ if (select.table) {
removeCellSelections();
- select.end = cell;
- selectedCells = dom.table.getCellsBetween(select.start, cell);
- addSelections(selectedCells);
+ dom.addClass(target, selection_class);
+ moveHandler = dom.observe(editable, "mousemove", handleMouseMove);
+ upHandler = dom.observe(editable, "mouseup", handleMouseUp);
}
}
- }
-
- function handleMouseUp (event) {
- moveHandler.stop();
- upHandler.stop();
- editor.fire("tableselect").fire("tableselect:composer");
- setTimeout(function() {
- bindSideclick();
- },0);
- }
-
- function bindSideclick () {
- var sideClickHandler = dom.observe(editable.ownerDocument, "click", function(event) {
- sideClickHandler.stop();
- if (dom.getParentElement(event.target, { nodeName: ["TABLE"] }) != select.table) {
- removeCellSelections();
- select.table = null;
- select.start = null;
- select.end = null;
- editor.fire("tableunselect").fire("tableunselect:composer");
+
+ // remove all selection classes
+ function removeCellSelections () {
+ if (editable) {
+ var selectedCells = editable.querySelectorAll('.' + selection_class);
+ if (selectedCells.length > 0) {
+ for (var i = 0; i < selectedCells.length; i++) {
+ dom.removeClass(selectedCells[i], selection_class);
+ }
+ }
}
- });
- }
-
- function selectCells (start, end) {
- select.start = start;
- select.end = end;
- select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
- selectedCells = dom.table.getCellsBetween(select.start, select.end);
- addSelections(selectedCells);
- bindSideclick();
+ }
+
+ function addSelections (cells) {
+ for (var i = 0; i < cells.length; i++) {
+ dom.addClass(cells[i], selection_class);
+ }
+ }
+
+ function handleMouseMove (event) {
+ var curTable = null,
+ cell = dom.getParentElement(event.target, { nodeName: ["TD","TH"] });
+
+ if (cell && select.table && select.start) {
+ curTable = dom.getParentElement(cell, { nodeName: ["TABLE"] });
+ if (curTable && curTable === select.table) {
+ removeCellSelections();
+ select.end = cell;
+ select.cells = dom.table.getCellsBetween(select.start, cell);
+ if (select.cells.length > 1) {
+ editor.composer.selection.deselect();
+ }
+ addSelections(select.cells);
+ }
+ }
+ }
+
+ function handleMouseUp (event) {
+ moveHandler.stop();
+ upHandler.stop();
editor.fire("tableselect").fire("tableselect:composer");
- }
-
- return init;
+ setTimeout(function() {
+ bindSideclick();
+ },0);
+ }
+
+ function bindSideclick () {
+ var sideClickHandler = dom.observe(editable.ownerDocument, "click", function(event) {
+ sideClickHandler.stop();
+ if (dom.getParentElement(event.target, { nodeName: ["TABLE"] }) != select.table) {
+ removeCellSelections();
+ select.table = null;
+ select.start = null;
+ select.end = null;
+ editor.fire("tableunselect").fire("tableunselect:composer");
+ }
+ });
+ }
-})();
+ function selectCells (start, end) {
+ select.start = start;
+ select.end = end;
+ select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
+ selectedCells = dom.table.getCellsBetween(select.start, select.end);
+ addSelections(selectedCells);
+ bindSideclick();
+ editor.fire("tableselect").fire("tableselect:composer");
+ }
+
+ return init();
+};
(function(wysihtml5) {
var RGBA_REGEX = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d\.]+)\s*\)/i,
RGB_REGEX = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/i,
HEX6_REGEX = /^#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])/i,
HEX3_REGEX = /^#([0-9a-f])([0-9a-f])([0-9a-f])/i;
-
+
var param_REGX = function (p) {
return new RegExp("(^|\\s|;)" + p + "\\s*:\\s*[^;$]+" , "gi");
};
-
+
wysihtml5.quirks.styleParser = {
-
+
parseColor: function(stylesStr, paramName) {
var paramRegex = param_REGX(paramName),
params = stylesStr.match(paramRegex),
radix = 10,
str, colorMatch;
-
- if (params) {
+
+ if (params) {
for (var i = params.length; i--;) {
params[i] = wysihtml5.lang.string(params[i].split(':')[1]).trim();
- }
+ }
str = params[params.length-1];
-
+
if (RGBA_REGEX.test(str)) {
colorMatch = str.match(RGBA_REGEX);
} else if (RGB_REGEX.test(str)) {
@@ -4057,7 +4132,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return (idx < 3) ? (parseInt(d, 16) * 16) + parseInt(d, 16): parseFloat(d);
});
}
-
+
if (colorMatch) {
colorMatch.shift();
if (!colorMatch[3]) {
@@ -4070,7 +4145,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return false;
},
-
+
unparseColor: function(val, props) {
if (props) {
if (props == "hex") {
@@ -4085,24 +4160,25 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return val[0] + "," + val[1] + "," + val[2] + "," + val[3];
}
}
-
+
if (val[3] && val[3] !== 1) {
return "rgba(" + val[0] + "," + val[1] + "," + val[2] + "," + val[3] + ")";
} else {
return "rgb(" + val[0] + "," + val[1] + "," + val[2] + ")";
}
},
-
+
parseFontSize: function(stylesStr) {
var params = stylesStr.match(param_REGX('font-size'));
- if (params) {
+ if (params) {
return wysihtml5.lang.string(params[params.length - 1].split(':')[1]).trim();
}
return false;
}
};
-
-})(wysihtml5);/**
+
+})(wysihtml5);
+/**
* Selection API
*
* @example
@@ -4110,7 +4186,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
*/
(function(wysihtml5) {
var dom = wysihtml5.dom;
-
+
function _getCumulativeOffsetTop(element) {
var top = 0;
if (element.parentNode) {
@@ -4121,20 +4197,20 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return top;
}
-
+
wysihtml5.Selection = Base.extend(
/** @scope wysihtml5.Selection.prototype */ {
constructor: function(editor, contain, unselectableClass) {
// Make sure that our external range library is initialized
window.rangy.init();
-
+
this.editor = editor;
this.composer = editor.composer;
this.doc = this.composer.doc;
this.contain = contain;
this.unselectableClass = unselectableClass || false;
},
-
+
/**
* Get the current selection as a bookmark to be able to later restore it
*
@@ -4181,7 +4257,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
*/
setAfter: function(node) {
var range = rangy.createRange(this.doc);
-
+
range.setStartAfter(node);
range.setEndAfter(node);
return this.setSelection(range);
@@ -4251,18 +4327,18 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return range ? range.commonAncestorContainer : this.doc.body;
}
},
-
+
getSelectedOwnNodes: function(controlRange) {
var selection,
ranges = this.getOwnRanges(),
ownNodes = [];
-
+
for (var i = 0, maxi = ranges.length; i < maxi; i++) {
ownNodes.push(ranges[i].commonAncestorContainer || this.doc.body);
}
return ownNodes;
},
-
+
findNodesInSelection: function(nodeTypes) {
var ranges = this.getOwnRanges(),
nodes = [], curNodes;
@@ -4274,20 +4350,20 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return nodes;
},
-
+
containsUneditable: function() {
var uneditables = this.getOwnUneditables(),
selection = this.getSelection();
-
+
for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
if (selection.containsNode(uneditables[i])) {
return true;
}
}
-
+
return false;
},
-
+
deleteContents: function() {
var ranges = this.getOwnRanges();
for (var i = ranges.length; i--;) {
@@ -4295,24 +4371,24 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
this.setSelection(ranges[0]);
},
-
+
getPreviousNode: function(node, ignoreEmpty) {
if (!node) {
var selection = this.getSelection();
node = selection.anchorNode;
}
-
+
if (node === this.contain) {
return false;
}
-
+
var ret = node.previousSibling,
parent;
-
+
if (ret === this.contain) {
return false;
}
-
+
if (ret && ret.nodeType !== 3 && ret.nodeType !== 1) {
// do not count comments and other node types
ret = this.getPreviousNode(ret, ignoreEmpty);
@@ -4329,25 +4405,25 @@ wysihtml5.quirks.ensureProperClearing = (function() {
ret = this.getPreviousNode(parent, ignoreEmpty);
}
}
-
+
return (ret !== this.contain) ? ret : false;
},
-
-
-
+
+
+
caretIsInTheBeginnig: function() {
var selection = this.getSelection(),
node = selection.anchorNode,
offset = selection.anchorOffset;
-
+
return (offset === 0 && !this.getPreviousNode(node, true));
},
-
+
caretIsBeforeUneditable: function() {
var selection = this.getSelection(),
node = selection.anchorNode,
offset = selection.anchorOffset;
-
+
if (offset === 0) {
var prevNode = this.getPreviousNode(node, true);
if (prevNode) {
@@ -4358,7 +4434,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
}
}
- }
+ }
return false;
},
@@ -4369,19 +4445,19 @@ wysihtml5.quirks.ensureProperClearing = (function() {
oldScrollLeft = restoreScrollPosition && body.scrollLeft,
className = "_wysihtml5-temp-placeholder",
placeholderHtml = '' + wysihtml5.INVISIBLE_SPACE + '',
- range = this.getRange(this.doc),
+ range = this.getRange(true),
caretPlaceholder,
newCaretPlaceholder,
nextSibling, prevSibling,
node, node2, range2,
newRange;
-
+
// Nothing selected, execute and say goodbye
if (!range) {
method(body, body);
return;
}
-
+
if (!range.collapsed) {
range2 = range.cloneRange();
node2 = range2.createContextualFragment(placeholderHtml);
@@ -4389,16 +4465,17 @@ wysihtml5.quirks.ensureProperClearing = (function() {
range2.insertNode(node2);
range2.detach();
}
+
node = range.createContextualFragment(placeholderHtml);
range.insertNode(node);
-
+
if (node2) {
caretPlaceholder = this.contain.querySelectorAll("." + className);
range.setStartBefore(caretPlaceholder[0]);
range.setEndAfter(caretPlaceholder[caretPlaceholder.length -1]);
}
this.setSelection(range);
-
+
// Make sure that a potential error doesn't cause our placeholder element to be left as a placeholder
try {
method(range.startContainer, range.endContainer);
@@ -4407,7 +4484,6 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
caretPlaceholder = this.contain.querySelectorAll("." + className);
if (caretPlaceholder && caretPlaceholder.length) {
-
newRange = rangy.createRange(this.doc);
nextSibling = caretPlaceholder[0].nextSibling;
if (caretPlaceholder.length > 1) {
@@ -4416,7 +4492,6 @@ wysihtml5.quirks.ensureProperClearing = (function() {
if (prevSibling && nextSibling) {
newRange.setStartBefore(nextSibling);
newRange.setEndAfter(prevSibling);
-
} else {
newCaretPlaceholder = this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
dom.insert(newCaretPlaceholder).before(caretPlaceholder[0]);
@@ -4427,10 +4502,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
for (var i = caretPlaceholder.length; i--;) {
caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
}
-
-
-
-
+
} else {
// fallback for when all hell breaks loose
this.contain.focus();
@@ -4489,13 +4561,13 @@ wysihtml5.quirks.ensureProperClearing = (function() {
try { newRange.setEnd(rangeBackup.endContainer, rangeBackup.endOffset); } catch(e2) {}
try { this.setSelection(newRange); } catch(e3) {}
},
-
+
set: function(node, offset) {
var newRange = rangy.createRange(this.doc);
newRange.setStart(node, offset || 0);
this.setSelection(newRange);
},
-
+
/**
* Insert html at the caret position and move the cursor after the inserted html
*
@@ -4507,7 +4579,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
var range = rangy.createRange(this.doc),
node = range.createContextualFragment(html),
lastChild = node.lastChild;
-
+
this.insertNode(node);
if (lastChild) {
this.setAfter(lastChild);
@@ -4539,7 +4611,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
if (ranges.length == 0) {
return nodes;
}
-
+
for (var i = ranges.length; i--;) {
node = this.doc.createElement(nodeOptions.nodeName);
nodes.push(node);
@@ -4558,26 +4630,26 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return nodes;
},
-
+
deblockAndSurround: function(nodeOptions) {
var tempElement = this.doc.createElement('div'),
range = rangy.createRange(this.doc),
tempDivElements,
tempElements,
firstChild;
-
+
tempElement.className = nodeOptions.className;
-
+
this.composer.commands.exec("formatBlock", nodeOptions.nodeName, nodeOptions.className);
tempDivElements = this.contain.querySelectorAll("." + nodeOptions.className);
if (tempDivElements[0]) {
tempDivElements[0].parentNode.insertBefore(tempElement, tempDivElements[0]);
-
+
range.setStartBefore(tempDivElements[0]);
range.setEndAfter(tempDivElements[tempDivElements.length - 1]);
tempElements = range.extractContents();
-
- while (tempElements.firstChild) {
+
+ while (tempElements.firstChild) {
firstChild = tempElements.firstChild;
if (firstChild.nodeType == 1 && wysihtml5.dom.hasClass(firstChild, nodeOptions.className)) {
while (firstChild.firstChild) {
@@ -4592,7 +4664,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
} else {
tempElement = null;
}
-
+
return tempElement;
},
@@ -4706,44 +4778,62 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return [];
}
},
-
+
fixRangeOverflow: function(range) {
-
- if (this.contain && range) {
- var containment = range.compareNode(this.contain);
-
- if (containment !== 2) {
- if (containment === 1) {
- range.setStartBefore(this.contain.firstChild);
- }
- if (containment === 0) {
- range.setEndAfter(this.contain.lastChild);
- }
- if (containment === 3) {
- range.setStartBefore(this.contain.firstChild);
- range.setEndAfter(this.contain.lastChild);
- }
-
- }
+ if (this.contain && this.contain.firstChild && range) {
+ var containment = range.compareNode(this.contain);
+ if (containment !== 2) {
+ if (containment === 1) {
+ range.setStartBefore(this.contain.firstChild);
+ }
+ if (containment === 0) {
+ range.setEndAfter(this.contain.lastChild);
+ }
+ if (containment === 3) {
+ range.setStartBefore(this.contain.firstChild);
+ range.setEndAfter(this.contain.lastChild);
+ }
+ } else if (this._detectInlineRangeProblems(range)) {
+ var previousElementSibling = range.endContainer.previousElementSibling;
+ if (previousElementSibling) {
+ range.setEnd(previousElementSibling, this._endOffsetForNode(previousElementSibling));
+ }
}
-
+ }
},
-
- getRange: function() {
+
+ _endOffsetForNode: function(node) {
+ var range = document.createRange()
+ range.selectNodeContents(node)
+ return range.endOffset;
+ },
+
+ _detectInlineRangeProblems: function(range) {
+ position = range.startContainer.compareDocumentPosition(range.endContainer);
+ return (
+ range.endOffset == 0 &&
+ position & Node.DOCUMENT_POSITION_FOLLOWING
+ );
+ },
+
+ getRange: function(dontFix) {
var selection = this.getSelection(),
range = selection && selection.rangeCount && selection.getRangeAt(0);
- this.fixRangeOverflow(range);
-
+
+ if (dontFix !== true) {
+ this.fixRangeOverflow(range);
+ }
+
return range;
},
-
+
getOwnUneditables: function() {
var allUneditables = dom.query(this.contain, '.' + this.unselectableClass),
deepUneditables = dom.query(allUneditables, '.' + this.unselectableClass);
-
+
return wysihtml5.lang.array(allUneditables).without(deepUneditables);
},
-
+
// Returns an array of ranges that belong only to this editable
// Needed as uneditable block in contenteditabel can split range into pieces
// If manipulating content reverse loop is usually needed as manipulation can shift subsequent ranges
@@ -4751,9 +4841,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
var ranges = [],
r = this.getRange(),
tmpRanges;
-
- if (r) { ranges.push(r); }
-
+
+ if (r) { ranges.push(r); }
+
if (this.unselectableClass && this.contain && r) {
var uneditables = this.getOwnUneditables(),
tmpRange;
@@ -4763,7 +4853,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
for (var j = 0, jmax = ranges.length; j < jmax; j++) {
if (ranges[j]) {
switch (ranges[j].compareNode(uneditables[i])) {
- case 2:
+ case 2:
// all selection inside uneditable. remove
break;
case 3:
@@ -4771,7 +4861,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
tmpRange = ranges[j].cloneRange();
tmpRange.setEndBefore(uneditables[i]);
tmpRanges.push(tmpRange);
-
+
tmpRange = ranges[j].cloneRange();
tmpRange.setStartAfter(uneditables[i]);
tmpRanges.push(tmpRange);
@@ -4798,17 +4888,21 @@ wysihtml5.quirks.ensureProperClearing = (function() {
selection = rangy.getSelection(win);
return selection.setSingleRange(range);
},
-
+
createRange: function() {
return rangy.createRange(this.doc);
},
-
+
isCollapsed: function() {
return this.getSelection().isCollapsed;
+ },
+
+ deselect: function() {
+ var sel = this.getSelection();
+ sel && sel.removeAllRanges();
}
-
});
-
+
})(wysihtml5);
/**
* Inspired by the rangy CSS Applier module written by Tim Down and licensed under the MIT license.
@@ -4820,18 +4914,18 @@ wysihtml5.quirks.ensureProperClearing = (function() {
*/
(function(wysihtml5, rangy) {
var defaultTagName = "span";
-
+
var REG_EXP_WHITE_SPACE = /\s+/g;
-
+
function hasClass(el, cssClass, regExp) {
if (!el.className) {
return false;
}
-
+
var matchingClassNames = el.className.match(regExp) || [];
return matchingClassNames[matchingClassNames.length - 1] === cssClass;
}
-
+
function hasStyleAttr(el, regExp) {
if (!el.getAttribute || !el.getAttribute('style')) {
return false;
@@ -4839,20 +4933,20 @@ wysihtml5.quirks.ensureProperClearing = (function() {
var matchingStyles = el.getAttribute('style').match(regExp);
return (el.getAttribute('style').match(regExp)) ? true : false;
}
-
+
function addStyle(el, cssStyle, regExp) {
if (el.getAttribute('style')) {
removeStyle(el, regExp);
if (el.getAttribute('style') && !(/\s+/).test(el.getAttribute('style'))) {
el.setAttribute('style', cssStyle + ";" + el.getAttribute('style'));
} else {
-
+
el.setAttribute('style', cssStyle);
}
} else {
el.setAttribute('style', cssStyle);
}
- }
+ }
function addClass(el, cssClass, regExp) {
if (el.className) {
@@ -4868,7 +4962,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
el.className = el.className.replace(regExp, "");
}
}
-
+
function removeStyle(el, regExp) {
var s,
s2 = [];
@@ -4886,16 +4980,16 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
}
}
-
+
function getMatchingStyleRegexp(el, style) {
var regexes = [],
sSplit = style.split(';'),
elStyle = el.getAttribute('style');
-
+
if (elStyle) {
- elStyle = elStyle.replace(/\s/gi, '').toLowerCase();
+ elStyle = elStyle.replace(/\s/gi, '').toLowerCase();
regexes.push(new RegExp("(^|\\s|;)" + style.replace(/\s/gi, '').replace(/([\(\)])/gi, "\\$1").toLowerCase().replace(";", ";?"), "gi"));
-
+
for (var i = sSplit.length; i-- > 0;) {
if (!(/^\s*$/).test(sSplit[i])) {
regexes.push(new RegExp("(^|\\s|;)" + sSplit[i].replace(/\s/gi, '').replace(/([\(\)])/gi, "\\$1").toLowerCase().replace(";", ";?"), "gi"));
@@ -4907,17 +5001,17 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
}
}
-
+
return false;
};
-
+
function removeOrChangeStyle(el, style, regExp) {
-
+
var exactRegex = getMatchingStyleRegexp(el, style);
-
+
/*new RegExp("(^|\\s|;)" + style.replace(/\s/gi, '').replace(/([\(\)])/gi, "\\$1").toLowerCase().replace(";", ";?"), "gi"),
elStyle = el.getAttribute('style');*/
-
+
if (exactRegex) {
// adding same style value on property again removes style
removeStyle(el, exactRegex);
@@ -4928,7 +5022,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return "change";
}
};
-
+
function hasSameClasses(el1, el2) {
return el1.className.replace(REG_EXP_WHITE_SPACE, " ") == el2.className.replace(REG_EXP_WHITE_SPACE, " ");
}
@@ -5004,7 +5098,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, rangy.dom.getNodeIndex(newNode));
}
-
+
function Merge(firstNode) {
this.isElementMerge = (firstNode.nodeType == wysihtml5.ELEMENT_NODE);
this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
@@ -5061,20 +5155,21 @@ wysihtml5.quirks.ensureProperClearing = (function() {
var cssClassMatch;
while (node) {
cssClassMatch = this.cssClass ? hasClass(node, this.cssClass, this.similarClassRegExp) : (this.cssStyle !== "") ? false : true;
- if (node.nodeType == wysihtml5.ELEMENT_NODE && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssClassMatch) {
+ if (node.nodeType == wysihtml5.ELEMENT_NODE && node.getAttribute("contenteditable") != "false" && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssClassMatch) {
return node;
}
node = node.parentNode;
}
return false;
},
-
+
// returns parents of node with given style attribute
getAncestorWithStyle: function(node) {
var cssStyleMatch;
while (node) {
cssStyleMatch = this.cssStyle ? hasStyleAttr(node, this.similarStyleRegExp) : false;
- if (node.nodeType == wysihtml5.ELEMENT_NODE && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssStyleMatch) {
+
+ if (node.nodeType == wysihtml5.ELEMENT_NODE && node.getAttribute("contenteditable") != "false" && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssStyleMatch) {
return node;
}
node = node.parentNode;
@@ -5138,7 +5233,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
range.setEnd(rangeEndNode, rangeEndOffset);
}
},
-
+
getAdjacentMergeableTextNode: function(node, forward) {
var isTextNode = (node.nodeType == wysihtml5.TEXT_NODE);
var el = isTextNode ? node.parentNode : node;
@@ -5159,7 +5254,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return null;
},
-
+
areElementsMergeable: function(el1, el2) {
return rangy.dom.arrayContains(this.tagNames, (el1.tagName || "").toLowerCase())
&& rangy.dom.arrayContains(this.tagNames, (el2.tagName || "").toLowerCase())
@@ -5181,7 +5276,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
applyToTextNode: function(textNode) {
var parent = textNode.parentNode;
if (parent.childNodes.length == 1 && rangy.dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
-
+
if (this.cssClass) {
addClass(parent, this.cssClass, this.similarClassRegExp);
}
@@ -5204,7 +5299,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
// Split out the portion of the ancestor from which we can remove the CSS class
var ancestorRange = range.cloneRange();
ancestorRange.selectNode(ancestor);
-
+
if (ancestorRange.isPointInRange(range.endContainer, range.endOffset) && isSplitPoint(range.endContainer, range.endOffset)) {
splitNodeAt(ancestor, range.endContainer, range.endOffset);
range.setEndAfter(ancestor);
@@ -5213,15 +5308,15 @@ wysihtml5.quirks.ensureProperClearing = (function() {
ancestor = splitNodeAt(ancestor, range.startContainer, range.startOffset);
}
}
-
+
if (!styleMode && this.similarClassRegExp) {
removeClass(ancestor, this.similarClassRegExp);
}
-
+
if (styleMode && this.similarStyleRegExp) {
styleChanged = (removeOrChangeStyle(ancestor, this.cssStyle, this.similarStyleRegExp) === "change");
}
-
+
if (this.isRemovable(ancestor) && !styleChanged) {
replaceWithOwnChildren(ancestor);
}
@@ -5229,9 +5324,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
applyToRange: function(range) {
var textNodes;
- for (var ri = range.length; ri--;) {
+ for (var ri = range.length; ri--;) {
textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
-
+
if (!textNodes.length) {
try {
var node = this.createContainer(range[ri].endContainer.ownerDocument);
@@ -5240,7 +5335,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
return;
} catch(e) {}
}
-
+
range[ri].splitBoundaries();
textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
if (textNodes.length) {
@@ -5252,22 +5347,22 @@ wysihtml5.quirks.ensureProperClearing = (function() {
this.applyToTextNode(textNode);
}
}
-
+
range[ri].setStart(textNodes[0], 0);
textNode = textNodes[textNodes.length - 1];
range[ri].setEnd(textNode, textNode.length);
-
+
if (this.normalize) {
this.postApply(textNodes, range[ri]);
}
}
-
+
}
},
undoToRange: function(range) {
var textNodes, textNode, ancestorWithClass, ancestorWithStyle;
-
+
for (var ri = range.length; ri--;) {
textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
if (textNodes.length) {
@@ -5280,8 +5375,8 @@ wysihtml5.quirks.ensureProperClearing = (function() {
range[ri].selectNode(node);
textNodes = [node];
}
-
-
+
+
for (var i = 0, len = textNodes.length; i < len; ++i) {
if (range[ri].isValid()) {
textNode = textNodes[i];
@@ -5294,7 +5389,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
}
}
-
+
if (len == 1) {
this.selectNode(range[ri], textNodes[0]);
} else {
@@ -5306,10 +5401,10 @@ wysihtml5.quirks.ensureProperClearing = (function() {
this.postApply(textNodes, range[ri]);
}
}
-
+
}
},
-
+
selectNode: function(range, node) {
var isElement = node.nodeType === wysihtml5.ELEMENT_NODE,
canHaveHTML = "canHaveHTML" in node ? node.canHaveHTML : true,
@@ -5328,7 +5423,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
range.setEndAfter(node);
}
},
-
+
getTextSelectedByRange: function(textNode, range) {
var textRange = range.cloneRange();
textRange.selectNodeContents(textNode);
@@ -5343,7 +5438,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
isAppliedToRange: function(range) {
var ancestors = [],
ancestor, styleAncestor, textNodes;
-
+
for (var ri = range.length; ri--;) {
textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
if (!textNodes.length) {
@@ -5353,7 +5448,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
return ancestor ? [ancestor] : false;
}
-
+
for (var i = 0, len = textNodes.length, selectedText; i < len; ++i) {
selectedText = this.getTextSelectedByRange(textNodes[i], range[ri]);
ancestor = this.getAncestorWithClass(textNodes[i]);
@@ -5365,7 +5460,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
}
}
}
-
+
return (ancestors.length) ? ancestors : false;
},
@@ -5379,10 +5474,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
};
wysihtml5.selection.HTMLApplier = HTMLApplier;
-
-})(wysihtml5, rangy);/**
+
+})(wysihtml5, rangy);
+/**
* Rich Text Query/Formatting Commands
- *
+ *
* @example
* var commands = new wysihtml5.Commands(editor);
*/
@@ -5393,7 +5489,7 @@ wysihtml5.Commands = Base.extend(
this.composer = editor.composer;
this.doc = this.composer.doc;
},
-
+
/**
* Check whether the browser supports the given command
*
@@ -5404,7 +5500,7 @@ wysihtml5.Commands = Base.extend(
support: function(command) {
return wysihtml5.browser.supportsCommand(this.doc, command);
},
-
+
/**
* Check whether the browser supports the given command
*
@@ -5418,9 +5514,9 @@ wysihtml5.Commands = Base.extend(
args = wysihtml5.lang.array(arguments).get(),
method = obj && obj.exec,
result = null;
-
+
this.editor.fire("beforecommand:composer");
-
+
if (method) {
args.unshift(this.composer);
result = method.apply(obj, args);
@@ -5430,11 +5526,11 @@ wysihtml5.Commands = Base.extend(
result = this.doc.execCommand(command, false, value);
} catch(e) {}
}
-
+
this.editor.fire("aftercommand:composer");
return result;
},
-
+
/**
* Check whether the current command is active
* If the caret is within a bold text, then calling this with command "bold" should return true
@@ -5461,7 +5557,7 @@ wysihtml5.Commands = Base.extend(
}
}
},
-
+
/* Get command state parsed value if command has stateValue parsing function */
stateValue: function(command) {
var obj = wysihtml5.commands[command],
@@ -5473,7 +5569,7 @@ wysihtml5.Commands = Base.extend(
} else {
return false;
}
- }
+ }
});
wysihtml5.commands.bold = {
exec: function(composer, command) {
@@ -5538,35 +5634,35 @@ wysihtml5.commands.bold = {
}
composer.selection.setAfter(elementToSetCaretAfter);
}
-
+
// Changes attributes of links
function _changeLinks(composer, anchors, attributes) {
var oldAttrs;
for (var a = anchors.length; a--;) {
-
+
// Remove all old attributes
oldAttrs = anchors[a].attributes;
for (var oa = oldAttrs.length; oa--;) {
anchors[a].removeAttribute(oldAttrs.item(oa).name);
}
-
+
// Set new attributes
for (var j in attributes) {
if (attributes.hasOwnProperty(j)) {
anchors[a].setAttribute(j, attributes[j]);
- }
+ }
}
-
+
}
}
-
+
wysihtml5.commands.createLink = {
/**
* TODO: Use HTMLApplier or formatInline here
*
* Turns selection into a link
* If selection is already a link, it just changes the attributes
- *
+ *
* @example
* // either ...
* wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
@@ -5591,9 +5687,10 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatInline.state(composer, command, "A");
}
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var dom = wysihtml5.dom;
-
+
function _removeFormat(composer, anchors) {
var length = anchors.length,
i = 0,
@@ -5615,16 +5712,16 @@ wysihtml5.commands.bold = {
}
}
}
-
+
wysihtml5.commands.removeLink = {
/*
* If selection is a link, it removes the link and wraps it with a element
* The element is needed to avoid auto linking
- *
+ *
* @example
* wysihtml5.commands.createLink.exec(composer, "removeLink");
*/
-
+
exec: function(composer, command) {
var anchors = this.state(composer, command);
if (anchors) {
@@ -5638,14 +5735,15 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatInline.state(composer, command, "A");
}
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* document.execCommand("fontSize") will create either inline styles (firefox, chrome) or use font tags
* which we don't want
* Instead we set a css class
*/
(function(wysihtml5) {
var REG_EXP = /wysiwyg-font-size-[0-9a-z\-]+/g;
-
+
wysihtml5.commands.fontSize = {
exec: function(composer, command, size) {
wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
@@ -5659,7 +5757,7 @@ wysihtml5.commands.bold = {
/* In case font size adjustment to any number defined by user is preferred, we cannot use classes and must use inline styles. */
(function(wysihtml5) {
var REG_EXP = /(\s|^)font-size\s*:\s*[^;\s]+;?/gi;
-
+
wysihtml5.commands.fontSizeStyle = {
exec: function(composer, command, size) {
size = (typeof(size) == "object") ? size.size : size;
@@ -5671,12 +5769,12 @@ wysihtml5.commands.bold = {
state: function(composer, command, size) {
return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "font-size", REG_EXP);
},
-
+
stateValue: function(composer, command) {
var st = this.state(composer, command),
styleStr, fontsizeMatches,
val = false;
-
+
if (st && wysihtml5.lang.object(st).isArray()) {
st = st[0];
}
@@ -5689,14 +5787,15 @@ wysihtml5.commands.bold = {
return false;
}
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* document.execCommand("foreColor") will create either inline styles (firefox, chrome) or use font tags
* which we don't want
* Instead we set a css class
*/
(function(wysihtml5) {
var REG_EXP = /wysiwyg-color-[0-9a-z]+/g;
-
+
wysihtml5.commands.foreColor = {
exec: function(composer, command, color) {
wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
@@ -5706,19 +5805,20 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
}
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* document.execCommand("foreColor") will create either inline styles (firefox, chrome) or use font tags
* which we don't want
* Instead we set a css class
*/
(function(wysihtml5) {
var REG_EXP = /(\s|^)color\s*:\s*[^;\s]+;?/gi;
-
+
wysihtml5.commands.foreColorStyle = {
exec: function(composer, command, color) {
var colorVals = wysihtml5.quirks.styleParser.parseColor((typeof(color) == "object") ? "color:" + color.color : "color:" + color, "color"),
colString;
-
+
if (colorVals) {
colString = "color: rgb(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ');';
if (colorVals[3] !== 1) {
@@ -5731,15 +5831,15 @@ wysihtml5.commands.bold = {
state: function(composer, command) {
return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "color", REG_EXP);
},
-
+
stateValue: function(composer, command, props) {
var st = this.state(composer, command),
colorStr;
-
+
if (st && wysihtml5.lang.object(st).isArray()) {
st = st[0];
}
-
+
if (st) {
colorStr = st.getAttribute('style');
if (colorStr) {
@@ -5751,17 +5851,18 @@ wysihtml5.commands.bold = {
}
return false;
}
-
+
};
-})(wysihtml5);/* In case background adjustment to any color defined by user is preferred, we cannot use classes and must use inline styles. */
+})(wysihtml5);
+/* In case background adjustment to any color defined by user is preferred, we cannot use classes and must use inline styles. */
(function(wysihtml5) {
var REG_EXP = /(\s|^)background-color\s*:\s*[^;\s]+;?/gi;
-
+
wysihtml5.commands.bgColorStyle = {
exec: function(composer, command, color) {
var colorVals = wysihtml5.quirks.styleParser.parseColor((typeof(color) == "object") ? "background-color:" + color.color : "background-color:" + color, "background-color"),
colString;
-
+
if (colorVals) {
colString = "background-color: rgb(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ');';
if (colorVals[3] !== 1) {
@@ -5770,20 +5871,20 @@ wysihtml5.commands.bold = {
wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", false, false, colString, REG_EXP);
}
},
-
+
state: function(composer, command) {
return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "background-color", REG_EXP);
},
-
+
stateValue: function(composer, command, props) {
var st = this.state(composer, command),
- colorStr,
+ colorStr,
val = false;
-
+
if (st && wysihtml5.lang.object(st).isArray()) {
st = st[0];
}
-
+
if (st) {
colorStr = st.getAttribute('style');
if (colorStr) {
@@ -5793,15 +5894,16 @@ wysihtml5.commands.bold = {
}
return false;
}
-
+
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var dom = wysihtml5.dom,
// Following elements are grouped
// when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
// instead of creating a H4 within a H1 which would result in semantically invalid html
BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE", "BLOCKQUOTE", "DIV"];
-
+
/**
* Remove similiar classes (based on classRegExp)
* and add the desired class name
@@ -5936,7 +6038,7 @@ wysihtml5.commands.bold = {
});
}
doc.execCommand(command, false, nodeName);
-
+
if (eventListener) {
eventListener.stop();
}
@@ -5947,13 +6049,13 @@ wysihtml5.commands.bold = {
if (composer.selection.isCollapsed()) {
composer.selection.selectLine();
}
-
+
var surroundedNodes = composer.selection.surround(options);
for (var i = 0, imax = surroundedNodes.length; i < imax; i++) {
_removeLineBreakBeforeAndAfter(surroundedNodes[i]);
_removeLastChildIfLineBreak(surroundedNodes[i]);
}
-
+
// rethink restoring selection
// composer.selection.selectNode(element, wysihtml5.browser.displaysCaretInEmptyContentEditableCorrectly());
}
@@ -5961,7 +6063,7 @@ wysihtml5.commands.bold = {
function _hasClasses(element) {
return !!wysihtml5.lang.string(element.className).trim();
}
-
+
wysihtml5.commands.formatBlock = {
exec: function(composer, command, nodeName, className, classRegExp) {
var doc = composer.doc,
@@ -5971,21 +6073,21 @@ wysihtml5.commands.bold = {
selectedNodes, classRemoveAction, blockRenameFound;
nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
-
+
if (blockElements.length) {
composer.selection.executeAndRestoreSimple(function() {
for (var b = blockElements.length; b--;) {
if (classRegExp) {
classRemoveAction = _removeClass(blockElements[b], classRegExp);
}
-
+
if (classRemoveAction && nodeName === null && blockElements[b].nodeName != defaultNodeName) {
// dont rename or remove element when just setting block formating class
return;
}
-
+
var hasClasses = _hasClasses(blockElements[b]);
-
+
if (!hasClasses && (useLineBreaks || nodeName === "P")) {
// Insert a line break afterwards and beforewards when there are siblings
// that are not of type line break or block element
@@ -5997,10 +6099,10 @@ wysihtml5.commands.bold = {
}
}
});
-
+
return;
}
-
+
// Find similiar block element and rename it ( => )
if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
selectedNodes = composer.selection.findNodesInSelection(BLOCK_ELEMENTS_GROUP).concat(composer.selection.getSelectedOwnNodes());
@@ -6020,13 +6122,13 @@ wysihtml5.commands.bold = {
if (className) {
_addClass(blockElement, className, classRegExp);
}
-
+
blockRenameFound = true;
}
}
-
+
});
-
+
if (blockRenameFound) {
return;
}
@@ -6044,19 +6146,19 @@ wysihtml5.commands.bold = {
if (composer.commands.support(command)) {
_execCommand(doc, composer, command, nodeName || defaultNodeName, className);
return;
- }
+ }
}
-
+
},
state: function(composer, command, nodeName, className, classRegExp) {
var nodes = composer.selection.getSelectedOwnNodes(),
parents = [],
parent;
-
+
nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
-
+
//var selectedNode = composer.selection.getSelectedNode();
for (var i = 0, maxi = nodes.length; i < maxi; i++) {
parent = dom.getParentElement(nodes[i], {
@@ -6073,22 +6175,23 @@ wysihtml5.commands.bold = {
}
return parents;
}
-
-
+
+
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* formatInline scenarios for tag "B" (| = caret, |foo| = selected text)
*
* #1 caret in unformatted text:
* abcdefg|
* output:
* abcdefg|
- *
+ *
* #2 unformatted text selected:
* abc|deg|h
* output:
* abc|deg|h
- *
+ *
* #3 unformatted text selected across boundaries:
* ab|c defg|h
* output:
@@ -6118,28 +6221,28 @@ wysihtml5.commands.bold = {
"i": "em"
},
htmlApplier = {};
-
+
function _getTagNames(tagName) {
var alias = ALIAS_MAPPING[tagName];
return alias ? [tagName.toLowerCase(), alias.toLowerCase()] : [tagName.toLowerCase()];
}
-
+
function _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp) {
var identifier = tagName + ":" + className;
if (cssStyle) {
- identifier += ":" + cssStyle
+ identifier += ":" + cssStyle;
}
if (!htmlApplier[identifier]) {
htmlApplier[identifier] = new wysihtml5.selection.HTMLApplier(_getTagNames(tagName), className, classRegExp, true, cssStyle, styleRegExp);
}
return htmlApplier[identifier];
}
-
+
wysihtml5.commands.formatInline = {
exec: function(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, dontRestoreSelect, noCleanup) {
var range = composer.selection.createRange();
ownRanges = composer.selection.getOwnRanges();
-
+
if (!ownRanges || ownRanges.length == 0) {
return false;
}
@@ -6161,7 +6264,7 @@ wysihtml5.commands.bold = {
}
}
},
-
+
// Executes so that if collapsed caret is in a state and executing that state it should unformat that state
// It is achieved by selecting the entire state element before executing.
// This works on built in contenteditable inline format commands
@@ -6172,7 +6275,7 @@ wysihtml5.commands.bold = {
composer.selection.executeAndRestoreSimple(function() {
var parent = state_element.parentNode;
composer.selection.selectNode(state_element, true);
- wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, true);
+ wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, true, true);
});
} else {
wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp);
@@ -6196,15 +6299,16 @@ wysihtml5.commands.bold = {
}
ownRanges = composer.selection.getOwnRanges();
-
+
if (ownRanges.length == 0) {
return false;
}
-
+
return _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp).isAppliedToRange(ownRanges);
}
};
-})(wysihtml5);wysihtml5.commands.insertHTML = {
+})(wysihtml5);
+wysihtml5.commands.insertHTML = {
exec: function(composer, command, html) {
if (composer.commands.support(command)) {
composer.doc.execCommand(command, false, html);
@@ -6219,12 +6323,12 @@ wysihtml5.commands.bold = {
};
(function(wysihtml5) {
var NODE_NAME = "IMG";
-
+
wysihtml5.commands.insertImage = {
/**
* Inserts an
* If selection is already an image link, it removes it
- *
+ *
* @example
* // either ...
* wysihtml5.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
@@ -6258,7 +6362,7 @@ wysihtml5.commands.bold = {
}
image = doc.createElement(NODE_NAME);
-
+
for (var i in value) {
image.setAttribute(i === "className" ? "class" : i, value[i]);
}
@@ -6314,9 +6418,10 @@ wysihtml5.commands.bold = {
return imagesInSelection[0];
}
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var LINE_BREAK = "
" + (wysihtml5.browser.needsSpaceAfterLineBreak() ? " " : "");
-
+
wysihtml5.commands.insertLineBreak = {
exec: function(composer, command) {
if (composer.commands.support(command)) {
@@ -6333,7 +6438,8 @@ wysihtml5.commands.bold = {
return false;
}
};
-})(wysihtml5);wysihtml5.commands.insertOrderedList = {
+})(wysihtml5);
+wysihtml5.commands.insertOrderedList = {
exec: function(composer, command) {
var doc = composer.doc,
selectedNode = composer.selection.getSelectedNode(),
@@ -6342,12 +6448,20 @@ wysihtml5.commands.bold = {
tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
isEmpty,
tempElement;
-
+
+ // do not count list elements outside of composer
+ if (list && !composer.element.contains(list)) {
+ list = null
+ }
+ if (otherList && !composer.element.contains(otherList)) {
+ otherList = null
+ }
+
if (!list && !otherList && composer.commands.support(command)) {
doc.execCommand(command, false, null);
return;
}
-
+
if (list) {
// Unwrap list
// - foo
- bar
@@ -6381,12 +6495,15 @@ wysihtml5.commands.bold = {
}
}
},
-
+
state: function(composer) {
- var selectedNode = composer.selection.getSelectedNode();
- return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "OL" });
+ var selectedNode = composer.selection.getSelectedNode(),
+ node = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "OL" });
+
+ return (composer.element.contains(node) ? node : false);
}
-};wysihtml5.commands.insertUnorderedList = {
+};
+wysihtml5.commands.insertUnorderedList = {
exec: function(composer, command) {
var doc = composer.doc,
selectedNode = composer.selection.getSelectedNode(),
@@ -6395,12 +6512,20 @@ wysihtml5.commands.bold = {
tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
isEmpty,
tempElement;
-
+
+ // do not count list elements outside of composer
+ if (list && !composer.element.contains(list)) {
+ list = null
+ }
+ if (otherList && !composer.element.contains(otherList)) {
+ otherList = null
+ }
+
if (!list && !otherList && composer.commands.support(command)) {
doc.execCommand(command, false, null);
return;
}
-
+
if (list) {
// Unwrap list
//
@@ -6434,12 +6559,15 @@ wysihtml5.commands.bold = {
}
}
},
-
+
state: function(composer) {
- var selectedNode = composer.selection.getSelectedNode();
- return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "UL" });
+ var selectedNode = composer.selection.getSelectedNode(),
+ node = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "UL" });
+
+ return (composer.element.contains(node) ? node : false);
}
-};wysihtml5.commands.italic = {
+};
+wysihtml5.commands.italic = {
exec: function(composer, command) {
wysihtml5.commands.formatInline.execWithToggle(composer, command, "i");
},
@@ -6452,10 +6580,11 @@ wysihtml5.commands.bold = {
// opera: only
return wysihtml5.commands.formatInline.state(composer, command, "i");
}
-};(function(wysihtml5) {
+};
+(function(wysihtml5) {
var CLASS_NAME = "wysiwyg-text-align-center",
REG_EXP = /wysiwyg-text-align-[0-9a-z]+/g;
-
+
wysihtml5.commands.justifyCenter = {
exec: function(composer, command) {
return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
@@ -6465,10 +6594,11 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
}
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var CLASS_NAME = "wysiwyg-text-align-left",
REG_EXP = /wysiwyg-text-align-[0-9a-z]+/g;
-
+
wysihtml5.commands.justifyLeft = {
exec: function(composer, command) {
return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
@@ -6478,10 +6608,11 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
}
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var CLASS_NAME = "wysiwyg-text-align-right",
REG_EXP = /wysiwyg-text-align-[0-9a-z]+/g;
-
+
wysihtml5.commands.justifyRight = {
exec: function(composer, command) {
return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
@@ -6491,10 +6622,11 @@ wysihtml5.commands.bold = {
return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
}
};
-})(wysihtml5);(function(wysihtml5) {
+})(wysihtml5);
+(function(wysihtml5) {
var CLASS_NAME = "wysiwyg-text-align-justify",
REG_EXP = /wysiwyg-text-align-[0-9a-z]+/g;
-
+
wysihtml5.commands.justifyFull = {
exec: function(composer, command) {
return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
@@ -6513,7 +6645,8 @@ wysihtml5.commands.redo = {
state: function(composer) {
return false;
}
-};wysihtml5.commands.underline = {
+};
+wysihtml5.commands.underline = {
exec: function(composer, command) {
wysihtml5.commands.formatInline.execWithToggle(composer, command, "u");
},
@@ -6521,7 +6654,8 @@ wysihtml5.commands.redo = {
state: function(composer, command) {
return wysihtml5.commands.formatInline.state(composer, command, "u");
}
-};wysihtml5.commands.undo = {
+};
+wysihtml5.commands.undo = {
exec: function(composer) {
return composer.undoManager.undo();
},
@@ -6529,11 +6663,17 @@ wysihtml5.commands.redo = {
state: function(composer) {
return false;
}
-};wysihtml5.commands.createTable = {
+};
+wysihtml5.commands.createTable = {
exec: function(composer, command, value) {
var col, row, html;
if (value && value.cols && value.rows && parseInt(value.cols, 10) > 0 && parseInt(value.rows, 10) > 0) {
- html = "";
+ if (value.tableStyle) {
+ html = "";
+ } else {
+ html = "";
+ }
+ html += "";
for (row = 0; row < value.rows; row ++) {
html += '';
for (col = 0; col < value.cols; col ++) {
@@ -6544,15 +6684,16 @@ wysihtml5.commands.redo = {
html += "
";
composer.commands.exec("insertHTML", html);
//composer.selection.insertHTML(html);
- }
-
-
+ }
+
+
},
state: function(composer, command) {
return false;
}
-};wysihtml5.commands.mergeTableCells = {
+};
+wysihtml5.commands.mergeTableCells = {
exec: function(composer, command) {
if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
if (this.state(composer, command)) {
@@ -6581,10 +6722,11 @@ wysihtml5.commands.redo = {
}
return false;
}
-};wysihtml5.commands.addTableCells = {
+};
+wysihtml5.commands.addTableCells = {
exec: function(composer, command, value) {
if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
-
+
// switches start and end if start is bigger than end (reverse selection)
var tableSelect = wysihtml5.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end);
if (value == "before" || value == "above") {
@@ -6601,19 +6743,20 @@ wysihtml5.commands.redo = {
state: function(composer, command) {
return false;
}
-};wysihtml5.commands.deleteTableCells = {
+};
+wysihtml5.commands.deleteTableCells = {
exec: function(composer, command, value) {
if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
var tableSelect = wysihtml5.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end),
idx = wysihtml5.dom.table.indexOf(tableSelect.start),
selCell,
table = composer.tableSelection.table;
-
+
wysihtml5.dom.table.removeCells(tableSelect.start, value);
setTimeout(function() {
// move selection to next or previous if not present
selCell = wysihtml5.dom.table.findCell(table, idx);
-
+
if (!selCell){
if (value == "row") {
selCell = wysihtml5.dom.table.findCell(table, {
@@ -6621,14 +6764,14 @@ wysihtml5.commands.redo = {
"col": idx.col
});
}
-
+
if (value == "column") {
selCell = wysihtml5.dom.table.findCell(table, {
"row": idx.row,
"col": idx.col - 1
});
}
- }
+ }
if (selCell) {
composer.tableSelection.select(selCell, selCell);
}
@@ -6640,7 +6783,8 @@ wysihtml5.commands.redo = {
state: function(composer, command) {
return false;
}
-};/**
+};
+/**
* Undo Manager for wysihtml5
* slightly inspired by http://rniwa.com/editing/undomanager.html#the-undomanager-interface
*/
@@ -6655,45 +6799,45 @@ wysihtml5.commands.redo = {
UNDO_HTML = '' + wysihtml5.INVISIBLE_SPACE + '',
REDO_HTML = '' + wysihtml5.INVISIBLE_SPACE + '',
dom = wysihtml5.dom;
-
+
function cleanTempElements(doc) {
var tempElement;
while (tempElement = doc.querySelector("._wysihtml5-temp")) {
tempElement.parentNode.removeChild(tempElement);
}
}
-
+
wysihtml5.UndoManager = wysihtml5.lang.Dispatcher.extend(
/** @scope wysihtml5.UndoManager.prototype */ {
constructor: function(editor) {
this.editor = editor;
this.composer = editor.composer;
this.element = this.composer.element;
-
+
this.position = 0;
this.historyStr = [];
this.historyDom = [];
-
+
this.transact();
-
+
this._observe();
},
-
+
_observe: function() {
var that = this,
doc = this.composer.sandbox.getDocument(),
lastKey;
-
+
// Catch CTRL+Z and CTRL+Y
dom.observe(this.element, "keydown", function(event) {
if (event.altKey || (!event.ctrlKey && !event.metaKey)) {
return;
}
-
+
var keyCode = event.keyCode,
isUndo = keyCode === Z_KEY && !event.shiftKey,
isRedo = (keyCode === Z_KEY && event.shiftKey) || (keyCode === Y_KEY);
-
+
if (isUndo) {
that.undo();
event.preventDefault();
@@ -6702,21 +6846,21 @@ wysihtml5.commands.redo = {
event.preventDefault();
}
});
-
+
// Catch delete and backspace
dom.observe(this.element, "keydown", function(event) {
var keyCode = event.keyCode;
if (keyCode === lastKey) {
return;
}
-
+
lastKey = keyCode;
-
+
if (keyCode === BACKSPACE_KEY || keyCode === DELETE_KEY) {
that.transact();
}
});
-
+
// Now this is very hacky:
// These days browsers don't offer a undo/redo event which we could hook into
// to be notified when the user hits undo/redo in the contextmenu.
@@ -6724,14 +6868,14 @@ wysihtml5.commands.redo = {
// The last element being inserted will be immediately be removed again by a exexCommand("undo")
// => When the second element appears in the dom tree then we know the user clicked "redo" in the context menu
// => When the first element disappears from the dom tree then we know the user clicked "undo" in the context menu
-
+
// TODO: unexpected behaviour. Tends to undo on contextmenu showing in chrome on newly inserted blocks
/*if (wysihtml5.browser.hasUndoInContextMenu()) {
var interval, observed, cleanUp = function() {
cleanTempElements(doc);
clearInterval(interval);
};
-
+
dom.observe(this.element, "contextmenu", function() {
cleanUp();
that.composer.selection.executeAndRestoreSimple(function() {
@@ -6763,104 +6907,104 @@ wysihtml5.commands.redo = {
}
});
}*/
-
+
this.editor
.on("newword:composer", function() {
that.transact();
})
-
+
.on("beforecommand:composer", function() {
that.transact();
});
},
-
+
transact: function() {
var previousHtml = this.historyStr[this.position - 1],
currentHtml = this.composer.getValue();
-
+
if (currentHtml === previousHtml) {
return;
}
-
+
var length = this.historyStr.length = this.historyDom.length = this.position;
if (length > MAX_HISTORY_ENTRIES) {
this.historyStr.shift();
this.historyDom.shift();
this.position--;
}
-
+
this.position++;
-
+
var range = this.composer.selection.getRange(),
node = (range && range.startContainer) ? range.startContainer : this.element,
offset = (range && range.startOffset) ? range.startOffset : 0,
element,
position;
-
+
if (node.nodeType === wysihtml5.ELEMENT_NODE) {
element = node;
} else {
element = node.parentNode;
position = this.getChildNodeIndex(element, node);
}
-
+
element.setAttribute(DATA_ATTR_OFFSET, offset);
if (typeof(position) !== "undefined") {
element.setAttribute(DATA_ATTR_NODE, position);
}
-
+
var clone = this.element.cloneNode(!!currentHtml);
this.historyDom.push(clone);
this.historyStr.push(currentHtml);
-
+
element.removeAttribute(DATA_ATTR_OFFSET);
element.removeAttribute(DATA_ATTR_NODE);
},
-
+
undo: function() {
this.transact();
-
+
if (!this.undoPossible()) {
return;
}
-
+
this.set(this.historyDom[--this.position - 1]);
this.editor.fire("undo:composer");
},
-
+
redo: function() {
if (!this.redoPossible()) {
return;
}
-
+
this.set(this.historyDom[++this.position - 1]);
this.editor.fire("redo:composer");
},
-
+
undoPossible: function() {
return this.position > 1;
},
-
+
redoPossible: function() {
return this.position < this.historyStr.length;
},
-
+
set: function(historyEntry) {
this.element.innerHTML = "";
-
+
var i = 0,
childNodes = historyEntry.childNodes,
length = historyEntry.childNodes.length;
-
+
for (; i
" ||
this.hasPlaceholderSet();
},
-
+
_initContentEditableArea: function() {
var that = this;
-
+
if (this.config.noTextarea) {
this.sandbox = new dom.ContentEditableArea(function() {
that._create();
@@ -7088,20 +7233,20 @@ wysihtml5.views.View = Base.extend(
_initSandbox: function() {
var that = this;
-
+
this.sandbox = new dom.Sandbox(function() {
that._create();
}, {
stylesheets: this.config.stylesheets
});
this.editableArea = this.sandbox.getIframe();
-
+
var textareaElement = this.textarea.element;
dom.insert(this.editableArea).after(textareaElement);
-
+
this._createWysiwygFormField();
},
-
+
// Creates hidden field which tells the server after submit, that the user used an wysiwyg editor
_createWysiwygFormField: function() {
if (this.textarea.element.form) {
@@ -7123,40 +7268,40 @@ wysihtml5.views.View = Base.extend(
} else {
this.cleanUp(); // cleans contenteditable on initiation as it may contain html
}
-
+
// Make sure our selection handler is ready
this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.uneditableContainerClassname);
-
+
// Make sure commands dispatcher is ready
this.commands = new wysihtml5.Commands(this.parent);
-
+
if (!this.config.noTextarea) {
dom.copyAttributes([
"className", "spellcheck", "title", "lang", "dir", "accessKey"
]).from(this.textarea.element).to(this.element);
}
-
+
dom.addClass(this.element, this.config.composerClassName);
- //
+ //
// Make the editor look like the original textarea, by syncing styles
if (this.config.style && !this.config.contentEditableMode) {
this.style();
}
-
+
this.observe();
-
+
var name = this.config.name;
if (name) {
dom.addClass(this.element, name);
if (!this.config.contentEditableMode) { dom.addClass(this.editableArea, name); }
}
-
+
this.enable();
-
+
if (!this.config.noTextarea && this.textarea.element.disabled) {
this.disable();
}
-
+
// Simulate html5 placeholder attribute on contentEditable element
var placeholderText = typeof(this.config.placeholder) === "string"
? this.config.placeholder
@@ -7164,34 +7309,34 @@ wysihtml5.views.View = Base.extend(
if (placeholderText) {
dom.simulatePlaceholder(this.parent, this, placeholderText);
}
-
+
// Make sure that the browser avoids using inline styles whenever possible
this.commands.exec("styleWithCSS", false);
-
+
this._initAutoLinking();
this._initObjectResizing();
this._initUndoManager();
this._initLineBreaking();
-
+
// Simulate html5 autofocus on contentEditable element
// This doesn't work on IOS (5.1.1)
if (!this.config.noTextarea && (this.textarea.element.hasAttribute("autofocus") || document.querySelector(":focus") == this.textarea.element) && !browser.isIos()) {
setTimeout(function() { that.focus(true); }, 100);
}
-
+
// IE sometimes leaves a single paragraph, which can't be removed by the user
if (!browser.clearsContentEditableCorrectly()) {
wysihtml5.quirks.ensureProperClearing(this);
}
-
+
// Set up a sync that makes sure that textarea and editor have the same content
if (this.initSync && this.config.sync) {
this.initSync();
}
-
+
// Okay hide the textarea, we are ready to go
if (!this.config.noTextarea) { this.textarea.hide(); }
-
+
// Fire global (before-)load event
this.parent.fire("beforeload").fire("load");
},
@@ -7218,7 +7363,7 @@ wysihtml5.views.View = Base.extend(
});
}
});
-
+
dom.observe(this.element, "blur", function() {
dom.autoLink(that.element);
});
@@ -7272,7 +7417,7 @@ wysihtml5.views.View = Base.extend(
_initObjectResizing: function() {
this.commands.exec("enableObjectResizing", true);
-
+
// IE sets inline styles after resizing objects
// The following lines make sure that the width/height css properties
// are copied over to the width/height attributes
@@ -7280,17 +7425,17 @@ wysihtml5.views.View = Base.extend(
var properties = ["width", "height"],
propertiesLength = properties.length,
element = this.element;
-
+
dom.observe(element, "resizeend", function(event) {
var target = event.target || event.srcElement,
style = target.style,
i = 0,
property;
-
+
if (target.nodeName !== "IMG") {
return;
}
-
+
for (; i or tags after paste
// Inserting an invisible white space in front of it fixes the issue
// This is too hacky and causes selection not to replace content on paste in chrome
@@ -7353,14 +7498,14 @@ wysihtml5.views.View = Base.extend(
});
}*/
-
+
dom.observe(this.element, "keydown", function(event) {
var keyCode = event.keyCode;
-
+
if (event.shiftKey) {
return;
}
-
+
if (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY) {
return;
}
@@ -7370,7 +7515,7 @@ wysihtml5.views.View = Base.extend(
// Unwrap paragraph after leaving a list or a H1-6
var selectedNode = that.selection.getSelectedNode(),
list;
-
+
if (blockElement.nodeName === "LI") {
if (!selectedNode) {
return;
@@ -7389,11 +7534,11 @@ wysihtml5.views.View = Base.extend(
}, 0);
return;
}
-
+
if (that.config.useLineBreaks && keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
event.preventDefault();
that.commands.exec("insertLineBreak");
-
+
}
});
}
@@ -7445,12 +7590,12 @@ wysihtml5.views.View = Base.extend(
"body > p:first-child { margin-top: 0; }",
"._wysihtml5-temp { display: none; }",
wysihtml5.browser.isGecko ?
- "body.placeholder { color: graytext !important; }" :
+ "body.placeholder { color: graytext !important; }" :
"body.placeholder { color: #a9a9a9 !important; }",
// Ensure that user see's broken images and can delete them
"img:-moz-broken { -moz-force-broken-image-icon: 1; height: 24px; width: 24px; }"
];
-
+
/**
* With "setActive" IE offers a smart way of focusing elements without scrolling them into view:
* http://msdn.microsoft.com/en-us/library/ms536738(v=vs.85).aspx
@@ -7474,7 +7619,7 @@ wysihtml5.views.View = Base.extend(
left: elementStyle.left,
WebkitUserSelect: elementStyle.WebkitUserSelect
};
-
+
dom.setStyles({
position: "absolute",
top: "-99999px",
@@ -7482,11 +7627,11 @@ wysihtml5.views.View = Base.extend(
// Don't ask why but temporarily setting -webkit-user-select to none makes the whole thing performing smoother
WebkitUserSelect: "none"
}).on(element);
-
+
element.focus();
-
+
dom.setStyles(originalStyles).on(element);
-
+
if (win.scrollTo) {
// Some browser extensions unset this method to prevent annoyances
// "Better PopUp Blocker" for Chrome http://code.google.com/p/betterpopupblocker/source/browse/trunk/blockStart.js#100
@@ -7495,8 +7640,8 @@ wysihtml5.views.View = Base.extend(
}
}
};
-
-
+
+
wysihtml5.views.Composer.prototype.style = function() {
var that = this,
originalActiveElement = doc.querySelector(":focus"),
@@ -7506,100 +7651,101 @@ wysihtml5.views.View = Base.extend(
originalDisplayValue = textareaElement.style.display,
originalDisabled = textareaElement.disabled,
displayValueForCopying;
-
+
this.focusStylesHost = HOST_TEMPLATE.cloneNode(false);
this.blurStylesHost = HOST_TEMPLATE.cloneNode(false);
this.disabledStylesHost = HOST_TEMPLATE.cloneNode(false);
-
+
// Remove placeholder before copying (as the placeholder has an affect on the computed style)
if (hasPlaceholder) {
textareaElement.removeAttribute("placeholder");
}
-
+
if (textareaElement === originalActiveElement) {
textareaElement.blur();
}
-
+
// enable for copying styles
textareaElement.disabled = false;
-
+
// set textarea to display="none" to get cascaded styles via getComputedStyle
textareaElement.style.display = displayValueForCopying = "none";
-
+
if ((textareaElement.getAttribute("rows") && dom.getStyle("height").from(textareaElement) === "auto") ||
(textareaElement.getAttribute("cols") && dom.getStyle("width").from(textareaElement) === "auto")) {
textareaElement.style.display = displayValueForCopying = originalDisplayValue;
}
-
+
// --------- iframe styles (has to be set before editor styles, otherwise IE9 sets wrong fontFamily on blurStylesHost) ---------
dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.editableArea).andTo(this.blurStylesHost);
-
+
// --------- editor styles ---------
dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.element).andTo(this.blurStylesHost);
-
+
// --------- apply standard rules ---------
dom.insertCSS(ADDITIONAL_CSS_RULES).into(this.element.ownerDocument);
-
+
// --------- :disabled styles ---------
textareaElement.disabled = true;
dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.disabledStylesHost);
dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.disabledStylesHost);
textareaElement.disabled = originalDisabled;
-
+
// --------- :focus styles ---------
textareaElement.style.display = originalDisplayValue;
focusWithoutScrolling(textareaElement);
textareaElement.style.display = displayValueForCopying;
-
+
dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.focusStylesHost);
dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.focusStylesHost);
-
+
// reset textarea
textareaElement.style.display = originalDisplayValue;
-
+
dom.copyStyles(["display"]).from(textareaElement).to(this.editableArea);
-
+
// Make sure that we don't change the display style of the iframe when copying styles oblur/onfocus
// this is needed for when the change_view event is fired where the iframe is hidden and then
// the blur event fires and re-displays it
var boxFormattingStyles = wysihtml5.lang.array(BOX_FORMATTING).without(["display"]);
-
+
// --------- restore focus ---------
if (originalActiveElement) {
originalActiveElement.focus();
} else {
textareaElement.blur();
}
-
+
// --------- restore placeholder ---------
if (hasPlaceholder) {
textareaElement.setAttribute("placeholder", originalPlaceholder);
}
-
+
// --------- Sync focus/blur styles ---------
this.parent.on("focus:composer", function() {
dom.copyStyles(boxFormattingStyles) .from(that.focusStylesHost).to(that.editableArea);
dom.copyStyles(TEXT_FORMATTING) .from(that.focusStylesHost).to(that.element);
});
-
+
this.parent.on("blur:composer", function() {
dom.copyStyles(boxFormattingStyles) .from(that.blurStylesHost).to(that.editableArea);
dom.copyStyles(TEXT_FORMATTING) .from(that.blurStylesHost).to(that.element);
});
-
+
this.parent.observe("disable:composer", function() {
dom.copyStyles(boxFormattingStyles) .from(that.disabledStylesHost).to(that.editableArea);
dom.copyStyles(TEXT_FORMATTING) .from(that.disabledStylesHost).to(that.element);
});
-
+
this.parent.observe("enable:composer", function() {
dom.copyStyles(boxFormattingStyles) .from(that.blurStylesHost).to(that.editableArea);
dom.copyStyles(TEXT_FORMATTING) .from(that.blurStylesHost).to(that.element);
});
-
+
return this;
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* Taking care of events
* - Simulating 'change' event on contentEditable element
* - Handling drag & drop logic
@@ -7618,7 +7764,7 @@ wysihtml5.views.View = Base.extend(
"73": "italic", // I
"85": "underline" // U
};
-
+
wysihtml5.views.Composer.prototype.observe = function() {
var that = this,
state = this.getValue(),
@@ -7643,15 +7789,15 @@ wysihtml5.views.View = Base.extend(
}
}, 250);
}
-
+
// --------- User interaction tracking --
-
+
dom.observe(focusBlurElement, interactionEvents, function() {
setTimeout(function() {
that.parent.fire("interaction").fire("interaction:composer");
}, 0);
});
-
+
if (this.config.handleTables) {
this.tableSelection = wysihtml5.quirks.tableCellsSelection(element, that.parent);
@@ -7703,13 +7849,13 @@ wysihtml5.views.View = Base.extend(
var allImages = element.querySelectorAll('img'),
notMyImages = element.querySelectorAll('.' + that.config.uneditableContainerClassname + ' img'),
myImages = wysihtml5.lang.array(allImages).without(notMyImages);
-
+
if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
that.selection.selectNode(target);
}
});
}
-
+
if (!browser.canSelectImagesInContentEditable()) {
dom.observe(element, "drop", function(event) {
// TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
@@ -7718,17 +7864,17 @@ wysihtml5.views.View = Base.extend(
}, 0);
});
}
-
+
if (browser.hasHistoryIssue() && browser.supportsSelectionModify()) {
dom.observe(element, "keydown", function(event) {
if (!event.metaKey && !event.ctrlKey) {
return;
}
-
+
var keyCode = event.keyCode,
win = element.ownerDocument.defaultView,
selection = win.getSelection();
-
+
if (keyCode === 37 || keyCode === 39) {
if (keyCode === 37) {
selection.modify("extend", "left", "lineboundary");
@@ -7746,7 +7892,7 @@ wysihtml5.views.View = Base.extend(
}
});
}
-
+
// --------- Shortcut logic ---------
dom.observe(element, "keydown", function(event) {
var keyCode = event.keyCode,
@@ -7756,7 +7902,7 @@ wysihtml5.views.View = Base.extend(
event.preventDefault();
}
if (keyCode == 8) {
-
+
if (that.selection.isCollapsed()) {
if (that.selection.caretIsInTheBeginnig()) {
event.preventDefault();
@@ -7764,17 +7910,17 @@ wysihtml5.views.View = Base.extend(
var beforeUneditable = that.selection.caretIsBeforeUneditable();
if (beforeUneditable) {
event.preventDefault();
-
+
// TODO: take the how to delete around uneditable out of here
// merge node with previous node from uneditable
var prevNode = that.selection.getPreviousNode(beforeUneditable, true),
curNode = that.selection.getSelectedNode();
-
- if (curNode.nodeType !== 1 && curNode.parentNode !== element) { curNode = curNode.parentNode; }
+
+ if (curNode.nodeType !== 1 && curNode.parentNode !== element) { curNode = curNode.parentNode; }
if (prevNode) {
if (curNode.nodeType == 1) {
var first = curNode.firstChild;
-
+
if (prevNode.nodeType == 1) {
while (curNode.firstChild) {
prevNode.appendChild(curNode.firstChild);
@@ -7804,7 +7950,7 @@ wysihtml5.views.View = Base.extend(
event.preventDefault();
that.selection.deleteContents();
}
-
+
}
});
@@ -7826,7 +7972,7 @@ wysihtml5.views.View = Base.extend(
event.preventDefault();
}
});
-
+
// --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the ) ---------
if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
dom.observe(this.iframe, "focus", function() {
@@ -7843,13 +7989,13 @@ wysihtml5.views.View = Base.extend(
}, 0);
});
}
-
+
// --------- Show url in tooltip when hovering links or images ---------
var titlePrefixes = {
IMG: "Image: ",
A: "Link: "
};
-
+
dom.observe(element, "mouseover", function(event) {
var target = event.target,
nodeName = target.nodeName,
@@ -7864,12 +8010,13 @@ wysihtml5.views.View = Base.extend(
}
});
};
-})(wysihtml5);/**
+})(wysihtml5);
+/**
* Class that takes care that the value of the composer and the textarea is always in sync
*/
(function(wysihtml5) {
var INTERVAL = 400;
-
+
wysihtml5.views.Synchronizer = Base.extend(
/** @scope wysihtml5.views.Synchronizer.prototype */ {
@@ -7964,17 +8111,17 @@ wysihtml5.views.View = Base.extend(
wysihtml5.views.Textarea = wysihtml5.views.View.extend(
/** @scope wysihtml5.views.Textarea.prototype */ {
name: "textarea",
-
+
constructor: function(parent, textareaElement, config) {
this.base(parent, textareaElement, config);
-
+
this._observe();
},
-
+
clear: function() {
this.element.value = "";
},
-
+
getValue: function(parse) {
var value = this.isEmpty() ? "" : this.element.value;
if (parse) {
@@ -7982,19 +8129,19 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
}
return value;
},
-
+
setValue: function(html, parse) {
if (parse) {
html = this.parent.parse(html);
}
this.element.value = html;
},
-
+
cleanUp: function() {
var html = this.parent.parse(this.element.value);
this.element.value = html;
},
-
+
hasPlaceholderSet: function() {
var supportsPlaceholder = wysihtml5.browser.supportsPlaceholderAttributeOn(this.element),
placeholderText = this.element.getAttribute("placeholder") || null,
@@ -8002,11 +8149,11 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
isEmpty = !value;
return (supportsPlaceholder && isEmpty) || (value === placeholderText);
},
-
+
isEmpty: function() {
return !wysihtml5.lang.string(this.element.value).trim() || this.hasPlaceholderSet();
},
-
+
_observe: function() {
var element = this.element,
parent = this.parent,
@@ -8019,19 +8166,20 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
* This is the case for focusin and focusout, so let's use them whenever possible, kkthxbai
*/
events = wysihtml5.browser.supportsEvent("focusin") ? ["focusin", "focusout", "change"] : ["focus", "blur", "change"];
-
+
parent.on("beforeload", function() {
wysihtml5.dom.observe(element, events, function(event) {
var eventName = eventMapping[event.type] || event.type;
parent.fire(eventName).fire(eventName + ":textarea");
});
-
+
wysihtml5.dom.observe(element, ["paste", "drop"], function() {
setTimeout(function() { parent.fire("paste").fire("paste:textarea"); }, 0);
});
});
}
-});/**
+});
+/**
* WYSIHTML5 Editor
*
* @param {Element} editableElement Reference to the textarea which should be turned into a rich text interface
@@ -8064,9 +8212,9 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
*/
(function(wysihtml5) {
var undef;
-
+
var defaultConfig = {
- // Give the editor a name, the name will also be set as class name on the iframe and on the iframe's body
+ // Give the editor a name, the name will also be set as class name on the iframe and on the iframe's body
name: undef,
// Whether the editor should look like the textarea (by adopting styles)
style: true,
@@ -8077,7 +8225,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
showToolbarAfterInit: true,
// Whether urls, entered by the user should automatically become clickable-links
autoLink: true,
- // Includes table editing events and cell selection tracking
+ // Includes table editing events and cell selection tracking
handleTables: true,
// Object which includes parser rules to apply when html gets inserted via copy & paste
// See parser_rules/*.js for examples
@@ -8102,17 +8250,17 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
contentEditableMode: false,
xingAlert: false,
// Classname of container that editor should not touch and pass through
- // Pass false to disable
+ // Pass false to disable
uneditableContainerClassname: "wysihtml5-uneditable-container"
};
-
+
wysihtml5.Editor = wysihtml5.lang.Dispatcher.extend(
/** @scope wysihtml5.Editor.prototype */ {
constructor: function(editableElement, config) {
this.editableElement = typeof(editableElement) === "string" ? document.getElementById(editableElement) : editableElement;
this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
this._isCompatible = wysihtml5.browser.supported();
-
+
if (this.editableElement.nodeName.toLowerCase() != "textarea") {
this.config.contentEditableMode = true;
this.config.noTextarea = true;
@@ -8121,32 +8269,32 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
this.textarea = new wysihtml5.views.Textarea(this, this.editableElement, this.config);
this.currentView = this.textarea;
}
-
+
// Sort out unsupported/unwanted browsers here
if (!this._isCompatible || (!this.config.supportTouchDevices && wysihtml5.browser.isTouchDevice())) {
var that = this;
setTimeout(function() { that.fire("beforeload").fire("load"); }, 0);
return;
}
-
+
// Add class name to body, to indicate that the editor is supported
wysihtml5.dom.addClass(document.body, this.config.bodyClassName);
-
+
this.composer = new wysihtml5.views.Composer(this, this.editableElement, this.config);
this.currentView = this.composer;
-
+
if (typeof(this.config.parser) === "function") {
this._initParser();
}
-
+
this.on("beforeload", this.handleBeforeLoad);
-
-
+
+
if (this.config.xingAlert) {
try { console.log("Heya! This page is using wysihtml5 for rich text editing. Check out https://github.com/xing/wysihtml5");} catch(e) {}
}
},
-
+
handleBeforeLoad: function() {
if (!this.config.noTextarea) {
this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
@@ -8155,7 +8303,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
this.toolbar = new wysihtml5.toolbar.Toolbar(this, this.config.toolbar, this.config.showToolbarAfterInit);
}
},
-
+
isCompatible: function() {
return this._isCompatible;
},
@@ -8171,15 +8319,15 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
setValue: function(html, parse) {
this.fire("unset_placeholder");
-
+
if (!html) {
return this.clear();
}
-
+
this.currentView.setValue(html, parse);
return this;
},
-
+
cleanUp: function() {
this.currentView.cleanUp();
},
@@ -8196,7 +8344,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
this.currentView.disable();
return this;
},
-
+
/**
* Activate editor
*/
@@ -8204,15 +8352,15 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
this.currentView.enable();
return this;
},
-
+
isEmpty: function() {
return this.currentView.isEmpty();
},
-
+
hasPlaceholderSet: function() {
return this.currentView.hasPlaceholderSet();
},
-
+
parse: function(htmlOrElement) {
var parseContext = (this.config.contentEditableMode) ? document : this.composer.sandbox.getDocument();
var returnValue = this.config.parser(htmlOrElement, {
@@ -8226,7 +8374,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
}
return returnValue;
},
-
+
/**
* Prepare html parser logic
* - Observes for paste and drop