From 65048f64d018c105be3921ca4bcb9f5183e2382e Mon Sep 17 00:00:00 2001 From: zuber Date: Fri, 14 Aug 2009 14:05:20 +0200 Subject: [PATCH] Zamiana skryptu edytora z editArea na CodeMirror. --- project/static/css/master.css | 8 +- project/static/css/xmlcolors.css | 51 + project/static/js/codemirror/codemirror.js | 310 ++++ project/static/js/codemirror/editor.js | 1307 +++++++++++++++++ project/static/js/codemirror/parsexml.js | 292 ++++ project/static/js/codemirror/select.js | 619 ++++++++ project/static/js/codemirror/stringstream.js | 140 ++ project/static/js/codemirror/tokenize.js | 57 + project/static/js/codemirror/undo.js | 403 +++++ project/static/js/codemirror/util.js | 116 ++ project/static/js/edit_area.css | 530 ------- project/static/js/edit_area.js | 525 ------- project/static/js/edit_area_compressor.php | 408 ----- project/static/js/edit_area_full.gz | Bin 29125 -> 0 bytes project/static/js/edit_area_full.js | 38 - .../static/js/edit_area_full_with_plugins.gz | Bin 29707 -> 0 bytes .../static/js/edit_area_full_with_plugins.js | 39 - project/static/js/edit_area_functions.js | 1203 --------------- project/static/js/edit_area_loader.js | 1080 -------------- project/static/js/elements_functions.js | 333 ----- project/static/js/highlight.js | 391 ----- project/static/js/images/autocompletion.gif | Bin 359 -> 0 bytes project/static/js/images/close.gif | Bin 102 -> 0 bytes project/static/js/images/fullscreen.gif | Bin 198 -> 0 bytes project/static/js/images/go_to_line.gif | Bin 1053 -> 0 bytes project/static/js/images/help.gif | Bin 295 -> 0 bytes project/static/js/images/highlight.gif | Bin 256 -> 0 bytes project/static/js/images/load.gif | Bin 1041 -> 0 bytes project/static/js/images/move.gif | Bin 257 -> 0 bytes project/static/js/images/newdocument.gif | Bin 170 -> 0 bytes project/static/js/images/opacity.png | Bin 147 -> 0 bytes project/static/js/images/processing.gif | Bin 825 -> 0 bytes project/static/js/images/redo.gif | Bin 169 -> 0 bytes project/static/js/images/reset_highlight.gif | Bin 168 -> 0 bytes project/static/js/images/save.gif | Bin 285 -> 0 bytes project/static/js/images/search.gif | Bin 191 -> 0 bytes project/static/js/images/smooth_selection.gif | Bin 174 -> 0 bytes project/static/js/images/spacer.gif | Bin 43 -> 0 bytes project/static/js/images/statusbar_resize.gif | Bin 79 -> 0 bytes project/static/js/images/undo.gif | Bin 175 -> 0 bytes project/static/js/images/word_wrap.gif | Bin 951 -> 0 bytes project/static/js/keyboard.js | 145 -- project/static/js/langs/bg.js | 73 - project/static/js/langs/cs.js | 67 - project/static/js/langs/de.js | 67 - project/static/js/langs/dk.js | 67 - project/static/js/langs/en.js | 67 - project/static/js/langs/eo.js | 67 - project/static/js/langs/es.js | 64 - project/static/js/langs/fi.js | 67 - project/static/js/langs/fr.js | 67 - project/static/js/langs/hr.js | 67 - project/static/js/langs/it.js | 67 - project/static/js/langs/ja.js | 67 - project/static/js/langs/mk.js | 67 - project/static/js/langs/nl.js | 67 - project/static/js/langs/pl.js | 67 - project/static/js/langs/pt.js | 67 - project/static/js/langs/ru.js | 67 - project/static/js/langs/sk.js | 67 - project/static/js/langs/zh.js | 67 - project/static/js/license_apache.txt | 7 - project/static/js/license_bsd.txt | 10 - project/static/js/license_lgpl.txt | 458 ------ project/static/js/manage_area.js | 623 -------- project/static/js/plugins/charmap/charmap.js | 90 -- .../static/js/plugins/charmap/css/charmap.css | 64 - .../js/plugins/charmap/images/charmap.gif | Bin 245 -> 0 bytes .../static/js/plugins/charmap/jscripts/map.js | 373 ----- project/static/js/plugins/charmap/langs/bg.js | 12 - project/static/js/plugins/charmap/langs/cs.js | 6 - project/static/js/plugins/charmap/langs/de.js | 6 - project/static/js/plugins/charmap/langs/dk.js | 6 - project/static/js/plugins/charmap/langs/en.js | 6 - project/static/js/plugins/charmap/langs/eo.js | 6 - project/static/js/plugins/charmap/langs/es.js | 6 - project/static/js/plugins/charmap/langs/fr.js | 6 - project/static/js/plugins/charmap/langs/hr.js | 6 - project/static/js/plugins/charmap/langs/it.js | 6 - project/static/js/plugins/charmap/langs/ja.js | 6 - project/static/js/plugins/charmap/langs/mk.js | 6 - project/static/js/plugins/charmap/langs/nl.js | 6 - project/static/js/plugins/charmap/langs/pl.js | 6 - project/static/js/plugins/charmap/langs/pt.js | 6 - project/static/js/plugins/charmap/langs/ru.js | 6 - project/static/js/plugins/charmap/langs/sk.js | 6 - project/static/js/plugins/charmap/langs/zh.js | 6 - project/static/js/plugins/charmap/popup.html | 24 - project/static/js/plugins/test/css/test.css | 3 - .../static/js/plugins/test/images/Thumbs.db | Bin 3584 -> 0 bytes .../static/js/plugins/test/images/test.gif | Bin 87 -> 0 bytes project/static/js/plugins/test/langs/bg.js | 10 - project/static/js/plugins/test/langs/cs.js | 4 - project/static/js/plugins/test/langs/de.js | 4 - project/static/js/plugins/test/langs/dk.js | 4 - project/static/js/plugins/test/langs/en.js | 4 - project/static/js/plugins/test/langs/eo.js | 4 - project/static/js/plugins/test/langs/es.js | 4 - project/static/js/plugins/test/langs/fr.js | 4 - project/static/js/plugins/test/langs/hr.js | 4 - project/static/js/plugins/test/langs/it.js | 4 - project/static/js/plugins/test/langs/ja.js | 4 - project/static/js/plugins/test/langs/mk.js | 4 - project/static/js/plugins/test/langs/nl.js | 4 - project/static/js/plugins/test/langs/pl.js | 4 - project/static/js/plugins/test/langs/pt.js | 4 - project/static/js/plugins/test/langs/ru.js | 4 - project/static/js/plugins/test/langs/sk.js | 4 - project/static/js/plugins/test/langs/zh.js | 4 - project/static/js/plugins/test/test.js | 110 -- project/static/js/plugins/test/test2.js | 1 - project/static/js/reg_syntax.js | 166 --- project/static/js/reg_syntax/basic.js | 69 - project/static/js/reg_syntax/brainfuck.js | 44 - project/static/js/reg_syntax/c.js | 62 - project/static/js/reg_syntax/coldfusion.js | 119 -- project/static/js/reg_syntax/cpp.js | 65 - project/static/js/reg_syntax/css.js | 84 -- project/static/js/reg_syntax/html.js | 50 - project/static/js/reg_syntax/java.js | 56 - project/static/js/reg_syntax/js.js | 93 -- project/static/js/reg_syntax/pas.js | 82 -- project/static/js/reg_syntax/perl.js | 88 -- project/static/js/reg_syntax/php.js | 156 -- project/static/js/reg_syntax/python.js | 144 -- project/static/js/reg_syntax/robotstxt.js | 24 - project/static/js/reg_syntax/ruby.js | 67 - project/static/js/reg_syntax/sql.js | 55 - project/static/js/reg_syntax/tsql.js | 87 -- project/static/js/reg_syntax/vb.js | 52 - project/static/js/reg_syntax/xml.js | 56 - project/static/js/regexp.js | 139 -- project/static/js/resize_area.js | 73 - project/static/js/search_replace.js | 174 --- project/static/js/template.html | 100 -- project/templates/explorer/file_xml.html | 24 +- 136 files changed, 3310 insertions(+), 10045 deletions(-) create mode 100644 project/static/css/xmlcolors.css create mode 100644 project/static/js/codemirror/codemirror.js create mode 100644 project/static/js/codemirror/editor.js create mode 100644 project/static/js/codemirror/parsexml.js create mode 100644 project/static/js/codemirror/select.js create mode 100644 project/static/js/codemirror/stringstream.js create mode 100644 project/static/js/codemirror/tokenize.js create mode 100644 project/static/js/codemirror/undo.js create mode 100644 project/static/js/codemirror/util.js delete mode 100755 project/static/js/edit_area.css delete mode 100755 project/static/js/edit_area.js delete mode 100755 project/static/js/edit_area_compressor.php delete mode 100755 project/static/js/edit_area_full.gz delete mode 100755 project/static/js/edit_area_full.js delete mode 100755 project/static/js/edit_area_full_with_plugins.gz delete mode 100755 project/static/js/edit_area_full_with_plugins.js delete mode 100755 project/static/js/edit_area_functions.js delete mode 100755 project/static/js/edit_area_loader.js delete mode 100755 project/static/js/elements_functions.js delete mode 100755 project/static/js/highlight.js delete mode 100755 project/static/js/images/autocompletion.gif delete mode 100755 project/static/js/images/close.gif delete mode 100755 project/static/js/images/fullscreen.gif delete mode 100755 project/static/js/images/go_to_line.gif delete mode 100755 project/static/js/images/help.gif delete mode 100755 project/static/js/images/highlight.gif delete mode 100755 project/static/js/images/load.gif delete mode 100755 project/static/js/images/move.gif delete mode 100755 project/static/js/images/newdocument.gif delete mode 100755 project/static/js/images/opacity.png delete mode 100755 project/static/js/images/processing.gif delete mode 100755 project/static/js/images/redo.gif delete mode 100755 project/static/js/images/reset_highlight.gif delete mode 100755 project/static/js/images/save.gif delete mode 100755 project/static/js/images/search.gif delete mode 100755 project/static/js/images/smooth_selection.gif delete mode 100755 project/static/js/images/spacer.gif delete mode 100755 project/static/js/images/statusbar_resize.gif delete mode 100755 project/static/js/images/undo.gif delete mode 100755 project/static/js/images/word_wrap.gif delete mode 100755 project/static/js/keyboard.js delete mode 100755 project/static/js/langs/bg.js delete mode 100755 project/static/js/langs/cs.js delete mode 100755 project/static/js/langs/de.js delete mode 100755 project/static/js/langs/dk.js delete mode 100755 project/static/js/langs/en.js delete mode 100755 project/static/js/langs/eo.js delete mode 100755 project/static/js/langs/es.js delete mode 100755 project/static/js/langs/fi.js delete mode 100755 project/static/js/langs/fr.js delete mode 100755 project/static/js/langs/hr.js delete mode 100755 project/static/js/langs/it.js delete mode 100755 project/static/js/langs/ja.js delete mode 100755 project/static/js/langs/mk.js delete mode 100755 project/static/js/langs/nl.js delete mode 100755 project/static/js/langs/pl.js delete mode 100755 project/static/js/langs/pt.js delete mode 100755 project/static/js/langs/ru.js delete mode 100755 project/static/js/langs/sk.js delete mode 100755 project/static/js/langs/zh.js delete mode 100755 project/static/js/license_apache.txt delete mode 100755 project/static/js/license_bsd.txt delete mode 100755 project/static/js/license_lgpl.txt delete mode 100755 project/static/js/manage_area.js delete mode 100755 project/static/js/plugins/charmap/charmap.js delete mode 100755 project/static/js/plugins/charmap/css/charmap.css delete mode 100755 project/static/js/plugins/charmap/images/charmap.gif delete mode 100755 project/static/js/plugins/charmap/jscripts/map.js delete mode 100755 project/static/js/plugins/charmap/langs/bg.js delete mode 100755 project/static/js/plugins/charmap/langs/cs.js delete mode 100755 project/static/js/plugins/charmap/langs/de.js delete mode 100755 project/static/js/plugins/charmap/langs/dk.js delete mode 100755 project/static/js/plugins/charmap/langs/en.js delete mode 100755 project/static/js/plugins/charmap/langs/eo.js delete mode 100755 project/static/js/plugins/charmap/langs/es.js delete mode 100755 project/static/js/plugins/charmap/langs/fr.js delete mode 100755 project/static/js/plugins/charmap/langs/hr.js delete mode 100755 project/static/js/plugins/charmap/langs/it.js delete mode 100755 project/static/js/plugins/charmap/langs/ja.js delete mode 100755 project/static/js/plugins/charmap/langs/mk.js delete mode 100755 project/static/js/plugins/charmap/langs/nl.js delete mode 100755 project/static/js/plugins/charmap/langs/pl.js delete mode 100755 project/static/js/plugins/charmap/langs/pt.js delete mode 100755 project/static/js/plugins/charmap/langs/ru.js delete mode 100755 project/static/js/plugins/charmap/langs/sk.js delete mode 100755 project/static/js/plugins/charmap/langs/zh.js delete mode 100755 project/static/js/plugins/charmap/popup.html delete mode 100755 project/static/js/plugins/test/css/test.css delete mode 100755 project/static/js/plugins/test/images/Thumbs.db delete mode 100755 project/static/js/plugins/test/images/test.gif delete mode 100755 project/static/js/plugins/test/langs/bg.js delete mode 100755 project/static/js/plugins/test/langs/cs.js delete mode 100755 project/static/js/plugins/test/langs/de.js delete mode 100755 project/static/js/plugins/test/langs/dk.js delete mode 100755 project/static/js/plugins/test/langs/en.js delete mode 100755 project/static/js/plugins/test/langs/eo.js delete mode 100755 project/static/js/plugins/test/langs/es.js delete mode 100755 project/static/js/plugins/test/langs/fr.js delete mode 100755 project/static/js/plugins/test/langs/hr.js delete mode 100755 project/static/js/plugins/test/langs/it.js delete mode 100755 project/static/js/plugins/test/langs/ja.js delete mode 100755 project/static/js/plugins/test/langs/mk.js delete mode 100755 project/static/js/plugins/test/langs/nl.js delete mode 100755 project/static/js/plugins/test/langs/pl.js delete mode 100755 project/static/js/plugins/test/langs/pt.js delete mode 100755 project/static/js/plugins/test/langs/ru.js delete mode 100755 project/static/js/plugins/test/langs/sk.js delete mode 100755 project/static/js/plugins/test/langs/zh.js delete mode 100755 project/static/js/plugins/test/test.js delete mode 100755 project/static/js/plugins/test/test2.js delete mode 100755 project/static/js/reg_syntax.js delete mode 100755 project/static/js/reg_syntax/basic.js delete mode 100755 project/static/js/reg_syntax/brainfuck.js delete mode 100755 project/static/js/reg_syntax/c.js delete mode 100755 project/static/js/reg_syntax/coldfusion.js delete mode 100755 project/static/js/reg_syntax/cpp.js delete mode 100755 project/static/js/reg_syntax/css.js delete mode 100755 project/static/js/reg_syntax/html.js delete mode 100755 project/static/js/reg_syntax/java.js delete mode 100755 project/static/js/reg_syntax/js.js delete mode 100755 project/static/js/reg_syntax/pas.js delete mode 100755 project/static/js/reg_syntax/perl.js delete mode 100755 project/static/js/reg_syntax/php.js delete mode 100755 project/static/js/reg_syntax/python.js delete mode 100755 project/static/js/reg_syntax/robotstxt.js delete mode 100755 project/static/js/reg_syntax/ruby.js delete mode 100755 project/static/js/reg_syntax/sql.js delete mode 100755 project/static/js/reg_syntax/tsql.js delete mode 100755 project/static/js/reg_syntax/vb.js delete mode 100755 project/static/js/reg_syntax/xml.js delete mode 100755 project/static/js/regexp.js delete mode 100755 project/static/js/resize_area.js delete mode 100755 project/static/js/search_replace.js delete mode 100755 project/static/js/template.html diff --git a/project/static/css/master.css b/project/static/css/master.css index a80c82b9..7c38ea0b 100644 --- a/project/static/css/master.css +++ b/project/static/css/master.css @@ -62,10 +62,9 @@ label { display: block; } -textarea { +#text_id { width: 480px; height: 480px; - padding: 5px 10px; } #file-text { @@ -79,10 +78,10 @@ textarea { #images { float: left; - width: 480px; + width: 49%; height: 480px; border: 1px solid #999; - padding: 5px 10px; +/* padding: 5px 10px;*/ overflow-y: scroll; overflow-x: hidden; } @@ -98,6 +97,7 @@ textarea { content: ''; clear: both; } + p { margin: 0; } \ No newline at end of file diff --git a/project/static/css/xmlcolors.css b/project/static/css/xmlcolors.css new file mode 100644 index 00000000..aa26579e --- /dev/null +++ b/project/static/css/xmlcolors.css @@ -0,0 +1,51 @@ +.editbox { + margin: .4em; + padding: 0; + font-family: monospace; + font-size: 10pt; + color: black; +} + +.editbox p { + margin: 0; +} + +span.xml-tagname { + color: #A0B; +} + +span.xml-attribute { + color: #281; +} + +span.xml-punctuation { + color: black; +} + +span.xml-attname { + color: #00F; +} + +span.xml-comment { + color: #A70; +} + +span.xml-cdata { + color: #48A; +} + +span.xml-processing { + color: #999; +} + +span.xml-entity { + color: #A22; +} + +span.xml-error { + color: #F00; +} + +span.xml-text { + color: black; +} diff --git a/project/static/js/codemirror/codemirror.js b/project/static/js/codemirror/codemirror.js new file mode 100644 index 00000000..97e2657b --- /dev/null +++ b/project/static/js/codemirror/codemirror.js @@ -0,0 +1,310 @@ +/* CodeMirror main module + * + * Implements the CodeMirror constructor and prototype, which take care + * of initializing the editor frame, and providing the outside interface. + */ + +// The CodeMirrorConfig object is used to specify a default +// configuration. If you specify such an object before loading this +// file, the values you put into it will override the defaults given +// below. You can also assign to it after loading. +var CodeMirrorConfig = window.CodeMirrorConfig || {}; + +var CodeMirror = (function(){ + function setDefaults(object, defaults) { + for (var option in defaults) { + if (!object.hasOwnProperty(option)) + object[option] = defaults[option]; + } + } + function forEach(array, action) { + for (var i = 0; i < array.length; i++) + action(array[i]); + } + + // These default options can be overridden by passing a set of + // options to a specific CodeMirror constructor. See manual.html for + // their meaning. + setDefaults(CodeMirrorConfig, { + stylesheet: "", + path: "", + parserfile: [], + basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"], + iframeClass: null, + passDelay: 200, + passTime: 50, + continuousScanning: false, + saveFunction: null, + onChange: null, + undoDepth: 50, + undoDelay: 800, + disableSpellcheck: true, + textWrapping: true, + readOnly: false, + width: "100%", + height: "300px", + autoMatchParens: false, + parserConfig: null, + tabMode: "indent", // or "spaces", "default", "shift" + reindentOnLoad: false, + activeTokens: null, + cursorActivity: null, + lineNumbers: false, + indentUnit: 2 + }); + + 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%"; + 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"; + place(container); + 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.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; + } + } + } + + if (nums.offsetWidth != barWidth) { + barWidth = nums.offsetWidth; + nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px"); + } + } + function update() { + var diff = 20 + Math.max(doc.body.offsetHeight, frame.offsetHeight) - scroller.offsetHeight; + for (var n = Math.ceil(diff / 10); n > 0; n--) { + var div = document.createElement("DIV"); + div.appendChild(document.createTextNode(nextNum++)); + scroller.appendChild(div); + } + nums.scrollTop = doc.body.scrollTop || doc.documentElement.scrollTop || 0; + } + sizeBar(); + update(); + win.addEventHandler(win, "scroll", update); + win.addEventHandler(win, "resize", update); + var sizeInterval = setInterval(sizeBar, 500); + } + + function CodeMirror(place, options) { + // Backward compatibility for deprecated options. + if (options.dumbTabs) options.tabMode = "spaces"; + else if (options.normalTab) options.tabMode = "default"; + + // Use passed options, if any, to override defaults. + this.options = options = options || {}; + setDefaults(options, CodeMirrorConfig); + + var frame = this.frame = document.createElement("IFRAME"); + if (options.iframeClass) frame.className = options.iframeClass; + frame.frameBorder = 0; + frame.src = "javascript:false;"; + frame.style.border = "0"; + 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"; + + if (place.appendChild) { + var node = place; + place = function(n){node.appendChild(n);}; + } + 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. + frame.CodeMirror = this; + this.win = frame.contentWindow; + + if (typeof options.parserfile == "string") + options.parserfile = [options.parserfile]; + if (typeof options.stylesheet == "string") + options.stylesheet = [options.stylesheet]; + + var html = [""]; + // Hack to work around a bunch of IE8-specific problems. + html.push(""); + forEach(options.stylesheet, function(file) { + html.push(""); + }); + forEach(options.basefiles.concat(options.parserfile), function(file) { + html.push(""); + }); + html.push(""); + + var doc = this.win.document; + doc.open(); + doc.write(html.join("")); + doc.close(); + } + + CodeMirror.prototype = { + init: function() { + if (this.options.initCallback) this.options.initCallback(this); + if (this.options.lineNumbers) applyLineNumbers(this.frame); + if (this.options.reindentOnLoad) this.reindent(); + }, + + getCode: function() {return this.editor.getCode();}, + setCode: function(code) {this.editor.importCode(code);}, + selection: function() {return this.editor.selectedText();}, + reindent: function() {this.editor.reindent();}, + reindentSelection: function() {this.editor.reindentSelection(null);}, + + focus: function() { + this.win.focus(); + if (this.editor.selectionSnapshot) // IE hack + this.win.select.selectCoords(this.win, this.editor.selectionSnapshot); + }, + replaceSelection: function(text) { + this.focus(); + this.editor.replaceSelection(text); + return true; + }, + replaceChars: function(text, start, end) { + this.editor.replaceChars(text, start, end); + }, + getSearchCursor: function(string, fromCursor) { + return this.editor.getSearchCursor(string, fromCursor); + }, + + undo: function() {this.editor.history.undo();}, + redo: function() {this.editor.history.redo();}, + historySize: function() {return this.editor.history.historySize();}, + clearHistory: function() {this.editor.history.clear();}, + + grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);}, + ungrabKeys: function() {this.editor.ungrabKeys();}, + + setParser: function(name) {this.editor.setParser(name);}, + + 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);}, + prevLine: function(line) {return this.editor.prevLine(line);}, + lineContent: function(line) {return this.editor.lineContent(line);}, + setLineContent: function(line, content) {this.editor.setLineContent(line, content);}, + insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);}, + selectLines: function(startLine, startOffset, endLine, endOffset) { + this.win.focus(); + this.editor.selectLines(startLine, startOffset, endLine, endOffset); + }, + nthLine: function(n) { + var line = this.firstLine(); + for (; n > 1 && line !== false; n--) + line = this.nextLine(line); + return line; + }, + lineNumber: function(line) { + var num = 0; + while (line !== false) { + num++; + line = this.prevLine(line); + } + return num; + }, + + // Old number-based line interface + jumpToLine: function(n) { + this.selectLines(this.nthLine(n), 0); + this.win.focus(); + }, + currentLine: function() { + return this.lineNumber(this.cursorPosition().line); + } + }; + + CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}}; + + CodeMirror.replace = function(element) { + if (typeof element == "string") + element = document.getElementById(element); + return function(newElement) { + element.parentNode.replaceChild(newElement, element); + }; + }; + + CodeMirror.fromTextArea = function(area, options) { + if (typeof area == "string") + area = document.getElementById(area); + + options = options || {}; + if (area.style.width && options.width == null) + options.width = area.style.width; + if (area.style.height && options.height == null) + options.height = area.style.height; + if (options.content == null) options.content = area.value; + + if (area.form) { + function updateField() { + area.value = mirror.getCode(); + } + if (typeof area.form.addEventListener == "function") + area.form.addEventListener("submit", updateField, false); + else + area.form.attachEvent("onsubmit", updateField); + } + + function insert(frame) { + if (area.nextSibling) + area.parentNode.insertBefore(frame, area.nextSibling); + else + area.parentNode.appendChild(frame); + } + + area.style.display = "none"; + var mirror = new CodeMirror(insert, options); + return mirror; + }; + + CodeMirror.isProbablySupported = function() { + // This is rather awful, but can be useful. + var match; + if (window.opera) + return Number(window.opera.version()) >= 9.52; + else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./))) + return Number(match[1]) >= 3; + else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/))) + return Number(match[1]) >= 6; + else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i)) + return Number(match[1]) >= 20050901; + else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/)) + return Number(match[1]) >= 525; + else + return null; + }; + + return CodeMirror; +})(); diff --git a/project/static/js/codemirror/editor.js b/project/static/js/codemirror/editor.js new file mode 100644 index 00000000..b8b7abc8 --- /dev/null +++ b/project/static/js/codemirror/editor.js @@ -0,0 +1,1307 @@ +/* The Editor object manages the content of the editable frame. It + * catches events, colours nodes, and indents lines. This file also + * holds some functions for transforming arbitrary DOM structures into + * plain sequences of and
elements + */ + +// Make sure a string does not contain two consecutive 'collapseable' +// whitespace characters. +function makeWhiteSpace(n) { + var buffer = [], nb = true; + for (; n > 0; n--) { + buffer.push((nb || n == 1) ? nbsp : " "); + nb = !nb; + } + return buffer.join(""); +} + +// Create a set of white-space characters that will not be collapsed +// by the browser, but will not break text-wrapping either. +function fixSpaces(string) { + if (string.charAt(0) == " ") string = nbsp + string.slice(1); + return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);}) + .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);}); +} + +function cleanText(text) { + return text.replace(/\u00a0/g, " ").replace(/\u200b/g, ""); +} + +// Create a SPAN node with the expected properties for document part +// spans. +function makePartSpan(value, doc) { + var text = value; + if (value.nodeType == 3) text = value.nodeValue; + else value = doc.createTextNode(text); + + var span = doc.createElement("SPAN"); + span.isPart = true; + span.appendChild(value); + span.currentText = text; + return span; +} + +// On webkit, when the last BR of the document does not have text +// behind it, the cursor can not be put on the line after it. This +// makes pressing enter at the end of the document occasionally do +// nothing (or at least seem to do nothing). To work around it, this +// function makes sure the document ends with a span containing a +// zero-width space character. The traverseDOM iterator filters such +// character out again, so that the parsers won't see them. This +// function is called from a few strategic places to make sure the +// zwsp is restored after the highlighting process eats it. +var webkitLastLineHack = webkit ? + function(container) { + var last = container.lastChild; + if (!last || !last.isPart || last.textContent != "\u200b") + container.appendChild(makePartSpan("\u200b", container.ownerDocument)); + } : function() {}; + +var Editor = (function(){ + // The HTML elements whose content should be suffixed by a newline + // when converting them to flat text. + var newlineElements = {"P": true, "DIV": true, "LI": true}; + + function asEditorLines(string) { + var tab = makeWhiteSpace(indentUnit); + return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces); + } + + // Helper function for traverseDOM. Flattens an arbitrary DOM node + // into an array of textnodes and
tags. + function simplifyDOM(root, atEnd) { + var doc = root.ownerDocument; + var result = []; + var leaving = true; + + function simplifyNode(node, top) { + if (node.nodeType == 3) { + var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " ")); + if (text.length) leaving = false; + result.push(node); + } + 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)) { + leaving = true; + if (!atEnd || !top) + result.push(doc.createElement("BR")); + } + } + } + + simplifyNode(root, true); + return result; + } + + // Creates a MochiKit-style iterator that goes over a series of DOM + // nodes. The values it yields are strings, the textual content of + // the nodes. It makes sure that all nodes up to and including the + // one whose text is being yielded have been 'normalized' to be just + // and
elements. + // See the story.html file for some short remarks about the use of + // continuation-passing style in this iterator. + function traverseDOM(start){ + function yield(value, c){cc = c; return value;} + function push(fun, arg, c){return function(){return fun(arg, c);};} + function stop(){cc = stop; throw StopIteration;}; + var cc = push(scanNode, start, stop); + var owner = start.ownerDocument; + var nodeQueue = []; + + // Create a function that can be used to insert nodes after the + // one given as argument. + function pointAt(node){ + var parent = node.parentNode; + var next = node.nextSibling; + return function(newnode) { + parent.insertBefore(newnode, next); + }; + } + var point = null; + + // Insert a normalized node at the current point. If it is a text + // node, wrap it in a , and give that span a currentText + // property -- this is used to cache the nodeValue, because + // directly accessing nodeValue is horribly slow on some browsers. + // The dirty property is used by the highlighter to determine + // which parts of the document have to be re-highlighted. + function insertPart(part){ + var text = "\n"; + if (part.nodeType == 3) { + select.snapshotChanged(); + part = makePartSpan(part, owner); + text = part.currentText; + } + part.dirty = true; + nodeQueue.push(part); + point(part); + return text; + } + + // Extract the text and newlines from a DOM node, insert them into + // the document, and yield the textual content. Used to replace + // non-normalized nodes. + function writeNode(node, c, end) { + var toYield = []; + forEach(simplifyDOM(node, end), function(part) { + toYield.push(insertPart(part)); + }); + return yield(toYield.join(""), c); + } + + // Check whether a node is a normalized element. + function partNode(node){ + if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + node.currentText = node.firstChild.nodeValue; + return !/[\n\t\r]/.test(node.currentText); + } + return false; + } + + // Handle a node. Add its successor to the continuation if there + // is one, find out whether the node is normalized. If it is, + // yield its content, otherwise, normalize it (writeNode will take + // care of yielding). + function scanNode(node, c){ + if (node.nextSibling) + c = push(scanNode, node.nextSibling, c); + + if (partNode(node)){ + nodeQueue.push(node); + return yield(node.currentText, c); + } + else if (node.nodeName == "BR") { + nodeQueue.push(node); + return yield("\n", c); + } + else { + var end = !node.nextSibling; + point = pointAt(node); + removeElement(node); + return writeNode(node, c, end); + } + } + + // MochiKit iterators are objects with a next function that + // returns the next value or throws StopIteration when there are + // no more values. + return {next: function(){return cc();}, nodes: nodeQueue}; + } + + // Determine the text size of a processed node. + function nodeSize(node) { + 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 && node.nodeName != "BR") node = node.previousSibling; + return node; + } + function endOfLine(node, container) { + if (!node) node = container.firstChild; + else if (node.nodeName == "BR") node = node.nextSibling; + + while (node && node.nodeName != "BR") 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 + // indicating whether anything was found, and can be called again to + // skip to the next find. Use the select and replace methods to + // actually do something with the found locations. + function SearchCursor(editor, string, fromCursor) { + this.editor = editor; + this.history = editor.history; + this.history.commit(); + + // Are we currently at an occurrence of the search string? + this.atOccurrence = false; + // The object stores a set of nodes coming after its current + // position, so that when the current point is taken out of the + // DOM tree, we can still try to continue. + this.fallbackSize = 15; + var cursor; + // Start from the cursor when specified and a cursor can be found. + if (fromCursor && (cursor = select.cursorPos(this.editor.container))) { + this.line = cursor.node; + this.offset = cursor.offset; + } + else { + this.line = null; + this.offset = 0; + } + this.valid = !!string; + + // Create a matcher function based on the kind of string we have. + var target = string.split("\n"), self = this; + this.matches = (target.length == 1) ? + // For one-line strings, searching can be done simply by calling + // indexOf on the current line. + function() { + var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string); + if (match > -1) + return {from: {node: self.line, offset: self.offset + match}, + to: {node: self.line, offset: self.offset + match + string.length}}; + } : + // Multi-line strings require internal iteration over lines, and + // some clunky checks to make sure the first match ends at the + // end of the line and the last match starts at the start. + function() { + var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset)); + var match = firstLine.lastIndexOf(target[0]); + if (match == -1 || match != firstLine.length - target[0].length) + return false; + var startOffset = self.offset + match; + + var line = self.history.nodeAfter(self.line); + for (var i = 1; i < target.length - 1; i++) { + if (cleanText(self.history.textAfter(line)) != target[i]) + return false; + line = self.history.nodeAfter(line); + } + + if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0) + return false; + + return {from: {node: self.line, offset: startOffset}, + to: {node: line, offset: target[target.length - 1].length}}; + }; + } + + SearchCursor.prototype = { + findNext: function() { + if (!this.valid) return false; + this.atOccurrence = false; + var self = this; + + // Go back to the start of the document if the current line is + // no longer in the DOM tree. + if (this.line && !this.line.parentNode) { + this.line = null; + this.offset = 0; + } + + // Set the cursor's position one character after the given + // position. + function saveAfter(pos) { + if (self.history.textAfter(pos.node).length > pos.offset) { + self.line = pos.node; + self.offset = pos.offset + 1; + } + else { + self.line = self.history.nodeAfter(pos.node); + self.offset = 0; + } + } + + while (true) { + var match = this.matches(); + // Found the search string. + if (match) { + this.atOccurrence = match; + saveAfter(match.from); + return true; + } + this.line = this.history.nodeAfter(this.line); + this.offset = 0; + // End of document. + if (!this.line) { + this.valid = false; + return false; + } + } + }, + + select: function() { + if (this.atOccurrence) { + select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to); + select.scrollToCursor(this.editor.container); + } + }, + + replace: function(string) { + if (this.atOccurrence) { + var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string); + this.line = end.node; + this.offset = end.offset; + this.atOccurrence = false; + } + } + }; + + // The Editor object is the main inside-the-iframe interface. + function Editor(options) { + this.options = options; + window.indentUnit = options.indentUnit; + this.parent = parent; + this.doc = document; + var container = this.container = this.doc.body; + this.win = window; + this.history = new History(container, options.undoDepth, options.undoDelay, + this, options.onChange); + var self = this; + + if (!Editor.Parser) + throw "No parser loaded."; + if (options.parserConfig && Editor.Parser.configure) + Editor.Parser.configure(options.parserConfig); + + if (!options.readOnly) + select.setCursorPos(container, {node: null, offset: 0}); + + 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) { + this.scanner = this.documentScanner(options.passTime); + this.delayScanning(); + } + + function setEditable() { + // In IE, designMode frames can not run any scripts, so we use + // contentEditable instead. + if (document.body.contentEditable != undefined && internetExplorer) + document.body.contentEditable = "true"; + else + document.designMode = "on"; + + document.documentElement.style.borderWidth = "0"; + if (!options.textWrapping) + container.style.whiteSpace = "nowrap"; + } + + // If setting the frame editable fails, try again when the user + // focus it (happens when the frame is not visible on + // initialisation, in Firefox). + try { + setEditable(); + } + catch(e) { + var focusEvent = addEventHandler(document, "focus", function() { + focusEvent(); + setEditable(); + }, true); + } + + addEventHandler(document, "keydown", method(this, "keyDown")); + addEventHandler(document, "keypress", method(this, "keyPress")); + addEventHandler(document, "keyup", method(this, "keyUp")); + + function cursorActivity() {self.cursorActivity(false);} + addEventHandler(document.body, "mouseup", 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); + } + } + }); + addEventHandler(document.body, "cut", cursorActivity); + + if (this.options.autoMatchParens) + addEventHandler(document.body, "click", method(this, "scheduleParenBlink")); + } + else if (!options.textWrapping) { + container.style.whiteSpace = "nowrap"; + } + } + + function isSafeKey(code) { + return (code >= 16 && code <= 18) || // shift, control, alt + (code >= 33 && code <= 40); // arrows, home, end + } + + Editor.prototype = { + // Import a piece of code into the editor. + importCode: function(code) { + this.history.push(null, null, asEditorLines(code)); + this.history.reset(); + }, + + // Extract the code from the editor. + getCode: function() { + if (!this.container.firstChild) + return ""; + + var accum = []; + select.markSelection(this.win); + forEach(traverseDOM(this.container.firstChild), method(accum, "push")); + webkitLastLineHack(this.container); + select.selectMarked(); + return cleanText(accum.join("")); + }, + + checkLine: function(node) { + if (node === false || !(node == null || node.parentNode == this.container)) + throw parent.CodeMirror.InvalidLineHandle; + }, + + cursorPosition: function(start) { + if (start == null) start = true; + var pos = select.cursorPos(this.container, start); + if (pos) return {line: pos.node, character: pos.offset}; + else return {line: null, character: 0}; + }, + + firstLine: function() { + return null; + }, + + lastLine: function() { + if (this.container.lastChild) return startOfLine(this.container.lastChild); + else return null; + }, + + nextLine: function(line) { + this.checkLine(line); + var end = endOfLine(line, this.container); + return end || false; + }, + + prevLine: function(line) { + this.checkLine(line); + if (line == null) return false; + return startOfLine(line.previousSibling); + }, + + selectLines: function(startLine, startOffset, endLine, endOffset) { + this.checkLine(startLine); + var start = {node: startLine, offset: startOffset}, end = null; + if (endOffset !== undefined) { + this.checkLine(endLine); + end = {node: endLine, offset: endOffset}; + } + select.setCursorPos(this.container, start, end); + select.scrollToCursor(this.container); + }, + + lineContent: function(line) { + this.checkLine(line); + var accum = []; + for (line = line ? line.nextSibling : this.container.firstChild; + line && line.nodeName != "BR"; line = line.nextSibling) + accum.push(nodeText(line)); + return cleanText(accum.join("")); + }, + + setLineContent: function(line, content) { + this.history.commit(); + this.replaceRange({node: line, offset: 0}, + {node: line, offset: this.history.textAfter(line).length}, + content); + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + insertIntoLine: function(line, position, content) { + var before = null; + if (position == "end") { + before = endOfLine(line, this.container); + } + else { + for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) { + if (position == 0) { + before = cur; + break; + } + var text = (cur.innerText || cur.textContent || cur.nodeValue || ""); + if (text.length > position) { + before = cur.nextSibling; + content = text.slice(0, position) + content + text.slice(position); + removeElement(cur); + break; + } + position -= text.length; + } + } + + var lines = asEditorLines(content), doc = this.container.ownerDocument; + for (var i = 0; i < lines.length; i++) { + if (i > 0) this.container.insertBefore(doc.createElement("BR"), before); + this.container.insertBefore(makePartSpan(lines[i], doc), before); + } + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + // Retrieve the selected text. + selectedText: function() { + var h = this.history; + h.commit(); + + var start = select.cursorPos(this.container, true), + end = select.cursorPos(this.container, false); + if (!start || !end) return ""; + + if (start.node == end.node) + return h.textAfter(start.node).slice(start.offset, end.offset); + + var text = [h.textAfter(start.node).slice(start.offset)]; + for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos)) + text.push(h.textAfter(pos)); + text.push(h.textAfter(end.node).slice(0, end.offset)); + return cleanText(text.join("\n")); + }, + + // 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); + }, + + replaceRange: function(from, to, text) { + var lines = asEditorLines(text); + lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0]; + var lastLine = lines[lines.length - 1]; + lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset); + var end = this.history.nodeAfter(to.node); + this.history.push(from.node, end, lines); + return {node: this.history.nodeBefore(end), + offset: lastLine.length}; + }, + + getSearchCursor: function(string, fromCursor) { + return new SearchCursor(this, string, fromCursor); + }, + + // Re-indent the whole buffer + reindent: function() { + if (this.container.firstChild) + this.indentRegion(null, this.container.lastChild); + }, + + reindentSelection: function(direction) { + if (!select.somethingSelected(this.win)) { + this.indentAtCursor(direction); + } + else { + var start = select.selectionTopNode(this.container, true), + end = select.selectionTopNode(this.container, false); + if (start === false || end === false) return; + this.indentRegion(start, end, direction); + } + }, + + grabKeys: function(eventHandler, filter) { + this.frozen = eventHandler; + this.keyFilter = filter; + }, + ungrabKeys: function() { + this.frozen = "leave"; + this.keyFilter = null; + }, + + setParser: function(name) { + Editor.Parser = window[name]; + if (this.container.firstChild) { + forEach(this.container.childNodes, function(n) { + if (n.nodeType != 3) n.dirty = true; + }); + this.addDirtyNode(this.firstChild); + this.scheduleHighlight(); + } + }, + + // 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))) { + event.stop(); + this.frozen(event); + return; + } + + var code = event.keyCode; + // Don't scan when the user is typing. + this.delayScanning(); + // Schedule a paren-highlight event, if configured. + if (this.options.autoMatchParens) + this.scheduleParenBlink(); + + // The variouschecks for !altKey are there because AltGr sets both + // ctrlKey and altKey to true, and should not be recognised as + // Control. + if (code == 13) { // enter + if (event.ctrlKey && !event.altKey) { + this.reparseBuffer(); + } + else { + select.insertNewlineAtCursor(this.win); + this.indentAtCursor(); + select.scrollToCursor(this.container); + } + event.stop(); + } + else if (code == 9 && this.options.tabMode != "default") { // tab + this.handleTab(!event.ctrlKey && !event.shiftKey); + event.stop(); + } + else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space + this.handleTab(true); + event.stop(); + } + else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home + if (this.home()) + event.stop(); + } + else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ] + this.blinkParens(event.shiftKey); + event.stop(); + } + else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right + var cursor = select.selectionTopNode(this.container); + if (cursor === false || !this.container.firstChild) return; + + if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container); + else { + var end = endOfLine(cursor, this.container); + select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container); + } + event.stop(); + } + else if ((event.ctrlKey || event.metaKey) && !event.altKey) { + if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y + select.scrollToNode(this.history.redo()); + event.stop(); + } + else if (code == 90 || (safari && code == 8)) { // Z, backspace + select.scrollToNode(this.history.undo()); + event.stop(); + } + else if (code == 83 && this.options.saveFunction) { // S + this.options.saveFunction(); + event.stop(); + } + } + }, + + // Check for characters that should re-indent the current line, + // and prevent Opera from handling enter and tab anyway. + keyPress: function(event) { + var electric = Editor.Parser.electricChars, self = this; + // Hack for Opera, and Firefox on OS X, in which stopping a + // 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.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); + }, + + // Mark the node at the cursor dirty when a non-safe key is + // released. + keyUp: function(event) { + this.cursorActivity(isSafeKey(event.keyCode)); + }, + + // Indent the line following a given
, or null for the first + // line. If given a
element, this must have been highlighted + // so that it has an indentation method. Returns the whitespace + // element that has been modified or created (if any). + indentLineAfter: function(start, direction) { + // whiteSpace is the whitespace span at the start of the line, + // or null if there is no such node. + var whiteSpace = start ? start.nextSibling : this.container.firstChild; + if (whiteSpace && !hasClass(whiteSpace, "whitespace")) + whiteSpace = null; + + // Sometimes the start of the line can influence the correct + // indentation, so we retrieve it. + var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild); + var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : ""; + + // Ask the lexical context for the correct indentation, and + // compute how much this differs from the current indentation. + var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0; + if (direction != null && this.options.tabMode == "shift") + newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit) + else if (start) + newIndent = start.indentation(nextChars, curIndent, direction); + else if (Editor.Parser.firstIndentation) + newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction); + var indentDiff = newIndent - curIndent; + + // If there is too much, this is just a matter of shrinking a span. + if (indentDiff < 0) { + if (newIndent == 0) { + if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0); + removeElement(whiteSpace); + whiteSpace = null; + } + else { + select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true); + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + } + // Not enough... + else if (indentDiff > 0) { + // If there is whitespace, we grow it. + if (whiteSpace) { + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + // Otherwise, we have to add a new whitespace node. + else { + whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc); + whiteSpace.className = "whitespace"; + if (start) insertAfter(whiteSpace, start); + else this.container.insertBefore(whiteSpace, this.container.firstChild); + } + if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true); + } + if (indentDiff != 0) this.addDirtyNode(start); + return whiteSpace; + }, + + // Re-highlight the selected part of the document. + highlightAtCursor: function() { + var pos = select.selectionTopNode(this.container, true); + var to = select.selectionTopNode(this.container, false); + if (pos === false || to === false) return; + + select.markSelection(this.win); + if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false) + return false; + select.selectMarked(); + return true; + }, + + // When tab is pressed with text selected, the whole selection is + // re-indented, when nothing is selected, the line with the cursor + // is re-indented. + handleTab: function(direction) { + if (this.options.tabMode == "spaces") + select.insertTabAtCursor(this.win); + else + this.reindentSelection(direction); + }, + + home: function() { + var cur = select.selectionTopNode(this.container, true), start = cur; + if (cur === false || !(!cur || cur.isPart || cur.nodeName == "BR") || !this.container.firstChild) + return false; + + 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); + else + select.focusAfterNode(cur, this.container); + + select.scrollToCursor(this.container); + return true; + }, + + // Delay (or initiate) the next paren blink event. + scheduleParenBlink: function() { + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + var self = this; + this.parenEvent = this.parent.setTimeout(function(){self.blinkParens();}, 300); + }, + + // Take the token before the cursor. If it contains a character in + // '()[]{}', search for the matching paren/brace/bracket, and + // highlight them in green for a moment, or red if no proper match + // was found. + blinkParens: function(jump) { + if (!window.select) return; + // Clear the event property. + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + this.parenEvent = null; + + // Extract a 'paren' from a piece of text. + function paren(node) { + if (node.currentText) { + var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/); + return match && match[1]; + } + } + // Determine the direction a paren is facing. + function forward(ch) { + return /[\(\[\{]/.test(ch); + } + + var ch, self = this, cursor = select.selectionTopNode(this.container, true); + if (!cursor || !this.highlightAtCursor()) return; + cursor = select.selectionTopNode(this.container, true); + if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor))))) + return; + // We only look for tokens with the same className. + var className = cursor.className, dir = forward(ch), match = matching[ch]; + + // Since parts of the document might not have been properly + // highlighted, and it is hard to know in advance which part we + // have to scan, we just try, and when we find dirty nodes we + // abort, parse them, and re-try. + 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 (forward(ch) == dir) + stack.push(ch); + else if (!stack.length) + ok = false; + else if (stack.pop() != matching[ch]) + ok = false; + if (!stack.length) break; + } + else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") { + return {node: runner, status: "dirty"}; + } + } + return {node: runner, status: runner && ok}; + } + // Temporarily give the relevant nodes a colour. + function blink(node, ok) { + node.style.fontWeight = "bold"; + node.style.color = ok ? "#8F8" : "#F88"; + self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500); + } + + while (true) { + var found = tryFindMatch(); + if (found.status == "dirty") { + this.highlight(found.node, endOfLine(found.node)); + // Needed because in some corner cases a highlight does not + // reach a node. + found.node.dirty = false; + continue; + } + else { + blink(cursor, found.status); + if (found.node) { + blink(found.node, found.status); + if (jump) select.focusAfterNode(found.node.previousSibling, this.container); + } + break; + } + } + }, + + // Adjust the amount of whitespace at the start of the line that + // the cursor is on so that it is indented properly. + indentAtCursor: function(direction) { + if (!this.container.firstChild) return; + // The line has to have up-to-date lexical information, so we + // highlight it first. + if (!this.highlightAtCursor()) return; + var cursor = select.selectionTopNode(this.container, false); + // If we couldn't determine the place of the cursor, + // there's nothing to indent. + if (cursor === false) + return; + var lineStart = startOfLine(cursor); + var whiteSpace = this.indentLineAfter(lineStart, direction); + if (cursor == lineStart && whiteSpace) + cursor = whiteSpace; + // This means the indentation has probably messed up the cursor. + if (cursor == whiteSpace) + select.focusAfterNode(cursor, this.container); + }, + + // Indent all lines whose start falls inside of the current + // 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); + + do { + var next = endOfLine(current, this.container); + if (current) this.highlight(before, next, true); + this.indentLineAfter(current, direction); + before = current; + current = next; + } while (current != end); + select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0}); + }, + + // Find the node that the cursor is in, mark it as dirty, and make + // sure a highlight pass is scheduled. + cursorActivity: function(safe) { + if (internetExplorer) { + this.container.createTextRange().execCommand("unlink"); + this.selectionSnapshot = select.selectionCoords(this.win); + } + + var activity = this.options.cursorActivity; + if (!safe || activity) { + var cursor = select.selectionTopNode(this.container, false); + if (cursor === false || !this.container.firstChild) return; + cursor = cursor || this.container.firstChild; + if (activity) activity(cursor); + if (!safe) { + this.scheduleHighlight(); + this.addDirtyNode(cursor); + } + } + }, + + reparseBuffer: function() { + forEach(this.container.childNodes, function(node) {node.dirty = true;}); + if (this.container.firstChild) + this.addDirtyNode(this.container.firstChild); + }, + + // Add a node to the set of dirty nodes, if it isn't already in + // there. + addDirtyNode: function(node) { + node = node || this.container.firstChild; + if (!node) return; + + for (var i = 0; i < this.dirty.length; i++) + if (this.dirty[i] == node) return; + + if (node.nodeType != 3) + node.dirty = true; + this.dirty.push(node); + }, + + // Cause a highlight pass to happen in options.passDelay + // milliseconds. Clear the existing timeout, if one exists. This + // way, the passes do not happen while the user is typing, and + // should as unobtrusive as possible. + scheduleHighlight: function() { + // Timeouts are routed through the parent window, because on + // some browsers designMode windows do not fire timeouts. + var self = this; + this.parent.clearTimeout(this.highlightTimeout); + this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay); + }, + + // Fetch one dirty node, and remove it from the dirty set. + getDirtyNode: function() { + while (this.dirty.length > 0) { + var found = this.dirty.pop(); + // IE8 sometimes throws an unexplainable 'invalid argument' + // exception for found.parentNode + try { + // If the node has been coloured in the meantime, or is no + // longer in the document, it should not be returned. + while (found && found.parentNode != this.container) + found = found.parentNode + if (found && (found.dirty || found.nodeType == 3)) + return found; + } catch (e) {} + } + return null; + }, + + // Pick dirty nodes, and highlight them, until options.passTime + // milliseconds have gone by. The highlight method will continue + // to next lines as long as it finds dirty nodes. It returns + // information about the place where it stopped. If there are + // dirty nodes left after this function has spent all its lines, + // it shedules another highlight to finish the job. + highlightDirty: function(force) { + // Prevent FF from raising an error when it is firing timeouts + // on a page that's no longer loaded. + if (!window.select) return; + + if (!this.options.readOnly) select.markSelection(this.win); + var start, endTime = force ? null : time() + this.options.passTime; + while (time() < endTime && (start = this.getDirtyNode())) { + var result = this.highlight(start, endTime); + if (result && result.node && result.dirty) + this.addDirtyNode(result.node); + } + if (!this.options.readOnly) select.selectMarked(); + if (start) this.scheduleHighlight(); + return this.dirty.length == 0; + }, + + // Creates a function that, when called through a timeout, will + // continuously re-parse the document. + documentScanner: function(passTime) { + var self = this, pos = null; + return function() { + // FF timeout weirdness workaround. + if (!window.select) return; + // If the current node is no longer in the document... oh + // well, we start over. + if (pos && pos.parentNode != self.container) + pos = null; + select.markSelection(self.win); + var result = self.highlight(pos, time() + passTime, true); + select.selectMarked(); + var newPos = result ? (result.node && result.node.nextSibling) : null; + pos = (pos == newPos) ? null : newPos; + self.delayScanning(); + }; + }, + + // Starts the continuous scanning process for this document after + // a given interval. + delayScanning: function() { + if (this.scanner) { + this.parent.clearTimeout(this.documentScan); + this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning); + } + }, + + // The function that does the actual highlighting/colouring (with + // help from the parser and the DOM normalizer). Its interface is + // rather overcomplicated, because it is used in different + // situations: ensuring that a certain line is highlighted, or + // highlighting up to X milliseconds starting from a certain + // point. The 'from' argument gives the node at which it should + // start. If this is null, it will start at the beginning of the + // document. When a timestamp is given with the 'target' argument, + // it will stop highlighting at that time. If this argument holds + // a DOM node, it will highlight until it reaches that node. If at + // any time it comes across two 'clean' lines (no dirty nodes), it + // will stop, except when 'cleanLines' is true. maxBacktrack is + // the maximum number of lines to backtrack to find an existing + // parser instance. This is used to give up in situations where a + // highlight would take too long and freeze the browser interface. + highlight: function(from, target, cleanLines, maxBacktrack){ + var container = this.container, self = this, active = this.options.activeTokens; + var endTime = (typeof target == "number" ? target : null); + + if (!container.firstChild) + return; + // 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) + return false; + from = from.previousSibling; + } + // If we are at the end of the document, do nothing. + if (from && !from.nextSibling) + return; + + // Check whether a part ( node) and the corresponding token + // match. + function correctPart(token, part){ + return !part.reduced && part.currentText == token.value && part.className == token.style; + } + // Shorten the text associated with a part by chopping off + // characters from the front. Note that only the currentText + // property gets changed. For efficiency reasons, we leave the + // nodeValue alone -- we set the reduced flag to indicate that + // this part must be replaced. + function shortenPart(part, minus){ + part.currentText = part.currentText.substring(minus); + part.reduced = true; + } + // Create a part corresponding to a given token. + function tokenPart(token){ + var part = makePartSpan(token.value, self.doc); + part.className = token.style; + return part; + } + + function maybeTouch(node) { + if (node) { + if (lineDirty || node.nextSibling != node.oldNextSibling) + self.history.touch(node); + node.oldNextSibling = node.nextSibling; + } + else { + if (lineDirty || self.container.firstChild != self.container.oldFirstChild) + self.history.touch(node); + self.container.oldFirstChild = self.container.firstChild; + } + } + + // Get the token stream. If from is null, we start with a new + // parser from the start of the frame, otherwise a partial parse + // is resumed. + var traversal = traverseDOM(from ? from.nextSibling : container.firstChild), + stream = stringStream(traversal), + parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream); + + // parts is an interface to make it possible to 'delay' fetching + // the next DOM node until we are completely done with the one + // before it. This is necessary because often the next node is + // not yet available when we want to proceed past the current + // one. + var parts = { + current: null, + // Fetch current node. + get: function(){ + if (!this.current) + this.current = traversal.nodes.shift(); + return this.current; + }, + // Advance to the next part (do not fetch it yet). + next: function(){ + this.current = null; + }, + // Remove the current part from the DOM tree, and move to the + // next. + remove: function(){ + container.removeChild(this.get()); + this.current = null; + }, + // Advance to the next part that is not empty, discarding empty + // parts. + getNonEmpty: function(){ + var part = this.get(); + // 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 == "") { + var old = part; + this.remove(); + part = this.get(); + // Adjust selection information, if any. See select.js for details. + select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0); + } + return part; + } + }; + + var lineDirty = false, prevLineDirty = true, lineNodes = 0; + + // This forEach loops over the tokens from the parsed stream, and + // at the same time uses the parts object to proceed through the + // corresponding DOM nodes. + forEach(parsed, function(token){ + var part = parts.getNonEmpty(); + + 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") + throw "Parser out of sync. Expected BR."; + + if (part.dirty || !part.indentation) lineDirty = true; + maybeTouch(from); + from = part; + + // Every
gets a copy of the parser state and a lexical + // context assigned to it. The first is used to be able to + // later resume parsing from this point, the second is used + // for indentation. + part.parserFromHere = parsed.copy(); + part.indentation = token.indentation; + part.dirty = false; + + // If the target argument wasn't an integer, go at least + // until that node. + if (endTime == null && part == target) throw StopIteration; + + // A clean line with more than one node means we are done. + // Throwing a StopIteration is the way to break out of a + // MochiKit forEach loop. + if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines)) + throw StopIteration; + prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0; + parts.next(); + } + else { + if (part.nodeName != "SPAN") + throw "Parser out of sync. Expected SPAN."; + if (part.dirty) + lineDirty = true; + lineNodes++; + + // If the part matches the token, we can leave it alone. + if (correctPart(token, part)){ + part.dirty = false; + parts.next(); + } + // Otherwise, we have to fix it. + else { + lineDirty = true; + // Insert the correct part. + var newPart = tokenPart(token); + container.insertBefore(newPart, part); + if (active) active(newPart, token, self); + var tokensize = token.value.length; + var offset = 0; + // Eat up parts until the text for this token has been + // removed, adjusting the stored selection info (see + // select.js) in the process. + while (tokensize > 0) { + part = parts.get(); + var partsize = part.currentText.length; + select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset); + if (partsize > tokensize){ + shortenPart(part, tokensize); + tokensize = 0; + } + else { + tokensize -= partsize; + offset += partsize; + parts.remove(); + } + } + } + } + }); + maybeTouch(from); + webkitLastLineHack(this.container); + + // The function returns some status information that is used by + // hightlightDirty to determine whether and where it has to + // continue. + return {node: parts.getNonEmpty(), + dirty: lineDirty}; + } + }; + + return Editor; +})(); + +addEventHandler(window, "load", function() { + var CodeMirror = window.frameElement.CodeMirror; + CodeMirror.editor = new Editor(CodeMirror.options); + this.parent.setTimeout(method(CodeMirror, "init"), 0); +}); diff --git a/project/static/js/codemirror/parsexml.js b/project/static/js/codemirror/parsexml.js new file mode 100644 index 00000000..95a80993 --- /dev/null +++ b/project/static/js/codemirror/parsexml.js @@ -0,0 +1,292 @@ +/* This file defines an XML parser, with a few kludges to make it + * useable for HTML. autoSelfClosers defines a set of tag names that + * are expected to not have a closing tag, and doNotIndent specifies + * the tags inside of which no indentation should happen (see Config + * object). These can be disabled by passing the editor an object like + * {useHTMLKludges: false} as parserConfig option. + */ + +var XMLParser = Editor.Parser = (function() { + var Kludges = { + autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true, + "meta": true, "col": true, "frame": true, "base": true, "area": true}, + doNotIndent: {"pre": true, "!cdata": true} + }; + var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}}; + var UseKludges = Kludges; + var alignCDATA = false; + + // Simple stateful tokenizer for XML documents. Returns a + // MochiKit-style iterator, with a state property that contains a + // function encapsulating the current state. See tokenize.js. + var tokenizeXML = (function() { + function inText(source, setState) { + var ch = source.next(); + if (ch == "<") { + if (source.equals("!")) { + source.next(); + if (source.equals("[")) { + if (source.lookAhead("[CDATA[", true)) { + setState(inBlock("xml-cdata", "]]>")); + return null; + } + else { + return "xml-text"; + } + } + else if (source.lookAhead("--", true)) { + setState(inBlock("xml-comment", "-->")); + return null; + } + else { + return "xml-text"; + } + } + else if (source.equals("?")) { + source.next(); + source.nextWhileMatches(/[\w\._\-]/); + setState(inBlock("xml-processing", "?>")); + return "xml-processing"; + } + else { + if (source.equals("/")) source.next(); + setState(inTag); + return "xml-punctuation"; + } + } + else if (ch == "&") { + while (!source.endOfLine()) { + if (source.next() == ";") + break; + } + return "xml-entity"; + } + else { + source.nextWhileMatches(/[^&<\n]/); + return "xml-text"; + } + } + + function inTag(source, setState) { + var ch = source.next(); + if (ch == ">") { + setState(inText); + return "xml-punctuation"; + } + else if (/[?\/]/.test(ch) && source.equals(">")) { + source.next(); + setState(inText); + return "xml-punctuation"; + } + else if (ch == "=") { + return "xml-punctuation"; + } + else if (/[\'\"]/.test(ch)) { + setState(inAttribute(ch)); + return null; + } + else { + source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/); + return "xml-name"; + } + } + + function inAttribute(quote) { + return function(source, setState) { + while (!source.endOfLine()) { + if (source.next() == quote) { + setState(inTag); + break; + } + } + return "xml-attribute"; + }; + } + + function inBlock(style, terminator) { + return function(source, setState) { + while (!source.endOfLine()) { + if (source.lookAhead(terminator, true)) { + setState(inText); + break; + } + source.next(); + } + return style; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || inText); + }; + })(); + + // The parser. The structure of this function largely follows that of + // parseJavaScript in parsejavascript.js (there is actually a bit more + // shared code than I'd like), but it is quite a bit simpler. + function parseXML(source) { + var tokens = tokenizeXML(source); + var cc = [base]; + var tokenNr = 0, indented = 0; + var currentTag = null, context = null; + var consume, marked; + + function push(fs) { + for (var i = fs.length - 1; i >= 0; i--) + cc.push(fs[i]); + } + function cont() { + push(arguments); + consume = true; + } + function pass() { + push(arguments); + consume = false; + } + + function mark(style) { + marked = style; + } + function expect(text) { + return function(style, content) { + if (content == text) cont(); + else mark("xml-error") || cont(arguments.callee); + }; + } + + function pushContext(tagname, startOfLine) { + var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent); + context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent}; + } + function popContext() { + context = context.prev; + } + function computeIndentation(baseContext) { + return function(nextChars, current) { + var context = baseContext; + if (context && context.noIndent) + return current; + if (alignCDATA && /")); + else if (style == "xml-cdata") { + if (!context || context.name != "!cdata") pushContext("!cdata"); + if (/\]\]>$/.test(content)) popContext(); + cont(); + } + else if (harmlessTokens.hasOwnProperty(style)) cont(); + else mark("xml-error") || cont(); + } + function tagname(style, content) { + if (style == "xml-name") { + currentTag = content.toLowerCase(); + mark("xml-tagname"); + cont(); + } + else { + currentTag = null; + pass(); + } + } + function closetagname(style, content) { + if (style == "xml-name" && context && content.toLowerCase() == context.name) { + popContext(); + mark("xml-tagname"); + } + else { + mark("xml-error"); + } + cont(); + } + function endtag(startOfLine) { + return function(style, content) { + if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont(); + else if (content == ">") pushContext(currentTag, startOfLine) || cont(); + else mark("xml-error") || cont(arguments.callee); + }; + } + function attributes(style) { + if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes); + else pass(); + } + function attribute(style, content) { + if (content == "=") cont(value); + else if (content == ">" || content == "/>") pass(endtag); + else pass(); + } + function value(style) { + if (style == "xml-attribute") cont(value); + else pass(); + } + + return { + indentation: function() {return indented;}, + + next: function(){ + var token = tokens.next(); + if (token.style == "whitespace" && tokenNr == 0) + indented = token.value.length; + else + tokenNr++; + if (token.content == "\n") { + indented = tokenNr = 0; + token.indentation = computeIndentation(context); + } + + if (token.style == "whitespace" || token.type == "xml-comment") + return token; + + while(true){ + consume = marked = false; + cc.pop()(token.style, token.content); + if (consume){ + if (marked) + token.style = marked; + return token; + } + } + }, + + copy: function(){ + var _cc = cc.concat([]), _tokenState = tokens.state, _context = context; + var parser = this; + + return function(input){ + cc = _cc.concat([]); + tokenNr = indented = 0; + context = _context; + tokens = tokenizeXML(input, _tokenState); + return parser; + }; + } + }; + } + + return { + make: parseXML, + electricChars: "/", + configure: function(config) { + if (config.useHTMLKludges != null) + UseKludges = config.useHTMLKludges ? Kludges : NoKludges; + if (config.alignCDATA) + alignCDATA = config.alignCDATA; + } + }; +})(); diff --git a/project/static/js/codemirror/select.js b/project/static/js/codemirror/select.js new file mode 100644 index 00000000..9ceb24e7 --- /dev/null +++ b/project/static/js/codemirror/select.js @@ -0,0 +1,619 @@ +/* Functionality for finding, storing, and restoring selections + * + * This does not provide a generic API, just the minimal functionality + * required by the CodeMirror system. + */ + +// Namespace object. +var select = {}; + +(function() { + select.ie_selection = document.selection && document.selection.createRangeCollection; + + // Find the 'top-level' (defined as 'a direct child of the node + // passed as the top argument') node that the given node is + // contained in. Return null if the given node is not inside the top + // node. + function topLevelNodeAt(node, top) { + while (node && node.parentNode != top) + node = node.parentNode; + return node; + } + + // Find the top-level node that contains the node before this one. + function topLevelNodeBefore(node, top) { + while (!node.previousSibling && node.parentNode != top) + node = node.parentNode; + return topLevelNodeAt(node.previousSibling, top); + } + + var fourSpaces = "\u00a0\u00a0\u00a0\u00a0"; + + select.scrollToNode = function(element) { + if (!element) return; + var doc = element.ownerDocument, body = doc.body, + win = (doc.defaultView || doc.parentWindow), + html = doc.documentElement, + atEnd = !element.nextSibling || !element.nextSibling.nextSibling + || !element.nextSibling.nextSibling.nextSibling; + // In Opera (and recent Webkit versions), BR elements *always* + // have a scrollTop property of zero. + var compensateHack = 0; + while (element && !element.offsetTop) { + compensateHack++; + element = element.previousSibling; + } + // atEnd is another kludge for these browsers -- if the cursor is + // at the end of the document, and the node doesn't have an + // offset, just scroll to the end. + if (compensateHack == 0) atEnd = false; + + var y = compensateHack * (element ? element.offsetHeight : 0), x = 0, pos = element; + while (pos && pos.offsetParent) { + y += pos.offsetTop; + // Don't count X offset for
nodes + if (pos.nodeName != "BR") + x += pos.offsetLeft; + pos = pos.offsetParent; + } + + var scroll_x = body.scrollLeft || html.scrollLeft || 0, + scroll_y = body.scrollTop || html.scrollTop || 0, + screen_x = x - scroll_x, screen_y = y - scroll_y, scroll = false; + + if (screen_x < 0 || screen_x > (win.innerWidth || html.clientWidth || 0)) { + scroll_x = x; + scroll = true; + } + if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) { + scroll_y = atEnd ? 1e10 : y; + scroll = true; + } + if (scroll) win.scrollTo(scroll_x, scroll_y); + }; + + select.scrollToCursor = function(container) { + select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild); + }; + + // Used to prevent restoring a selection when we do not need to. + var currentSelection = null; + + select.snapshotChanged = function() { + if (currentSelection) currentSelection.changed = true; + }; + + // This is called by the code in editor.js whenever it is replacing + // a text node. The function sees whether the given oldNode is part + // of the current selection, and updates this selection if it is. + // Because nodes are often only partially replaced, the length of + // the part that gets replaced has to be taken into account -- the + // selection might stay in the oldNode if the newNode is smaller + // than the selection's offset. The offset argument is needed in + // case the selection does move to the new object, and the given + // length is not the whole length of the new node (part of it might + // have been used to replace another node). + select.snapshotReplaceNode = function(from, to, length, offset) { + if (!currentSelection) return; + + function replace(point) { + if (from == point.node) { + currentSelection.changed = true; + if (length && point.offset > length) { + point.offset -= length; + } + else { + point.node = to; + point.offset += (offset || 0); + } + } + } + replace(currentSelection.start); + replace(currentSelection.end); + }; + + select.snapshotMove = function(from, to, distance, relative, ifAtStart) { + if (!currentSelection) return; + + function move(point) { + if (from == point.node && (!ifAtStart || point.offset == 0)) { + currentSelection.changed = true; + point.node = to; + if (relative) point.offset = Math.max(0, point.offset + distance); + else point.offset = distance; + } + } + move(currentSelection.start); + move(currentSelection.end); + }; + + // Most functions are defined in two ways, one for the IE selection + // model, one for the W3C one. + if (select.ie_selection) { + function selectionNode(win, start) { + var range = win.document.selection.createRange(); + range.collapse(start); + + function nodeAfter(node) { + var found = null; + while (!found && node) { + found = node.nextSibling; + node = node.parentNode; + } + return nodeAtStartOf(found); + } + + function nodeAtStartOf(node) { + while (node && node.firstChild) node = node.firstChild; + return {node: node, offset: 0}; + } + + var containing = range.parentElement(); + if (!isAncestor(win.document.body, containing)) return null; + if (!containing.firstChild) return nodeAtStartOf(containing); + + var working = range.duplicate(); + working.moveToElementText(containing); + working.collapse(true); + for (var cur = containing.firstChild; cur; cur = cur.nextSibling) { + if (cur.nodeType == 3) { + var size = cur.nodeValue.length; + working.move("character", size); + } + else { + working.moveToElementText(cur); + working.collapse(false); + } + + var dir = range.compareEndPoints("StartToStart", working); + if (dir == 0) return nodeAfter(cur); + if (dir == 1) continue; + if (cur.nodeType != 3) return nodeAtStartOf(cur); + + working.setEndPoint("StartToEnd", range); + return {node: cur, offset: size - working.text.length}; + } + return nodeAfter(containing); + } + + select.markSelection = function(win) { + currentSelection = null; + var sel = win.document.selection; + if (!sel) return; + var start = selectionNode(win, true), + end = selectionNode(win, false); + if (!start || !end) return; + currentSelection = {start: start, end: end, window: win, changed: false}; + }; + + select.selectMarked = function() { + if (!currentSelection || !currentSelection.changed) return; + var win = currentSelection.window, doc = win.document; + + function makeRange(point) { + var range = doc.body.createTextRange(), + node = point.node; + if (!node) { + range.moveToElementText(currentSelection.window.document.body); + range.collapse(false); + } + else if (node.nodeType == 3) { + range.moveToElementText(node.parentNode); + var offset = point.offset; + while (node.previousSibling) { + node = node.previousSibling; + offset += (node.innerText || "").length; + } + range.move("character", offset); + } + else { + range.moveToElementText(node); + range.collapse(true); + } + return range; + } + + var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end); + start.setEndPoint("StartToEnd", end); + start.select(); + }; + + // Get the top-level node that one end of the cursor is inside or + // after. Note that this returns false for 'no cursor', and null + // for 'start of document'. + select.selectionTopNode = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return false; + + var range = selection.createRange(), range2 = range.duplicate(); + range.collapse(start); + var around = range.parentElement(); + if (around && isAncestor(container, around)) { + // Only use this node if the selection is not at its start. + range2.moveToElementText(around); + if (range.compareEndPoints("StartToStart", range2) == 1) + return topLevelNodeAt(around, container); + } + + // 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) { + if (node.nodeType == 3) { + var count = 0, cur = node.previousSibling; + while (cur && cur.nodeType == 3) { + count += cur.nodeValue.length; + cur = cur.previousSibling; + } + if (cur) { + try{range.moveToElementText(cur);} + catch(e){alert(cur + " " + cur.nodeType + " " + (cur && cur.outerHTML));} + range.collapse(false); + } + else range.moveToElementText(node.parentNode); + if (count) range.move("character", count); + } + else range.moveToElementText(node); + } + + // 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 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 (range.compareEndPoints("StartToStart", range2) == 1) + start = middle; + else + end = middle - 1; + } + return container.childNodes[start] || null; + }; + + // Place the cursor after this.start. This is only useful when + // manually moving the cursor instead of restoring it to its old + // position. + select.focusAfterNode = function(node, container) { + var range = container.ownerDocument.body.createTextRange(); + range.moveToElementText(node || container); + range.collapse(!node); + range.select(); + }; + + select.somethingSelected = function(win) { + var sel = win.document.selection; + return sel && (sel.createRange().text != ""); + }; + + function insertAtCursor(window, html) { + var selection = window.document.selection; + if (selection) { + var range = selection.createRange(); + range.pasteHTML(html); + range.collapse(false); + range.select(); + } + } + + // Used to normalize the effect of the enter key, since browsers + // do widely different things when pressing enter in designMode. + select.insertNewlineAtCursor = function(window) { + insertAtCursor(window, "
"); + }; + + select.insertTabAtCursor = function(window) { + insertAtCursor(window, fourSpaces); + }; + + // Get the BR node at the start of the line on which the cursor + // currently is, and the offset into the line. Returns null as + // node if cursor is on first line. + select.cursorPos = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return null; + + var topNode = select.selectionTopNode(container, start); + while (topNode && topNode.nodeName != "BR") + topNode = topNode.previousSibling; + + var range = selection.createRange(), range2 = range.duplicate(); + range.collapse(start); + if (topNode) { + range2.moveToElementText(topNode); + range2.collapse(false); + } + else { + // When nothing is selected, we can get all kinds of funky errors here. + try { range2.moveToElementText(container); } + catch (e) { return null; } + range2.collapse(true); + } + range.setEndPoint("StartToStart", range2); + + return {node: topNode, offset: range.text.length}; + }; + + select.setCursorPos = function(container, from, to) { + function rangeAt(pos) { + var range = container.ownerDocument.body.createTextRange(); + if (!pos.node) { + range.moveToElementText(container); + range.collapse(true); + } + else { + range.moveToElementText(pos.node); + range.collapse(false); + } + range.move("character", pos.offset); + return range; + } + + var range = rangeAt(from); + if (to && to != from) + range.setEndPoint("EndToEnd", rangeAt(to)); + range.select(); + } + + // Some hacks for storing and re-storing the selection when the editor loses and regains focus. + select.selectionCoords = function (win) { + var selection = win.document.selection; + if (!selection) return null; + var start = selection.createRange(), end = start.duplicate(); + start.collapse(true); + end.collapse(false); + + var body = win.document.body; + return {start: {x: start.boundingLeft + body.scrollLeft - 1, + y: start.boundingTop + body.scrollTop}, + end: {x: end.boundingLeft + body.scrollLeft - 1, + y: end.boundingTop + body.scrollTop}}; + }; + + // Restore a stored selection. + select.selectCoords = function(win, coords) { + if (!coords) return; + + var range1 = win.document.body.createTextRange(), range2 = range1.duplicate(); + // This can fail for various hard-to-handle reasons. + try { + range1.moveToPoint(coords.start.x, coords.start.y); + range2.moveToPoint(coords.end.x, coords.end.y); + range1.setEndPoint("EndToStart", range2); + range1.select(); + } catch(e) {} + }; + } + // W3C model + else { + // Store start and end nodes, and offsets within these, and refer + // back to the selection object from those nodes, so that this + // object can be updated when the nodes are replaced before the + // selection is restored. + select.markSelection = function (win) { + var selection = win.getSelection(); + if (!selection || selection.rangeCount == 0) + return (currentSelection = null); + var range = selection.getRangeAt(0); + + currentSelection = { + start: {node: range.startContainer, offset: range.startOffset}, + end: {node: range.endContainer, offset: range.endOffset}, + window: win, + changed: false + }; + + // We want the nodes right at the cursor, not one of their + // 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") { + var newNode = point.node.childNodes[point.offset] || point.node.nextSibling; + point.offset = 0; + while (!newNode && point.node.parentNode) { + point.node = point.node.parentNode; + newNode = point.node.nextSibling; + } + point.node = newNode; + if (!newNode) + break; + } + } + + normalize(currentSelection.start); + normalize(currentSelection.end); + }; + + select.selectMarked = function () { + if (!currentSelection || !currentSelection.changed) return; + var win = currentSelection.window, range = win.document.createRange(); + + function setPoint(point, which) { + if (point.node) { + // Some magic to generalize the setting of the start and end + // of a range. + if (point.offset == 0) + range["set" + which + "Before"](point.node); + else + range["set" + which](point.node, point.offset); + } + else { + range.setStartAfter(win.document.body.lastChild || win.document.body); + } + } + + setPoint(currentSelection.end, "End"); + setPoint(currentSelection.start, "Start"); + selectRange(range, win); + }; + + // Helper for selecting a range object. + function selectRange(range, window) { + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + }; + function selectionRange(window) { + var selection = window.getSelection(); + if (!selection || selection.rangeCount == 0) + return false; + else + return selection.getRangeAt(0); + } + + // Finding the top-level node at the cursor in the W3C is, as you + // can see, quite an involved process. + select.selectionTopNode = function(container, start) { + var range = selectionRange(container.ownerDocument.defaultView); + if (!range) return false; + + var node = start ? range.startContainer : range.endContainer; + 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") + offset--; + + // For text nodes, we look at the node itself if the cursor is + // inside, or at the node before it if the cursor is at the + // start. + if (node.nodeType == 3){ + if (offset > 0) + return topLevelNodeAt(node, container); + else + return topLevelNodeBefore(node, container); + } + // 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") { + return (offset == 1 ? null : container.lastChild); + } + // If the given node is our 'container', we just look up the + // correct node by using the offset. + else if (node == container) { + return (offset == 0) ? null : node.childNodes[offset - 1]; + } + // In any other case, we have a regular node. If the cursor is + // at the end of the node, we use the node itself, if it is at + // the start, we use the node before it, and in any other + // case, we look up the child before the cursor and use that. + else { + if (offset == node.childNodes.length) + return topLevelNodeAt(node, container); + else if (offset == 0) + return topLevelNodeBefore(node, container); + else + return topLevelNodeAt(node.childNodes[offset - 1], container); + } + }; + + select.focusAfterNode = function(node, container) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + range.setStartBefore(container.firstChild || container); + // In Opera, setting the end of a range at the end of a line + // (before a BR) will cause the cursor to appear on the next + // line, so we set the end inside of the start node when + // possible. + if (node && !node.firstChild) + range.setEndAfter(node); + else if (node) + range.setEnd(node, node.childNodes.length); + else + range.setEndBefore(container.firstChild || container); + range.collapse(false); + selectRange(range, win); + }; + + select.somethingSelected = function(win) { + var range = selectionRange(win); + return range && !range.collapsed; + }; + + function insertNodeAtCursor(window, node) { + var range = selectionRange(window); + if (!range) return; + + range.deleteContents(); + range.insertNode(node); + webkitLastLineHack(window.document.body); + range = window.document.createRange(); + range.selectNode(node); + range.collapse(false); + selectRange(range, window); + } + + select.insertNewlineAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createElement("BR")); + }; + + select.insertTabAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createTextNode(fourSpaces)); + }; + + select.cursorPos = function(container, start) { + var range = selectionRange(window); + if (!range) return; + + var topNode = select.selectionTopNode(container, start); + while (topNode && topNode.nodeName != "BR") + topNode = topNode.previousSibling; + + range = range.cloneRange(); + range.collapse(start); + if (topNode) + range.setStartAfter(topNode); + else + range.setStartBefore(container); + return {node: topNode, offset: range.toString().length}; + }; + + select.setCursorPos = function(container, from, to) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + + function setPoint(node, offset, side) { + if (!node) + node = container.firstChild; + else + node = node.nextSibling; + + if (!node) + return; + + if (offset == 0) { + range["set" + side + "Before"](node); + return true; + } + + var backlog = [] + function decompose(node) { + if (node.nodeType == 3) + backlog.push(node); + else + forEach(node.childNodes, decompose); + } + while (true) { + while (node && !backlog.length) { + decompose(node); + node = node.nextSibling; + } + var cur = backlog.shift(); + if (!cur) return false; + + var length = cur.nodeValue.length; + if (length >= offset) { + range["set" + side](cur, offset); + return true; + } + offset -= length; + } + } + + to = to || from; + if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start")) + selectRange(range, win); + }; + } +})(); diff --git a/project/static/js/codemirror/stringstream.js b/project/static/js/codemirror/stringstream.js new file mode 100644 index 00000000..6d9355f4 --- /dev/null +++ b/project/static/js/codemirror/stringstream.js @@ -0,0 +1,140 @@ +/* String streams are the things fed to parsers (which can feed them + * to a tokenizer if they want). They provide peek and next methods + * for looking at the current character (next 'consumes' this + * character, peek does not), and a get method for retrieving all the + * text that was consumed since the last time get was called. + * + * 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. + */ + +// 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. +window.stringStream = function(source){ + // String that's currently being iterated over. + var current = ""; + // Position in that string. + var pos = 0; + // Accumulator for strings that have been iterated over but not + // get()-ed yet. + var accum = ""; + // Make sure there are more characters ready, or throw + // StopIteration. + function ensureChars() { + while (pos == current.length) { + accum += current; + current = ""; // In case source.next() throws + pos = 0; + try {current = source.next();} + catch (e) { + if (e != StopIteration) throw e; + else return false; + } + } + return true; + } + + return { + // Return the next character in the stream. + peek: function() { + if (!ensureChars()) return null; + return current.charAt(pos); + }, + // Get the next character, throw StopIteration if at end, check + // for unused content. + next: function() { + if (!ensureChars()) { + if (accum.length > 0) + throw "End of stringstream reached without emptying buffer ('" + accum + "')."; + else + throw StopIteration; + } + return current.charAt(pos++); + }, + // Return the characters iterated over since the last call to + // .get(). + get: function() { + var temp = accum; + accum = ""; + if (pos > 0){ + temp += current.slice(0, pos); + current = current.slice(pos); + pos = 0; + } + return temp; + }, + // Push a string back into the stream. + push: function(str) { + current = current.slice(0, pos) + str + current.slice(pos); + }, + lookAhead: function(str, consume, skipSpaces, caseInsensitive) { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + str = cased(str); + var found = false; + + var _accum = accum, _pos = pos; + if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/); + + while (true) { + var end = pos + str.length, left = current.length - pos; + if (end <= current.length) { + found = str == cased(current.slice(pos, end)); + pos = end; + break; + } + else if (str.slice(0, left) == cased(current.slice(pos))) { + accum += current; current = ""; + try {current = source.next();} + catch (e) {break;} + pos = 0; + str = str.slice(left); + } + else { + break; + } + } + + if (!(found && consume)) { + current = accum.slice(_accum.length) + current; + pos = _pos; + accum = _accum; + } + + return found; + }, + + // Utils built on top of the above + more: function() { + return this.peek() !== null; + }, + applies: function(test) { + var next = this.peek(); + return (next !== null && test(next)); + }, + nextWhile: function(test) { + var next; + while ((next = this.peek()) !== null && test(next)) + this.next(); + }, + matches: function(re) { + var next = this.peek(); + return (next !== null && re.test(next)); + }, + nextWhileMatches: function(re) { + var next; + while ((next = this.peek()) !== null && re.test(next)) + this.next(); + }, + equals: function(ch) { + return ch === this.peek(); + }, + endOfLine: function() { + var next = this.peek(); + return next == null || next == "\n"; + } + }; +}; diff --git a/project/static/js/codemirror/tokenize.js b/project/static/js/codemirror/tokenize.js new file mode 100644 index 00000000..071970ce --- /dev/null +++ b/project/static/js/codemirror/tokenize.js @@ -0,0 +1,57 @@ +// A framework for simple tokenizers. Takes care of newlines and +// white-space, and of getting the text from the source stream into +// the token object. A state is a function of two arguments -- a +// string stream and a setState function. The second can be used to +// change the tokenizer's state, and can be ignored for stateless +// tokenizers. This function should advance the stream over a token +// and return a string or object containing information about the next +// token, or null to pass and have the (new) state be called to finish +// the token. When a string is given, it is wrapped in a {style, type} +// object. In the resulting object, the characters consumed are stored +// under the content property. Any whitespace following them is also +// automatically consumed, and added to the value property. (Thus, +// content is the actual meaningful part of the token, while value +// contains all the text it spans.) + +function tokenizer(source, state) { + // Newlines are always a separate token. + function isWhiteSpace(ch) { + // The messy regexp is because IE's regexp matcher is of the + // opinion that non-breaking spaces are no whitespace. + return ch != "\n" && /^[\s\u00a0]*$/.test(ch); + } + + var tokenizer = { + state: state, + + take: function(type) { + if (typeof(type) == "string") + type = {style: type, type: type}; + + type.content = (type.content || "") + source.get(); + if (!/\n$/.test(type.content)) + source.nextWhile(isWhiteSpace); + type.value = type.content + source.get(); + return type; + }, + + next: function () { + if (!source.more()) throw StopIteration; + + var type; + if (source.equals("\n")) { + source.next(); + return this.take("whitespace"); + } + + if (source.applies(isWhiteSpace)) + type = "whitespace"; + else + while (!type) + type = this.state(source, function(s) {tokenizer.state = s;}); + + return this.take(type); + } + }; + return tokenizer; +} diff --git a/project/static/js/codemirror/undo.js b/project/static/js/codemirror/undo.js new file mode 100644 index 00000000..1930cfbd --- /dev/null +++ b/project/static/js/codemirror/undo.js @@ -0,0 +1,403 @@ +/** + * Storage and control for undo information within a CodeMirror + * editor. 'Why on earth is such a complicated mess required for + * that?', I hear you ask. The goal, in implementing this, was to make + * the complexity of storing and reverting undo information depend + * only on the size of the edited or restored content, not on the size + * of the whole document. This makes it necessary to use a kind of + * 'diff' system, which, when applied to a DOM tree, causes some + * complexity and hackery. + * + * In short, the editor 'touches' BR elements as it parses them, and + * the History stores these. When nothing is touched in commitDelay + * milliseconds, the changes are committed: It goes over all touched + * nodes, throws out the ones that did not change since last commit or + * are no longer in the document, and assembles the rest into zero or + * more 'chains' -- arrays of adjacent lines. Links back to these + * chains are added to the BR nodes, while the chain that previously + * spanned these nodes is added to the undo history. Undoing a change + * means taking such a chain off the undo history, restoring its + * content (text is saved per line) and linking it back into the + * document. + */ + +// A history object needs to know about the DOM container holding the +// document, the maximum amount of undo levels it should store, the +// delay (of no input) after which it commits a set of changes, and, +// unfortunately, the 'parent' window -- a window that is not in +// designMode, and on which setTimeout works in every browser. +function History(container, maxDepth, commitDelay, editor, onChange) { + this.container = container; + this.maxDepth = maxDepth; this.commitDelay = commitDelay; + this.editor = editor; this.parent = editor.parent; + this.onChange = onChange; + // This line object represents the initial, empty editor. + var initial = {text: "", from: null, to: null}; + // As the borders between lines are represented by BR elements, the + // start of the first line and the end of the last one are + // represented by null. Since you can not store any properties + // (links to line objects) in null, these properties are used in + // those cases. + this.first = initial; this.last = initial; + // Similarly, a 'historyTouched' property is added to the BR in + // front of lines that have already been touched, and 'firstTouched' + // is used for the first line. + this.firstTouched = false; + // History is the set of committed changes, touched is the set of + // nodes touched since the last commit. + this.history = []; this.redoHistory = []; this.touched = []; +} + +History.prototype = { + // Schedule a commit (if no other touches come in for commitDelay + // milliseconds). + scheduleCommit: function() { + var self = this; + this.parent.clearTimeout(this.commitTimeout); + this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay); + }, + + // Mark a node as touched. Null is a valid argument. + touch: function(node) { + this.setTouched(node); + this.scheduleCommit(); + }, + + // Undo the last change. + undo: function() { + // Make sure pending changes have been committed. + this.commit(); + + if (this.history.length) { + // Take the top diff from the history, apply it, and store its + // shadow in the redo history. + var item = this.history.pop(); + this.redoHistory.push(this.updateTo(item, "applyChain")); + if (this.onChange) this.onChange(); + return this.chainNode(item); + } + }, + + // Redo the last undone change. + redo: function() { + this.commit(); + if (this.redoHistory.length) { + // The inverse of undo, basically. + var item = this.redoHistory.pop(); + this.addUndoLevel(this.updateTo(item, "applyChain")); + if (this.onChange) this.onChange(); + return this.chainNode(item); + } + }, + + clear: function() { + this.history = []; + this.redoHistory = []; + }, + + // Ask for the size of the un/redo histories. + historySize: function() { + return {undo: this.history.length, redo: this.redoHistory.length}; + }, + + // Push a changeset into the document. + push: function(from, to, lines) { + var chain = []; + for (var i = 0; i < lines.length; i++) { + var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR"); + chain.push({from: from, to: end, text: cleanText(lines[i])}); + from = end; + } + this.pushChains([chain], from == null && to == null); + }, + + pushChains: function(chains, doNotHighlight) { + this.commit(doNotHighlight); + this.addUndoLevel(this.updateTo(chains, "applyChain")); + this.redoHistory = []; + }, + + // Retrieve a DOM node from a chain (for scrolling to it after undo/redo). + chainNode: function(chains) { + for (var i = 0; i < chains.length; i++) { + var start = chains[i][0], node = start && (start.from || start.to); + if (node) return node; + } + }, + + // Clear the undo history, make the current document the start + // position. + reset: function() { + this.history = []; this.redoHistory = []; + }, + + textAfter: function(br) { + return this.after(br).text; + }, + + nodeAfter: function(br) { + return this.after(br).to; + }, + + nodeBefore: function(br) { + return this.before(br).from; + }, + + // 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(); + else this.scheduleCommit(); + }, + + // Check whether the touched nodes hold any changes, if so, commit + // them. + commit: function(doNotHighlight) { + this.parent.clearTimeout(this.commitTimeout); + // Make sure there are no pending dirty nodes. + if (!doNotHighlight) this.editor.highlightDirty(true); + // Build set of chains. + var chains = this.touchedChains(), self = this; + + if (chains.length) { + this.addUndoLevel(this.updateTo(chains, "linkChain")); + this.redoHistory = []; + if (this.onChange) this.onChange(); + } + }, + + // [ end of public interface ] + + // Update the document with a given set of chains, return its + // shadow. updateFunc should be "applyChain" or "linkChain". In the + // second case, the chains are taken to correspond the the current + // document, and only the state of the line data is updated. In the + // first case, the content of the chains is also pushed iinto the + // document. + updateTo: function(chains, updateFunc) { + var shadows = [], dirty = []; + for (var i = 0; i < chains.length; i++) { + shadows.push(this.shadowChain(chains[i])); + dirty.push(this[updateFunc](chains[i])); + } + if (updateFunc == "applyChain") + this.notifyDirty(dirty); + return shadows; + }, + + // Notify the editor that some nodes have changed. + notifyDirty: function(nodes) { + forEach(nodes, method(this.editor, "addDirtyNode")) + this.editor.scheduleHighlight(); + }, + + // Link a chain into the DOM nodes (or the first/last links for null + // nodes). + linkChain: function(chain) { + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + if (line.from) line.from.historyAfter = line; + else this.first = line; + if (line.to) line.to.historyBefore = line; + else this.last = line; + } + }, + + // Get the line object after/before a given node. + after: function(node) { + return node ? node.historyAfter : this.first; + }, + before: function(node) { + return node ? node.historyBefore : this.last; + }, + + // Mark a node as touched if it has not already been marked. + setTouched: function(node) { + if (node) { + if (!node.historyTouched) { + this.touched.push(node); + node.historyTouched = true; + } + } + else { + this.firstTouched = true; + } + }, + + // Store a new set of undo info, throw away info if there is more of + // it than allowed. + addUndoLevel: function(diffs) { + this.history.push(diffs); + if (this.history.length > this.maxDepth) + this.history.shift(); + }, + + // Build chains from a set of touched nodes. + touchedChains: function() { + var self = this; + + // The temp system is a crummy hack to speed up determining + // whether a (currently touched) node has a line object associated + // with it. nullTemp is used to store the object for the first + // line, other nodes get it stored in their historyTemp property. + var nullTemp = null; + function temp(node) {return node ? node.historyTemp : nullTemp;} + function setTemp(node, line) { + if (node) node.historyTemp = line; + else nullTemp = line; + } + + function buildLine(node) { + var text = []; + for (var cur = node ? node.nextSibling : self.container.firstChild; + cur && cur.nodeName != "BR"; cur = cur.nextSibling) + if (cur.currentText) text.push(cur.currentText); + return {from: node, to: cur, text: cleanText(text.join(""))}; + } + + // Filter out unchanged lines and nodes that are no longer in the + // document. Build up line objects for remaining nodes. + var lines = []; + if (self.firstTouched) self.touched.push(null); + forEach(self.touched, function(node) { + if (node && node.parentNode != self.container) return; + + if (node) node.historyTouched = false; + else self.firstTouched = false; + + var line = buildLine(node), shadow = self.after(node); + if (!shadow || shadow.text != line.text || shadow.to != line.to) { + lines.push(line); + setTemp(node, line); + } + }); + + // 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") + search = search[link]; + return search; + } + + // Assemble line objects into chains by scanning the DOM tree + // around them. + var chains = []; self.touched = []; + forEach(lines, function(line) { + // Note that this makes the loop skip line objects that have + // been pulled into chains by lines before them. + if (!temp(line.from)) return; + + var chain = [], curNode = line.from, safe = true; + // Put any line objects (referred to by temp info) before this + // one on the front of the array. + while (true) { + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.unshift(curLine); + setTemp(curNode, null); + if (!curNode) break; + safe = self.after(curNode); + curNode = nextBR(curNode, "previous"); + } + curNode = line.to; safe = self.before(line.from); + // Add lines after this one at end of array. + while (true) { + if (!curNode) break; + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.push(curLine); + setTemp(curNode, null); + safe = self.before(curNode); + curNode = nextBR(curNode, "next"); + } + chains.push(chain); + }); + + return chains; + }, + + // Find the 'shadow' of a given chain by following the links in the + // DOM nodes at its start and end. + shadowChain: function(chain) { + var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to; + while (true) { + shadows.push(next); + var nextNode = next.to; + if (!nextNode || nextNode == end) + break; + else + next = nextNode.historyAfter || this.before(end); + // (The this.before(end) is a hack -- FF sometimes removes + // properties from BR nodes, in which case the best we can hope + // for is to not break.) + } + return shadows; + }, + + // Update the DOM tree to contain the lines specified in a given + // chain, link this chain into the DOM nodes. + applyChain: function(chain) { + // Some attempt is made to prevent the cursor from jumping + // randomly when an undo or redo happens. It still behaves a bit + // strange sometimes. + var cursor = select.cursorPos(this.container, false), self = this; + + // Remove all nodes in the DOM tree between from and to (null for + // start/end of container). + function removeRange(from, to) { + var pos = from ? from.nextSibling : self.container.firstChild; + while (pos != to) { + var temp = pos.nextSibling; + removeElement(pos); + pos = temp; + } + } + + var start = chain[0].from, end = chain[chain.length - 1].to; + // Clear the space where this change has to be made. + removeRange(start, end); + + // Insert the content specified by the chain into the DOM tree. + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + // The start and end of the space are already correct, but BR + // tags inside it have to be put back. + if (i > 0) + self.container.insertBefore(line.from, end); + + // Add the text. + var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument); + self.container.insertBefore(node, end); + // See if the cursor was on this line. Put it back, adjusting + // for changed line length, if it was. + if (cursor && cursor.node == line.from) { + var cursordiff = 0; + var prev = this.after(line.from); + if (prev && i == chain.length - 1) { + // Only adjust if the cursor is after the unchanged part of + // the line. + for (var match = 0; match < cursor.offset && + line.text.charAt(match) == prev.text.charAt(match); match++); + if (cursor.offset > match) + cursordiff = line.text.length - prev.text.length; + } + select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)}); + } + // Cursor was in removed line, this is last new line. + else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) { + select.setCursorPos(this.container, {node: line.from, offset: line.text.length}); + } + } + + // Anchor the chain in the DOM tree. + this.linkChain(chain); + return start; + } +}; diff --git a/project/static/js/codemirror/util.js b/project/static/js/codemirror/util.js new file mode 100644 index 00000000..a1390a91 --- /dev/null +++ b/project/static/js/codemirror/util.js @@ -0,0 +1,116 @@ +/* A few useful utility functions. */ + +var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent); +var webkit = /AppleWebKit/.test(navigator.userAgent); +var safari = /Apple Computers, Inc/.test(navigator.vendor); + +// Capture a method on an object. +function method(obj, name) { + return function() {obj[name].apply(obj, arguments);}; +} + +// The value used to signal the end of a sequence in iterators. +var StopIteration = {toString: function() {return "StopIteration"}}; + +// Apply a function to each element in a sequence. +function forEach(iter, f) { + if (iter.next) { + try {while (true) f(iter.next());} + catch (e) {if (e != StopIteration) throw e;} + } + else { + for (var i = 0; i < iter.length; i++) + f(iter[i]); + } +} + +// Map a function over a sequence, producing an array of results. +function map(iter, f) { + var accum = []; + forEach(iter, function(val) {accum.push(f(val));}); + return accum; +} + +// Create a predicate function that tests a string againsts a given +// regular expression. No longer used but might be used by 3rd party +// parsers. +function matcher(regexp){ + return function(value){return regexp.test(value);}; +} + +// Test whether a DOM node has a certain CSS class. Much faster than +// the MochiKit equivalent, for some reason. +function hasClass(element, className){ + var classes = element.className; + return classes && new RegExp("(^| )" + className + "($| )").test(classes); +} + +// Insert a DOM node after another node. +function insertAfter(newNode, oldNode) { + var parent = oldNode.parentNode; + parent.insertBefore(newNode, oldNode.nextSibling); + return newNode; +} + +function removeElement(node) { + if (node.parentNode) + node.parentNode.removeChild(node); +} + +function clearElement(node) { + while (node.firstChild) + node.removeChild(node.firstChild); +} + +// Check whether a node is contained in another one. +function isAncestor(node, child) { + while (child = child.parentNode) { + if (node == child) + return true; + } + return false; +} + +// The non-breaking space character. +var nbsp = "\u00a0"; +var matching = {"{": "}", "[": "]", "(": ")", + "}": "{", "]": "[", ")": "("}; + +// Standardize a few unportable event properties. +function normalizeEvent(event) { + if (!event.stopPropagation) { + event.stopPropagation = function() {this.cancelBubble = true;}; + event.preventDefault = function() {this.returnValue = false;}; + } + if (!event.stop) { + event.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + } + + if (event.type == "keypress") { + event.code = (event.charCode == null) ? event.keyCode : event.charCode; + event.character = String.fromCharCode(event.code); + } + return event; +} + +// Portably register event handlers. +function addEventHandler(node, type, handler, removeFunc) { + function wrapHandler(event) { + handler(normalizeEvent(event || window.event)); + } + if (typeof node.addEventListener == "function") { + node.addEventListener(type, wrapHandler, false); + if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);}; + } + else { + node.attachEvent("on" + type, wrapHandler); + if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);}; + } +} + +function nodeText(node) { + return node.innerText || node.textContent || node.nodeValue || ""; +} diff --git a/project/static/js/edit_area.css b/project/static/js/edit_area.css deleted file mode 100755 index 172b366f..00000000 --- a/project/static/js/edit_area.css +++ /dev/null @@ -1,530 +0,0 @@ -body, html{ - margin: 0; - padding: 0; - height: 100%; - border: none; - overflow: hidden; - background-color: #FFF; -} - -body, html, table, form, textarea{ - font: 12px monospace, sans-serif; -} - -#editor{ - border: solid #888 1px; - overflow: hidden; -} - -#result{ - z-index: 4; - overflow-x: auto; - overflow-y: scroll; - border-top: solid #888 1px; - border-bottom: solid #888 1px; - position: relative; - clear: both; -} - -#result.empty{ - overflow: hidden; -} - -#container{ - overflow: hidden; - border: solid blue 0; - position: relative; - z-index: 10; - padding: 0 5px 0 45px; - /*padding-right: 5px;*/ -} - -#textarea{ - position: relative; - top: 0; - left: 0; - margin: 0; - padding: 0; - width: 100%; - height: 100%; - overflow: hidden; - z-index: 7; - border-width: 0; - background-color: transparent; - resize: none; -} - -#textarea, #textarea:hover{ - outline: none; /* safari outline fix */ -} - -#content_highlight{ - white-space: pre; - margin: 0; - padding: 0; - position : absolute; - z-index: 4; - overflow: visible; -} - - -#selection_field, #selection_field_text{ - margin: 0; - background-color: #E1F2F9; -/* height: 1px; */ - position: absolute; - z-index: 5; - top: -100px; - padding: 0; - white-space: pre; - overflow: hidden; -} - -#selection_field.show_colors { - z-index: 3; - background-color:#EDF9FC; - -} - -#selection_field strong{ - font-weight:normal; -} - -#selection_field.show_colors *, #selection_field_text * { - visibility: hidden; -} - -#selection_field_text{ - background-color:transparent; -} - -#selection_field_text strong{ - font-weight:normal; - background-color:#3399FE; - color: #FFF; - visibility:visible; -} - -#container.word_wrap #content_highlight, -#container.word_wrap #selection_field, -#container.word_wrap #selection_field_text, -#container.word_wrap #test_font_size{ - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - width: 99%; -} - -#line_number{ - position: absolute; - overflow: hidden; - border-right: solid black 1px; - z-index:8; - width: 38px; - padding: 0 5px 0 0; - margin: 0 0 0 -45px; - text-align: right; - color: #AAAAAA; -} - -#test_font_size{ - padding: 0; - margin: 0; - visibility: hidden; - position: absolute; - white-space: pre; -} - -pre{ - margin: 0; - padding: 0; -} - -.hidden{ - opacity: 0.2; - filter:alpha(opacity=20); -} - -#result .edit_area_cursor{ - position: absolute; - z-index:6; - background-color: #FF6633; - top: -100px; - margin: 0; -} - -#result .edit_area_selection_field .overline{ - background-color: #996600; -} - - -/* area popup */ -.editarea_popup{ - border: solid 1px #888888; - background-color: #ECE9D8; - width: 250px; - padding: 4px; - position: absolute; - visibility: hidden; - z-index: 15; - top: -500px; -} - -.editarea_popup, .editarea_popup table{ - font-family: sans-serif; - font-size: 10pt; -} - -.editarea_popup img{ - border: 0; -} - -.editarea_popup .close_popup{ - float: right; - line-height: 16px; - border: 0; - padding: 0; -} - -.editarea_popup h1,.editarea_popup h2,.editarea_popup h3,.editarea_popup h4,.editarea_popup h5,.editarea_popup h6{ - margin: 0; - padding: 0; -} - -.editarea_popup .copyright{ - text-align: right; -} - -/* Area_search */ -div#area_search_replace{ - /*width: 250px;*/ -} - -div#area_search_replace img{ - border: 0; -} - -div#area_search_replace div.button{ - text-align: center; - line-height: 1.7em; -} - -div#area_search_replace .button a{ - cursor: pointer; - border: solid 1px #888888; - background-color: #DEDEDE; - text-decoration: none; - padding: 0 2px; - color: #000000; - white-space: nowrap; -} - -div#area_search_replace a:hover{ - /*border: solid 1px #888888;*/ - background-color: #EDEDED; -} - -div#area_search_replace #move_area_search_replace{ - cursor: move; - border: solid 1px #888; -} - -div#area_search_replace #close_area_search_replace{ - text-align: right; - vertical-align: top; - white-space: nowrap; -} - -div#area_search_replace #area_search_msg{ - height: 18px; - overflow: hidden; - border-top: solid 1px #888; - margin-top: 3px; -} - -/* area help */ -#edit_area_help{ - width: 350px; -} - -#edit_area_help div.close_popup{ - float: right; -} - -/* area_toolbar */ -.area_toolbar{ - /*font: 11px sans-serif;*/ - width: 100%; - /*height: 21px; */ - margin: 0; - padding: 0; - background-color: #ECE9D8; - text-align: center; -} - -.area_toolbar, .area_toolbar table{ - font: 11px sans-serif; -} - -.area_toolbar img{ - border: 0; - vertical-align: middle; -} - -.area_toolbar input{ - margin: 0; - padding: 0; -} - -.area_toolbar select{ - font-family: 'MS Sans Serif',sans-serif,Verdana,Arial; - font-size: 7pt; - font-weight: normal; - margin: 2px 0 0 0 ; - padding: 0; - vertical-align: top; - background-color: #F0F0EE; -} - -table.statusbar{ - width: 100%; -} - -.area_toolbar td.infos{ - text-align: center; - width: 130px; - border-right: solid 1px #888; - border-width: 0 1px 0 0; - padding: 0; -} - -.area_toolbar td.total{ - text-align: right; - width: 50px; - padding: 0; -} - -.area_toolbar td.resize{ - text-align: right; -} -/* -.area_toolbar span{ - line-height: 1px; - padding: 0; - margin: 0; -}*/ - -.area_toolbar span#resize_area{ - cursor: nw-resize; - visibility: hidden; -} - -/* toolbar buttons */ -.editAreaButtonNormal, .editAreaButtonOver, .editAreaButtonDown, .editAreaSeparator, .editAreaSeparatorLine, .editAreaButtonDisabled, .editAreaButtonSelected { - border: 0; margin: 0; padding: 0; background: transparent; - margin-top: 0; - margin-left: 1px; - padding: 0; -} - -.editAreaButtonNormal { - border: 1px solid #ECE9D8 !important; - cursor: pointer; -} - -.editAreaButtonOver { - border: 1px solid #0A246A !important; - cursor: pointer; - background-color: #B6BDD2; -} - -.editAreaButtonDown { - cursor: pointer; - border: 1px solid #0A246A !important; - background-color: #8592B5; -} - -.editAreaButtonSelected { - border: 1px solid #C0C0BB !important; - cursor: pointer; - background-color: #F4F2E8; -} - -.editAreaButtonDisabled { - filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); - -moz-opacity:0.3; - opacity: 0.3; - border: 1px solid #F0F0EE !important; - cursor: pointer; -} - -.editAreaSeparatorLine { - margin: 1px 2px; - background-color: #C0C0BB; - width: 2px; - height: 18px; -} - -/* waiting screen */ -#processing{ - display: none; - background-color:#ECE9D8; - border: solid #888 1px; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 100; - text-align: center; -} - -#processing_text{ - position:absolute; - left: 50%; - top: 50%; - width: 200px; - height: 20px; - margin-left: -100px; - margin-top: -10px; - text-align: center; -} -/* end */ - - -/**** tab browsing area ****/ -#tab_browsing_area{ - display: none; - background-color: #CCC9A8; - border-top: 1px solid #888; - text-align: left; - margin: 0; -} - -#tab_browsing_list { - padding: 0; - margin: 0; - list-style-type: none; - white-space: nowrap; -} -#tab_browsing_list li { - float: left; - margin: -1px; -} -#tab_browsing_list a { - position: relative; - display: block; - text-decoration: none; - float: left; - cursor: pointer; - line-height:14px; -} - -#tab_browsing_list a span { - display: block; - color: #000; - background: #ECE9D8; - border: 1px solid #888; - border-width: 1px 1px 0; - text-align: center; - padding: 2px 2px 1px 4px; - position: relative; /*IE 6 hack */ -} - -#tab_browsing_list a b { - display: block; - border-bottom: 2px solid #617994; -} - -#tab_browsing_list a .edited { - display: none; -} - -#tab_browsing_list a.edited .edited { - display: inline; -} - -#tab_browsing_list a img{ - margin-left: 7px; -} - -#tab_browsing_list a.edited img{ - margin-left: 3px; -} - -#tab_browsing_list a:hover span { - background: #F4F2E8; - border-color: #0A246A; -} - -#tab_browsing_list .selected a span{ - background: #046380; - color: #FFF; -} - - -#no_file_selected{ - height: 100%; - width: 150%; /* Opera need more than 100% */ - background: #CCC; - display: none; - z-index: 20; - position: absolute; -} - - -/*** Non-editable mode ***/ -.non_editable #editor -{ - border-width: 0 1px; -} - -.non_editable .area_toolbar -{ - display: none; -} - -/*** Auto completion ***/ -#auto_completion_area -{ - background: #FFF; - border: solid 1px #888; - position: absolute; - z-index: 15; - width: 280px; - height: 180px; - overflow: auto; - display:none; -} - -#auto_completion_area a, #auto_completion_area a:visited -{ - display: block; - padding: 0 2px 1px; - color: #000; - text-decoration:none; -} - -#auto_completion_area a:hover, #auto_completion_area a:focus, #auto_completion_area a.focus -{ - background: #D6E1FE; - text-decoration:none; -} - -#auto_completion_area ul -{ - margin: 0; - padding: 0; - list-style: none inside; -} -#auto_completion_area li -{ - padding: 0; -} -#auto_completion_area .prefix -{ - font-style: italic; - padding: 0 3px; -} \ No newline at end of file diff --git a/project/static/js/edit_area.js b/project/static/js/edit_area.js deleted file mode 100755 index 27674f67..00000000 --- a/project/static/js/edit_area.js +++ /dev/null @@ -1,525 +0,0 @@ -/****** - * - * EditArea - * Developped by Christophe Dolivet - * Released under LGPL, Apache and BSD licenses (use the one you want) - * -******/ - - function EditArea(){ - var t=this; - t.error= false; // to know if load is interrrupt - - t.inlinePopup= [{popup_id: "area_search_replace", icon_id: "search"}, - {popup_id: "edit_area_help", icon_id: "help"}]; - t.plugins= {}; - - t.line_number=0; - - parent.editAreaLoader.set_browser_infos(t); // navigator identification - // fix IE8 detection as we run in IE7 emulate mode through X-UA tag - if( t.isIE >= 8 ) - t.isIE = 7; - - t.last_selection={}; - t.last_text_to_highlight=""; - t.last_hightlighted_text= ""; - t.syntax_list= []; - t.allready_used_syntax= {}; - t.check_line_selection_timer= 50; // the timer delay for modification and/or selection change detection - - t.textareaFocused= false; - t.highlight_selection_line= null; - t.previous= []; - t.next= []; - t.last_undo=""; - t.files= {}; - t.filesIdAssoc= {}; - t.curr_file= ''; - //t.loaded= false; - t.assocBracket={}; - t.revertAssocBracket= {}; - // bracket selection init - t.assocBracket["("]=")"; - t.assocBracket["{"]="}"; - t.assocBracket["["]="]"; - for(var index in t.assocBracket){ - t.revertAssocBracket[t.assocBracket[index]]=index; - } - t.is_editable= true; - - - /*t.textarea=""; - - t.state="declare"; - t.code = []; // store highlight syntax for languagues*/ - // font datas - t.lineHeight= 16; - /*t.default_font_family= "monospace"; - t.default_font_size= 10;*/ - t.tab_nb_char= 8; //nb of white spaces corresponding to a tabulation - if(t.isOpera) - t.tab_nb_char= 6; - - t.is_tabbing= false; - - t.fullscreen= {'isFull': false}; - - t.isResizing=false; // resize var - - // init with settings and ID (area_id is a global var defined by editAreaLoader on iframe creation - t.id= area_id; - t.settings= editAreas[t.id]["settings"]; - - if((""+t.settings['replace_tab_by_spaces']).match(/^[0-9]+$/)) - { - t.tab_nb_char= t.settings['replace_tab_by_spaces']; - t.tabulation=""; - for(var i=0; i0) - t.syntax_list= t.settings["syntax_selection_allow"].replace(/ /g,"").split(","); - - if(t.settings['syntax']) - t.allready_used_syntax[t.settings['syntax']]=true; - - - }; - EditArea.prototype.init= function(){ - var t=this, a, s=t.settings; - t.textarea = _$("textarea"); - t.container = _$("container"); - t.result = _$("result"); - t.content_highlight = _$("content_highlight"); - t.selection_field = _$("selection_field"); - t.selection_field_text= _$("selection_field_text"); - t.processing_screen = _$("processing"); - t.editor_area = _$("editor"); - t.tab_browsing_area = _$("tab_browsing_area"); - t.test_font_size = _$("test_font_size"); - a = t.textarea; - - if(!s['is_editable']) - t.set_editable(false); - - t.set_show_line_colors( s['show_line_colors'] ); - - if(syntax_selec= _$("syntax_selection")) - { - // set up syntax selection lsit in the toolbar - for(var i=0; i= '3' ) { - t.content_highlight.style.paddingLeft= "1px"; - t.selection_field.style.paddingLeft= "1px"; - t.selection_field_text.style.paddingLeft= "1px"; - } - - if(t.isIE && t.isIE < 8 ){ - a.style.marginTop= "-1px"; - } - /* - if(t.isOpera){ - t.editor_area.style.position= "absolute"; - }*/ - - if( t.isSafari ){ - t.editor_area.style.position = "absolute"; - a.style.marginLeft ="-3px"; - if( t.isSafari < 3.2 ) // Safari 3.0 (3.1?) - a.style.marginTop ="1px"; - } - - // si le textarea n'est pas grand, un click sous le textarea doit provoquer un focus sur le textarea - parent.editAreaLoader.add_event(t.result, "click", function(e){ if((e.target || e.srcElement)==editArea.result) { editArea.area_select(editArea.textarea.value.length, 0);} }); - - if(s['is_multi_files']!=false) - t.open_file({'id': t.curr_file, 'text': ''}); - - t.set_word_wrap( s['word_wrap'] ); - - setTimeout("editArea.focus();editArea.manage_size();editArea.execCommand('EA_load');", 10); - //start checkup routine - t.check_undo(); - t.check_line_selection(true); - t.scroll_to_view(); - - for(var i in t.plugins){ - if(typeof(t.plugins[i].onload)=="function") - t.plugins[i].onload(); - } - if(s['fullscreen']==true) - t.toggle_full_screen(true); - - parent.editAreaLoader.add_event(window, "resize", editArea.update_size); - parent.editAreaLoader.add_event(parent.window, "resize", editArea.update_size); - parent.editAreaLoader.add_event(top.window, "resize", editArea.update_size); - parent.editAreaLoader.add_event(window, "unload", function(){ - // in case where editAreaLoader have been already cleaned - if( parent.editAreaLoader ) - { - parent.editAreaLoader.remove_event(parent.window, "resize", editArea.update_size); - parent.editAreaLoader.remove_event(top.window, "resize", editArea.update_size); - } - if(editAreas[editArea.id] && editAreas[editArea.id]["displayed"]){ - editArea.execCommand("EA_unload"); - } - }); - - - /*date= new Date(); - alert(date.getTime()- parent.editAreaLoader.start_time);*/ - }; - - - - //called by the toggle_on - EditArea.prototype.update_size= function(){ - var d=document,pd=parent.document,height,width,popup,maxLeft,maxTop; - - if( typeof editAreas != 'undefined' && editAreas[editArea.id] && editAreas[editArea.id]["displayed"]==true){ - if(editArea.fullscreen['isFull']){ - pd.getElementById("frame_"+editArea.id).style.width = pd.getElementsByTagName("html")[0].clientWidth + "px"; - pd.getElementById("frame_"+editArea.id).style.height = pd.getElementsByTagName("html")[0].clientHeight + "px"; - } - - if(editArea.tab_browsing_area.style.display=='block' && ( !editArea.isIE || editArea.isIE >= 8 ) ) - { - editArea.tab_browsing_area.style.height = "0px"; - editArea.tab_browsing_area.style.height = (editArea.result.offsetTop - editArea.tab_browsing_area.offsetTop -1)+"px"; - } - - height = d.body.offsetHeight - editArea.get_all_toolbar_height() - 4; - editArea.result.style.height = height +"px"; - - width = d.body.offsetWidth -2; - editArea.result.style.width = width+"px"; - //alert("result h: "+ height+" w: "+width+"\ntoolbar h: "+this.get_all_toolbar_height()+"\nbody_h: "+document.body.offsetHeight); - - // check that the popups don't get out of the screen - for( i=0; i < editArea.inlinePopup.length; i++ ) - { - popup = _$(editArea.inlinePopup[i]["popup_id"]); - maxLeft = d.body.offsetWidth - popup.offsetWidth; - maxTop = d.body.offsetHeight - popup.offsetHeight; - if( popup.offsetTop > maxTop ) - popup.style.top = maxTop+"px"; - if( popup.offsetLeft > maxLeft ) - popup.style.left = maxLeft+"px"; - } - - editArea.manage_size( true ); - editArea.fixLinesHeight( editArea.textarea.value, 0,-1); - } - }; - - - EditArea.prototype.manage_size= function(onlyOneTime){ - if(!editAreas[this.id]) - return false; - - if(editAreas[this.id]["displayed"]==true && this.textareaFocused) - { - var area_height,resized= false; - - //1) Manage display width - //1.1) Calc the new width to use for display - if( !this.settings['word_wrap'] ) - { - var area_width= this.textarea.scrollWidth; - area_height= this.textarea.scrollHeight; - // bug on old opera versions - if(this.isOpera && this.isOpera < 9.6 ){ - area_width=10000; - } - //1.2) the width is not the same, we must resize elements - if(this.textarea.previous_scrollWidth!=area_width) - { - this.container.style.width= area_width+"px"; - this.textarea.style.width= area_width+"px"; - this.content_highlight.style.width= area_width+"px"; - this.textarea.previous_scrollWidth=area_width; - resized=true; - } - } - // manage wrap width - if( this.settings['word_wrap'] ) - { - newW=this.textarea.offsetWidth; - if( this.isFirefox || this.isIE ) - newW-=2; - if( this.isSafari ) - newW-=6; - this.content_highlight.style.width=this.selection_field_text.style.width=this.selection_field.style.width=this.test_font_size.style.width=newW+"px"; - } - - //2) Manage display height - //2.1) Calc the new height to use for display - if( this.isOpera || this.isFirefox || this.isSafari ) { - area_height= this.getLinePosTop( this.last_selection["nb_line"] + 1 ); - } else { - area_height = this.textarea.scrollHeight; - } - //2.2) the width is not the same, we must resize elements - if(this.textarea.previous_scrollHeight!=area_height) - { - this.container.style.height= (area_height+2)+"px"; - this.textarea.style.height= area_height+"px"; - this.content_highlight.style.height= area_height+"px"; - this.textarea.previous_scrollHeight= area_height; - resized=true; - } - - //3) if there is new lines, we add new line numbers in the line numeration area - if(this.last_selection["nb_line"] >= this.line_number) - { - var newLines= '', destDiv=_$("line_number"), start=this.line_number, end=this.last_selection["nb_line"]+100; - for( i = start+1; i < end; i++ ) - { - newLines+='
'+i+"
"; - this.line_number++; - } - destDiv.innerHTML= destDiv.innerHTML + newLines; - - this.fixLinesHeight( this.textarea.value, start, -1 ); - } - - //4) be sure the text is well displayed - this.textarea.scrollTop="0px"; - this.textarea.scrollLeft="0px"; - if(resized==true){ - this.scroll_to_view(); - } - } - - if(!onlyOneTime) - setTimeout("editArea.manage_size();", 100); - }; - - EditArea.prototype.execCommand= function(cmd, param){ - - for(var i in this.plugins){ - if(typeof(this.plugins[i].execCommand)=="function"){ - if(!this.plugins[i].execCommand(cmd, param)) - return; - } - } - switch(cmd){ - case "save": - if(this.settings["save_callback"].length>0) - eval("parent."+this.settings["save_callback"]+"('"+ this.id +"', editArea.textarea.value);"); - break; - case "load": - if(this.settings["load_callback"].length>0) - eval("parent."+this.settings["load_callback"]+"('"+ this.id +"');"); - break; - case "onchange": - if(this.settings["change_callback"].length>0) - eval("parent."+this.settings["change_callback"]+"('"+ this.id +"');"); - break; - case "EA_load": - if(this.settings["EA_load_callback"].length>0) - eval("parent."+this.settings["EA_load_callback"]+"('"+ this.id +"');"); - break; - case "EA_unload": - if(this.settings["EA_unload_callback"].length>0) - eval("parent."+this.settings["EA_unload_callback"]+"('"+ this.id +"');"); - break; - case "toggle_on": - if(this.settings["EA_toggle_on_callback"].length>0) - eval("parent."+this.settings["EA_toggle_on_callback"]+"('"+ this.id +"');"); - break; - case "toggle_off": - if(this.settings["EA_toggle_off_callback"].length>0) - eval("parent."+this.settings["EA_toggle_off_callback"]+"('"+ this.id +"');"); - break; - case "re_sync": - if(!this.do_highlight) - break; - case "file_switch_on": - if(this.settings["EA_file_switch_on_callback"].length>0) - eval("parent."+this.settings["EA_file_switch_on_callback"]+"(param);"); - break; - case "file_switch_off": - if(this.settings["EA_file_switch_off_callback"].length>0) - eval("parent."+this.settings["EA_file_switch_off_callback"]+"(param);"); - break; - case "file_close": - if(this.settings["EA_file_close_callback"].length>0) - return eval("parent."+this.settings["EA_file_close_callback"]+"(param);"); - break; - - default: - if(typeof(eval("editArea."+cmd))=="function") - { - if(this.settings["debug"]) - eval("editArea."+ cmd +"(param);"); - else - try{eval("editArea."+ cmd +"(param);");}catch(e){}; - } - } - }; - - EditArea.prototype.get_translation= function(word, mode){ - if(mode=="template") - return parent.editAreaLoader.translate(word, this.settings["language"], mode); - else - return parent.editAreaLoader.get_word_translation(word, this.settings["language"]); - }; - - EditArea.prototype.add_plugin= function(plug_name, plug_obj){ - for(var i=0; i"); - } - }; - - EditArea.prototype.load_script= function(url){ - try{ - script = document.createElement("script"); - script.type = "text/javascript"; - script.src = url; - script.charset= "UTF-8"; - head = document.getElementsByTagName("head"); - head[0].appendChild(script); - }catch(e){ - document.write("\";\n", $sub_scripts); - - - // add the script and use a last compression - if( $this->param['compress'] ) - { - $last_comp = array( 'Á' => 'this', - 'Â' => 'textarea', - 'Ã' => 'function', - 'Ä' => 'prototype', - 'Å' => 'settings', - 'Æ' => 'length', - 'Ç' => 'style', - 'È' => 'parent', - 'É' => 'last_selection', - 'Ê' => 'value', - 'Ë' => 'true', - 'Ì' => 'false' - /*, - 'Î' => '"', - 'Ï' => "\n", - 'À' => "\r"*/); - } - else - { - $last_comp = array(); - } - - $js_replace= ''; - foreach( $last_comp as $key => $val ) - $js_replace .= ".replace(/". $key ."/g,'". str_replace( array("\n", "\r"), array('\n','\r'), $val ) ."')"; - - $this->datas.= sprintf("editAreaLoader.iframe_script= \"\"%s;\n", - str_replace( array_values($last_comp), array_keys($last_comp), $sub_scripts ), - $js_replace); - - if($this->load_all_plugins) - $this->datas.="editAreaLoader.all_plugins_loaded=true;\n"; - - - // load the template - $this->datas.= sprintf("editAreaLoader.template= \"%s\";\n", $this->get_html_content("template.html")); - // load the css - $this->datas.= sprintf("editAreaLoader.iframe_css= \"\";\n", $this->get_css_content("edit_area.css")); - - // $this->datas= "function editArea(){};editArea.prototype.loader= function(){alert('bouhbouh');} var a= new editArea();a.loader();"; - - } - - function send_datas() - { - if($this->param['debug']){ - $header=sprintf("/* USE PHP COMPRESSION\n"); - $header.=sprintf("javascript size: based files: %s => PHP COMPRESSION => %s ", $this->file_loaded_size, strlen($this->datas)); - if($this->use_gzip){ - $gzip_datas= gzencode($this->datas, 9, FORCE_GZIP); - $header.=sprintf("=> GZIP COMPRESSION => %s", strlen($gzip_datas)); - $ratio = round(100 - strlen($gzip_datas) / $this->file_loaded_size * 100.0); - }else{ - $ratio = round(100 - strlen($this->datas) / $this->file_loaded_size * 100.0); - } - $header.=sprintf(", reduced by %s%%\n", $ratio); - $header.=sprintf("compression time: %s\n", $this->get_microtime()-$this->start_time); - $header.=sprintf("%s\n", implode("\n", $this->infos)); - $header.=sprintf("*/\n"); - $this->datas= $header.$this->datas; - } - $mtime= time(); // ensure that the 2 disk files will have the same update time - // generate gzip file and cahce it if using disk cache - if($this->use_gzip){ - $this->gzip_datas= gzencode($this->datas, 9, FORCE_GZIP); - if($this->param['use_disk_cache']) - $this->file_put_contents($this->gzip_cache_file, $this->gzip_datas, $mtime); - } - - // generate full js file and cache it if using disk cache - if($this->param['use_disk_cache']) - $this->file_put_contents($this->full_cache_file, $this->datas, $mtime); - - // generate output - if($this->use_gzip) - echo $this->gzip_datas; - else - echo $this->datas; - -// die; - } - - - function get_content($end_uri) - { - $end_uri=preg_replace("/\.\./", "", $end_uri); // Remove any .. (security) - $file= $this->path.$end_uri; - if(file_exists($file)){ - $this->infos[]=sprintf("'%s' loaded", $end_uri); - /*$fd = fopen($file, 'rb'); - $content = fread($fd, filesize($file)); - fclose($fd); - return $content;*/ - return $this->file_get_contents($file); - }else{ - $this->infos[]=sprintf("'%s' not loaded", $end_uri); - return ""; - } - } - - function get_javascript_content($end_uri) - { - $val=$this->get_content($end_uri); - - $this->compress_javascript($val); - $this->prepare_string_for_quotes($val); - return $val; - } - - function compress_javascript(&$code) - { - if($this->param['compress']) - { - // remove all comments - // (\"(?:[^\"\\]*(?:\\\\)*(?:\\\"?)?)*(?:\"|$))|(\'(?:[^\'\\]*(?:\\\\)*(?:\\'?)?)*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$)) - $code= preg_replace("/(\"(?:[^\"\\\\]*(?:\\\\\\\\)*(?:\\\\\"?)?)*(?:\"|$))|(\'(?:[^\'\\\\]*(?:\\\\\\\\)*(?:\\\\\'?)?)*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "$1$2$3", $code); - // remove line return, empty line and tabulation - $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', " ", $code); - // add line break before "else" otherwise navigators can't manage to parse the file - $code= preg_replace('/(\b(else)\b)/', "\n$1", $code); - // remove unnecessary spaces - $code= preg_replace('/( |\t|\r)*(;|\{|\}|=|==|\-|\+|,|\(|\)|\|\||&\&|\:)( |\t|\r)*/', "$2", $code); - } - } - - function get_css_content($end_uri){ - $code=$this->get_content($end_uri); - // remove comments - $code= preg_replace("/(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "", $code); - // remove spaces - $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', "", $code); - // remove spaces - $code= preg_replace('/( |\t|\r)?(\:|,|\{|\})( |\t|\r)+/', "$2", $code); - - $this->prepare_string_for_quotes($code); - return $code; - } - - function get_html_content($end_uri){ - $code=$this->get_content($end_uri); - //$code= preg_replace('/(\"(?:\\\"|[^\"])*(?:\"|$))|' . "(\'(?:\\\'|[^\'])*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "$1$2$3", $code); - $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', " ", $code); - $this->prepare_string_for_quotes($code); - return $code; - } - - function prepare_string_for_quotes(&$str){ - // prepare the code to be putted into quotes - /*$pattern= array("/(\\\\)?\"/", '/\\\n/' , '/\\\r/' , "/(\r?\n)/"); - $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , '\\\n"$1+"');*/ - $pattern= array("/(\\\\)?\"/", '/\\\n/' , '/\\\r/' , "/(\r?\n)/"); - if($this->param['compress']) - $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , '\n'); - else - $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , "\\n\"\n+\""); - $str= preg_replace($pattern, $replace, $str); - } - - function replace_scripts($var, $param1, $param2) - { - $this->$var=stripslashes($param2); - return $param1."[];"; - } - - /* for php version that have not thoses functions */ - function file_get_contents($file) - { - $fd = fopen($file, 'rb'); - $content = fread($fd, filesize($file)); - fclose($fd); - $this->file_loaded_size+= strlen($content); - return $content; - } - - function file_put_contents($file, &$content, $mtime=-1) - { - if($mtime==-1) - $mtime=time(); - $fp = @fopen($file, "wb"); - if ($fp) { - fwrite($fp, $content); - fclose($fp); - touch($file, $mtime); - return true; - } - return false; - } - - function get_microtime() - { - list($usec, $sec) = explode(" ", microtime()); - return ((float)$usec + (float)$sec); - } - } -?> diff --git a/project/static/js/edit_area_full.gz b/project/static/js/edit_area_full.gz deleted file mode 100755 index 8cfc846f1649c8de8bc1f12dc660d90d717e67ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29125 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.isGecko||(t.isWebKit&&!t.isSafari<3)||t.isOpera>=9||t.isCamino)t.isValidBrowser=true; -else t.isValidBrowser=false;t.set_base_url();for(var i=0;i0)s["toolbar"]=s["begin_toolbar"]+","+s["toolbar"];if(s["end_toolbar"].length>0)s["toolbar"]=s["toolbar"]+","+s["end_toolbar"];s["tab_toolbar"]=s["toolbar"].replace(/ /g,"").split(",");s["plugins"]=s["plugins"].replace(/ /g,"").split(",");for(i=0;i0){s["syntax"]=s["syntax"].toLowerCase();t.load_script(t.baseURL+"reg_syntax/"+s["syntax"]+".js");}eAs[s["id"]]={"settings":s};eAs[s["id"]]["displayed"]=false;eAs[s["id"]]["hidden"]=false;t.start(s["id"]);},delete_instance:function(id){var d=document,fs=window.frames,span,iframe;eAL.execCommand(id,"EA_delete");if(fs["frame_"+id]&&fs["frame_"+id].editArea){if(eAs[id]["displayed"])eAL.toggle(id,"off");fs["frame_"+id].editArea.execCommand("EA_unload");}span=d.getElementById("EditAreaArroundInfos_"+id);if(span)span.parentNode.removeChild(span);iframe=d.getElementById("frame_"+id);if(iframe){iframe.parentNode.removeChild(iframe);try{delete fs["frame_"+id];}catch(e){}}delete eAs[id];},start:function(id){var t=this,d=document,f,span,father,next,html='',html_toolbar_content='',template,content,i;if(t.win!="loaded"){setTimeout("eAL.start('"+id+"');",50);return;}for(i in t.waiting_loading){if(t.waiting_loading[i]!="loaded"&&typeof(t.waiting_loading[i])!="function"){setTimeout("eAL.start('"+id+"');",50);return;}}if(!t.lang[eAs[id]["settings"]["language"]]||(eAs[id]["settings"]["syntax"].length>0&&!t.load_syntax[eAs[id]["settings"]["syntax"]])){setTimeout("eAL.start('"+id+"');",50);return;}if(eAs[id]["settings"]["syntax"].length>0)t.init_syntax_regexp();if(!d.getElementById("EditAreaArroundInfos_"+id)&&(eAs[id]["settings"]["debug"]||eAs[id]["settings"]["allow_toggle"])){span=d.createElement("span");span.id="EditAreaArroundInfos_"+id;if(eAs[id]["settings"]["allow_toggle"]){checked=(eAs[id]["settings"]["display"]=="onload")?"checked='checked'":"";html+="
";html+="";html+="
";}if(eAs[id]["settings"]["debug"])html+="
";html=t.translate(html,eAs[id]["settings"]["language"]);span.innerHTML=html;father=d.getElementById(id).parentNode;next=d.getElementById(id).nextSibling;if(next==null)father.appendChild(span); -else father.insertBefore(span,next);}if(!eAs[id]["initialized"]){t.execCommand(id,"EA_init");if(eAs[id]["settings"]["display"]=="later"){eAs[id]["initialized"]=true;return;}}if(t.isIE){t.init_ie_textarea(id);}area=eAs[id];for(i=0;i';}for(i=0;i';t.iframe_script+='';}if(!t.iframe_css){t.iframe_css="";}template=t.template.replace(/\[__BASEURL__\]/g,t.baseURL);template=template.replace("[__TOOLBAR__]",html_toolbar_content);template=t.translate(template,area["settings"]["language"],"template");template=template.replace("[__CSSRULES__]",t.iframe_css);template=template.replace("[__JSCODE__]",t.iframe_script);template=template.replace("[__EA_VERSION__]",t.version);area.textarea=d.getElementById(area["settings"]["id"]);eAs[area["settings"]["id"]]["textarea"]=area.textarea;if(typeof(window.frames["frame_"+area["settings"]["id"]])!='undefined')delete window.frames["frame_"+area["settings"]["id"]];father=area.textarea.parentNode;content=d.createElement("iframe");content.name="frame_"+area["settings"]["id"];content.id="frame_"+area["settings"]["id"];content.style.borderWidth="0px";setAttribute(content,"frameBorder","0");content.style.overflow="hidden";content.style.display="none";next=area.textarea.nextSibling;if(next==null)father.appendChild(content); -else father.insertBefore(content,next);f=window.frames["frame_"+area["settings"]["id"]];f.document.open();f.eAs=eAs;f.area_id=area["settings"]["id"];f.document.area_id=area["settings"]["id"];f.document.write(template);f.document.close();},toggle:function(id,toggle_to){if(!toggle_to)toggle_to=(eAs[id]["displayed"]==true)?"off":"on";if(eAs[id]["displayed"]==true&&toggle_to=="off"){this.toggle_off(id);} -else if(eAs[id]["displayed"]==false&&toggle_to=="on"){this.toggle_on(id);}return false;},toggle_off:function(id){var fs=window.frames,f,t,parNod,nxtSib,selStart,selEnd,scrollTop,scrollLeft;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];if(f.editArea.fullscreen['isFull'])f.editArea.toggle_full_screen(false);eAs[id]["displayed"]=false;t.wrap="off";setAttribute(t,"wrap","off");parNod=t.parentNode;nxtSib=t.nextSibling;parNod.removeChild(t);parNod.insertBefore(t,nxtSib);t.value=f.editArea.textarea.value;selStart=f.editArea.last_selection["selectionStart"];selEnd=f.editArea.last_selection["selectionEnd"];scrollTop=f.document.getElementById("result").scrollTop;scrollLeft=f.document.getElementById("result").scrollLeft;document.getElementById("frame_"+id).style.display='none';t.style.display="inline";try{t.focus();}catch(e){};if(this.isIE){t.selectionStart=selStart;t.selectionEnd=selEnd;t.focused=true;set_IE_selection(t);} -else{if(this.isOpera&&this.isOpera < 9.6){t.setSelectionRange(0,0);}try{t.setSelectionRange(selStart,selEnd);}catch(e){};}t.scrollTop=scrollTop;t.scrollLeft=scrollLeft;f.editArea.execCommand("toggle_off");}},toggle_on:function(id){var fs=window.frames,f,t,selStart=0,selEnd=0,scrollTop=0,scrollLeft=0,curPos,elem;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];area=f.editArea;area.textarea.value=t.value;curPos=eAs[id]["settings"]["cursor_position"];if(t.use_last==true){selStart=t.last_selectionStart;selEnd=t.last_selectionEnd;scrollTop=t.last_scrollTop;scrollLeft=t.last_scrollLeft;t.use_last=false;} -else if(curPos=="auto"){try{selStart=t.selectionStart;selEnd=t.selectionEnd;scrollTop=t.scrollTop;scrollLeft=t.scrollLeft;}catch(ex){}}this.set_editarea_size_from_textarea(id,document.getElementById("frame_"+id));t.style.display="none";document.getElementById("frame_"+id).style.display="inline";area.execCommand("focus");eAs[id]["displayed"]=true;area.execCommand("update_size");f.document.getElementById("result").scrollTop=scrollTop;f.document.getElementById("result").scrollLeft=scrollLeft;area.area_select(selStart,selEnd-selStart);area.execCommand("toggle_on");} -else{elem=document.getElementById(id);elem.last_selectionStart=elem.selectionStart;elem.last_selectionEnd=elem.selectionEnd;elem.last_scrollTop=elem.scrollTop;elem.last_scrollLeft=elem.scrollLeft;elem.use_last=true;eAL.start(id);}},set_editarea_size_from_textarea:function(id,frame){var elem,width,height;elem=document.getElementById(id);width=Math.max(eAs[id]["settings"]["min_width"],elem.offsetWidth)+"px";height=Math.max(eAs[id]["settings"]["min_height"],elem.offsetHeight)+"px";if(elem.style.width.indexOf("%")!=-1)width=elem.style.width;if(elem.style.height.indexOf("%")!=-1)height=elem.style.height;frame.style.width=width;frame.style.height=height;},set_base_url:function(){var t=this,elems,i,docBasePath;if(!this.baseURL){elems=document.getElementsByTagName('script');for(i=0;i';html+='';return html;},get_control_html:function(button_name,lang){var t=this,i,but,html,si;for(i=0;i";case "|":case "separator":return '';case "select_font":html="";return html;case "syntax_selection":html="";return html;}return "["+button_name+"]";},get_template:function(){if(this.template==""){var xhr_object=null;if(window.XMLHttpRequest)xhr_object=new XMLHttpRequest(); -else if(window.ActiveXObject)xhr_object=new ActiveXObject("Microsoft.XMLHTTP"); -else{alert("XMLHTTPRequest not supported. EditArea not loaded");return;}xhr_object.open("GET",this.baseURL+"template.html",false);xhr_object.send(null);if(xhr_object.readyState==4)this.template=xhr_object.responseText; -else this.has_error();}},translate:function(text,lang,mode){if(mode=="word")text=eAL.get_word_translation(text,lang); -else if(mode="template"){eAL.current_language=lang;text=text.replace(/\{\$([^\}]+)\}/gm,eAL.translate_template);}return text;},translate_template:function(){return eAL.get_word_translation(EAL.prototype.translate_template.arguments[1],eAL.current_language);},get_word_translation:function(val,lang){var i;for(i in eAL.lang[lang]){if(i==val)return eAL.lang[lang][i];}return "_"+val;},load_script:function(url){var t=this,d=document,script,head;if(t.loadedFiles[url])return;try{script=d.createElement("script");script.type="text/javascript";script.src=url;script.charset="UTF-8";d.getElementsByTagName("head")[0].appendChild(script);}catch(e){d.write('');}t.loadedFiles[url]=true;},add_event:function(obj,name,handler){try{if(obj.attachEvent){obj.attachEvent("on"+name,handler);} -else{obj.addEventListener(name,handler,false);}}catch(e){}},remove_event:function(obj,name,handler){try{if(obj.detachEvent)obj.detachEvent("on"+name,handler); -else obj.removeEventListener(name,handler,false);}catch(e){}},reset:function(e){var formObj,is_child,i,x;formObj=eAL.isIE ? window.event.srcElement:e.target;if(formObj.tagName!='FORM')formObj=formObj.form;for(i in eAs){is_child=false;for(x=0;x old_sel["start"])this.setSelectionRange(id,new_sel["end"],new_sel["end"]); -else this.setSelectionRange(id,old_sel["start"]+open_tag.length,old_sel["start"]+open_tag.length);},hide:function(id){var fs=window.frames,d=document,t=this,scrollTop,scrollLeft,span;if(d.getElementById(id)&&!t.hidden[id]){t.hidden[id]={};t.hidden[id]["selectionRange"]=t.getSelectionRange(id);if(d.getElementById(id).style.display!="none"){t.hidden[id]["scrollTop"]=d.getElementById(id).scrollTop;t.hidden[id]["scrollLeft"]=d.getElementById(id).scrollLeft;}if(fs["frame_"+id]){t.hidden[id]["toggle"]=eAs[id]["displayed"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){scrollTop=fs["frame_"+id].document.getElementById("result").scrollTop;scrollLeft=fs["frame_"+id].document.getElementById("result").scrollLeft;} -else{scrollTop=d.getElementById(id).scrollTop;scrollLeft=d.getElementById(id).scrollLeft;}t.hidden[id]["scrollTop"]=scrollTop;t.hidden[id]["scrollLeft"]=scrollLeft;if(eAs[id]["displayed"]==true)eAL.toggle_off(id);}span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='none';}d.getElementById(id).style.display="none";}},show:function(id){var fs=window.frames,d=document,t=this,span;if((elem=d.getElementById(id))&&t.hidden[id]){elem.style.display="inline";elem.scrollTop=t.hidden[id]["scrollTop"];elem.scrollLeft=t.hidden[id]["scrollLeft"];span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='inline';}if(fs["frame_"+id]){elem.style.display="inline";if(t.hidden[id]["toggle"]==true)eAL.toggle_on(id);scrollTop=t.hidden[id]["scrollTop"];scrollLeft=t.hidden[id]["scrollLeft"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].document.getElementById("result").scrollTop=scrollTop;fs["frame_"+id].document.getElementById("result").scrollLeft=scrollLeft;} -else{elem.scrollTop=scrollTop;elem.scrollLeft=scrollLeft;}}sel=t.hidden[id]["selectionRange"];t.setSelectionRange(id,sel["start"],sel["end"]);delete t.hidden[id];}},getCurrentFile:function(id){return this.execCommand(id,'get_file',this.execCommand(id,'curr_file'));},getFile:function(id,file_id){return this.execCommand(id,'get_file',file_id);},getAllFiles:function(id){return this.execCommand(id,'get_all_files()');},openFile:function(id,file_infos){return this.execCommand(id,'open_file',file_infos);},closeFile:function(id,file_id){return this.execCommand(id,'close_file',file_id);},setFileEditedMode:function(id,file_id,to){var reg1,reg2;reg1=new RegExp('\\\\','g');reg2=new RegExp('"','g');return this.execCommand(id,'set_file_edited_mode("'+file_id.replace(reg1,'\\\\').replace(reg2,'\\"')+'",'+to+')');},execCommand:function(id,cmd,fct_param){switch(cmd){case "EA_init":if(eAs[id]['settings']["EA_init_callback"].length>0)eval(eAs[id]['settings']["EA_init_callback"]+"('"+id+"');");break;case "EA_delete":if(eAs[id]['settings']["EA_delete_callback"].length>0)eval(eAs[id]['settings']["EA_delete_callback"]+"('"+id+"');");break;case "EA_submit":if(eAs[id]['settings']["submit_callback"].length>0)eval(eAs[id]['settings']["submit_callback"]+"('"+id+"');");break;}if(window.frames["frame_"+id]&&window.frames["frame_"+id].editArea){if(fct_param!=undefined)return eval('window.frames["frame_'+id+'"].editArea.'+cmd+'(fct_param);'); -else return eval('window.frames["frame_'+id+'"].editArea.'+cmd+';');}return false;}};var eAL=new EAL();var eAs={}; function getAttribute(elm,aName){var aValue,taName,i;try{aValue=elm.getAttribute(aName);}catch(exept){}if(! aValue){for(i=0;i < elm.attributes.length;i++){taName=elm.attributes[i] .name.toLowerCase();if(taName==aName){aValue=elm.attributes[i] .value;return aValue;}}}return aValue;};function setAttribute(elm,attr,val){if(attr=="class"){elm.setAttribute("className",val);elm.setAttribute("class",val);} -else{elm.setAttribute(attr,val);}};function getChildren(elem,elem_type,elem_attribute,elem_attribute_match,option,depth){if(!option)var option="single";if(!depth)var depth=-1;if(elem){var children=elem.childNodes;var result=null;var results=[];for(var x=0;x0){results=results.concat(result);}} -else if(result!=null){return result;}}}}if(option=="all")return results;}return null;};function isChildOf(elem,parent){if(elem){if(elem==parent)return true;while(elem.parentNode !='undefined'){return isChildOf(elem.parentNode,parent);}}return false;};function getMouseX(e){if(e!=null&&typeof(e.pageX)!="undefined"){return e.pageX;} -else{return(e!=null?e.x:event.x)+document.documentElement.scrollLeft;}};function getMouseY(e){if(e!=null&&typeof(e.pageY)!="undefined"){return e.pageY;} -else{return(e!=null?e.y:event.y)+document.documentElement.scrollTop;}};function calculeOffsetLeft(r){return calculeOffset(r,"offsetLeft")};function calculeOffsetTop(r){return calculeOffset(r,"offsetTop")};function calculeOffset(element,attr){var offset=0;while(element){offset+=element[attr];element=element.offsetParent}return offset;};function get_css_property(elem,prop){if(document.defaultView){return document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop);} -else if(elem.currentStyle){var prop=prop.replace(/-\D/gi,function(sMatch){return sMatch.charAt(sMatch.length-1).toUpperCase();});return elem.currentStyle[prop];} -else return null;}var _mCE;function start_move_element(e,id,frame){var elem_id=(e.target||e.srcElement).id;if(id)elem_id=id;if(!frame)frame=window;if(frame.event)e=frame.event;_mCE=frame.document.getElementById(elem_id);_mCE.frame=frame;frame.document.onmousemove=move_element;frame.document.onmouseup=end_move_element;mouse_x=getMouseX(e);mouse_y=getMouseY(e);_mCE.start_pos_x=mouse_x-(_mCE.style.left.replace("px","")||calculeOffsetLeft(_mCE));_mCE.start_pos_y=mouse_y-(_mCE.style.top.replace("px","")||calculeOffsetTop(_mCE));return false;};function end_move_element(e){_mCE.frame.document.onmousemove="";_mCE.frame.document.onmouseup="";_mCE=null;};function move_element(e){var newTop,newLeft,maxLeft;if(_mCE.frame&&_mCE.frame.event)e=_mCE.frame.event;newTop=getMouseY(e)-_mCE.start_pos_y;newLeft=getMouseX(e)-_mCE.start_pos_x;maxLeft=_mCE.frame.document.body.offsetWidth-_mCE.offsetWidth;max_top=_mCE.frame.document.body.offsetHeight-_mCE.offsetHeight;newTop=Math.min(Math.max(0,newTop),max_top);newLeft=Math.min(Math.max(0,newLeft),maxLeft);_mCE.style.top=newTop+"px";_mCE.style.left=newLeft+"px";return false;};var nav=eAL.nav;function getSelectionRange(textarea){return{"start":textarea.selectionStart,"end":textarea.selectionEnd};};function setSelectionRange(t,start,end){t.focus();start=Math.max(0,Math.min(t.value.length,start));end=Math.max(start,Math.min(t.value.length,end));if(this.isOpera&&this.isOpera < 9.6){t.selectionEnd=1;t.selectionStart=0;t.selectionEnd=1;t.selectionStart=0;}t.selectionStart=start;t.selectionEnd=end;if(isIE)set_IE_selection(t);};function get_IE_selection(t){var d=document,div,range,stored_range,elem,scrollTop,relative_top,line_start,line_nb,range_start,range_end,tab;if(t&&t.focused){if(!t.ea_line_height){div=d.createElement("div");div.style.fontFamily=get_css_property(t,"font-family");div.style.fontSize=get_css_property(t,"font-size");div.style.visibility="hidden";div.innerHTML="0";d.body.appendChild(div);t.ea_line_height=div.offsetHeight;d.body.removeChild(div);}range=d.selection.createRange();try{stored_range=range.duplicate();stored_range.moveToElementText(t);stored_range.setEndPoint('EndToEnd',range);if(stored_range.parentElement()==t){elem=t;scrollTop=0;while(elem.parentNode){scrollTop+=elem.scrollTop;elem=elem.parentNode;}relative_top=range.offsetTop-calculeOffsetTop(t)+scrollTop;line_start=Math.round((relative_top / t.ea_line_height)+1);line_nb=Math.round(range.boundingHeight / t.ea_line_height);range_start=stored_range.text.length-range.text.length;tab=t.value.substr(0,range_start).split("\n");range_start+=(line_start-tab.length)*2;t.selectionStart=range_start;range_end=t.selectionStart+range.text.length;tab=t.value.substr(0,range_start+range.text.length).split("\n");range_end+=(line_start+line_nb-1-tab.length)*2;t.selectionEnd=range_end;}}catch(e){}}setTimeout("get_IE_selection(document.getElementById('"+t.id+"'));",50);};function IE_textarea_focus(){event.srcElement.focused=true;}function IE_textarea_blur(){event.srcElement.focused=false;}function set_IE_selection(t){var nbLineStart,nbLineStart,nbLineEnd,range;if(!window.closed){nbLineStart=t.value.substr(0,t.selectionStart).split("\n").length-1;nbLineEnd=t.value.substr(0,t.selectionEnd).split("\n").length-1;try{range=document.selection.createRange();range.moveToElementText(t);range.setEndPoint('EndToStart',range);range.moveStart('character',t.selectionStart-nbLineStart);range.moveEnd('character',t.selectionEnd-nbLineEnd-(t.selectionStart-nbLineStart));range.select();}catch(e){}}};eAL.waiting_loading["elements_functions.js"]="loaded"; - EAL.prototype.start_resize_area=function(){var d=document,a,div,width,height,father;d.onmouseup=eAL.end_resize_area;d.onmousemove=eAL.resize_area;eAL.toggle(eAL.resize["id"]);a=eAs[eAL.resize["id"]]["textarea"];div=d.getElementById("edit_area_resize");if(!div){div=d.createElement("div");div.id="edit_area_resize";div.style.border="dashed #888888 1px";}width=a.offsetWidth-2;height=a.offsetHeight-2;div.style.display="block";div.style.width=width+"px";div.style.height=height+"px";father=a.parentNode;father.insertBefore(div,a);a.style.display="none";eAL.resize["start_top"]=calculeOffsetTop(div);eAL.resize["start_left"]=calculeOffsetLeft(div);};EAL.prototype.end_resize_area=function(e){var d=document,div,a,width,height;d.onmouseup="";d.onmousemove="";div=d.getElementById("edit_area_resize");a=eAs[eAL.resize["id"]]["textarea"];width=Math.max(eAs[eAL.resize["id"]]["settings"]["min_width"],div.offsetWidth-4);height=Math.max(eAs[eAL.resize["id"]]["settings"]["min_height"],div.offsetHeight-4);if(eAL.isIE==6){width-=2;height-=2;}a.style.width=width+"px";a.style.height=height+"px";div.style.display="none";a.style.display="inline";a.selectionStart=eAL.resize["selectionStart"];a.selectionEnd=eAL.resize["selectionEnd"];eAL.toggle(eAL.resize["id"]);return false;};EAL.prototype.resize_area=function(e){var allow,newHeight,newWidth;allow=eAs[eAL.resize["id"]]["settings"]["allow_resize"];if(allow=="both"||allow=="y"){newHeight=Math.max(20,getMouseY(e)-eAL.resize["start_top"]);document.getElementById("edit_area_resize").style.height=newHeight+"px";}if(allow=="both"||allow=="x"){newWidth=Math.max(20,getMouseX(e)-eAL.resize["start_left"]);document.getElementById("edit_area_resize").style.width=newWidth+"px";}return false;};eAL.waiting_loading["resize_area.js"]="loaded"; - EAL.prototype.get_regexp=function(text_array){res="(\\b)(";for(i=0;i0)res+="|";res+=this.get_escaped_regexp(text_array[i]);}res+=")(\\b)";reg=new RegExp(res);return res;};EAL.prototype.get_escaped_regexp=function(str){return str.toString().replace(/(\.|\?|\*|\+|\\|\(|\)|\[|\]|\}|\{|\$|\^|\|)/g,"\\$1");};EAL.prototype.init_syntax_regexp=function(){var lang_style={};for(var lang in this.load_syntax){if(!this.syntax[lang]){this.syntax[lang]={};this.syntax[lang]["keywords_reg_exp"]={};this.keywords_reg_exp_nb=0;if(this.load_syntax[lang]['KEYWORDS']){param="g";if(this.load_syntax[lang]['KEYWORD_CASE_SENSITIVE']===false)param+="i";for(var i in this.load_syntax[lang]['KEYWORDS']){if(typeof(this.load_syntax[lang]['KEYWORDS'][i])=="function")continue;this.syntax[lang]["keywords_reg_exp"][i]=new RegExp(this.get_regexp(this.load_syntax[lang]['KEYWORDS'][i]),param);this.keywords_reg_exp_nb++;}}if(this.load_syntax[lang]['OPERATORS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['OPERATORS']){if(typeof(this.load_syntax[lang]['OPERATORS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['OPERATORS'][i]);nb++;}if(str.length>0)this.syntax[lang]["operators_reg_exp"]=new RegExp("("+str+")","g");}if(this.load_syntax[lang]['DELIMITERS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['DELIMITERS']){if(typeof(this.load_syntax[lang]['DELIMITERS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['DELIMITERS'][i]);nb++;}if(str.length>0)this.syntax[lang]["delimiters_reg_exp"]=new RegExp("("+str+")","g");}var syntax_trace=[];this.syntax[lang]["quotes"]={};var quote_tab=[];if(this.load_syntax[lang]['QUOTEMARKS']){for(var i in this.load_syntax[lang]['QUOTEMARKS']){if(typeof(this.load_syntax[lang]['QUOTEMARKS'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['QUOTEMARKS'][i]);this.syntax[lang]["quotes"][x]=x;quote_tab[quote_tab.length]="("+x+"(\\\\.|[^"+x+"])*(?:"+x+"|$))";syntax_trace.push(x);}}this.syntax[lang]["comments"]={};if(this.load_syntax[lang]['COMMENT_SINGLE']){for(var i in this.load_syntax[lang]['COMMENT_SINGLE']){if(typeof(this.load_syntax[lang]['COMMENT_SINGLE'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_SINGLE'][i]);quote_tab[quote_tab.length]="("+x+"(.|\\r|\\t)*(\\n|$))";syntax_trace.push(x);this.syntax[lang]["comments"][x]="\n";}}if(this.load_syntax[lang]['COMMENT_MULTI']){for(var i in this.load_syntax[lang]['COMMENT_MULTI']){if(typeof(this.load_syntax[lang]['COMMENT_MULTI'][i])=="function")continue;var start=this.get_escaped_regexp(i);var end=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_MULTI'][i]);quote_tab[quote_tab.length]="("+start+"(.|\\n|\\r)*?("+end+"|$))";syntax_trace.push(start);syntax_trace.push(end);this.syntax[lang]["comments"][i]=this.load_syntax[lang]['COMMENT_MULTI'][i];}}if(quote_tab.length>0)this.syntax[lang]["comment_or_quote_reg_exp"]=new RegExp("("+quote_tab.join("|")+")","gi");if(syntax_trace.length>0)this.syntax[lang]["syntax_trace_regexp"]=new RegExp("((.|\n)*?)(\\\\*("+syntax_trace.join("|")+"|$))","gmi");if(this.load_syntax[lang]['SCRIPT_DELIMITERS']){this.syntax[lang]["script_delimiters"]={};for(var i in this.load_syntax[lang]['SCRIPT_DELIMITERS']){if(typeof(this.load_syntax[lang]['SCRIPT_DELIMITERS'][i])=="function")continue;this.syntax[lang]["script_delimiters"][i]=this.load_syntax[lang]['SCRIPT_DELIMITERS'];}}this.syntax[lang]["custom_regexp"]={};if(this.load_syntax[lang]['REGEXPS']){for(var i in this.load_syntax[lang]['REGEXPS']){if(typeof(this.load_syntax[lang]['REGEXPS'][i])=="function")continue;var val=this.load_syntax[lang]['REGEXPS'][i];if(!this.syntax[lang]["custom_regexp"][val['execute']])this.syntax[lang]["custom_regexp"][val['execute']]={};this.syntax[lang]["custom_regexp"][val['execute']][i]={'regexp':new RegExp(val['search'],val['modifiers']),'class':val['class']};}}if(this.load_syntax[lang]['STYLES']){lang_style[lang]={};for(var i in this.load_syntax[lang]['STYLES']){if(typeof(this.load_syntax[lang]['STYLES'][i])=="function")continue;if(typeof(this.load_syntax[lang]['STYLES'][i])!="string"){for(var j in this.load_syntax[lang]['STYLES'][i]){lang_style[lang][j]=this.load_syntax[lang]['STYLES'][i][j];}} -else{lang_style[lang][i]=this.load_syntax[lang]['STYLES'][i];}}}var style="";for(var i in lang_style[lang]){if(lang_style[lang][i].length>0){style+="."+lang+" ."+i.toLowerCase()+" span{"+lang_style[lang][i]+"}\n";style+="."+lang+" ."+i.toLowerCase()+"{"+lang_style[lang][i]+"}\n";}}this.syntax[lang]["styles"]=style;}}};eAL.waiting_loading["reg_syntax.js"]="loaded"; -var editAreaLoader= eAL;var editAreas=eAs;EditAreaLoader=EAL;editAreaLoader.iframe_script= "".replace(/Á/g,'this').replace(/Â/g,'textarea').replace(/Ã/g,'function').replace(/Ä/g,'prototype').replace(/Å/g,'settings').replace(/Æ/g,'length').replace(/Ç/g,'style').replace(/È/g,'parent').replace(/É/g,'last_selection').replace(/Ê/g,'value').replace(/Ë/g,'true').replace(/Ì/g,'false'); -editAreaLoader.template= " EditArea [__CSSRULES__] [__JSCODE__]
[__TOOLBAR__]
 
 
{$position}: {$line_abbr} 0, {$char_abbr} 0 {$total}: {$line_abbr} 0, {$char_abbr} 0 resize
{$processing}
{$search} {$close_popup}
{$replace} {$move_popup}

{$find_next} {$replace} {$replace_all}
{$close_popup}

Editarea [__EA_VERSION__]


{$shortcuts}:

{$tab}: {$add_tab}
{$shift}+{$tab}: {$remove_tab}
{$ctrl}+f: {$search_command}
{$ctrl}+r: {$replace_command}
{$ctrl}+h: {$highlight}
{$ctrl}+g: {$go_to_line}
{$ctrl}+z: {$undo}
{$ctrl}+y: {$redo}
{$ctrl}+e: {$help}
{$ctrl}+q, {$esc}: {$close_popup}
{$accesskey} E: {$toggle}

{$about_notice}
"; -editAreaLoader.iframe_css= ""; diff --git a/project/static/js/edit_area_full_with_plugins.gz b/project/static/js/edit_area_full_with_plugins.gz deleted file mode 100755 index 19cd98c4f3b64b927cf72c89048c842fc7700acd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29707 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%0AH#Zu2xRM{&R`R1|83;fL<+Yl7 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 exMxy~3~JJ>!?5Uh$kpus=6||t.isGecko||(t.isWebKit&&!t.isSafari<3)||t.isOpera>=9||t.isCamino)t.isValidBrowser=true; -else t.isValidBrowser=false;t.set_base_url();for(var i=0;i0)s["toolbar"]=s["begin_toolbar"]+","+s["toolbar"];if(s["end_toolbar"].length>0)s["toolbar"]=s["toolbar"]+","+s["end_toolbar"];s["tab_toolbar"]=s["toolbar"].replace(/ /g,"").split(",");s["plugins"]=s["plugins"].replace(/ /g,"").split(",");for(i=0;i0){s["syntax"]=s["syntax"].toLowerCase();t.load_script(t.baseURL+"reg_syntax/"+s["syntax"]+".js");}eAs[s["id"]]={"settings":s};eAs[s["id"]]["displayed"]=false;eAs[s["id"]]["hidden"]=false;t.start(s["id"]);},delete_instance:function(id){var d=document,fs=window.frames,span,iframe;eAL.execCommand(id,"EA_delete");if(fs["frame_"+id]&&fs["frame_"+id].editArea){if(eAs[id]["displayed"])eAL.toggle(id,"off");fs["frame_"+id].editArea.execCommand("EA_unload");}span=d.getElementById("EditAreaArroundInfos_"+id);if(span)span.parentNode.removeChild(span);iframe=d.getElementById("frame_"+id);if(iframe){iframe.parentNode.removeChild(iframe);try{delete fs["frame_"+id];}catch(e){}}delete eAs[id];},start:function(id){var t=this,d=document,f,span,father,next,html='',html_toolbar_content='',template,content,i;if(t.win!="loaded"){setTimeout("eAL.start('"+id+"');",50);return;}for(i in t.waiting_loading){if(t.waiting_loading[i]!="loaded"&&typeof(t.waiting_loading[i])!="function"){setTimeout("eAL.start('"+id+"');",50);return;}}if(!t.lang[eAs[id]["settings"]["language"]]||(eAs[id]["settings"]["syntax"].length>0&&!t.load_syntax[eAs[id]["settings"]["syntax"]])){setTimeout("eAL.start('"+id+"');",50);return;}if(eAs[id]["settings"]["syntax"].length>0)t.init_syntax_regexp();if(!d.getElementById("EditAreaArroundInfos_"+id)&&(eAs[id]["settings"]["debug"]||eAs[id]["settings"]["allow_toggle"])){span=d.createElement("span");span.id="EditAreaArroundInfos_"+id;if(eAs[id]["settings"]["allow_toggle"]){checked=(eAs[id]["settings"]["display"]=="onload")?"checked='checked'":"";html+="
";html+="";html+="
";}if(eAs[id]["settings"]["debug"])html+="
";html=t.translate(html,eAs[id]["settings"]["language"]);span.innerHTML=html;father=d.getElementById(id).parentNode;next=d.getElementById(id).nextSibling;if(next==null)father.appendChild(span); -else father.insertBefore(span,next);}if(!eAs[id]["initialized"]){t.execCommand(id,"EA_init");if(eAs[id]["settings"]["display"]=="later"){eAs[id]["initialized"]=true;return;}}if(t.isIE){t.init_ie_textarea(id);}area=eAs[id];for(i=0;i';}for(i=0;i';t.iframe_script+='';}if(!t.iframe_css){t.iframe_css="";}template=t.template.replace(/\[__BASEURL__\]/g,t.baseURL);template=template.replace("[__TOOLBAR__]",html_toolbar_content);template=t.translate(template,area["settings"]["language"],"template");template=template.replace("[__CSSRULES__]",t.iframe_css);template=template.replace("[__JSCODE__]",t.iframe_script);template=template.replace("[__EA_VERSION__]",t.version);area.textarea=d.getElementById(area["settings"]["id"]);eAs[area["settings"]["id"]]["textarea"]=area.textarea;if(typeof(window.frames["frame_"+area["settings"]["id"]])!='undefined')delete window.frames["frame_"+area["settings"]["id"]];father=area.textarea.parentNode;content=d.createElement("iframe");content.name="frame_"+area["settings"]["id"];content.id="frame_"+area["settings"]["id"];content.style.borderWidth="0px";setAttribute(content,"frameBorder","0");content.style.overflow="hidden";content.style.display="none";next=area.textarea.nextSibling;if(next==null)father.appendChild(content); -else father.insertBefore(content,next);f=window.frames["frame_"+area["settings"]["id"]];f.document.open();f.eAs=eAs;f.area_id=area["settings"]["id"];f.document.area_id=area["settings"]["id"];f.document.write(template);f.document.close();},toggle:function(id,toggle_to){if(!toggle_to)toggle_to=(eAs[id]["displayed"]==true)?"off":"on";if(eAs[id]["displayed"]==true&&toggle_to=="off"){this.toggle_off(id);} -else if(eAs[id]["displayed"]==false&&toggle_to=="on"){this.toggle_on(id);}return false;},toggle_off:function(id){var fs=window.frames,f,t,parNod,nxtSib,selStart,selEnd,scrollTop,scrollLeft;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];if(f.editArea.fullscreen['isFull'])f.editArea.toggle_full_screen(false);eAs[id]["displayed"]=false;t.wrap="off";setAttribute(t,"wrap","off");parNod=t.parentNode;nxtSib=t.nextSibling;parNod.removeChild(t);parNod.insertBefore(t,nxtSib);t.value=f.editArea.textarea.value;selStart=f.editArea.last_selection["selectionStart"];selEnd=f.editArea.last_selection["selectionEnd"];scrollTop=f.document.getElementById("result").scrollTop;scrollLeft=f.document.getElementById("result").scrollLeft;document.getElementById("frame_"+id).style.display='none';t.style.display="inline";try{t.focus();}catch(e){};if(this.isIE){t.selectionStart=selStart;t.selectionEnd=selEnd;t.focused=true;set_IE_selection(t);} -else{if(this.isOpera&&this.isOpera < 9.6){t.setSelectionRange(0,0);}try{t.setSelectionRange(selStart,selEnd);}catch(e){};}t.scrollTop=scrollTop;t.scrollLeft=scrollLeft;f.editArea.execCommand("toggle_off");}},toggle_on:function(id){var fs=window.frames,f,t,selStart=0,selEnd=0,scrollTop=0,scrollLeft=0,curPos,elem;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];area=f.editArea;area.textarea.value=t.value;curPos=eAs[id]["settings"]["cursor_position"];if(t.use_last==true){selStart=t.last_selectionStart;selEnd=t.last_selectionEnd;scrollTop=t.last_scrollTop;scrollLeft=t.last_scrollLeft;t.use_last=false;} -else if(curPos=="auto"){try{selStart=t.selectionStart;selEnd=t.selectionEnd;scrollTop=t.scrollTop;scrollLeft=t.scrollLeft;}catch(ex){}}this.set_editarea_size_from_textarea(id,document.getElementById("frame_"+id));t.style.display="none";document.getElementById("frame_"+id).style.display="inline";area.execCommand("focus");eAs[id]["displayed"]=true;area.execCommand("update_size");f.document.getElementById("result").scrollTop=scrollTop;f.document.getElementById("result").scrollLeft=scrollLeft;area.area_select(selStart,selEnd-selStart);area.execCommand("toggle_on");} -else{elem=document.getElementById(id);elem.last_selectionStart=elem.selectionStart;elem.last_selectionEnd=elem.selectionEnd;elem.last_scrollTop=elem.scrollTop;elem.last_scrollLeft=elem.scrollLeft;elem.use_last=true;eAL.start(id);}},set_editarea_size_from_textarea:function(id,frame){var elem,width,height;elem=document.getElementById(id);width=Math.max(eAs[id]["settings"]["min_width"],elem.offsetWidth)+"px";height=Math.max(eAs[id]["settings"]["min_height"],elem.offsetHeight)+"px";if(elem.style.width.indexOf("%")!=-1)width=elem.style.width;if(elem.style.height.indexOf("%")!=-1)height=elem.style.height;frame.style.width=width;frame.style.height=height;},set_base_url:function(){var t=this,elems,i,docBasePath;if(!this.baseURL){elems=document.getElementsByTagName('script');for(i=0;i';html+='';return html;},get_control_html:function(button_name,lang){var t=this,i,but,html,si;for(i=0;i";case "|":case "separator":return '';case "select_font":html="";return html;case "syntax_selection":html="";return html;}return "["+button_name+"]";},get_template:function(){if(this.template==""){var xhr_object=null;if(window.XMLHttpRequest)xhr_object=new XMLHttpRequest(); -else if(window.ActiveXObject)xhr_object=new ActiveXObject("Microsoft.XMLHTTP"); -else{alert("XMLHTTPRequest not supported. EditArea not loaded");return;}xhr_object.open("GET",this.baseURL+"template.html",false);xhr_object.send(null);if(xhr_object.readyState==4)this.template=xhr_object.responseText; -else this.has_error();}},translate:function(text,lang,mode){if(mode=="word")text=eAL.get_word_translation(text,lang); -else if(mode="template"){eAL.current_language=lang;text=text.replace(/\{\$([^\}]+)\}/gm,eAL.translate_template);}return text;},translate_template:function(){return eAL.get_word_translation(EAL.prototype.translate_template.arguments[1],eAL.current_language);},get_word_translation:function(val,lang){var i;for(i in eAL.lang[lang]){if(i==val)return eAL.lang[lang][i];}return "_"+val;},load_script:function(url){var t=this,d=document,script,head;if(t.loadedFiles[url])return;try{script=d.createElement("script");script.type="text/javascript";script.src=url;script.charset="UTF-8";d.getElementsByTagName("head")[0].appendChild(script);}catch(e){d.write('');}t.loadedFiles[url]=true;},add_event:function(obj,name,handler){try{if(obj.attachEvent){obj.attachEvent("on"+name,handler);} -else{obj.addEventListener(name,handler,false);}}catch(e){}},remove_event:function(obj,name,handler){try{if(obj.detachEvent)obj.detachEvent("on"+name,handler); -else obj.removeEventListener(name,handler,false);}catch(e){}},reset:function(e){var formObj,is_child,i,x;formObj=eAL.isIE ? window.event.srcElement:e.target;if(formObj.tagName!='FORM')formObj=formObj.form;for(i in eAs){is_child=false;for(x=0;x old_sel["start"])this.setSelectionRange(id,new_sel["end"],new_sel["end"]); -else this.setSelectionRange(id,old_sel["start"]+open_tag.length,old_sel["start"]+open_tag.length);},hide:function(id){var fs=window.frames,d=document,t=this,scrollTop,scrollLeft,span;if(d.getElementById(id)&&!t.hidden[id]){t.hidden[id]={};t.hidden[id]["selectionRange"]=t.getSelectionRange(id);if(d.getElementById(id).style.display!="none"){t.hidden[id]["scrollTop"]=d.getElementById(id).scrollTop;t.hidden[id]["scrollLeft"]=d.getElementById(id).scrollLeft;}if(fs["frame_"+id]){t.hidden[id]["toggle"]=eAs[id]["displayed"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){scrollTop=fs["frame_"+id].document.getElementById("result").scrollTop;scrollLeft=fs["frame_"+id].document.getElementById("result").scrollLeft;} -else{scrollTop=d.getElementById(id).scrollTop;scrollLeft=d.getElementById(id).scrollLeft;}t.hidden[id]["scrollTop"]=scrollTop;t.hidden[id]["scrollLeft"]=scrollLeft;if(eAs[id]["displayed"]==true)eAL.toggle_off(id);}span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='none';}d.getElementById(id).style.display="none";}},show:function(id){var fs=window.frames,d=document,t=this,span;if((elem=d.getElementById(id))&&t.hidden[id]){elem.style.display="inline";elem.scrollTop=t.hidden[id]["scrollTop"];elem.scrollLeft=t.hidden[id]["scrollLeft"];span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='inline';}if(fs["frame_"+id]){elem.style.display="inline";if(t.hidden[id]["toggle"]==true)eAL.toggle_on(id);scrollTop=t.hidden[id]["scrollTop"];scrollLeft=t.hidden[id]["scrollLeft"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].document.getElementById("result").scrollTop=scrollTop;fs["frame_"+id].document.getElementById("result").scrollLeft=scrollLeft;} -else{elem.scrollTop=scrollTop;elem.scrollLeft=scrollLeft;}}sel=t.hidden[id]["selectionRange"];t.setSelectionRange(id,sel["start"],sel["end"]);delete t.hidden[id];}},getCurrentFile:function(id){return this.execCommand(id,'get_file',this.execCommand(id,'curr_file'));},getFile:function(id,file_id){return this.execCommand(id,'get_file',file_id);},getAllFiles:function(id){return this.execCommand(id,'get_all_files()');},openFile:function(id,file_infos){return this.execCommand(id,'open_file',file_infos);},closeFile:function(id,file_id){return this.execCommand(id,'close_file',file_id);},setFileEditedMode:function(id,file_id,to){var reg1,reg2;reg1=new RegExp('\\\\','g');reg2=new RegExp('"','g');return this.execCommand(id,'set_file_edited_mode("'+file_id.replace(reg1,'\\\\').replace(reg2,'\\"')+'",'+to+')');},execCommand:function(id,cmd,fct_param){switch(cmd){case "EA_init":if(eAs[id]['settings']["EA_init_callback"].length>0)eval(eAs[id]['settings']["EA_init_callback"]+"('"+id+"');");break;case "EA_delete":if(eAs[id]['settings']["EA_delete_callback"].length>0)eval(eAs[id]['settings']["EA_delete_callback"]+"('"+id+"');");break;case "EA_submit":if(eAs[id]['settings']["submit_callback"].length>0)eval(eAs[id]['settings']["submit_callback"]+"('"+id+"');");break;}if(window.frames["frame_"+id]&&window.frames["frame_"+id].editArea){if(fct_param!=undefined)return eval('window.frames["frame_'+id+'"].editArea.'+cmd+'(fct_param);'); -else return eval('window.frames["frame_'+id+'"].editArea.'+cmd+';');}return false;}};var eAL=new EAL();var eAs={}; function getAttribute(elm,aName){var aValue,taName,i;try{aValue=elm.getAttribute(aName);}catch(exept){}if(! aValue){for(i=0;i < elm.attributes.length;i++){taName=elm.attributes[i] .name.toLowerCase();if(taName==aName){aValue=elm.attributes[i] .value;return aValue;}}}return aValue;};function setAttribute(elm,attr,val){if(attr=="class"){elm.setAttribute("className",val);elm.setAttribute("class",val);} -else{elm.setAttribute(attr,val);}};function getChildren(elem,elem_type,elem_attribute,elem_attribute_match,option,depth){if(!option)var option="single";if(!depth)var depth=-1;if(elem){var children=elem.childNodes;var result=null;var results=[];for(var x=0;x0){results=results.concat(result);}} -else if(result!=null){return result;}}}}if(option=="all")return results;}return null;};function isChildOf(elem,parent){if(elem){if(elem==parent)return true;while(elem.parentNode !='undefined'){return isChildOf(elem.parentNode,parent);}}return false;};function getMouseX(e){if(e!=null&&typeof(e.pageX)!="undefined"){return e.pageX;} -else{return(e!=null?e.x:event.x)+document.documentElement.scrollLeft;}};function getMouseY(e){if(e!=null&&typeof(e.pageY)!="undefined"){return e.pageY;} -else{return(e!=null?e.y:event.y)+document.documentElement.scrollTop;}};function calculeOffsetLeft(r){return calculeOffset(r,"offsetLeft")};function calculeOffsetTop(r){return calculeOffset(r,"offsetTop")};function calculeOffset(element,attr){var offset=0;while(element){offset+=element[attr];element=element.offsetParent}return offset;};function get_css_property(elem,prop){if(document.defaultView){return document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop);} -else if(elem.currentStyle){var prop=prop.replace(/-\D/gi,function(sMatch){return sMatch.charAt(sMatch.length-1).toUpperCase();});return elem.currentStyle[prop];} -else return null;}var _mCE;function start_move_element(e,id,frame){var elem_id=(e.target||e.srcElement).id;if(id)elem_id=id;if(!frame)frame=window;if(frame.event)e=frame.event;_mCE=frame.document.getElementById(elem_id);_mCE.frame=frame;frame.document.onmousemove=move_element;frame.document.onmouseup=end_move_element;mouse_x=getMouseX(e);mouse_y=getMouseY(e);_mCE.start_pos_x=mouse_x-(_mCE.style.left.replace("px","")||calculeOffsetLeft(_mCE));_mCE.start_pos_y=mouse_y-(_mCE.style.top.replace("px","")||calculeOffsetTop(_mCE));return false;};function end_move_element(e){_mCE.frame.document.onmousemove="";_mCE.frame.document.onmouseup="";_mCE=null;};function move_element(e){var newTop,newLeft,maxLeft;if(_mCE.frame&&_mCE.frame.event)e=_mCE.frame.event;newTop=getMouseY(e)-_mCE.start_pos_y;newLeft=getMouseX(e)-_mCE.start_pos_x;maxLeft=_mCE.frame.document.body.offsetWidth-_mCE.offsetWidth;max_top=_mCE.frame.document.body.offsetHeight-_mCE.offsetHeight;newTop=Math.min(Math.max(0,newTop),max_top);newLeft=Math.min(Math.max(0,newLeft),maxLeft);_mCE.style.top=newTop+"px";_mCE.style.left=newLeft+"px";return false;};var nav=eAL.nav;function getSelectionRange(textarea){return{"start":textarea.selectionStart,"end":textarea.selectionEnd};};function setSelectionRange(t,start,end){t.focus();start=Math.max(0,Math.min(t.value.length,start));end=Math.max(start,Math.min(t.value.length,end));if(this.isOpera&&this.isOpera < 9.6){t.selectionEnd=1;t.selectionStart=0;t.selectionEnd=1;t.selectionStart=0;}t.selectionStart=start;t.selectionEnd=end;if(isIE)set_IE_selection(t);};function get_IE_selection(t){var d=document,div,range,stored_range,elem,scrollTop,relative_top,line_start,line_nb,range_start,range_end,tab;if(t&&t.focused){if(!t.ea_line_height){div=d.createElement("div");div.style.fontFamily=get_css_property(t,"font-family");div.style.fontSize=get_css_property(t,"font-size");div.style.visibility="hidden";div.innerHTML="0";d.body.appendChild(div);t.ea_line_height=div.offsetHeight;d.body.removeChild(div);}range=d.selection.createRange();try{stored_range=range.duplicate();stored_range.moveToElementText(t);stored_range.setEndPoint('EndToEnd',range);if(stored_range.parentElement()==t){elem=t;scrollTop=0;while(elem.parentNode){scrollTop+=elem.scrollTop;elem=elem.parentNode;}relative_top=range.offsetTop-calculeOffsetTop(t)+scrollTop;line_start=Math.round((relative_top / t.ea_line_height)+1);line_nb=Math.round(range.boundingHeight / t.ea_line_height);range_start=stored_range.text.length-range.text.length;tab=t.value.substr(0,range_start).split("\n");range_start+=(line_start-tab.length)*2;t.selectionStart=range_start;range_end=t.selectionStart+range.text.length;tab=t.value.substr(0,range_start+range.text.length).split("\n");range_end+=(line_start+line_nb-1-tab.length)*2;t.selectionEnd=range_end;}}catch(e){}}setTimeout("get_IE_selection(document.getElementById('"+t.id+"'));",50);};function IE_textarea_focus(){event.srcElement.focused=true;}function IE_textarea_blur(){event.srcElement.focused=false;}function set_IE_selection(t){var nbLineStart,nbLineStart,nbLineEnd,range;if(!window.closed){nbLineStart=t.value.substr(0,t.selectionStart).split("\n").length-1;nbLineEnd=t.value.substr(0,t.selectionEnd).split("\n").length-1;try{range=document.selection.createRange();range.moveToElementText(t);range.setEndPoint('EndToStart',range);range.moveStart('character',t.selectionStart-nbLineStart);range.moveEnd('character',t.selectionEnd-nbLineEnd-(t.selectionStart-nbLineStart));range.select();}catch(e){}}};eAL.waiting_loading["elements_functions.js"]="loaded"; - EAL.prototype.start_resize_area=function(){var d=document,a,div,width,height,father;d.onmouseup=eAL.end_resize_area;d.onmousemove=eAL.resize_area;eAL.toggle(eAL.resize["id"]);a=eAs[eAL.resize["id"]]["textarea"];div=d.getElementById("edit_area_resize");if(!div){div=d.createElement("div");div.id="edit_area_resize";div.style.border="dashed #888888 1px";}width=a.offsetWidth-2;height=a.offsetHeight-2;div.style.display="block";div.style.width=width+"px";div.style.height=height+"px";father=a.parentNode;father.insertBefore(div,a);a.style.display="none";eAL.resize["start_top"]=calculeOffsetTop(div);eAL.resize["start_left"]=calculeOffsetLeft(div);};EAL.prototype.end_resize_area=function(e){var d=document,div,a,width,height;d.onmouseup="";d.onmousemove="";div=d.getElementById("edit_area_resize");a=eAs[eAL.resize["id"]]["textarea"];width=Math.max(eAs[eAL.resize["id"]]["settings"]["min_width"],div.offsetWidth-4);height=Math.max(eAs[eAL.resize["id"]]["settings"]["min_height"],div.offsetHeight-4);if(eAL.isIE==6){width-=2;height-=2;}a.style.width=width+"px";a.style.height=height+"px";div.style.display="none";a.style.display="inline";a.selectionStart=eAL.resize["selectionStart"];a.selectionEnd=eAL.resize["selectionEnd"];eAL.toggle(eAL.resize["id"]);return false;};EAL.prototype.resize_area=function(e){var allow,newHeight,newWidth;allow=eAs[eAL.resize["id"]]["settings"]["allow_resize"];if(allow=="both"||allow=="y"){newHeight=Math.max(20,getMouseY(e)-eAL.resize["start_top"]);document.getElementById("edit_area_resize").style.height=newHeight+"px";}if(allow=="both"||allow=="x"){newWidth=Math.max(20,getMouseX(e)-eAL.resize["start_left"]);document.getElementById("edit_area_resize").style.width=newWidth+"px";}return false;};eAL.waiting_loading["resize_area.js"]="loaded"; - EAL.prototype.get_regexp=function(text_array){res="(\\b)(";for(i=0;i0)res+="|";res+=this.get_escaped_regexp(text_array[i]);}res+=")(\\b)";reg=new RegExp(res);return res;};EAL.prototype.get_escaped_regexp=function(str){return str.toString().replace(/(\.|\?|\*|\+|\\|\(|\)|\[|\]|\}|\{|\$|\^|\|)/g,"\\$1");};EAL.prototype.init_syntax_regexp=function(){var lang_style={};for(var lang in this.load_syntax){if(!this.syntax[lang]){this.syntax[lang]={};this.syntax[lang]["keywords_reg_exp"]={};this.keywords_reg_exp_nb=0;if(this.load_syntax[lang]['KEYWORDS']){param="g";if(this.load_syntax[lang]['KEYWORD_CASE_SENSITIVE']===false)param+="i";for(var i in this.load_syntax[lang]['KEYWORDS']){if(typeof(this.load_syntax[lang]['KEYWORDS'][i])=="function")continue;this.syntax[lang]["keywords_reg_exp"][i]=new RegExp(this.get_regexp(this.load_syntax[lang]['KEYWORDS'][i]),param);this.keywords_reg_exp_nb++;}}if(this.load_syntax[lang]['OPERATORS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['OPERATORS']){if(typeof(this.load_syntax[lang]['OPERATORS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['OPERATORS'][i]);nb++;}if(str.length>0)this.syntax[lang]["operators_reg_exp"]=new RegExp("("+str+")","g");}if(this.load_syntax[lang]['DELIMITERS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['DELIMITERS']){if(typeof(this.load_syntax[lang]['DELIMITERS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['DELIMITERS'][i]);nb++;}if(str.length>0)this.syntax[lang]["delimiters_reg_exp"]=new RegExp("("+str+")","g");}var syntax_trace=[];this.syntax[lang]["quotes"]={};var quote_tab=[];if(this.load_syntax[lang]['QUOTEMARKS']){for(var i in this.load_syntax[lang]['QUOTEMARKS']){if(typeof(this.load_syntax[lang]['QUOTEMARKS'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['QUOTEMARKS'][i]);this.syntax[lang]["quotes"][x]=x;quote_tab[quote_tab.length]="("+x+"(\\\\.|[^"+x+"])*(?:"+x+"|$))";syntax_trace.push(x);}}this.syntax[lang]["comments"]={};if(this.load_syntax[lang]['COMMENT_SINGLE']){for(var i in this.load_syntax[lang]['COMMENT_SINGLE']){if(typeof(this.load_syntax[lang]['COMMENT_SINGLE'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_SINGLE'][i]);quote_tab[quote_tab.length]="("+x+"(.|\\r|\\t)*(\\n|$))";syntax_trace.push(x);this.syntax[lang]["comments"][x]="\n";}}if(this.load_syntax[lang]['COMMENT_MULTI']){for(var i in this.load_syntax[lang]['COMMENT_MULTI']){if(typeof(this.load_syntax[lang]['COMMENT_MULTI'][i])=="function")continue;var start=this.get_escaped_regexp(i);var end=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_MULTI'][i]);quote_tab[quote_tab.length]="("+start+"(.|\\n|\\r)*?("+end+"|$))";syntax_trace.push(start);syntax_trace.push(end);this.syntax[lang]["comments"][i]=this.load_syntax[lang]['COMMENT_MULTI'][i];}}if(quote_tab.length>0)this.syntax[lang]["comment_or_quote_reg_exp"]=new RegExp("("+quote_tab.join("|")+")","gi");if(syntax_trace.length>0)this.syntax[lang]["syntax_trace_regexp"]=new RegExp("((.|\n)*?)(\\\\*("+syntax_trace.join("|")+"|$))","gmi");if(this.load_syntax[lang]['SCRIPT_DELIMITERS']){this.syntax[lang]["script_delimiters"]={};for(var i in this.load_syntax[lang]['SCRIPT_DELIMITERS']){if(typeof(this.load_syntax[lang]['SCRIPT_DELIMITERS'][i])=="function")continue;this.syntax[lang]["script_delimiters"][i]=this.load_syntax[lang]['SCRIPT_DELIMITERS'];}}this.syntax[lang]["custom_regexp"]={};if(this.load_syntax[lang]['REGEXPS']){for(var i in this.load_syntax[lang]['REGEXPS']){if(typeof(this.load_syntax[lang]['REGEXPS'][i])=="function")continue;var val=this.load_syntax[lang]['REGEXPS'][i];if(!this.syntax[lang]["custom_regexp"][val['execute']])this.syntax[lang]["custom_regexp"][val['execute']]={};this.syntax[lang]["custom_regexp"][val['execute']][i]={'regexp':new RegExp(val['search'],val['modifiers']),'class':val['class']};}}if(this.load_syntax[lang]['STYLES']){lang_style[lang]={};for(var i in this.load_syntax[lang]['STYLES']){if(typeof(this.load_syntax[lang]['STYLES'][i])=="function")continue;if(typeof(this.load_syntax[lang]['STYLES'][i])!="string"){for(var j in this.load_syntax[lang]['STYLES'][i]){lang_style[lang][j]=this.load_syntax[lang]['STYLES'][i][j];}} -else{lang_style[lang][i]=this.load_syntax[lang]['STYLES'][i];}}}var style="";for(var i in lang_style[lang]){if(lang_style[lang][i].length>0){style+="."+lang+" ."+i.toLowerCase()+" span{"+lang_style[lang][i]+"}\n";style+="."+lang+" ."+i.toLowerCase()+"{"+lang_style[lang][i]+"}\n";}}this.syntax[lang]["styles"]=style;}}};eAL.waiting_loading["reg_syntax.js"]="loaded"; -var editAreaLoader= eAL;var editAreas=eAs;EditAreaLoader=EAL;editAreaLoader.iframe_script= "".replace(/Á/g,'this').replace(/Â/g,'textarea').replace(/Ã/g,'function').replace(/Ä/g,'prototype').replace(/Å/g,'settings').replace(/Æ/g,'length').replace(/Ç/g,'style').replace(/È/g,'parent').replace(/É/g,'last_selection').replace(/Ê/g,'value').replace(/Ë/g,'true').replace(/Ì/g,'false'); -editAreaLoader.all_plugins_loaded=true; -editAreaLoader.template= " EditArea [__CSSRULES__] [__JSCODE__]
[__TOOLBAR__]
 
 
{$position}: {$line_abbr} 0, {$char_abbr} 0 {$total}: {$line_abbr} 0, {$char_abbr} 0 resize
{$processing}
{$search} {$close_popup}
{$replace} {$move_popup}

{$find_next} {$replace} {$replace_all}
{$close_popup}

Editarea [__EA_VERSION__]


{$shortcuts}:

{$tab}: {$add_tab}
{$shift}+{$tab}: {$remove_tab}
{$ctrl}+f: {$search_command}
{$ctrl}+r: {$replace_command}
{$ctrl}+h: {$highlight}
{$ctrl}+g: {$go_to_line}
{$ctrl}+z: {$undo}
{$ctrl}+y: {$redo}
{$ctrl}+e: {$help}
{$ctrl}+q, {$esc}: {$close_popup}
{$accesskey} E: {$toggle}

{$about_notice}
"; -editAreaLoader.iframe_css= ""; diff --git a/project/static/js/edit_area_functions.js b/project/static/js/edit_area_functions.js deleted file mode 100755 index 984d4c61..00000000 --- a/project/static/js/edit_area_functions.js +++ /dev/null @@ -1,1203 +0,0 @@ - //replace tabulation by the good number of white spaces - EditArea.prototype.replace_tab= function(text){ - return text.replace(/((\n?)([^\t\n]*)\t)/gi, editArea.smartTab); // slower than simple replace... - }; - - // call by the replace_tab function - EditArea.prototype.smartTab= function(){ - val=" "; - return EditArea.prototype.smartTab.arguments[2] + EditArea.prototype.smartTab.arguments[3] + val.substr(0, editArea.tab_nb_char - (EditArea.prototype.smartTab.arguments[3].length)%editArea.tab_nb_char); - }; - - EditArea.prototype.show_waiting_screen= function(){ - width = this.editor_area.offsetWidth; - height = this.editor_area.offsetHeight; - if( !(this.isIE && this.isIE<6) ) - { - width -= 2; - height -= 2; - } - this.processing_screen.style.display= "block"; - this.processing_screen.style.width = width+"px"; - this.processing_screen.style.height = height+"px"; - this.waiting_screen_displayed = true; - }; - - EditArea.prototype.hide_waiting_screen= function(){ - this.processing_screen.style.display="none"; - this.waiting_screen_displayed= false; - }; - - EditArea.prototype.add_style= function(styles){ - if(styles.length>0){ - newcss = document.createElement("style"); - newcss.type="text/css"; - newcss.media="all"; - if(newcss.styleSheet){ // IE - newcss.styleSheet.cssText = styles; - } else { // W3C - newcss.appendChild(document.createTextNode(styles)); - } - document.getElementsByTagName("head")[0].appendChild(newcss); - } - }; - - EditArea.prototype.set_font= function(family, size){ - var t=this, a=this.textarea, s=this.settings, elem_font, i, elem; - // list all elements concerned by font changes - var elems= ["textarea", "content_highlight", "cursor_pos", "end_bracket", "selection_field", "selection_field_text", "line_number"]; - - if(family && family!="") - s["font_family"]= family; - if(size && size>0) - s["font_size"] = size; - if( t.isOpera && t.isOpera < 9.6 ) // opera<9.6 can't manage non monospace font - s['font_family']="monospace"; - - // update the select tag - if( elem_font = _$("area_font_size") ) - { - for( i = 0; i < elem_font.length; i++ ) - { - if( elem_font.options[i].value && elem_font.options[i].value == s["font_size"] ) - elem_font.options[i].selected=true; - } - } - - /* - * somethimes firefox has rendering mistake with non-monospace font for text width in textarea vs in div for changing font size (eg: verdana change between 11pt to 12pt) - * => looks like a browser internal random bug as text width can change while content_highlight is updated - * we'll check if the font-size produce the same text width inside textarea and div and if not, we'll increment the font-size - * - * This is an ugly fix - */ - if( t.isFirefox ) - { - var nbTry = 3; - do { - var div1 = document.createElement( 'div' ), text1 = document.createElement( 'textarea' ); - var styles = { - width: '40px', - overflow: 'scroll', - zIndex: 50, - visibility: 'hidden', - fontFamily: s["font_family"], - fontSize: s["font_size"]+"pt", - lineHeight: t.lineHeight+"px", - padding: '0', - margin: '0', - border: 'none', - whiteSpace: 'nowrap' - }; - var diff, changed = false; - for( i in styles ) - { - div1.style[ i ] = styles[i]; - text1.style[ i ] = styles[i]; - } - // no wrap for this text - text1.wrap = 'off'; - text1.setAttribute('wrap', 'off'); - t.container.appendChild( div1 ); - t.container.appendChild( text1 ); - // try to make FF to bug - div1.innerHTML = text1.value = 'azertyuiopqsdfghjklm'; - div1.innerHTML = text1.value = text1.value+'wxcvbn^p*ù$!:;,,'; - diff = text1.scrollWidth - div1.scrollWidth; - - // firefox return here a diff of 1 px between equals scrollWidth (can't explain) - if( Math.abs( diff ) >= 2 ) - { - s["font_size"]++; - changed = true; - } - t.container.removeChild( div1 ); - t.container.removeChild( text1 ); - nbTry--; - }while( changed && nbTry > 0 ); - } - - - // calc line height - elem = t.test_font_size; - elem.style.fontFamily = ""+s["font_family"]; - elem.style.fontSize = s["font_size"]+"pt"; - elem.innerHTML = "0"; - t.lineHeight = elem.offsetHeight; - - // update font for all concerned elements - for( i=0; i tags - t.add_style("pre{font-family:"+s["font_family"]+"}"); - - // old opera and IE>=8 doesn't update font changes to the textarea - if( ( t.isOpera && t.isOpera < 9.6 ) || t.isIE >= 8 ) - { - var parNod = a.parentNode, nxtSib = a.nextSibling, start= a.selectionStart, end= a.selectionEnd; - parNod.removeChild(a); - parNod.insertBefore(a, nxtSib); - t.area_select(start, end-start); - } - - // force update of selection field - this.focus(); - this.update_size(); - this.check_line_selection(); - }; - - EditArea.prototype.change_font_size= function(){ - var size=_$("area_font_size").value; - if(size>0) - this.set_font("", size); - }; - - - EditArea.prototype.open_inline_popup= function(popup_id){ - this.close_all_inline_popup(); - var popup= _$(popup_id); - var editor= _$("editor"); - - // search matching icon - for(var i=0; i lines.length) - start= this.textarea.value.length; - else{ - for(var i=0; i0){ - //alert(miss_top); - zone.scrollTop= zone.scrollTop + miss_top; - }else if( zone.scrollTop > cursor_pos_top){ - // when erase all the content -> does'nt scroll back to the top - //alert("else: "+cursor_pos_top); - zone.scrollTop= cursor_pos_top; - } - - // manage left scroll - //var cursor_pos_left= parseInt(_$("cursor_pos").style.left.replace("px","")); - var cursor_pos_left= _$("cursor_pos").cursor_left; - var max_width_visible= zone.clientWidth + zone.scrollLeft; - var miss_left= cursor_pos_left + 10 - max_width_visible; - if(miss_left>0){ - zone.scrollLeft= zone.scrollLeft + miss_left + 50; - }else if( zone.scrollLeft > cursor_pos_left){ - zone.scrollLeft= cursor_pos_left ; - }else if( zone.scrollLeft == 45){ - // show the line numbers if textarea align to it's left - zone.scrollLeft=0; - } - }; - - EditArea.prototype.check_undo= function(only_once){ - if(!editAreas[this.id]) - return false; - if(this.textareaFocused && editAreas[this.id]["displayed"]==true){ - var text=this.textarea.value; - if(this.previous.length<=1) - this.switchClassSticky(_$("undo"), 'editAreaButtonDisabled', true); - - if(!this.previous[this.previous.length-1] || this.previous[this.previous.length-1]["text"] != text){ - this.previous.push({"text": text, "selStart": this.textarea.selectionStart, "selEnd": this.textarea.selectionEnd}); - if(this.previous.length > this.settings["max_undo"]+1) - this.previous.shift(); - - } - if(this.previous.length >= 2) - this.switchClassSticky(_$("undo"), 'editAreaButtonNormal', false); - } - - if(!only_once) - setTimeout("editArea.check_undo()", 3000); - }; - - EditArea.prototype.undo= function(){ - //alert("undo"+this.previous.length); - if(this.previous.length > 0) - { - this.getIESelection(); - // var pos_cursor=this.textarea.selectionStart; - this.next.push( { "text": this.textarea.value, "selStart": this.textarea.selectionStart, "selEnd": this.textarea.selectionEnd } ); - var prev= this.previous.pop(); - if( prev["text"] == this.textarea.value && this.previous.length > 0 ) - prev =this.previous.pop(); - this.textarea.value = prev["text"]; - this.last_undo = prev["text"]; - this.area_select(prev["selStart"], prev["selEnd"]-prev["selStart"]); - this.switchClassSticky(_$("redo"), 'editAreaButtonNormal', false); - this.resync_highlight(true); - //alert("undo"+this.previous.length); - this.check_file_changes(); - } - }; - - EditArea.prototype.redo= function(){ - if(this.next.length > 0) - { - /*this.getIESelection();*/ - //var pos_cursor=this.textarea.selectionStart; - var next= this.next.pop(); - this.previous.push(next); - this.textarea.value= next["text"]; - this.last_undo= next["text"]; - this.area_select(next["selStart"], next["selEnd"]-next["selStart"]); - this.switchClassSticky(_$("undo"), 'editAreaButtonNormal', false); - this.resync_highlight(true); - this.check_file_changes(); - } - if( this.next.length == 0) - this.switchClassSticky(_$("redo"), 'editAreaButtonDisabled', true); - }; - - EditArea.prototype.check_redo= function(){ - if(editArea.next.length == 0 || editArea.textarea.value!=editArea.last_undo){ - editArea.next= []; // undo the ability to use "redo" button - editArea.switchClassSticky(_$("redo"), 'editAreaButtonDisabled', true); - } - else - { - this.switchClassSticky(_$("redo"), 'editAreaButtonNormal', false); - } - }; - - - // functions that manage icons roll over, disabled, etc... - EditArea.prototype.switchClass = function(element, class_name, lock_state) { - var lockChanged = false; - - if (typeof(lock_state) != "undefined" && element != null) { - element.classLock = lock_state; - lockChanged = true; - } - - if (element != null && (lockChanged || !element.classLock)) { - element.oldClassName = element.className; - element.className = class_name; - } - }; - - EditArea.prototype.restoreAndSwitchClass = function(element, class_name) { - if (element != null && !element.classLock) { - this.restoreClass(element); - this.switchClass(element, class_name); - } - }; - - EditArea.prototype.restoreClass = function(element) { - if (element != null && element.oldClassName && !element.classLock) { - element.className = element.oldClassName; - element.oldClassName = null; - } - }; - - EditArea.prototype.setClassLock = function(element, lock_state) { - if (element != null) - element.classLock = lock_state; - }; - - EditArea.prototype.switchClassSticky = function(element, class_name, lock_state) { - var lockChanged = false; - if (typeof(lock_state) != "undefined" && element != null) { - element.classLock = lock_state; - lockChanged = true; - } - - if (element != null && (lockChanged || !element.classLock)) { - element.className = class_name; - element.oldClassName = class_name; - } - }; - - //make the "page up" and "page down" buttons works correctly - EditArea.prototype.scroll_page= function(params){ - var dir= params["dir"], shift_pressed= params["shift"]; - var lines= this.textarea.value.split("\n"); - var new_pos=0, length=0, char_left=0, line_nb=0, curLine=0; - var toScrollAmount = _$("result").clientHeight -30; - var nbLineToScroll = 0, diff= 0; - - if(dir=="up"){ - nbLineToScroll = Math.ceil( toScrollAmount / this.lineHeight ); - - // fix number of line to scroll - for( i = this.last_selection["line_start"]; i - diff > this.last_selection["line_start"] - nbLineToScroll ; i-- ) - { - if( elem = _$('line_'+ i) ) - { - diff += Math.floor( ( elem.offsetHeight - 1 ) / this.lineHeight ); - } - } - nbLineToScroll -= diff; - - if(this.last_selection["selec_direction"]=="up"){ - for(line_nb=0; line_nb< Math.min(this.last_selection["line_start"]-nbLineToScroll, lines.length); line_nb++){ - new_pos+= lines[line_nb].length + 1; - } - char_left=Math.min(lines[Math.min(lines.length-1, line_nb)].length, this.last_selection["curr_pos"]-1); - if(shift_pressed) - length=this.last_selection["selectionEnd"]-new_pos-char_left; - this.area_select(new_pos+char_left, length); - view="top"; - }else{ - view="bottom"; - for(line_nb=0; line_nb< Math.min(this.last_selection["line_start"]+this.last_selection["line_nb"]-1-nbLineToScroll, lines.length); line_nb++){ - new_pos+= lines[line_nb].length + 1; - } - char_left=Math.min(lines[Math.min(lines.length-1, line_nb)].length, this.last_selection["curr_pos"]-1); - if(shift_pressed){ - //length=this.last_selection["selectionEnd"]-new_pos-char_left; - start= Math.min(this.last_selection["selectionStart"], new_pos+char_left); - length= Math.max(new_pos+char_left, this.last_selection["selectionStart"] )- start ; - if(new_pos+char_left < this.last_selection["selectionStart"]) - view="top"; - }else - start=new_pos+char_left; - this.area_select(start, length); - - } - } - else - { - var nbLineToScroll= Math.floor( toScrollAmount / this.lineHeight ); - // fix number of line to scroll - for( i = this.last_selection["line_start"]; i + diff < this.last_selection["line_start"] + nbLineToScroll ; i++ ) - { - if( elem = _$('line_'+ i) ) - { - diff += Math.floor( ( elem.offsetHeight - 1 ) / this.lineHeight ); - } - } - nbLineToScroll -= diff; - - if(this.last_selection["selec_direction"]=="down"){ - view="bottom"; - for(line_nb=0; line_nb< Math.min(this.last_selection["line_start"]+this.last_selection["line_nb"]-2+nbLineToScroll, lines.length); line_nb++){ - if(line_nb==this.last_selection["line_start"]-1) - char_left= this.last_selection["selectionStart"] -new_pos; - new_pos+= lines[line_nb].length + 1; - - } - if(shift_pressed){ - length=Math.abs(this.last_selection["selectionStart"]-new_pos); - length+=Math.min(lines[Math.min(lines.length-1, line_nb)].length, this.last_selection["curr_pos"]); - //length+=Math.min(lines[Math.min(lines.length-1, line_nb)].length, char_left); - this.area_select(Math.min(this.last_selection["selectionStart"], new_pos), length); - }else{ - this.area_select(new_pos+char_left, 0); - } - - }else{ - view="top"; - for(line_nb=0; line_nb< Math.min(this.last_selection["line_start"]+nbLineToScroll-1, lines.length, lines.length); line_nb++){ - if(line_nb==this.last_selection["line_start"]-1) - char_left= this.last_selection["selectionStart"] -new_pos; - new_pos+= lines[line_nb].length + 1; - } - if(shift_pressed){ - length=Math.abs(this.last_selection["selectionEnd"]-new_pos-char_left); - length+=Math.min(lines[Math.min(lines.length-1, line_nb)].length, this.last_selection["curr_pos"])- char_left-1; - //length+=Math.min(lines[Math.min(lines.length-1, line_nb)].length, char_left); - this.area_select(Math.min(this.last_selection["selectionEnd"], new_pos+char_left), length); - if(new_pos+char_left > this.last_selection["selectionEnd"]) - view="bottom"; - }else{ - this.area_select(new_pos+char_left, 0); - } - - } - } - //console.log( new_pos, char_left, length, nbLineToScroll, toScrollAmount, _$("result").clientHeigh ); - this.check_line_selection(); - this.scroll_to_view(view); - }; - - EditArea.prototype.start_resize= function(e){ - parent.editAreaLoader.resize["id"] = editArea.id; - parent.editAreaLoader.resize["start_x"] = (e)? e.pageX : event.x + document.body.scrollLeft; - parent.editAreaLoader.resize["start_y"] = (e)? e.pageY : event.y + document.body.scrollTop; - if(editArea.isIE) - { - editArea.textarea.focus(); - editArea.getIESelection(); - } - parent.editAreaLoader.resize["selectionStart"] = editArea.textarea.selectionStart; - parent.editAreaLoader.resize["selectionEnd"] = editArea.textarea.selectionEnd; - parent.editAreaLoader.start_resize_area(); - }; - - EditArea.prototype.toggle_full_screen= function(to){ - var t=this, p=parent, a=t.textarea, html, frame, selStart, selEnd, old, icon; - if(typeof(to)=="undefined") - to= !t.fullscreen['isFull']; - old = t.fullscreen['isFull']; - t.fullscreen['isFull']= to; - icon = _$("fullscreen"); - selStart = t.textarea.selectionStart; - selEnd = t.textarea.selectionEnd; - html = p.document.getElementsByTagName("html")[0]; - frame = p.document.getElementById("frame_"+t.id); - - if(to && to!=old) - { // toogle on fullscreen - - t.fullscreen['old_overflow'] = p.get_css_property(html, "overflow"); - t.fullscreen['old_height'] = p.get_css_property(html, "height"); - t.fullscreen['old_width'] = p.get_css_property(html, "width"); - t.fullscreen['old_scrollTop'] = html.scrollTop; - t.fullscreen['old_scrollLeft'] = html.scrollLeft; - t.fullscreen['old_zIndex'] = p.get_css_property(frame, "z-index"); - if(t.isOpera){ - html.style.height = "100%"; - html.style.width = "100%"; - } - html.style.overflow = "hidden"; - html.scrollTop = 0; - html.scrollLeft = 0; - - frame.style.position = "absolute"; - frame.style.width = html.clientWidth+"px"; - frame.style.height = html.clientHeight+"px"; - frame.style.display = "block"; - frame.style.zIndex = "999999"; - frame.style.top = "0px"; - frame.style.left = "0px"; - - // if the iframe was in a div with position absolute, the top and left are the one of the div, - // so I fix it by seeing at witch position the iframe start and correcting it - frame.style.top = "-"+p.calculeOffsetTop(frame)+"px"; - frame.style.left = "-"+p.calculeOffsetLeft(frame)+"px"; - - // parent.editAreaLoader.execCommand(t.id, "update_size();"); - // var body=parent.document.getElementsByTagName("body")[0]; - // body.appendChild(frame); - - t.switchClassSticky(icon, 'editAreaButtonSelected', false); - t.fullscreen['allow_resize']= t.resize_allowed; - t.allow_resize(false); - - //t.area_select(selStart, selEnd-selStart); - - - // opera can't manage to do a direct size update - if(t.isFirefox){ - p.editAreaLoader.execCommand(t.id, "update_size();"); - t.area_select(selStart, selEnd-selStart); - t.scroll_to_view(); - t.focus(); - }else{ - setTimeout("p.editAreaLoader.execCommand('"+ t.id +"', 'update_size();');editArea.focus();", 10); - } - - - } - else if(to!=old) - { // toogle off fullscreen - frame.style.position="static"; - frame.style.zIndex= t.fullscreen['old_zIndex']; - - if(t.isOpera) - { - html.style.height = "auto"; - html.style.width = "auto"; - html.style.overflow = "auto"; - } - else if(t.isIE && p!=top) - { // IE doesn't manage html overflow in frames like in normal page... - html.style.overflow = "auto"; - } - else - { - html.style.overflow = t.fullscreen['old_overflow']; - } - html.scrollTop = t.fullscreen['old_scrollTop']; - html.scrollLeft = t.fullscreen['old_scrollLeft']; - - p.editAreaLoader.hide(t.id); - p.editAreaLoader.show(t.id); - - t.switchClassSticky(icon, 'editAreaButtonNormal', false); - if(t.fullscreen['allow_resize']) - t.allow_resize(t.fullscreen['allow_resize']); - if(t.isFirefox){ - t.area_select(selStart, selEnd-selStart); - setTimeout("editArea.scroll_to_view();", 10); - } - - //p.editAreaLoader.remove_event(p.window, "resize", editArea.update_size); - } - - }; - - EditArea.prototype.allow_resize= function(allow){ - var resize= _$("resize_area"); - if(allow){ - - resize.style.visibility="visible"; - parent.editAreaLoader.add_event(resize, "mouseup", editArea.start_resize); - }else{ - resize.style.visibility="hidden"; - parent.editAreaLoader.remove_event(resize, "mouseup", editArea.start_resize); - } - this.resize_allowed= allow; - }; - - - EditArea.prototype.change_syntax= function(new_syntax, is_waiting){ - // alert("cahnge to "+new_syntax); - // the syntax is the same - if(new_syntax==this.settings['syntax']) - return true; - - // check that the syntax is one allowed - var founded= false; - for(var i=0; i"; - elem.innerHTML= "*"+ this.files[id]['title'] + close +""; - _$('tab_browsing_list').appendChild(elem); - var elem= document.createElement('text'); - this.update_size(); - } - - // open file callback (for plugin) - if(id!="") - this.execCommand('file_open', this.files[id]); - - this.switch_to_file(id, true); - return true; - } - else - return false; - }; - - // close the given file - EditArea.prototype.close_file= function(id){ - if(this.files[id]) - { - this.save_file(id); - - // close file callback - if(this.execCommand('file_close', this.files[id])!==false) - { - // remove the tab in the toolbar - var li= _$(this.files[id]['html_id']); - li.parentNode.removeChild(li); - // select a new file - if(id== this.curr_file) - { - var next_file= ""; - var is_next= false; - for(var i in this.files) - { - if( is_next ) - { - next_file = i; - break; - } - else if( i == id ) - is_next = true; - else - next_file = i; - } - // display the next file - this.switch_to_file(next_file); - } - // clear datas - delete (this.files[id]); - this.update_size(); - } - } - }; - - // backup current file datas - EditArea.prototype.save_file= function(id){ - var t= this, save, a_links, a_selects, save_butt, img, i; - if(t.files[id]) - { - var save= t.files[id]; - save['last_selection'] = t.last_selection; - save['last_text_to_highlight'] = t.last_text_to_highlight; - save['last_hightlighted_text'] = t.last_hightlighted_text; - save['previous'] = t.previous; - save['next'] = t.next; - save['last_undo'] = t.last_undo; - save['smooth_selection'] = t.smooth_selection; - save['do_highlight'] = t.do_highlight; - save['syntax'] = t.settings['syntax']; - save['text'] = t.textarea.value; - save['scroll_top'] = t.result.scrollTop; - save['scroll_left'] = t.result.scrollLeft; - save['selection_start'] = t.last_selection["selectionStart"]; - save['selection_end'] = t.last_selection["selectionEnd"]; - save['font_size'] = t.settings["font_size"]; - save['font_family'] = t.settings["font_family"]; - save['word_wrap'] = t.settings["word_wrap"]; - save['toolbar'] = {'links':{}, 'selects': {}}; - - // save toolbar buttons state for fileSpecific buttons - a_links= _$("toolbar_1").getElementsByTagName("a"); - for( i=0; i