From b264c171a5885976ef8f077be37e7cc83bf8a0fd Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Rekucki?= Date: Mon, 5 Oct 2009 12:56:58 +0200 Subject: [PATCH] Reverted CM to version 0.63 with 2 patches: * pass anb event to filter instead of just the key code * check text-value properly --- apps/explorer/context_processors.py | 4 +- .../static/js/lib/codemirror/codemirror.js | 25 +- project/static/js/lib/codemirror/editor.js | 43 ++-- project/static/js/lib/codemirror/select.js | 31 ++- project/static/js/lib/codemirror/undo.js | 4 +- project/static/js/lib/codemirror/util.js | 9 - project/static/js/views/button_toolbar.js | 37 +-- project/static/js/views/xml.js | 218 ++++++++++++------ project/templates/explorer/editor.html | 7 +- project/templates/html4print.html | 13 ++ 10 files changed, 240 insertions(+), 151 deletions(-) create mode 100644 project/templates/html4print.html diff --git a/apps/explorer/context_processors.py b/apps/explorer/context_processors.py index 634b0155..eff3032c 100755 --- a/apps/explorer/context_processors.py +++ b/apps/explorer/context_processors.py @@ -4,6 +4,8 @@ __date__ ="$2009-09-03 08:34:10$" def settings(request): from django.conf import settings - return {'MEDIA_URL': settings.MEDIA_URL, 'STATIC_URL': settings.STATIC_URL} + return {'MEDIA_URL': settings.MEDIA_URL, + 'STATIC_URL': settings.STATIC_URL, + 'REDMINE_URL': settings.REDMINE_URL } diff --git a/project/static/js/lib/codemirror/codemirror.js b/project/static/js/lib/codemirror/codemirror.js index f63ed07e..97e2657b 100644 --- a/project/static/js/lib/codemirror/codemirror.js +++ b/project/static/js/lib/codemirror/codemirror.js @@ -81,10 +81,13 @@ var CodeMirror = (function(){ var nextNum = 1, barWidth = null; function sizeBar() { - for (var root = frame; root.parentNode; root = root.parentNode); - if (root != document || !win.Editor) { - clearInterval(sizeInterval); - return; + if (!frame.offsetWidth || !win.Editor) { + for (var cur = frame; cur.parentNode; cur = cur.parentNode) { + if (cur != document) { + clearInterval(sizeInterval); + return; + } + } } if (nums.offsetWidth != barWidth) { @@ -132,7 +135,6 @@ var CodeMirror = (function(){ var node = place; place = function(n){node.appendChild(n);}; } - if (options.lineNumbers) place = wrapLineNumberDiv(place); place(frame); @@ -173,14 +175,10 @@ var CodeMirror = (function(){ getCode: function() {return this.editor.getCode();}, setCode: function(code) {this.editor.importCode(code);}, - selection: function() {this.focusIfIE(); return this.editor.selectedText();}, + selection: function() {return this.editor.selectedText();}, reindent: function() {this.editor.reindent();}, - reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);}, + reindentSelection: function() {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 @@ -208,7 +206,10 @@ var CodeMirror = (function(){ setParser: function(name) {this.editor.setParser(name);}, - cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);}, + cursorPosition: function(start) { + if (this.win.select.ie_selection) this.focus(); + 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);}, diff --git a/project/static/js/lib/codemirror/editor.js b/project/static/js/lib/codemirror/editor.js index b7c53c70..0f7a8af0 100644 --- a/project/static/js/lib/codemirror/editor.js +++ b/project/static/js/lib/codemirror/editor.js @@ -80,13 +80,13 @@ var Editor = (function(){ if (text.length) leaving = false; result.push(node); } - else if (isBR(node) && node.childNodes.length == 0) { + else if (node.nodeName == "BR" && node.childNodes.length == 0) { leaving = true; result.push(node); } else { forEach(node.childNodes, simplifyNode); - if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) { + if (!leaving && newlineElements.hasOwnProperty(node.nodeName)) { leaving = true; if (!atEnd || !top) result.push(doc.createElement("BR")); @@ -175,7 +175,7 @@ var Editor = (function(){ nodeQueue.push(node); return yield(node.currentText, c); } - else if (isBR(node)) { + else if (node.nodeName == "BR") { nodeQueue.push(node); return yield("\n", c); } @@ -195,20 +195,23 @@ var Editor = (function(){ // Determine the text size of a processed node. function nodeSize(node) { - return isBR(node) ? 1 : node.currentText.length; + if (node.nodeName == "BR") + return 1; + else + return 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 && !isBR(node)) node = node.previousSibling; + while (node && node.nodeName != "BR") node = node.previousSibling; return node; } function endOfLine(node, container) { if (!node) node = container.firstChild; - else if (isBR(node)) node = node.nextSibling; + else if (node.nodeName == "BR") node = node.nextSibling; - while (node && !isBR(node)) node = node.nextSibling; + while (node && node.nodeName != "BR") node = node.nextSibling; return node; } @@ -363,6 +366,8 @@ var Editor = (function(){ 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) { @@ -501,7 +506,7 @@ var Editor = (function(){ this.checkLine(line); var accum = []; for (line = line ? line.nextSibling : this.container.firstChild; - line && !isBR(line); line = line.nextSibling) + line && line.nodeName != "BR"; line = line.nextSibling) accum.push(nodeText(line)); return cleanText(accum.join("")); }, @@ -526,7 +531,7 @@ var Editor = (function(){ before = cur; break; } - var text = nodeText(cur); + var text = (cur.innerText || cur.textContent || cur.nodeValue || ""); if (text.length > position) { before = cur.nextSibling; content = text.slice(0, position) + content + text.slice(position); @@ -658,7 +663,7 @@ var Editor = (function(){ // 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.keyCode))) { + if (this.frozen && (!this.keyFilter || this.keyFilter(event)) ) { event.stop(); this.frozen(event); return; @@ -838,10 +843,10 @@ var Editor = (function(){ home: function() { var cur = select.selectionTopNode(this.container, true), start = cur; - if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild) + if (cur === false || !(!cur || cur.isPart || cur.nodeName == "BR") || !this.container.firstChild) return false; - while (cur && !isBR(cur)) cur = cur.previousSibling; + while (cur && cur.nodeName != "BR") 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); @@ -896,7 +901,7 @@ var Editor = (function(){ function tryFindMatch() { var stack = [], ch, ok = true;; for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) { - if (runner.className == className && isSpan(runner) && (ch = paren(runner))) { + if (runner.className == className && runner.nodeName == "SPAN" && (ch = paren(runner))) { if (forward(ch) == dir) stack.push(ch); else if (!stack.length) @@ -905,7 +910,7 @@ var Editor = (function(){ ok = false; if (!stack.length) break; } - else if (runner.dirty || !isSpan(runner) && !isBR(runner)) { + else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") { return {node: runner, status: "dirty"}; } } @@ -963,7 +968,7 @@ var Editor = (function(){ // selection. indentRegion: function(start, end, direction) { var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling); - if (!isBR(end)) end = endOfLine(end, this.container); + if (end.nodeName != "BR") end = endOfLine(end, this.container); do { var next = endOfLine(current, this.container); @@ -1122,7 +1127,7 @@ var Editor = (function(){ // Backtrack to the first node before from that has a partial // parse stored. while (from && (!from.parserFromHere || from.dirty)) { - if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0) + if (maxBacktrack != null && from.nodeName == "BR" && (--maxBacktrack) < 0) return false; from = from.previousSibling; } @@ -1203,7 +1208,7 @@ var Editor = (function(){ // Allow empty nodes when they are alone on a line, needed // for the FF cursor bug workaround (see select.js, // insertNewlineAtCursor). - while (part && isSpan(part) && part.currentText == "") { + while (part && part.nodeName == "SPAN" && part.currentText == "") { var old = part; this.remove(); part = this.get(); @@ -1225,7 +1230,7 @@ var Editor = (function(){ if (token.value == "\n"){ // The idea of the two streams actually staying synchronized // is such a long shot that we explicitly check. - if (!isBR(part)) + if (part.nodeName != "BR") throw "Parser out of sync. Expected BR."; if (part.dirty || !part.indentation) lineDirty = true; @@ -1253,7 +1258,7 @@ var Editor = (function(){ parts.next(); } else { - if (!isSpan(part)) + if (part.nodeName != "SPAN") throw "Parser out of sync. Expected SPAN."; if (part.dirty) lineDirty = true; diff --git a/project/static/js/lib/codemirror/select.js b/project/static/js/lib/codemirror/select.js index 7746240e..d513ba5f 100644 --- a/project/static/js/lib/codemirror/select.js +++ b/project/static/js/lib/codemirror/select.js @@ -52,7 +52,7 @@ var select = {}; while (pos && pos.offsetParent) { y += pos.offsetTop; // Don't count X offset for
nodes - if (!isBR(pos)) + if (pos.nodeName != "BR") x += pos.offsetLeft; pos = pos.offsetParent; } @@ -247,17 +247,13 @@ var select = {}; } if (cur) { try{range.moveToElementText(cur);} - catch(e){return false;} + catch(e){} range.collapse(false); } else range.moveToElementText(node.parentNode); if (count) range.move("character", count); } - else { - try{range.moveToElementText(node);} - catch(e){return false;} - } - return true; + else range.moveToElementText(node); } // Do a binary search through the container object, comparing @@ -266,7 +262,7 @@ var select = {}; 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. - if (!moveToNodeStart(range2, node)) return false; + moveToNodeStart(range2, node); if (range.compareEndPoints("StartToStart", range2) == 1) start = middle; else @@ -318,7 +314,7 @@ var select = {}; if (!selection) return null; var topNode = select.selectionTopNode(container, start); - while (topNode && !isBR(topNode)) + while (topNode && topNode.nodeName != "BR") topNode = topNode.previousSibling; var range = selection.createRange(), range2 = range.duplicate(); @@ -411,7 +407,7 @@ var select = {}; // 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 && !isBR(point.node)) { + while (point.node.nodeType != 3 && point.node.nodeName != "BR") { var newNode = point.node.childNodes[point.offset] || point.node.nextSibling; point.offset = 0; while (!newNode && point.node.parentNode) { @@ -429,9 +425,8 @@ var select = {}; }; select.selectMarked = function () { - var cs = currentSelection; - if (!(cs && (cs.changed || (webkit && cs.start.node == cs.end.node)))) return; - var win = cs.window, range = win.document.createRange(); + if (!currentSelection || !currentSelection.changed) return; + var win = currentSelection.window, range = win.document.createRange(); function setPoint(point, which) { if (point.node) { @@ -447,8 +442,8 @@ var select = {}; } } - setPoint(cs.end, "End"); - setPoint(cs.start, "Start"); + setPoint(currentSelection.end, "End"); + setPoint(currentSelection.start, "Start"); selectRange(range, win); }; @@ -476,7 +471,7 @@ var select = {}; 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] && isBR(container.childNodes[range.startOffset])) + container.childNodes[range.startOffset] && container.childNodes[range.startOffset].nodeName == "BR") offset--; // For text nodes, we look at the node itself if the cursor is @@ -491,7 +486,7 @@ var select = {}; // 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.toUpperCase() == "HTML") { + else if (node.nodeName == "HTML") { return (offset == 1 ? null : container.lastChild); } // If the given node is our 'container', we just look up the @@ -562,7 +557,7 @@ var select = {}; if (!range) return; var topNode = select.selectionTopNode(container, start); - while (topNode && !isBR(topNode)) + while (topNode && topNode.nodeName != "BR") topNode = topNode.previousSibling; range = range.cloneRange(); diff --git a/project/static/js/lib/codemirror/undo.js b/project/static/js/lib/codemirror/undo.js index 97daf593..5f717a9e 100644 --- a/project/static/js/lib/codemirror/undo.js +++ b/project/static/js/lib/codemirror/undo.js @@ -250,7 +250,7 @@ History.prototype = { function buildLine(node) { var text = []; for (var cur = node ? node.nextSibling : self.container.firstChild; - cur && !isBR(cur); cur = cur.nextSibling) + cur && cur.nodeName != "BR"; cur = cur.nextSibling) if (cur.currentText) text.push(cur.currentText); return {from: node, to: cur, text: cleanText(text.join(""))}; } @@ -275,7 +275,7 @@ History.prototype = { // Get the BR element after/before the given node. function nextBR(node, dir) { var link = dir + "Sibling", search = node[link]; - while (search && !isBR(search)) + while (search && search.nodeName != "BR") search = search[link]; return search; } diff --git a/project/static/js/lib/codemirror/util.js b/project/static/js/lib/codemirror/util.js index 0cd91d4e..796025ee 100644 --- a/project/static/js/lib/codemirror/util.js +++ b/project/static/js/lib/codemirror/util.js @@ -123,12 +123,3 @@ function nodeTop(node) { } 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"; -} diff --git a/project/static/js/views/button_toolbar.js b/project/static/js/views/button_toolbar.js index 71bfa975..7e4b6eab 100644 --- a/project/static/js/views/button_toolbar.js +++ b/project/static/js/views/button_toolbar.js @@ -39,23 +39,30 @@ var ButtonToolbarView = View.extend({ $(self.element).trigger('resize'); }); - $('.buttontoolbarview-button', this.element).bind('click.buttontoolbarview', function(event) { - var groupIndex = parseInt($(this).attr('ui:groupindex'), 10); - var buttonIndex = parseInt($(this).attr('ui:buttonindex'), 10); - var button = self.get('buttons')[groupIndex].buttons[buttonIndex]; - var scriptletId = button.scriptlet_id; - var params = eval('(' + button.params + ')'); // To nie powinno być potrzebne - - console.log('Executing', scriptletId, 'with params', params); - try { - scriptletCenter.scriptlets[scriptletId](self.parent, params); - } catch(e) { - console.log("Scriptlet", scriptletId, "failed."); - } - }); - + $('.buttontoolbarview-button', this.element). + bind('click.buttontoolbarview', this.buttonPressed.bind(this) ); + $(this.element).trigger('resize'); }, + + buttonPressed: function(event) + { + var target = event.target; + + var groupIndex = parseInt($(target).attr('ui:groupindex'), 10); + var buttonIndex = parseInt($(target).attr('ui:buttonindex'), 10); + var button = this.get('buttons')[groupIndex].buttons[buttonIndex]; + var scriptletId = button.scriptlet_id; + var params = eval('(' + button.params + ')'); // To nie powinno być potrzebne + + console.log('Executing', scriptletId, 'with params', params); + try { + scriptletCenter.scriptlets[scriptletId](this.parent, params); + } catch(e) { + console.log("Scriptlet", scriptletId, "failed."); + } + + }, dispose: function() { $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview'); diff --git a/project/static/js/views/xml.js b/project/static/js/views/xml.js index caafa715..d24dfa94 100644 --- a/project/static/js/views/xml.js +++ b/project/static/js/views/xml.js @@ -1,92 +1,164 @@ /*global View CodeMirror ButtonToolbarView render_template panels */ var XMLView = View.extend({ - _className: 'XMLView', - element: null, - model: null, - template: 'xml-view-template', - editor: null, - buttonToolbar: null, + _className: 'XMLView', + element: null, + model: null, + template: 'xml-view-template', + editor: null, + buttonToolbar: null, - init: function(element, model, parent, template) { - this._super(element, model, template); - this.parent = parent; - this.buttonToolbar = new ButtonToolbarView( - $('.xmlview-toolbar', this.element), - this.model.toolbarButtonsModel, parent); + init: function(element, model, parent, template) { + this._super(element, model, template); + this.parent = parent; + this.buttonToolbar = new ButtonToolbarView( + $('.xmlview-toolbar', this.element), + this.model.toolbarButtonsModel, parent); - $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this)); + this.hotkeys = []; + var self = this; + + $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this)); + - this.parent.freeze('Ładowanie edytora...'); - this.editor = new CodeMirror($('.xmlview', this.element).get(0), { - parserfile: 'parsexml.js', - path: "/static/js/lib/codemirror/", - stylesheet: "/static/css/xmlcolors.css", - parserConfig: {useHTMLKludges: false}, - textWrapping: false, - tabMode: 'spaces', - indentUnit: 0, - onChange: this.editorDataChanged.bind(this), - initCallback: this.editorDidLoad.bind(this) - }); - }, + this.parent.freeze('Ładowanie edytora...'); + this.editor = new CodeMirror($('.xmlview', this.element).get(0), { + parserfile: 'parsexml.js', + path: "/static/js/lib/codemirror/", + stylesheet: "/static/css/xmlcolors.css", + parserConfig: { + useHTMLKludges: false + }, + textWrapping: false, + tabMode: 'spaces', + indentUnit: 0, + onChange: this.editorDataChanged.bind(this), + initCallback: this.editorDidLoad.bind(this) + }); + }, - resized: function(event) { - var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight(); - $('.xmlview', this.element).height(height); - }, + resized: function(event) { + var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight(); + $('.xmlview', this.element).height(height); + }, - reload: function() { - this.model.load(true); - }, + reload: function() { + this.model.load(true); + }, - editorDidLoad: function(editor) { - $(editor.frame).css({width: '100%', height: '100%'}); - this.model - .addObserver(this, 'data', this.modelDataChanged.bind(this)) - .addObserver(this, 'state', this.modelStateChanged.bind(this)) - .load(); + editorDidLoad: function(editor) { + $(editor.frame).css({ + width: '100%', + height: '100%' + }); + this.model + .addObserver(this, 'data', this.modelDataChanged.bind(this)) + .addObserver(this, 'state', this.modelStateChanged.bind(this)) + .load(); - this.parent.unfreeze(); + this.parent.unfreeze(); - this.editor.setCode(this.model.get('data')); - this.modelStateChanged('state', this.model.get('state')); + this.editor.setCode(this.model.get('data')); + this.modelStateChanged('state', this.model.get('state')); - // editor.grabKeys( - // $.fbind(self, self.hotkeyPressed), - // $.fbind(self, self.isHotkey) - // ); - }, + editor.grabKeys( + this.hotkeyPressed.bind(this), + this.isHotkey.bind(this) + ); + }, - editorDataChanged: function() { - this.model.set('data', this.editor.getCode()); - }, + editorDataChanged: function() { + this.model.set('data', this.editor.getCode()); + }, - modelDataChanged: function(property, value) { - if (this.editor.getCode() != value) { - this.editor.setCode(value); - } - }, + modelDataChanged: function(property, value) { + if (this.editor.getCode() != value) { + this.editor.setCode(value); + } + }, - modelStateChanged: function(property, value) { - if (value == 'synced' || value == 'dirty') { - this.unfreeze(); - } else if (value == 'unsynced') { - this.freeze('Niezsynchronizowany...'); - } else if (value == 'loading') { - this.freeze('Ładowanie...'); - } else if (value == 'saving') { - this.freeze('Zapisywanie...'); - } else if (value == 'error') { - this.freeze(this.model.get('error')); - } - }, + modelStateChanged: function(property, value) { + if (value == 'synced' || value == 'dirty') { + this.unfreeze(); + } else if (value == 'unsynced') { + this.freeze('Niezsynchronizowany...'); + } else if (value == 'loading') { + this.freeze('Ładowanie...'); + } else if (value == 'saving') { + this.freeze('Zapisywanie...'); + } else if (value == 'error') { + this.freeze(this.model.get('error')); + } + }, - dispose: function() { - this.model.removeObserver(this); - $(this.editor.frame).remove(); - this._super(); - } + dispose: function() { + this.model.removeObserver(this); + $(this.editor.frame).remove(); + this._super(); + }, + + getHotkey: function(event) { + var code = event.keyCode; + var ch = String.fromCharCode(code & 0xff).toLowerCase(); + var button = $('.buttontoolbarview-button[title='+ch+']', this.element)[0] + + console.log(ch, '#', button); + var mod = 0; + + if(event.altKey) mod |= 0x01; + if(event.ctrlKey) mod |= 0x02; + if(event.shiftKey) mod |= 0x04; + + if(button) { + var match = null; + + $(button).each(function() { + if( parseInt($(this).attr('ui:hotkey_mod')) == mod ) { + match = this; + return; + } + }) + + return match; + } + else { + return null; + } + }, + + isHotkey: function() { + console.log(arguments); + + if(this.getHotkey.apply(this, arguments)) + return true; + else + return false; + }, + + hotkeyPressed: function() { + var button = this.getHotkey.apply(this, arguments); + this.buttonToolbar.buttonPressed({ + target: button + }); + } + }); +function Hotkey(code) { + this.code = code; + this.has_alt = ((code & 0x01 << 8) !== 0); + this.has_ctrl = ((code & 0x01 << 9) !== 0); + this.has_shift = ((code & 0x01 << 10) !== 0); + this.character = String.fromCharCode(code & 0xff); +} + +Hotkey.prototype.toString = function() { + var mods = []; + if(this.has_alt) mods.push('Alt'); + if(this.has_ctrl) mods.push('Ctrl'); + if(this.has_shift) mods.push('Shift'); + mods.push('"'+this.character+'"'); + return mods.join('+'); +}; + // Register view panels['xml'] = XMLView; diff --git a/project/templates/explorer/editor.html b/project/templates/explorer/editor.html index 7d288d3c..85808f22 100644 --- a/project/templates/explorer/editor.html +++ b/project/templates/explorer/editor.html @@ -122,7 +122,10 @@