You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lenya.apache.org by an...@apache.org on 2009/01/29 10:49:42 UTC
svn commit: r738808 [1/3] - in /lenya/trunk: legal/
src/modules/editors/config/ src/modules/editors/resources/codemirror/
src/modules/editors/resources/codemirror/0.60/
src/modules/editors/resources/codemirror/0.60/css/
src/modules/editors/resources/co...
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:marijnh@gmail.com">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: "{}/"};
+})();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@lenya.apache.org
For additional commands, e-mail: commits-help@lenya.apache.org