function wrapLineNumberDiv(place) {
return function(node) {
-
var container = document.createElement("DIV"),
nums = document.createElement("DIV"),
scroller = document.createElement("DIV");
-
+ container.style.position = "relative";
nums.style.position = "absolute";
- nums.style.height = "100%";
+ nums.style.height = "100%";
if (nums.style.setExpression) {
try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
}
nums.style.top = "0px";
- nums.style.overflow = "hidden";
-
- container.style.position = "absolute";
- container.style.left = "0px";
- container.style.right = "0px";
- container.style.bottom = "0px";
- container.style.top = "0px";
-
- node.style.position = "absolute";
- node.style.top = "0px";
- node.style.right = "0px";
- node.style.bottom = "0px";
- node.style.left = "16px"
-
+ nums.style.overflow = "hidden";
place(container);
- container.appendChild(node);
+ container.appendChild(node);
container.appendChild(nums);
scroller.className = "CodeMirror-line-numbers";
nums.appendChild(scroller);
function applyLineNumbers(frame) {
var win = frame.contentWindow, doc = win.document,
- nums = frame.parentNode.nextSibling, scroller = nums.firstChild;
+ nums = frame.nextSibling, scroller = nums.firstChild;
var nextNum = 1, barWidth = null;
function sizeBar() {
- if (!frame.offsetWidth || !win.Editor) {
- for (var cur = frame; cur.parentNode; cur = cur.parentNode) {
- if (cur != document) {
- clearInterval(sizeInterval);
- return;
- }
- }
+ for (var root = frame; root.parentNode; root = root.parentNode);
+ if (root != document || !win.Editor) {
+ clearInterval(sizeInterval);
+ return;
}
if (nums.offsetWidth != barWidth) {
barWidth = nums.offsetWidth;
- // nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
+ nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
}
}
function update() {
frame.frameBorder = 0;
frame.src = "javascript:false;";
frame.style.border = "0";
- frame.style.width = "100%";
- frame.style.height = "100%";
+ frame.style.width = options.width;
+ frame.style.height = options.height;
// display: block occasionally suppresses some Firefox bugs, so we
// always add it, redundant as it sounds.
frame.style.display = "block";
var node = place;
place = function(n){node.appendChild(n);};
}
-
- var iframe_container = document.createElement("DIV");
- iframe_container.appendChild(frame);
-
- var content_wrapper = document.createElement("DIV");
- content_wrapper.appendChild(iframe_container);
- content_wrapper.style.position = 'relative';
- content_wrapper.className = 'CodeMirror-content-wrapper';
-
- iframe_container.style.position = 'absolute';
- iframe_container.style.top = '0px';
- iframe_container.style.right = '0px';
- iframe_container.style.bottom = '0px';
- iframe_container.style.left = '28px';
-
- if (options.lineNumbers) {
- var nums = document.createElement("DIV"),
- scroller = document.createElement("DIV");
-
- nums.style.position = "absolute";
- nums.style.height = "100%";
-
- nums.style.top = "0px";
- nums.style.left = "0px";
- nums.style.overflow = "hidden";
-
- scroller.className = "CodeMirror-line-numbers";
- nums.appendChild(scroller);
- content_wrapper.appendChild(nums);
-
- iframe_container.style.right = nums.width;
- }
-
- place(content_wrapper);
+ if (options.lineNumbers) place = wrapLineNumberDiv(place);
+ place(frame);
// Link back to this object, so that the editor can fetch options
// and add a reference to itself.
getCode: function() {return this.editor.getCode();},
setCode: function(code) {this.editor.importCode(code);},
- selection: function() {return this.editor.selectedText();},
+ selection: function() {this.focusIfIE(); return this.editor.selectedText();},
reindent: function() {this.editor.reindent();},
- reindentSelection: function() {this.editor.reindentSelection(null);},
+ reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
+ focusIfIE: function() {
+ // in IE, a lot of selection-related functionality only works when the frame is focused
+ if (this.win.select.ie_selection) this.focus();
+ },
focus: function() {
this.win.focus();
if (this.editor.selectionSnapshot) // IE hack
setParser: function(name) {this.editor.setParser(name);},
- cursorPosition: function(start) {
- if (this.win.select.ie_selection) this.focus();
- return this.editor.cursorPosition(start);
- },
+ cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
firstLine: function() {return this.editor.firstLine();},
lastLine: function() {return this.editor.lastLine();},
nextLine: function(line) {return this.editor.nextLine(line);},
if (text.length) leaving = false;
result.push(node);
}
- else if (node.nodeName == "BR" && node.childNodes.length == 0) {
+ else if (isBR(node) && node.childNodes.length == 0) {
leaving = true;
result.push(node);
}
else {
forEach(node.childNodes, simplifyNode);
- if (!leaving && newlineElements.hasOwnProperty(node.nodeName)) {
+ if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
leaving = true;
if (!atEnd || !top)
result.push(doc.createElement("BR"));
nodeQueue.push(node);
return yield(node.currentText, c);
}
- else if (node.nodeName == "BR") {
+ else if (isBR(node)) {
nodeQueue.push(node);
return yield("\n", c);
}
// Determine the text size of a processed node.
function nodeSize(node) {
- if (node.nodeName == "BR")
- return 1;
- else
- return node.currentText.length;
+ return isBR(node) ? 1 : node.currentText.length;
}
// Search backwards through the top-level nodes until the next BR or
// the start of the frame.
function startOfLine(node) {
- while (node && node.nodeName != "BR") node = node.previousSibling;
+ while (node && !isBR(node)) node = node.previousSibling;
return node;
}
function endOfLine(node, container) {
if (!node) node = container.firstChild;
- else if (node.nodeName == "BR") node = node.nextSibling;
+ else if (isBR(node)) node = node.nextSibling;
- while (node && node.nodeName != "BR") node = node.nextSibling;
+ while (node && !isBR(node)) node = node.nextSibling;
return node;
}
function time() {return new Date().getTime();}
- // Replace all DOM nodes in the current selection with new ones.
- // Needed to prevent issues in IE where the old DOM nodes can be
- // pasted back into the document, still holding their old undo
- // information.
- function scrubPasted(container, start, start2) {
- var end = select.selectionTopNode(container, true),
- doc = container.ownerDocument;
- if (start != null && start.parentNode != container) start = start2;
- if (start === false) start = null;
- if (start == end || !end || !container.firstChild) return;
-
- var clear = traverseDOM(start ? start.nextSibling : container.firstChild);
- while (end.parentNode == container) try{clear.next();}catch(e){break;}
- forEach(clear.nodes, function(node) {
- var newNode = node.nodeName == "BR" ? doc.createElement("BR") : makePartSpan(node.currentText, doc);
- container.replaceChild(newNode, node);
- });
- }
-
// Client interface for searching the content of the editor. Create
// these by calling CodeMirror.getSearchCursor. To use, call
// findNext on the resulting object -- this returns a boolean
this.dirty = [];
if (options.content)
this.importCode(options.content);
- else // FF acts weird when the editable document is completely empty
- container.appendChild(this.doc.createElement("BR"));
if (!options.readOnly) {
if (options.continuousScanning !== false) {
function cursorActivity() {self.cursorActivity(false);}
addEventHandler(document.body, "mouseup", cursorActivity);
+ addEventHandler(document.body, "cut", cursorActivity);
+
addEventHandler(document.body, "paste", function(event) {
cursorActivity();
- if (internetExplorer) {
- var text = null;
- try {text = window.clipboardData.getData("Text");}catch(e){}
- if (text != null) {
- self.replaceSelection(text);
- event.stop();
- }
- else {
- var start = select.selectionTopNode(self.container, true),
- start2 = start && start.previousSibling;
- setTimeout(function(){scrubPasted(self.container, start, start2);}, 0);
- }
+ var text = null;
+ try {
+ var clipboardData = event.clipboardData || window.clipboardData;
+ if (clipboardData) text = clipboardData.getData('Text');
+ }
+ catch(e) {}
+ if (text !== null) {
+ self.replaceSelection(text);
+ event.stop();
}
});
- addEventHandler(document.body, "cut", cursorActivity);
+
+ addEventHandler(document.body, "beforepaste", method(this, "reroutePasteEvent"));
if (this.options.autoMatchParens)
addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
this.checkLine(line);
var accum = [];
for (line = line ? line.nextSibling : this.container.firstChild;
- line && line.nodeName != "BR"; line = line.nextSibling)
+ line && !isBR(line); line = line.nextSibling)
accum.push(nodeText(line));
return cleanText(accum.join(""));
},
before = cur;
break;
}
- var text = (cur.innerText || cur.textContent || cur.nodeValue || "");
+ var text = nodeText(cur);
if (text.length > position) {
before = cur.nextSibling;
content = text.slice(0, position) + content + text.slice(position);
// Replace the selection with another piece of text.
replaceSelection: function(text) {
this.history.commit();
+
var start = select.cursorPos(this.container, true),
end = select.cursorPos(this.container, false);
if (!start || !end) return;
end = this.replaceRange(start, end, text);
- select.setCursorPos(this.container, start, end);
+ select.setCursorPos(this.container, end);
+ webkitLastLineHack(this.container);
+ },
+
+ reroutePasteEvent: function() {
+ if (this.capturingPaste || window.opera) return;
+ this.capturingPaste = true;
+ var te = parent.document.createElement("TEXTAREA");
+ te.style.position = "absolute";
+ te.style.left = "-500px";
+ te.style.width = "10px";
+ te.style.top = nodeTop(frameElement) + "px";
+ parent.document.body.appendChild(te);
+ parent.focus();
+ te.focus();
+
+ var self = this;
+ this.parent.setTimeout(function() {
+ self.capturingPaste = false;
+ self.win.focus();
+ if (self.selectionSnapshot) // IE hack
+ self.win.select.selectCoords(self.win, self.selectionSnapshot);
+ var text = te.value;
+ if (text) self.replaceSelection(text);
+ removeElement(te);
+ }, 10);
},
replaceRange: function(from, to, text) {
// Intercept enter and tab, and assign their new functions.
keyDown: function(event) {
if (this.frozen == "leave") this.frozen = null;
- if (this.frozen && (!this.keyFilter || this.keyFilter(event))) {
+ if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) {
event.stop();
this.frozen(event);
return;
if (this.options.autoMatchParens)
this.scheduleParenBlink();
- // The variouschecks for !altKey are there because AltGr sets both
+ // The various checks for !altKey are there because AltGr sets both
// ctrlKey and altKey to true, and should not be recognised as
// Control.
if (code == 13) { // enter
// keydown event does not prevent the associated keypress event
// from happening, so we have to cancel enter and tab again
// here.
- if ((this.frozen && (!this.keyFilter || this.keyFilter(event))) ||
+ if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) ||
event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
(event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default"))
event.stop();
else if (electric && electric.indexOf(event.character) != -1)
this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
+ else if ((event.character == "v" || event.character == "V")
+ && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
+ this.reroutePasteEvent();
},
// Mark the node at the cursor dirty when a non-safe key is
home: function() {
var cur = select.selectionTopNode(this.container, true), start = cur;
- if (cur === false || !(!cur || cur.isPart || cur.nodeName == "BR") || !this.container.firstChild)
+ if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
return false;
- while (cur && cur.nodeName != "BR") cur = cur.previousSibling;
+ while (cur && !isBR(cur)) cur = cur.previousSibling;
var next = cur ? cur.nextSibling : this.container.firstChild;
if (next && next != start && next.isPart && hasClass(next, "whitespace"))
select.focusAfterNode(next, this.container);
function tryFindMatch() {
var stack = [], ch, ok = true;;
for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
- if (runner.className == className && runner.nodeName == "SPAN" && (ch = paren(runner))) {
+ if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
if (forward(ch) == dir)
stack.push(ch);
else if (!stack.length)
ok = false;
if (!stack.length) break;
}
- else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") {
+ else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
return {node: runner, status: "dirty"};
}
}
// selection.
indentRegion: function(start, end, direction) {
var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
- if (end.nodeName != "BR") end = endOfLine(end, this.container);
+ if (!isBR(end)) end = endOfLine(end, this.container);
do {
var next = endOfLine(current, this.container);
if (!this.options.readOnly) select.markSelection(this.win);
var start, endTime = force ? null : time() + this.options.passTime;
- while (time() < endTime && (start = this.getDirtyNode())) {
+ while ((time() < endTime || force) && (start = this.getDirtyNode())) {
var result = this.highlight(start, endTime);
if (result && result.node && result.dirty)
this.addDirtyNode(result.node);
// Backtrack to the first node before from that has a partial
// parse stored.
while (from && (!from.parserFromHere || from.dirty)) {
- if (maxBacktrack != null && from.nodeName == "BR" && (--maxBacktrack) < 0)
+ if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
return false;
from = from.previousSibling;
}
function maybeTouch(node) {
if (node) {
- if (lineDirty || node.nextSibling != node.oldNextSibling)
+ var old = node.oldNextSibling;
+ if (lineDirty || old === undefined || node.nextSibling != old)
self.history.touch(node);
node.oldNextSibling = node.nextSibling;
}
else {
- if (lineDirty || self.container.firstChild != self.container.oldFirstChild)
- self.history.touch(node);
+ var old = self.container.oldFirstChild;
+ if (lineDirty || old === undefined || self.container.firstChild != old)
+ self.history.touch(null);
self.container.oldFirstChild = self.container.firstChild;
}
}
// Allow empty nodes when they are alone on a line, needed
// for the FF cursor bug workaround (see select.js,
// insertNewlineAtCursor).
- while (part && part.nodeName == "SPAN" && part.currentText == "") {
+ while (part && isSpan(part) && part.currentText == "") {
var old = part;
this.remove();
part = this.get();
if (token.value == "\n"){
// The idea of the two streams actually staying synchronized
// is such a long shot that we explicitly check.
- if (part.nodeName != "BR")
+ if (!isBR(part))
throw "Parser out of sync. Expected BR.";
if (part.dirty || !part.indentation) lineDirty = true;
parts.next();
}
else {
- if (part.nodeName != "SPAN")
+ if (!isSpan(part))
throw "Parser out of sync. Expected SPAN.";
if (part.dirty)
lineDirty = true;
while (pos && pos.offsetParent) {
y += pos.offsetTop;
// Don't count X offset for <br> nodes
- if (pos.nodeName != "BR")
+ if (!isBR(pos))
x += pos.offsetLeft;
pos = pos.offsetParent;
}
// Move the start of a range to the start of a node,
// compensating for the fact that you can't call
// moveToElementText with text nodes.
- function moveToNodeStart(range, node) {
+ function moveToNodeStart(range, node) {
if (node.nodeType == 3) {
var count = 0, cur = node.previousSibling;
while (cur && cur.nodeType == 3) {
}
if (cur) {
try{range.moveToElementText(cur);}
- catch(e){alert(cur + " " + cur.nodeType + " " + (cur && cur.outerHTML));}
+ catch(e){return false;}
range.collapse(false);
}
else range.moveToElementText(node.parentNode);
if (count) range.move("character", count);
}
- else try{range.moveToElementText(node);} catch(e) {};
+ else {
+ try{range.moveToElementText(node);}
+ catch(e){return false;}
+ }
+ return true;
}
// Do a binary search through the container object, comparing
// the start of each node to the selection
- var start = 0, end = container.childNodes.length;
- while (start != end) {
+ var start = 0, end = container.childNodes.length - 1;
+ while (start < end) {
var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
if (!node) return false; // Don't ask. IE6 manages this sometimes.
- moveToNodeStart(range2, node);
+ if (!moveToNodeStart(range2, node)) return false;
if (range.compareEndPoints("StartToStart", range2) == 1)
start = middle;
else
if (!selection) return null;
var topNode = select.selectionTopNode(container, start);
- while (topNode && topNode.nodeName != "BR")
+ while (topNode && !isBR(topNode))
topNode = topNode.previousSibling;
var range = selection.createRange(), range2 = range.duplicate();
// ancestors with a suitable offset. This goes down the DOM tree
// until a 'leaf' is reached (or is it *up* the DOM tree?).
function normalize(point){
- while (point.node.nodeType != 3 && point.node.nodeName != "BR") {
+ while (point.node.nodeType != 3 && !isBR(point.node)) {
var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
point.offset = 0;
while (!newNode && point.node.parentNode) {
};
select.selectMarked = function () {
- if (!currentSelection || !currentSelection.changed) return;
- var win = currentSelection.window, range = win.document.createRange();
+ var cs = currentSelection;
+ if (!(cs && (cs.changed || (webkit && cs.start.node == cs.end.node)))) return;
+ var win = cs.window, range = win.document.createRange();
function setPoint(point, which) {
if (point.node) {
}
}
- setPoint(currentSelection.end, "End");
- setPoint(currentSelection.start, "Start");
+ setPoint(cs.end, "End");
+ setPoint(cs.start, "Start");
selectRange(range, win);
};
var offset = start ? range.startOffset : range.endOffset;
// Work around (yet another) bug in Opera's selection model.
if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
- container.childNodes[range.startOffset] && container.childNodes[range.startOffset].nodeName == "BR")
+ container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
offset--;
// For text nodes, we look at the node itself if the cursor is
// Occasionally, browsers will return the HTML node as
// selection. If the offset is 0, we take the start of the frame
// ('after null'), otherwise, we take the last node.
- else if (node.nodeName == "HTML") {
+ else if (node.nodeName.toUpperCase() == "HTML") {
return (offset == 1 ? null : container.lastChild);
}
// If the given node is our 'container', we just look up the
if (!range) return;
var topNode = select.selectionTopNode(container, start);
- while (topNode && topNode.nodeName != "BR")
+ while (topNode && !isBR(topNode))
topNode = topNode.previousSibling;
range = range.cloneRange();
* An easy mistake to make is to let a StopIteration exception finish
* the token stream while there are still characters pending in the
* string stream (hitting the end of the buffer while parsing a
- * token). To make it easier to detect such errors, the strings throw
- * an exception when this happens.
+ * token). To make it easier to detect such errors, the stringstreams
+ * throw an exception when this happens.
*/
-// Make a string stream out of an iterator that returns strings. This
-// is applied to the result of traverseDOM (see codemirror.js), and
-// the resulting stream is fed to the parser.
+// Make a stringstream stream out of an iterator that returns strings.
+// This is applied to the result of traverseDOM (see codemirror.js),
+// and the resulting stream is fed to the parser.
window.stringStream = function(source){
// String that's currently being iterated over.
var current = "";
// Commit unless there are pending dirty nodes.
tryCommit: function() {
if (!window.History) return; // Stop when frame has been unloaded
- if (this.editor.highlightDirty()) this.commit();
+ if (this.editor.highlightDirty()) this.commit(true);
else this.scheduleCommit();
},
function buildLine(node) {
var text = [];
for (var cur = node ? node.nextSibling : self.container.firstChild;
- cur && cur.nodeName != "BR"; cur = cur.nextSibling)
+ cur && !isBR(cur); cur = cur.nextSibling)
if (cur.currentText) text.push(cur.currentText);
return {from: node, to: cur, text: cleanText(text.join(""))};
}
// Get the BR element after/before the given node.
function nextBR(node, dir) {
var link = dir + "Sibling", search = node[link];
- while (search && search.nodeName != "BR")
+ while (search && !isBR(search))
search = search[link];
return search;
}
function nodeText(node) {
return node.textContent || node.innerText || node.nodeValue || "";
}
+
+function nodeTop(node) {
+ var top = 0;
+ while (node.offsetParent) {
+ top += node.offsetTop;
+ node = node.offsetParent;
+ }
+ return top;
+}
+
+function isBR(node) {
+ var nn = node.nodeName;
+ return nn == "BR" || nn == "br";
+}
+function isSpan(node) {
+ var nn = node.nodeName;
+ return nn == "SPAN" || nn == "span";
+}
</script>
{% endblock extrahead %}
-{% block extrabody %}
- <script type="text/javascript" charset="utf-8">
- $(function() {
- {% for scriptlet in scriptlets %}
- editor.registerScriptlet("{{scriptlet.name}}",
- function(editor, panel, params) {
- {{ scriptlet.code|safe }}
- });
- {% endfor %}
- });
- </script>
-{% endblock extrabody %}
-
-{% load explorer_tags %}
-
-{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> > {{ fileid|bookname }}{% endblock breadcrumbs %}
-
-{% block header-toolbar %}
-<button type="button" class="requires-save toolbar-button" id="toolbar-button-commit">Merge</button>
-<button type="button" class="requires-save toolbar-button" id="toolbar-button-update"
- ui:ajax-action="{% url file_update fileid %}">Update</button>
-<button type="button" class="provides-save toolbar-button" id="toolbar-button-save" disabled="disabled">Commit</button>
-<button type="button" class="provides-save toolbar-button" id="toolbar-button-quick-save" disabled="disabled">Quick Save</button>
-{% endblock %}
+{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> > {{ fileid }}{% endblock breadcrumbs %}
{% block message-box %}
<div class="msg-info" id="save-waiting"><p>ZapisujÄ™ dane na serwerze.</p><p class="data"></p></div>