From 16b7404d9bc2f9ba264f140310948897689fe3ca Mon Sep 17 00:00:00 2001 From: goatchurch Date: Sun, 28 Jun 2009 21:26:35 +0100 Subject: [PATCH] [svn] horrid .svns copied accidentally --- media/CodeMirror-0.62/LICENSE | 23 + media/CodeMirror-0.62/contrib/lua/LICENSE | 32 + .../contrib/lua/css/luacolors.css | 59 + media/CodeMirror-0.62/contrib/lua/index.html | 68 + .../contrib/lua/js/parselua.js | 253 ++++ media/CodeMirror-0.62/contrib/php/LICENSE | 37 + .../contrib/php/css/phpcolors.css | 110 ++ media/CodeMirror-0.62/contrib/php/index.html | 292 ++++ .../contrib/php/js/parsephp.js | 371 +++++ .../contrib/php/js/parsephphtmlmixed.js | 90 ++ .../contrib/php/js/tokenizephp.js | 1007 +++++++++++++ media/CodeMirror-0.62/contrib/python/LICENSE | 32 + .../contrib/python/css/pythoncolors.css | 54 + .../CodeMirror-0.62/contrib/python/index.html | 141 ++ .../contrib/python/js/parsepython.js | 544 +++++++ media/CodeMirror-0.62/css/csscolors.css | 47 + media/CodeMirror-0.62/css/docs.css | 46 + media/CodeMirror-0.62/css/jscolors.css | 55 + media/CodeMirror-0.62/css/people.jpg | Bin 0 -> 14122 bytes media/CodeMirror-0.62/css/sparqlcolors.css | 39 + media/CodeMirror-0.62/css/survexcolors.css | 70 + media/CodeMirror-0.62/css/xmlcolors.css | 51 + media/CodeMirror-0.62/csstest.html | 60 + media/CodeMirror-0.62/highlight.html | 82 ++ media/CodeMirror-0.62/js/codemirror.js | 298 ++++ media/CodeMirror-0.62/js/editor.js | 1303 +++++++++++++++++ media/CodeMirror-0.62/js/highlight.js | 68 + media/CodeMirror-0.62/js/mirrorframe.js | 81 + media/CodeMirror-0.62/js/parsecss.js | 155 ++ media/CodeMirror-0.62/js/parsedummy.js | 32 + media/CodeMirror-0.62/js/parsehtmlmixed.js | 74 + media/CodeMirror-0.62/js/parsejavascript.js | 341 +++++ media/CodeMirror-0.62/js/parsesparql.js | 162 ++ media/CodeMirror-0.62/js/parsesurvex.js | 107 ++ media/CodeMirror-0.62/js/parsexml.js | 292 ++++ media/CodeMirror-0.62/js/select.js | 583 ++++++++ media/CodeMirror-0.62/js/stringstream.js | 140 ++ media/CodeMirror-0.62/js/tokenize.js | 57 + .../CodeMirror-0.62/js/tokenizejavascript.js | 175 +++ media/CodeMirror-0.62/js/undo.js | 403 +++++ media/CodeMirror-0.62/js/util.js | 115 ++ media/CodeMirror-0.62/jstest.html | 56 + media/CodeMirror-0.62/mixedtest.html | 52 + media/CodeMirror-0.62/sparqltest.html | 41 + media/CodeMirror-0.62/story.html | 652 +++++++++ 45 files changed, 8750 insertions(+) create mode 100644 media/CodeMirror-0.62/LICENSE create mode 100644 media/CodeMirror-0.62/contrib/lua/LICENSE create mode 100644 media/CodeMirror-0.62/contrib/lua/css/luacolors.css create mode 100644 media/CodeMirror-0.62/contrib/lua/index.html create mode 100644 media/CodeMirror-0.62/contrib/lua/js/parselua.js create mode 100644 media/CodeMirror-0.62/contrib/php/LICENSE create mode 100644 media/CodeMirror-0.62/contrib/php/css/phpcolors.css create mode 100644 media/CodeMirror-0.62/contrib/php/index.html create mode 100644 media/CodeMirror-0.62/contrib/php/js/parsephp.js create mode 100644 media/CodeMirror-0.62/contrib/php/js/parsephphtmlmixed.js create mode 100644 media/CodeMirror-0.62/contrib/php/js/tokenizephp.js create mode 100644 media/CodeMirror-0.62/contrib/python/LICENSE create mode 100644 media/CodeMirror-0.62/contrib/python/css/pythoncolors.css create mode 100644 media/CodeMirror-0.62/contrib/python/index.html create mode 100644 media/CodeMirror-0.62/contrib/python/js/parsepython.js create mode 100644 media/CodeMirror-0.62/css/csscolors.css create mode 100644 media/CodeMirror-0.62/css/docs.css create mode 100644 media/CodeMirror-0.62/css/jscolors.css create mode 100644 media/CodeMirror-0.62/css/people.jpg create mode 100644 media/CodeMirror-0.62/css/sparqlcolors.css create mode 100644 media/CodeMirror-0.62/css/survexcolors.css create mode 100644 media/CodeMirror-0.62/css/xmlcolors.css create mode 100644 media/CodeMirror-0.62/csstest.html create mode 100644 media/CodeMirror-0.62/highlight.html create mode 100644 media/CodeMirror-0.62/js/codemirror.js create mode 100644 media/CodeMirror-0.62/js/editor.js create mode 100644 media/CodeMirror-0.62/js/highlight.js create mode 100644 media/CodeMirror-0.62/js/mirrorframe.js create mode 100644 media/CodeMirror-0.62/js/parsecss.js create mode 100644 media/CodeMirror-0.62/js/parsedummy.js create mode 100644 media/CodeMirror-0.62/js/parsehtmlmixed.js create mode 100644 media/CodeMirror-0.62/js/parsejavascript.js create mode 100644 media/CodeMirror-0.62/js/parsesparql.js create mode 100644 media/CodeMirror-0.62/js/parsesurvex.js create mode 100644 media/CodeMirror-0.62/js/parsexml.js create mode 100644 media/CodeMirror-0.62/js/select.js create mode 100644 media/CodeMirror-0.62/js/stringstream.js create mode 100644 media/CodeMirror-0.62/js/tokenize.js create mode 100644 media/CodeMirror-0.62/js/tokenizejavascript.js create mode 100644 media/CodeMirror-0.62/js/undo.js create mode 100644 media/CodeMirror-0.62/js/util.js create mode 100644 media/CodeMirror-0.62/jstest.html create mode 100644 media/CodeMirror-0.62/mixedtest.html create mode 100644 media/CodeMirror-0.62/sparqltest.html create mode 100644 media/CodeMirror-0.62/story.html diff --git a/media/CodeMirror-0.62/LICENSE b/media/CodeMirror-0.62/LICENSE new file mode 100644 index 0000000..44ceed6 --- /dev/null +++ b/media/CodeMirror-0.62/LICENSE @@ -0,0 +1,23 @@ + Copyright (c) 2007-2009 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 diff --git a/media/CodeMirror-0.62/contrib/lua/LICENSE b/media/CodeMirror-0.62/contrib/lua/LICENSE new file mode 100644 index 0000000..1af1908 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/lua/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2009, Franciszek Wawrzak +All rights reserved. + +This software is provided for use in connection with the +CodeMirror suite of modules and utilities, hosted and maintained +at http://marijn.haverbeke.nl/codemirror/. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/media/CodeMirror-0.62/contrib/lua/css/luacolors.css b/media/CodeMirror-0.62/contrib/lua/css/luacolors.css new file mode 100644 index 0000000..6df44b5 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/lua/css/luacolors.css @@ -0,0 +1,59 @@ +.editbox { + margin: .4em; + padding: 0; + font-family: monospace; + font-size: 10pt; + color: black; +} + +pre.code, .editbox { + color: #666666; +} + +.editbox p { + margin: 0; +} + +span.lua-comment { + color: #BB9977; +} + +span.lua-keyword { + font-weight: bold; + color: blue; +} + +span.lua-string { + color: #AA2222; +} + +span.lua-stdfunc { + font-weight: bold; + color: #077; +} +span.lua-customfunc { + font-weight: bold; + color: #0AA; +} + + +span.lua-identifier { + color: black; +} + +span.lua-number { + color: #3A3; +} + +span.lua-token { + color: #151; +} + +span.lua-error { + color: #FFF; + background-color: #F00; +} + + + + diff --git a/media/CodeMirror-0.62/contrib/lua/index.html b/media/CodeMirror-0.62/contrib/lua/index.html new file mode 100644 index 0000000..03a3229 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/lua/index.html @@ -0,0 +1,68 @@ + + + + CodeMirror: Lua demonstration + + + +

This page demonstrates CodeMirror's +Lua parser. Written by Franciszek +Wawrzak, released under a BSD-style license.

+ +
+ +
+ + + + diff --git a/media/CodeMirror-0.62/contrib/lua/js/parselua.js b/media/CodeMirror-0.62/contrib/lua/js/parselua.js new file mode 100644 index 0000000..2bc891b --- /dev/null +++ b/media/CodeMirror-0.62/contrib/lua/js/parselua.js @@ -0,0 +1,253 @@ +/* + Simple parser for LUA + Written for Lua 5.1, based on parsecss and other parsers. + features: highlights keywords, strings, comments (no leveling supported! ("[==[")),tokens, basic indenting + + to make this parser highlight your special functions pass table with this functions names to parserConfig argument of creator, + + parserConfig: ["myfunction1","myfunction2"], + */ + + +function findFirstRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")", "i"); +} + +function matchRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$", "i"); +} + + + +var luaCustomFunctions= matchRegexp([]); + +function configureLUA(parserConfig){ + if(parserConfig) + luaCustomFunctions= matchRegexp(parserConfig); +} + + +//long list of standard functions from lua manual +var luaStdFunctions = matchRegexp([ +"_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load","loadfile","loadstring","module","next","pairs","pcall","print","rawequal","rawget","rawset","require","select","setfenv","setmetatable","tonumber","tostring","type","unpack","xpcall", + +"coroutine.create","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield", + +"debug.debug","debug.getfenv","debug.gethook","debug.getinfo","debug.getlocal","debug.getmetatable","debug.getregistry","debug.getupvalue","debug.setfenv","debug.sethook","debug.setlocal","debug.setmetatable","debug.setupvalue","debug.traceback", + +"close","flush","lines","read","seek","setvbuf","write", + +"io.close","io.flush","io.input","io.lines","io.open","io.output","io.popen","io.read","io.stderr","io.stdin","io.stdout","io.tmpfile","io.type","io.write", + +"math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.cos","math.cosh","math.deg","math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10","math.max","math.min","math.modf","math.pi","math.pow","math.rad","math.random","math.randomseed","math.sin","math.sinh","math.sqrt","math.tan","math.tanh", + +"os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale","os.time","os.tmpname", + +"package.cpath","package.loaded","package.loaders","package.loadlib","package.path","package.preload","package.seeall", + +"string.byte","string.char","string.dump","string.find","string.format","string.gmatch","string.gsub","string.len","string.lower","string.match","string.rep","string.reverse","string.sub","string.upper", + +"table.concat","table.insert","table.maxn","table.remove","table.sort" +]); + + + + var luaKeywords = matchRegexp(["and","break","elseif","false","nil","not","or","return", + "true","function", "end", "if", "then", "else", "do", + "while", "repeat", "until", "for", "in", "local" ]); + + var luaIndentKeys = matchRegexp(["function", "if","repeat","for","while", "[\(]", "{"]); + var luaUnindentKeys = matchRegexp(["end", "until", "[\)]", "}"]); + + var luaUnindentKeys2 = findFirstRegexp(["end", "until", "[\)]", "}"]); + var luaMiddleKeys = findFirstRegexp(["else","elseif"]); + + + +var LUAParser = Editor.Parser = (function() { + var tokenizeLUA = (function() { + function normal(source, setState) { + var ch = source.next(); + + if (ch == "-" && source.equals("-")) { + source.next(); + setState(inSLComment); + return null; + } + else if (ch == "\"" || ch == "'") { + setState(inString(ch)); + return null; + } + if (ch == "[" && (source.equals("[") || source.equals("="))) { + var level = 0; + while(source.equals("=")){ + level ++; + source.next(); + } + if(! source.equals("[") ) + return "lua-error"; + setState(inMLSomething(level,"lua-string")); + return null; + } + + else if (ch == "=") { + if (source.equals("=")) + source.next(); + return "lua-token"; + } + + else if (ch == ".") { + if (source.equals(".")) + source.next(); + if (source.equals(".")) + source.next(); + return "lua-token"; + } + + else if (ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "%" || ch == "^" || ch == "#" ) { + return "lua-token"; + } + else if (ch == ">" || ch == "<" || ch == "(" || ch == ")" || ch == "{" || ch == "}" || ch == "[" ) { + return "lua-token"; + } + else if (ch == "]" || ch == ";" || ch == ":" || ch == ",") { + return "lua-token"; + } + else if (source.equals("=") && (ch == "~" || ch == "<" || ch == ">")) { + source.next(); + return "lua-token"; + } + + else if (/\d/.test(ch)) { + source.nextWhileMatches(/[\w.%]/); + return "lua-number"; + } + else { + source.nextWhileMatches(/[\w\\\-_.]/); + return "lua-identifier"; + } + } + +function inSLComment(source, setState) { + var start = true; + var count=0; + while (!source.endOfLine()) { + var ch = source.next(); + var level = 0; + if ((ch =="[") && start) + while(source.equals("=")){ + source.next(); + level++; + } + if (source.equals("[")){ + setState(inMLSomething(level,"lua-comment")); + return null; + } + start = false; + } + setState(normal); + return "lua-comment"; + + } + + function inMLSomething(level,what) { + //wat sholud be "lua-string" or "lua-comment", level is the number of "=" in opening mark. + return function(source, setState){ + var dashes = 0; + while (!source.endOfLine()) { + var ch = source.next(); + if (dashes == level+1 && ch == "]" ) { + setState(normal); + break; + } + if (dashes == 0) + dashes = (ch == "]") ? 1:0; + else + dashes = (ch == "=") ? dashes + 1 : 0; + } + return what; + } + } + + + 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 "lua-string"; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); + + function indentLUA(indentDepth, base) { + return function(nextChars) { + + var closing = (luaUnindentKeys2.test(nextChars) || luaMiddleKeys.test(nextChars)); + + + return base + ( indentUnit * (indentDepth - (closing?1:0)) ); + }; + } + + +function parseLUA(source,basecolumn) { + basecolumn = basecolumn || 0; + + var tokens = tokenizeLUA(source); + var indentDepth = 0; + + var iter = { + next: function() { + var token = tokens.next(), style = token.style, content = token.content; + + + + if (style == "lua-identifier" && luaKeywords.test(content)){ + token.style = "lua-keyword"; + } + if (style == "lua-identifier" && luaStdFunctions.test(content)){ + token.style = "lua-stdfunc"; + } + if (style == "lua-identifier" && luaCustomFunctions.test(content)){ + token.style = "lua-customfunc"; + } + + if (luaIndentKeys.test(content)) + indentDepth++; + else if (luaUnindentKeys.test(content)) + indentDepth--; + + + if (content == "\n") + token.indentation = indentLUA( indentDepth, basecolumn); + + return token; + }, + + copy: function() { + var _tokenState = tokens.state, _indentDepth = indentDepth; + return function(source) { + tokens = tokenizeLUA(source, _tokenState); + + indentDepth = _indentDepth; + return iter; + }; + } + }; + return iter; + } + + return {make: parseLUA, configure:configureLUA, electricChars: "delf})"}; //en[d] els[e] unti[l] elsei[f] // this should be taken from Keys keywords +})(); + diff --git a/media/CodeMirror-0.62/contrib/php/LICENSE b/media/CodeMirror-0.62/contrib/php/LICENSE new file mode 100644 index 0000000..ce4c5e4 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/php/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) 2008-2009, Yahoo! Inc. +All rights reserved. + +This software is provided for use in connection with the +CodeMirror suite of modules and utilities, hosted and maintained +at http://marijn.haverbeke.nl/codemirror/. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Yahoo! Inc. nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of Yahoo! Inc. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/media/CodeMirror-0.62/contrib/php/css/phpcolors.css b/media/CodeMirror-0.62/contrib/php/css/phpcolors.css new file mode 100644 index 0000000..1dd1aec --- /dev/null +++ b/media/CodeMirror-0.62/contrib/php/css/phpcolors.css @@ -0,0 +1,110 @@ +/* +Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. +The copyrights embodied in the content of this file are licensed by +Yahoo! Inc. under the BSD (revised) open source license + +@author Dan Vlad Dascalescu +*/ + +.editbox { + margin: .4em; + padding: 0; + font-family: monospace; + font-size: 10pt; +} + +/*We should define specific styles for every element of the syntax. + the setting below will cause some annoying color to show through if we missed + defining a style for a token. This is also the "color" of the whitespace and + of the cursor. +*/ +pre.code, .editbox { + color: red; +} + +.editbox p { + margin: 0; +} + +span.php-punctuation { + color: blue; +} + +span.php-keyword { + color: #770088; + font-weight: bold; +} + +span.php-operator { + color: blue; +} + +/* __FILE__ etc.; http://php.net/manual/en/reserved.php */ +span.php-compile-time-constant { + color: #776088; + font-weight: bold; +} + +/* output of get_defined_constants(). Differs from http://php.net/manual/en/reserved.constants.php */ +span.php-predefined-constant { + color: darkgreen; + font-weight: bold; +} + +/* PHP reserved "language constructs"... echo() etc.; http://php.net/manual/en/reserved.php */ +span.php-reserved-language-construct { + color: green; + font-weight: bold; +} + +/* PHP built-in functions: glob(), chr() etc.; output of get_defined_functions()["internal"] */ +span.php-predefined-function { + color: green; +} + +/* PHP predefined classes: PDO, Exception etc.; output of get_declared_classes() and different from http://php.net/manual/en/reserved.classes.php */ +span.php-predefined-class { + color: green; +} + +span.php-atom { + color: #228811; +} + +/* class, interface, namespace or function names, but not $variables */ +span.php-t_string { + color: black; +} + +span.php-variable { + color: black; + font-weight: bold; +} + + +span.js-localvariable { + color: #004499; +} + +span.php-comment { + color: #AA7700; + font-stretch: condensed; +/* font-style: italic; This causes line height to slightly change, getting line numbers out of sync */ +} + +span.php-string-single-quoted { + color: #AA2222; +} +/* double quoted strings allow interpolation */ +span.php-string-double-quoted { + color: #AA2222; + font-weight: bold; +} + +span.syntax-error { + background-color: red; +} + +span.deprecated { + font-size: smaller; +} diff --git a/media/CodeMirror-0.62/contrib/php/index.html b/media/CodeMirror-0.62/contrib/php/index.html new file mode 100644 index 0000000..f35f5d2 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/php/index.html @@ -0,0 +1,292 @@ + + + + + + + CodeMirror: PHP+HTML+JavaScript+CSS mixed-mode demonstration + + + + +

This is a complex demonstration of the PHP+HTML+JavaScript+CSS mixed-mode + syntax highlight capabilities of CodeMirror. + <?php ... ?> tags use the PHP parser, <script> tags use the JavaScript + parser, and <style> tags use the CSS parser. The rest of the content is + parsed using the XML parser in HTML mode.

+ +

Features of the PHP parser: +

+

+ +
+ +
+ + + + + + diff --git a/media/CodeMirror-0.62/contrib/php/js/parsephp.js b/media/CodeMirror-0.62/contrib/php/js/parsephp.js new file mode 100644 index 0000000..92d1e27 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/php/js/parsephp.js @@ -0,0 +1,371 @@ +/* +Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. +The copyrights embodied in the content of this file are licensed by +Yahoo! Inc. under the BSD (revised) open source license + +@author Dan Vlad Dascalescu + + +Parse function for PHP. Makes use of the tokenizer from tokenizephp.js. +Based on parsejavascript.js by Marijn Haverbeke. + + +Features: + + special "deprecated" style for PHP4 keywords like 'var' + + support for PHP 5.3 keywords: 'namespace', 'use' + + 911 predefined constants, 1301 predefined functions, 105 predeclared classes + from a typical PHP installation in a LAMP environment + + new feature: syntax error flagging, thus enabling strict parsing of: + + function definitions with explicitly or implicitly typed arguments and default values + + modifiers (public, static etc.) applied to method and member definitions + + foreach(array_expression as $key [=> $value]) loops + + differentiation between single-quoted strings and double-quoted interpolating strings + +*/ + + +// add the Array.indexOf method for JS engines that don't support it (e.g. IE) +// code from https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Array/IndexOf +if (!Array.prototype.indexOf) +{ + Array.prototype.indexOf = function(elt /*, from*/) + { + var len = this.length; + + var from = Number(arguments[1]) || 0; + from = (from < 0) + ? Math.ceil(from) + : Math.floor(from); + if (from < 0) + from += len; + + for (; from < len; from++) + { + if (from in this && + this[from] === elt) + return from; + } + return -1; + }; +}; + + +var PHPParser = Editor.Parser = (function() { + // Token types that can be considered to be atoms, part of operator expressions + var atomicTypes = { + "atom": true, "number": true, "variable": true, "string": true + }; + // Constructor for the lexical context objects. + function PHPLexical(indented, column, type, align, prev, info) { + // indentation at start of this line + this.indented = indented; + // column at which this scope was opened + this.column = column; + // type of scope ('stat' (statement), 'form' (special form), '[', '{', or '(') + this.type = type; + // '[', '{', or '(' blocks that have any text after their opening + // character are said to be 'aligned' -- any lines below are + // indented all the way to the opening character. + if (align != null) + this.align = align; + // Parent scope, if any. + this.prev = prev; + this.info = info; + }; + + // PHP indentation rules + function indentPHP(lexical) { + return function(firstChars) { + var firstChar = firstChars && firstChars.charAt(0), type = lexical.type; + var closing = firstChar == type; + if (type == "form" && firstChar == "{") + return lexical.indented; + else if (type == "stat" || type == "form") + return lexical.indented + indentUnit; + else if (lexical.info == "switch" && !closing) + return lexical.indented + (/^(?:case|default)\b/.test(firstChars) ? indentUnit : 2 * indentUnit); + else if (lexical.align) + return lexical.column - (closing ? 1 : 0); + else + return lexical.indented + (closing ? 0 : indentUnit); + }; + }; + + // The parser-iterator-producing function itself. + function parsePHP(input, basecolumn) { + // Wrap the input in a token stream + var tokens = tokenizePHP(input); + // The parser state. cc is a stack of actions that have to be + // performed to finish the current statement. For example we might + // know that we still need to find a closing parenthesis and a + // semicolon. Actions at the end of the stack go first. It is + // initialized with an infinitely looping action that consumes + // whole statements. + var cc = [statements]; + // The lexical scope, used mostly for indentation. + var lexical = new PHPLexical((basecolumn || 0) - indentUnit, 0, "block", false); + // Current column, and the indentation at the start of the current + // line. Used to create lexical scope objects. + var column = 0; + var indented = 0; + // Variables which are used by the mark, cont, and pass functions + // below to communicate with the driver loop in the 'next' function. + var consume, marked; + + // The iterator object. + var parser = {next: next, copy: copy}; + + // parsing is accomplished by calling next() repeatedly + function next(){ + // Start by performing any 'lexical' actions (adjusting the + // lexical variable), or the operations below will be working + // with the wrong lexical state. + while(cc[cc.length - 1].lex) + cc.pop()(); + + // Fetch the next token. + var token = tokens.next(); + + // Adjust column and indented. + if (token.type == "whitespace" && column == 0) + indented = token.value.length; + column += token.value.length; + if (token.content == "\n"){ + indented = column = 0; + // If the lexical scope's align property is still undefined at + // the end of the line, it is an un-aligned scope. + if (!("align" in lexical)) + lexical.align = false; + // Newline tokens get an indentation function associated with + // them. + token.indentation = indentPHP(lexical); + } + // No more processing for meaningless tokens. + if (token.type == "whitespace" || token.type == "comment" + || token.type == "string_not_terminated" ) + return token; + // When a meaningful token is found and the lexical scope's + // align is undefined, it is an aligned scope. + if (!("align" in lexical)) + lexical.align = true; + + // Execute actions until one 'consumes' the token and we can + // return it. 'marked' is used to change the style of the current token. + while(true) { + consume = marked = false; + // Take and execute the topmost action. + var action = cc.pop(); + action(token); + + if (consume){ + if (marked) + token.style = marked; + // Here we differentiate between local and global variables. + return token; + } + } + return 1; // Firebug workaround for http://code.google.com/p/fbug/issues/detail?id=1239#c1 + } + + // This makes a copy of the parser state. It stores all the + // stateful variables in a closure, and returns a function that + // will restore them when called with a new input stream. Note + // that the cc array has to be copied, because it is contantly + // being modified. Lexical objects are not mutated, so they can + // be shared between runs of the parser. + function copy(){ + var _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state; + + return function copyParser(input){ + lexical = _lexical; + cc = _cc.concat([]); // copies the array + column = indented = 0; + tokens = tokenizePHP(input, _tokenState); + return parser; + }; + } + + // Helper function for pushing a number of actions onto the cc + // stack in reverse order. + function push(fs){ + for (var i = fs.length - 1; i >= 0; i--) + cc.push(fs[i]); + } + // cont and pass are used by the action functions to add other + // actions to the stack. cont will cause the current token to be + // consumed, pass will leave it for the next action. + function cont(){ + push(arguments); + consume = true; + } + function pass(){ + push(arguments); + consume = false; + } + // Used to change the style of the current token. + function mark(style){ + marked = style; + } + // Add a lyer of style to the current token, for example syntax-error + function mark_add(style){ + marked = marked + ' ' + style; + } + + // Push a new lexical context of the given type. + function pushlex(type, info) { + var result = function pushlexing() { + lexical = new PHPLexical(indented, column, type, null, lexical, info) + }; + result.lex = true; + return result; + } + // Pop off the current lexical context. + function poplex(){ + lexical = lexical.prev; + } + poplex.lex = true; + // The 'lex' flag on these actions is used by the 'next' function + // to know they can (and have to) be ran before moving on to the + // next token. + + // Creates an action that discards tokens until it finds one of + // the given type. This will ignore (and recover from) syntax errors. + function expect(wanted){ + return function expecting(token){ + if (token.type == wanted) cont(); // consume the token + else { + cont(arguments.callee); // continue expecting() - call itself + } + }; + } + + // Require a specific token type, or one of the tokens passed in the 'wanted' array + // Used to detect blatant syntax errors. 'execute' is used to pass extra code + // to be executed if the token is matched. For example, a '(' match could + // 'execute' a cont( compasep(funcarg), require(")") ) + function require(wanted, execute){ + return function requiring(token){ + var ok; + var type = token.type; + if (typeof(wanted) == "string") + ok = (type == wanted) -1; + else + ok = wanted.indexOf(type); + if (ok >= 0) { + if (execute && typeof(execute[ok]) == "function") + execute[ok](token); + cont(); // just consume the token + } + else { + if (!marked) mark(token.style); + mark_add("syntax-error"); + cont(arguments.callee); + } + }; + } + + // Looks for a statement, and then calls itself. + function statements(token){ + return pass(statement, statements); + } + // Dispatches various types of statements based on the type of the current token. + function statement(token){ + var type = token.type; + if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex); + else if (type == "keyword b") cont(pushlex("form"), statement, poplex); + else if (type == "{") cont(pushlex("}"), block, poplex); + else if (type == "function") funcdef(); + // technically, "class implode {...}" is correct, but we'll flag that as an error because it overrides a predefined function + else if (type == "class") cont(require("t_string"), expect("{"), pushlex("}"), block, poplex); + else if (type == "foreach") cont(pushlex("form"), require("("), pushlex(")"), expression, require("as"), require("variable"), /* => $value */ expect(")"), poplex, statement, poplex); + else if (type == "for") cont(pushlex("form"), require("("), pushlex(")"), expression, require(";"), expression, require(";"), expression, require(")"), poplex, statement, poplex); + // public final function foo(), protected static $bar; + else if (type == "modifier") cont(require(["modifier", "variable", "function"], [null, null, funcdef])); + else if (type == "switch") cont(pushlex("form"), require("("), expression, require(")"), pushlex("}", "switch"), require([":", "{"]), block, poplex, poplex); + else if (type == "case") cont(expression, require(":")); + else if (type == "default") cont(require(":")); + else if (type == "catch") cont(pushlex("form"), require("("), require("t_string"), require("variable"), require(")"), statement, poplex); + else if (type == "const") cont(require("t_string")); // 'const static x=5' is a syntax error + // technically, "namespace implode {...}" is correct, but we'll flag that as an error because it overrides a predefined function + else if (type == "namespace") cont(namespacedef, require(";")); + // $variables may be followed by operators, () for variable function calls, or [] subscripts + else pass(pushlex("stat"), expression, require(";"), poplex); + } + // Dispatch expression types. + function expression(token){ + var type = token.type; + if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator); + else if (type == "<<<") cont(require("string"), maybeoperator); // heredoc/nowdoc + else if (type == "t_string") cont(maybe_double_colon, maybeoperator); + else if (type == "keyword c") cont(expression); + // function call or parenthesized expression: $a = ($b + 1) * 2; + else if (type == "(") cont(pushlex(")"), commasep(expression), require(")"), poplex, maybeoperator); + else if (type == "operator") cont(expression); + } + // Called for places where operators, function calls, or subscripts are + // valid. Will skip on to the next action if none is found. + function maybeoperator(token){ + var type = token.type; + if (type == "operator") { + if (token.content == "?") cont(expression, require(":"), expression); // ternary operator + else cont(expression); + } + else if (type == "(") cont(pushlex(")"), expression, commasep(expression), require(")"), poplex, maybeoperator /* $varfunc() + 3 */); + else if (type == "[") cont(pushlex("]"), expression, require("]"), maybeoperator /* for multidimensional arrays, or $func[$i]() */, poplex); + } + // A regular use of the double colon to specify a class, as in self::func() or myclass::$var; + // Differs from `namespace` or `use` in that only one class can be the parent; chains (A::B::$var) are a syntax error. + function maybe_double_colon(token) { + if (token.type == "t_double_colon") + // A::$var, A::func(), A::const + cont(require(["t_string", "variable"]), maybeoperator); + else { + // a t_string wasn't followed by ::, such as in a function call: foo() + pass(expression) + } + } + // the declaration or definition of a function + function funcdef() { + cont(require("t_string"), require("("), pushlex(")"), commasep(funcarg), require(")"), poplex, block); + } + // Parses a comma-separated list of the things that are recognized + // by the 'what' argument. + function commasep(what){ + function proceed(token) { + if (token.type == ",") cont(what, proceed); + }; + return function commaSeparated() { + pass(what, proceed); + }; + } + // Look for statements until a closing brace is found. + function block(token) { + if (token.type == "}") cont(); + else pass(statement, block); + } + function maybedefaultparameter(token){ + if (token.content == "=") cont(expression); + } + // support for default arguments: http://us.php.net/manual/en/functions.arguments.php#functions.arguments.default + function funcarg(token){ + // function foo(myclass $obj) {...} + if (token.type == "t_string") cont(require("variable"), maybedefaultparameter); + // function foo($string) {...} + else if (token.type == "variable") cont(maybedefaultparameter); + } + + // A namespace definition or use + function maybe_double_colon_def(token) { + if (token.type == "t_double_colon") + cont(namespacedef); + } + function namespacedef(token) { + pass(require("t_string"), maybe_double_colon_def); + } + + return parser; + } + + return {make: parsePHP, electricChars: "{}:"}; + +})(); diff --git a/media/CodeMirror-0.62/contrib/php/js/parsephphtmlmixed.js b/media/CodeMirror-0.62/contrib/php/js/parsephphtmlmixed.js new file mode 100644 index 0000000..ee4418d --- /dev/null +++ b/media/CodeMirror-0.62/contrib/php/js/parsephphtmlmixed.js @@ -0,0 +1,90 @@ +/* +Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. +The copyrights embodied in the content of this file are licensed by +Yahoo! Inc. under the BSD (revised) open source license + +@author Dan Vlad Dascalescu + +Based on parsehtmlmixed.js by Marijn Haverbeke. +*/ + +var PHPHTMLMixedParser = Editor.Parser = (function() { + if (!(PHPParser && CSSParser && JSParser && XMLParser)) + throw new Error("PHP, CSS, JS, and XML parsers must be loaded for PHP+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.type == "xml-processing") { + // dispatch on PHP + if (token.content == ""); + } + // "xml-processing" tokens are ignored, because they should be handled by a specific local parser + else if (token.content == ">") { + if (inTag == "script") + iter.next = local(JSParser, " + + +Tokenizer for PHP code + +References: + + http://php.net/manual/en/reserved.php + + http://php.net/tokens + + get_defined_constants(), get_defined_functions(), get_declared_classes() + executed on a realistic (not vanilla) PHP installation with typical LAMP modules. + Specifically, the PHP bundled with the Uniform Web Server (www.uniformserver.com). + +*/ + + +// add the forEach method for JS engines that don't support it (e.g. IE) +// code from https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:forEach +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fun /*, thisp*/) + { + var len = this.length; + if (typeof fun != "function") + throw new TypeError(); + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in this) + fun.call(thisp, this[i], i, this); + } + }; +} + + +var tokenizePHP = (function() { + /* A map of PHP's reserved words (keywords, predefined classes, functions and + constants. Each token has a type ('keyword', 'operator' etc.) and a style. + The style corresponds to the CSS span class in phpcolors.css. + + Keywords can be of three types: + a - takes an expression and forms a statement - e.g. if + b - takes just a statement - e.g. else + c - takes an optinoal expression, but no statement - e.g. return + This distinction gives the parser enough information to parse + correct code correctly (we don't care that much how we parse + incorrect code). + + Reference: http://us.php.net/manual/en/reserved.php + */ + var keywords = function(){ + function token(type, style){ + return {type: type, style: style}; + } + var result = {}; + + // for each(var element in ["...", "..."]) can pick up elements added to + // Array.prototype, so we'll use the loop structure below. See also + // http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for_each...in + + // keywords that take an expression and form a statement + ["if", "elseif", "while", "declare"].forEach(function(element, index, array) { + result[element] = token("keyword a", "php-keyword"); + }); + + // keywords that take just a statement + ["do", "else", "try" ].forEach(function(element, index, array) { + result[element] = token("keyword b", "php-keyword"); + }); + + // keywords that take an optional expression, but no statement + ["return", "break", "continue", // the expression is optional + "new", "clone", "throw" // the expression is mandatory + ].forEach(function(element, index, array) { + result[element] = token("keyword c", "php-keyword"); + }); + + ["__CLASS__", "__DIR__", "__FILE__", "__FUNCTION__", "__METHOD__", "__NAMESPACE__"].forEach(function(element, index, array) { + result[element] = token("atom", "php-compile-time-constant"); + }); + + ["true", "false", "null"].forEach(function(element, index, array) { + result[element] = token("atom", "php-atom"); + }); + + ["and", "or", "xor", "instanceof"].forEach(function(element, index, array) { + result[element] = token("operator", "php-keyword php-operator"); + }); + + ["class", "interface"].forEach(function(element, index, array) { + result[element] = token("class", "php-keyword"); + }); + ["namespace", "use", "extends", "implements"].forEach(function(element, index, array) { + result[element] = token("namespace", "php-keyword"); + }); + + // reserved "language constructs"... http://php.net/manual/en/reserved.php + [ "die", "echo", "empty", "exit", "eval", "include", "include_once", "isset", + "list", "require", "require_once", "return", "print", "unset", + "array" // a keyword rather, but mandates a parenthesized parameter list + ].forEach(function(element, index, array) { + result[element] = token("t_string", "php-reserved-language-construct"); + }); + + result["switch"] = token("switch", "php-keyword"); + result["case"] = token("case", "php-keyword"); + result["default"] = token("default", "php-keyword"); + result["catch"] = token("catch", "php-keyword"); + result["function"] = token("function", "php-keyword"); + + // http://php.net/manual/en/control-structures.alternative-syntax.php must be followed by a ':' + ["endif", "endwhile", "endfor", "endforeach", "endswitch", "enddeclare"].forEach(function(element, index, array) { + result[element] = token("default", "php-keyword"); + }); + + result["const"] = token("const", "php-keyword"); + + ["abstract", "final", "private", "protected", "public", "global", "static"].forEach(function(element, index, array) { + result[element] = token("modifier", "php-keyword"); + }); + result["var"] = token("modifier", "php-keyword deprecated"); + + result["foreach"] = token("foreach", "php-keyword"); + result["as"] = token("as", "php-keyword"); + result["for"] = token("for", "php-keyword"); + + // PHP built-in functions - output of get_defined_functions()["internal"] + [ "zend_version", "func_num_args", "func_get_arg", "func_get_args", "strlen", + "strcmp", "strncmp", "strcasecmp", "strncasecmp", "each", "error_reporting", + "define", "defined", "get_class", "get_parent_class", "method_exists", + "property_exists", "class_exists", "interface_exists", "function_exists", + "get_included_files", "get_required_files", "is_subclass_of", "is_a", + "get_class_vars", "get_object_vars", "get_class_methods", "trigger_error", + "user_error", "set_error_handler", "restore_error_handler", + "set_exception_handler", "restore_exception_handler", "get_declared_classes", + "get_declared_interfaces", "get_defined_functions", "get_defined_vars", + "create_function", "get_resource_type", "get_loaded_extensions", + "extension_loaded", "get_extension_funcs", "get_defined_constants", + "debug_backtrace", "debug_print_backtrace", "bcadd", "bcsub", "bcmul", "bcdiv", + "bcmod", "bcpow", "bcsqrt", "bcscale", "bccomp", "bcpowmod", "jdtogregorian", + "gregoriantojd", "jdtojulian", "juliantojd", "jdtojewish", "jewishtojd", + "jdtofrench", "frenchtojd", "jddayofweek", "jdmonthname", "easter_date", + "easter_days", "unixtojd", "jdtounix", "cal_to_jd", "cal_from_jd", + "cal_days_in_month", "cal_info", "variant_set", "variant_add", "variant_cat", + "variant_sub", "variant_mul", "variant_and", "variant_div", "variant_eqv", + "variant_idiv", "variant_imp", "variant_mod", "variant_or", "variant_pow", + "variant_xor", "variant_abs", "variant_fix", "variant_int", "variant_neg", + "variant_not", "variant_round", "variant_cmp", "variant_date_to_timestamp", + "variant_date_from_timestamp", "variant_get_type", "variant_set_type", + "variant_cast", "com_create_guid", "com_event_sink", "com_print_typeinfo", + "com_message_pump", "com_load_typelib", "com_get_active_object", "ctype_alnum", + "ctype_alpha", "ctype_cntrl", "ctype_digit", "ctype_lower", "ctype_graph", + "ctype_print", "ctype_punct", "ctype_space", "ctype_upper", "ctype_xdigit", + "strtotime", "date", "idate", "gmdate", "mktime", "gmmktime", "checkdate", + "strftime", "gmstrftime", "time", "localtime", "getdate", "date_create", + "date_parse", "date_format", "date_modify", "date_timezone_get", + "date_timezone_set", "date_offset_get", "date_time_set", "date_date_set", + "date_isodate_set", "timezone_open", "timezone_name_get", + "timezone_name_from_abbr", "timezone_offset_get", "timezone_transitions_get", + "timezone_identifiers_list", "timezone_abbreviations_list", + "date_default_timezone_set", "date_default_timezone_get", "date_sunrise", + "date_sunset", "date_sun_info", "filter_input", "filter_var", + "filter_input_array", "filter_var_array", "filter_list", "filter_has_var", + "filter_id", "ftp_connect", "ftp_login", "ftp_pwd", "ftp_cdup", "ftp_chdir", + "ftp_exec", "ftp_raw", "ftp_mkdir", "ftp_rmdir", "ftp_chmod", "ftp_alloc", + "ftp_nlist", "ftp_rawlist", "ftp_systype", "ftp_pasv", "ftp_get", "ftp_fget", + "ftp_put", "ftp_fput", "ftp_size", "ftp_mdtm", "ftp_rename", "ftp_delete", + "ftp_site", "ftp_close", "ftp_set_option", "ftp_get_option", "ftp_nb_fget", + "ftp_nb_get", "ftp_nb_continue", "ftp_nb_put", "ftp_nb_fput", "ftp_quit", + "hash", "hash_file", "hash_hmac", "hash_hmac_file", "hash_init", "hash_update", + "hash_update_stream", "hash_update_file", "hash_final", "hash_algos", "iconv", + "ob_iconv_handler", "iconv_get_encoding", "iconv_set_encoding", "iconv_strlen", + "iconv_substr", "iconv_strpos", "iconv_strrpos", "iconv_mime_encode", + "iconv_mime_decode", "iconv_mime_decode_headers", "json_encode", "json_decode", + "odbc_autocommit", "odbc_binmode", "odbc_close", "odbc_close_all", + "odbc_columns", "odbc_commit", "odbc_connect", "odbc_cursor", + "odbc_data_source", "odbc_execute", "odbc_error", "odbc_errormsg", "odbc_exec", + "odbc_fetch_array", "odbc_fetch_object", "odbc_fetch_row", "odbc_fetch_into", + "odbc_field_len", "odbc_field_scale", "odbc_field_name", "odbc_field_type", + "odbc_field_num", "odbc_free_result", "odbc_gettypeinfo", "odbc_longreadlen", + "odbc_next_result", "odbc_num_fields", "odbc_num_rows", "odbc_pconnect", + "odbc_prepare", "odbc_result", "odbc_result_all", "odbc_rollback", + "odbc_setoption", "odbc_specialcolumns", "odbc_statistics", "odbc_tables", + "odbc_primarykeys", "odbc_columnprivileges", "odbc_tableprivileges", + "odbc_foreignkeys", "odbc_procedures", "odbc_procedurecolumns", "odbc_do", + "odbc_field_precision", "preg_match", "preg_match_all", "preg_replace", + "preg_replace_callback", "preg_split", "preg_quote", "preg_grep", + "preg_last_error", "session_name", "session_module_name", "session_save_path", + "session_id", "session_regenerate_id", "session_decode", "session_register", + "session_unregister", "session_is_registered", "session_encode", + "session_start", "session_destroy", "session_unset", + "session_set_save_handler", "session_cache_limiter", "session_cache_expire", + "session_set_cookie_params", "session_get_cookie_params", + "session_write_close", "session_commit", "spl_classes", "spl_autoload", + "spl_autoload_extensions", "spl_autoload_register", "spl_autoload_unregister", + "spl_autoload_functions", "spl_autoload_call", "class_parents", + "class_implements", "spl_object_hash", "iterator_to_array", "iterator_count", + "iterator_apply", "constant", "bin2hex", "sleep", "usleep", "flush", + "wordwrap", "htmlspecialchars", "htmlentities", "html_entity_decode", + "htmlspecialchars_decode", "get_html_translation_table", "sha1", "sha1_file", + "md5", "md5_file", "crc32", "iptcparse", "iptcembed", "getimagesize", + "image_type_to_mime_type", "image_type_to_extension", "phpinfo", "phpversion", + "phpcredits", "php_logo_guid", "php_real_logo_guid", "php_egg_logo_guid", + "zend_logo_guid", "php_sapi_name", "php_uname", "php_ini_scanned_files", + "strnatcmp", "strnatcasecmp", "substr_count", "strspn", "strcspn", "strtok", + "strtoupper", "strtolower", "strpos", "stripos", "strrpos", "strripos", + "strrev", "hebrev", "hebrevc", "nl2br", "basename", "dirname", "pathinfo", + "stripslashes", "stripcslashes", "strstr", "stristr", "strrchr", "str_shuffle", + "str_word_count", "str_split", "strpbrk", "substr_compare", "strcoll", + "substr", "substr_replace", "quotemeta", "ucfirst", "ucwords", "strtr", + "addslashes", "addcslashes", "rtrim", "str_replace", "str_ireplace", + "str_repeat", "count_chars", "chunk_split", "trim", "ltrim", "strip_tags", + "similar_text", "explode", "implode", "setlocale", "localeconv", "soundex", + "levenshtein", "chr", "ord", "parse_str", "str_pad", "chop", "strchr", + "sprintf", "printf", "vprintf", "vsprintf", "fprintf", "vfprintf", "sscanf", + "fscanf", "parse_url", "urlencode", "urldecode", "rawurlencode", + "rawurldecode", "http_build_query", "unlink", "exec", "system", + "escapeshellcmd", "escapeshellarg", "passthru", "shell_exec", "proc_open", + "proc_close", "proc_terminate", "proc_get_status", "rand", "srand", + "getrandmax", "mt_rand", "mt_srand", "mt_getrandmax", "getservbyname", + "getservbyport", "getprotobyname", "getprotobynumber", "getmyuid", "getmygid", + "getmypid", "getmyinode", "getlastmod", "base64_decode", "base64_encode", + "convert_uuencode", "convert_uudecode", "abs", "ceil", "floor", "round", "sin", + "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "pi", + "is_finite", "is_nan", "is_infinite", "pow", "exp", "log", "log10", "sqrt", + "hypot", "deg2rad", "rad2deg", "bindec", "hexdec", "octdec", "decbin", + "decoct", "dechex", "base_convert", "number_format", "fmod", "ip2long", + "long2ip", "getenv", "putenv", "microtime", "gettimeofday", "uniqid", + "quoted_printable_decode", "convert_cyr_string", "get_current_user", + "set_time_limit", "get_cfg_var", "magic_quotes_runtime", + "set_magic_quotes_runtime", "get_magic_quotes_gpc", "get_magic_quotes_runtime", + "import_request_variables", "error_log", "error_get_last", "call_user_func", + "call_user_func_array", "call_user_method", "call_user_method_array", + "serialize", "unserialize", "var_dump", "var_export", "debug_zval_dump", + "print_r", "memory_get_usage", "memory_get_peak_usage", + "register_shutdown_function", "register_tick_function", + "unregister_tick_function", "highlight_file", "show_source", + "highlight_string", "php_strip_whitespace", "ini_get", "ini_get_all", + "ini_set", "ini_alter", "ini_restore", "get_include_path", "set_include_path", + "restore_include_path", "setcookie", "setrawcookie", "header", "headers_sent", + "headers_list", "connection_aborted", "connection_status", "ignore_user_abort", + "parse_ini_file", "is_uploaded_file", "move_uploaded_file", "gethostbyaddr", + "gethostbyname", "gethostbynamel", "intval", "floatval", "doubleval", "strval", + "gettype", "settype", "is_null", "is_resource", "is_bool", "is_long", + "is_float", "is_int", "is_integer", "is_double", "is_real", "is_numeric", + "is_string", "is_array", "is_object", "is_scalar", "is_callable", "ereg", + "ereg_replace", "eregi", "eregi_replace", "split", "spliti", "join", + "sql_regcase", "dl", "pclose", "popen", "readfile", "rewind", "rmdir", "umask", + "fclose", "feof", "fgetc", "fgets", "fgetss", "fread", "fopen", "fpassthru", + "ftruncate", "fstat", "fseek", "ftell", "fflush", "fwrite", "fputs", "mkdir", + "rename", "copy", "tempnam", "tmpfile", "file", "file_get_contents", + "file_put_contents", "stream_select", "stream_context_create", + "stream_context_set_params", "stream_context_set_option", + "stream_context_get_options", "stream_context_get_default", + "stream_filter_prepend", "stream_filter_append", "stream_filter_remove", + "stream_socket_client", "stream_socket_server", "stream_socket_accept", + "stream_socket_get_name", "stream_socket_recvfrom", "stream_socket_sendto", + "stream_socket_enable_crypto", "stream_socket_shutdown", + "stream_copy_to_stream", "stream_get_contents", "fgetcsv", "fputcsv", "flock", + "get_meta_tags", "stream_set_write_buffer", "set_file_buffer", + "set_socket_blocking", "stream_set_blocking", "socket_set_blocking", + "stream_get_meta_data", "stream_get_line", "stream_wrapper_register", + "stream_register_wrapper", "stream_wrapper_unregister", + "stream_wrapper_restore", "stream_get_wrappers", "stream_get_transports", + "get_headers", "stream_set_timeout", "socket_set_timeout", "socket_get_status", + "realpath", "fsockopen", "pfsockopen", "pack", "unpack", "get_browser", + "crypt", "opendir", "closedir", "chdir", "getcwd", "rewinddir", "readdir", + "dir", "scandir", "glob", "fileatime", "filectime", "filegroup", "fileinode", + "filemtime", "fileowner", "fileperms", "filesize", "filetype", "file_exists", + "is_writable", "is_writeable", "is_readable", "is_executable", "is_file", + "is_dir", "is_link", "stat", "lstat", "chown", "chgrp", "chmod", "touch", + "clearstatcache", "disk_total_space", "disk_free_space", "diskfreespace", + "mail", "ezmlm_hash", "openlog", "syslog", "closelog", + "define_syslog_variables", "lcg_value", "metaphone", "ob_start", "ob_flush", + "ob_clean", "ob_end_flush", "ob_end_clean", "ob_get_flush", "ob_get_clean", + "ob_get_length", "ob_get_level", "ob_get_status", "ob_get_contents", + "ob_implicit_flush", "ob_list_handlers", "ksort", "krsort", "natsort", + "natcasesort", "asort", "arsort", "sort", "rsort", "usort", "uasort", "uksort", + "shuffle", "array_walk", "array_walk_recursive", "count", "end", "prev", + "next", "reset", "current", "key", "min", "max", "in_array", "array_search", + "extract", "compact", "array_fill", "array_fill_keys", "range", + "array_multisort", "array_push", "array_pop", "array_shift", "array_unshift", + "array_splice", "array_slice", "array_merge", "array_merge_recursive", + "array_keys", "array_values", "array_count_values", "array_reverse", + "array_reduce", "array_pad", "array_flip", "array_change_key_case", + "array_rand", "array_unique", "array_intersect", "array_intersect_key", + "array_intersect_ukey", "array_uintersect", "array_intersect_assoc", + "array_uintersect_assoc", "array_intersect_uassoc", "array_uintersect_uassoc", + "array_diff", "array_diff_key", "array_diff_ukey", "array_udiff", + "array_diff_assoc", "array_udiff_assoc", "array_diff_uassoc", + "array_udiff_uassoc", "array_sum", "array_product", "array_filter", + "array_map", "array_chunk", "array_combine", "array_key_exists", "pos", + "sizeof", "key_exists", "assert", "assert_options", "version_compare", + "str_rot13", "stream_get_filters", "stream_filter_register", + "stream_bucket_make_writeable", "stream_bucket_prepend", + "stream_bucket_append", "stream_bucket_new", "output_add_rewrite_var", + "output_reset_rewrite_vars", "sys_get_temp_dir", "token_get_all", "token_name", + "readgzfile", "gzrewind", "gzclose", "gzeof", "gzgetc", "gzgets", "gzgetss", + "gzread", "gzopen", "gzpassthru", "gzseek", "gztell", "gzwrite", "gzputs", + "gzfile", "gzcompress", "gzuncompress", "gzdeflate", "gzinflate", "gzencode", + "ob_gzhandler", "zlib_get_coding_type", "libxml_set_streams_context", + "libxml_use_internal_errors", "libxml_get_last_error", "libxml_clear_errors", + "libxml_get_errors", "dom_import_simplexml", "simplexml_load_file", + "simplexml_load_string", "simplexml_import_dom", "wddx_serialize_value", + "wddx_serialize_vars", "wddx_packet_start", "wddx_packet_end", "wddx_add_vars", + "wddx_deserialize", "xml_parser_create", "xml_parser_create_ns", + "xml_set_object", "xml_set_element_handler", "xml_set_character_data_handler", + "xml_set_processing_instruction_handler", "xml_set_default_handler", + "xml_set_unparsed_entity_decl_handler", "xml_set_notation_decl_handler", + "xml_set_external_entity_ref_handler", "xml_set_start_namespace_decl_handler", + "xml_set_end_namespace_decl_handler", "xml_parse", "xml_parse_into_struct", + "xml_get_error_code", "xml_error_string", "xml_get_current_line_number", + "xml_get_current_column_number", "xml_get_current_byte_index", + "xml_parser_free", "xml_parser_set_option", "xml_parser_get_option", + "utf8_encode", "utf8_decode", "xmlwriter_open_uri", "xmlwriter_open_memory", + "xmlwriter_set_indent", "xmlwriter_set_indent_string", + "xmlwriter_start_comment", "xmlwriter_end_comment", + "xmlwriter_start_attribute", "xmlwriter_end_attribute", + "xmlwriter_write_attribute", "xmlwriter_start_attribute_ns", + "xmlwriter_write_attribute_ns", "xmlwriter_start_element", + "xmlwriter_end_element", "xmlwriter_full_end_element", + "xmlwriter_start_element_ns", "xmlwriter_write_element", + "xmlwriter_write_element_ns", "xmlwriter_start_pi", "xmlwriter_end_pi", + "xmlwriter_write_pi", "xmlwriter_start_cdata", "xmlwriter_end_cdata", + "xmlwriter_write_cdata", "xmlwriter_text", "xmlwriter_write_raw", + "xmlwriter_start_document", "xmlwriter_end_document", + "xmlwriter_write_comment", "xmlwriter_start_dtd", "xmlwriter_end_dtd", + "xmlwriter_write_dtd", "xmlwriter_start_dtd_element", + "xmlwriter_end_dtd_element", "xmlwriter_write_dtd_element", + "xmlwriter_start_dtd_attlist", "xmlwriter_end_dtd_attlist", + "xmlwriter_write_dtd_attlist", "xmlwriter_start_dtd_entity", + "xmlwriter_end_dtd_entity", "xmlwriter_write_dtd_entity", + "xmlwriter_output_memory", "xmlwriter_flush", "gd_info", "imagearc", + "imageellipse", "imagechar", "imagecharup", "imagecolorat", + "imagecolorallocate", "imagepalettecopy", "imagecreatefromstring", + "imagecolorclosest", "imagecolordeallocate", "imagecolorresolve", + "imagecolorexact", "imagecolorset", "imagecolortransparent", + "imagecolorstotal", "imagecolorsforindex", "imagecopy", "imagecopymerge", + "imagecopymergegray", "imagecopyresized", "imagecreate", + "imagecreatetruecolor", "imageistruecolor", "imagetruecolortopalette", + "imagesetthickness", "imagefilledarc", "imagefilledellipse", + "imagealphablending", "imagesavealpha", "imagecolorallocatealpha", + "imagecolorresolvealpha", "imagecolorclosestalpha", "imagecolorexactalpha", + "imagecopyresampled", "imagegrabwindow", "imagegrabscreen", "imagerotate", + "imageantialias", "imagesettile", "imagesetbrush", "imagesetstyle", + "imagecreatefrompng", "imagecreatefromgif", "imagecreatefromjpeg", + "imagecreatefromwbmp", "imagecreatefromxbm", "imagecreatefromgd", + "imagecreatefromgd2", "imagecreatefromgd2part", "imagepng", "imagegif", + "imagejpeg", "imagewbmp", "imagegd", "imagegd2", "imagedestroy", + "imagegammacorrect", "imagefill", "imagefilledpolygon", "imagefilledrectangle", + "imagefilltoborder", "imagefontwidth", "imagefontheight", "imageinterlace", + "imageline", "imageloadfont", "imagepolygon", "imagerectangle", + "imagesetpixel", "imagestring", "imagestringup", "imagesx", "imagesy", + "imagedashedline", "imagettfbbox", "imagettftext", "imageftbbox", + "imagefttext", "imagepsloadfont", "imagepsfreefont", "imagepsencodefont", + "imagepsextendfont", "imagepsslantfont", "imagepstext", "imagepsbbox", + "imagetypes", "jpeg2wbmp", "png2wbmp", "image2wbmp", "imagelayereffect", + "imagecolormatch", "imagexbm", "imagefilter", "imageconvolution", + "mb_convert_case", "mb_strtoupper", "mb_strtolower", "mb_language", + "mb_internal_encoding", "mb_http_input", "mb_http_output", "mb_detect_order", + "mb_substitute_character", "mb_parse_str", "mb_output_handler", + "mb_preferred_mime_name", "mb_strlen", "mb_strpos", "mb_strrpos", "mb_stripos", + "mb_strripos", "mb_strstr", "mb_strrchr", "mb_stristr", "mb_strrichr", + "mb_substr_count", "mb_substr", "mb_strcut", "mb_strwidth", "mb_strimwidth", + "mb_convert_encoding", "mb_detect_encoding", "mb_list_encodings", + "mb_convert_kana", "mb_encode_mimeheader", "mb_decode_mimeheader", + "mb_convert_variables", "mb_encode_numericentity", "mb_decode_numericentity", + "mb_send_mail", "mb_get_info", "mb_check_encoding", "mb_regex_encoding", + "mb_regex_set_options", "mb_ereg", "mb_eregi", "mb_ereg_replace", + "mb_eregi_replace", "mb_split", "mb_ereg_match", "mb_ereg_search", + "mb_ereg_search_pos", "mb_ereg_search_regs", "mb_ereg_search_init", + "mb_ereg_search_getregs", "mb_ereg_search_getpos", "mb_ereg_search_setpos", + "mbregex_encoding", "mbereg", "mberegi", "mbereg_replace", "mberegi_replace", + "mbsplit", "mbereg_match", "mbereg_search", "mbereg_search_pos", + "mbereg_search_regs", "mbereg_search_init", "mbereg_search_getregs", + "mbereg_search_getpos", "mbereg_search_setpos", "mysql_connect", + "mysql_pconnect", "mysql_close", "mysql_select_db", "mysql_query", + "mysql_unbuffered_query", "mysql_db_query", "mysql_list_dbs", + "mysql_list_tables", "mysql_list_fields", "mysql_list_processes", + "mysql_error", "mysql_errno", "mysql_affected_rows", "mysql_insert_id", + "mysql_result", "mysql_num_rows", "mysql_num_fields", "mysql_fetch_row", + "mysql_fetch_array", "mysql_fetch_assoc", "mysql_fetch_object", + "mysql_data_seek", "mysql_fetch_lengths", "mysql_fetch_field", + "mysql_field_seek", "mysql_free_result", "mysql_field_name", + "mysql_field_table", "mysql_field_len", "mysql_field_type", + "mysql_field_flags", "mysql_escape_string", "mysql_real_escape_string", + "mysql_stat", "mysql_thread_id", "mysql_client_encoding", "mysql_ping", + "mysql_get_client_info", "mysql_get_host_info", "mysql_get_proto_info", + "mysql_get_server_info", "mysql_info", "mysql_set_charset", "mysql", + "mysql_fieldname", "mysql_fieldtable", "mysql_fieldlen", "mysql_fieldtype", + "mysql_fieldflags", "mysql_selectdb", "mysql_freeresult", "mysql_numfields", + "mysql_numrows", "mysql_listdbs", "mysql_listtables", "mysql_listfields", + "mysql_db_name", "mysql_dbname", "mysql_tablename", "mysql_table_name", + "mysqli_affected_rows", "mysqli_autocommit", "mysqli_change_user", + "mysqli_character_set_name", "mysqli_close", "mysqli_commit", "mysqli_connect", + "mysqli_connect_errno", "mysqli_connect_error", "mysqli_data_seek", + "mysqli_debug", "mysqli_disable_reads_from_master", "mysqli_disable_rpl_parse", + "mysqli_dump_debug_info", "mysqli_enable_reads_from_master", + "mysqli_enable_rpl_parse", "mysqli_embedded_server_end", + "mysqli_embedded_server_start", "mysqli_errno", "mysqli_error", + "mysqli_stmt_execute", "mysqli_execute", "mysqli_fetch_field", + "mysqli_fetch_fields", "mysqli_fetch_field_direct", "mysqli_fetch_lengths", + "mysqli_fetch_array", "mysqli_fetch_assoc", "mysqli_fetch_object", + "mysqli_fetch_row", "mysqli_field_count", "mysqli_field_seek", + "mysqli_field_tell", "mysqli_free_result", "mysqli_get_charset", + "mysqli_get_client_info", "mysqli_get_client_version", "mysqli_get_host_info", + "mysqli_get_proto_info", "mysqli_get_server_info", "mysqli_get_server_version", + "mysqli_get_warnings", "mysqli_init", "mysqli_info", "mysqli_insert_id", + "mysqli_kill", "mysqli_set_local_infile_default", + "mysqli_set_local_infile_handler", "mysqli_master_query", + "mysqli_more_results", "mysqli_multi_query", "mysqli_next_result", + "mysqli_num_fields", "mysqli_num_rows", "mysqli_options", "mysqli_ping", + "mysqli_prepare", "mysqli_report", "mysqli_query", "mysqli_real_connect", + "mysqli_real_escape_string", "mysqli_real_query", "mysqli_rollback", + "mysqli_rpl_parse_enabled", "mysqli_rpl_probe", "mysqli_rpl_query_type", + "mysqli_select_db", "mysqli_set_charset", "mysqli_stmt_attr_get", + "mysqli_stmt_attr_set", "mysqli_stmt_field_count", "mysqli_stmt_init", + "mysqli_stmt_prepare", "mysqli_stmt_result_metadata", + "mysqli_stmt_send_long_data", "mysqli_stmt_bind_param", + "mysqli_stmt_bind_result", "mysqli_stmt_fetch", "mysqli_stmt_free_result", + "mysqli_stmt_get_warnings", "mysqli_stmt_insert_id", "mysqli_stmt_reset", + "mysqli_stmt_param_count", "mysqli_send_query", "mysqli_slave_query", + "mysqli_sqlstate", "mysqli_ssl_set", "mysqli_stat", + "mysqli_stmt_affected_rows", "mysqli_stmt_close", "mysqli_stmt_data_seek", + "mysqli_stmt_errno", "mysqli_stmt_error", "mysqli_stmt_num_rows", + "mysqli_stmt_sqlstate", "mysqli_store_result", "mysqli_stmt_store_result", + "mysqli_thread_id", "mysqli_thread_safe", "mysqli_use_result", + "mysqli_warning_count", "mysqli_bind_param", "mysqli_bind_result", + "mysqli_client_encoding", "mysqli_escape_string", "mysqli_fetch", + "mysqli_param_count", "mysqli_get_metadata", "mysqli_send_long_data", + "mysqli_set_opt", "pdo_drivers", "socket_select", "socket_create", + "socket_create_listen", "socket_accept", "socket_set_nonblock", + "socket_set_block", "socket_listen", "socket_close", "socket_write", + "socket_read", "socket_getsockname", "socket_getpeername", "socket_connect", + "socket_strerror", "socket_bind", "socket_recv", "socket_send", + "socket_recvfrom", "socket_sendto", "socket_get_option", "socket_set_option", + "socket_shutdown", "socket_last_error", "socket_clear_error", "socket_getopt", + "socket_setopt", "eaccelerator_put", "eaccelerator_get", "eaccelerator_rm", + "eaccelerator_gc", "eaccelerator_lock", "eaccelerator_unlock", + "eaccelerator_caching", "eaccelerator_optimizer", "eaccelerator_clear", + "eaccelerator_clean", "eaccelerator_info", "eaccelerator_purge", + "eaccelerator_cached_scripts", "eaccelerator_removed_scripts", + "eaccelerator_list_keys", "eaccelerator_encode", "eaccelerator_load", + "_eaccelerator_loader_file", "_eaccelerator_loader_line", + "eaccelerator_set_session_handlers", "_eaccelerator_output_handler", + "eaccelerator_cache_page", "eaccelerator_rm_page", "eaccelerator_cache_output", + "eaccelerator_cache_result", "xdebug_get_stack_depth", + "xdebug_get_function_stack", "xdebug_print_function_stack", + "xdebug_get_declared_vars", "xdebug_call_class", "xdebug_call_function", + "xdebug_call_file", "xdebug_call_line", "xdebug_var_dump", "xdebug_debug_zval", + "xdebug_debug_zval_stdout", "xdebug_enable", "xdebug_disable", + "xdebug_is_enabled", "xdebug_break", "xdebug_start_trace", "xdebug_stop_trace", + "xdebug_get_tracefile_name", "xdebug_get_profiler_filename", + "xdebug_dump_aggr_profiling_data", "xdebug_clear_aggr_profiling_data", + "xdebug_memory_usage", "xdebug_peak_memory_usage", "xdebug_time_index", + "xdebug_start_error_collection", "xdebug_stop_error_collection", + "xdebug_get_collected_errors", "xdebug_start_code_coverage", + "xdebug_stop_code_coverage", "xdebug_get_code_coverage", + "xdebug_get_function_count", "xdebug_dump_superglobals", + "_" // alias for gettext() + ].forEach(function(element, index, array) { + result[element] = token("t_string", "php-predefined-function"); + }); + + // output of get_defined_constants(). Differs significantly from http://php.net/manual/en/reserved.constants.php + [ "E_ERROR", "E_RECOVERABLE_ERROR", "E_WARNING", "E_PARSE", "E_NOTICE", + "E_STRICT", "E_CORE_ERROR", "E_CORE_WARNING", "E_COMPILE_ERROR", + "E_COMPILE_WARNING", "E_USER_ERROR", "E_USER_WARNING", "E_USER_NOTICE", + "E_ALL", "TRUE", "FALSE", "NULL", "ZEND_THREAD_SAFE", "PHP_VERSION", "PHP_OS", + "PHP_SAPI", "DEFAULT_INCLUDE_PATH", "PEAR_INSTALL_DIR", "PEAR_EXTENSION_DIR", + "PHP_EXTENSION_DIR", "PHP_PREFIX", "PHP_BINDIR", "PHP_LIBDIR", "PHP_DATADIR", + "PHP_SYSCONFDIR", "PHP_LOCALSTATEDIR", "PHP_CONFIG_FILE_PATH", + "PHP_CONFIG_FILE_SCAN_DIR", "PHP_SHLIB_SUFFIX", "PHP_EOL", "PHP_EOL", + "PHP_INT_MAX", "PHP_INT_SIZE", "PHP_OUTPUT_HANDLER_START", + "PHP_OUTPUT_HANDLER_CONT", "PHP_OUTPUT_HANDLER_END", "UPLOAD_ERR_OK", + "UPLOAD_ERR_INI_SIZE", "UPLOAD_ERR_FORM_SIZE", "UPLOAD_ERR_PARTIAL", + "UPLOAD_ERR_NO_FILE", "UPLOAD_ERR_NO_TMP_DIR", "UPLOAD_ERR_CANT_WRITE", + "UPLOAD_ERR_EXTENSION", "CAL_GREGORIAN", "CAL_JULIAN", "CAL_JEWISH", + "CAL_FRENCH", "CAL_NUM_CALS", "CAL_DOW_DAYNO", "CAL_DOW_SHORT", "CAL_DOW_LONG", + "CAL_MONTH_GREGORIAN_SHORT", "CAL_MONTH_GREGORIAN_LONG", + "CAL_MONTH_JULIAN_SHORT", "CAL_MONTH_JULIAN_LONG", "CAL_MONTH_JEWISH", + "CAL_MONTH_FRENCH", "CAL_EASTER_DEFAULT", "CAL_EASTER_ROMAN", + "CAL_EASTER_ALWAYS_GREGORIAN", "CAL_EASTER_ALWAYS_JULIAN", + "CAL_JEWISH_ADD_ALAFIM_GERESH", "CAL_JEWISH_ADD_ALAFIM", + "CAL_JEWISH_ADD_GERESHAYIM", "CLSCTX_INPROC_SERVER", "CLSCTX_INPROC_HANDLER", + "CLSCTX_LOCAL_SERVER", "CLSCTX_REMOTE_SERVER", "CLSCTX_SERVER", "CLSCTX_ALL", + "VT_NULL", "VT_EMPTY", "VT_UI1", "VT_I1", "VT_UI2", "VT_I2", "VT_UI4", "VT_I4", + "VT_R4", "VT_R8", "VT_BOOL", "VT_ERROR", "VT_CY", "VT_DATE", "VT_BSTR", + "VT_DECIMAL", "VT_UNKNOWN", "VT_DISPATCH", "VT_VARIANT", "VT_INT", "VT_UINT", + "VT_ARRAY", "VT_BYREF", "CP_ACP", "CP_MACCP", "CP_OEMCP", "CP_UTF7", "CP_UTF8", + "CP_SYMBOL", "CP_THREAD_ACP", "VARCMP_LT", "VARCMP_EQ", "VARCMP_GT", + "VARCMP_NULL", "NORM_IGNORECASE", "NORM_IGNORENONSPACE", "NORM_IGNORESYMBOLS", + "NORM_IGNOREWIDTH", "NORM_IGNOREKANATYPE", "DISP_E_DIVBYZERO", + "DISP_E_OVERFLOW", "DISP_E_BADINDEX", "MK_E_UNAVAILABLE", "INPUT_POST", + "INPUT_GET", "INPUT_COOKIE", "INPUT_ENV", "INPUT_SERVER", "INPUT_SESSION", + "INPUT_REQUEST", "FILTER_FLAG_NONE", "FILTER_REQUIRE_SCALAR", + "FILTER_REQUIRE_ARRAY", "FILTER_FORCE_ARRAY", "FILTER_NULL_ON_FAILURE", + "FILTER_VALIDATE_INT", "FILTER_VALIDATE_BOOLEAN", "FILTER_VALIDATE_FLOAT", + "FILTER_VALIDATE_REGEXP", "FILTER_VALIDATE_URL", "FILTER_VALIDATE_EMAIL", + "FILTER_VALIDATE_IP", "FILTER_DEFAULT", "FILTER_UNSAFE_RAW", + "FILTER_SANITIZE_STRING", "FILTER_SANITIZE_STRIPPED", + "FILTER_SANITIZE_ENCODED", "FILTER_SANITIZE_SPECIAL_CHARS", + "FILTER_SANITIZE_EMAIL", "FILTER_SANITIZE_URL", "FILTER_SANITIZE_NUMBER_INT", + "FILTER_SANITIZE_NUMBER_FLOAT", "FILTER_SANITIZE_MAGIC_QUOTES", + "FILTER_CALLBACK", "FILTER_FLAG_ALLOW_OCTAL", "FILTER_FLAG_ALLOW_HEX", + "FILTER_FLAG_STRIP_LOW", "FILTER_FLAG_STRIP_HIGH", "FILTER_FLAG_ENCODE_LOW", + "FILTER_FLAG_ENCODE_HIGH", "FILTER_FLAG_ENCODE_AMP", + "FILTER_FLAG_NO_ENCODE_QUOTES", "FILTER_FLAG_EMPTY_STRING_NULL", + "FILTER_FLAG_ALLOW_FRACTION", "FILTER_FLAG_ALLOW_THOUSAND", + "FILTER_FLAG_ALLOW_SCIENTIFIC", "FILTER_FLAG_SCHEME_REQUIRED", + "FILTER_FLAG_HOST_REQUIRED", "FILTER_FLAG_PATH_REQUIRED", + "FILTER_FLAG_QUERY_REQUIRED", "FILTER_FLAG_IPV4", "FILTER_FLAG_IPV6", + "FILTER_FLAG_NO_RES_RANGE", "FILTER_FLAG_NO_PRIV_RANGE", "FTP_ASCII", + "FTP_TEXT", "FTP_BINARY", "FTP_IMAGE", "FTP_AUTORESUME", "FTP_TIMEOUT_SEC", + "FTP_AUTOSEEK", "FTP_FAILED", "FTP_FINISHED", "FTP_MOREDATA", "HASH_HMAC", + "ICONV_IMPL", "ICONV_VERSION", "ICONV_MIME_DECODE_STRICT", + "ICONV_MIME_DECODE_CONTINUE_ON_ERROR", "ODBC_TYPE", "ODBC_BINMODE_PASSTHRU", + "ODBC_BINMODE_RETURN", "ODBC_BINMODE_CONVERT", "SQL_ODBC_CURSORS", + "SQL_CUR_USE_DRIVER", "SQL_CUR_USE_IF_NEEDED", "SQL_CUR_USE_ODBC", + "SQL_CONCURRENCY", "SQL_CONCUR_READ_ONLY", "SQL_CONCUR_LOCK", + "SQL_CONCUR_ROWVER", "SQL_CONCUR_VALUES", "SQL_CURSOR_TYPE", + "SQL_CURSOR_FORWARD_ONLY", "SQL_CURSOR_KEYSET_DRIVEN", "SQL_CURSOR_DYNAMIC", + "SQL_CURSOR_STATIC", "SQL_KEYSET_SIZE", "SQL_FETCH_FIRST", "SQL_FETCH_NEXT", + "SQL_CHAR", "SQL_VARCHAR", "SQL_LONGVARCHAR", "SQL_DECIMAL", "SQL_NUMERIC", + "SQL_BIT", "SQL_TINYINT", "SQL_SMALLINT", "SQL_INTEGER", "SQL_BIGINT", + "SQL_REAL", "SQL_FLOAT", "SQL_DOUBLE", "SQL_BINARY", "SQL_VARBINARY", + "SQL_LONGVARBINARY", "SQL_DATE", "SQL_TIME", "SQL_TIMESTAMP", + "PREG_PATTERN_ORDER", "PREG_SET_ORDER", "PREG_OFFSET_CAPTURE", + "PREG_SPLIT_NO_EMPTY", "PREG_SPLIT_DELIM_CAPTURE", "PREG_SPLIT_OFFSET_CAPTURE", + "PREG_GREP_INVERT", "PREG_NO_ERROR", "PREG_INTERNAL_ERROR", + "PREG_BACKTRACK_LIMIT_ERROR", "PREG_RECURSION_LIMIT_ERROR", + "PREG_BAD_UTF8_ERROR", "DATE_ATOM", "DATE_COOKIE", "DATE_ISO8601", + "DATE_RFC822", "DATE_RFC850", "DATE_RFC1036", "DATE_RFC1123", "DATE_RFC2822", + "DATE_RFC3339", "DATE_RSS", "DATE_W3C", "SUNFUNCS_RET_TIMESTAMP", + "SUNFUNCS_RET_STRING", "SUNFUNCS_RET_DOUBLE", "LIBXML_VERSION", + "LIBXML_DOTTED_VERSION", "LIBXML_NOENT", "LIBXML_DTDLOAD", "LIBXML_DTDATTR", + "LIBXML_DTDVALID", "LIBXML_NOERROR", "LIBXML_NOWARNING", "LIBXML_NOBLANKS", + "LIBXML_XINCLUDE", "LIBXML_NSCLEAN", "LIBXML_NOCDATA", "LIBXML_NONET", + "LIBXML_COMPACT", "LIBXML_NOXMLDECL", "LIBXML_NOEMPTYTAG", "LIBXML_ERR_NONE", + "LIBXML_ERR_WARNING", "LIBXML_ERR_ERROR", "LIBXML_ERR_FATAL", + "CONNECTION_ABORTED", "CONNECTION_NORMAL", "CONNECTION_TIMEOUT", "INI_USER", + "INI_PERDIR", "INI_SYSTEM", "INI_ALL", "PHP_URL_SCHEME", "PHP_URL_HOST", + "PHP_URL_PORT", "PHP_URL_USER", "PHP_URL_PASS", "PHP_URL_PATH", + "PHP_URL_QUERY", "PHP_URL_FRAGMENT", "M_E", "M_LOG2E", "M_LOG10E", "M_LN2", + "M_LN10", "M_PI", "M_PI_2", "M_PI_4", "M_1_PI", "M_2_PI", "M_SQRTPI", + "M_2_SQRTPI", "M_LNPI", "M_EULER", "M_SQRT2", "M_SQRT1_2", "M_SQRT3", "INF", + "NAN", "INFO_GENERAL", "INFO_CREDITS", "INFO_CONFIGURATION", "INFO_MODULES", + "INFO_ENVIRONMENT", "INFO_VARIABLES", "INFO_LICENSE", "INFO_ALL", + "CREDITS_GROUP", "CREDITS_GENERAL", "CREDITS_SAPI", "CREDITS_MODULES", + "CREDITS_DOCS", "CREDITS_FULLPAGE", "CREDITS_QA", "CREDITS_ALL", + "HTML_SPECIALCHARS", "HTML_ENTITIES", "ENT_COMPAT", "ENT_QUOTES", + "ENT_NOQUOTES", "STR_PAD_LEFT", "STR_PAD_RIGHT", "STR_PAD_BOTH", + "PATHINFO_DIRNAME", "PATHINFO_BASENAME", "PATHINFO_EXTENSION", + "PATHINFO_FILENAME", "CHAR_MAX", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", + "LC_COLLATE", "LC_MONETARY", "LC_ALL", "SEEK_SET", "SEEK_CUR", "SEEK_END", + "LOCK_SH", "LOCK_EX", "LOCK_UN", "LOCK_NB", "STREAM_NOTIFY_CONNECT", + "STREAM_NOTIFY_AUTH_REQUIRED", "STREAM_NOTIFY_AUTH_RESULT", + "STREAM_NOTIFY_MIME_TYPE_IS", "STREAM_NOTIFY_FILE_SIZE_IS", + "STREAM_NOTIFY_REDIRECTED", "STREAM_NOTIFY_PROGRESS", "STREAM_NOTIFY_FAILURE", + "STREAM_NOTIFY_COMPLETED", "STREAM_NOTIFY_RESOLVE", + "STREAM_NOTIFY_SEVERITY_INFO", "STREAM_NOTIFY_SEVERITY_WARN", + "STREAM_NOTIFY_SEVERITY_ERR", "STREAM_FILTER_READ", "STREAM_FILTER_WRITE", + "STREAM_FILTER_ALL", "STREAM_CLIENT_PERSISTENT", "STREAM_CLIENT_ASYNC_CONNECT", + "STREAM_CLIENT_CONNECT", "STREAM_CRYPTO_METHOD_SSLv2_CLIENT", + "STREAM_CRYPTO_METHOD_SSLv3_CLIENT", "STREAM_CRYPTO_METHOD_SSLv23_CLIENT", + "STREAM_CRYPTO_METHOD_TLS_CLIENT", "STREAM_CRYPTO_METHOD_SSLv2_SERVER", + "STREAM_CRYPTO_METHOD_SSLv3_SERVER", "STREAM_CRYPTO_METHOD_SSLv23_SERVER", + "STREAM_CRYPTO_METHOD_TLS_SERVER", "STREAM_SHUT_RD", "STREAM_SHUT_WR", + "STREAM_SHUT_RDWR", "STREAM_PF_INET", "STREAM_PF_INET6", "STREAM_PF_UNIX", + "STREAM_IPPROTO_IP", "STREAM_IPPROTO_TCP", "STREAM_IPPROTO_UDP", + "STREAM_IPPROTO_ICMP", "STREAM_IPPROTO_RAW", "STREAM_SOCK_STREAM", + "STREAM_SOCK_DGRAM", "STREAM_SOCK_RAW", "STREAM_SOCK_SEQPACKET", + "STREAM_SOCK_RDM", "STREAM_PEEK", "STREAM_OOB", "STREAM_SERVER_BIND", + "STREAM_SERVER_LISTEN", "FILE_USE_INCLUDE_PATH", "FILE_IGNORE_NEW_LINES", + "FILE_SKIP_EMPTY_LINES", "FILE_APPEND", "FILE_NO_DEFAULT_CONTEXT", + "PSFS_PASS_ON", "PSFS_FEED_ME", "PSFS_ERR_FATAL", "PSFS_FLAG_NORMAL", + "PSFS_FLAG_FLUSH_INC", "PSFS_FLAG_FLUSH_CLOSE", "CRYPT_SALT_LENGTH", + "CRYPT_STD_DES", "CRYPT_EXT_DES", "CRYPT_MD5", "CRYPT_BLOWFISH", + "DIRECTORY_SEPARATOR", "PATH_SEPARATOR", "GLOB_BRACE", "GLOB_MARK", + "GLOB_NOSORT", "GLOB_NOCHECK", "GLOB_NOESCAPE", "GLOB_ERR", "GLOB_ONLYDIR", + "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", "LOG_WARNING", "LOG_NOTICE", + "LOG_INFO", "LOG_DEBUG", "LOG_KERN", "LOG_USER", "LOG_MAIL", "LOG_DAEMON", + "LOG_AUTH", "LOG_SYSLOG", "LOG_LPR", "LOG_NEWS", "LOG_UUCP", "LOG_CRON", + "LOG_AUTHPRIV", "LOG_PID", "LOG_CONS", "LOG_ODELAY", "LOG_NDELAY", + "LOG_NOWAIT", "LOG_PERROR", "EXTR_OVERWRITE", "EXTR_SKIP", "EXTR_PREFIX_SAME", + "EXTR_PREFIX_ALL", "EXTR_PREFIX_INVALID", "EXTR_PREFIX_IF_EXISTS", + "EXTR_IF_EXISTS", "EXTR_REFS", "SORT_ASC", "SORT_DESC", "SORT_REGULAR", + "SORT_NUMERIC", "SORT_STRING", "SORT_LOCALE_STRING", "CASE_LOWER", + "CASE_UPPER", "COUNT_NORMAL", "COUNT_RECURSIVE", "ASSERT_ACTIVE", + "ASSERT_CALLBACK", "ASSERT_BAIL", "ASSERT_WARNING", "ASSERT_QUIET_EVAL", + "STREAM_USE_PATH", "STREAM_IGNORE_URL", "STREAM_ENFORCE_SAFE_MODE", + "STREAM_REPORT_ERRORS", "STREAM_MUST_SEEK", "STREAM_URL_STAT_LINK", + "STREAM_URL_STAT_QUIET", "STREAM_MKDIR_RECURSIVE", "IMAGETYPE_GIF", + "IMAGETYPE_JPEG", "IMAGETYPE_PNG", "IMAGETYPE_SWF", "IMAGETYPE_PSD", + "IMAGETYPE_BMP", "IMAGETYPE_TIFF_II", "IMAGETYPE_TIFF_MM", "IMAGETYPE_JPC", + "IMAGETYPE_JP2", "IMAGETYPE_JPX", "IMAGETYPE_JB2", "IMAGETYPE_SWC", + "IMAGETYPE_IFF", "IMAGETYPE_WBMP", "IMAGETYPE_JPEG2000", "IMAGETYPE_XBM", + "T_INCLUDE", "T_INCLUDE_ONCE", "T_EVAL", "T_REQUIRE", "T_REQUIRE_ONCE", + "T_LOGICAL_OR", "T_LOGICAL_XOR", "T_LOGICAL_AND", "T_PRINT", "T_PLUS_EQUAL", + "T_MINUS_EQUAL", "T_MUL_EQUAL", "T_DIV_EQUAL", "T_CONCAT_EQUAL", "T_MOD_EQUAL", + "T_AND_EQUAL", "T_OR_EQUAL", "T_XOR_EQUAL", "T_SL_EQUAL", "T_SR_EQUAL", + "T_BOOLEAN_OR", "T_BOOLEAN_AND", "T_IS_EQUAL", "T_IS_NOT_EQUAL", + "T_IS_IDENTICAL", "T_IS_NOT_IDENTICAL", "T_IS_SMALLER_OR_EQUAL", + "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "T_INC", "T_DEC", "T_INT_CAST", + "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", + "T_BOOL_CAST", "T_UNSET_CAST", "T_NEW", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", + "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", + "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_CHARACTER", + "T_BAD_CHARACTER", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", + "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", + "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_ENDSWITCH", + "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_FUNCTION", "T_CONST", + "T_RETURN", "T_USE", "T_GLOBAL", "T_STATIC", "T_VAR", "T_UNSET", "T_ISSET", + "T_EMPTY", "T_CLASS", "T_EXTENDS", "T_INTERFACE", "T_IMPLEMENTS", + "T_OBJECT_OPERATOR", "T_DOUBLE_ARROW", "T_LIST", "T_ARRAY", "T_CLASS_C", + "T_FUNC_C", "T_METHOD_C", "T_LINE", "T_FILE", "T_COMMENT", "T_DOC_COMMENT", + "T_OPEN_TAG", "T_OPEN_TAG_WITH_ECHO", "T_CLOSE_TAG", "T_WHITESPACE", + "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", + "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_DOUBLE_COLON", "T_ABSTRACT", + "T_CATCH", "T_FINAL", "T_INSTANCEOF", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", + "T_THROW", "T_TRY", "T_CLONE", "T_HALT_COMPILER", "FORCE_GZIP", + "FORCE_DEFLATE", "XML_ELEMENT_NODE", "XML_ATTRIBUTE_NODE", "XML_TEXT_NODE", + "XML_CDATA_SECTION_NODE", "XML_ENTITY_REF_NODE", "XML_ENTITY_NODE", + "XML_PI_NODE", "XML_COMMENT_NODE", "XML_DOCUMENT_NODE", + "XML_DOCUMENT_TYPE_NODE", "XML_DOCUMENT_FRAG_NODE", "XML_NOTATION_NODE", + "XML_HTML_DOCUMENT_NODE", "XML_DTD_NODE", "XML_ELEMENT_DECL_NODE", + "XML_ATTRIBUTE_DECL_NODE", "XML_ENTITY_DECL_NODE", "XML_NAMESPACE_DECL_NODE", + "XML_LOCAL_NAMESPACE", "XML_ATTRIBUTE_CDATA", "XML_ATTRIBUTE_ID", + "XML_ATTRIBUTE_IDREF", "XML_ATTRIBUTE_IDREFS", "XML_ATTRIBUTE_ENTITY", + "XML_ATTRIBUTE_NMTOKEN", "XML_ATTRIBUTE_NMTOKENS", "XML_ATTRIBUTE_ENUMERATION", + "XML_ATTRIBUTE_NOTATION", "DOM_PHP_ERR", "DOM_INDEX_SIZE_ERR", + "DOMSTRING_SIZE_ERR", "DOM_HIERARCHY_REQUEST_ERR", "DOM_WRONG_DOCUMENT_ERR", + "DOM_INVALID_CHARACTER_ERR", "DOM_NO_DATA_ALLOWED_ERR", + "DOM_NO_MODIFICATION_ALLOWED_ERR", "DOM_NOT_FOUND_ERR", + "DOM_NOT_SUPPORTED_ERR", "DOM_INUSE_ATTRIBUTE_ERR", "DOM_INVALID_STATE_ERR", + "DOM_SYNTAX_ERR", "DOM_INVALID_MODIFICATION_ERR", "DOM_NAMESPACE_ERR", + "DOM_INVALID_ACCESS_ERR", "DOM_VALIDATION_ERR", "XML_ERROR_NONE", + "XML_ERROR_NO_MEMORY", "XML_ERROR_SYNTAX", "XML_ERROR_NO_ELEMENTS", + "XML_ERROR_INVALID_TOKEN", "XML_ERROR_UNCLOSED_TOKEN", + "XML_ERROR_PARTIAL_CHAR", "XML_ERROR_TAG_MISMATCH", + "XML_ERROR_DUPLICATE_ATTRIBUTE", "XML_ERROR_JUNK_AFTER_DOC_ELEMENT", + "XML_ERROR_PARAM_ENTITY_REF", "XML_ERROR_UNDEFINED_ENTITY", + "XML_ERROR_RECURSIVE_ENTITY_REF", "XML_ERROR_ASYNC_ENTITY", + "XML_ERROR_BAD_CHAR_REF", "XML_ERROR_BINARY_ENTITY_REF", + "XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF", "XML_ERROR_MISPLACED_XML_PI", + "XML_ERROR_UNKNOWN_ENCODING", "XML_ERROR_INCORRECT_ENCODING", + "XML_ERROR_UNCLOSED_CDATA_SECTION", "XML_ERROR_EXTERNAL_ENTITY_HANDLING", + "XML_OPTION_CASE_FOLDING", "XML_OPTION_TARGET_ENCODING", + "XML_OPTION_SKIP_TAGSTART", "XML_OPTION_SKIP_WHITE", "XML_SAX_IMPL", "IMG_GIF", + "IMG_JPG", "IMG_JPEG", "IMG_PNG", "IMG_WBMP", "IMG_XPM", "IMG_COLOR_TILED", + "IMG_COLOR_STYLED", "IMG_COLOR_BRUSHED", "IMG_COLOR_STYLEDBRUSHED", + "IMG_COLOR_TRANSPARENT", "IMG_ARC_ROUNDED", "IMG_ARC_PIE", "IMG_ARC_CHORD", + "IMG_ARC_NOFILL", "IMG_ARC_EDGED", "IMG_GD2_RAW", "IMG_GD2_COMPRESSED", + "IMG_EFFECT_REPLACE", "IMG_EFFECT_ALPHABLEND", "IMG_EFFECT_NORMAL", + "IMG_EFFECT_OVERLAY", "GD_BUNDLED", "IMG_FILTER_NEGATE", + "IMG_FILTER_GRAYSCALE", "IMG_FILTER_BRIGHTNESS", "IMG_FILTER_CONTRAST", + "IMG_FILTER_COLORIZE", "IMG_FILTER_EDGEDETECT", "IMG_FILTER_GAUSSIAN_BLUR", + "IMG_FILTER_SELECTIVE_BLUR", "IMG_FILTER_EMBOSS", "IMG_FILTER_MEAN_REMOVAL", + "IMG_FILTER_SMOOTH", "PNG_NO_FILTER", "PNG_FILTER_NONE", "PNG_FILTER_SUB", + "PNG_FILTER_UP", "PNG_FILTER_AVG", "PNG_FILTER_PAETH", "PNG_ALL_FILTERS", + "MB_OVERLOAD_MAIL", "MB_OVERLOAD_STRING", "MB_OVERLOAD_REGEX", "MB_CASE_UPPER", + "MB_CASE_LOWER", "MB_CASE_TITLE", "MYSQL_ASSOC", "MYSQL_NUM", "MYSQL_BOTH", + "MYSQL_CLIENT_COMPRESS", "MYSQL_CLIENT_SSL", "MYSQL_CLIENT_INTERACTIVE", + "MYSQL_CLIENT_IGNORE_SPACE", "MYSQLI_READ_DEFAULT_GROUP", + "MYSQLI_READ_DEFAULT_FILE", "MYSQLI_OPT_CONNECT_TIMEOUT", + "MYSQLI_OPT_LOCAL_INFILE", "MYSQLI_INIT_COMMAND", "MYSQLI_CLIENT_SSL", + "MYSQLI_CLIENT_COMPRESS", "MYSQLI_CLIENT_INTERACTIVE", + "MYSQLI_CLIENT_IGNORE_SPACE", "MYSQLI_CLIENT_NO_SCHEMA", + "MYSQLI_CLIENT_FOUND_ROWS", "MYSQLI_STORE_RESULT", "MYSQLI_USE_RESULT", + "MYSQLI_ASSOC", "MYSQLI_NUM", "MYSQLI_BOTH", + "MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH", "MYSQLI_STMT_ATTR_CURSOR_TYPE", + "MYSQLI_CURSOR_TYPE_NO_CURSOR", "MYSQLI_CURSOR_TYPE_READ_ONLY", + "MYSQLI_CURSOR_TYPE_FOR_UPDATE", "MYSQLI_CURSOR_TYPE_SCROLLABLE", + "MYSQLI_STMT_ATTR_PREFETCH_ROWS", "MYSQLI_NOT_NULL_FLAG", + "MYSQLI_PRI_KEY_FLAG", "MYSQLI_UNIQUE_KEY_FLAG", "MYSQLI_MULTIPLE_KEY_FLAG", + "MYSQLI_BLOB_FLAG", "MYSQLI_UNSIGNED_FLAG", "MYSQLI_ZEROFILL_FLAG", + "MYSQLI_AUTO_INCREMENT_FLAG", "MYSQLI_TIMESTAMP_FLAG", "MYSQLI_SET_FLAG", + "MYSQLI_NUM_FLAG", "MYSQLI_PART_KEY_FLAG", "MYSQLI_GROUP_FLAG", + "MYSQLI_TYPE_DECIMAL", "MYSQLI_TYPE_TINY", "MYSQLI_TYPE_SHORT", + "MYSQLI_TYPE_LONG", "MYSQLI_TYPE_FLOAT", "MYSQLI_TYPE_DOUBLE", + "MYSQLI_TYPE_NULL", "MYSQLI_TYPE_TIMESTAMP", "MYSQLI_TYPE_LONGLONG", + "MYSQLI_TYPE_INT24", "MYSQLI_TYPE_DATE", "MYSQLI_TYPE_TIME", + "MYSQLI_TYPE_DATETIME", "MYSQLI_TYPE_YEAR", "MYSQLI_TYPE_NEWDATE", + "MYSQLI_TYPE_ENUM", "MYSQLI_TYPE_SET", "MYSQLI_TYPE_TINY_BLOB", + "MYSQLI_TYPE_MEDIUM_BLOB", "MYSQLI_TYPE_LONG_BLOB", "MYSQLI_TYPE_BLOB", + "MYSQLI_TYPE_VAR_STRING", "MYSQLI_TYPE_STRING", "MYSQLI_TYPE_CHAR", + "MYSQLI_TYPE_INTERVAL", "MYSQLI_TYPE_GEOMETRY", "MYSQLI_TYPE_NEWDECIMAL", + "MYSQLI_TYPE_BIT", "MYSQLI_RPL_MASTER", "MYSQLI_RPL_SLAVE", "MYSQLI_RPL_ADMIN", + "MYSQLI_NO_DATA", "MYSQLI_DATA_TRUNCATED", "MYSQLI_REPORT_INDEX", + "MYSQLI_REPORT_ERROR", "MYSQLI_REPORT_STRICT", "MYSQLI_REPORT_ALL", + "MYSQLI_REPORT_OFF", "AF_UNIX", "AF_INET", "AF_INET6", "SOCK_STREAM", + "SOCK_DGRAM", "SOCK_RAW", "SOCK_SEQPACKET", "SOCK_RDM", "MSG_OOB", + "MSG_WAITALL", "MSG_PEEK", "MSG_DONTROUTE", "SO_DEBUG", "SO_REUSEADDR", + "SO_KEEPALIVE", "SO_DONTROUTE", "SO_LINGER", "SO_BROADCAST", "SO_OOBINLINE", + "SO_SNDBUF", "SO_RCVBUF", "SO_SNDLOWAT", "SO_RCVLOWAT", "SO_SNDTIMEO", + "SO_RCVTIMEO", "SO_TYPE", "SO_ERROR", "SOL_SOCKET", "SOMAXCONN", + "PHP_NORMAL_READ", "PHP_BINARY_READ", "SOCKET_EINTR", "SOCKET_EBADF", + "SOCKET_EACCES", "SOCKET_EFAULT", "SOCKET_EINVAL", "SOCKET_EMFILE", + "SOCKET_EWOULDBLOCK", "SOCKET_EINPROGRESS", "SOCKET_EALREADY", + "SOCKET_ENOTSOCK", "SOCKET_EDESTADDRREQ", "SOCKET_EMSGSIZE", + "SOCKET_EPROTOTYPE", "SOCKET_ENOPROTOOPT", "SOCKET_EPROTONOSUPPORT", + "SOCKET_ESOCKTNOSUPPORT", "SOCKET_EOPNOTSUPP", "SOCKET_EPFNOSUPPORT", + "SOCKET_EAFNOSUPPORT", "SOCKET_EADDRINUSE", "SOCKET_EADDRNOTAVAIL", + "SOCKET_ENETDOWN", "SOCKET_ENETUNREACH", "SOCKET_ENETRESET", + "SOCKET_ECONNABORTED", "SOCKET_ECONNRESET", "SOCKET_ENOBUFS", "SOCKET_EISCONN", + "SOCKET_ENOTCONN", "SOCKET_ESHUTDOWN", "SOCKET_ETOOMANYREFS", + "SOCKET_ETIMEDOUT", "SOCKET_ECONNREFUSED", "SOCKET_ELOOP", + "SOCKET_ENAMETOOLONG", "SOCKET_EHOSTDOWN", "SOCKET_EHOSTUNREACH", + "SOCKET_ENOTEMPTY", "SOCKET_EPROCLIM", "SOCKET_EUSERS", "SOCKET_EDQUOT", + "SOCKET_ESTALE", "SOCKET_EREMOTE", "SOCKET_EDISCON", "SOCKET_SYSNOTREADY", + "SOCKET_VERNOTSUPPORTED", "SOCKET_NOTINITIALISED", "SOCKET_HOST_NOT_FOUND", + "SOCKET_TRY_AGAIN", "SOCKET_NO_RECOVERY", "SOCKET_NO_DATA", + "SOCKET_NO_ADDRESS", "SOL_TCP", "SOL_UDP", "EACCELERATOR_VERSION", + "EACCELERATOR_SHM_AND_DISK", "EACCELERATOR_SHM", "EACCELERATOR_SHM_ONLY", + "EACCELERATOR_DISK_ONLY", "EACCELERATOR_NONE", "XDEBUG_TRACE_APPEND", + "XDEBUG_TRACE_COMPUTERIZED", "XDEBUG_TRACE_HTML", "XDEBUG_CC_UNUSED", + "XDEBUG_CC_DEAD_CODE", "STDIN", "STDOUT", "STDERR", + ].forEach(function(element, index, array) { + result[element] = token("atom", "php-predefined-constant"); + }); + + // PHP declared classes - output of get_declared_classes(). Differs from http://php.net/manual/en/reserved.classes.php + [ "stdClass", "Exception", "ErrorException", "COMPersistHelper", "com_exception", + "com_safearray_proxy", "variant", "com", "dotnet", "ReflectionException", + "Reflection", "ReflectionFunctionAbstract", "ReflectionFunction", + "ReflectionParameter", "ReflectionMethod", "ReflectionClass", + "ReflectionObject", "ReflectionProperty", "ReflectionExtension", "DateTime", + "DateTimeZone", "LibXMLError", "__PHP_Incomplete_Class", "php_user_filter", + "Directory", "SimpleXMLElement", "DOMException", "DOMStringList", + "DOMNameList", "DOMImplementationList", "DOMImplementationSource", + "DOMImplementation", "DOMNode", "DOMNameSpaceNode", "DOMDocumentFragment", + "DOMDocument", "DOMNodeList", "DOMNamedNodeMap", "DOMCharacterData", "DOMAttr", + "DOMElement", "DOMText", "DOMComment", "DOMTypeinfo", "DOMUserDataHandler", + "DOMDomError", "DOMErrorHandler", "DOMLocator", "DOMConfiguration", + "DOMCdataSection", "DOMDocumentType", "DOMNotation", "DOMEntity", + "DOMEntityReference", "DOMProcessingInstruction", "DOMStringExtend", + "DOMXPath", "RecursiveIteratorIterator", "IteratorIterator", "FilterIterator", + "RecursiveFilterIterator", "ParentIterator", "LimitIterator", + "CachingIterator", "RecursiveCachingIterator", "NoRewindIterator", + "AppendIterator", "InfiniteIterator", "RegexIterator", + "RecursiveRegexIterator", "EmptyIterator", "ArrayObject", "ArrayIterator", + "RecursiveArrayIterator", "SplFileInfo", "DirectoryIterator", + "RecursiveDirectoryIterator", "SplFileObject", "SplTempFileObject", + "SimpleXMLIterator", "LogicException", "BadFunctionCallException", + "BadMethodCallException", "DomainException", "InvalidArgumentException", + "LengthException", "OutOfRangeException", "RuntimeException", + "OutOfBoundsException", "OverflowException", "RangeException", + "UnderflowException", "UnexpectedValueException", "SplObjectStorage", + "XMLReader", "XMLWriter", "mysqli_sql_exception", "mysqli_driver", "mysqli", + "mysqli_warning", "mysqli_result", "mysqli_stmt", "PDOException", "PDO", + "PDOStatement", "PDORow" + ].forEach(function(element, index, array) { + result[element] = token("t_string", "php-predefined-class"); + }); + + return result; + + }(); + + // Helper regexps + var isOperatorChar = /[+*&%\/=<>!?.|-]/; + var isHexDigit = /[0-9A-Fa-f]/; + var isWordChar = /[\w\$_]/; + + // Wrapper around phpToken that helps maintain parser state (whether + // we are inside of a multi-line comment) + function phpTokenState(inside) { + return function(source, setState) { + var newInside = inside; + var type = phpToken(inside, source, function(c) {newInside = c;}); + if (newInside != inside) + setState(phpTokenState(newInside)); + return type; + }; + } + + // The token reader, inteded to be used by the tokenizer from + // tokenize.js (through phpTokenState). Advances the source stream + // over a token, and returns an object containing the type and style + // of that token. + function phpToken(inside, source, setInside) { + function readHexNumber(){ + source.next(); // skip the 'x' + source.nextWhileMatches(isHexDigit); + return {type: "number", style: "php-atom"}; + } + + function readNumber() { + source.nextWhileMatches(/[0-9]/); + if (source.equals(".")){ + source.next(); + source.nextWhileMatches(/[0-9]/); + } + if (source.equals("e") || source.equals("E")){ + source.next(); + if (source.equals("-")) + source.next(); + source.nextWhileMatches(/[0-9]/); + } + return {type: "number", style: "php-atom"}; + } + // Read a word and look it up in the keywords array. If found, it's a + // keyword of that type; otherwise it's a PHP T_STRING. + function readWord() { + source.nextWhileMatches(isWordChar); + var word = source.get(); + var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word]; + // since we called get(), tokenize::take won't get() anything. Thus, we must set token.content + return known ? {type: known.type, style: known.style, content: word} : + {type: "t_string", style: "php-t_string", content: word}; + } + function readVariable() { + source.nextWhileMatches(isWordChar); + var word = source.get(); + // in PHP, '$this' is a reserved word, but 'this' isn't. You can have function this() {...} + if (word == "$this") + return {type: "variable", style: "php-keyword", content: word}; + else + return {type: "variable", style: "php-variable", content: word}; + } + + // Advance the stream until the given character (not preceded by a + // backslash) is encountered, or the end of the line is reached. + function nextUntilUnescaped(source, end) { + var escaped = false; + var next; + while(!source.endOfLine()){ + var next = source.next(); + if (next == end && !escaped) + return false; + escaped = next == "\\"; + } + return escaped; + } + + function readSingleLineComment() { + // read until the end of the line or until ?>, which terminates single-line comments + // ` foo` will display "1 foo" + while(!source.lookAhead("?>") && !source.endOfLine()) + source.next(); + return {type: "comment", style: "php-comment"}; + } + /* For multi-line comments, we want to return a comment token for + every line of the comment, but we also want to return the newlines + in them as regular newline tokens. We therefore need to save a + state variable ("inside") to indicate whether we are inside a + multi-line comment. + */ + + function readMultilineComment(start){ + var newInside = "/*"; + var maybeEnd = (start == "*"); + while (true) { + if (source.endOfLine()) + break; + var next = source.next(); + if (next == "/" && maybeEnd){ + newInside = null; + break; + } + maybeEnd = (next == "*"); + } + setInside(newInside); + return {type: "comment", style: "php-comment"}; + } + + // similar to readMultilineComment and nextUntilUnescaped + // unlike comments, strings are not stopped by ?> + function readMultilineString(start){ + var newInside = start; + var escaped = false; + while (true) { + if (source.endOfLine()) + break; + var next = source.next(); + if (next == start && !escaped){ + newInside = null; // we're outside of the string now + break; + } + escaped = (next == "\\"); + } + setInside(newInside); + return { + type: newInside == null? "string" : "string_not_terminated", + style: (start == "'"? "php-string-single-quoted" : "php-string-double-quoted") + }; + } + + // http://php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc + // See also 'nowdoc' on the page. Heredocs are not interrupted by the '?>' token. + function readHeredoc(identifier){ + var token = {}; + if (identifier == "<<<") { + // on our first invocation after reading the <<<, we must determine the closing identifier + if (source.equals("'")) { + // nowdoc + source.nextWhileMatches(isWordChar); + identifier = "'" + source.get() + "'"; + source.next(); // consume the closing "'" + } else if (source.matches(/[A-Za-z_]/)) { + // heredoc + source.nextWhileMatches(isWordChar); + identifier = source.get(); + } else { + // syntax error + setInside(null); + return { type: "error", style: "syntax-error" }; + } + setInside(identifier); + token.type = "string_not_terminated"; + token.style = identifier.charAt(0) == "'"? "php-string-single-quoted" : "php-string-double-quoted"; + token.content = identifier; + } else { + token.style = identifier.charAt(0) == "'"? "php-string-single-quoted" : "php-string-double-quoted"; + // consume a line of heredoc and check if it equals the closing identifier plus an optional semicolon + if (source.lookAhead(identifier, true) && (source.lookAhead(";\n") || source.endOfLine())) { + // the closing identifier can only appear at the beginning of the line + // note that even whitespace after the ";" is forbidden by the PHP heredoc syntax + token.type = "string"; + token.content = source.get(); // don't get the ";" if there is one + setInside(null); + } else { + token.type = "string_not_terminated"; + source.nextWhileMatches(/[^\n]/); + token.content = source.get(); + } + } + return token; + } + + function readOperator() { + source.nextWhileMatches(isOperatorChar); + return {type: "operator", style: "php-operator"}; + } + function readStringSingleQuoted() { + var endBackSlash = nextUntilUnescaped(source, "'", false); + setInside(endBackSlash ? "'" : null); + return {type: "string", style: "php-string-single-quoted"}; + } + function readStringDoubleQuoted() { + var endBackSlash = nextUntilUnescaped(source, "\"", false); + setInside(endBackSlash ? "\"": null); + return {type: "string", style: "php-string-double-quoted"}; + } + + // Fetch the next token. Dispatches on first character in the + // stream, or first two characters when the first is a slash. + switch (inside) { + case null: + case false: break; + case "'": + case "\"": return readMultilineString(inside); + case "/*": return readMultilineComment(source.next()); + default: return readHeredoc(inside); + } + var ch = source.next(); + if (ch == "'" || ch == "\"") + return readMultilineString(ch) + else if (ch == "#") + return readSingleLineComment(); + else if (ch == "$") + return readVariable(); + else if (ch == ":" && source.equals(":")) { + source.next(); + // the T_DOUBLE_COLON can only follow a T_STRING (class name) + return {type: "t_double_colon", style: "php-operator"} + } + // with punctuation, the type of the token is the symbol itself + else if (/[\[\]{}\(\),;:]/.test(ch)) { + return {type: ch, style: "php-punctuation"}; + } + else if (ch == "0" && (source.equals("x") || source.equals("X"))) + return readHexNumber(); + else if (/[0-9]/.test(ch)) + return readNumber(); + else if (ch == "/") { + if (source.equals("*")) + { source.next(); return readMultilineComment(ch); } + else if (source.equals("/")) + return readSingleLineComment(); + else + return readOperator(); + } + else if (ch == "<") { + if (source.lookAhead("<<", true)) { + setInside("<<<"); + return {type: "<<<", style: "php-punctuation"}; + } + else + return readOperator(); + } + else if (isOperatorChar.test(ch)) + return readOperator(); + else + return readWord(); + } + + // The external interface to the tokenizer. + return function(source, startState) { + return tokenizer(source, startState || phpTokenState(false, true)); + }; +})(); diff --git a/media/CodeMirror-0.62/contrib/python/LICENSE b/media/CodeMirror-0.62/contrib/python/LICENSE new file mode 100644 index 0000000..c237889 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/python/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2009, Timothy Farrell +All rights reserved. + +This software is provided for use in connection with the +CodeMirror suite of modules and utilities, hosted and maintained +at http://marijn.haverbeke.nl/codemirror/. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/media/CodeMirror-0.62/contrib/python/css/pythoncolors.css b/media/CodeMirror-0.62/contrib/python/css/pythoncolors.css new file mode 100644 index 0000000..a642a6a --- /dev/null +++ b/media/CodeMirror-0.62/contrib/python/css/pythoncolors.css @@ -0,0 +1,54 @@ +.editbox { + padding: .4em; + margin: 0; + font-family: monospace; + font-size: 10pt; + line-height: 1.1em; + color: black; +} + +pre.code, .editbox { + color: #666666; +} + +.editbox p { + margin: 0; +} + +span.py-delimiter, span.py-special { + color: #666666; +} + +span.py-operator { + color: #666666; +} + +span.py-error { + background-color: #660000; + color: #FFFFFF; +} + +span.py-keyword { + color: #770088; + font-weight: bold; +} + +span.py-literal { + color: #228811; +} + +span.py-identifier, span.py-func { + color: black; +} + +span.py-type, span.py-decorator { + color: #0000FF; +} + +span.py-comment { + color: #AA7700; +} + +span.py-string, span.py-bytes, span.py-raw, span.py-unicode { + color: #AA2222; +} diff --git a/media/CodeMirror-0.62/contrib/python/index.html b/media/CodeMirror-0.62/contrib/python/index.html new file mode 100644 index 0000000..31d2f0a --- /dev/null +++ b/media/CodeMirror-0.62/contrib/python/index.html @@ -0,0 +1,141 @@ + + + + CodeMirror: Python demonstration + + + +

+ This is a simple demonstration of the Python syntax highlighting module + for CodeMirror. +

+

+ Features of this parser include: +

+
    +
  • Token-based syntax highlighting - currently very little lexical analysis happens. Few lexical errors will be detected.
  • +
  • Use the normal indentation mode to enforce regular indentation, otherwise the "shift" indentation mode will give you more flexibility.
  • +
  • Parser Options: +
      +
    • pythonVersion (Integer) - 2 or 3 to indicate which version of Python to parse. Default = 2
    • +
    • strictErrors (Bool) - true to highlight errors that may not be Python errors but cause confusion for this parser. Default = true
    • +
    +
  • +
+

Written by Timothy Farrell (license). Special +thanks to Adam Brand and Marijn Haverbeke for their help in debugging +and providing for this parser.

+ +
+ +
+ + + + diff --git a/media/CodeMirror-0.62/contrib/python/js/parsepython.js b/media/CodeMirror-0.62/contrib/python/js/parsepython.js new file mode 100644 index 0000000..4847c76 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/python/js/parsepython.js @@ -0,0 +1,544 @@ +var PythonParser = Editor.Parser = (function() { + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$"); + } + var DELIMITERCLASS = 'py-delimiter'; + var LITERALCLASS = 'py-literal'; + var ERRORCLASS = 'py-error'; + var OPERATORCLASS = 'py-operator'; + var IDENTIFIERCLASS = 'py-identifier'; + var STRINGCLASS = 'py-string'; + var BYTESCLASS = 'py-bytes'; + var UNICODECLASS = 'py-unicode'; + var RAWCLASS = 'py-raw'; + var NORMALCONTEXT = 'normal'; + var STRINGCONTEXT = 'string'; + var singleOperators = '+-*/%&|^~<>'; + var doubleOperators = wordRegexp(['==', '!=', '\\<=', '\\>=', '\\<\\>', + '\\<\\<', '\\>\\>', '\\/\\/', '\\*\\*']); + var singleDelimiters = '()[]{}@,:.`=;'; + var doubleDelimiters = ['\\+=', '\\-=', '\\*=', '/=', '%=', '&=', '\\|=', + '\\^=']; + var tripleDelimiters = wordRegexp(['//=','\\>\\>=','\\<\\<=','\\*\\*=']); + var singleStarters = singleOperators + singleDelimiters + '=!'; + var doubleStarters = '=<>*/'; + var identifierStarters = /[_A-Za-z]/; + + var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']); + var commonkeywords = ['as', 'assert', 'break', 'class', 'continue', + 'def', 'del', 'elif', 'else', 'except', 'finally', + 'for', 'from', 'global', 'if', 'import', + 'lambda', 'pass', 'raise', 'return', + 'try', 'while', 'with', 'yield']; + var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate', + 'float', 'frozenset', 'int', 'list', 'object', + 'property', 'reversed', 'set', 'slice', 'staticmethod', + 'str', 'super', 'tuple', 'type']; + var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode', + 'xrange'], + 'keywords': ['exec', 'print'], + 'version': 2 }; + var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview', + 'open', 'range', 'zip'], + 'keywords': ['nonlocal'], + 'version': 3}; + + var py, keywords, types, stringStarters, stringTypes, config; + + function configure(conf) { + if (!conf.hasOwnProperty('pythonVersion')) { + conf.pythonVersion = 2; + } + if (!conf.hasOwnProperty('strictErrors')) { + conf.strictErrors = true; + } + if (conf.pythonVersion != 2 && conf.pythonVersion != 3) { + alert('CodeMirror: Unknown Python Version "' + + conf.pythonVersion + + '", defaulting to Python 2.x.'); + conf.pythonVersion = 2; + } + if (conf.pythonVersion == 3) { + py = py3; + stringStarters = /[\'\"rbRB]/; + stringTypes = /[rb]/; + doubleDelimiters.push('\\-\\>'); + } else { + py = py2; + stringStarters = /['"RUru]/; + stringTypes = /[ru]/; + } + config = conf; + keywords = wordRegexp(commonkeywords.concat(py.keywords)); + types = wordRegexp(commontypes.concat(py.types)); + doubleDelimiters = wordRegexp(doubleDelimiters); + } + + var tokenizePython = (function() { + function normal(source, setState) { + var stringDelim, threeStr, temp, type, word, possible = {}; + var ch = source.next(); + + function filterPossible(token, styleIfPossible) { + if (!possible.style && !possible.content) { + return token; + } else if (typeof(token) == STRINGCONTEXT) { + token = {content: source.get(), style: token}; + } + if (possible.style || styleIfPossible) { + token.style = styleIfPossible ? styleIfPossible : possible.style; + } + if (possible.content) { + token.content = possible.content + token.content; + } + possible = {}; + return token; + } + + // Handle comments + if (ch == '#') { + while (!source.endOfLine()) { + source.next(); + } + return 'py-comment'; + } + // Handle special chars + if (ch == '\\') { + if (source.peek() != '\n') { + var whitespace = true; + while (!source.endOfLine()) { + if(!(/\s/.test(source.next()))) { + whitespace = false; + } + } + if (!whitespace) { + return ERRORCLASS; + } + } + return 'py-special'; + } + // Handle operators and delimiters + if (singleStarters.indexOf(ch) != -1) { + if (doubleStarters.indexOf(source.peek()) != -1) { + temp = ch + source.peek(); + // It must be a double delimiter or operator or triple delimiter + if (doubleOperators.test(temp)) { + source.next(); + if (tripleDelimiters.test(temp + source.peek())) { + source.next(); + return DELIMITERCLASS; + } else { + return OPERATORCLASS; + } + } else if (doubleDelimiters.test(temp)) { + source.next(); + return DELIMITERCLASS; + } + } + // It must be a single delimiter or operator + if (singleOperators.indexOf(ch) != -1) { + return OPERATORCLASS; + } else if (singleDelimiters.indexOf(ch) != -1) { + if (ch == '@' && /\w/.test(source.peek())) { + possible = {style:'py-decorator', + content: source.get()}; + ch = source.next(); + } else if (ch == '.' && /\d/.test(source.peek())) { + possible = {style:LITERALCLASS, + content: source.get()}; + ch = source.next(); + } else { + return DELIMITERCLASS; + } + } else { + return ERRORCLASS; + } + } + // Handle number literals + if (/\d/.test(ch)) { + if (ch === '0' && !source.endOfLine()) { + switch (source.peek()) { + case 'o': + case 'O': + source.next(); + source.nextWhileMatches(/[0-7]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + case 'x': + case 'X': + source.next(); + source.nextWhileMatches(/[0-9A-Fa-f]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + case 'b': + case 'B': + source.next(); + source.nextWhileMatches(/[01]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + } + } + source.nextWhileMatches(/\d/); + if (source.peek() == '.') { + source.next(); + source.nextWhileMatches(/\d/); + } + // Grab an exponent + if (source.peek().toLowerCase() == 'e') { + source.next(); + if (source.peek() == '+' || source.peek() == '-') { + source.next(); + } + if (/\d/.test(source.peek())) { + source.nextWhileMatches(/\d/); + } else { + return filterPossible(ERRORCLASS); + } + } + // Grab a complex number + if (source.peek().toLowerCase() == 'j') { + source.next(); + } + + return filterPossible(LITERALCLASS); + } + // Handle strings + if (stringStarters.test(ch)) { + var peek = source.peek(); + var stringType = STRINGCLASS; + if ((stringTypes.test(ch)) && (peek == '"' || peek == "'")) { + switch (ch.toLowerCase()) { + case 'b': + stringType = BYTESCLASS; + break; + case 'r': + stringType = RAWCLASS; + break; + case 'u': + stringType = UNICODECLASS; + break; + } + ch = source.next(); + stringDelim = ch; + if (source.peek() != stringDelim) { + setState(inString(stringType, stringDelim)); + return null; + } else { + source.next(); + if (source.peek() == stringDelim) { + source.next(); + threeStr = stringDelim + stringDelim + stringDelim; + setState(inString(stringType, threeStr)); + return null; + } else { + return stringType; + } + } + } else if (ch == "'" || ch == '"') { + stringDelim = ch; + if (source.peek() != stringDelim) { + setState(inString(stringType, stringDelim)); + return null; + } else { + source.next(); + if (source.peek() == stringDelim) { + source.next(); + threeStr = stringDelim + stringDelim + stringDelim; + setState(inString(stringType, threeStr)); + return null; + } else { + return stringType; + } + } + } + } + // Handle Identifier + if (identifierStarters.test(ch)) { + source.nextWhileMatches(/[\w\d]/); + word = source.get(); + if (wordOperators.test(word)) { + type = OPERATORCLASS; + } else if (keywords.test(word)) { + type = 'py-keyword'; + } else if (types.test(word)) { + type = 'py-type'; + } else { + type = IDENTIFIERCLASS; + while (source.peek() == '.') { + source.next(); + if (identifierStarters.test(source.peek())) { + source.nextWhileMatches(/[\w\d]/); + } else { + type = ERRORCLASS; + break; + } + } + word = word + source.get(); + } + return filterPossible({style: type, content: word}); + } + + // Register Dollar sign and Question mark as errors. Always! + if (/\$\?/.test(ch)) { + return filterPossible(ERRORCLASS); + } + + return filterPossible(ERRORCLASS); + } + + function inString(style, terminator) { + return function(source, setState) { + var matches = []; + var found = false; + while (!found && !source.endOfLine()) { + var ch = source.next(), newMatches = []; + // Skip escaped characters + if (ch == '\\') { + if (source.peek() == '\n') { + break; + } + ch = source.next(); + ch = source.next(); + } + if (ch == terminator.charAt(0)) { + matches.push(terminator); + } + for (var i = 0; i < matches.length; i++) { + var match = matches[i]; + if (match.charAt(0) == ch) { + if (match.length == 1) { + setState(normal); + found = true; + break; + } else { + newMatches.push(match.slice(1)); + } + } + } + matches = newMatches; + } + return style; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); + + function parsePython(source) { + if (!keywords) { + configure({}); + } + + var tokens = tokenizePython(source); + var lastToken = null; + var column = 0; + var context = {prev: null, + endOfScope: false, + startNewScope: false, + level: 0, + next: null, + type: NORMALCONTEXT + }; + + function pushContext(level, type) { + type = type ? type : NORMALCONTEXT; + context = {prev: context, + endOfScope: false, + startNewScope: false, + level: level, + next: null, + type: type + }; + } + + function popContext(remove) { + remove = remove ? remove : false; + if (context.prev) { + if (remove) { + context = context.prev; + context.next = null; + } else { + context.prev.next = context; + context = context.prev; + } + } + } + + function indentPython(context) { + var temp; + return function(nextChars, currentLevel, direction) { + if (direction === null || direction === undefined) { + if (nextChars) { + while (context.next) { + context = context.next; + } + } + return context.level; + } + else if (direction === true) { + if (currentLevel == context.level) { + if (context.next) { + return context.next.level; + } else { + return context.level; + } + } else { + temp = context; + while (temp.prev && temp.prev.level > currentLevel) { + temp = temp.prev; + } + return temp.level; + } + } else if (direction === false) { + if (currentLevel > context.level) { + return context.level; + } else if (context.prev) { + temp = context; + while (temp.prev && temp.prev.level >= currentLevel) { + temp = temp.prev; + } + if (temp.prev) { + return temp.prev.level; + } else { + return temp.level; + } + } + } + return context.level; + }; + } + + var iter = { + next: function() { + var token = tokens.next(); + var type = token.style; + var content = token.content; + + if (lastToken) { + if (lastToken.content == 'def' && type == IDENTIFIERCLASS) { + token.style = 'py-func'; + } + if (lastToken.content == '\n') { + var tempCtx = context; + // Check for a different scope + if (type == 'whitespace' && context.type == NORMALCONTEXT) { + if (token.value.length < context.level) { + while (token.value.length < context.level) { + popContext(); + } + + if (token.value.length != context.level) { + context = tempCtx; + if (config.strictErrors) { + token.style = ERRORCLASS; + } + } else { + context.next = null; + } + } + } else if (context.level !== 0 && + context.type == NORMALCONTEXT) { + while (0 !== context.level) { + popContext(); + } + + if (context.level !== 0) { + context = tempCtx; + if (config.strictErrors) { + token.style = ERRORCLASS; + } + } + } + } + } + + // Handle Scope Changes + switch(type) { + case STRINGCLASS: + case BYTESCLASS: + case RAWCLASS: + case UNICODECLASS: + if (context.type !== STRINGCONTEXT) { + pushContext(context.level + 1, STRINGCONTEXT); + } + break; + default: + if (context.type === STRINGCONTEXT) { + popContext(true); + } + break; + } + switch(content) { + case '.': + case '@': + // These delimiters don't appear by themselves + if (content !== token.value) { + token.style = ERRORCLASS; + } + break; + case ':': + // Colons only delimit scope inside a normal scope + if (context.type === NORMALCONTEXT) { + context.startNewScope = context.level+indentUnit; + } + break; + case '(': + case '[': + case '{': + // These start a sequence scope + pushContext(column + content.length, 'sequence'); + break; + case ')': + case ']': + case '}': + // These end a sequence scope + popContext(true); + break; + case 'pass': + case 'return': + // These end a normal scope + if (context.type === NORMALCONTEXT) { + context.endOfScope = true; + } + break; + case '\n': + // Reset our column + column = 0; + // Make any scope changes + if (context.endOfScope) { + context.endOfScope = false; + popContext(); + } else if (context.startNewScope !== false) { + var temp = context.startNewScope; + context.startNewScope = false; + pushContext(temp, NORMALCONTEXT); + } + // Newlines require an indentation function wrapped in a closure for proper context. + token.indentation = indentPython(context); + break; + } + + // Keep track of current column for certain scopes. + if (content != '\n') { + column += token.value.length; + } + + lastToken = token; + return token; + }, + + copy: function() { + var _context = context, _tokenState = tokens.state; + return function(source) { + tokens = tokenizePython(source, _tokenState); + context = _context; + return iter; + }; + } + }; + return iter; + } + + return {make: parsePython, + electricChars: "", + configure: configure}; +})(); diff --git a/media/CodeMirror-0.62/css/csscolors.css b/media/CodeMirror-0.62/css/csscolors.css new file mode 100644 index 0000000..100c93f --- /dev/null +++ b/media/CodeMirror-0.62/css/csscolors.css @@ -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; +} diff --git a/media/CodeMirror-0.62/css/docs.css b/media/CodeMirror-0.62/css/docs.css new file mode 100644 index 0000000..c86b7d7 --- /dev/null +++ b/media/CodeMirror-0.62/css/docs.css @@ -0,0 +1,46 @@ +body { + margin: 0; + padding: 3em 6em; + color: black; + max-width: 50em; +} + +h1 { + font-size: 22pt; +} + +.underline { + border-bottom: 3px solid #C44; +} + +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: #144; +} + +pre.code { + margin: 1.1em 12px; + border: 1px solid #CCCCCC; + color: black; + padding: .4em; + font-family: courier, monospace; +} + +.warn { + color: #C00; +} diff --git a/media/CodeMirror-0.62/css/jscolors.css b/media/CodeMirror-0.62/css/jscolors.css new file mode 100644 index 0000000..3067628 --- /dev/null +++ b/media/CodeMirror-0.62/css/jscolors.css @@ -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; +} diff --git a/media/CodeMirror-0.62/css/people.jpg b/media/CodeMirror-0.62/css/people.jpg new file mode 100644 index 0000000000000000000000000000000000000000..734789542b3533f464c3eb18908f06e48f44e55d GIT binary patch literal 14122 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 + + + CodeMirror: CSS demonstration + + + + +

Demonstration of CodeMirror's CSS +highlighter.

+ +
+ +
+ + + + + diff --git a/media/CodeMirror-0.62/highlight.html b/media/CodeMirror-0.62/highlight.html new file mode 100644 index 0000000..6f1dd34 --- /dev/null +++ b/media/CodeMirror-0.62/highlight.html @@ -0,0 +1,82 @@ + + + + + + + + CodeMirror: String highlight demonstration + + + + +
+ +
+ + + +
+
+

+
+ + + + + diff --git a/media/CodeMirror-0.62/js/codemirror.js b/media/CodeMirror-0.62/js/codemirror.js new file mode 100644 index 0000000..aac55f5 --- /dev/null +++ b/media/CodeMirror-0.62/js/codemirror.js @@ -0,0 +1,298 @@ +/* CodeMirror main module + * + * Implements the CodeMirror constructor and prototype, which take care + * of initializing the editor frame, and providing the outside interface. + */ + +// The CodeMirrorConfig object is used to specify a default +// configuration. If you specify such an object before loading this +// file, the values you put into it will override the defaults given +// below. You can also assign to it after loading. +var CodeMirrorConfig = window.CodeMirrorConfig || {}; + +var CodeMirror = (function(){ + function setDefaults(object, defaults) { + for (var option in defaults) { + if (!object.hasOwnProperty(option)) + object[option] = defaults[option]; + } + } + function forEach(array, action) { + for (var i = 0; i < array.length; i++) + action(array[i]); + } + + // These default options can be overridden by passing a set of + // options to a specific CodeMirror constructor. See manual.html for + // their meaning. + setDefaults(CodeMirrorConfig, { + stylesheet: "", + path: "", + parserfile: [], + basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"], + iframeClass: null, + passDelay: 200, + passTime: 50, + continuousScanning: false, + saveFunction: null, + onChange: null, + undoDepth: 50, + undoDelay: 800, + disableSpellcheck: true, + textWrapping: true, + readOnly: false, + width: "100%", + height: "300px", + autoMatchParens: false, + parserConfig: null, + tabMode: "indent", // or "spaces", "default", "shift" + reindentOnLoad: false, + activeTokens: null, + cursorActivity: null, + lineNumbers: false, + indentUnit: 2 + }); + + function wrapLineNumberDiv(place) { + return function(node) { + var container = document.createElement("DIV"), + nums = document.createElement("DIV"), + scroller = document.createElement("DIV"); + container.style.position = "relative"; + nums.style.position = "absolute"; + nums.style.height = "100%"; + if (nums.style.setExpression) { + try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");} + catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions + } + nums.style.top = "0px"; + nums.style.overflow = "hidden"; + place(container); + container.appendChild(node); + container.appendChild(nums); + scroller.className = "CodeMirror-line-numbers"; + nums.appendChild(scroller); + } + } + + function applyLineNumbers(frame) { + var win = frame.contentWindow, doc = win.document, + nums = frame.nextSibling, scroller = nums.firstChild; + + var nextNum = 1, barWidth = null; + function sizeBar() { + if (nums.offsetWidth != barWidth) { + barWidth = nums.offsetWidth; + nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px"); + } + } + function update() { + var diff = 20 + Math.max(doc.body.offsetHeight, frame.offsetHeight) - scroller.offsetHeight; + for (var n = Math.ceil(diff / 10); n > 0; n--) { + var div = document.createElement("DIV"); + div.appendChild(document.createTextNode(nextNum++)); + scroller.appendChild(div); + } + nums.scrollTop = doc.body.scrollTop || doc.documentElement.scrollTop || 0; + } + sizeBar(); + update(); + win.addEventHandler(win, "scroll", update); + setInterval(sizeBar, 500); + } + + function CodeMirror(place, options) { + // Backward compatibility for deprecated options. + if (options.dumbTabs) options.tabMode = "spaces"; + else if (options.normalTab) options.tabMode = "default"; + + // Use passed options, if any, to override defaults. + this.options = options = options || {}; + setDefaults(options, CodeMirrorConfig); + + var frame = this.frame = document.createElement("IFRAME"); + if (options.iframeClass) frame.className = options.iframeClass; + frame.frameBorder = 0; + frame.src = "javascript:false;"; + frame.style.border = "0"; + frame.style.width = options.width; + frame.style.height = options.height; + // display: block occasionally suppresses some Firefox bugs, so we + // always add it, redundant as it sounds. + frame.style.display = "block"; + + if (place.appendChild) { + var node = place; + place = function(n){node.appendChild(n);}; + } + if (options.lineNumbers) place = wrapLineNumberDiv(place); + place(frame); + + // Link back to this object, so that the editor can fetch options + // and add a reference to itself. + frame.CodeMirror = this; + this.win = frame.contentWindow; + + if (typeof options.parserfile == "string") + options.parserfile = [options.parserfile]; + if (typeof options.stylesheet == "string") + options.stylesheet = [options.stylesheet]; + + var html = [""]; + forEach(options.stylesheet, function(file) { + html.push(""); + }); + forEach(options.basefiles.concat(options.parserfile), function(file) { + html.push(""); + }); + html.push(""); + + var doc = this.win.document; + doc.open(); + doc.write(html.join("")); + doc.close(); + } + + CodeMirror.prototype = { + init: function() { + if (this.options.initCallback) this.options.initCallback(this); + if (this.options.lineNumbers) applyLineNumbers(this.frame); + if (this.options.reindentOnLoad) this.reindent(); + }, + + getCode: function() {return this.editor.getCode();}, + setCode: function(code) {this.editor.importCode(code);}, + selection: function() {return this.editor.selectedText();}, + reindent: function() {this.editor.reindent();}, + reindentSelection: function() {this.editor.reindentSelection(null);}, + + focus: function() { + this.win.focus(); + if (this.editor.selectionSnapshot) // IE hack + this.win.select.selectCoords(this.win, this.editor.selectionSnapshot); + }, + replaceSelection: function(text) { + this.focus(); + this.editor.replaceSelection(text); + return true; + }, + replaceChars: function(text, start, end) { + this.editor.replaceChars(text, start, end); + }, + getSearchCursor: function(string, fromCursor) { + return this.editor.getSearchCursor(string, fromCursor); + }, + + undo: function() {this.editor.history.undo();}, + redo: function() {this.editor.history.redo();}, + historySize: function() {return this.editor.history.historySize();}, + clearHistory: function() {this.editor.history.clear();}, + + grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);}, + ungrabKeys: function() {this.editor.ungrabKeys();}, + + setParser: function(name) {this.editor.setParser(name);}, + + cursorPosition: function(start) { + if (this.win.select.ie_selection) this.focus(); + return this.editor.cursorPosition(start); + }, + firstLine: function() {return this.editor.firstLine();}, + lastLine: function() {return this.editor.lastLine();}, + nextLine: function(line) {return this.editor.nextLine(line);}, + prevLine: function(line) {return this.editor.prevLine(line);}, + lineContent: function(line) {return this.editor.lineContent(line);}, + setLineContent: function(line, content) {this.editor.setLineContent(line, content);}, + insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);}, + selectLines: function(startLine, startOffset, endLine, endOffset) { + this.win.focus(); + this.editor.selectLines(startLine, startOffset, endLine, endOffset); + }, + nthLine: function(n) { + var line = this.firstLine(); + for (; n > 1 && line !== false; n--) + line = this.nextLine(line); + return line; + }, + lineNumber: function(line) { + var num = 0; + while (line !== false) { + num++; + line = this.prevLine(line); + } + return num; + }, + + // Old number-based line interface + jumpToLine: function(n) { + this.selectLines(this.nthLine(n), 0); + this.win.focus(); + }, + currentLine: function() { + return this.lineNumber(this.cursorPosition().line); + } + }; + + CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}}; + + CodeMirror.replace = function(element) { + if (typeof element == "string") + element = document.getElementById(element); + return function(newElement) { + element.parentNode.replaceChild(newElement, element); + }; + }; + + CodeMirror.fromTextArea = function(area, options) { + if (typeof area == "string") + area = document.getElementById(area); + + options = options || {}; + if (area.style.width && options.width == null) + options.width = area.style.width; + if (area.style.height && options.height == null) + options.height = area.style.height; + if (options.content == null) options.content = area.value; + + if (area.form) { + function updateField() { + area.value = mirror.getCode(); + } + if (typeof area.form.addEventListener == "function") + area.form.addEventListener("submit", updateField, false); + else + area.form.attachEvent("onsubmit", updateField); + } + + function insert(frame) { + if (area.nextSibling) + area.parentNode.insertBefore(frame, area.nextSibling); + else + area.parentNode.appendChild(frame); + } + + area.style.display = "none"; + var mirror = new CodeMirror(insert, options); + return mirror; + }; + + CodeMirror.isProbablySupported = function() { + // This is rather awful, but can be useful. + var match; + if (window.opera) + return Number(window.opera.version()) >= 9.52; + else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./))) + return Number(match[1]) >= 3; + else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/))) + return Number(match[1]) >= 6; + else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i)) + return Number(match[1]) >= 20050901; + else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/)) + return Number(match[1]) >= 525; + else + return null; + }; + + return CodeMirror; +})(); diff --git a/media/CodeMirror-0.62/js/editor.js b/media/CodeMirror-0.62/js/editor.js new file mode 100644 index 0000000..e580ccb --- /dev/null +++ b/media/CodeMirror-0.62/js/editor.js @@ -0,0 +1,1303 @@ +/* The Editor object manages the content of the editable frame. It + * catches events, colours nodes, and indents lines. This file also + * holds some functions for transforming arbitrary DOM structures into + * plain sequences of and
elements + */ + +// Make sure a string does not contain two consecutive 'collapseable' +// whitespace characters. +function makeWhiteSpace(n) { + var buffer = [], nb = true; + for (; n > 0; n--) { + buffer.push((nb || n == 1) ? nbsp : " "); + nb = !nb; + } + return buffer.join(""); +} + +// Create a set of white-space characters that will not be collapsed +// by the browser, but will not break text-wrapping either. +function fixSpaces(string) { + if (string.charAt(0) == " ") string = nbsp + string.slice(1); + return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);}) + .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);}); +} + +function cleanText(text) { + return text.replace(/\u00a0/g, " ").replace(/\u200b/g, ""); +} + +// Create a SPAN node with the expected properties for document part +// spans. +function makePartSpan(value, doc) { + var text = value; + if (value.nodeType == 3) text = value.nodeValue; + else value = doc.createTextNode(text); + + var span = doc.createElement("SPAN"); + span.isPart = true; + span.appendChild(value); + span.currentText = text; + return span; +} + +// On webkit, when the last BR of the document does not have text +// behind it, the cursor can not be put on the line after it. This +// makes pressing enter at the end of the document occasionally do +// nothing (or at least seem to do nothing). To work around it, this +// function makes sure the document ends with a span containing a +// zero-width space character. The traverseDOM iterator filters such +// character out again, so that the parsers won't see them. This +// function is called from a few strategic places to make sure the +// zwsp is restored after the highlighting process eats it. +var webkitLastLineHack = webkit ? + function(container) { + var last = container.lastChild; + if (!last || !last.isPart || last.textContent != "\u200b") + container.appendChild(makePartSpan("\u200b", container.ownerDocument)); + } : function() {}; + +var Editor = (function(){ + // The HTML elements whose content should be suffixed by a newline + // when converting them to flat text. + var newlineElements = {"P": true, "DIV": true, "LI": true}; + + function asEditorLines(string) { + var tab = makeWhiteSpace(indentUnit); + return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces); + } + + // Helper function for traverseDOM. Flattens an arbitrary DOM node + // into an array of textnodes and
tags. + function simplifyDOM(root) { + var doc = root.ownerDocument; + var result = []; + var leaving = true; + + function simplifyNode(node) { + if (node.nodeType == 3) { + var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " ")); + if (text.length) leaving = false; + result.push(node); + } + else if (node.nodeName == "BR" && node.childNodes.length == 0) { + leaving = true; + result.push(node); + } + else { + forEach(node.childNodes, simplifyNode); + if (!leaving && newlineElements.hasOwnProperty(node.nodeName)) { + leaving = true; + 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 + // and
elements. + // See the story.html file for some short remarks about the use of + // continuation-passing style in this iterator. + function traverseDOM(start){ + function yield(value, c){cc = c; return value;} + function push(fun, arg, c){return function(){return fun(arg, c);};} + function stop(){cc = stop; throw StopIteration;}; + var cc = push(scanNode, start, stop); + var owner = start.ownerDocument; + var nodeQueue = []; + + // Create a function that can be used to insert nodes after the + // one given as argument. + function pointAt(node){ + var parent = node.parentNode; + var next = node.nextSibling; + return function(newnode) { + parent.insertBefore(newnode, next); + }; + } + var point = null; + + // Insert a normalized node at the current point. If it is a text + // node, wrap it in a , and give that span a currentText + // property -- this is used to cache the nodeValue, because + // directly accessing nodeValue is horribly slow on some browsers. + // The dirty property is used by the highlighter to determine + // which parts of the document have to be re-highlighted. + function insertPart(part){ + var text = "\n"; + if (part.nodeType == 3) { + select.snapshotChanged(); + part = makePartSpan(part, owner); + text = part.currentText; + } + part.dirty = true; + nodeQueue.push(part); + point(part); + return text; + } + + // Extract the text and newlines from a DOM node, insert them into + // the document, and yield the textual content. Used to replace + // non-normalized nodes. + function writeNode(node, c){ + var toYield = []; + forEach(simplifyDOM(node), function(part) { + toYield.push(insertPart(part)); + }); + return yield(toYield.join(""), c); + } + + // Check whether a node is a normalized element. + function partNode(node){ + if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + node.currentText = node.firstChild.nodeValue; + return !/[\n\t\r]/.test(node.currentText); + } + return false; + } + + // Handle a node. Add its successor to the continuation if there + // is one, find out whether the node is normalized. If it is, + // yield its content, otherwise, normalize it (writeNode will take + // care of yielding). + function scanNode(node, c){ + if (node.nextSibling) + c = push(scanNode, node.nextSibling, c); + + if (partNode(node)){ + nodeQueue.push(node); + return yield(node.currentText, c); + } + else if (node.nodeName == "BR") { + nodeQueue.push(node); + return yield("\n", c); + } + else { + 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; + else if (node.nodeName == "BR") node = node.nextSibling; + + while (node && node.nodeName != "BR") node = node.nextSibling; + return node; + } + + function time() {return new Date().getTime();} + + // Replace all DOM nodes in the current selection with new ones. + // Needed to prevent issues in IE where the old DOM nodes can be + // pasted back into the document, still holding their old undo + // information. + function scrubPasted(container, start, start2) { + var end = select.selectionTopNode(container, true), + doc = container.ownerDocument; + if (start != null && start.parentNode != container) start = start2; + if (start === false) start = null; + if (start == end || !end || !container.firstChild) return; + + var clear = traverseDOM(start ? start.nextSibling : container.firstChild); + while (end.parentNode == container) try{clear.next();}catch(e){break;} + forEach(clear.nodes, function(node) { + var newNode = node.nodeName == "BR" ? doc.createElement("BR") : makePartSpan(node.currentText, doc); + container.replaceChild(newNode, node); + }); + } + + // Client interface for searching the content of the editor. Create + // these by calling CodeMirror.getSearchCursor. To use, call + // findNext on the resulting object -- this returns a boolean + // indicating whether anything was found, and can be called again to + // skip to the next find. Use the select and replace methods to + // actually do something with the found locations. + function SearchCursor(editor, string, fromCursor) { + this.editor = editor; + this.history = editor.history; + this.history.commit(); + + // Are we currently at an occurrence of the search string? + this.atOccurrence = false; + // The object stores a set of nodes coming after its current + // position, so that when the current point is taken out of the + // DOM tree, we can still try to continue. + this.fallbackSize = 15; + var cursor; + // Start from the cursor when specified and a cursor can be found. + if (fromCursor && (cursor = select.cursorPos(this.editor.container))) { + this.line = cursor.node; + this.offset = cursor.offset; + } + else { + this.line = null; + this.offset = 0; + } + this.valid = !!string; + + // Create a matcher function based on the kind of string we have. + var target = string.split("\n"), self = this; + this.matches = (target.length == 1) ? + // For one-line strings, searching can be done simply by calling + // indexOf on the current line. + function() { + var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string); + if (match > -1) + return {from: {node: self.line, offset: self.offset + match}, + to: {node: self.line, offset: self.offset + match + string.length}}; + } : + // Multi-line strings require internal iteration over lines, and + // some clunky checks to make sure the first match ends at the + // end of the line and the last match starts at the start. + function() { + var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset)); + var match = firstLine.lastIndexOf(target[0]); + if (match == -1 || match != firstLine.length - target[0].length) + return false; + var startOffset = self.offset + match; + + var line = self.history.nodeAfter(self.line); + for (var i = 1; i < target.length - 1; i++) { + if (cleanText(self.history.textAfter(line)) != target[i]) + return false; + line = self.history.nodeAfter(line); + } + + if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0) + return false; + + return {from: {node: self.line, offset: startOffset}, + to: {node: line, offset: target[target.length - 1].length}}; + }; + } + + SearchCursor.prototype = { + findNext: function() { + if (!this.valid) return false; + this.atOccurrence = false; + var self = this; + + // Go back to the start of the document if the current line is + // no longer in the DOM tree. + if (this.line && !this.line.parentNode) { + this.line = null; + this.offset = 0; + } + + // Set the cursor's position one character after the given + // position. + function saveAfter(pos) { + if (self.history.textAfter(pos.node).length < pos.offset) { + self.line = pos.node; + self.offset = pos.offset + 1; + } + else { + self.line = self.history.nodeAfter(pos.node); + self.offset = 0; + } + } + + while (true) { + var match = this.matches(); + // Found the search string. + if (match) { + this.atOccurrence = match; + saveAfter(match.from); + return true; + } + this.line = this.history.nodeAfter(this.line); + this.offset = 0; + // End of document. + if (!this.line) { + this.valid = false; + return false; + } + } + }, + + select: function() { + if (this.atOccurrence) { + select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to); + select.scrollToCursor(this.editor.container); + } + }, + + replace: function(string) { + if (this.atOccurrence) { + var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string); + this.line = end.node; + this.offset = end.offset; + this.atOccurrence = false; + } + } + }; + + // The Editor object is the main inside-the-iframe interface. + function Editor(options) { + this.options = options; + window.indentUnit = options.indentUnit; + this.parent = parent; + this.doc = document; + var container = this.container = this.doc.body; + this.win = window; + this.history = new History(container, options.undoDepth, options.undoDelay, + this, options.onChange); + var self = this; + + if (!Editor.Parser) + throw "No parser loaded."; + if (options.parserConfig && Editor.Parser.configure) + Editor.Parser.configure(options.parserConfig); + + if (!options.readOnly) + select.setCursorPos(container, {node: null, offset: 0}); + + this.dirty = []; + if (options.content) + this.importCode(options.content); + else // FF acts weird when the editable document is completely empty + container.appendChild(this.doc.createElement("BR")); + + if (!options.readOnly) { + if (options.continuousScanning !== false) { + this.scanner = this.documentScanner(options.passTime); + this.delayScanning(); + } + + function setEditable() { + // In IE, designMode frames can not run any scripts, so we use + // contentEditable instead. + if (document.body.contentEditable != undefined && internetExplorer) + document.body.contentEditable = "true"; + else + document.designMode = "on"; + + document.documentElement.style.borderWidth = "0"; + if (!options.textWrapping) + container.style.whiteSpace = "nowrap"; + } + + // If setting the frame editable fails, try again when the user + // focus it (happens when the frame is not visible on + // initialisation, in Firefox). + try { + setEditable(); + } + catch(e) { + var focusEvent = addEventHandler(document, "focus", function() { + focusEvent(); + setEditable(); + }, true); + } + + addEventHandler(document, "keydown", method(this, "keyDown")); + addEventHandler(document, "keypress", method(this, "keyPress")); + addEventHandler(document, "keyup", method(this, "keyUp")); + + function cursorActivity() {self.cursorActivity(false);} + addEventHandler(document.body, "mouseup", cursorActivity); + addEventHandler(document.body, "paste", function(event) { + cursorActivity(); + if (internetExplorer) { + var text = null; + try {text = window.clipboardData.getData("Text");}catch(e){} + if (text != null) { + self.replaceSelection(text); + event.stop(); + } + else { + var start = select.selectionTopNode(self.container, true), + start2 = start && start.previousSibling; + setTimeout(function(){scrubPasted(self.container, start, start2);}, 0); + } + } + }); + addEventHandler(document.body, "cut", cursorActivity); + + if (this.options.autoMatchParens) + addEventHandler(document.body, "click", method(this, "scheduleParenBlink")); + } + else if (!options.textWrapping) { + container.style.whiteSpace = "nowrap"; + } + } + + function isSafeKey(code) { + return (code >= 16 && code <= 18) || // shift, control, alt + (code >= 33 && code <= 40); // arrows, home, end + } + + Editor.prototype = { + // Import a piece of code into the editor. + importCode: function(code) { + this.history.push(null, null, asEditorLines(code)); + this.history.reset(); + }, + + // Extract the code from the editor. + getCode: function() { + if (!this.container.firstChild) + return ""; + + var accum = []; + select.markSelection(this.win); + forEach(traverseDOM(this.container.firstChild), method(accum, "push")); + webkitLastLineHack(this.container); + select.selectMarked(); + return cleanText(accum.join("")); + }, + + checkLine: function(node) { + if (node === false || !(node == null || node.parentNode == this.container)) + throw parent.CodeMirror.InvalidLineHandle; + }, + + cursorPosition: function(start) { + if (start == null) start = true; + var pos = select.cursorPos(this.container, start); + if (pos) return {line: pos.node, character: pos.offset}; + else return {line: null, character: 0}; + }, + + firstLine: function() { + return null; + }, + + lastLine: function() { + if (this.container.lastChild) return startOfLine(this.container.lastChild); + else return null; + }, + + nextLine: function(line) { + this.checkLine(line); + var end = endOfLine(line, this.container); + return end || false; + }, + + prevLine: function(line) { + this.checkLine(line); + if (line == null) return false; + return startOfLine(line.previousSibling); + }, + + selectLines: function(startLine, startOffset, endLine, endOffset) { + this.checkLine(startLine); + var start = {node: startLine, offset: startOffset}, end = null; + if (endOffset !== undefined) { + this.checkLine(endLine); + end = {node: endLine, offset: endOffset}; + } + select.setCursorPos(this.container, start, end); + select.scrollToCursor(this.container); + }, + + lineContent: function(line) { + this.checkLine(line); + var accum = []; + for (line = line ? line.nextSibling : this.container.firstChild; + line && line.nodeName != "BR"; line = line.nextSibling) + accum.push(nodeText(line)); + return cleanText(accum.join("")); + }, + + setLineContent: function(line, content) { + this.history.commit(); + this.replaceRange({node: line, offset: 0}, + {node: line, offset: this.history.textAfter(line).length}, + content); + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + insertIntoLine: function(line, position, content) { + var before = null; + if (position == "end") { + before = endOfLine(line, this.container); + } + else { + for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) { + if (position == 0) { + before = cur; + break; + } + var text = (cur.innerText || cur.textContent || cur.nodeValue || ""); + if (text.length > position) { + before = cur.nextSibling; + content = text.slice(0, position) + content + text.slice(position); + removeElement(cur); + break; + } + position -= text.length; + } + } + + var lines = asEditorLines(content), doc = this.container.ownerDocument; + for (var i = 0; i < lines.length; i++) { + if (i > 0) this.container.insertBefore(doc.createElement("BR"), before); + this.container.insertBefore(makePartSpan(lines[i], doc), before); + } + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + // Retrieve the selected text. + selectedText: function() { + var h = this.history; + h.commit(); + + var start = select.cursorPos(this.container, true), + end = select.cursorPos(this.container, false); + if (!start || !end) return ""; + + if (start.node == end.node) + return h.textAfter(start.node).slice(start.offset, end.offset); + + var text = [h.textAfter(start.node).slice(start.offset)]; + for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos)) + text.push(h.textAfter(pos)); + text.push(h.textAfter(end.node).slice(0, end.offset)); + return cleanText(text.join("\n")); + }, + + // Replace the selection with another piece of text. + replaceSelection: function(text) { + this.history.commit(); + var start = select.cursorPos(this.container, true), + end = select.cursorPos(this.container, false); + if (!start || !end) return; + + end = this.replaceRange(start, end, text); + select.setCursorPos(this.container, start, end); + }, + + replaceRange: function(from, to, text) { + var lines = asEditorLines(text); + lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0]; + var lastLine = lines[lines.length - 1]; + lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset); + var end = this.history.nodeAfter(to.node); + this.history.push(from.node, end, lines); + return {node: this.history.nodeBefore(end), + offset: lastLine.length}; + }, + + getSearchCursor: function(string, fromCursor) { + return new SearchCursor(this, string, fromCursor); + }, + + // Re-indent the whole buffer + reindent: function() { + if (this.container.firstChild) + this.indentRegion(null, this.container.lastChild); + }, + + reindentSelection: function(direction) { + if (!select.somethingSelected(this.win)) { + this.indentAtCursor(direction); + } + else { + var start = select.selectionTopNode(this.container, true), + end = select.selectionTopNode(this.container, false); + if (start === false || end === false) return; + this.indentRegion(start, end, direction); + } + }, + + grabKeys: function(eventHandler, filter) { + this.frozen = eventHandler; + this.keyFilter = filter; + }, + ungrabKeys: function() { + this.frozen = "leave"; + this.keyFilter = null; + }, + + setParser: function(name) { + Editor.Parser = window[name]; + if (this.container.firstChild) { + forEach(this.container.childNodes, function(n) { + if (n.nodeType != 3) n.dirty = true; + }); + this.addDirtyNode(this.firstChild); + this.scheduleHighlight(); + } + }, + + // Intercept enter and tab, and assign their new functions. + keyDown: function(event) { + if (this.frozen == "leave") this.frozen = null; + if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) { + event.stop(); + this.frozen(event); + return; + } + + var code = event.keyCode; + // Don't scan when the user is typing. + this.delayScanning(); + // Schedule a paren-highlight event, if configured. + if (this.options.autoMatchParens) + this.scheduleParenBlink(); + + // The variouschecks for !altKey are there because AltGr sets both + // ctrlKey and altKey to true, and should not be recognised as + // Control. + if (code == 13) { // enter + if (event.ctrlKey && !event.altKey) { + this.reparseBuffer(); + } + else { + select.insertNewlineAtCursor(this.win); + this.indentAtCursor(); + select.scrollToCursor(this.container); + } + event.stop(); + } + else if (code == 9 && this.options.tabMode != "default") { // tab + this.handleTab(!event.ctrlKey && !event.shiftKey); + event.stop(); + } + else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space + this.handleTab(true); + event.stop(); + } + else if (code == 36 && !event.shiftKey) { // home + if (this.home()) + event.stop(); + } + else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ] + this.blinkParens(event.shiftKey); + event.stop(); + } + else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right + var cursor = select.selectionTopNode(this.container); + if (cursor === false || !this.container.firstChild) return; + + if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container); + else { + var end = endOfLine(cursor, this.container); + select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container); + } + event.stop(); + } + else if ((event.ctrlKey || event.metaKey) && !event.altKey) { + if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y + select.scrollToNode(this.history.redo()); + event.stop(); + } + else if (code == 90 || code == 8) { // Z, backspace + select.scrollToNode(this.history.undo()); + event.stop(); + } + else if (code == 83 && this.options.saveFunction) { // S + this.options.saveFunction(); + event.stop(); + } + } + }, + + // Check for characters that should re-indent the current line, + // and prevent Opera from handling enter and tab anyway. + keyPress: function(event) { + var electric = /indent|default/.test(this.options.tabMode) && 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 ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) || + event.code == 13 || (event.code == 9 && this.options.tabMode != "default") || + (event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default")) + event.stop(); + else if (electric && electric.indexOf(event.character) != -1) + this.parent.setTimeout(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
, or null for the first + // line. If given a
element, this must have been highlighted + // so that it has an indentation method. Returns the whitespace + // element that has been modified or created (if any). + indentLineAfter: function(start, direction) { + // whiteSpace is the whitespace span at the start of the line, + // or null if there is no such node. + var whiteSpace = start ? start.nextSibling : this.container.firstChild; + if (whiteSpace && !hasClass(whiteSpace, "whitespace")) + whiteSpace = null; + + // Sometimes the start of the line can influence the correct + // indentation, so we retrieve it. + var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild); + var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : ""; + + // Ask the lexical context for the correct indentation, and + // compute how much this differs from the current indentation. + var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0; + if (direction != null && this.options.tabMode == "shift") + newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit) + else if (start) + newIndent = start.indentation(nextChars, curIndent, direction); + else if (Editor.Parser.firstIndentation) + newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction); + var indentDiff = newIndent - curIndent; + + // If there is too much, this is just a matter of shrinking a span. + if (indentDiff < 0) { + if (newIndent == 0) { + if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0); + removeElement(whiteSpace); + whiteSpace = null; + } + else { + select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true); + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + } + // Not enough... + else if (indentDiff > 0) { + // If there is whitespace, we grow it. + if (whiteSpace) { + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + // Otherwise, we have to add a new whitespace node. + else { + whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc); + whiteSpace.className = "whitespace"; + if (start) insertAfter(whiteSpace, start); + else this.container.insertBefore(whiteSpace, this.container.firstChild); + } + if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true); + } + if (indentDiff != 0) this.addDirtyNode(start); + return whiteSpace; + }, + + // Re-highlight the selected part of the document. + highlightAtCursor: function() { + var pos = select.selectionTopNode(this.container, true); + var to = select.selectionTopNode(this.container, false); + if (pos === false || to === false) return; + + select.markSelection(this.win); + if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false) + return false; + select.selectMarked(); + return true; + }, + + // When tab is pressed with text selected, the whole selection is + // re-indented, when nothing is selected, the line with the cursor + // is re-indented. + handleTab: function(direction) { + if (this.options.tabMode == "spaces") + select.insertTabAtCursor(this.win); + else + this.reindentSelection(direction); + }, + + home: function() { + var cur = select.selectionTopNode(this.container, true), start = cur; + if (cur === false || !(!cur || cur.isPart || cur.nodeName == "BR") || !this.container.firstChild) + return false; + + while (cur && cur.nodeName != "BR") cur = cur.previousSibling; + var next = cur ? cur.nextSibling : this.container.firstChild; + if (next && next != start && next.isPart && hasClass(next, "whitespace")) + select.focusAfterNode(next, this.container); + else + select.focusAfterNode(cur, this.container); + return true; + }, + + // Delay (or initiate) the next paren blink event. + scheduleParenBlink: function() { + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + var self = this; + this.parenEvent = this.parent.setTimeout(function(){self.blinkParens();}, 300); + }, + + // Take the token before the cursor. If it contains a character in + // '()[]{}', search for the matching paren/brace/bracket, and + // highlight them in green for a moment, or red if no proper match + // was found. + blinkParens: function(jump) { + if (!window.select) return; + // Clear the event property. + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + this.parenEvent = null; + + // Extract a 'paren' from a piece of text. + function paren(node) { + if (node.currentText) { + var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/); + return match && match[1]; + } + } + // Determine the direction a paren is facing. + function forward(ch) { + return /[\(\[\{]/.test(ch); + } + + var ch, self = this, cursor = select.selectionTopNode(this.container, true); + if (!cursor || !this.highlightAtCursor()) return; + cursor = select.selectionTopNode(this.container, true); + if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor))))) + return; + // We only look for tokens with the same className. + var className = cursor.className, dir = forward(ch), match = matching[ch]; + + // Since parts of the document might not have been properly + // highlighted, and it is hard to know in advance which part we + // have to scan, we just try, and when we find dirty nodes we + // abort, parse them, and re-try. + function tryFindMatch() { + var stack = [], ch, ok = true;; + for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) { + if (runner.className == className && runner.nodeName == "SPAN" && (ch = paren(runner))) { + if (forward(ch) == dir) + stack.push(ch); + else if (!stack.length) + ok = false; + else if (stack.pop() != matching[ch]) + ok = false; + if (!stack.length) break; + } + else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") { + return {node: runner, status: "dirty"}; + } + } + return {node: runner, status: runner && ok}; + } + // Temporarily give the relevant nodes a colour. + function blink(node, ok) { + node.style.fontWeight = "bold"; + node.style.color = ok ? "#8F8" : "#F88"; + self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500); + } + + while (true) { + var found = tryFindMatch(); + if (found.status == "dirty") { + this.highlight(found.node, endOfLine(found.node)); + // Needed because in some corner cases a highlight does not + // reach a node. + found.node.dirty = false; + continue; + } + else { + blink(cursor, found.status); + if (found.node) { + blink(found.node, found.status); + if (jump) select.focusAfterNode(found.node.previousSibling, this.container); + } + break; + } + } + }, + + // Adjust the amount of whitespace at the start of the line that + // the cursor is on so that it is indented properly. + indentAtCursor: function(direction) { + if (!this.container.firstChild) return; + // The line has to have up-to-date lexical information, so we + // highlight it first. + if (!this.highlightAtCursor()) return; + var cursor = select.selectionTopNode(this.container, false); + // If we couldn't determine the place of the cursor, + // there's nothing to indent. + if (cursor === false) + return; + var lineStart = startOfLine(cursor); + var whiteSpace = this.indentLineAfter(lineStart, direction); + if (cursor == lineStart && whiteSpace) + cursor = whiteSpace; + // This means the indentation has probably messed up the cursor. + if (cursor == whiteSpace) + select.focusAfterNode(cursor, this.container); + }, + + // Indent all lines whose start falls inside of the current + // selection. + indentRegion: function(start, end, direction) { + var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling); + if (end.nodeName != "BR") end = endOfLine(end, this.container); + + do { + var next = endOfLine(current, this.container); + if (current) this.highlight(before, next, true); + this.indentLineAfter(current, direction); + before = current; + current = next; + } while (current != end); + select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0}); + }, + + // Find the node that the cursor is in, mark it as dirty, and make + // sure a highlight pass is scheduled. + cursorActivity: function(safe) { + if (internetExplorer) { + this.container.createTextRange().execCommand("unlink"); + this.selectionSnapshot = select.selectionCoords(this.win); + } + + var activity = this.options.cursorActivity; + if (!safe || activity) { + var cursor = select.selectionTopNode(this.container, false); + if (cursor === false || !this.container.firstChild) return; + cursor = cursor || this.container.firstChild; + if (activity) activity(cursor); + if (!safe) { + this.scheduleHighlight(); + this.addDirtyNode(cursor); + } + } + }, + + reparseBuffer: function() { + forEach(this.container.childNodes, function(node) {node.dirty = true;}); + if (this.container.firstChild) + this.addDirtyNode(this.container.firstChild); + }, + + // Add a node to the set of dirty nodes, if it isn't already in + // there. + addDirtyNode: function(node) { + node = node || this.container.firstChild; + if (!node) return; + + for (var i = 0; i < this.dirty.length; i++) + if (this.dirty[i] == node) return; + + if (node.nodeType != 3) + node.dirty = true; + this.dirty.push(node); + }, + + // Cause a highlight pass to happen in options.passDelay + // milliseconds. Clear the existing timeout, if one exists. This + // way, the passes do not happen while the user is typing, and + // should as unobtrusive as possible. + scheduleHighlight: function() { + // Timeouts are routed through the parent window, because on + // some browsers designMode windows do not fire timeouts. + var self = this; + this.parent.clearTimeout(this.highlightTimeout); + this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay); + }, + + // Fetch one dirty node, and remove it from the dirty set. + getDirtyNode: function() { + while (this.dirty.length > 0) { + var found = this.dirty.pop(); + // IE8 sometimes throws an unexplainable 'invalid argument' + // exception for found.parentNode + try { + // If the node has been coloured in the meantime, or is no + // longer in the document, it should not be returned. + while (found && found.parentNode != this.container) + found = found.parentNode + if (found && (found.dirty || found.nodeType == 3)) + return found; + } catch (e) {} + } + return null; + }, + + // Pick dirty nodes, and highlight them, until options.passTime + // milliseconds have gone by. The highlight method will continue + // to next lines as long as it finds dirty nodes. It returns + // information about the place where it stopped. If there are + // dirty nodes left after this function has spent all its lines, + // it shedules another highlight to finish the job. + highlightDirty: function(force) { + // Prevent FF from raising an error when it is firing timeouts + // on a page that's no longer loaded. + if (!window.select) return; + + if (!this.options.readOnly) select.markSelection(this.win); + var start, endTime = force ? null : time() + this.options.passTime; + while (time() < endTime && (start = this.getDirtyNode())) { + var result = this.highlight(start, endTime); + if (result && result.node && result.dirty) + this.addDirtyNode(result.node); + } + if (!this.options.readOnly) select.selectMarked(); + if (start) this.scheduleHighlight(); + return this.dirty.length == 0; + }, + + // Creates a function that, when called through a timeout, will + // continuously re-parse the document. + documentScanner: function(passTime) { + var self = this, pos = null; + return function() { + // FF timeout weirdness workaround. + if (!window.select) return; + // If the current node is no longer in the document... oh + // well, we start over. + if (pos && pos.parentNode != self.container) + pos = null; + select.markSelection(self.win); + var result = self.highlight(pos, time() + passTime, true); + select.selectMarked(); + var newPos = result ? (result.node && result.node.nextSibling) : null; + pos = (pos == newPos) ? null : newPos; + self.delayScanning(); + }; + }, + + // Starts the continuous scanning process for this document after + // a given interval. + delayScanning: function() { + if (this.scanner) { + this.parent.clearTimeout(this.documentScan); + this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning); + } + }, + + // The function that does the actual highlighting/colouring (with + // help from the parser and the DOM normalizer). Its interface is + // rather overcomplicated, because it is used in different + // situations: ensuring that a certain line is highlighted, or + // highlighting up to X milliseconds starting from a certain + // point. The 'from' argument gives the node at which it should + // start. If this is null, it will start at the beginning of the + // document. When a timestamp is given with the 'target' argument, + // it will stop highlighting at that time. If this argument holds + // a DOM node, it will highlight until it reaches that node. If at + // any time it comes across two 'clean' lines (no dirty nodes), it + // will stop, except when 'cleanLines' is true. maxBacktrack is + // the maximum number of lines to backtrack to find an existing + // parser instance. This is used to give up in situations where a + // highlight would take too long and freeze the browser interface. + highlight: function(from, target, cleanLines, maxBacktrack){ + var container = this.container, self = this, active = this.options.activeTokens; + var endTime = (typeof target == "number" ? target : null); + + if (!container.firstChild) + return; + // Backtrack to the first node before from that has a partial + // parse stored. + while (from && (!from.parserFromHere || from.dirty)) { + if (maxBacktrack != null && from.nodeName == "BR" && (--maxBacktrack) < 0) + return false; + from = from.previousSibling; + } + // If we are at the end of the document, do nothing. + if (from && !from.nextSibling) + return; + + // Check whether a part ( node) and the corresponding token + // match. + function correctPart(token, part){ + return !part.reduced && part.currentText == token.value && part.className == token.style; + } + // Shorten the text associated with a part by chopping off + // characters from the front. Note that only the currentText + // property gets changed. For efficiency reasons, we leave the + // nodeValue alone -- we set the reduced flag to indicate that + // this part must be replaced. + function shortenPart(part, minus){ + part.currentText = part.currentText.substring(minus); + part.reduced = true; + } + // Create a part corresponding to a given token. + function tokenPart(token){ + var part = makePartSpan(token.value, self.doc); + part.className = token.style; + return part; + } + + function maybeTouch(node) { + if (node) { + if (lineDirty || node.nextSibling != node.oldNextSibling) + self.history.touch(node); + node.oldNextSibling = node.nextSibling; + } + else { + if (lineDirty || self.container.firstChild != self.container.oldFirstChild) + self.history.touch(node); + self.container.oldFirstChild = self.container.firstChild; + } + } + + // Get the token stream. If from is null, we start with a new + // parser from the start of the frame, otherwise a partial parse + // is resumed. + var traversal = traverseDOM(from ? from.nextSibling : container.firstChild), + stream = stringStream(traversal), + parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream); + + // parts is an interface to make it possible to 'delay' fetching + // the next DOM node until we are completely done with the one + // before it. This is necessary because often the next node is + // not yet available when we want to proceed past the current + // one. + var parts = { + current: null, + // Fetch current node. + get: function(){ + if (!this.current) + this.current = traversal.nodes.shift(); + return this.current; + }, + // Advance to the next part (do not fetch it yet). + next: function(){ + this.current = null; + }, + // Remove the current part from the DOM tree, and move to the + // next. + remove: function(){ + container.removeChild(this.get()); + this.current = null; + }, + // Advance to the next part that is not empty, discarding empty + // parts. + getNonEmpty: function(){ + var part = this.get(); + // Allow empty nodes when they are alone on a line, needed + // for the FF cursor bug workaround (see select.js, + // insertNewlineAtCursor). + while (part && part.nodeName == "SPAN" && part.currentText == "") { + var old = part; + this.remove(); + part = this.get(); + // Adjust selection information, if any. See select.js for details. + select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0); + } + return part; + } + }; + + var lineDirty = false, prevLineDirty = true, lineNodes = 0; + + // This forEach loops over the tokens from the parsed stream, and + // at the same time uses the parts object to proceed through the + // corresponding DOM nodes. + forEach(parsed, function(token){ + var part = parts.getNonEmpty(); + + if (token.value == "\n"){ + // The idea of the two streams actually staying synchronized + // is such a long shot that we explicitly check. + if (part.nodeName != "BR") + throw "Parser out of sync. Expected BR."; + + if (part.dirty || !part.indentation) lineDirty = true; + maybeTouch(from); + from = part; + + // Every
gets a copy of the parser state and a lexical + // context assigned to it. The first is used to be able to + // later resume parsing from this point, the second is used + // for indentation. + part.parserFromHere = parsed.copy(); + part.indentation = token.indentation; + part.dirty = false; + + // If the target argument wasn't an integer, go at least + // until that node. + if (endTime == null && part == target) throw StopIteration; + + // A clean line with more than one node means we are done. + // Throwing a StopIteration is the way to break out of a + // MochiKit forEach loop. + if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines)) + throw StopIteration; + prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0; + parts.next(); + } + else { + if (part.nodeName != "SPAN") + throw "Parser out of sync. Expected SPAN."; + if (part.dirty) + lineDirty = true; + lineNodes++; + + // If the part matches the token, we can leave it alone. + if (correctPart(token, part)){ + part.dirty = false; + parts.next(); + } + // Otherwise, we have to fix it. + else { + lineDirty = true; + // Insert the correct part. + var newPart = tokenPart(token); + container.insertBefore(newPart, part); + if (active) active(newPart, token, self); + var tokensize = token.value.length; + var offset = 0; + // Eat up parts until the text for this token has been + // removed, adjusting the stored selection info (see + // select.js) in the process. + while (tokensize > 0) { + part = parts.get(); + var partsize = part.currentText.length; + select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset); + if (partsize > tokensize){ + shortenPart(part, tokensize); + tokensize = 0; + } + else { + tokensize -= partsize; + offset += partsize; + parts.remove(); + } + } + } + } + }); + maybeTouch(from); + webkitLastLineHack(this.container); + + // The function returns some status information that is used by + // hightlightDirty to determine whether and where it has to + // continue. + return {node: parts.getNonEmpty(), + dirty: lineDirty}; + } + }; + + return Editor; +})(); + +addEventHandler(window, "load", function() { + var CodeMirror = window.frameElement.CodeMirror; + CodeMirror.editor = new Editor(CodeMirror.options); + this.parent.setTimeout(method(CodeMirror, "init"), 0); +}); diff --git a/media/CodeMirror-0.62/js/highlight.js b/media/CodeMirror-0.62/js/highlight.js new file mode 100644 index 0000000..f0de59c --- /dev/null +++ b/media/CodeMirror-0.62/js/highlight.js @@ -0,0 +1,68 @@ +// Minimal framing needed to use CodeMirror-style parsers to highlight +// code. Load this along with tokenize.js, stringstream.js, and your +// parser. Then call highlightText, passing a string as the first +// argument, and as the second argument either a callback function +// that will be called with an array of SPAN nodes for every line in +// the code, or a DOM node to which to append these spans, and +// optionally (not needed if you only loaded one parser) a parser +// object. + +// Stuff from util.js that the parsers are using. +var StopIteration = {toString: function() {return "StopIteration"}}; + +var Editor = {}; +var indentUnit = 2; + +(function(){ + function normaliseString(string) { + var tab = ""; + for (var i = 0; i < indentUnit; i++) tab += " "; + + string = string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n"); + var pos = 0, parts = [], lines = string.split("\n"); + for (var line = 0; line < lines.length; line++) { + if (line != 0) parts.push("\n"); + parts.push(lines[line]); + } + + return { + next: function() { + if (pos < parts.length) return parts[pos++]; + else throw StopIteration; + } + }; + } + + window.highlightText = function(string, callback, parser) { + var parser = (parser || Editor.Parser).make(stringStream(normaliseString(string))); + var line = []; + if (callback.nodeType == 1) { + var node = callback; + callback = function(line) { + for (var i = 0; i < line.length; i++) + node.appendChild(line[i]); + node.appendChild(document.createElement("BR")); + }; + } + + try { + while (true) { + var token = parser.next(); + if (token.value == "\n") { + callback(line); + line = []; + } + else { + var span = document.createElement("SPAN"); + span.className = token.style; + span.appendChild(document.createTextNode(token.value)); + line.push(span); + } + } + } + catch (e) { + if (e != StopIteration) throw e; + } + if (line.length) callback(line); + } +})(); diff --git a/media/CodeMirror-0.62/js/mirrorframe.js b/media/CodeMirror-0.62/js/mirrorframe.js new file mode 100644 index 0000000..7f6ad1a --- /dev/null +++ b/media/CodeMirror-0.62/js/mirrorframe.js @@ -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(); + } +}; diff --git a/media/CodeMirror-0.62/js/parsecss.js b/media/CodeMirror-0.62/js/parsecss.js new file mode 100644 index 0000000..4f90d59 --- /dev/null +++ b/media/CodeMirror-0.62/js/parsecss.js @@ -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.nextWhileMatches(/\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.nextWhileMatches(/\w/); + return "css-hash"; + } + else if (ch == "!") { + source.nextWhileMatches(/[ \t]/); + source.nextWhileMatches(/\w/); + return "css-important"; + } + else if (/\d/.test(ch)) { + source.nextWhileMatches(/[\w.%]/); + return "css-unit"; + } + else if (/[,.+>*\/]/.test(ch)) { + return "css-select-op"; + } + else if (/[;{}:\[\]]/.test(ch)) { + return "css-punctuation"; + } + else { + source.nextWhileMatches(/[\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 + indentUnit * 2; + else return base + indentUnit; + }; + } + + // 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: "}"}; +})(); diff --git a/media/CodeMirror-0.62/js/parsedummy.js b/media/CodeMirror-0.62/js/parsedummy.js new file mode 100644 index 0000000..9e63caa --- /dev/null +++ b/media/CodeMirror-0.62/js/parsedummy.js @@ -0,0 +1,32 @@ +var DummyParser = Editor.Parser = (function() { + function tokenizeDummy(source) { + while (!source.endOfLine()) source.next(); + return "text"; + } + function parseDummy(source) { + function indentTo(n) {return function() {return n;}} + source = tokenizer(source, tokenizeDummy); + var space = 0; + + var iter = { + next: function() { + var tok = source.next(); + if (tok.type == "whitespace") { + if (tok.value == "\n") tok.indentation = indentTo(space); + else space = tok.value.length; + } + return tok; + }, + copy: function() { + var _space = space; + return function(_source) { + space = _space; + source = tokenizer(_source, tokenizeDummy); + return iter; + }; + } + }; + return iter; + } + return {make: parseDummy}; +})(); diff --git a/media/CodeMirror-0.62/js/parsehtmlmixed.js b/media/CodeMirror-0.62/js/parsehtmlmixed.js new file mode 100644 index 0000000..ed1a608 --- /dev/null +++ b/media/CodeMirror-0.62/js/parsehtmlmixed.js @@ -0,0 +1,74 @@ +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, "= 0; i--) + cc.push(fs[i]); + } + // cont and pass are used by the action functions to add other + // actions to the stack. cont will cause the current token to be + // consumed, pass will leave it for the next action. + function cont(){ + push(arguments); + consume = true; + } + function pass(){ + push(arguments); + consume = false; + } + // Used to change the style of the current token. + function mark(style){ + marked = style; + } + + // Push a new scope. Will automatically link the current scope. + function pushcontext(){ + context = {prev: context, vars: {"this": true, "arguments": true}}; + } + // Pop off the current scope. + function popcontext(){ + context = context.prev; + } + // Register a variable in the current scope. + function register(varname){ + if (context){ + mark("js-variabledef"); + context.vars[varname] = true; + } + } + // Check whether a variable is defined in the current scope. + function inScope(varname){ + var cursor = context; + while (cursor) { + if (cursor.vars[varname]) + return true; + cursor = cursor.prev; + } + return false; + } + + // Push a new lexical context of the given type. + function pushlex(type, info) { + var result = function(){ + lexical = new JSLexical(indented, column, type, null, lexical, info) + }; + result.lex = true; + return result; + } + // Pop off the current lexical context. + function poplex(){ + lexical = lexical.prev; + } + poplex.lex = true; + // The 'lex' flag on these actions is used by the 'next' function + // to know they can (and have to) be ran before moving on to the + // next token. + + // Creates an action that discards tokens until it finds one of + // the given type. + function expect(wanted){ + return function expecting(type){ + if (type == wanted) cont(); + else cont(arguments.callee); + }; + } + + // Looks for a statement, and then calls itself. + function statements(type){ + return pass(statement, statements); + } + // Dispatches various types of statements based on the type of the + // current token. + function statement(type){ + if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex); + else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex); + else if (type == "keyword b") cont(pushlex("form"), statement, poplex); + else if (type == "{") cont(pushlex("}"), block, poplex); + else if (type == "function") cont(functiondef); + else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex); + else if (type == "variable") cont(pushlex("stat"), maybelabel); + else if (type == "switch") cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), block, poplex, poplex); + else if (type == "case") cont(expression, expect(":")); + else if (type == "default") cont(expect(":")); + else if (type == "catch") cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext); + else pass(pushlex("stat"), expression, expect(";"), poplex); + } + // Dispatch expression types. + function expression(type){ + if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator); + else if (type == "function") cont(functiondef); + else if (type == "keyword c") cont(expression); + else if (type == "(") cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator); + else if (type == "operator") cont(expression); + else if (type == "[") cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); + else if (type == "{") cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); + } + // Called for places where operators, function calls, or + // subscripts are valid. Will skip on to the next action if none + // is found. + function maybeoperator(type){ + if (type == "operator") cont(expression); + else if (type == "(") cont(pushlex(")"), expression, commasep(expression, ")"), poplex, maybeoperator); + else if (type == ".") cont(property, maybeoperator); + else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); + } + // When a statement starts with a variable name, it might be a + // label. If no colon follows, it's a regular statement. + function maybelabel(type){ + if (type == ":") cont(poplex, statement); + else pass(maybeoperator, expect(";"), poplex); + } + // Property names need to have their style adjusted -- the + // tokenizer thinks they are variables. + function property(type){ + if (type == "variable") {mark("js-property"); cont();} + } + // This parses a property and its value in an object literal. + function objprop(type){ + if (type == "variable") mark("js-property"); + if (atomicTypes.hasOwnProperty(type)) cont(expect(":"), expression); + } + // Parses a comma-separated list of the things that are recognized + // by the 'what' argument. + function commasep(what, end){ + function proceed(type) { + if (type == ",") cont(what, proceed); + else if (type == end) cont(); + else cont(expect(end)); + }; + return function commaSeparated(type) { + if (type == end) cont(); + else pass(what, proceed); + }; + } + // Look for statements until a closing brace is found. + function block(type){ + if (type == "}") cont(); + else pass(statement, block); + } + // Variable definitions are split into two actions -- 1 looks for + // a name or the end of the definition, 2 looks for an '=' sign or + // a comma. + function vardef1(type, value){ + if (type == "variable"){register(value); cont(vardef2);} + else cont(); + } + function vardef2(type, value){ + if (value == "=") cont(expression, vardef2); + else if (type == ",") cont(vardef1); + } + // For loops. + function forspec1(type){ + if (type == "var") cont(vardef1, forspec2); + else if (type == ";") pass(forspec2); + else if (type == "variable") cont(formaybein); + else pass(forspec2); + } + function formaybein(type, value){ + if (value == "in") cont(expression); + else cont(maybeoperator, forspec2); + } + function forspec2(type, value){ + if (type == ";") cont(forspec3); + else if (value == "in") cont(expression); + else cont(expression, expect(";"), forspec3); + } + function forspec3(type) { + if (type == ")") pass(); + else cont(expression); + } + // A function definition creates a new context, and the variables + // in its argument list have to be added to this context. + function functiondef(type, value){ + if (type == "variable"){register(value); cont(functiondef);} + else if (type == "(") cont(pushcontext, commasep(funarg, ")"), statement, popcontext); + } + function funarg(type, value){ + if (type == "variable"){register(value); cont();} + } + + return parser; + } + + return {make: parseJS, electricChars: "{}:"}; +})(); diff --git a/media/CodeMirror-0.62/js/parsesparql.js b/media/CodeMirror-0.62/js/parsesparql.js new file mode 100644 index 0000000..4b1dcaf --- /dev/null +++ b/media/CodeMirror-0.62/js/parsesparql.js @@ -0,0 +1,162 @@ +var SparqlParser = Editor.Parser = (function() { + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$", "i"); + } + var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri", + "isblank", "isliteral", "union", "a"]); + var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe", + "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional", + "graph", "by", "asc", "desc", ]); + var operatorChars = /[*+\-<>=&|]/; + + var tokenizeSparql = (function() { + function normal(source, setState) { + var ch = source.next(); + if (ch == "$" || ch == "?") { + source.nextWhileMatches(/[\w\d]/); + return "sp-var"; + } + else if (ch == "<" && !source.matches(/[\s\u00a0=]/)) { + source.nextWhileMatches(/[^\s\u00a0>]/); + if (source.equals(">")) source.next(); + return "sp-uri"; + } + else if (ch == "\"" || ch == "'") { + setState(inLiteral(ch)); + return null; + } + else if (/[{}\(\),\.;\[\]]/.test(ch)) { + return "sp-punc"; + } + else if (ch == "#") { + while (!source.endOfLine()) source.next(); + return "sp-comment"; + } + else if (operatorChars.test(ch)) { + source.nextWhileMatches(operatorChars); + return "sp-operator"; + } + else if (ch == ":") { + source.nextWhileMatches(/[\w\d\._\-]/); + return "sp-prefixed"; + } + else { + source.nextWhileMatches(/[_\w\d]/); + if (source.equals(":")) { + source.next(); + source.nextWhileMatches(/[\w\d_\-]/); + return "sp-prefixed"; + } + var word = source.get(), type; + if (ops.test(word)) + type = "sp-operator"; + else if (keywords.test(word)) + type = "sp-keyword"; + else + type = "sp-word"; + return {style: type, content: word}; + } + } + + function inLiteral(quote) { + return function(source, setState) { + var escaped = false; + while (!source.endOfLine()) { + var ch = source.next(); + if (ch == quote && !escaped) { + setState(normal); + break; + } + escaped = !escaped && ch == "\\"; + } + return "sp-literal"; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); + + function indentSparql(context) { + return function(nextChars) { + var firstChar = nextChars && nextChars.charAt(0); + if (/[\]\}]/.test(firstChar)) + while (context && context.type == "pattern") context = context.prev; + + var closing = context && firstChar == matching[context.type]; + if (!context) + return 0; + else if (context.type == "pattern") + return context.col; + else if (context.align) + return context.col - (closing ? context.width : 0); + else + return context.indent + (closing ? 0 : indentUnit); + } + } + + function parseSparql(source) { + var tokens = tokenizeSparql(source); + var context = null, indent = 0, col = 0; + function pushContext(type, width) { + context = {prev: context, indent: indent, col: col, type: type, width: width}; + } + function popContext() { + context = context.prev; + } + + var iter = { + next: function() { + var token = tokens.next(), type = token.style, content = token.content, width = token.value.length; + + if (content == "\n") { + token.indentation = indentSparql(context); + indent = col = 0; + if (context && context.align == null) context.align = false; + } + else if (type == "whitespace" && col == 0) { + indent = width; + } + else if (type != "sp-comment" && context && context.align == null) { + context.align = true; + } + + if (content != "\n") col += width; + + if (/[\[\{\(]/.test(content)) { + pushContext(content, width); + } + else if (/[\]\}\)]/.test(content)) { + while (context && context.type == "pattern") + popContext(); + if (context && content == matching[context.type]) + popContext(); + } + else if (content == "." && context && context.type == "pattern") { + popContext(); + } + else if ((type == "sp-word" || type == "sp-prefixed" || type == "sp-uri" || type == "sp-var" || type == "sp-literal") && + context && /[\{\[]/.test(context.type)) { + pushContext("pattern", width); + } + + return token; + }, + + copy: function() { + var _context = context, _indent = indent, _col = col, _tokenState = tokens.state; + return function(source) { + tokens = tokenizeSparql(source, _tokenState); + context = _context; + indent = _indent; + col = _col; + return iter; + }; + } + }; + return iter; + } + + return {make: parseSparql, electricChars: "}]"}; +})(); diff --git a/media/CodeMirror-0.62/js/parsesurvex.js b/media/CodeMirror-0.62/js/parsesurvex.js new file mode 100644 index 0000000..941b347 --- /dev/null +++ b/media/CodeMirror-0.62/js/parsesurvex.js @@ -0,0 +1,107 @@ +/* Simple parser for Survex files (based on the CSS example) */ + +// The tokenizer breaks up the text into convincing chunks (I think the white-space is parser automatically) +var SVXParser = Editor.Parser = (function() { + var tokenizeSVX = (function() { + function normal(source, setState) { + var ch = source.next(); + + if (ch == ";") { + source.nextWhile(matcher(/[^\n]/)); + return "svx-comment"; + } + else if (ch == "*") { + source.nextWhile(matcher(/\w/)); + return "svx-command"; + } + else if (ch == "\"" || ch == "'") { + var escaped = false; + while (!source.endOfLine()) { + var nch = source.next(); + if (nch == ch && !escaped) + break; + escaped = !escaped && nch == "\\"; + } + return "svx-string"; + } + else if (/[\d\-+.]/.test(ch)) { + source.nextWhile(matcher(/[\d.]/)); + return "svx-measure"; + } + else { + source.nextWhile(matcher(/\S/)); + return "svx-word"; + } + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); + + // survex doesn't have indentation; but you get double linefeeds if you leave this out. + function indentSVX() { + return function(nextChars) { + return 0; + }; + } + + // Then this simple parser fixes up the obvious errors made by the tokenizer (which could only operate on characters) + // A very fancy upgrade could make it capable of handling the *data commands which make it accept different orderings of + // the parameters -- though this may be a challenge because the whole file needs reparsing when that happens -- don't + // know how optimized the basic code is to be able to call for such to happen when a formatting command like this changes. + function parseSVX(source, basecolumn) { + basecolumn = basecolumn || 0; + var tokens = tokenizeSVX(source); + var inCommand = false; + var ntokeninline = -1; + + var iter = { + next: function() { + var token = tokens.next(), style = token.style, content = token.content; + + if (content == "\n") { + ntokeninline = -1; + inCommand = false; + token.indentation = indentSVX(); + } + else if (style != "whitespace") + ntokeninline += 1; + + if (style == "svx-command") { + inCommand = (ntokeninline == 0); + if (!inCommand) + token.style = "svx-word"; + else if (content == "*begin") + token.style = "svx-begin"; + else if (content == "*end") + token.style = "svx-end"; + } + + if (!inCommand && style == "svx-measure") { + if (ntokeninline < 2) + token.style = "svx-word"; + } + if (!inCommand && style == "svx-word" && (ntokeninline == 4)) { + if (content == "down" || content == "up") + token.style = "svx-measure"; + } + + return token; + }, + + copy: function() { + var _inCommand = inCommand, _tokenState = tokens.state, _ntokeninline = ntokeninline; + return function(source) { + tokens = tokenizeSVX(source, _tokenState); + inCommand = _inCommand; + ntokeninline = _ntokeninline; + return iter; + }; + } + }; + return iter; + } + + return {make: parseSVX}; +})(); diff --git a/media/CodeMirror-0.62/js/parsexml.js b/media/CodeMirror-0.62/js/parsexml.js new file mode 100644 index 0000000..95a8099 --- /dev/null +++ b/media/CodeMirror-0.62/js/parsexml.js @@ -0,0 +1,292 @@ +/* This file defines an XML parser, with a few kludges to make it + * useable for HTML. autoSelfClosers defines a set of tag names that + * are expected to not have a closing tag, and doNotIndent specifies + * the tags inside of which no indentation should happen (see Config + * object). These can be disabled by passing the editor an object like + * {useHTMLKludges: false} as parserConfig option. + */ + +var XMLParser = Editor.Parser = (function() { + var Kludges = { + autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true, + "meta": true, "col": true, "frame": true, "base": true, "area": true}, + doNotIndent: {"pre": true, "!cdata": true} + }; + var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}}; + var UseKludges = Kludges; + var alignCDATA = false; + + // Simple stateful tokenizer for XML documents. Returns a + // MochiKit-style iterator, with a state property that contains a + // function encapsulating the current state. See tokenize.js. + var tokenizeXML = (function() { + function inText(source, setState) { + var ch = source.next(); + if (ch == "<") { + if (source.equals("!")) { + source.next(); + if (source.equals("[")) { + if (source.lookAhead("[CDATA[", true)) { + setState(inBlock("xml-cdata", "]]>")); + return null; + } + else { + return "xml-text"; + } + } + else if (source.lookAhead("--", true)) { + setState(inBlock("xml-comment", "-->")); + return null; + } + else { + return "xml-text"; + } + } + else if (source.equals("?")) { + source.next(); + source.nextWhileMatches(/[\w\._\-]/); + setState(inBlock("xml-processing", "?>")); + return "xml-processing"; + } + else { + if (source.equals("/")) source.next(); + setState(inTag); + return "xml-punctuation"; + } + } + else if (ch == "&") { + while (!source.endOfLine()) { + if (source.next() == ";") + break; + } + return "xml-entity"; + } + else { + source.nextWhileMatches(/[^&<\n]/); + return "xml-text"; + } + } + + function inTag(source, setState) { + var ch = source.next(); + if (ch == ">") { + setState(inText); + return "xml-punctuation"; + } + else if (/[?\/]/.test(ch) && source.equals(">")) { + source.next(); + setState(inText); + return "xml-punctuation"; + } + else if (ch == "=") { + return "xml-punctuation"; + } + else if (/[\'\"]/.test(ch)) { + setState(inAttribute(ch)); + return null; + } + else { + source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/); + return "xml-name"; + } + } + + function inAttribute(quote) { + return function(source, setState) { + while (!source.endOfLine()) { + if (source.next() == quote) { + setState(inTag); + break; + } + } + return "xml-attribute"; + }; + } + + function inBlock(style, terminator) { + return function(source, setState) { + while (!source.endOfLine()) { + if (source.lookAhead(terminator, true)) { + setState(inText); + break; + } + source.next(); + } + return style; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || inText); + }; + })(); + + // The parser. The structure of this function largely follows that of + // parseJavaScript in parsejavascript.js (there is actually a bit more + // shared code than I'd like), but it is quite a bit simpler. + function parseXML(source) { + var tokens = tokenizeXML(source); + var cc = [base]; + var tokenNr = 0, indented = 0; + var currentTag = null, context = null; + var consume, marked; + + function push(fs) { + for (var i = fs.length - 1; i >= 0; i--) + cc.push(fs[i]); + } + function cont() { + push(arguments); + consume = true; + } + function pass() { + push(arguments); + consume = false; + } + + function mark(style) { + marked = style; + } + function expect(text) { + return function(style, content) { + if (content == text) cont(); + else mark("xml-error") || cont(arguments.callee); + }; + } + + function pushContext(tagname, startOfLine) { + var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent); + context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent}; + } + function popContext() { + context = context.prev; + } + function computeIndentation(baseContext) { + return function(nextChars, current) { + var context = baseContext; + if (context && context.noIndent) + return current; + if (alignCDATA && /")); + else if (style == "xml-cdata") { + if (!context || context.name != "!cdata") pushContext("!cdata"); + if (/\]\]>$/.test(content)) popContext(); + cont(); + } + else if (harmlessTokens.hasOwnProperty(style)) cont(); + else mark("xml-error") || cont(); + } + function tagname(style, content) { + if (style == "xml-name") { + currentTag = content.toLowerCase(); + mark("xml-tagname"); + cont(); + } + else { + currentTag = null; + pass(); + } + } + function closetagname(style, content) { + if (style == "xml-name" && context && content.toLowerCase() == context.name) { + popContext(); + mark("xml-tagname"); + } + else { + mark("xml-error"); + } + cont(); + } + function endtag(startOfLine) { + return function(style, content) { + if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont(); + else if (content == ">") pushContext(currentTag, startOfLine) || cont(); + else mark("xml-error") || cont(arguments.callee); + }; + } + function attributes(style) { + if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes); + else pass(); + } + function attribute(style, content) { + if (content == "=") cont(value); + else if (content == ">" || content == "/>") pass(endtag); + else pass(); + } + function value(style) { + if (style == "xml-attribute") cont(value); + else pass(); + } + + return { + indentation: function() {return indented;}, + + next: function(){ + var token = tokens.next(); + if (token.style == "whitespace" && tokenNr == 0) + indented = token.value.length; + else + tokenNr++; + if (token.content == "\n") { + indented = tokenNr = 0; + token.indentation = computeIndentation(context); + } + + if (token.style == "whitespace" || token.type == "xml-comment") + return token; + + while(true){ + consume = marked = false; + cc.pop()(token.style, token.content); + if (consume){ + if (marked) + token.style = marked; + return token; + } + } + }, + + copy: function(){ + var _cc = cc.concat([]), _tokenState = tokens.state, _context = context; + var parser = this; + + return function(input){ + cc = _cc.concat([]); + tokenNr = indented = 0; + context = _context; + tokens = tokenizeXML(input, _tokenState); + return parser; + }; + } + }; + } + + return { + make: parseXML, + electricChars: "/", + configure: function(config) { + if (config.useHTMLKludges != null) + UseKludges = config.useHTMLKludges ? Kludges : NoKludges; + if (config.alignCDATA) + alignCDATA = config.alignCDATA; + } + }; +})(); diff --git a/media/CodeMirror-0.62/js/select.js b/media/CodeMirror-0.62/js/select.js new file mode 100644 index 0000000..4a11c54 --- /dev/null +++ b/media/CodeMirror-0.62/js/select.js @@ -0,0 +1,583 @@ +/* Functionality for finding, storing, and restoring selections + * + * This does not provide a generic API, just the minimal functionality + * required by the CodeMirror system. + */ + +// Namespace object. +var select = {}; + +(function() { + select.ie_selection = document.selection && document.selection.createRangeCollection; + + // Find the 'top-level' (defined as 'a direct child of the node + // passed as the top argument') node that the given node is + // contained in. Return null if the given node is not inside the top + // node. + function topLevelNodeAt(node, top) { + while (node && node.parentNode != top) + node = node.parentNode; + return node; + } + + // Find the top-level node that contains the node before this one. + function topLevelNodeBefore(node, top) { + while (!node.previousSibling && node.parentNode != top) + node = node.parentNode; + return topLevelNodeAt(node.previousSibling, top); + } + + // Used to prevent restoring a selection when we do not need to. + var currentSelection = null; + + var fourSpaces = "\u00a0\u00a0\u00a0\u00a0"; + + select.snapshotChanged = function() { + if (currentSelection) currentSelection.changed = true; + }; + + // This is called by the code in editor.js whenever it is replacing + // a text node. The function sees whether the given oldNode is part + // of the current selection, and updates this selection if it is. + // Because nodes are often only partially replaced, the length of + // the part that gets replaced has to be taken into account -- the + // selection might stay in the oldNode if the newNode is smaller + // than the selection's offset. The offset argument is needed in + // case the selection does move to the new object, and the given + // length is not the whole length of the new node (part of it might + // have been used to replace another node). + select.snapshotReplaceNode = function(from, to, length, offset) { + if (!currentSelection) return; + currentSelection.changed = true; + + function replace(point) { + if (from == point.node) { + if (length && point.offset > length) { + point.offset -= length; + } + else { + point.node = to; + point.offset += (offset || 0); + } + } + } + replace(currentSelection.start); + replace(currentSelection.end); + }; + + select.snapshotMove = function(from, to, distance, relative, ifAtStart) { + if (!currentSelection) return; + currentSelection.changed = true; + + function move(point) { + if (from == point.node && (!ifAtStart || point.offset == 0)) { + point.node = to; + if (relative) point.offset = Math.max(0, point.offset + distance); + else point.offset = distance; + } + } + move(currentSelection.start); + move(currentSelection.end); + }; + + // Most functions are defined in two ways, one for the IE selection + // model, one for the W3C one. + if (select.ie_selection) { + function selectionNode(win, start) { + var range = win.document.selection.createRange(); + range.collapse(start); + + function nodeAfter(node) { + var found = null; + while (!found && node) { + found = node.nextSibling; + node = node.parentNode; + } + return nodeAtStartOf(found); + } + + function nodeAtStartOf(node) { + while (node && node.firstChild) node = node.firstChild; + return {node: node, offset: 0}; + } + + var containing = range.parentElement(); + if (!isAncestor(win.document.body, containing)) return null; + if (!containing.firstChild) return nodeAtStartOf(containing); + + var working = range.duplicate(); + working.moveToElementText(containing); + working.collapse(true); + for (var cur = containing.firstChild; cur; cur = cur.nextSibling) { + if (cur.nodeType == 3) { + var size = cur.nodeValue.length; + working.move("character", size); + } + else { + working.moveToElementText(cur); + working.collapse(false); + } + + var dir = range.compareEndPoints("StartToStart", working); + if (dir == 0) return nodeAfter(cur); + if (dir == 1) continue; + if (cur.nodeType != 3) return nodeAtStartOf(cur); + + working.setEndPoint("StartToEnd", range); + return {node: cur, offset: size - working.text.length}; + } + return nodeAfter(containing); + } + + select.markSelection = function(win) { + currentSelection = null; + var sel = win.document.selection; + if (!sel) return; + var start = selectionNode(win, true), + end = selectionNode(win, false); + if (!start || !end) return; + currentSelection = {start: start, end: end, window: win, changed: false}; + }; + + select.selectMarked = function() { + if (!currentSelection || !currentSelection.changed) return; + + function makeRange(point) { + var range = currentSelection.window.document.body.createTextRange(); + var node = point.node; + if (!node) { + range.moveToElementText(currentSelection.window.document.body); + range.collapse(false); + } + else if (node.nodeType == 3) { + range.moveToElementText(node.parentNode); + var offset = point.offset; + while (node.previousSibling) { + node = node.previousSibling; + offset += (node.innerText || "").length; + } + range.move("character", offset); + } + else { + range.moveToElementText(node); + range.collapse(true); + } + return range; + } + + var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end); + start.setEndPoint("StartToEnd", end); + start.select(); + }; + + // Get the top-level node that one end of the cursor is inside or + // after. Note that this returns false for 'no cursor', and null + // for 'start of document'. + select.selectionTopNode = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return false; + + var range = selection.createRange(); + range.collapse(start); + var around = range.parentElement(); + if (around && isAncestor(container, around)) { + // Only use this node if the selection is not at its start. + var range2 = range.duplicate(); + range2.moveToElementText(around); + if (range.compareEndPoints("StartToStart", range2) == -1) + return topLevelNodeAt(around, container); + } + // Fall-back hack + try {range.pasteHTML("");} + catch (e) {return false;} + + var temp = container.ownerDocument.getElementById("xxx-temp-xxx"); + if (temp) { + var result = topLevelNodeBefore(temp, container); + removeElement(temp); + return result; + } + return false; + }; + + // Place the cursor after this.start. This is only useful when + // manually moving the cursor instead of restoring it to its old + // position. + select.focusAfterNode = function(node, container) { + var range = container.ownerDocument.body.createTextRange(); + range.moveToElementText(node || container); + range.collapse(!node); + range.select(); + }; + + select.somethingSelected = function(win) { + var sel = win.document.selection; + return sel && (sel.createRange().text != ""); + }; + + function insertAtCursor(window, html) { + var selection = window.document.selection; + if (selection) { + var range = selection.createRange(); + range.pasteHTML(html); + range.collapse(false); + range.select(); + } + } + + // Used to normalize the effect of the enter key, since browsers + // do widely different things when pressing enter in designMode. + select.insertNewlineAtCursor = function(window) { + insertAtCursor(window, "
"); + }; + + select.insertTabAtCursor = function(window) { + insertAtCursor(window, fourSpaces); + }; + + // Get the BR node at the start of the line on which the cursor + // currently is, and the offset into the line. Returns null as + // node if cursor is on first line. + select.cursorPos = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return null; + + var topNode = select.selectionTopNode(container, start); + while (topNode && topNode.nodeName != "BR") + topNode = topNode.previousSibling; + + var range = selection.createRange(), range2 = range.duplicate(); + range.collapse(start); + if (topNode) { + range2.moveToElementText(topNode); + range2.collapse(false); + } + else { + // When nothing is selected, we can get all kinds of funky errors here. + try { range2.moveToElementText(container); } + catch (e) { return null; } + range2.collapse(true); + } + range.setEndPoint("StartToStart", range2); + + return {node: topNode, offset: range.text.length}; + }; + + select.setCursorPos = function(container, from, to) { + function rangeAt(pos) { + var range = container.ownerDocument.body.createTextRange(); + if (!pos.node) { + range.moveToElementText(container); + range.collapse(true); + } + else { + range.moveToElementText(pos.node); + range.collapse(false); + } + range.move("character", pos.offset); + return range; + } + + var range = rangeAt(from); + if (to && to != from) + range.setEndPoint("EndToEnd", rangeAt(to)); + range.select(); + } + + // Make sure the cursor is visible. + select.scrollToCursor = function(container) { + var selection = container.ownerDocument.selection; + if (!selection) return null; + selection.createRange().scrollIntoView(); + }; + + select.scrollToNode = function(node) { + if (!node) return; + node.scrollIntoView(); + }; + + // Some hacks for storing and re-storing the selection when the editor loses and regains focus. + select.selectionCoords = function (win) { + var selection = win.document.selection; + if (!selection) return null; + var start = selection.createRange(), end = start.duplicate(); + start.collapse(true); + end.collapse(false); + + var body = win.document.body; + return {start: {x: start.boundingLeft + body.scrollLeft - 1, + y: start.boundingTop + body.scrollTop}, + end: {x: end.boundingLeft + body.scrollLeft - 1, + y: end.boundingTop + body.scrollTop}}; + }; + + // Restore a stored selection. + select.selectCoords = function(win, coords) { + if (!coords) return; + + var range1 = win.document.body.createTextRange(), range2 = range1.duplicate(); + // This can fail for various hard-to-handle reasons. + try { + range1.moveToPoint(coords.start.x, coords.start.y); + range2.moveToPoint(coords.end.x, coords.end.y); + range1.setEndPoint("EndToStart", range2); + range1.select(); + } catch(e) {alert(e.message);} + }; + } + // W3C model + else { + // Store start and end nodes, and offsets within these, and refer + // back to the selection object from those nodes, so that this + // object can be updated when the nodes are replaced before the + // selection is restored. + select.markSelection = function (win) { + var selection = win.getSelection(); + if (!selection || selection.rangeCount == 0) + return (currentSelection = null); + var range = selection.getRangeAt(0); + + currentSelection = { + start: {node: range.startContainer, offset: range.startOffset}, + end: {node: range.endContainer, offset: range.endOffset}, + window: win, + changed: false + }; + + // We want the nodes right at the cursor, not one of their + // ancestors with a suitable offset. This goes down the DOM tree + // until a 'leaf' is reached (or is it *up* the DOM tree?). + function normalize(point){ + while (point.node.nodeType != 3 && point.node.nodeName != "BR") { + var newNode = point.node.childNodes[point.offset] || point.node.nextSibling; + point.offset = 0; + while (!newNode && point.node.parentNode) { + point.node = point.node.parentNode; + newNode = point.node.nextSibling; + } + point.node = newNode; + if (!newNode) + break; + } + } + + normalize(currentSelection.start); + normalize(currentSelection.end); + }; + + select.selectMarked = function () { + if (!currentSelection || !currentSelection.changed) return; + var win = currentSelection.window, range = win.document.createRange(); + + function setPoint(point, which) { + if (point.node) { + // Some magic to generalize the setting of the start and end + // of a range. + if (point.offset == 0) + range["set" + which + "Before"](point.node); + else + range["set" + which](point.node, point.offset); + } + else { + range.setStartAfter(win.document.body.lastChild || win.document.body); + } + } + + setPoint(currentSelection.end, "End"); + setPoint(currentSelection.start, "Start"); + selectRange(range, win); + }; + + // Helper for selecting a range object. + function selectRange(range, window) { + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + }; + function selectionRange(window) { + var selection = window.getSelection(); + if (!selection || selection.rangeCount == 0) + return false; + else + return selection.getRangeAt(0); + } + + // Finding the top-level node at the cursor in the W3C is, as you + // can see, quite an involved process. + select.selectionTopNode = function(container, start) { + var range = selectionRange(container.ownerDocument.defaultView); + if (!range) return false; + + var node = start ? range.startContainer : range.endContainer; + var offset = start ? range.startOffset : range.endOffset; + // Work around (yet another) bug in Opera's selection model. + if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 && + container.childNodes[range.startOffset] && container.childNodes[range.startOffset].nodeName == "BR") + offset--; + + // For text nodes, we look at the node itself if the cursor is + // inside, or at the node before it if the cursor is at the + // start. + if (node.nodeType == 3){ + if (offset > 0) + return topLevelNodeAt(node, container); + else + return topLevelNodeBefore(node, container); + } + // Occasionally, browsers will return the HTML node as + // selection. If the offset is 0, we take the start of the frame + // ('after null'), otherwise, we take the last node. + else if (node.nodeName == "HTML") { + return (offset == 1 ? null : container.lastChild); + } + // If the given node is our 'container', we just look up the + // correct node by using the offset. + else if (node == container) { + return (offset == 0) ? null : node.childNodes[offset - 1]; + } + // In any other case, we have a regular node. If the cursor is + // at the end of the node, we use the node itself, if it is at + // the start, we use the node before it, and in any other + // case, we look up the child before the cursor and use that. + else { + if (offset == node.childNodes.length) + return topLevelNodeAt(node, container); + else if (offset == 0) + return topLevelNodeBefore(node, container); + else + return topLevelNodeAt(node.childNodes[offset - 1], container); + } + }; + + select.focusAfterNode = function(node, container) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + range.setStartBefore(container.firstChild || container); + // In Opera, setting the end of a range at the end of a line + // (before a BR) will cause the cursor to appear on the next + // line, so we set the end inside of the start node when + // possible. + if (node && !node.firstChild) + range.setEndAfter(node); + else if (node) + range.setEnd(node, node.childNodes.length); + else + range.setEndBefore(container.firstChild || container); + range.collapse(false); + selectRange(range, win); + }; + + select.somethingSelected = function(win) { + var range = selectionRange(win); + return range && !range.collapsed; + }; + + function insertNodeAtCursor(window, node) { + var range = selectionRange(window); + if (!range) return; + + range.deleteContents(); + range.insertNode(node); + webkitLastLineHack(window.document.body); + range = window.document.createRange(); + range.selectNode(node); + range.collapse(false); + selectRange(range, window); + } + + select.insertNewlineAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createElement("BR")); + }; + + select.insertTabAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createTextNode(fourSpaces)); + }; + + select.cursorPos = function(container, start) { + var range = selectionRange(window); + if (!range) return; + + var topNode = select.selectionTopNode(container, start); + while (topNode && topNode.nodeName != "BR") + topNode = topNode.previousSibling; + + range = range.cloneRange(); + range.collapse(start); + if (topNode) + range.setStartAfter(topNode); + else + range.setStartBefore(container); + return {node: topNode, offset: range.toString().length}; + }; + + select.setCursorPos = function(container, from, to) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + + function setPoint(node, offset, side) { + if (!node) + node = container.firstChild; + else + node = node.nextSibling; + + if (!node) + return; + + if (offset == 0) { + range["set" + side + "Before"](node); + return true; + } + + var backlog = [] + function decompose(node) { + if (node.nodeType == 3) + backlog.push(node); + else + forEach(node.childNodes, decompose); + } + while (true) { + while (node && !backlog.length) { + decompose(node); + node = node.nextSibling; + } + var cur = backlog.shift(); + if (!cur) return false; + + var length = cur.nodeValue.length; + if (length >= offset) { + range["set" + side](cur, offset); + return true; + } + offset -= length; + } + } + + to = to || from; + if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start")) + selectRange(range, win); + }; + + select.scrollToNode = function(element) { + if (!element) return; + var doc = element.ownerDocument, body = doc.body, win = doc.defaultView, html = doc.documentElement; + + // In Opera, BR elements *always* have a scrollTop property of zero. Go Opera. + while (element && !element.offsetTop) + element = element.previousSibling; + + var y = 0, pos = element; + while (pos && pos.offsetParent) { + y += pos.offsetTop; + pos = pos.offsetParent; + } + + var screen_y = y - (body.scrollTop || html.scrollTop || 0); + if (screen_y < 0 || screen_y > win.innerHeight - 30) + win.scrollTo(body.scrollLeft || html.scrollLeft || 0, y); + }; + + select.scrollToCursor = function(container) { + select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild); + }; + } +})(); diff --git a/media/CodeMirror-0.62/js/stringstream.js b/media/CodeMirror-0.62/js/stringstream.js new file mode 100644 index 0000000..6d9355f --- /dev/null +++ b/media/CodeMirror-0.62/js/stringstream.js @@ -0,0 +1,140 @@ +/* String streams are the things fed to parsers (which can feed them + * to a tokenizer if they want). They provide peek and next methods + * for looking at the current character (next 'consumes' this + * character, peek does not), and a get method for retrieving all the + * text that was consumed since the last time get was called. + * + * An easy mistake to make is to let a StopIteration exception finish + * the token stream while there are still characters pending in the + * string stream (hitting the end of the buffer while parsing a + * token). To make it easier to detect such errors, the strings throw + * an exception when this happens. + */ + +// Make a string stream out of an iterator that returns strings. This +// is applied to the result of traverseDOM (see codemirror.js), and +// the resulting stream is fed to the parser. +window.stringStream = function(source){ + // String that's currently being iterated over. + var current = ""; + // Position in that string. + var pos = 0; + // Accumulator for strings that have been iterated over but not + // get()-ed yet. + var accum = ""; + // Make sure there are more characters ready, or throw + // StopIteration. + function ensureChars() { + while (pos == current.length) { + accum += current; + current = ""; // In case source.next() throws + pos = 0; + try {current = source.next();} + catch (e) { + if (e != StopIteration) throw e; + else return false; + } + } + return true; + } + + return { + // Return the next character in the stream. + peek: function() { + if (!ensureChars()) return null; + return current.charAt(pos); + }, + // Get the next character, throw StopIteration if at end, check + // for unused content. + next: function() { + if (!ensureChars()) { + if (accum.length > 0) + throw "End of stringstream reached without emptying buffer ('" + accum + "')."; + else + throw StopIteration; + } + return current.charAt(pos++); + }, + // Return the characters iterated over since the last call to + // .get(). + get: function() { + var temp = accum; + accum = ""; + if (pos > 0){ + temp += current.slice(0, pos); + current = current.slice(pos); + pos = 0; + } + return temp; + }, + // Push a string back into the stream. + push: function(str) { + current = current.slice(0, pos) + str + current.slice(pos); + }, + lookAhead: function(str, consume, skipSpaces, caseInsensitive) { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + str = cased(str); + var found = false; + + var _accum = accum, _pos = pos; + if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/); + + while (true) { + var end = pos + str.length, left = current.length - pos; + if (end <= current.length) { + found = str == cased(current.slice(pos, end)); + pos = end; + break; + } + else if (str.slice(0, left) == cased(current.slice(pos))) { + accum += current; current = ""; + try {current = source.next();} + catch (e) {break;} + pos = 0; + str = str.slice(left); + } + else { + break; + } + } + + if (!(found && consume)) { + current = accum.slice(_accum.length) + current; + pos = _pos; + accum = _accum; + } + + return found; + }, + + // Utils built on top of the above + more: function() { + return this.peek() !== null; + }, + applies: function(test) { + var next = this.peek(); + return (next !== null && test(next)); + }, + nextWhile: function(test) { + var next; + while ((next = this.peek()) !== null && test(next)) + this.next(); + }, + matches: function(re) { + var next = this.peek(); + return (next !== null && re.test(next)); + }, + nextWhileMatches: function(re) { + var next; + while ((next = this.peek()) !== null && re.test(next)) + this.next(); + }, + equals: function(ch) { + return ch === this.peek(); + }, + endOfLine: function() { + var next = this.peek(); + return next == null || next == "\n"; + } + }; +}; diff --git a/media/CodeMirror-0.62/js/tokenize.js b/media/CodeMirror-0.62/js/tokenize.js new file mode 100644 index 0000000..071970c --- /dev/null +++ b/media/CodeMirror-0.62/js/tokenize.js @@ -0,0 +1,57 @@ +// A framework for simple tokenizers. Takes care of newlines and +// white-space, and of getting the text from the source stream into +// the token object. A state is a function of two arguments -- a +// string stream and a setState function. The second can be used to +// change the tokenizer's state, and can be ignored for stateless +// tokenizers. This function should advance the stream over a token +// and return a string or object containing information about the next +// token, or null to pass and have the (new) state be called to finish +// the token. When a string is given, it is wrapped in a {style, type} +// object. In the resulting object, the characters consumed are stored +// under the content property. Any whitespace following them is also +// automatically consumed, and added to the value property. (Thus, +// content is the actual meaningful part of the token, while value +// contains all the text it spans.) + +function tokenizer(source, state) { + // Newlines are always a separate token. + function isWhiteSpace(ch) { + // The messy regexp is because IE's regexp matcher is of the + // opinion that non-breaking spaces are no whitespace. + return ch != "\n" && /^[\s\u00a0]*$/.test(ch); + } + + var tokenizer = { + state: state, + + take: function(type) { + if (typeof(type) == "string") + type = {style: type, type: type}; + + type.content = (type.content || "") + source.get(); + if (!/\n$/.test(type.content)) + source.nextWhile(isWhiteSpace); + type.value = type.content + source.get(); + return type; + }, + + next: function () { + if (!source.more()) throw StopIteration; + + var type; + if (source.equals("\n")) { + source.next(); + return this.take("whitespace"); + } + + if (source.applies(isWhiteSpace)) + type = "whitespace"; + else + while (!type) + type = this.state(source, function(s) {tokenizer.state = s;}); + + return this.take(type); + } + }; + return tokenizer; +} diff --git a/media/CodeMirror-0.62/js/tokenizejavascript.js b/media/CodeMirror-0.62/js/tokenizejavascript.js new file mode 100644 index 0000000..f55dfce --- /dev/null +++ b/media/CodeMirror-0.62/js/tokenizejavascript.js @@ -0,0 +1,175 @@ +/* Tokenizer for JavaScript code */ + +var tokenizeJavaScript = (function() { + // Advance the stream until the given character (not preceded by a + // backslash) is encountered, or the end of the line is reached. + function nextUntilUnescaped(source, end) { + var escaped = false; + var next; + while (!source.endOfLine()) { + var next = source.next(); + if (next == end && !escaped) + return false; + escaped = !escaped && next == "\\"; + } + return escaped; + } + + // A map of JavaScript's keywords. The a/b/c keyword distinction is + // very rough, but it gives the parser enough information to parse + // correct code correctly (we don't care that much how we parse + // incorrect code). The style information included in these objects + // is used by the highlighter to pick the correct CSS style for a + // token. + var keywords = function(){ + function result(type, style){ + return {type: type, style: "js-" + style}; + } + // keywords that take a parenthised expression, and then a + // statement (if) + var keywordA = result("keyword a", "keyword"); + // keywords that take just a statement (else) + var keywordB = result("keyword b", "keyword"); + // keywords that optionally take an expression, and form a + // statement (return) + var keywordC = result("keyword c", "keyword"); + var operator = result("operator", "keyword"); + var atom = result("atom", "atom"); + return { + "if": keywordA, "while": keywordA, "with": keywordA, + "else": keywordB, "do": keywordB, "try": keywordB, "finally": keywordB, + "return": keywordC, "break": keywordC, "continue": keywordC, "new": keywordC, "delete": keywordC, "throw": keywordC, + "in": operator, "typeof": operator, "instanceof": operator, + "var": result("var", "keyword"), "function": result("function", "keyword"), "catch": result("catch", "keyword"), + "for": result("for", "keyword"), "switch": result("switch", "keyword"), + "case": result("case", "keyword"), "default": result("default", "keyword"), + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom + }; + }(); + + // Some helper regexps + var isOperatorChar = /[+\-*&%\/=<>!?|]/; + var isHexDigit = /[0-9A-Fa-f]/; + var isWordChar = /[\w\$_]/; + + // Wrapper around jsToken that helps maintain parser state (whether + // we are inside of a multi-line comment and whether the next token + // could be a regular expression). + function jsTokenState(inside, regexp) { + return function(source, setState) { + var newInside = inside; + var type = jsToken(inside, regexp, source, function(c) {newInside = c;}); + var newRegexp = type.type == "operator" || type.type == "keyword c" || type.type.match(/^[\[{}\(,;:]$/); + if (newRegexp != regexp || newInside != inside) + setState(jsTokenState(newInside, newRegexp)); + return type; + }; + } + + // The token reader, inteded to be used by the tokenizer from + // tokenize.js (through jsTokenState). Advances the source stream + // over a token, and returns an object containing the type and style + // of that token. + function jsToken(inside, regexp, source, setInside) { + function readHexNumber(){ + source.next(); // skip the 'x' + source.nextWhileMatches(isHexDigit); + return {type: "number", style: "js-atom"}; + } + + function readNumber() { + source.nextWhileMatches(/[0-9]/); + if (source.equals(".")){ + source.next(); + source.nextWhileMatches(/[0-9]/); + } + if (source.equals("e") || source.equals("E")){ + source.next(); + if (source.equals("-")) + source.next(); + source.nextWhileMatches(/[0-9]/); + } + return {type: "number", style: "js-atom"}; + } + // Read a word, look it up in keywords. If not found, it is a + // variable, otherwise it is a keyword of the type found. + function readWord() { + source.nextWhileMatches(isWordChar); + var word = source.get(); + var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word]; + return known ? {type: known.type, style: known.style, content: word} : + {type: "variable", style: "js-variable", content: word}; + } + function readRegexp() { + nextUntilUnescaped(source, "/"); + source.nextWhileMatches(/[gi]/); + return {type: "regexp", style: "js-string"}; + } + // Mutli-line comments are tricky. We want to return the newlines + // embedded in them as regular newline tokens, and then continue + // returning a comment token for every line of the comment. So + // some state has to be saved (inside) to indicate whether we are + // inside a /* */ sequence. + function readMultilineComment(start){ + var newInside = "/*"; + var maybeEnd = (start == "*"); + while (true) { + if (source.endOfLine()) + break; + var next = source.next(); + if (next == "/" && maybeEnd){ + newInside = null; + break; + } + maybeEnd = (next == "*"); + } + setInside(newInside); + return {type: "comment", style: "js-comment"}; + } + function readOperator() { + source.nextWhileMatches(isOperatorChar); + return {type: "operator", style: "js-operator"}; + } + function readString(quote) { + var endBackSlash = nextUntilUnescaped(source, quote); + setInside(endBackSlash ? quote : null); + return {type: "string", style: "js-string"}; + } + + // Fetch the next token. Dispatches on first character in the + // stream, or first two characters when the first is a slash. + if (inside == "\"" || inside == "'") + return readString(inside); + var ch = source.next(); + if (inside == "/*") + return readMultilineComment(ch); + else if (ch == "\"" || ch == "'") + return readString(ch); + // with punctuation, the type of the token is the symbol itself + else if (/[\[\]{}\(\),;\:\.]/.test(ch)) + return {type: ch, style: "js-punctuation"}; + else if (ch == "0" && (source.equals("x") || source.equals("X"))) + return readHexNumber(); + else if (/[0-9]/.test(ch)) + return readNumber(); + else if (ch == "/"){ + if (source.equals("*")) + { source.next(); return readMultilineComment(ch); } + else if (source.equals("/")) + { nextUntilUnescaped(source, null); return {type: "comment", style: "js-comment"};} + else if (regexp) + return readRegexp(); + else + return readOperator(); + } + else if (isOperatorChar.test(ch)) + return readOperator(); + else + return readWord(); + } + + // The external interface to the tokenizer. + return function(source, startState) { + return tokenizer(source, startState || jsTokenState(false, true)); + }; +})(); diff --git a/media/CodeMirror-0.62/js/undo.js b/media/CodeMirror-0.62/js/undo.js new file mode 100644 index 0000000..1930cfb --- /dev/null +++ b/media/CodeMirror-0.62/js/undo.js @@ -0,0 +1,403 @@ +/** + * Storage and control for undo information within a CodeMirror + * editor. 'Why on earth is such a complicated mess required for + * that?', I hear you ask. The goal, in implementing this, was to make + * the complexity of storing and reverting undo information depend + * only on the size of the edited or restored content, not on the size + * of the whole document. This makes it necessary to use a kind of + * 'diff' system, which, when applied to a DOM tree, causes some + * complexity and hackery. + * + * In short, the editor 'touches' BR elements as it parses them, and + * the History stores these. When nothing is touched in commitDelay + * milliseconds, the changes are committed: It goes over all touched + * nodes, throws out the ones that did not change since last commit or + * are no longer in the document, and assembles the rest into zero or + * more 'chains' -- arrays of adjacent lines. Links back to these + * chains are added to the BR nodes, while the chain that previously + * spanned these nodes is added to the undo history. Undoing a change + * means taking such a chain off the undo history, restoring its + * content (text is saved per line) and linking it back into the + * document. + */ + +// A history object needs to know about the DOM container holding the +// document, the maximum amount of undo levels it should store, the +// delay (of no input) after which it commits a set of changes, and, +// unfortunately, the 'parent' window -- a window that is not in +// designMode, and on which setTimeout works in every browser. +function History(container, maxDepth, commitDelay, editor, onChange) { + this.container = container; + this.maxDepth = maxDepth; this.commitDelay = commitDelay; + this.editor = editor; this.parent = editor.parent; + this.onChange = onChange; + // This line object represents the initial, empty editor. + var initial = {text: "", from: null, to: null}; + // As the borders between lines are represented by BR elements, the + // start of the first line and the end of the last one are + // represented by null. Since you can not store any properties + // (links to line objects) in null, these properties are used in + // those cases. + this.first = initial; this.last = initial; + // Similarly, a 'historyTouched' property is added to the BR in + // front of lines that have already been touched, and 'firstTouched' + // is used for the first line. + this.firstTouched = false; + // History is the set of committed changes, touched is the set of + // nodes touched since the last commit. + this.history = []; this.redoHistory = []; this.touched = []; +} + +History.prototype = { + // Schedule a commit (if no other touches come in for commitDelay + // milliseconds). + scheduleCommit: function() { + var self = this; + this.parent.clearTimeout(this.commitTimeout); + this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay); + }, + + // Mark a node as touched. Null is a valid argument. + touch: function(node) { + this.setTouched(node); + this.scheduleCommit(); + }, + + // Undo the last change. + undo: function() { + // Make sure pending changes have been committed. + this.commit(); + + if (this.history.length) { + // Take the top diff from the history, apply it, and store its + // shadow in the redo history. + var item = this.history.pop(); + this.redoHistory.push(this.updateTo(item, "applyChain")); + if (this.onChange) this.onChange(); + return this.chainNode(item); + } + }, + + // Redo the last undone change. + redo: function() { + this.commit(); + if (this.redoHistory.length) { + // The inverse of undo, basically. + var item = this.redoHistory.pop(); + this.addUndoLevel(this.updateTo(item, "applyChain")); + if (this.onChange) this.onChange(); + return this.chainNode(item); + } + }, + + clear: function() { + this.history = []; + this.redoHistory = []; + }, + + // Ask for the size of the un/redo histories. + historySize: function() { + return {undo: this.history.length, redo: this.redoHistory.length}; + }, + + // Push a changeset into the document. + push: function(from, to, lines) { + var chain = []; + for (var i = 0; i < lines.length; i++) { + var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR"); + chain.push({from: from, to: end, text: cleanText(lines[i])}); + from = end; + } + this.pushChains([chain], from == null && to == null); + }, + + pushChains: function(chains, doNotHighlight) { + this.commit(doNotHighlight); + this.addUndoLevel(this.updateTo(chains, "applyChain")); + this.redoHistory = []; + }, + + // Retrieve a DOM node from a chain (for scrolling to it after undo/redo). + chainNode: function(chains) { + for (var i = 0; i < chains.length; i++) { + var start = chains[i][0], node = start && (start.from || start.to); + if (node) return node; + } + }, + + // Clear the undo history, make the current document the start + // position. + reset: function() { + this.history = []; this.redoHistory = []; + }, + + textAfter: function(br) { + return this.after(br).text; + }, + + nodeAfter: function(br) { + return this.after(br).to; + }, + + nodeBefore: function(br) { + return this.before(br).from; + }, + + // Commit unless there are pending dirty nodes. + tryCommit: function() { + if (!window.History) return; // Stop when frame has been unloaded + if (this.editor.highlightDirty()) this.commit(); + else this.scheduleCommit(); + }, + + // Check whether the touched nodes hold any changes, if so, commit + // them. + commit: function(doNotHighlight) { + this.parent.clearTimeout(this.commitTimeout); + // Make sure there are no pending dirty nodes. + if (!doNotHighlight) this.editor.highlightDirty(true); + // Build set of chains. + var chains = this.touchedChains(), self = this; + + if (chains.length) { + this.addUndoLevel(this.updateTo(chains, "linkChain")); + this.redoHistory = []; + if (this.onChange) this.onChange(); + } + }, + + // [ end of public interface ] + + // Update the document with a given set of chains, return its + // shadow. updateFunc should be "applyChain" or "linkChain". In the + // second case, the chains are taken to correspond the the current + // document, and only the state of the line data is updated. In the + // first case, the content of the chains is also pushed iinto the + // document. + updateTo: function(chains, updateFunc) { + var shadows = [], dirty = []; + for (var i = 0; i < chains.length; i++) { + shadows.push(this.shadowChain(chains[i])); + dirty.push(this[updateFunc](chains[i])); + } + if (updateFunc == "applyChain") + this.notifyDirty(dirty); + return shadows; + }, + + // Notify the editor that some nodes have changed. + notifyDirty: function(nodes) { + forEach(nodes, method(this.editor, "addDirtyNode")) + this.editor.scheduleHighlight(); + }, + + // Link a chain into the DOM nodes (or the first/last links for null + // nodes). + linkChain: function(chain) { + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + if (line.from) line.from.historyAfter = line; + else this.first = line; + if (line.to) line.to.historyBefore = line; + else this.last = line; + } + }, + + // Get the line object after/before a given node. + after: function(node) { + return node ? node.historyAfter : this.first; + }, + before: function(node) { + return node ? node.historyBefore : this.last; + }, + + // Mark a node as touched if it has not already been marked. + setTouched: function(node) { + if (node) { + if (!node.historyTouched) { + this.touched.push(node); + node.historyTouched = true; + } + } + else { + this.firstTouched = true; + } + }, + + // Store a new set of undo info, throw away info if there is more of + // it than allowed. + addUndoLevel: function(diffs) { + this.history.push(diffs); + if (this.history.length > this.maxDepth) + this.history.shift(); + }, + + // Build chains from a set of touched nodes. + touchedChains: function() { + var self = this; + + // The temp system is a crummy hack to speed up determining + // whether a (currently touched) node has a line object associated + // with it. nullTemp is used to store the object for the first + // line, other nodes get it stored in their historyTemp property. + var nullTemp = null; + function temp(node) {return node ? node.historyTemp : nullTemp;} + function setTemp(node, line) { + if (node) node.historyTemp = line; + else nullTemp = line; + } + + function buildLine(node) { + var text = []; + for (var cur = node ? node.nextSibling : self.container.firstChild; + cur && cur.nodeName != "BR"; cur = cur.nextSibling) + if (cur.currentText) text.push(cur.currentText); + return {from: node, to: cur, text: cleanText(text.join(""))}; + } + + // Filter out unchanged lines and nodes that are no longer in the + // document. Build up line objects for remaining nodes. + var lines = []; + if (self.firstTouched) self.touched.push(null); + forEach(self.touched, function(node) { + if (node && node.parentNode != self.container) return; + + if (node) node.historyTouched = false; + else self.firstTouched = false; + + var line = buildLine(node), shadow = self.after(node); + if (!shadow || shadow.text != line.text || shadow.to != line.to) { + lines.push(line); + setTemp(node, line); + } + }); + + // Get the BR element after/before the given node. + function nextBR(node, dir) { + var link = dir + "Sibling", search = node[link]; + while (search && search.nodeName != "BR") + search = search[link]; + return search; + } + + // Assemble line objects into chains by scanning the DOM tree + // around them. + var chains = []; self.touched = []; + forEach(lines, function(line) { + // Note that this makes the loop skip line objects that have + // been pulled into chains by lines before them. + if (!temp(line.from)) return; + + var chain = [], curNode = line.from, safe = true; + // Put any line objects (referred to by temp info) before this + // one on the front of the array. + while (true) { + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.unshift(curLine); + setTemp(curNode, null); + if (!curNode) break; + safe = self.after(curNode); + curNode = nextBR(curNode, "previous"); + } + curNode = line.to; safe = self.before(line.from); + // Add lines after this one at end of array. + while (true) { + if (!curNode) break; + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.push(curLine); + setTemp(curNode, null); + safe = self.before(curNode); + curNode = nextBR(curNode, "next"); + } + chains.push(chain); + }); + + return chains; + }, + + // Find the 'shadow' of a given chain by following the links in the + // DOM nodes at its start and end. + shadowChain: function(chain) { + var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to; + while (true) { + shadows.push(next); + var nextNode = next.to; + if (!nextNode || nextNode == end) + break; + else + next = nextNode.historyAfter || this.before(end); + // (The this.before(end) is a hack -- FF sometimes removes + // properties from BR nodes, in which case the best we can hope + // for is to not break.) + } + return shadows; + }, + + // Update the DOM tree to contain the lines specified in a given + // chain, link this chain into the DOM nodes. + applyChain: function(chain) { + // Some attempt is made to prevent the cursor from jumping + // randomly when an undo or redo happens. It still behaves a bit + // strange sometimes. + var cursor = select.cursorPos(this.container, false), self = this; + + // Remove all nodes in the DOM tree between from and to (null for + // start/end of container). + function removeRange(from, to) { + var pos = from ? from.nextSibling : self.container.firstChild; + while (pos != to) { + var temp = pos.nextSibling; + removeElement(pos); + pos = temp; + } + } + + var start = chain[0].from, end = chain[chain.length - 1].to; + // Clear the space where this change has to be made. + removeRange(start, end); + + // Insert the content specified by the chain into the DOM tree. + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + // The start and end of the space are already correct, but BR + // tags inside it have to be put back. + if (i > 0) + self.container.insertBefore(line.from, end); + + // Add the text. + var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument); + self.container.insertBefore(node, end); + // See if the cursor was on this line. Put it back, adjusting + // for changed line length, if it was. + if (cursor && cursor.node == line.from) { + var cursordiff = 0; + var prev = this.after(line.from); + if (prev && i == chain.length - 1) { + // Only adjust if the cursor is after the unchanged part of + // the line. + for (var match = 0; match < cursor.offset && + line.text.charAt(match) == prev.text.charAt(match); match++); + if (cursor.offset > match) + cursordiff = line.text.length - prev.text.length; + } + select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)}); + } + // Cursor was in removed line, this is last new line. + else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) { + select.setCursorPos(this.container, {node: line.from, offset: line.text.length}); + } + } + + // Anchor the chain in the DOM tree. + this.linkChain(chain); + return start; + } +}; diff --git a/media/CodeMirror-0.62/js/util.js b/media/CodeMirror-0.62/js/util.js new file mode 100644 index 0000000..f552767 --- /dev/null +++ b/media/CodeMirror-0.62/js/util.js @@ -0,0 +1,115 @@ +/* A few useful utility functions. */ + +var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent); +var webkit = /AppleWebKit/.test(navigator.userAgent); + +// Capture a method on an object. +function method(obj, name) { + return function() {obj[name].apply(obj, arguments);}; +} + +// The value used to signal the end of a sequence in iterators. +var StopIteration = {toString: function() {return "StopIteration"}}; + +// Apply a function to each element in a sequence. +function forEach(iter, f) { + if (iter.next) { + try {while (true) f(iter.next());} + catch (e) {if (e != StopIteration) throw e;} + } + else { + for (var i = 0; i < iter.length; i++) + f(iter[i]); + } +} + +// Map a function over a sequence, producing an array of results. +function map(iter, f) { + var accum = []; + forEach(iter, function(val) {accum.push(f(val));}); + return accum; +} + +// Create a predicate function that tests a string againsts a given +// regular expression. No longer used but might be used by 3rd party +// parsers. +function matcher(regexp){ + return function(value){return regexp.test(value);}; +} + +// Test whether a DOM node has a certain CSS class. Much faster than +// the MochiKit equivalent, for some reason. +function hasClass(element, className){ + var classes = element.className; + return classes && new RegExp("(^| )" + className + "($| )").test(classes); +} + +// Insert a DOM node after another node. +function insertAfter(newNode, oldNode) { + var parent = oldNode.parentNode; + parent.insertBefore(newNode, oldNode.nextSibling); + return newNode; +} + +function removeElement(node) { + if (node.parentNode) + node.parentNode.removeChild(node); +} + +function clearElement(node) { + while (node.firstChild) + node.removeChild(node.firstChild); +} + +// Check whether a node is contained in another one. +function isAncestor(node, child) { + while (child = child.parentNode) { + if (node == child) + return true; + } + return false; +} + +// The non-breaking space character. +var nbsp = "\u00a0"; +var matching = {"{": "}", "[": "]", "(": ")", + "}": "{", "]": "[", ")": "("}; + +// Standardize a few unportable event properties. +function normalizeEvent(event) { + if (!event.stopPropagation) { + event.stopPropagation = function() {this.cancelBubble = true;}; + event.preventDefault = function() {this.returnValue = false;}; + } + if (!event.stop) { + event.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + } + + if (event.type == "keypress") { + event.code = (event.charCode == null) ? event.keyCode : event.charCode; + event.character = String.fromCharCode(event.code); + } + return event; +} + +// Portably register event handlers. +function addEventHandler(node, type, handler, removeFunc) { + function wrapHandler(event) { + handler(normalizeEvent(event || window.event)); + } + if (typeof node.addEventListener == "function") { + node.addEventListener(type, wrapHandler, false); + if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);}; + } + else { + node.attachEvent("on" + type, wrapHandler); + if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);}; + } +} + +function nodeText(node) { + return node.innerText || node.textContent || node.nodeValue || ""; +} diff --git a/media/CodeMirror-0.62/jstest.html b/media/CodeMirror-0.62/jstest.html new file mode 100644 index 0000000..13879a7 --- /dev/null +++ b/media/CodeMirror-0.62/jstest.html @@ -0,0 +1,56 @@ + + + + + CodeMirror: JavaScript demonstration + + + + +

This page demonstrates CodeMirror's +JavaScript parser. Note that the ugly buttons at the top are not are +not part of CodeMirror proper -- they demonstrate the way it can be +embedded in a web-application.

+ +
+ +
+ + + + + diff --git a/media/CodeMirror-0.62/mixedtest.html b/media/CodeMirror-0.62/mixedtest.html new file mode 100644 index 0000000..46d25da --- /dev/null +++ b/media/CodeMirror-0.62/mixedtest.html @@ -0,0 +1,52 @@ + + + + CodeMirror: HTML mixed-mode demonstration + + + + +

This is a simple demonstration of the HTML mixed-mode indentation +module for CodeMirror. Script tags use the JS +parser, style tags use the CSS parser.

+ +
+ +
+ + + + diff --git a/media/CodeMirror-0.62/sparqltest.html b/media/CodeMirror-0.62/sparqltest.html new file mode 100644 index 0000000..5d65c93 --- /dev/null +++ b/media/CodeMirror-0.62/sparqltest.html @@ -0,0 +1,41 @@ + + + + CodeMirror: Sparql demonstration + + + + +

Demonstration of CodeMirror's Sparql +highlighter.

+ +
+ +
+ + + + + diff --git a/media/CodeMirror-0.62/story.html b/media/CodeMirror-0.62/story.html new file mode 100644 index 0000000..d845858 --- /dev/null +++ b/media/CodeMirror-0.62/story.html @@ -0,0 +1,652 @@ + + + Implementing a syntax-higlighting JavaScript editor in JavaScript + + + + +

Implementing a syntax-higlighting JavaScript editor in JavaScript

+

or

+

A brutal odyssey to the dark side of the DOM tree

+ +

+ Topic: JavaScript, advanced browser weirdness, cool programming techniques
+ Audience: Programmers, especially JavaScript programmers
+ Author: Marijn Haverbeke
+ Date: May 24th 2007 +

+ +

Note: some of the details given here no + longer apply to the current CodeMirror + codebase, which has evolved quite a bit in the meantime.

+ +

In one of his (very informative) video + lectures, Douglas Crockford remarks that writing JavaScript + for the web is 'programming in a hostile environment'. I had done + my fair share of weird workarounds, and even occasonally gave up + an on idea entirely because browsers just wouldn't support it, but + before this project I never really realized just how powerless a + programmer can be in the face of buggy, incompatible, and poorly + designed platforms.

+ +

The plan was not ridiculously ambitious. I wanted to 'enhance' a + textarea to the point where writing code in it is pleasant. This meant + automatic indentation and, if possible at all, syntax highlighting.

+ +

In this document I describe the story of implementing this, for your + education and amusement. A demonstration of the resulting program, + along with the source code, can be found at my website.

+ +

Take one: Only indentation

+ +

The very first attempt merely added auto-indentation to a textarea + element. It would scan backwards through the content of the area, + starting from the cursor, until it had enough information to decide + how to indent the current line. It took me a while to figure out a + decent model for indenting JavaScript code, but in the end this seems + to work:

+ +
    +
  • Code that sits inside a block is indented one unit (generally two + spaces) more than the statement or brace that opened the block.
  • +
  • A statement that is continued to the next line is indented one unit + more than the line that starts the statement.
  • +
  • When dealing with lists of arguments or the content of array and + object literals there are two possible models. If there is any text + directly after the opening brace, bracket, or parenthesis, + subsequent lines are aligned with this opening character. If the + opening character is followed by a newline (optionally with whitespace + or comments before it), the next line is indented one unit further + than the line that started the list.
  • +
  • And, obviously, if a statement follows another statement it is + indented the same amount as the one before it.
  • +
+ +

When scanning backwards through code one has to take string values, + comments, and regular expressions (which are delimited by slashes) + into account, because braces and semicolons and such are not + significant when they appear inside them. Single-line ('//') comments + turned out to be rather inefficient to check for when doing a + backwards scan, since every time you encounter a newline you have to + go on to the next newline to determine whether this line ends in a + comment or not. Regular expressions are even worse ― without + contextual information they are impossible to distinguish from the + division operator, and I didn't get them working in this first + version.

+ +

To find out which line to indent, and to make sure that adding or + removing whitespace doesn't cause the cursor to jump in strange ways, + it is necessary to determine which text the user has selected. Even + though I was working with just a simple textarea at this point, this + was already a bit of a headache.

+ +

On W3C-standards-respecting browsers, textarea nodes have + selectionStart and selectionEnd + properties which nicely give you the amount of characters before + the start and end of the selection. Great!

+ +

Then, there is Internet Explorer. Internet Explorer also has an API + for looking at and manipulating selections. It gives you information + such as a detailed map of the space the selected lines take up on the + screen, in pixels, and of course the text inside the selection. It + does, however, not give you much of a clue on where the selection is + located in the document.

+ +

After some experimentation I managed to work out an elaborate + method for getting something similar to the + selectionStart and selectionEnd values + in other browsers. It worked like this:

+ +
    +
  • Get the TextRange object corresponding to the selection.
  • +
  • Record the length of the text inside it.
  • +
  • Make another TextRange that covers the whole textarea element.
  • +
  • Set the start of the first TextRange to the start of the second one.
  • +
  • Again get the length of the text in the first object.
  • +
  • Now selectionEnd is the second length, and selectionStart is + the second minus the first one.
  • +
+ +

That seemed to work, but when resetting the selection after modifying + the content of the textarea I ran into another interesting feature of + these TextRanges: You can move their endpoints by a given number of + characters, which is useful when trying to set a cursor at the Nth + character of a textarea, but in this context, newlines are not + considered to be characters, so you'll always end up one character too + far for every newline you passed. Of course, you can count newlines + and compensate for this (though it is still not possible to position + the cursor right in front of a newline). Sheesh.

+ +

After ragging on Internet Explorer for a while, let us move on and rag + on Firefox a bit. It turns out that, in Firefox, getting and setting + the text content of a DOM element is unexplainably expensive, + especially when there is a lot of text involved. As soon as I tried to + use my indentation code to indent itself (some 400 lines), I found + myself waiting for over four seconds every time I pressed enter. That + seemed a little slow.

+ +

designMode it is

+ +

The solution was obvious: Since the text inside a textarea can only be + manipulated as one single big string, I had to spread it out over + multiple nodes. How do you spread editable content over multiple + nodes? Right! designMode or contentEditable.

+ +

Now I wasn't entirely naive about designMode, I had been looking + into writing a non-messy WYSIWYG editor before, and at that time I had + concluded two things:

+ +
    +
  • It is impossible to prevent the user from inserting whichever HTML + junk he wants into the document.
  • +
  • In Internet Explorer, it is extemely hard to get a good view + on what nodes the user has selected.
  • +
+ +

Basically, the good folks at Microsoft designed a really bad interface + for putting editable documents in pages, and the other browsers, not + wanting to be left behind, more or less copied that. And there isn't + much hope for a better way to do this appearing anytime soon. Wise + people probably use a Flash movie or (God forbid) a Java applet for + these kind of things, though those are not without drawbacks either.

+ +

Anyway, seeing how using an editable document would also make syntax + highlighting possible, I foolishly went ahead. There is something + perversely fascinating about trying to build a complicated system on a + lousy, unsuitable platform.

+ +

A parser

+ +

How does one do decent syntax highlighting? A very simple scanning can + tell the difference between strings, comments, keywords, and other + code. But this time I wanted to actually be able to recognize regular + expressions, so that I didn't have any blatant incorrect behaviour + anymore.

+ +

That brought me to the idea of doing a serious parse on the code. This + would not only make detecting regular expressions much easier, it + would also give me detailed information about the code, which can be + used to determine proper indentation levels, and to make subtle + distinctions in colouring, for example the difference between variable + names and property names.

+ +

And hey, when we're parsing the whole thing, it would even be possible + to make a distinction between local and global variables, and colour + them differently. If you've ever programmed JavaScript you can + probably imagine how useful this would be ― it is ridiculously easy + to accidentally create global instead of local variables. I don't + consider myself a JavaScript rookie anymore, but it was (embarrasingly + enough) only this week that I realized that my habit of typing for + (name in object) ... was creating a global variable name, and that + I should be typing for (var name in object) ... instead.

+ +

Re-parsing all the code the user has typed in every time he hits a key + is obviously not feasible. So how does one combine on-the-fly + highlighting with a serious parser? One option would be to split the + code into top-level statements (functions, variable definitions, etc.) + and parse these separately. This is horribly clunky though, especially + considering the fact that modern JavaScripters often put all the code + in a file in a single big object or function to prevent namespace + pollution.

+ +

I have always liked continuation-passing style and generators. So the + idea I came up with is this: An interruptable, resumable parser. This + is a parser that does not run through a whole document at once, but + parses on-demand, a little bit at a time. At any moment you can create + a copy of its current state, which can be resumed later. You start + parsing at the top of the code, and keep going as long as you like, + but throughout the document, for example at every end of line, you + store a copy of the current parser state. Later on, when line 106 + changes, you grab the interrupted parser that was stored at the end of + line 105, and use it to re-parse line 106. It still knows exactly what + the context was at that point, which local variables were defined, + which unfinished statements were encountered, and so on.

+ +

But that, unfortunately, turned out to be not quite as easy as it + sounds.

+ +

The DOM nodes underfoot

+ +

Of course, when working inside an editable frame we don't just + have to deal with text. The code will be represented by some kind + of DOM tree. My first idea was to set the white-space: + pre style for the frame and try to work with mostly text, + with the occasional coloured span element. It turned + out that support for white-space: pre in browsers, + especially in editable frames, is so hopelessly glitchy that this + was unworkable.

+ +

Next I tried a series of div elements, one per + line, with span elements inside them. This seemed to + nicely reflect the structure of the code in a shallowly + hierarchical way. I soon realized, however, that my code would be + much more straightfoward when using no hierarchy whatsoever + ― a series of spans, with br tags + at the end of every line. This way, the DOM nodes form a flat + sequence that corresponds to the sequence of the text ― + just extract text from span nodes and substitute + newlines for br nodes.

+ +

It would be a shame if the editor would fall apart as soon as + someone pastes some complicated HTML into it. I wanted it to be + able to deal with whatever mess it finds. This means using some + kind of HTML-normalizer that takes arbitrary HTML and flattens it + into a series of brs and span elements + that contain a single text node. Just like the parsing process, it + would be best if this did not have to done to the entire buffer + every time something changes.

+ +

It took some banging my head against my keyboard, but I found a very + nice way to model this. It makes heavy use of generators, for which I + used MochiKit's iterator + framework. Bob Ippolito explains the concepts in this library very + well in his blog + post about it. (Also notice some of the dismissive comments at the + bottom of that post. They say "I don't think I really want to learn + this, so I'll make up some silly reason to condemn it.")

+ +

The highlighting process consists of the following elements: + normalizing the DOM tree, extracting the text from the DOM tree, + tokenizing this text, parsing the tokens, and finally adjusting the + DOM nodes to reflect the structure of the code.

+ +

The first two, I put into a single generator. It scans the DOM + tree, fixing anything that is not a simple top-level + span or br, and it produces the text + content of the nodes (or a newline in case of a br) + as its output ― each time it is called, it yields a string. + Continuation passing style was a good way to model this process in + an iterator, which has to be processed one step at a time. Look at + this simplified version:

+ +
function traverseDOM(start){
+  var cc = function(){scanNode(start, stop);};
+  function stop(){
+    cc = stop;
+    throw StopIteration;
+  }
+  function yield(value, c){
+    cc = c;
+    return value;
+  }
+
+  function scanNode(node, c){
+    if (node.nextSibling)
+      var nextc = function(){scanNode(node.nextSibling, c);};
+    else
+      var nextc = c;
+
+    if (/* node is proper span element */)
+      return yield(node.firstChild.nodeValue, nextc);
+    else if (/* node is proper br element */)
+      return yield("\n", nextc);
+    else
+      /* flatten node, yield its textual content */;
+  }
+
+  return {next: function(){return cc();}};
+}
+ +

The variable c stands for 'continuation', and cc for 'current + continuation' ― that last variable is used to store the function to + continue with, when yielding a value to the outside world. Every time + control leaves this function, it has to make sure that cc is set to + a suitable value, which is what yield and stop take care of.

+ +

The object that is returned contains a next method, which is + MochiKit's idea of an iterator, and the initial continuation just + throws a StopIteration, which is how MochiKit signals that an + iterator has reached its end.

+ +

The first lines of scanNode extend the continuation with the task of + scanning the next node, if there is a next node. The rest of the + function decides what kind of value to yield. Note that this is a + rather trivial example of this technique, since the process of going + through these nodes is basically linear (it was much, much more + complex in earlier versions), but still the trick with the + continuations makes the code shorter and, for those in the know, + clearer than the equivalent 'storing the iterator state in variables' + approach.

+ +

The next iterator that the input passes through is the + tokenizer. Well, actually, there is another iterator in between + that isolates the tokenizer from the fact that the DOM traversal + yields a bunch of separate strings, and presents them as a single + character stream (with a convenient peek operation), + but this is not a very interesting one. What the tokenizer returns + is a stream of token objects, each of which has a + value, its textual content, a type, like + "variable", "operator", or just itself, + "{" for example, in the case of significant + punctuation or special keywords. They also have a + style, which is used later by the highlighter to give + their span elements a class name (the parser will + still adjust this in some cases).

+ +

At first I assumed the parser would have to talk back to the + tokenizer about the current context, in order to be able to + distinguish those accursed regular expressions from divisions, but + it seems that regular expressions are only allowed if the previous + (non-whitespace, non-comment) token was either an operator, a + keyword like new or throw, or a specific + kind of punctuation ("[{}(,;:") that indicates a new + expression can be started here. This made things considerably + easier, since the 'regexp or no regexp' question could stay + entirely within the tokenizer.

+ +

The next step, then, is the parser. It does not do a very + thorough job because, firstly, it has to be fast, and secondly, it + should not go to pieces when fed an incorrect program. So only + superficial constructs are recognized, keywords that resemble each + other in syntax, such as while and if, + are treated in precisely the same way, as are try and + else ― the parser doesn't mind if an + else appears without an if. Stuff that + binds variables, var, function, and + catch to be precise, is treated with more care, + because the parser wants to know about local variables.

+ +

Inside the parser, three kinds of context are stored. Firstly, a set + of known local variables, which is used to adjust the style of + variable tokens. Every time the parser enters a function, a new set of + variables is created. If there was already such a set (entering an + inner function), a pointer to the old one is stored in the new one. At + the end of the function, the current variable set is 'popped' off and + the previous one is restored.

+ +

The second kind of context is the lexical context, this keeps track of + whether we are inside a statement, block, or list. Like the variable + context, it also forms a stack of contexts, with each one containing a + pointer to the previous ones so that they can be popped off again when + they are finished. This information is used for indentation. Every + time the parser encounters a newline token, it attaches the current + lexical context and a 'copy' of itself (more about that later) to this + token.

+ +

The third context is a continuation context. This parser does not use + straight continuation style, instead it uses a stack of actions that + have to be performed. These actions are simple functions, a kind of + minilanguage, they act on tokens, and decide what kind of new actions + should be pushed onto the stack. Here are some examples:

+ +
function expression(type){
+  if (type in atomicTypes) cont(maybeoperator);
+  else if (type == "function") cont(functiondef);
+  else if (type == "(") cont(pushlex("list"), expression, expect(")"), poplex);
+  else if (type == "operator") cont(expression);
+  else if (type == "[") cont(pushlex("list"), commasep(expression), expect("]"), poplex);
+  else if (type == "{") cont(pushlex("list"), commasep(objprop), expect("}"), poplex);
+  else if (type == "keyword c") cont(expression);
+}
+
+function block(type){
+  if (type == "}") cont();
+  else pass(statement, block);
+}
+ +

The function cont (for continue), will push the actions it is given + onto the stack (in reverse order, so that the first one will be popped + first). Actions such as pushlex and poplex merely adjust the + lexical environment, while others, such as expression itself, do + actual parsing. pass, as seen in block, is similar to cont, but + it does not 'consume' the current token, so the next action will again + see this same token. In block, this happens when the function + determines that we are not at the end of the block yet, so it pushes + the statement function which will interpret the current token as the + start of a statement.

+ +

These actions are called by a 'driver' function, which filters out the + whitespace and comments, so that the parser actions do not have to + think about those, and keeps track of some things like the indentation + of the current line and the column at which the current token ends, + which are stored in the lexical context and used for indentation. + After calling an action, if the action called cont, this driver + function will return the current token, if pass (or nothing) was + called, it will immediately continue with the next action.

+ +

This goes to show that it is viable to write a quite elaborate + minilanguage in a macro-less language like JavaScript. I don't think + it would be possible to do something like this without closures (or + similarly powerful abstraction) though, I've certainly never seen + anything like it in Java code.

+ +

The way a 'copy' of the parser was produced shows a nice usage + of closures. Like with the DOM transformer shown above, most of + the local state of the parser is held in a closure produced by + calling parse(stream). The function + copy, which is local to the parser function, produces + a new closure, with copies of all the relevant variables:

+ +
function copy(){
+  var _context = context, _lexical = lexical, _actions = copyArray(actions);
+
+  return function(_tokens){
+    context = _context;
+    lexical = _lexical;
+    actions = copyArray(_actions);
+    tokens = _tokens;
+    return parser;
+  };
+}
+ +

Where parser is the object that contains the next (driver) + function, and a reference to this copy function. When the function + that copy produces is called with a token stream as argument, it + updates the local variables in the parser closure, and returns the + corresponding iterator object.

+ +

Moving on, we get to the last stop in this chain of generators, the + actual highlighter. You can view this one as taking two streams as + input, on the one hand there is the stream of tokens from the parser, + and on the other hand there is the DOM tree as left by the DOM + transformer. If everything went correctly, these two should be + synchronized. The highlighter can look at the current token, see if + the span in the DOM tree corresponds to it (has the same text + content, and the correct class), and if not it can chop up the DOM + nodes to conform to the tokens.

+ +

Every time the parser yields a newline token, the highligher + encounters a br element in the DOM stream. It takes the copy of the + parser and the lexical context from this token and attaches them to + the DOM node. This way, a new highlighting process can be started from + that node by re-starting the copy of the parser with a new token + stream, which reads tokens from the DOM nodes starting at that br + element, and the indentation code can use the lexical context + information to determine the correct indentation at that point.

+ +

Selection woes

+ +

All the above can be done using the DOM interface that all major + browsers have in common, and which is relatively free of weird bugs + and abberrations. However, when the user is typing in new code, this + must also be highlighted. For this to happen, the program must know + where the cursor currently is, and because it mucks up the DOM tree, + it has to restore this cursor position after doing the highlighting.

+ +

Re-highlighting always happens per line, because the copy of the + parser is stored only at the end of lines. Doing this every time the + user presses a key is terribly slow and obnoxious, so what I did was + keep a list of 'dirty' nodes, and as soon as the user didn't type + anyting for 300 milliseconds the program starts re-highlighting these + nodes. If it finds more than ten lines must be re-parsed, it does only + ten and waits another 300 milliseconds before it continues, this way + the browser never freezes up entirely.

+ +

As mentioned earlier, Internet Explorer's selection model is not the + most practical one. My attempts to build a wrapper that makes it look + like the W3C model all stranded. In the end I came to the conclusion + that I only needed two operations:

+ +
    +
  • Creating a selection 'snapshot' that can be restored after + highlighting, in such a way that it still works if some of the nodes + that were selected are replaced by other nodes with the same + size but a different structure.
  • +
  • Finding the top-level node around or before the cursor, to mark it + dirty or to insert indentation whitespace at the start of that line.
  • +
+ +

It turns out that the pixel-based selection model that Internet + Explorer uses, which always seemed completely ludricrous to me, is + perfect for the first case. Since the DOM transformation (generally) + does not change the position of things, storing the pixel offsets of + the selection makes it possible to restore that same selection, never + mind what happened to the underlying DOM structure.

+ +

[Later addition: Note that this, due to the very random design + of the TextRange + interface, only really works when the whole selection falls + within the visible part of the document.]

+ +

Doing the same with the W3C selection model is a lot harder. What I + ended up with was this:

+ +
    +
  • Create an object pointing to the nodes at the start and end of the + selection, and the offset within those nodes. This is basically the + information that the Range object gives you.
  • +
  • Make references from these nodes back to that object.
  • +
  • When replacing (part of) a node with another one, check for such a + reference, and when it is present, check whether this new node will + get the selection. If it does, move the reference from the old to the + new node, if it does not, adjust the offset in the selection object to + reflect the fact that part of the old node has been replaced.
  • +
+ +

Now in the second case (getting the top-level node at the + cursor) the Internet Explorer cheat does not work. In the W3C + model this is rather easy, you have to do some creative parent- + and sibling-pointer following to arrive at the correct top-level + node, but nothing weird. In Internet Explorer, all we have to go + on is the parentElement method on a + TextRange, which gives the first element that + completely envelops the selection. If the cursor is inside a text + node, this is good, that text node tells us where we are. If the + cursor is between nodes, for example between two br + nodes, you get to top-level node itself back, which is remarkably + useless. In cases like this I stoop to a rather ugly hack (which + fortunately turned out to be acceptably fast) ― I create a + temporary empty span with an ID inside the selection, + get a reference to this span by ID, take its + previousSibling, and remove it again.

+ +

Unfortunately, Opera's selection implementation is buggy, and it + will give wildly incorrect Range objects when the cursor + is between two nodes. This is a bit of a showstopper, and until I find + a workaround for that or it gets fixed, the highlighter doesn't work + properly in Opera.

+ +

Also, when one presses enter in a designMode + document in Firefox or Opera, a br tag is inserted. + In Internet Explorer, pressing enter causes some maniacal gnome to + come out and start wrapping all the content before and after the + cursor in p tags. I suppose there is something to be + said for that, in principle, though if you saw the tag soup of + fonts and nested paragraphs Internet Explorer + generates you would soon enough forget all about principle. + Anyway, getting unwanted p tags slowed the + highlighter down terribly ― it had to overhaul the whole + DOM tree to remove them again, every time the user pressed enter. + Fortunately I could fix this by capturing the enter presses and + manually inserting a br tag at the cursor.

+ +

On the subject of Internet Explorer's tag soup, here is an interesting + anecdote: One time, when testing the effect that modifying the content + of a selection had, I inspected the DOM tree and found a "/B" + element. This was not a closing tag, there are no closing tags in the + DOM tree, just elements. The nodeName of this element was actually + "/B". That was when I gave up any notions of ever understanding the + profound mystery that is Internet Explorer.

+ +

Closing thoughts

+ +

Well, I despaired at times, but I did end up with a working JavaScript + editor. I did not keep track of the amount of time I wasted on this, + but I would estimate it to be around fifty hours. Finding workarounds + for browser bugs can be a terribly nonlinear process. I just spent + half a day working on a weird glitch in Firefox that caused the cursor + in the editable frame to be displayed 3/4 line too high when it was at + the very end of the document. Then I found out that setting the + style.display of the iframe to "block" fixed this (why not?). I'm + amazed how often issues that seem hopeless do turn out to be + avoidable, even if it takes hours of screwing around and some truly + non-obvious ideas.

+ +

For a lot of things, JavaScript + DOM elements are a surprisingly + powerful platform. Simple interactive documents and forms can be + written in browsers with very little effort, generally less than with + most 'traditional' platforms (Java, Win32, things like WxWidgets). + Libraries like Dojo (and a similar monster I once wrote myself) even + make complex, composite widgets workable. However, when applications + go sufficiently beyond the things that browsers were designed for, the + available APIs do not give enough control, are nonstandard and buggy, + and are often poorly designed. Because of this, writing such + applications, when it is even possible, is painful process.

+ +

And who likes pain? Sure, when finding that crazy workaround, + subdueing the damn browser, and getting everything to work, there + is a certain macho thrill. But one can't help wondering how much + easier things like preventing the user from pasting pictures in + his source code would be on another platform. Maybe something like + Silverlight or whatever other new browser plugin gizmos people are + pushing these days will become the way to solve things like this + in the future. But, personally, I would prefer for those browser + companies to put some real effort into things like cleaning up and + standardising shady things like designMode, fixing + their bugs, and getting serious about ECMAScript 4.

+ +

Which is probably not realistically going to happen anytime soon.

+ +
+ +

Some interesting projects similar to this:

+ + + +
+ +

If you have any remarks, criticism, or hints related to the + above, drop me an e-mail at marijnh@gmail.com. If you say + something generally interesting, I'll include your reaction here + at the bottom of this page.

+ + +