Author: andreas
Date: Thu Jan 29 09:49:40 2009
New Revision: 738808
URL: http://svn.apache.org/viewvc?rev=738808&view=rev
Log:
Adding CodeMirror syntax highlighting for source editor. See bug 46611.
Added:
lenya/trunk/legal/codemirror.license.txt
lenya/trunk/src/modules/editors/resources/codemirror/
lenya/trunk/src/modules/editors/resources/codemirror/0.60/
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/csscolors.css
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/docs.css
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/jscolors.css
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/people.jpg (with props)
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/sparqlcolors.css
lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/xmlcolors.css
lenya/trunk/src/modules/editors/resources/codemirror/0.60/csstest.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/htmltest.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/index.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/codemirror.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/editor.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/mirrorframe.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsecss.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsehtmlmixed.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsejavascript.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsesparql.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsexml.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/select.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/stringstream.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/tokenize.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/tokenizejavascript.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/undo.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/util.js
lenya/trunk/src/modules/editors/resources/codemirror/0.60/jstest.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/manual.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/mixedtest.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/sparqltest.html
lenya/trunk/src/modules/editors/resources/codemirror/0.60/story.html
lenya/trunk/src/modules/editors/resources/css/
lenya/trunk/src/modules/editors/resources/css/codemirror.css
lenya/trunk/src/modules/editors/resources/css/sourceEditor.css
lenya/trunk/src/modules/editors/resources/javascript/sourceEditor/
lenya/trunk/src/modules/editors/resources/javascript/sourceEditor/codemirror.js
lenya/trunk/src/modules/editors/resources/javascript/sourceEditor/lenya_glue.js
Removed:
lenya/trunk/src/modules/editors/resources/javascript/oneform_lenya_glue.js
Modified:
lenya/trunk/src/modules/editors/config/module.xml
lenya/trunk/src/modules/editors/usecases/forms/oneform.jx
lenya/trunk/src/modules/editors/usecases/forms/oneform.xsl
Added: lenya/trunk/legal/codemirror.license.txt
URL: http://svn.apache.org/viewvc/lenya/trunk/legal/codemirror.license.txt?rev=738808&view=auto
==============================================================================
--- lenya/trunk/legal/codemirror.license.txt (added)
+++ lenya/trunk/legal/codemirror.license.txt Thu Jan 29 09:49:40 2009
@@ -0,0 +1,23 @@
+ Copyright (c) 2007-2008 Marijn Haverbeke
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ Marijn Haverbeke
+ marijnh at gmail
Modified: lenya/trunk/src/modules/editors/config/module.xml
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/config/module.xml?rev=738808&r1=738807&r2=738808&view=diff
==============================================================================
--- lenya/trunk/src/modules/editors/config/module.xml (original)
+++ lenya/trunk/src/modules/editors/config/module.xml Thu Jan 29 09:49:40 2009
@@ -25,6 +25,7 @@
<depends module="org.apache.lenya.modules.sitemanagement"/>
<depends module="org.apache.lenya.modules.linking"/>
<depends module="org.apache.lenya.modules.workflow"/>
+ <depends module="org.apache.lenya.modules.codemirror"/>
<package>org.apache.lenya.modules</package>
<version>0.3-dev</version>
<name>Editors</name>
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/csscolors.css
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/csscolors.css?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/csscolors.css (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/csscolors.css Thu Jan 29 09:49:40 2009
@@ -0,0 +1,47 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+pre.code, .editbox {
+ color: #666666;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.css-at {
+ color: #770088;
+}
+
+span.css-unit {
+ color: #228811;
+}
+
+span.css-value {
+ color: #770088;
+}
+
+span.css-identifier {
+ color: black;
+}
+
+span.css-important {
+ color: #0000FF;
+}
+
+span.css-colorcode {
+ color: #004499;
+}
+
+span.css-comment {
+ color: #AA7700;
+}
+
+span.css-string {
+ color: #AA2222;
+}
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/docs.css
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/docs.css?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/docs.css (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/docs.css Thu Jan 29 09:49:40 2009
@@ -0,0 +1,42 @@
+body {
+ margin: 0;
+ font-family: tahoma, arial, sans-serif;
+ padding: 3em 6em;
+ color: black;
+}
+
+h1 {
+ font-size: 22pt;
+}
+
+h2 {
+ font-size: 14pt;
+}
+
+p.rel {
+ padding-left: 2em;
+ text-indent: -2em;
+}
+
+div.border {
+ border: 1px solid black;
+ padding: 3px;
+}
+
+code {
+ font-family: courier, monospace;
+ font-size: 90%;
+ color: #155;
+}
+
+pre.code {
+ margin: 1.1em 12px;
+ border: 1px solid #CCCCCC;
+ color: black;
+ padding: .4em;
+ font-family: courier, monospace;
+}
+
+.warn {
+ color: #C00;
+}
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/jscolors.css
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/jscolors.css?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/jscolors.css (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/jscolors.css Thu Jan 29 09:49:40 2009
@@ -0,0 +1,55 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+pre.code, .editbox {
+ color: #666666;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.js-punctuation {
+ color: #666666;
+}
+
+span.js-operator {
+ color: #666666;
+}
+
+span.js-keyword {
+ color: #770088;
+}
+
+span.js-atom {
+ color: #228811;
+}
+
+span.js-variable {
+ color: black;
+}
+
+span.js-variabledef {
+ color: #0000FF;
+}
+
+span.js-localvariable {
+ color: #004499;
+}
+
+span.js-property {
+ color: black;
+}
+
+span.js-comment {
+ color: #AA7700;
+}
+
+span.js-string {
+ color: #AA2222;
+}
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/people.jpg
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/people.jpg?rev=738808&view=auto
==============================================================================
Binary file - no diff available.
Propchange: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/people.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/sparqlcolors.css
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/sparqlcolors.css?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/sparqlcolors.css (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/sparqlcolors.css Thu Jan 29 09:49:40 2009
@@ -0,0 +1,39 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.sp-keyword {
+ color: #708;
+}
+
+span.sp-prefixed {
+ color: #5d1;
+}
+
+span.sp-var {
+ color: #00c;
+}
+
+span.sp-comment {
+ color: #a70;
+}
+
+span.sp-literal {
+ color: #a22;
+}
+
+span.sp-uri {
+ color: #292;
+}
+
+span.sp-operator {
+ color: #088;
+}
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/xmlcolors.css
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/xmlcolors.css?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/xmlcolors.css (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/css/xmlcolors.css Thu Jan 29 09:49:40 2009
@@ -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;
+}
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/csstest.html
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/csstest.html?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/csstest.html (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/csstest.html Thu Jan 29 09:49:40 2009
@@ -0,0 +1,60 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script src="js/codemirror.js" type="text/javascript"></script>
+ <title>CodeMirror: CSS demonstration</title>
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ </head>
+ <body style="padding: 20px;">
+
+<p>Demonstration of <a href="index.html">CodeMirror</a>'s CSS
+highlighter.</p>
+
+<div class="border">
+<textarea id="code" cols="120" rows="30">
+/* Some example CSS */
+
+@import url("something.css");
+
+body {
+ margin: 0;
+ padding: 3em 6em;
+ font-family: tahoma, arial, sans-serif;
+ color: #000;
+}
+
+#navigation a {
+ font-weight: bold;
+ text-decoration: none !important;
+}
+
+h1 {
+ font-size: 2.5em;
+}
+
+h2 {
+ font-size: 1.7em;
+}
+
+h1:before, h2:before {
+ content: "::";
+}
+
+code {
+ font-family: courier, monospace;
+ font-size: 80%;
+ color: #418A8A;
+}
+</textarea>
+</div>
+
+<script type="text/javascript">
+ var editor = CodeMirror.fromTextArea('code', {
+ height: "350px",
+ parserfile: "parsecss.js",
+ stylesheet: "css/csscolors.css",
+ path: "js/"
+ });
+</script>
+
+ </body>
+</html>
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/htmltest.html
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/htmltest.html?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/htmltest.html (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/htmltest.html Thu Jan 29 09:49:40 2009
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script src="js/codemirror.js" type="text/javascript"></script>
+ <title>CodeMirror: HTML/XML demonstration</title>
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ </head>
+ <body style="padding: 20px;">
+
+<p>This is a simple demonstration of the XML/HTML indentation module
+for <a href="index.html">CodeMirror</a>. The <a
+href="js/parsexml.js">javascript</a> file contains some comments with
+more information.</p>
+
+<div style="border: 1px solid black; padding: 3px;">
+<textarea id="code" cols="120" rows="30">
+<html style="color: green">
+ <!-- this is a comment -->
+ <head>
+ <title>HTML Example</title>
+ </head>
+ <body>
+ The indentation tries to be <em>somewhat &quot;do what
+ I mean&quot;</em>... but might not match your style.
+ </body>
+</html>
+</textarea>
+</div>
+
+<script type="text/javascript">
+ var editor = CodeMirror.fromTextArea('code', {
+ height: "350px",
+ parserfile: "parsexml.js",
+ stylesheet: "css/xmlcolors.css",
+ path: "js/",
+ continuousScanning: 500
+ });
+</script>
+ </body>
+</html>
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/index.html
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/index.html?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/index.html (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/index.html Thu Jan 29 09:49:40 2009
@@ -0,0 +1,163 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>CodeMirror: In-browser code editing</title>
+
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ <style type="text/css">
+ div.top {text-align: center;}
+ div.top h1 {margin-bottom: 0;}
+ div.top h2 {margin-top: 0; margin-bottom: 1.5em;}
+ </style>
+ </head>
+ <body>
+
+<div style="float: right; padding-left: 10px;"><img src="css/people.jpg" alt=""/></div>
+<div class="top">
+ <h1>CodeMirror</h1>
+ <h2>In-browser code editing made slightly less painful</h2>
+</div>
+
+<p>CodeMirror is a JavaScript program that can be used to create a
+relatively pleasant editor interface for code-like content ― computer
+programs, HTML markup, and similar. If a parser has been written for
+the language you are editing (we currently have <a
+href="jstest.html">JavaScript</a>, <a href="htmltest.html">XML</a>, <a
+href="csstest.html">CSS</a>, and <a href="sparqltest.html">SPARQL</a>
+covered), the code will be prettily coloured, and indentation will be
+taken care of for you, meaning the cursor will be placed at the right
+position when you press enter, and it is possible to re-indent blocks
+of code automatically.</p>
+
+<p>To get a look at CodeMirror, see the test pages for the various
+parsers...</p>
+
+<ul>
+ <li><a href="jstest.html">JavaScript</a></li>
+ <li><a href="htmltest.html">XML/HTML</a></li>
+ <li><a href="csstest.html">CSS</a></li>
+ <li><a href="sparqltest.html">SPARQL</a></li>
+ <li><a href="mixedtest.html">HTML mixed-mode</a></li>
+</ul>
+
+<p>Or take a look at some real-world uses of the system...</p>
+
+<ul>
+ <li><a href="http://code.google.com/p/rainbow4firebug/">Rainbow for Firebug</a></li>
+ <li><a href="http://dev.freebaseapps.com/">Freebase's Acre IDE</a></li>
+ <li><a href="http://kml-samples.googlecode.com/svn/trunk/interactive/index.html">Google Earth KML sampler</a></li>
+ <li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript's console</a></li>
+ <li><a href="http://billmill.org/static/canvastutorial/index.html">A cool tutorial about the <canvas> element</a></li>
+ <li><a href="http://orc.csres.utexas.edu/tryorc.shtml">An online IDE for the Orc programming language</a></li>
+</ul>
+
+<h2>Releases</h2>
+
+<p class="rel"><em>29-12-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.60.zip">Version
+0.60</a>: This release makes lots of internal changes, so test before
+you upgrade. More robust selection-preservation on IE, allowing styles
+with different font sizes. New <code>activeTokens</code> and
+<code>cursorActivity</code> callbacks, and a more powerful, line-based
+interface for inspecting and manipulating the content of the editor
+(see <a href="manual.html">manual</a>). Fixes the
+<code>replaceSelection</code> problem in IE, and a lot of other,
+smaller issues.</p>
+
+<p class="rel"><em>28-09-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.58.zip">Version
+0.58</a>: Add parsers for SPARQL and HTML-mixed-mode (nests CSS and JS
+parsers). Also: bracket highlighting, a 'dumb tabs' mode, an
+<code>onChange</code> callback, and heaps of bugfixes. See the manual
+for details on the new features.</p>
+
+<p class="rel"><em>04-07-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.57.zip">Version
+0.57</a>: A CSS parser and a nice tokenizer framework, bugfixes in the
+XML parser, a few browser-issue workarounds, one of which should fix
+the age-old Firefox cursor-showing-on-wrong line bug.</p>
+
+<p class="rel"><em>25-04-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.56.zip">Version
+0.56</a>: Fixes for some minor bugs, no new features.</p>
+
+<p class="rel"><em>30-03-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.55.zip">Version
+0.55</a>: Fixes a bug that broke redo, adds <code>readOnly</code>
+option.</p>
+
+<p class="rel"><em>10-03-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.54.zip">Version
+0.54</a>: Some new utility functions and options (see
+<code>fromTextArea</code>, <code>saveFunction</code>,
+<code>initCallback</code>, <code>textWrapping</code>,
+<code>disableSpellcheck</code>, <code>jumpToLine</code>, and
+<code>reindent</code> in the <a href="manual.html">manual</a>), many
+bugfixes, and some cleaning up and shrinking of the codebase (<a
+href="http://www.mochikit.com">MochiKit</a> is no longer used).</p>
+
+<p class="rel"><em>26-01-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.53.zip">Version
+0.53</a>: Adds support for continuous scanning, search/replace,
+undo/redo, jump-to-line, selection getting and setting. See <a
+href="manual.html">the manual</a> for details. Also fixes a bucket of
+bugs.</p>
+
+<h2 id="supported">Supported browsers</h2>
+
+<p>At this time, the following browsers are supported:</p>
+
+<ul>
+ <li>Firefox 1.5 or higher</li>
+ <li>Internet Explorer 6 or higher</li>
+ <li>Safari 3 or higher</li>
+ <li>Opera 9.52 or higher</li>
+ <li>Chrome</li>
+</ul>
+
+<p>Making it work on other browsers that have decent support for the
+W3C DOM model should not be too hard, but I am not actively testing
+against those.</p>
+
+<h2>Getting the code</h2>
+
+<p>All of CodeMirror is released under a <a
+href="LICENSE">BSD-style</a> license. To get it, you can download the
+<a href="http://marijn.haverbeke.nl/codemirror/codemirror.zip">latest
+release</a> or the current <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-latest.zip">development
+snapshot</a> as zip files, or use the <a
+href="http://www.darcs.net/">darcs</a> version control system to get
+the repository:</p>
+
+<pre class="code">darcs get http://marijn.haverbeke.nl/codemirror</pre>
+
+<p>This second method is recommended if you are planning to hack on
+CodeMirror ― it makes it easy to record your patches and share them
+with me. To see the repository online, visit the <a
+href="http://marijn.haverbeke.nl/darcsweb.cgi?r=CodeMirror">CodeMirror
+darcsweb</a>.</p>
+
+<h2>Support</h2>
+
+<p>There is a <a
+href="http://groups.google.com/group/codemirror">Google group</a> (a
+sort of mailing list/newsgroup thingy) for discussion and news related
+to CodeMirror. You can also e-mail me directly: <a
+href="mailto:[email protected]">Marijn Haverbeke</a>.</p>
+
+<h2>Documentation</h2>
+
+<ul>
+ <li>The <a href="manual.html">manual</a> is all most users will need
+ to read (or skim).</li>
+ <li>If you're interested in working on the code, <a
+ href="story.html">this document</a> about CodeMirror's architecture
+ will be useful.</li>
+ <li>The <a
+ href="http://marijn.haverbeke.nl/darcsweb.cgi?r=CodeMirror;a=tree">source
+ code</a> is, for the most part, rather well commented, so if all
+ else fails, you can try reading it.</li>
+</ul>
+
+ </body>
+</html>
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/codemirror.js
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/codemirror.js?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/codemirror.js (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/codemirror.js Thu Jan 29 09:49:40 2009
@@ -0,0 +1,219 @@
+/* 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"],
+ linesPerPass: 15,
+ passDelay: 200,
+ continuousScanning: false,
+ saveFunction: null,
+ onChange: null,
+ undoDepth: 20,
+ undoDelay: 800,
+ disableSpellcheck: true,
+ textWrapping: true,
+ readOnly: false,
+ width: "100%",
+ height: "300px",
+ autoMatchParens: false,
+ parserConfig: null,
+ dumbTabs: false,
+ activeTokens: null,
+ cursorActivity: null
+ });
+
+ function CodeMirror(place, options) {
+ // Use passed options, if any, to override defaults.
+ this.options = options = options || {};
+ setDefaults(options, CodeMirrorConfig);
+
+ var frame = this.frame = document.createElement("IFRAME");
+ 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)
+ place.appendChild(frame);
+ else
+ 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 = ["<html><head>"];
+ forEach(options.stylesheet, function(file) {
+ html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
+ });
+ forEach(options.basefiles.concat(options.parserfile), function(file) {
+ html.push("<script type=\"text/javascript\" src=\"" + options.path + file + "\"></script>");
+ });
+ html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
+ (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
+
+ var doc = this.win.document;
+ doc.open();
+ doc.write(html.join(""));
+ doc.close();
+ }
+
+ CodeMirror.prototype = {
+ getCode: function() {return this.editor.getCode();},
+ setCode: function(code) {this.editor.importCode(code);},
+ selection: function() {return this.editor.selectedText();},
+ reindent: function() {this.editor.reindent();},
+
+ 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);
+ },
+
+ 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 = area.style.width;
+ if (area.style.height) 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 (/Chrome\//.test(navigator.userAgent))
+ return true;
+ else
+ return null;
+ };
+
+ return CodeMirror;
+})();
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/editor.js
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/editor.js?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/editor.js (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/editor.js Thu Jan 29 09:49:40 2009
@@ -0,0 +1,1176 @@
+/* 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 <span> and <br> elements
+ */
+
+var safeWhiteSpace, splitSpaces;
+function setWhiteSpaceModel(collapsing) {
+ safeWhiteSpace = collapsing ?
+ // Make sure a string does not contain two consecutive 'collapseable'
+ // whitespace characters.
+ function(n) {
+ var buffer = [], nb = true;
+ for (; n > 0; n--) {
+ buffer.push((nb || n == 1) ? nbsp : " ");
+ nb = !nb;
+ }
+ return buffer.join("");
+ } :
+ function(n) {
+ var buffer = [];
+ for (; n > 0; n--) buffer.push(" ");
+ return buffer.join("");
+ };
+ splitSpaces = collapsing ?
+ // Create a set of white-space characters that will not be collapsed
+ // by the browser, but will not break text-wrapping either.
+ function(string) {
+ if (string.charAt(0) == " ") string = nbsp + string.slice(1);
+ return string.replace(/[\t \u00a0]{2,}/g, function(s) {return safeWhiteSpace(s.length);});
+ } :
+ function(string) {return string;};
+}
+
+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;
+}
+
+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) {
+ return splitSpaces(string.replace(/\t/g, " ").replace(/\u00a0/g, " ")).replace(/\r\n?/g, "\n").split("\n");
+ }
+
+ var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
+
+ // Helper function for traverseDOM. Flattens an arbitrary DOM node
+ // into an array of textnodes and <br> tags.
+ function simplifyDOM(root) {
+ var doc = root.ownerDocument;
+ var result = [];
+ var leaving = false;
+
+ function simplifyNode(node) {
+ if (node.nodeType == 3) {
+ var text = node.nodeValue = splitSpaces(node.nodeValue.replace(/[\n\r]/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;
+ result.push(doc.createElement("BR"));
+ }
+ }
+ }
+
+ simplifyNode(root);
+ 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
+ // <span> and <br> 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 <span>, 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){
+ var toYield = [];
+ forEach(simplifyDOM(node), function(part) {
+ toYield.push(insertPart(part));
+ });
+ return yield(toYield.join(""), c);
+ }
+
+ // Check whether a node is a normalized <span> element.
+ function partNode(node){
+ if (node.nodeName == "SPAN" && node.childNodes.length == 1 && node.firstChild.nodeType == 3 && node.isPart) {
+ 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 {
+ point = pointAt(node);
+ removeElement(node);
+ return writeNode(node, c);
+ }
+ }
+
+ // 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;
+ while (node && node.nodeName != "BR") node = node.nextSibling;
+ return node;
+ }
+
+ function cleanText(text) {
+ return text.replace(/\u00a0/g, " ");
+ }
+
+ // 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;
+ this.parent = parent;
+ this.doc = document;
+ this.container = this.doc.body;
+ this.win = window;
+ this.history = new History(this.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.textWrapping)
+ this.container.style.whiteSpace = "pre";
+ setWhiteSpaceModel(options.textWrapping);
+
+ if (!options.readOnly)
+ select.setCursorPos(this.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
+ this.container.appendChild(this.doc.createElement("BR"));
+
+ if (!options.readOnly) {
+ if (options.continuousScanning !== false) {
+ this.scanner = this.documentScanner(options.linesPerPass);
+ this.delayScanning();
+ }
+
+ function setEditable() {
+ // In IE, designMode frames can not run any scripts, so we use
+ // contentEditable instead.
+ if (document.body.contentEditable != undefined && /MSIE/.test(navigator.userAgent))
+ document.body.contentEditable = "true";
+ else
+ document.designMode = "on";
+ }
+
+ // 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, "paste", cursorActivity);
+ addEventHandler(document.body, "cut", cursorActivity);
+ addEventHandler(document.body, "mouseup", cursorActivity);
+
+ if (this.options.autoMatchParens)
+ addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
+ }
+ }
+
+ 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"));
+ 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 ? line.nextSibling : this.container.firstChild, 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);
+ },
+
+ 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(line.innerText || line.textContent || line.nodeValue || "");
+ 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 ? line.nextSibling : this.container.firstChild, 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 (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);
+ },
+
+ // Intercept enter and tab, and assign their new functions.
+ keyDown: function(event) {
+ // Don't scan when the user is typing.
+ this.delayScanning();
+ // Schedule a paren-highlight event, if configured.
+ if (this.options.autoMatchParens)
+ this.scheduleParenBlink();
+
+ if (event.keyCode == 13) { // enter
+ if (event.ctrlKey) {
+ this.reparseBuffer();
+ }
+ else {
+ select.insertNewlineAtCursor(this.win);
+ this.indentAtCursor();
+ select.scrollToCursor(this.container);
+ }
+ event.stop();
+ }
+ else if (event.keyCode == 9) { // tab
+ this.handleTab(!event.ctrlKey && !event.shiftKey);
+ event.stop();
+ }
+ else if (event.ctrlKey || event.metaKey) {
+ if (event.keyCode == 90 || event.keyCode == 8) { // Z, backspace
+ this.history.undo();
+ event.stop();
+ }
+ else if (event.keyCode == 89) { // Y
+ this.history.redo();
+ event.stop();
+ }
+ else if (event.keyCode == 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;
+ // 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 (event.code == 13 || event.code == 9)
+ event.stop();
+ else if ((event.character == "[" || event.character == "]") && event.ctrlKey)
+ event.stop(), this.blinkParens();
+ else if (electric && electric.indexOf(event.character) != -1)
+ this.parent.setTimeout(method(this, "indentAtCursor"), 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 <br>, or null for the first
+ // line. If given a <br> 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 (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 = safeWhiteSpace(newIndent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ }
+ // Not enough...
+ else if (indentDiff > 0) {
+ // If there is whitespace, we grow it.
+ if (whiteSpace) {
+ whiteSpace.currentText = safeWhiteSpace(newIndent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ // Otherwise, we have to add a new whitespace node.
+ else {
+ whiteSpace = makePartSpan(safeWhiteSpace(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) return;
+ // Skip one node ahead to make sure the cursor itself is
+ // *inside* a highlighted line.
+ if (to.nextSibling) to = to.nextSibling;
+
+ select.markSelection(this.win);
+ var toIsText = to.nodeType == 3;
+ if (!toIsText) to.dirty = true;
+
+ // Highlight lines as long as to is in the document and dirty.
+ while (to.parentNode == this.container && (toIsText || to.dirty)) {
+ var result = this.highlight(pos, 1, true);
+ if (result) pos = result.node;
+ if (!result || result.left) break;
+ }
+ select.selectMarked();
+ },
+
+ // 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.dumbTabs) {
+ select.insertTabAtCursor(this.win);
+ }
+ else 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);
+ }
+ },
+
+ // Delay (or initiate) the next paren blink event.
+ scheduleParenBlink: function() {
+ if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+ this.parenEvent = this.parent.setTimeout(method(this, "blinkParens"), 300);
+ },
+
+ isNearParsedNode: function(node) {
+ var distance = 0;
+ while (node && (!node.parserFromHere || node.dirty)) {
+ distance += (node.textContent || node.innerText || "-").length;
+ if (distance > 800) return false;
+ node = node.previousSibling;
+ }
+ return true;
+ },
+
+ // 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() {
+ // 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.isNearParsedNode(cursor)) return;
+ this.highlightAtCursor();
+ cursor = select.selectionTopNode(this.container, true);
+ if (!cursor || !(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, 1);
+ // 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);
+ 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.
+ this.highlightAtCursor();
+ 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(current, end, direction) {
+ select.markSelection(this.win);
+ current = startOfLine(current);
+ end = endOfLine(end, this.container);
+
+ do {
+ this.highlight(current);
+ var hl = this.highlight(current, 1);
+ this.indentLineAfter(current, direction);
+ current = hl ? hl.node : null;
+ } while (current != end);
+ select.selectMarked();
+ },
+
+ // 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.linesPerPass lines have been highlighted. The highlight
+ // method will continue to next lines as long as it finds dirty
+ // nodes. It returns an object indicating the amount of lines
+ // left, and 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) {
+ var lines = force ? Infinity : this.options.linesPerPass;
+ if (!this.options.readOnly) select.markSelection(this.win);
+ var start;
+ while (lines > 0 && (start = this.getDirtyNode())){
+ var result = this.highlight(start, lines);
+ if (result) {
+ lines = result.left;
+ if (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(linesPer) {
+ var self = this, pos = null;
+ return function() {
+ // 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, linesPer, 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 lines 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 frame. When
+ // a number of lines is given with the 'lines' argument, it will
+ // colour no more than that amount. If at any time it comes across
+ // a 'clean' line (no dirty nodes), it will stop, except when
+ // 'cleanLines' is true.
+ highlight: function(from, lines, cleanLines){
+ var container = this.container, self = this, active = this.options.activeTokens, origFrom = from;
+
+ if (!container.firstChild)
+ return;
+ // lines given as null means 'make sure this BR node has up to date parser information'
+ if (lines == null) {
+ if (!from) return;
+ else from = from.previousSibling;
+ }
+ // Backtrack to the first node before from that has a partial
+ // parse stored.
+ while (from && (!from.parserFromHere || from.dirty))
+ from = from.previousSibling;
+ // If we are at the end of the document, do nothing.
+ if (from && !from.nextSibling)
+ return;
+
+ // Check whether a part (<span> 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;
+ }
+
+ // 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.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;
+ if (lineDirty) self.history.touch(from);
+ from = part;
+
+ // Every <br> 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;
+
+ // No line argument passed means 'go at least until this node'.
+ if (lines == null && part == origFrom) 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 ((lines !== undefined && --lines <= 0) || (!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();
+ }
+ }
+ }
+ }
+ });
+ if (lineDirty) this.history.touch(from);
+
+ // The function returns some status information that is used by
+ // hightlightDirty to determine whether and where it has to
+ // continue.
+ return {left: lines,
+ node: parts.get(),
+ dirty: lineDirty};
+ }
+ };
+
+ return Editor;
+})();
+
+addEventHandler(window, "load", function() {
+ var CodeMirror = window.frameElement.CodeMirror;
+ CodeMirror.editor = new Editor(CodeMirror.options);
+ if (CodeMirror.options.initCallback) {
+ this.parent.setTimeout(function(){
+ CodeMirror.options.initCallback(CodeMirror);
+ }, 0);
+ }
+});
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/mirrorframe.js
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/mirrorframe.js?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/mirrorframe.js (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/mirrorframe.js Thu Jan 29 09:49:40 2009
@@ -0,0 +1,81 @@
+/* Demonstration of embedding CodeMirror in a bigger application. The
+ * interface defined here is a mess of prompts and confirms, and
+ * should probably not be used in a real project.
+ */
+
+function MirrorFrame(place, options) {
+ this.home = document.createElement("DIV");
+ if (place.appendChild)
+ place.appendChild(this.home);
+ else
+ place(this.home);
+
+ var self = this;
+ function makeButton(name, action) {
+ var button = document.createElement("INPUT");
+ button.type = "button";
+ button.value = name;
+ self.home.appendChild(button);
+ button.onclick = function(){self[action].call(self);};
+ }
+
+ makeButton("Search", "search");
+ makeButton("Replace", "replace");
+ makeButton("Current line", "line");
+ makeButton("Jump to line", "jump");
+ makeButton("Insert constructor", "macro");
+ makeButton("Indent all", "reindent");
+
+ this.mirror = new CodeMirror(this.home, options);
+}
+
+MirrorFrame.prototype = {
+ search: function() {
+ var text = prompt("Enter search term:", "");
+ if (!text) return;
+
+ var first = true;
+ do {
+ var cursor = this.mirror.getSearchCursor(text, first);
+ first = false;
+ while (cursor.findNext()) {
+ cursor.select();
+ if (!confirm("Search again?"))
+ return;
+ }
+ } while (confirm("End of document reached. Start over?"));
+ },
+
+ replace: function() {
+ // This is a replace-all, but it is possible to implement a
+ // prompting replace.
+ var from = prompt("Enter search string:", ""), to;
+ if (from) to = prompt("What should it be replaced with?", "");
+ if (to == null) return;
+
+ var cursor = this.mirror.getSearchCursor(from, false);
+ while (cursor.findNext())
+ cursor.replace(to);
+ },
+
+ jump: function() {
+ var line = prompt("Jump to line:", "");
+ if (line && !isNaN(Number(line)))
+ this.mirror.jumpToLine(Number(line));
+ },
+
+ line: function() {
+ alert("The cursor is currently at line " + this.mirror.currentLine());
+ this.mirror.focus();
+ },
+
+ macro: function() {
+ var name = prompt("Name your constructor:", "");
+ if (name)
+ this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n");
+ },
+
+ reindent: function() {
+ this.mirror.reindent();
+ }
+};
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsecss.js
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsecss.js?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsecss.js (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsecss.js Thu Jan 29 09:49:40 2009
@@ -0,0 +1,155 @@
+/* Simple parser for CSS */
+
+var CSSParser = Editor.Parser = (function() {
+ var tokenizeCSS = (function() {
+ function normal(source, setState) {
+ var ch = source.next();
+ if (ch == "@") {
+ source.nextWhile(matcher(/\w/));
+ return "css-at";
+ }
+ else if (ch == "/" && source.equals("*")) {
+ setState(inCComment);
+ return null;
+ }
+ else if (ch == "<" && source.equals("!")) {
+ setState(inSGMLComment);
+ return null;
+ }
+ else if (ch == "=") {
+ return "css-compare";
+ }
+ else if (source.equals("=") && (ch == "~" || ch == "|")) {
+ source.next();
+ return "css-compare";
+ }
+ else if (ch == "\"" || ch == "'") {
+ setState(inString(ch));
+ return null;
+ }
+ else if (ch == "#") {
+ source.nextWhile(matcher(/\w/));
+ return "css-hash";
+ }
+ else if (ch == "!") {
+ source.nextWhile(matcher(/[ \t]/));
+ source.nextWhile(matcher(/\w/));
+ return "css-important";
+ }
+ else if (/\d/.test(ch)) {
+ source.nextWhile(matcher(/[\w.%]/));
+ return "css-unit";
+ }
+ else if (/[,.+>*\/]/.test(ch)) {
+ return "css-select-op";
+ }
+ else if (/[;{}:\[\]]/.test(ch)) {
+ return "css-punctuation";
+ }
+ else {
+ source.nextWhile(matcher(/[\w\\\-_]/));
+ return "css-identifier";
+ }
+ }
+
+ function inCComment(source, setState) {
+ var maybeEnd = false;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (maybeEnd && ch == "/") {
+ setState(normal);
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return "css-comment";
+ }
+
+ function inSGMLComment(source, setState) {
+ var dashes = 0;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (dashes >= 2 && ch == ">") {
+ setState(normal);
+ break;
+ }
+ dashes = (ch == "-") ? dashes + 1 : 0;
+ }
+ return "css-comment";
+ }
+
+ function inString(quote) {
+ return function(source, setState) {
+ var escaped = false;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (ch == quote && !escaped)
+ break;
+ escaped = !escaped && ch == "\\";
+ }
+ if (!escaped)
+ setState(normal);
+ return "css-string";
+ };
+ }
+
+ return function(source, startState) {
+ return tokenizer(source, startState || normal);
+ };
+ })();
+
+ function indentCSS(inBraces, inRule, base) {
+ return function(nextChars) {
+ if (!inBraces || /^\}/.test(nextChars)) return base;
+ else if (inRule) return base + 4;
+ else return base + 2;
+ };
+ }
+
+ // This is a very simplistic parser -- since CSS does not really
+ // nest, it works acceptably well, but some nicer colouroing could
+ // be provided with a more complicated parser.
+ function parseCSS(source, basecolumn) {
+ basecolumn = basecolumn || 0;
+ var tokens = tokenizeCSS(source);
+ var inBraces = false, inRule = false;
+
+ var iter = {
+ next: function() {
+ var token = tokens.next(), style = token.style, content = token.content;
+
+ if (style == "css-identifier" && inRule)
+ token.style = "css-value";
+ if (style == "css-hash")
+ token.style = inRule ? "css-colorcode" : "css-identifier";
+
+ if (content == "\n")
+ token.indentation = indentCSS(inBraces, inRule, basecolumn);
+
+ if (content == "{")
+ inBraces = true;
+ else if (content == "}")
+ inBraces = inRule = false;
+ else if (inBraces && content == ";")
+ inRule = false;
+ else if (inBraces && style != "css-comment" && style != "whitespace")
+ inRule = true;
+
+ return token;
+ },
+
+ copy: function() {
+ var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state;
+ return function(source) {
+ tokens = tokenizeCSS(source, _tokenState);
+ inBraces = _inBraces;
+ inRule = _inRule;
+ return iter;
+ };
+ }
+ };
+ return iter;
+ }
+
+ return {make: parseCSS, electricChars: "}"};
+})();
Added: lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsehtmlmixed.js
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsehtmlmixed.js?rev=738808&view=auto
==============================================================================
--- lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsehtmlmixed.js (added)
+++ lenya/trunk/src/modules/editors/resources/codemirror/0.60/js/parsehtmlmixed.js Thu Jan 29 09:49:40 2009
@@ -0,0 +1,73 @@
+var HTMLMixedParser = Editor.Parser = (function() {
+ if (!(CSSParser && JSParser && XMLParser))
+ throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work.");
+ XMLParser.configure({useHTMLKludges: true});
+
+ function parseMixed(stream) {
+ var htmlParser = XMLParser.make(stream), localParser = null, inTag = false;
+ var iter = {next: top, copy: copy};
+
+ function top() {
+ var token = htmlParser.next();
+ if (token.content == "<")
+ inTag = true;
+ else if (token.style == "xml-tagname" && inTag === true)
+ inTag = token.content.toLowerCase();
+ else if (token.content == ">") {
+ if (inTag == "script")
+ iter.next = local(JSParser, "</script");
+ else if (inTag == "style")
+ iter.next = local(CSSParser, "</style");
+ inTag = false;
+ }
+ return token;
+ }
+ function local(parser, tag) {
+ var baseIndent = htmlParser.indentation();
+ localParser = parser.make(stream, baseIndent + 2);
+ return function() {
+ if (stream.lookAhead(tag, false, false, true)) {
+ localParser = null;
+ iter.next = top;
+ return top();
+ }
+
+ var token = localParser.next();
+ var lt = token.value.lastIndexOf("<"), sz = Math.min(token.value.length - lt, tag.length);
+ if (lt != -1 && token.value.slice(lt, lt + sz).toLowerCase() == tag.slice(0, sz) &&
+ stream.lookAhead(tag.slice(sz), false, false, true)) {
+ stream.push(token.value.slice(lt));
+ token.value = token.value.slice(0, lt);
+ }
+
+ if (token.indentation) {
+ var oldIndent = token.indentation;
+ token.indentation = function(chars) {
+ if (chars == "</")
+ return baseIndent;
+ else
+ return oldIndent(chars);
+ }
+ }
+
+ return token;
+ };
+ }
+
+ function copy() {
+ var _html = htmlParser.copy(), _local = localParser && localParser.copy(),
+ _next = iter.next, _inTag = inTag;
+ return function(_stream) {
+ stream = _stream;
+ htmlParser = _html(_stream);
+ localParser = _local && _local(_stream);
+ iter.next = _next;
+ inTag = _inTag;
+ return iter;
+ };
+ }
+ return iter;
+ }
+
+ return {make: parseMixed, electricChars: "{}/"};
+})();
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.