From 3cedb3bef6937a09fe7f016c89d7c7e0fb77a2b0 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 15 Jan 2011 17:31:19 +0100 Subject: [PATCH 01/60] add old worker experiments --- experiments/worker.html | 33 ++++++++ experiments/worker.js | 3 + lib/ace/WorkerTokenizer.js | 91 +++++++++++++++++++++ lib/ace/worker.js | 163 +++++++++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 experiments/worker.html create mode 100644 experiments/worker.js create mode 100644 lib/ace/WorkerTokenizer.js create mode 100644 lib/ace/worker.js diff --git a/experiments/worker.html b/experiments/worker.html new file mode 100644 index 00000000..f3c75bde --- /dev/null +++ b/experiments/worker.html @@ -0,0 +1,33 @@ + + + + + + worker + + + + + + + + + + diff --git a/experiments/worker.js b/experiments/worker.js new file mode 100644 index 00000000..1335f55e --- /dev/null +++ b/experiments/worker.js @@ -0,0 +1,3 @@ +onmessage = function(e) { + onmessage = new Function("e", e.data); +}; \ No newline at end of file diff --git a/lib/ace/WorkerTokenizer.js b/lib/ace/WorkerTokenizer.js new file mode 100644 index 00000000..743f9936 --- /dev/null +++ b/lib/ace/WorkerTokenizer.js @@ -0,0 +1,91 @@ +/** + * Ajax.org Code Editor (ACE) + * + * @copyright 2010, Ajax.org Services B.V. + * @license LGPLv3 + * @author Fabian Jakobs + */ +require.def("ace/WorkerTokenizer", ["ace/lib/oop", "ace/MEventEmitter"], function(oop, MEventEmitter) { + +var WorkerTokenizer = function(tokenizer) { + var workerUrl = require.nameToUrl("ace/worker", null, "_"); + this.$worker = new Worker(workerUrl); + + this.callbackId = 1; + this.callbacks = {}; + + var _self = this; + this.$worker.onerror = function(e) { + throw e; + }; + this.$worker.onmessage = function(e) { + var msg = e.data; + //console.log("rec", msg) + switch(msg.type) { + case "log": + console.log(msg.data); + break; + + case "event": + _self.$dispatchEvent(msg.name, {data: msg.data}); + break; + + case "call": + var callback = _self.callbacks[msg.id]; + if (callback) { + callback(msg.data); + delete _self.callbacks[msg.id]; + } + break; + } + }; +}; + +(function(){ + + oop.implement(this, MEventEmitter); + + this.$send = function(cmd, args) { + this.$worker.postMessage({command: cmd, args: args}); + }; + + this.$call = function(cmd, args, callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + this.$send(cmd, args); + }; + + this.setTokenizer = function(tokenizer) { + this.$send("setRules", ["ace/mode/JavaScriptHighlightRules"]); + }; + + this.setLines = function(textLines) { + this.lines = textLines; + this.$send("setLines", [textLines]); + }; + + this.start = function(startRow) { + // TODO don't send all lines on each update!! + this.$send("setLines", [this.lines]); + this.$send("start", [startRow]) + }; + + this.stop = function() { + this.$send("stop", []) + }; + + this.getTokens = function(firstRow, lastRow, callback) { + this.$call("getTokens", [firstRow, lastRow], function(tokens) { + callback(tokens) + }); + }; + + this.getState = function(row, callback) { + this.$call("getState", [row], callback); + }; + +}).call(WorkerTokenizer.prototype); + +return WorkerTokenizer; +}); diff --git a/lib/ace/worker.js b/lib/ace/worker.js new file mode 100644 index 00000000..f90819f1 --- /dev/null +++ b/lib/ace/worker.js @@ -0,0 +1,163 @@ +postMessage("Juhu Kinners"); + +var console = { + log: function(msg) { + postMessage({type: "log", data: msg}); + } +}; +var window = { + console: console +}; + +var require = function(name) { + if (require.modules[name]) + return require.modules[name]; + + importScripts(require.baseUrl + "/" + name + ".js"); + return require.modules[name]; +}; + +require.def = function(name, deps, callback) { + if (!callback) { + callback = deps; + deps = []; + } + var modules = deps.map(function(dep) { + return require(dep); + }); + require.modules[name] = callback.apply(this, modules); +}; + +require.baseUrl = ".."; +require.modules = {}; + +var Tokenizer = require("ace/Tokenizer"); + +var bgtokenizer = { + running: false, + textLines: [], + lines: [], + currentLine: 0, + tokenizer: null, + + init: function() { + var self = this; + + this.$worker = function() { + if (!self.running) { return; } + + var workerStart = new Date(); + var startLine = self.currentLine; + var textLines = self.textLines; + + var processedLines = 0; + + while (self.currentLine < textLines.length) { + self.lines[self.currentLine] = self.$tokenizeRows(self.currentLine, self.currentLine)[0]; + self.currentLine++; + + // only check every 5 lines + processedLines += 1; + if ((processedLines % 5 == 0) && (new Date() - workerStart) > 40) { + self.$event("update", {first: startLine, last: self.currentLine-1}); + self.running = setTimeout(self.$worker, 0); + return; + } + } + + self.running = false; + self.$event("update", {first: startLine, last: textLines.length - 1}); + }; + }, + + setRules: function(rules) { + var Rules = require(rules); + this.tokenizer = new Tokenizer(new Rules().getRules()); + this.lines = []; + + this.start(0); + }, + + setLines: function(textLines) { + this.textLines = textLines; + this.lines = []; + + this.stop(); + }, + + start: function(startRow) { + this.currentLine = Math.min(startRow || 0, this.currentLine, + this.textLines.length); + + // remove all cached items below this line + this.lines.splice(this.currentLine, this.lines.length); + + this.stop(); + this.running = setTimeout(this.$worker, 0); + }, + + stop: function() { + if (this.running) + clearTimeout(this.running); + + this.running = false; + }, + + getTokens: function(firstRow, lastRow, callbackId) { + this.$callback(this.$tokenizeRows(firstRow, lastRow), callbackId); + }, + + getState: function(row, callbackId) { + this.$callback(this.$tokenizeRows(row, row)[0].state, callbackId); + }, + + $tokenizeRows: function(firstRow, lastRow) { + var rows = []; + + // determin start state + var state = "start"; + var doCache = false; + if (firstRow > 0 && this.lines[firstRow - 1]) { + state = this.lines[firstRow - 1].state; + doCache = true; + } + + for (var row=firstRow; row<=lastRow; row++) { + if (!this.lines[row]) { + var tokens = this.tokenizer.getLineTokens(this.textLines[row] || "", state); + var state = tokens.state; + rows.push(tokens); + + if (doCache) { + this.lines[row] = tokens; + } + } + else + rows.push(this.lines[row]); + } + return rows; + }, + + $callback: function(data, callbackId) { + postMessage({ + type: "call", + id: callbackId, + data: data + }); + }, + + $event: function(name, data) { + postMessage({ + type: "event", + name: name, + data: data + }); + } +}; + +bgtokenizer.init(); + +var onmessage = function(e) { + var msg = e.data; + bgtokenizer[msg.command].apply(bgtokenizer, msg.args); +}; \ No newline at end of file From 06960222180aaba3f96284ec0c2255e1ee340bb9 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Mon, 17 Jan 2011 08:44:53 +0100 Subject: [PATCH 02/60] add generic worker infrastructure --- demo/startup.js | 5 + lib/ace/WorkerTokenizer.js | 91 ------------------ lib/ace/worker.js | 163 --------------------------------- lib/ace/worker/Demo.js | 16 ++++ lib/ace/worker/WorkerClient.js | 90 ++++++++++++++++++ lib/ace/worker/host.js | 97 ++++++++++++++++++++ 6 files changed, 208 insertions(+), 254 deletions(-) delete mode 100644 lib/ace/WorkerTokenizer.js delete mode 100644 lib/ace/worker.js create mode 100644 lib/ace/worker/Demo.js create mode 100644 lib/ace/worker/WorkerClient.js create mode 100644 lib/ace/worker/host.js diff --git a/demo/startup.js b/demo/startup.js index b2e473a8..7f32c255 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -58,12 +58,17 @@ exports.launch = function(env) { var vim = require("ace/keyboard/keybinding/vim").Vim; var emacs = require("ace/keyboard/keybinding/emacs").Emacs; var HashHandler = require("ace/keyboard/hash_handler").HashHandler; + + var WorkerClient = require("ace/worker/WorkerClient").WorkerClient; var docs = {}; docs.js = new Document(document.getElementById("jstext").innerHTML); docs.js.setMode(new JavaScriptMode()); docs.js.setUndoManager(new UndoManager()); + + var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/demo", "Demo"); + docs.css = new Document(document.getElementById("csstext").innerHTML); docs.css.setMode(new CssMode()); diff --git a/lib/ace/WorkerTokenizer.js b/lib/ace/WorkerTokenizer.js deleted file mode 100644 index 743f9936..00000000 --- a/lib/ace/WorkerTokenizer.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Ajax.org Code Editor (ACE) - * - * @copyright 2010, Ajax.org Services B.V. - * @license LGPLv3 - * @author Fabian Jakobs - */ -require.def("ace/WorkerTokenizer", ["ace/lib/oop", "ace/MEventEmitter"], function(oop, MEventEmitter) { - -var WorkerTokenizer = function(tokenizer) { - var workerUrl = require.nameToUrl("ace/worker", null, "_"); - this.$worker = new Worker(workerUrl); - - this.callbackId = 1; - this.callbacks = {}; - - var _self = this; - this.$worker.onerror = function(e) { - throw e; - }; - this.$worker.onmessage = function(e) { - var msg = e.data; - //console.log("rec", msg) - switch(msg.type) { - case "log": - console.log(msg.data); - break; - - case "event": - _self.$dispatchEvent(msg.name, {data: msg.data}); - break; - - case "call": - var callback = _self.callbacks[msg.id]; - if (callback) { - callback(msg.data); - delete _self.callbacks[msg.id]; - } - break; - } - }; -}; - -(function(){ - - oop.implement(this, MEventEmitter); - - this.$send = function(cmd, args) { - this.$worker.postMessage({command: cmd, args: args}); - }; - - this.$call = function(cmd, args, callback) { - var id = this.callbackId++; - this.callbacks[id] = callback; - args.push(id); - this.$send(cmd, args); - }; - - this.setTokenizer = function(tokenizer) { - this.$send("setRules", ["ace/mode/JavaScriptHighlightRules"]); - }; - - this.setLines = function(textLines) { - this.lines = textLines; - this.$send("setLines", [textLines]); - }; - - this.start = function(startRow) { - // TODO don't send all lines on each update!! - this.$send("setLines", [this.lines]); - this.$send("start", [startRow]) - }; - - this.stop = function() { - this.$send("stop", []) - }; - - this.getTokens = function(firstRow, lastRow, callback) { - this.$call("getTokens", [firstRow, lastRow], function(tokens) { - callback(tokens) - }); - }; - - this.getState = function(row, callback) { - this.$call("getState", [row], callback); - }; - -}).call(WorkerTokenizer.prototype); - -return WorkerTokenizer; -}); diff --git a/lib/ace/worker.js b/lib/ace/worker.js deleted file mode 100644 index f90819f1..00000000 --- a/lib/ace/worker.js +++ /dev/null @@ -1,163 +0,0 @@ -postMessage("Juhu Kinners"); - -var console = { - log: function(msg) { - postMessage({type: "log", data: msg}); - } -}; -var window = { - console: console -}; - -var require = function(name) { - if (require.modules[name]) - return require.modules[name]; - - importScripts(require.baseUrl + "/" + name + ".js"); - return require.modules[name]; -}; - -require.def = function(name, deps, callback) { - if (!callback) { - callback = deps; - deps = []; - } - var modules = deps.map(function(dep) { - return require(dep); - }); - require.modules[name] = callback.apply(this, modules); -}; - -require.baseUrl = ".."; -require.modules = {}; - -var Tokenizer = require("ace/Tokenizer"); - -var bgtokenizer = { - running: false, - textLines: [], - lines: [], - currentLine: 0, - tokenizer: null, - - init: function() { - var self = this; - - this.$worker = function() { - if (!self.running) { return; } - - var workerStart = new Date(); - var startLine = self.currentLine; - var textLines = self.textLines; - - var processedLines = 0; - - while (self.currentLine < textLines.length) { - self.lines[self.currentLine] = self.$tokenizeRows(self.currentLine, self.currentLine)[0]; - self.currentLine++; - - // only check every 5 lines - processedLines += 1; - if ((processedLines % 5 == 0) && (new Date() - workerStart) > 40) { - self.$event("update", {first: startLine, last: self.currentLine-1}); - self.running = setTimeout(self.$worker, 0); - return; - } - } - - self.running = false; - self.$event("update", {first: startLine, last: textLines.length - 1}); - }; - }, - - setRules: function(rules) { - var Rules = require(rules); - this.tokenizer = new Tokenizer(new Rules().getRules()); - this.lines = []; - - this.start(0); - }, - - setLines: function(textLines) { - this.textLines = textLines; - this.lines = []; - - this.stop(); - }, - - start: function(startRow) { - this.currentLine = Math.min(startRow || 0, this.currentLine, - this.textLines.length); - - // remove all cached items below this line - this.lines.splice(this.currentLine, this.lines.length); - - this.stop(); - this.running = setTimeout(this.$worker, 0); - }, - - stop: function() { - if (this.running) - clearTimeout(this.running); - - this.running = false; - }, - - getTokens: function(firstRow, lastRow, callbackId) { - this.$callback(this.$tokenizeRows(firstRow, lastRow), callbackId); - }, - - getState: function(row, callbackId) { - this.$callback(this.$tokenizeRows(row, row)[0].state, callbackId); - }, - - $tokenizeRows: function(firstRow, lastRow) { - var rows = []; - - // determin start state - var state = "start"; - var doCache = false; - if (firstRow > 0 && this.lines[firstRow - 1]) { - state = this.lines[firstRow - 1].state; - doCache = true; - } - - for (var row=firstRow; row<=lastRow; row++) { - if (!this.lines[row]) { - var tokens = this.tokenizer.getLineTokens(this.textLines[row] || "", state); - var state = tokens.state; - rows.push(tokens); - - if (doCache) { - this.lines[row] = tokens; - } - } - else - rows.push(this.lines[row]); - } - return rows; - }, - - $callback: function(data, callbackId) { - postMessage({ - type: "call", - id: callbackId, - data: data - }); - }, - - $event: function(name, data) { - postMessage({ - type: "event", - name: name, - data: data - }); - } -}; - -bgtokenizer.init(); - -var onmessage = function(e) { - var msg = e.data; - bgtokenizer[msg.command].apply(bgtokenizer, msg.args); -}; \ No newline at end of file diff --git a/lib/ace/worker/Demo.js b/lib/ace/worker/Demo.js new file mode 100644 index 00000000..7d1fd5df --- /dev/null +++ b/lib/ace/worker/Demo.js @@ -0,0 +1,16 @@ + +define(function(require, exports, module) { + +exports.Demo = function(sender) { + this.sender = sender; +} + +(function() { + + this.juhu = function() { + console.log("JUHU") + } + +}).call(exports.Demot.prototype); + +}); \ No newline at end of file diff --git a/lib/ace/worker/WorkerClient.js b/lib/ace/worker/WorkerClient.js new file mode 100644 index 00000000..00a633c6 --- /dev/null +++ b/lib/ace/worker/WorkerClient.js @@ -0,0 +1,90 @@ +/** + * Ajax.org Code Editor (ACE) + * + * @copyright 2010, Ajax.org Services B.V. + * @license LGPLv3 + * @author Fabian Jakobs + */ + +define(function(require, exports, module) { + +var oop = require("pilot/oop"); +var EventEmitter = require("pilot/event_emitter").EventEmitter; + +var WorkerClient = function(baseUrl, topLevelNamespaces, module, clazz) { + + var workerUrl = require.nameToUrl("ace/worker/host", null, "_"); + var worker = this.$worker = new Worker(workerUrl); + + //context = s.contexts[contextName], + //config = context.config; + //debugger; + + console.log(require.nameToUrl("ace", null, "_")) + console.log(require.nameToUrl("pilot", null, "_")) + + var tlns = {}; + for (var i=0; i Date: Mon, 17 Jan 2011 08:45:15 +0100 Subject: [PATCH 03/60] add old experiments --- experiments/capture.html | 45 +++++++++++++++ experiments/cut_copy.html | 105 ++++++++++++++++++++++++++++++++++ experiments/triple_click.html | 34 +++++++++++ 3 files changed, 184 insertions(+) create mode 100644 experiments/capture.html create mode 100644 experiments/cut_copy.html create mode 100644 experiments/triple_click.html diff --git a/experiments/capture.html b/experiments/capture.html new file mode 100644 index 00000000..9df5e3cd --- /dev/null +++ b/experiments/capture.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + +
+ +
+ + + + + diff --git a/experiments/cut_copy.html b/experiments/cut_copy.html new file mode 100644 index 00000000..3299f3ad --- /dev/null +++ b/experiments/cut_copy.html @@ -0,0 +1,105 @@ + + + + + + Text Events + + + + + + + +
+ +
+
+ + +
+ +
+ + + + + diff --git a/experiments/triple_click.html b/experiments/triple_click.html new file mode 100644 index 00000000..da953a94 --- /dev/null +++ b/experiments/triple_click.html @@ -0,0 +1,34 @@ + + + + + + triple_click + + + + + +
+ Juhu Kinners +
+ + + + + + + From d2b141b68e184411e4052f883c77f7095c376f3c Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 18 Jan 2011 09:01:22 +0100 Subject: [PATCH 04/60] cleanup worker basics --- demo/startup.js | 2 +- lib/ace/worker/Demo.js | 6 +++--- lib/ace/worker/WorkerClient.js | 14 +++----------- lib/ace/worker/host.js | 17 +++++------------ 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/demo/startup.js b/demo/startup.js index 7f32c255..41d8321f 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -68,7 +68,7 @@ exports.launch = function(env) { docs.js.setUndoManager(new UndoManager()); var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/demo", "Demo"); - + worker.send("juhu"); docs.css = new Document(document.getElementById("csstext").innerHTML); docs.css.setMode(new CssMode()); diff --git a/lib/ace/worker/Demo.js b/lib/ace/worker/Demo.js index 7d1fd5df..7d3b0689 100644 --- a/lib/ace/worker/Demo.js +++ b/lib/ace/worker/Demo.js @@ -1,9 +1,9 @@ define(function(require, exports, module) { -exports.Demo = function(sender) { +var Demo = exports.Demo = function(sender) { this.sender = sender; -} +}; (function() { @@ -11,6 +11,6 @@ exports.Demo = function(sender) { console.log("JUHU") } -}).call(exports.Demot.prototype); +}).call(Demo.prototype); }); \ No newline at end of file diff --git a/lib/ace/worker/WorkerClient.js b/lib/ace/worker/WorkerClient.js index 00a633c6..dd18abea 100644 --- a/lib/ace/worker/WorkerClient.js +++ b/lib/ace/worker/WorkerClient.js @@ -11,31 +11,24 @@ define(function(require, exports, module) { var oop = require("pilot/oop"); var EventEmitter = require("pilot/event_emitter").EventEmitter; -var WorkerClient = function(baseUrl, topLevelNamespaces, module, clazz) { +var WorkerClient = function(baseUrl, topLevelNamespaces, module, classname) { + this.callbacks = []; var workerUrl = require.nameToUrl("ace/worker/host", null, "_"); var worker = this.$worker = new Worker(workerUrl); - //context = s.contexts[contextName], - //config = context.config; - //debugger; - - console.log(require.nameToUrl("ace", null, "_")) - console.log(require.nameToUrl("pilot", null, "_")) - var tlns = {}; for (var i=0; i Date: Tue, 18 Jan 2011 09:01:29 +0100 Subject: [PATCH 05/60] add jslint --- lib/ace/worker/jslint.js | 5747 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 5747 insertions(+) create mode 100644 lib/ace/worker/jslint.js diff --git a/lib/ace/worker/jslint.js b/lib/ace/worker/jslint.js new file mode 100644 index 00000000..a36dd16e --- /dev/null +++ b/lib/ace/worker/jslint.js @@ -0,0 +1,5747 @@ +// jslint.js +// 2011-01-09 + +/* +Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + JSLINT is a global function. It takes two parameters. + + var myResult = JSLINT(source, option); + + The first parameter is either a string or an array of strings. If it is a + string, it will be split on '\n' or '\r'. If it is an array of strings, it + is assumed that each string represents one line. The source can be a + JavaScript text, or HTML text, or a JSON text, or a CSS text. + + The second parameter is an optional object of options which control the + operation of JSLINT. Most of the options are booleans: They are all + optional and have a default value of false. One of the options, predef, + can be an array of names, which will be used to declare global variables, + or an object whose keys are used as global names, with a boolean value + that determines if they are assignable. + + If it checks out, JSLINT returns true. Otherwise, it returns false. + + If false, you can inspect JSLINT.errors to find out the problems. + JSLINT.errors is an array of objects containing these members: + + { + line : The line (relative to 0) at which the lint was found + character : The character (relative to 0) at which the lint was found + reason : The problem + evidence : The text line in which the problem occurred + raw : The raw message before the details were inserted + a : The first detail + b : The second detail + c : The third detail + d : The fourth detail + } + + If a fatal error was found, a null will be the last element of the + JSLINT.errors array. + + You can request a Function Report, which shows all of the functions + and the parameters and vars that they use. This can be used to find + implied global variables and other problems. The report is in HTML and + can be inserted in an HTML . + + var myReport = JSLINT.report(limited); + + If limited is true, then the report will be limited to only errors. + + You can request a data structure which contains JSLint's results. + + var myData = JSLINT.data(); + + It returns a structure with this form: + + { + errors: [ + { + line: NUMBER, + character: NUMBER, + reason: STRING, + evidence: STRING + } + ], + functions: [ + name: STRING, + line: NUMBER, + last: NUMBER, + param: [ + STRING + ], + closure: [ + STRING + ], + var: [ + STRING + ], + exception: [ + STRING + ], + outer: [ + STRING + ], + unused: [ + STRING + ], + global: [ + STRING + ], + label: [ + STRING + ] + ], + globals: [ + STRING + ], + member: { + STRING: NUMBER + }, + unuseds: [ + { + name: STRING, + line: NUMBER + } + ], + implieds: [ + { + name: STRING, + line: NUMBER + } + ], + urls: [ + STRING + ], + json: BOOLEAN + } + + Empty arrays will not be included. + + You can obtain the parse tree that JSLint constructed while parsing. The + latest tree is kept in JSLINT.tree. A nice stringication can be produced + with + + JSON.stringify(JSLINT.tree, [ + 'value', 'arity', 'name', 'first', + 'second', 'third', 'block', 'else' + ], 4)); + +*/ + +/*jslint + evil: true, nomen: false, onevar: false, regexp: false, strict: true +*/ + +/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "'", "%", + "(begin)", "(breakage)", "(context)", "(end)", "(error)", "(global)", + "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)", + "(params)", "(scope)", "(statement)", "(token)", "(verb)", "*", "+", "++", "-", + "--", "\/", "<", "<=", "", ">=", + ADSAFE, ActiveXObject, Array, Boolean, COM, CScript, Canvas, + CustomAnimation, Date, Debug, E, Enumerator, Error, EvalError, + FadeAnimation, Flash, FormField, Frame, Function, HotKey, Image, JSON, + LN10, LN2, LOG10E, LOG2E, MAX_VALUE, MIN_VALUE, Math, MenuItem, + MoveAnimation, NEGATIVE_INFINITY, Number, Object, Option, PI, + POSITIVE_INFINITY, Point, RangeError, Rectangle, ReferenceError, RegExp, + ResizeAnimation, RotateAnimation, SQRT1_2, SQRT2, ScrollBar, String, + Style, SyntaxError, System, Text, TextArea, Timer, TypeError, URIError, + URL, VBArray, WScript, Web, Window, XMLDOM, XMLHttpRequest, "\\", a, + abbr, acronym, activeborder, activecaption, addEventListener, address, + adsafe, alert, aliceblue, all, animator, antiquewhite, appleScript, + applet, apply, approved, appworkspace, aqua, aquamarine, area, + arguments, arity, article, aside, assign, audio, autocomplete, azure, b, + background, "background-attachment", "background-color", + "background-image", "background-position", "background-repeat", base, + bdo, beep, beige, big, bisque, bitwise, black, blanchedalmond, block, + blockquote, blue, blueviolet, blur, body, border, "border-bottom", + "border-bottom-color", "border-bottom-style", "border-bottom-width", + "border-collapse", "border-color", "border-left", "border-left-color", + "border-left-style", "border-left-width", "border-right", + "border-right-color", "border-right-style", "border-right-width", + "border-spacing", "border-style", "border-top", "border-top-color", + "border-top-style", "border-top-width", "border-width", bottom, br, + braille, brown, browser, burlywood, button, buttonface, buttonhighlight, + buttonshadow, buttontext, bytesToUIString, c, cadetblue, call, callee, + caller, canvas, cap, caption, "caption-side", captiontext, case, catch, + center, charAt, charCodeAt, character, chartreuse, chocolate, + chooseColor, chooseFile, chooseFolder, cite, clear, clearInterval, + clearTimeout, clip, close, closeWidget, closed, closure, cm, code, col, + colgroup, color, command, comment, confirm, console, constructor, + content, convertPathToHFS, convertPathToPlatform, coral, cornflowerblue, + cornsilk, "counter-increment", "counter-reset", create, crimson, css, + cursor, cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen, + darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred, + darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise, + darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent, + deeppink, deepskyblue, default, defaultStatus, defineClass, del, + deserialize, details, devel, dfn, dialog, dimgray, dir, direction, + display, disrupt, div, dl, do, document, dodgerblue, + dt, edition, else, em, embed, embossed, empty, "empty-cells", encodeURI, + encodeURIComponent, entityify, errors, es5, escape, eval, event, + evidence, evil, ex, exception, exec, fieldset, figure, filesystem, + finally, firebrick, first, float, floor, floralwhite, focus, + focusWidget, font, "font-family", "font-size", "font-size-adjust", + "font-stretch", "font-style", "font-variant", "font-weight", footer, + for, forestgreen, forin, form, fragment, frame, frames, frameset, from, + fromCharCode, fuchsia, fud, funct, function, functions, g, gainsboro, + gc, getComputedStyle, ghostwhite, global, globals, gold, goldenrod, + gray, graytext, green, greenyellow, h1, h2, h3, h4, h5, h6, handheld, + hasOwnProperty, head, header, height, help, hgroup, highlight, + highlighttext, history, honeydew, hotpink, hr, "hta:application", html, + i, iTunes, id, identifier, iframe, img, immed, implieds, in, + inactiveborder, inactivecaption, inactivecaptiontext, include, indent, + indexOf, indianred, indigo, infobackground, infotext, init, initial, + input, ins, isAlpha, isApplicationRunning, isArray, isDigit, isFinite, + isNaN, ivory, join, jslint, json, kbd, keygen, keys, khaki, + konfabulatorVersion, label, lang, last, lavender, lavenderblush, + lawngreen, lbp, led, left, legend, lemonchiffon, length, + "letter-spacing", li, lib, lightblue, lightcoral, lightcyan, + lightgoldenrodyellow, lightgreen, lightpink, lightsalmon, lightseagreen, + lightskyblue, lightslategray, lightsteelblue, lightyellow, lime, + limegreen, line, "line-height", linen, link, "list-style", + "list-style-image", "list-style-position", "list-style-type", load, + loadClass, location, log, m, magenta, map, margin, "margin-bottom", + "margin-left", "margin-right", "margin-top", mark, "marker-offset", + maroon, match, "max-height", "max-width", maxerr, maxlen, md5, + mediumaquamarine, mediumblue, mediumorchid, mediumpurple, + mediumseagreen, mediumslateblue, mediumspringgreen, mediumturquoise, + mediumvioletred, member, menu, menutext, message, meta, meter, + midnightblue, "min-height", "min-width", mintcream, mistyrose, mm, + moccasin, moveBy, moveTo, name, nav, navajowhite, navigator, navy, new, + newcap, noframes, nomen, noscript, nud, object, ol, oldlace, olive, + olivedrab, on, onbeforeunload, onblur, onerror, onevar, onfocus, onload, + onresize, onunload, opacity, open, openURL, opener, opera, optgroup, + option, orange, orangered, orchid, outer, outline, "outline-color", + "outline-style", "outline-width", output, overflow, "overflow-x", + "overflow-y", p, padding, "padding-bottom", "padding-left", + "padding-right", "padding-top", "page-break-after", "page-break-before", + palegoldenrod, palegreen, paleturquoise, palevioletred, papayawhip, + param, parent, parseFloat, parseInt, passfail, pc, peachpuff, peru, + pink, play, plum, plusplus, pop, popupMenu, position, powderblue, pre, + predef, preferenceGroups, preferences, print, progress, projection, + prompt, prototype, pt, purple, push, px, q, quit, quotes, random, range, + raw, readFile, readUrl, reason, red, regexp, reloadWidget, + removeEventListener, replace, report, reserved, resizeBy, resizeTo, + resolvePath, resumeUpdates, rhino, right, rosybrown, royalblue, rp, rt, + ruby, runCommand, runCommandInBg, saddlebrown, safe, salmon, samp, + sandybrown, saveAs, savePreferences, screen, script, scroll, scrollBy, + scrollTo, scrollbar, seagreen, seal, search, seashell, second, section, + select, serialize, setInterval, setTimeout, shift, + showWidgetPreferences, sienna, silver, skyblue, slateblue, slategray, + sleep, slice, small, snow, sort, source, span, spawn, speak, speech, + split, springgreen, src, stack, status, steelblue, strict, strong, + style, styleproperty, sub, substr, sup, supplant, suppressUpdates, + switch, sync, system, table, "table-layout", tan, tbody, td, teal, + tellWidget, test, "text-align", "text-decoration", "text-indent", + "text-shadow", "text-transform", textarea, tfoot, th, thead, third, + thistle, threeddarkshadow, threedface, threedhighlight, + threedlightshadow, threedshadow, thru, time, title, toLowerCase, toString, + toUpperCase, toint32, token, tomato, top, tr, tree, tt, tty, turquoise, tv, + type, u, ul, undef, unescape, "unicode-bidi", unused, unwatch, + updateNow, urls, value, valueOf, var, version, "vertical-align", video, + violet, visibility, watch, wheat, while, white, "white-space", + whitesmoke, widget, width, window, windowframe, windows, windowtext, + "word-spacing", "word-wrap", yahooCheckLogin, yahooLogin, yahooLogout, + yellow, yellowgreen, "z-index", "}" +*/ + +// We build the application inside a function so that we produce only a single +// global variable. That function will be invoked immediately, and its return +// value is the JSLINT function itself. That function is also an object that +// can contain data and other functions. + +define(function(require, exports, module) { + +exports.lint = (function () { + "use strict"; + + var adsafe_id, // The widget's ADsafe id. + adsafe_may, // The widget may load approved scripts. + adsafe_went, // ADSAFE.go has been called. + anonname, // The guessed name for anonymous functions. + approved, // ADsafe approved urls. + +// These are operators that should not be used with the ! operator. + + bang = { + '<': true, + '<=': true, + '==': true, + '===': true, + '!==': true, + '!=': true, + '>': true, + '>=': true, + '+': true, + '-': true, + '*': true, + '/': true, + '%': true + }, + +// These are property names that should not be permitted in the safe subset. + + banned = { // the member names that ADsafe prohibits. + 'arguments' : true, + callee : true, + caller : true, + constructor : true, + 'eval' : true, + prototype : true, + stack : true, + unwatch : true, + valueOf : true, + watch : true + }, + + +// These are the JSLint boolean options. + + boolOptions = { + adsafe : true, // if ADsafe should be enforced + bitwise : true, // if bitwise operators should not be allowed + browser : true, // if the standard browser globals should be predefined + cap : true, // if upper case HTML should be allowed + css : true, // if CSS workarounds should be tolerated + debug : true, // if debugger statements should be allowed + devel : true, // if logging should be allowed (console, alert, etc.) + es5 : true, // if ES5 syntax should be allowed + evil : true, // if eval should be allowed + forin : true, // if for in statements must filter + fragment : true, // if HTML fragments should be allowed + newcap : true, // if constructor names must be capitalized + nomen : true, // if names should be checked + on : true, // if HTML event handlers should be allowed + onevar : true, // if only one var statement per function should be allowed + passfail : true, // if the scan should stop on first error + plusplus : true, // if increment/decrement should not be allowed + regexp : true, // if the . should not be allowed in regexp literals + rhino : true, // if the Rhino environment globals should be predefined + undef : true, // if variables should be declared before used + safe : true, // if use of some browser features should be restricted + windows : true, // if MS Windows-specigic globals should be predefined + strict : true, // require the "use strict"; pragma + sub : true, // if all forms of subscript notation are tolerated + white : true, // if strict whitespace rules apply + widget : true // if the Yahoo Widgets globals should be predefined + }, + +// browser contains a set of global names which are commonly provided by a +// web browser environment. + + browser = { + addEventListener: false, + blur : false, + clearInterval : false, + clearTimeout : false, + close : false, + closed : false, + defaultStatus : false, + document : false, + event : false, + focus : false, + frames : false, + getComputedStyle: false, + history : false, + Image : false, + length : false, + location : false, + moveBy : false, + moveTo : false, + name : false, + navigator : false, + onbeforeunload : true, + onblur : true, + onerror : true, + onfocus : true, + onload : true, + onresize : true, + onunload : true, + open : false, + opener : false, + Option : false, + parent : false, + print : false, + removeEventListener: false, + resizeBy : false, + resizeTo : false, + screen : false, + scroll : false, + scrollBy : false, + scrollTo : false, + setInterval : false, + setTimeout : false, + status : false, + top : false, + XMLHttpRequest : false + }, + + cssAttributeData, + cssAny, + + cssColorData = { + "aliceblue" : true, + "antiquewhite" : true, + "aqua" : true, + "aquamarine" : true, + "azure" : true, + "beige" : true, + "bisque" : true, + "black" : true, + "blanchedalmond" : true, + "blue" : true, + "blueviolet" : true, + "brown" : true, + "burlywood" : true, + "cadetblue" : true, + "chartreuse" : true, + "chocolate" : true, + "coral" : true, + "cornflowerblue" : true, + "cornsilk" : true, + "crimson" : true, + "cyan" : true, + "darkblue" : true, + "darkcyan" : true, + "darkgoldenrod" : true, + "darkgray" : true, + "darkgreen" : true, + "darkkhaki" : true, + "darkmagenta" : true, + "darkolivegreen" : true, + "darkorange" : true, + "darkorchid" : true, + "darkred" : true, + "darksalmon" : true, + "darkseagreen" : true, + "darkslateblue" : true, + "darkslategray" : true, + "darkturquoise" : true, + "darkviolet" : true, + "deeppink" : true, + "deepskyblue" : true, + "dimgray" : true, + "dodgerblue" : true, + "firebrick" : true, + "floralwhite" : true, + "forestgreen" : true, + "fuchsia" : true, + "gainsboro" : true, + "ghostwhite" : true, + "gold" : true, + "goldenrod" : true, + "gray" : true, + "green" : true, + "greenyellow" : true, + "honeydew" : true, + "hotpink" : true, + "indianred" : true, + "indigo" : true, + "ivory" : true, + "khaki" : true, + "lavender" : true, + "lavenderblush" : true, + "lawngreen" : true, + "lemonchiffon" : true, + "lightblue" : true, + "lightcoral" : true, + "lightcyan" : true, + "lightgoldenrodyellow" : true, + "lightgreen" : true, + "lightpink" : true, + "lightsalmon" : true, + "lightseagreen" : true, + "lightskyblue" : true, + "lightslategray" : true, + "lightsteelblue" : true, + "lightyellow" : true, + "lime" : true, + "limegreen" : true, + "linen" : true, + "magenta" : true, + "maroon" : true, + "mediumaquamarine" : true, + "mediumblue" : true, + "mediumorchid" : true, + "mediumpurple" : true, + "mediumseagreen" : true, + "mediumslateblue" : true, + "mediumspringgreen" : true, + "mediumturquoise" : true, + "mediumvioletred" : true, + "midnightblue" : true, + "mintcream" : true, + "mistyrose" : true, + "moccasin" : true, + "navajowhite" : true, + "navy" : true, + "oldlace" : true, + "olive" : true, + "olivedrab" : true, + "orange" : true, + "orangered" : true, + "orchid" : true, + "palegoldenrod" : true, + "palegreen" : true, + "paleturquoise" : true, + "palevioletred" : true, + "papayawhip" : true, + "peachpuff" : true, + "peru" : true, + "pink" : true, + "plum" : true, + "powderblue" : true, + "purple" : true, + "red" : true, + "rosybrown" : true, + "royalblue" : true, + "saddlebrown" : true, + "salmon" : true, + "sandybrown" : true, + "seagreen" : true, + "seashell" : true, + "sienna" : true, + "silver" : true, + "skyblue" : true, + "slateblue" : true, + "slategray" : true, + "snow" : true, + "springgreen" : true, + "steelblue" : true, + "tan" : true, + "teal" : true, + "thistle" : true, + "tomato" : true, + "turquoise" : true, + "violet" : true, + "wheat" : true, + "white" : true, + "whitesmoke" : true, + "yellow" : true, + "yellowgreen" : true, + + "activeborder" : true, + "activecaption" : true, + "appworkspace" : true, + "background" : true, + "buttonface" : true, + "buttonhighlight" : true, + "buttonshadow" : true, + "buttontext" : true, + "captiontext" : true, + "graytext" : true, + "highlight" : true, + "highlighttext" : true, + "inactiveborder" : true, + "inactivecaption" : true, + "inactivecaptiontext" : true, + "infobackground" : true, + "infotext" : true, + "menu" : true, + "menutext" : true, + "scrollbar" : true, + "threeddarkshadow" : true, + "threedface" : true, + "threedhighlight" : true, + "threedlightshadow" : true, + "threedshadow" : true, + "window" : true, + "windowframe" : true, + "windowtext" : true + }, + + cssBorderStyle, + cssBreak, + + cssLengthData = { + '%': true, + 'cm': true, + 'em': true, + 'ex': true, + 'in': true, + 'mm': true, + 'pc': true, + 'pt': true, + 'px': true + }, + + cssMedia, + cssOverflow, + + devel = { + alert : false, + confirm : false, + console : false, + Debug : false, + opera : false, + prompt : false + }, + + escapes = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '/' : '\\/', + '\\': '\\\\' + }, + + funct, // The current function + + functionicity = [ + 'closure', 'exception', 'global', 'label', + 'outer', 'unused', 'var' + ], + + functions, // All of the functions + + global, // The global scope + htmltag = { + a: {}, + abbr: {}, + acronym: {}, + address: {}, + applet: {}, + area: {empty: true, parent: ' map '}, + article: {}, + aside: {}, + audio: {}, + b: {}, + base: {empty: true, parent: ' head '}, + bdo: {}, + big: {}, + blockquote: {}, + body: {parent: ' html noframes '}, + br: {empty: true}, + button: {}, + canvas: {parent: ' body p div th td '}, + caption: {parent: ' table '}, + center: {}, + cite: {}, + code: {}, + col: {empty: true, parent: ' table colgroup '}, + colgroup: {parent: ' table '}, + command: {parent: ' menu '}, + datalist: {}, + dd: {parent: ' dl '}, + del: {}, + details: {}, + dialog: {}, + dfn: {}, + dir: {}, + div: {}, + dl: {}, + dt: {parent: ' dl '}, + em: {}, + embed: {}, + fieldset: {}, + figure: {}, + font: {}, + footer: {}, + form: {}, + frame: {empty: true, parent: ' frameset '}, + frameset: {parent: ' html frameset '}, + h1: {}, + h2: {}, + h3: {}, + h4: {}, + h5: {}, + h6: {}, + head: {parent: ' html '}, + header: {}, + hgroup: {}, + hr: {empty: true}, + 'hta:application': + {empty: true, parent: ' head '}, + html: {parent: '*'}, + i: {}, + iframe: {}, + img: {empty: true}, + input: {empty: true}, + ins: {}, + kbd: {}, + keygen: {}, + label: {}, + legend: {parent: ' details fieldset figure '}, + li: {parent: ' dir menu ol ul '}, + link: {empty: true, parent: ' head '}, + map: {}, + mark: {}, + menu: {}, + meta: {empty: true, parent: ' head noframes noscript '}, + meter: {}, + nav: {}, + noframes: {parent: ' html body '}, + noscript: {parent: ' body head noframes '}, + object: {}, + ol: {}, + optgroup: {parent: ' select '}, + option: {parent: ' optgroup select '}, + output: {}, + p: {}, + param: {empty: true, parent: ' applet object '}, + pre: {}, + progress: {}, + q: {}, + rp: {}, + rt: {}, + ruby: {}, + samp: {}, + script: {empty: true, parent: ' body div frame head iframe p pre span '}, + section: {}, + select: {}, + small: {}, + span: {}, + source: {}, + strong: {}, + style: {parent: ' head ', empty: true}, + sub: {}, + sup: {}, + table: {}, + tbody: {parent: ' table '}, + td: {parent: ' tr '}, + textarea: {}, + tfoot: {parent: ' table '}, + th: {parent: ' tr '}, + thead: {parent: ' table '}, + time: {}, + title: {parent: ' head '}, + tr: {parent: ' table tbody thead tfoot '}, + tt: {}, + u: {}, + ul: {}, + 'var': {}, + video: {} + }, + + ids, // HTML ids + implied, // Implied globals + inblock, + jsonmode, + labelled = { + 'do': true, + 'for': true, + 'switch': true, + 'while': true + }, + lines, + lookahead, + member, + membersOnly, + nexttoken, + option, + postscript = { + '(end)': true, + '(error)': true, + '>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, +// html token + hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/, +// characters in strings that need escapement + nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, + nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +// outer html token + ox = /[>&]|<[\/!]?|--/, +// star slash + lx = /\*\/|\/\*/, +// identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, +// javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, +// url badness + ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i, +// style + sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, + ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, +// attributes characters + qx = /[^a-zA-Z0-9+\-_\/ ]/, +// query characters for ids + dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, + + rx = { + outer: hx, + html: hx, + style: sx, + styleproperty: ssx + }; + + + function F() {} // Used by Object.create + + function is_own(object, name) { + +// The object.hasOwnProperty method fails when the property under consideration +// is named 'hasOwnProperty'. So we have to use this more convoluted form. + + return Object.prototype.hasOwnProperty.call(object, name); + } + +// Provide critical ES5 functions to ES3. + + if (typeof Array.isArray !== 'function') { + Array.isArray = function (o) { + return Object.prototype.toString.apply(o) === '[object Array]'; + }; + } + + if (typeof Object.create !== 'function') { + Object.create = function (o) { + F.prototype = o; + return new F(); + }; + } + + if (typeof Object.keys !== 'function') { + Object.keys = function (o) { + var a = [], k; + for (k in o) { + if (is_own(o, k)) { + a.push(k); + } + } + return a; + }; + } + +// Non standard methods + + if (typeof String.prototype.entityify !== 'function') { + String.prototype.entityify = function () { + return this + .replace(/&/g, '&') + .replace(//g, '>'); + }; + } + + if (typeof String.prototype.isAlpha !== 'function') { + String.prototype.isAlpha = function () { + return (this >= 'a' && this <= 'z\uffff') || + (this >= 'A' && this <= 'Z\uffff'); + }; + } + + if (typeof String.prototype.isDigit !== 'function') { + String.prototype.isDigit = function () { + return (this >= '0' && this <= '9'); + }; + } + + if (typeof String.prototype.supplant !== 'function') { + String.prototype.supplant = function (o) { + return this.replace(/\{([^{}]*)\}/g, function (a, b) { + var r = o[b]; + return typeof r === 'string' || typeof r === 'number' ? r : a; + }); + }; + } + + if (typeof String.prototype.name !== 'function') { + String.prototype.name = function () { + +// If the string looks like an identifier, then we can return it as is. +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can simply slap some quotes around it. +// Otherwise we must also replace the offending characters with safe +// sequences. + + if (ix.test(this)) { + return this; + } + if (nx.test(this)) { + return '"' + this.replace(nxg, function (a) { + var c = escapes[a]; + if (c) { + return c; + } + return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); + }) + '"'; + } + return '"' + this + '"'; + }; + } + + + function combine(t, o) { + var n; + for (n in o) { + if (is_own(o, n)) { + t[n] = o[n]; + } + } + } + + function assume() { + if (!option.safe) { + if (option.rhino) { + combine(predefined, rhino); + } + if (option.devel) { + combine(predefined, devel); + } + if (option.browser) { + combine(predefined, browser); + } + if (option.windows) { + combine(predefined, windows); + } + if (option.widget) { + combine(predefined, widget); + } + } + } + + +// Produce an error warning. + + function quit(m, l, ch) { + throw { + name: 'JSLintError', + line: l, + character: ch, + message: m + " (" + Math.floor((l / lines.length) * 100) + + "% scanned)." + }; + } + + function warning(m, t, a, b, c, d) { + var ch, l, w; + t = t || nexttoken; + if (t.id === '(end)') { // `~ + t = token; + } + l = t.line || 0; + ch = t.from || 0; + w = { + id: '(error)', + raw: m, + evidence: lines[l - 1] || '', + line: l, + character: ch, + a: a, + b: b, + c: c, + d: d + }; + w.reason = m.supplant(w); + JSLINT.errors.push(w); + if (option.passfail) { + quit('Stopping. ', l, ch); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit("Too many errors.", l, ch); + } + return w; + } + + function warningAt(m, l, ch, a, b, c, d) { + return warning(m, { + line: l, + from: ch + }, a, b, c, d); + } + + function error(m, t, a, b, c, d) { + var w = warning(m, t, a, b, c, d); + quit("Stopping, unable to continue.", w.line, w.character); + } + + function errorAt(m, l, ch, a, b, c, d) { + return error(m, { + line: l, + from: ch + }, a, b, c, d); + } + + + +// lexical analysis and token construction + + var lex = (function lex() { + var character, from, line, s; + +// Private lex methods + + function nextLine() { + var at; + if (line >= lines.length) { + return false; + } + character = 1; + s = lines[line]; + line += 1; + at = s.search(/ \t/); + if (at >= 0) { + warningAt("Mixed spaces and tabs.", line, at + 1); + } + s = s.replace(/\t/g, tab); + at = s.search(cx); + if (at >= 0) { + warningAt("Unsafe character.", line, at); + } + if (option.maxlen && option.maxlen < s.length) { + warningAt("Line too long.", line, s.length); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var i, t; + if (type === '(color)' || type === '(range)') { + t = {type: type}; + } else if (type === '(punctuator)' || + (type === '(identifier)' && is_own(syntax, value))) { + t = syntax[value] || syntax['(error)']; + } else { + t = syntax[type]; + } + t = Object.create(t); + if (type === '(string)' || type === '(range)') { + if (jx.test(value)) { + warningAt("Script URL.", line, from); + } + } + if (type === '(identifier)') { + t.identifier = true; + if (value === '__iterator__' || value === '__proto__') { + errorAt("Reserved name '{a}'.", + line, from, value); + } else if (option.nomen && + (value.charAt(0) === '_' || + value.charAt(value.length - 1) === '_')) { + warningAt("Unexpected {a} in '{b}'.", line, from, + "dangling '_'", value); + } + } + if (value !== undefined) { + t.value = value; + } + t.line = line; + t.thru = character; + t.from = from; + i = t.id; + prereg = i && + (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) || + i === 'return'); + return t; + } + +// Public lex methods + + return { + init: function (source) { + if (typeof source === 'string') { + lines = source + .replace(/\r\n/g, '\n') + .replace(/\r/g, '\n') + .split('\n'); + } else { + lines = source; + } + line = 0; + nextLine(); + from = 1; + }, + + range: function (begin, end) { + var c, value = ''; + from = character; + if (s.charAt(0) !== begin) { + errorAt("Expected '{a}' and instead saw '{b}'.", + line, character, begin, s.charAt(0)); + } + for (;;) { + s = s.slice(1); + character += 1; + c = s.charAt(0); + switch (c) { + case '': + errorAt("Missing '{a}'.", line, character, c); + break; + case end: + s = s.slice(1); + character += 1; + return it('(range)', value); + case xquote: + case '\\': + warningAt("Unexpected '{a}'.", line, character, c); + break; + } + value += c; + } + }, + +// token -- this is called by advance to get the next token. + + token: function () { + var b, c, captures, d, depth, high, i, l, low, q, t; + + function match(x) { + var r = x.exec(s), r1; + if (r) { + l = r[0].length; + r1 = r[1]; + c = r1.charAt(0); + s = s.substr(l); + from = character + l - r1.length; + character += l; + return r1; + } + } + + function string(x) { + var c, j, r = ''; + + if (jsonmode && x !== '"') { + warningAt("Strings must use doublequote.", + line, character); + } + + if (xquote === x || (xmode === 'scriptstring' && !xquote)) { + return it('(punctuator)', x); + } + + function esc(n) { + var i = parseInt(s.substr(j + 1, n), 16); + j += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warningAt("Unnecessary escapement.", line, character); + } + character += n; + c = String.fromCharCode(i); + } + j = 0; + for (;;) { + while (j >= s.length) { + j = 0; + if (xmode !== 'html' || !nextLine()) { + errorAt("Unclosed string.", line, from); + } + } + c = s.charAt(j); + if (c === x) { + character += 1; + s = s.substr(j + 1); + return it('(string)', r, x); + } + if (c < ' ') { + if (c === '\n' || c === '\r') { + break; + } + warningAt("Control character in string: {a}.", + line, character + j, s.slice(0, j)); + } else if (c === xquote) { + warningAt("Bad HTML string", line, character + j); + } else if (c === '<') { + if (option.safe && xmode === 'html') { + warningAt("ADsafe string violation.", + line, character + j); + } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) { + warningAt("Expected '<\\/' and instead saw ' 0) { + character += 1; + s = s.slice(i); + break; + } else { + if (!nextLine()) { + return it('(end)', ''); + } + } + } + t = match(rx[xmode] || tx); + if (!t) { + t = ''; + c = ''; + while (s && s < '!') { + s = s.substr(1); + } + if (s) { + if (xmode === 'html') { + return it('(error)', s.charAt(0)); + } else { + errorAt("Unexpected '{a}'.", + line, character, s.substr(0, 1)); + } + } + } else { + + // identifier + + if (c.isAlpha() || c === '_' || c === '$') { + return it('(identifier)', t); + } + + // number + + if (c.isDigit()) { + if (xmode !== 'style' && !isFinite(Number(t))) { + warningAt("Bad number '{a}'.", + line, character, t); + } + if (xmode !== 'style' && + xmode !== 'styleproperty' && + s.substr(0, 1).isAlpha()) { + warningAt("Missing space after '{a}'.", + line, character, t); + } + if (c === '0') { + d = t.substr(1, 1); + if (d.isDigit()) { + if (token.id !== '.' && xmode !== 'styleproperty') { + warningAt("Don't use extra leading zeros '{a}'.", + line, character, t); + } + } else if (jsonmode && (d === 'x' || d === 'X')) { + warningAt("Avoid 0x-. '{a}'.", + line, character, t); + } + } + if (t.substr(t.length - 1) === '.') { + warningAt( +"A trailing decimal point can be confused with a dot '{a}'.", line, character, t); + } + return it('(number)', t); + } + switch (t) { + + // string + + case '"': + case "'": + return string(t); + + // // comment + + case '//': + if (src || (xmode && xmode !== 'script')) { + warningAt("Unexpected comment.", line, character); + } else if (xmode === 'script' && /<\s*\//i.test(s)) { + warningAt("Unexpected <\/ in comment.", line, character); + } else if ((option.safe || xmode === 'script') && ax.test(s)) { + warningAt("Dangerous comment.", line, character); + } + s = ''; + token.comment = true; + break; + + // /* comment + + case '/*': + if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) { + warningAt("Unexpected comment.", line, character); + } + if (option.safe && ax.test(s)) { + warningAt("ADsafe comment violation.", line, character); + } + for (;;) { + i = s.search(lx); + if (i >= 0) { + break; + } + if (!nextLine()) { + errorAt("Unclosed comment.", line, character); + } else { + if (option.safe && ax.test(s)) { + warningAt("ADsafe comment violation.", + line, character); + } + } + } + character += i + 2; + if (s.substr(i, 1) === '/') { + errorAt("Nested comment.", line, character); + } + s = s.substr(i + 2); + token.comment = true; + break; + + // /*members /*jslint /*global + + case '/*members': + case '/*member': + case '/*jslint': + case '/*global': + case '*/': + return { + value: t, + type: 'special', + line: line, + thru: character, + from: from + }; + + case '': + break; + // / + case '/': + if (token.id === '/=') { + errorAt( +"A regular expression literal can be confused with '/='.", line, from); + } + if (prereg) { + depth = 0; + captures = 0; + l = 0; + for (;;) { + b = true; + c = s.charAt(l); + l += 1; + switch (c) { + case '': + errorAt("Unclosed regular expression.", + line, from); + return; + case '/': + if (depth > 0) { + warningAt("Unescaped '{a}'.", + line, from + l, '/'); + } + c = s.substr(0, l - 1); + q = { + g: true, + i: true, + m: true + }; + while (q[s.charAt(l)] === true) { + q[s.charAt(l)] = false; + l += 1; + } + character += l; + s = s.substr(l); + q = s.charAt(0); + if (q === '/' || q === '*') { + errorAt("Confusing regular expression.", + line, from); + } + return it('(regexp)', c); + case '\\': + c = s.charAt(l); + if (c < ' ') { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === '<') { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + break; + case '(': + depth += 1; + b = false; + if (s.charAt(l) === '?') { + l += 1; + switch (s.charAt(l)) { + case ':': + case '=': + case '!': + l += 1; + break; + default: + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l)); + } + } else { + captures += 1; + } + break; + case '|': + b = false; + break; + case ')': + if (depth === 0) { + warningAt("Unescaped '{a}'.", + line, from + l, ')'); + } else { + depth -= 1; + } + break; + case ' ': + q = 1; + while (s.charAt(l) === ' ') { + l += 1; + q += 1; + } + if (q > 1) { + warningAt( +"Spaces are hard to count. Use {{a}}.", line, from + l, q); + } + break; + case '[': + c = s.charAt(l); + if (c === '^') { + l += 1; + if (option.regexp) { + warningAt("Insecure '{a}'.", + line, from + l, c); + } else if (s.charAt(l) === ']') { + errorAt("Unescaped '{a}'.", + line, from + l, '^'); + } + } + q = false; + if (c === ']') { + warningAt("Empty class.", line, + from + l - 1); + q = true; + } +klass: do { + c = s.charAt(l); + l += 1; + switch (c) { + case '[': + case '^': + warningAt("Unescaped '{a}'.", + line, from + l, c); + q = true; + break; + case '-': + if (q) { + q = false; + } else { + warningAt("Unescaped '{a}'.", + line, from + l, '-'); + q = true; + } + break; + case ']': + if (!q) { + warningAt("Unescaped '{a}'.", + line, from + l - 1, '-'); + } + break klass; + case '\\': + c = s.charAt(l); + if (c < ' ') { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === '<') { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + q = true; + break; + case '/': + warningAt("Unescaped '{a}'.", + line, from + l - 1, '/'); + q = true; + break; + case '<': + if (xmode === 'script') { + c = s.charAt(l); + if (c === '!' || c === '/') { + warningAt( +"HTML confusion in regular expression '<{a}'.", line, from + l, c); + } + } + q = true; + break; + default: + q = true; + } + } while (c); + break; + case '.': + if (option.regexp) { + warningAt("Insecure '{a}'.", line, + from + l, c); + } + break; + case ']': + case '?': + case '{': + case '}': + case '+': + case '*': + warningAt("Unescaped '{a}'.", line, + from + l, c); + break; + case '<': + if (xmode === 'script') { + c = s.charAt(l); + if (c === '!' || c === '/') { + warningAt( +"HTML confusion in regular expression '<{a}'.", line, from + l, c); + } + } + break; + } + if (b) { + switch (s.charAt(l)) { + case '?': + case '+': + case '*': + l += 1; + if (s.charAt(l) === '?') { + l += 1; + } + break; + case '{': + l += 1; + c = s.charAt(l); + if (c < '0' || c > '9') { + warningAt( +"Expected a number and instead saw '{a}'.", line, from + l, c); + } + l += 1; + low = +c; + for (;;) { + c = s.charAt(l); + if (c < '0' || c > '9') { + break; + } + l += 1; + low = +c + (low * 10); + } + high = low; + if (c === ',') { + l += 1; + high = Infinity; + c = s.charAt(l); + if (c >= '0' && c <= '9') { + l += 1; + high = +c; + for (;;) { + c = s.charAt(l); + if (c < '0' || c > '9') { + break; + } + l += 1; + high = +c + (high * 10); + } + } + } + if (s.charAt(l) !== '}') { + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c); + } else { + l += 1; + } + if (s.charAt(l) === '?') { + l += 1; + } + if (low > high) { + warningAt( +"'{a}' should not be greater than '{b}'.", line, from + l, low, high); + } + break; + } + } + } + c = s.substr(0, l - 1); + character += l; + s = s.substr(l); + return it('(regexp)', c); + } + return it('(punctuator)', t); + + // punctuator + + case '.", line, character); + } + character += 3; + s = s.slice(i + 3); + break; + case '#': + if (xmode === 'html' || xmode === 'styleproperty') { + for (;;) { + c = s.charAt(0); + if ((c < '0' || c > '9') && + (c < 'a' || c > 'f') && + (c < 'A' || c > 'F')) { + break; + } + character += 1; + s = s.substr(1); + t += c; + } + if (t.length !== 4 && t.length !== 7) { + warningAt("Bad hex color '{a}'.", line, + from + l, t); + } + return it('(color)', t); + } + return it('(punctuator)', t); + default: + if (xmode === 'outer' && c === '&') { + character += 1; + s = s.substr(1); + for (;;) { + c = s.charAt(0); + character += 1; + s = s.substr(1); + if (c === ';') { + break; + } + if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + c === '#')) { + errorAt("Bad entity", line, from + l, + character); + } + } + break; + } + return it('(punctuator)', t); + } + } + } + } + }; + }()); + + + function addlabel(t, type) { + + if (option.safe && funct['(global)'] && + typeof predefined[t] !== 'boolean') { + warning('ADsafe global: ' + t + '.', token); + } else if (t === 'hasOwnProperty') { + warning("'hasOwnProperty' is a really bad name."); + } + +// Define t in the current function in the current scope. + + if (is_own(funct, t) && !funct['(global)']) { + warning(funct[t] === true ? + "'{a}' was used before it was defined." : + "'{a}' is already defined.", + nexttoken, t); + } + funct[t] = type; + if (funct['(global)']) { + global[t] = funct; + if (is_own(implied, t)) { + warning("'{a}' was used before it was defined.", nexttoken, t); + delete implied[t]; + } + } else { + scope[t] = funct; + } + } + + + function doOption() { + var b, obj, filter, o = nexttoken.value, t, v; + switch (o) { + case '*/': + error("Unbegun comment."); + break; + case '/*members': + case '/*member': + o = '/*members'; + if (!membersOnly) { + membersOnly = {}; + } + obj = membersOnly; + break; + case '/*jslint': + if (option.safe) { + warning("ADsafe restriction."); + } + obj = option; + filter = boolOptions; + break; + case '/*global': + if (option.safe) { + warning("ADsafe restriction."); + } + obj = predefined; + break; + default: + error("What?"); + } + t = lex.token(); +loop: for (;;) { + for (;;) { + if (t.type === 'special' && t.value === '*/') { + break loop; + } + if (t.id !== ',') { + break; + } + t = lex.token(); + } + if (t.type !== '(string)' && t.type !== '(identifier)' && + o !== '/*members') { + error("Bad option.", t); + } + v = lex.token(); + if (v.id === ':') { + v = lex.token(); + if (obj === membersOnly) { + error("Expected '{a}' and instead saw '{b}'.", + t, '*/', ':'); + } + if (t.value === 'indent' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.white = true; + obj.indent = b; + } else if (t.value === 'maxerr' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.maxerr = b; + } else if (t.value === 'maxlen' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.maxlen = b; + } else if (v.value === 'true') { + obj[t.value] = true; + } else if (v.value === 'false') { + obj[t.value] = false; + } else { + error("Bad option value.", v); + } + t = lex.token(); + } else { + if (o === '/*jslint') { + error("Missing option value.", t); + } + obj[t.value] = false; + t = v; + } + } + if (filter) { + assume(); + } + } + + +// We need a peek function. If it has an argument, it peeks that much farther +// ahead. It is used to distinguish +// for ( var i in ... +// from +// for ( var i = ... + + function peek(p) { + var i = p || 0, j = 0, t; + + while (j <= i) { + t = lookahead[j]; + if (!t) { + t = lookahead[j] = lex.token(); + } + j += 1; + } + return t; + } + + + +// Produce the next token. It looks for programming errors. + + function advance(id, t) { + switch (token.id) { + case '(number)': + if (nexttoken.id === '.') { + warning( +"A dot following a number can be confused with a decimal point.", token); + } + break; + case '-': + if (nexttoken.id === '-' || nexttoken.id === '--') { + warning("Confusing minusses."); + } + break; + case '+': + if (nexttoken.id === '+' || nexttoken.id === '++') { + warning("Confusing plusses."); + } + break; + } + if (token.type === '(string)' || token.identifier) { + anonname = token.value; + } + + if (id && nexttoken.id !== id) { + if (t) { + if (nexttoken.id === '(end)') { + warning("Unmatched '{a}'.", t, t.id); + } else { + warning( +"Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + nexttoken, id, t.id, t.line, nexttoken.value); + } + } else if (nexttoken.type !== '(identifier)' || + nexttoken.value !== id) { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, id, nexttoken.value); + } + } + prevtoken = token; + token = nexttoken; + for (;;) { + nexttoken = lookahead.shift() || lex.token(); + if (nexttoken.type !== 'special') { + break; + } + doOption(); + } + } + +// Functions for conformance of style. + + function one_space_only(left, right) { + left = left || token; + right = right || nexttoken; + if (right.id !== '(end)' && (left.line !== right.line || + (option.white && left.thru + 1 !== right.from))) { + warning("Expected exactly one space between '{a}' and '{b}'.", + right, left.value, right.value); + } + } + + function one_space(left, right) { + left = left || token; + right = right || nexttoken; + if (right.id !== '(end)' && option.white && + (token.line !== right.line || + token.thru + 1 !== right.from)) { + warning("Expected exactly one space between '{a}' and '{b}'.", + right, token.value, right.value); + } + } + + function no_space(left, right) { + left = left || token; + right = right || nexttoken; + if ((option.white || xmode === 'styleproperty' || xmode === 'style') && + left.thru !== right.from && left.line === right.line) { + warning("Unexpected space between '{a}' and '{b}'.", right, + left.value, right.value); + } + } + + function no_space_only(left, right) { + left = left || token; + right = right || nexttoken; + if (right.id !== '(end)' && (left.line !== right.line || + (option.white && left.thru !== right.from))) { + warning("Unexpected space between '{a}' and '{b}'.", + right, left.value, right.value); + } + } + + function spaces(left, right) { + if (option.white) { + left = left || token; + right = right || nexttoken; + if (left.thru === right.from && left.line === right.line) { + warning("Missing space between '{a}' and '{b}'.", + right, left.value, right.value); + } + } + } + + function comma() { + no_space_only(); + advance(','); + spaces(); + } + + + function semicolon() { + no_space_only(); + advance(';'); + switch (nexttoken.id) { + case ';': + case '"': + case '\'': + case ')': + break; + default: + spaces(); + } + } + + function use_strict() { + if (nexttoken.value === 'use strict') { + if (strict_mode) { + warning("Unnecessary \"use strict\"."); + } + advance(); + semicolon(); + strict_mode = true; + option.newcap = true; + option.undef = true; + return true; + } else { + return false; + } + } + + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is +// like .nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define statement-oriented languages like +// JavaScript. I retained Pratt's nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + +// rbp is the right binding power. +// initial indicates that this is the first expression of a statement. + + var left; + if (nexttoken.id === '(end)') { + error("Unexpected early end of program.", token); + } + advance(); + if (option.safe && typeof predefined[token.value] === 'boolean' && + (nexttoken.id !== '(' && nexttoken.id !== '.')) { + warning('ADsafe violation.', token); + } + if (initial) { + anonname = 'anonymous'; + funct['(verb)'] = token.value; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (nexttoken.type === '(number)' && token.id === '.') { + warning( +"A leading decimal point can be confused with a dot: '.{a}'.", + token, nexttoken.value); + advance(); + return token; + } else { + error("Expected an identifier and instead saw '{a}'.", + token, token.id); + } + } + while (rbp < nexttoken.lbp) { + advance(); + if (token.led) { + left = token.led(left); + } else { + error("Expected an operator and instead saw '{a}'.", + token, token.id); + } + } + } + return left; + } + + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, p) { + var x = syntax[s]; + if (!x || typeof x !== 'object') { + syntax[s] = x = { + id: s, + lbp: p, + value: s + }; + } + return x; + } + + + function delim(s) { + return symbol(s, 0); + } + + + function ultimate(s) { + var x = symbol(s, 0); + x.from = 0; + x.thru = 0; + s.value = s; + return x; + } + + + function stmt(s, f) { + var x = delim(s); + x.identifier = x.reserved = true; + x.fud = f; + return x; + } + + function disruptstmt(s, f) { + var x = stmt(s, f); + x.disrupt = true; + } + + + function reserveName(x) { + var c = x.id.charAt(0); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + x.identifier = x.reserved = true; + } + return x; + } + + + function prefix(s, f) { + var x = symbol(s, 150); + reserveName(x); + x.nud = (typeof f === 'function') ? f : function () { + if (s === 'typeof') { + one_space(); + } else { + no_space_only(); + } + this.first = expression(150); + this.arity = 'prefix'; + if (this.id === '++' || this.id === '--') { + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!this.first.identifier || this.first.reserved) && + this.first.id !== '.' && this.first.id !== '[') { + warning("Bad operand.", this); + } + } + return this; + }; + return x; + } + + + function type(s, f) { + var x = delim(s); + x.type = s; + x.nud = f; + return x; + } + + + function reserve(s, f) { + var x = type(s, f); + x.identifier = x.reserved = true; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (typeof v === 'function') { + v(this); + } + return this; + }); + } + + + function infix(s, p, f, w) { + var x = symbol(s, p); + reserveName(x); + x.led = function (left) { + this.arity = 'infix'; + if (!w) { + spaces(prevtoken, token); + spaces(); + } + if (typeof f === 'function') { + return f(left, this); + } else { + this.first = left; + this.second = expression(p); + return this; + } + }; + return x; + } + + + function relation(s, eqeq) { + var x = infix(s, 100, function (left, that) { + var right = expression(100); + if (eqeq) { + warning("Expected '{a}' and instead saw '{b}'.", + that, eqeq, that.id); + } else if (left.id === 'NaN' || right.id === 'NaN') { + warning("Use the isNaN function to compare with NaN.", that); + } + if (left.id === '!') { + warning("Confusing use of '{a}'.", left, '!'); + } + if (right.id === '!') { + warning("Confusing use of '{a}'.", left, '!'); + } + that.first = left; + that.second = right; + return that; + }); + return x; + } + + + function assignop(s, bit) { + var x = infix(s, 20, function (left, that) { + var l; + if (option.bitwise && bit) { + warning("Unexpected use of '{a}'.", that, that.id); + } + that.first = left; + if (predefined[left.value] === false && + scope[left.value]['(global)'] === true) { + warning("Read only.", left); + } else if (left['function']) { + warning("'{a}' is a function.", left, left.value); + } + if (option.safe) { + l = left; + do { + if (typeof predefined[l.value] === 'boolean') { + warning('ADsafe violation.', l); + } + l = l.first; + } while (l); + } + if (left) { + if (left.id === '.' || left.id === '[') { + if (!left.first || left.first.value === 'arguments') { + warning('Bad assignment.', that); + } + that.second = expression(19); + return that; + } else if (left.identifier && !left.reserved) { + if (funct[left.value] === 'exception') { + warning("Do not assign to the exception parameter.", left); + } + that.second = expression(19); + return that; + } + if (left === syntax['function']) { + warning( +"Expected an identifier in an assignment and instead saw a function invocation.", + token); + } + } + error("Bad assignment.", that); + }); + x.assign = true; + return x; + } + + + function bitwise(s, p) { + return infix(s, p, function (left, that) { + if (option.bitwise) { + warning("Unexpected use of '{a}'.", that, that.id); + } + that.first = left; + that.second = expression(p); + return that; + }); + } + + + function suffix(s, f) { + var x = symbol(s, 150); + x.led = function (left) { + no_space_only(prevtoken, token); + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!left.identifier || left.reserved) && + left.id !== '.' && left.id !== '[') { + warning("Bad operand.", this); + } + this.first = left; + this.arity = 'suffix'; + return this; + }; + return x; + } + + + function optionalidentifier() { + if (nexttoken.identifier) { + advance(); + if (option.safe && banned[token.value]) { + warning("ADsafe violation: '{a}'.", token, token.value); + } else if (token.reserved && !option.es5) { + warning("Expected an identifier and instead saw '{a}' (a reserved word).", + token, token.id); + } + return token.value; + } + } + + + function identifier() { + var i = optionalidentifier(); + if (i) { + return i; + } + if (token.id === 'function' && nexttoken.id === '(') { + warning("Missing name in function statement."); + } else { + error("Expected an identifier and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + } + + + function statement(noindent) { + +// Usually a statement starts a line. Exceptions include the var statement in the +// initialization part of a for statement, and an if after an else. + + var r, s = scope, t = nexttoken; + +// We don't like the empty statement. + + if (t.id === ';') { + warning("Unnecessary semicolon.", t); + advance(';'); + return; + } + +// Is this a labelled statement? + + if (t.identifier && !t.reserved && peek().id === ':') { + advance(); + advance(':'); + scope = Object.create(s); + addlabel(t.value, 'label'); + if (labelled[nexttoken.id] !== true) { + warning("Label '{a}' on '{b}' statement.", + nexttoken, t.value, nexttoken.value); + } + if (jx.test(t.value + ':')) { + warning("Label '{a}' looks like a javascript url.", + t, t.value); + } + nexttoken.label = t.value; + t = nexttoken; + } + +// Parse the statement. + + r = expression(0, true); + +// Look for the final semicolon. + + if (r.arity === 'statement') { + if (r.id !== 'switch' && (!r.block || r.id === 'do')) { + semicolon(); + } else { + spaces(); + } + } else { + if (r.id === '(' && r.first.id === 'new') { + warning("Do not use 'new' for side effects."); + } else if (!r.assign && r.id !== 'delete' && r.id !== '++' && + r.id !== '--' && r.id !== '(') { + warning( +"Expected an assignment or function call and instead saw an expression.", + token); + } + if (nexttoken.id !== ';') { + warningAt("Missing semicolon.", token.line, + token.from + token.value.length); + } else { + semicolon(); + } + } + scope = s; + return r; + } + + + function statements(begin) { + var a = [], d, f, p, s; + if (option.adsafe) { + switch (begin) { + case 'script': + +// JSLint is also the static analizer for ADsafe. See www.ADsafe.org. + + if (!adsafe_may) { + if (nexttoken.value !== 'ADSAFE' || + peek(0).id !== '.' || + (peek(1).value !== 'id' && + peek(1).value !== 'go')) { + error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.', + nexttoken); + } + } + if (nexttoken.value === 'ADSAFE' && + peek(0).id === '.' && + peek(1).value === 'id') { + if (adsafe_may) { + error('ADsafe violation.', nexttoken); + } + advance('ADSAFE'); + advance('.'); + advance('id'); + advance('('); + if (nexttoken.value !== adsafe_id) { + error('ADsafe violation: id does not match.', nexttoken); + } + advance('(string)'); + advance(')'); + semicolon(); + adsafe_may = true; + } + break; + case 'lib': + if (nexttoken.value === 'ADSAFE') { + advance('ADSAFE'); + advance('.'); + advance('lib'); + advance('('); + advance('(string)'); + comma(); + f = expression(0); + if (f.id !== 'function') { + error('The second argument to lib must be a function.', f); + } + p = f.funct['(params)']; + p = p && p.join(', '); + if (p && p !== 'lib') { + error("Expected '{a}' and instead saw '{b}'.", + f, '(lib)', '(' + p + ')'); + } + advance(')'); + semicolon(); + return a; + } else { + error("ADsafe lib violation."); + } + break; + } + } + +// A disrupt statement may not be followed by any other statement. +// If the last statement is disrupt, then the sequence is disrupt. + + while (postscript[nexttoken.id] !== true) { + if (nexttoken.id === ';') { + warning("Unnecessary semicolon."); + advance(';'); + } else { + if (d) { + warning("Unreachable '{a}' after '{b}'.", nexttoken, + nexttoken.value, d.value); + d = null; + } + s = statement(); + a.push(s); + if (s.disrupt) { + d = s; + a.disrupt = true; + } + } + } + return a; + } + + + function block(ordinary) { + +// A block is a sequence of statements wrapped in braces. +// ordinary is false for function bodies and try blocks. +// ordinary is true for if statements, while, etc. + + var a, + b = inblock, + m = strict_mode, + s = scope, + t; + inblock = ordinary; + scope = Object.create(scope); + spaces(); + t = nexttoken; + if (nexttoken.id === '{') { + advance('{'); + if (!ordinary && !use_strict() && !m && option.strict && + funct['(context)']['(global)']) { + warning("Missing \"use strict\" statement."); + } + a = statements(); + strict_mode = m; + advance('}', t); + } else if (!ordinary) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, '{', nexttoken.value); + } else { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, '{', nexttoken.value); + a = [statement()]; + if (a[0].disrupt) { + a.disrupt = true; + } + } + funct['(verb)'] = null; + scope = s; + inblock = b; + if (ordinary && a.length === 0) { + warning("Empty block."); + } + return a; + } + + + function countMember(m) { + if (membersOnly && typeof membersOnly[m] !== 'boolean') { + warning("Unexpected /*member '{a}'.", token, m); + } + if (typeof member[m] === 'number') { + member[m] += 1; + } else { + member[m] = 1; + } + } + + + function note_implied(token) { + var name = token.value, line = token.line, a = implied[name]; + if (typeof a === 'function') { + a = false; + } + if (!a) { + a = [line]; + implied[name] = a; + } else if (a[a.length - 1] !== line) { + a.push(line); + } + } + + +// Build the syntax table by declaring the syntactic elements of the language. + + type('(number)', function () { + this.arity = 'number'; + return this; + }); + type('(string)', function () { + this.arity = 'string'; + return this; + }); + + syntax['(identifier)'] = { + type: '(identifier)', + lbp: 0, + identifier: true, + nud: function () { + var v = this.value, + s = scope[v], + f; + if (typeof s === 'function') { + +// Protection against accidental inheritance. + + s = undefined; + } else if (typeof s === 'boolean') { + f = funct; + funct = functions[0]; + addlabel(v, 'var'); + s = funct; + funct = f; + } + +// The name is in scope and defined in the current function. + + if (funct === s) { + +// Change 'unused' to 'var', and reject labels. + + switch (funct[v]) { + case 'unused': + funct[v] = 'var'; + break; + case 'unction': + funct[v] = 'function'; + this['function'] = true; + break; + case 'function': + this['function'] = true; + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; + } + +// The name is not defined in the function. If we are in the global scope, +// then we have an undefined variable. + + } else if (funct['(global)']) { + if (option.undef && typeof predefined[v] !== 'boolean') { + warning("'{a}' is not defined.", token, v); + } + note_implied(token); + +// If the name is already defined in the current +// function, but not as outer, then there is a scope error. + + } else { + switch (funct[v]) { + case 'closure': + case 'function': + case 'var': + case 'unused': + warning("'{a}' used out of scope.", token, v); + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; + case 'outer': + case 'global': + break; + default: + +// If the name is defined in an outer function, make an outer entry, and if +// it was unused, make it var. + + if (s === true) { + funct[v] = true; + } else if (s === null) { + warning("'{a}' is not allowed.", token, v); + note_implied(token); + } else if (typeof s !== 'object') { + if (option.undef) { + warning("'{a}' is not defined.", token, v); + } else { + funct[v] = true; + } + note_implied(token); + } else { + switch (s[v]) { + case 'function': + case 'unction': + this['function'] = true; + s[v] = 'closure'; + funct[v] = s['(global)'] ? 'global' : 'outer'; + break; + case 'var': + case 'unused': + s[v] = 'closure'; + funct[v] = s['(global)'] ? 'global' : 'outer'; + break; + case 'closure': + case 'parameter': + funct[v] = s['(global)'] ? 'global' : 'outer'; + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; + } + } + } + } + return this; + }, + led: function () { + error("Expected an operator and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + }; + + type('(regexp)', function () { + return this; + }); + + +// ECMAScript parser + + ultimate('(begin)'); + ultimate('(end)'); + ultimate('(error)'); + delim(''); + delim('}'); + delim(')'); + delim(']'); + delim('"'); + delim("'"); + delim(';'); + delim(':'); + delim(','); + delim('#'); + delim('@'); + reserve('else'); + reserve('case'); + reserve('catch'); + reserve('default'); + reserve('finally'); + reservevar('arguments', function (x) { + if (strict_mode && funct['(global)']) { + warning("Strict violation.", x); + } else if (option.safe) { + warning("ADsafe violation.", x); + } + }); + reservevar('eval', function (x) { + if (option.safe) { + warning("ADsafe violation.", x); + } + }); + reservevar('false'); + reservevar('Infinity'); + reservevar('NaN'); + reservevar('null'); + reservevar('this', function (x) { + if (strict_mode && ((funct['(statement)'] && //// correct this test. + funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) { + warning("Strict violation.", x); + } else if (option.safe) { + warning("ADsafe violation.", x); + } + }); + reservevar('true'); + reservevar('undefined'); + assignop('='); + assignop('+='); + assignop('-='); + assignop('*='); + assignop('/=').nud = function () { + error("A regular expression literal can be confused with '/='."); + }; + assignop('%='); + assignop('&=', true); + assignop('|=', true); + assignop('^=', true); + assignop('<<=', true); + assignop('>>=', true); + assignop('>>>=', true); + infix('?', 30, function (left, that) { + that.first = left; + that.second = expression(10); + spaces(); + advance(':'); + spaces(); + that.third = expression(10); + return that; + }); + + infix('||', 40); + infix('&&', 50); + bitwise('|', 70); + bitwise('^', 80); + bitwise('&', 90); + relation('==', '==='); + relation('==='); + relation('!=', '!=='); + relation('!=='); + relation('<'); + relation('>'); + relation('<='); + relation('>='); + bitwise('<<', 120); + bitwise('>>', 120); + bitwise('>>>', 120); + infix('in', 120); + infix('instanceof', 120); + infix('+', 130, function (left, that) { + var right = expression(130); + if (left && right && left.id === '(string)' && right.id === '(string)') { + left.value += right.value; + left.thru = right.thru; + if (jx.test(left.value)) { + warning("JavaScript URL.", left); + } + return left; + } + that.first = left; + that.second = right; + return that; + }); + prefix('+', 'num'); + prefix('+++', function () { + warning("Confusing pluses."); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('+++', 130, function (left) { + warning("Confusing pluses."); + this.first = left; + this.second = expression(130); + return this; + }); + infix('-', 130); + prefix('-'); + prefix('---', function () { + warning("Confusing minuses."); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('---', 130, function (left) { + warning("Confusing minuses."); + this.first = left; + this.second = expression(130); + return this; + }); + infix('*', 140); + infix('/', 140); + infix('%', 140); + + suffix('++'); + prefix('++'); + + suffix('--'); + prefix('--'); + prefix('delete', function () { + one_space(); + var p = expression(0); + if (!p || (p.id !== '.' && p.id !== '[')) { + warning("Only properties should be deleted."); + } + this.first = p; + return this; + }); + + + prefix('~', function () { + no_space_only(); + if (option.bitwise) { + warning("Unexpected '{a}'.", this, '~'); + } + expression(150); + return this; + }); + prefix('!', function () { + no_space_only(); + this.first = expression(150); + this.arity = 'prefix'; + if (bang[this.first.id] === true) { + warning("Confusing use of '{a}'.", this, '!'); + } + return this; + }); + prefix('typeof'); + prefix('new', function () { + one_space(); + var c = expression(160), i; + if (c.id !== 'function') { + if (c.identifier) { + switch (c.value) { + case 'Object': + warning("Use the object literal notation {}.", token); + break; + case 'Array': + if (nexttoken.id !== '(') { + warning("Use the array literal notation [].", token); + } else { + advance('('); + if (nexttoken.id === ')') { + warning("Use the array literal notation [].", token); + } + advance(')'); + } + this.first = c; + return this; + case 'Number': + case 'String': + case 'Boolean': + case 'Math': + case 'JSON': + warning("Do not use {a} as a constructor.", token, c.value); + break; + case 'Function': + if (!option.evil) { + warning("The Function constructor is eval."); + } + break; + case 'Date': + case 'RegExp': + break; + default: + if (c.id !== 'function') { + i = c.value.substr(0, 1); + if (option.newcap && (i < 'A' || i > 'Z')) { + warning( + "A constructor name should start with an uppercase letter.", + token); + } + } + } + } else { + if (c.id !== '.' && c.id !== '[' && c.id !== '(') { + warning("Bad constructor.", token); + } + } + } else { + warning("Weird construction. Delete 'new'.", this); + } + if (nexttoken.id !== '(') { + warning("Missing '()' invoking a constructor."); + } + this.first = c; + return this; + }); + + infix('(', 160, function (left, that) { + no_space_only(prevtoken, token); + if (!left.immed && left.id === 'function') { + warning("Wrap an immediate function invocation in parentheses " + + "to assist the reader in understanding that the expression " + + "is the result of a function, and not the function itself."); + } + var p = []; + if (left) { + if (left.type === '(identifier)') { + if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if (left.value !== 'Number' && left.value !== 'String' && + left.value !== 'Boolean' && + left.value !== 'Date') { + if (left.value === 'Math') { + warning("Math is not a function.", left); + } else if (option.newcap) { + warning( +"Missing 'new' prefix when invoking a constructor.", left); + } + } + } + } else if (left.id === '.') { + if (option.safe && left.first.value === 'Math' && + left.second === 'random') { + warning("ADsafe violation.", left); + } + } + } + if (nexttoken.id !== ')') { + no_space(); + for (;;) { + p.push(expression(10)); + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + no_space(); + advance(')'); + if (typeof left === 'object') { + if (left.value === 'parseInt' && p.length === 1) { + warning("Missing radix parameter.", left); + } + if (!option.evil) { + if (left.value === 'eval' || left.value === 'Function' || + left.value === 'execScript') { + warning("eval is evil.", left); + } else if (p[0] && p[0].id === '(string)' && + (left.value === 'setTimeout' || + left.value === 'setInterval')) { + warning( + "Implied eval is evil. Pass a function instead of a string.", left); + } + } + if (!left.identifier && left.id !== '.' && left.id !== '[' && + left.id !== '(' && left.id !== '&&' && left.id !== '||' && + left.id !== '?') { + warning("Bad invocation.", left); + } + } + that.first = left; + that.second = p; + return that; + }, true); + + prefix('(', function () { + no_space(); + if (nexttoken.id === 'function') { + nexttoken.immed = true; + } + var v = expression(0); + no_space(); + advance(')', this); + if (v.id === 'function') { + if (nexttoken.id === '(') { + warning( +"Move the invocation into the parens that contain the function.", nexttoken); + } else { + warning( +"Do not wrap function literals in parens unless they are to be immediately invoked.", + this); + } + } + return v; + }); + + infix('.', 170, function (left, that) { + no_space(prevtoken, token); + no_space(); + var m = identifier(); + if (typeof m === 'string') { + countMember(m); + } + that.first = left; + that.second = m; + if (left && left.value === 'arguments' && + (m === 'callee' || m === 'caller')) { + warning("Avoid arguments.{a}.", left, m); + } else if (!option.evil && left && left.value === 'document' && + (m === 'write' || m === 'writeln')) { + warning("document.write can be a form of eval.", left); + } else if (option.adsafe) { + if (left && left.value === 'ADSAFE') { + if (m === 'id' || m === 'lib') { + warning("ADsafe violation.", that); + } else if (m === 'go') { + if (xmode !== 'script') { + warning("ADsafe violation.", that); + } else if (adsafe_went || nexttoken.id !== '(' || + peek(0).id !== '(string)' || + peek(0).value !== adsafe_id || + peek(1).id !== ',') { + error("ADsafe violation: go.", that); + } + adsafe_went = true; + adsafe_may = false; + } + } + } + if (!option.evil && (m === 'eval' || m === 'execScript')) { + warning('eval is evil.'); + } else if (option.safe) { + for (;;) { + if (banned[m] === true) { + warning("ADsafe restricted word '{a}'.", token, m); + } + if (typeof predefined[left.value] !== 'boolean' || + nexttoken.id === '(') { + break; + } + if (standard_member[m] === true) { + if (nexttoken.id === '.') { + warning("ADsafe violation.", that); + } + break; + } + if (nexttoken.id !== '.') { + warning("ADsafe violation.", that); + break; + } + advance('.'); + token.first = that; + token.second = m; + that = token; + m = identifier(); + if (typeof m === 'string') { + countMember(m); + } + } + } + return that; + }, true); + + infix('[', 170, function (left, that) { + no_space_only(prevtoken, token); + no_space(); + var e = expression(0), s; + if (e && e.type === '(string)') { + if (option.safe && banned[e.value] === true) { + warning("ADsafe restricted word '{a}'.", that, e.value); + } else if (!option.evil && + (e.value === 'eval' || e.value === 'execScript')) { + warning("eval is evil.", that); + } else if (option.safe && + (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) { + warning("ADsafe restricted subscript '{a}'.", that, e.value); + } + countMember(e.value); + if (!option.sub && ix.test(e.value)) { + s = syntax[e.value]; + if (!s || !s.reserved) { + warning("['{a}'] is better written in dot notation.", + e, e.value); + } + } + } else if (!e || e.type !== '(number)' || e.value < 0) { + if (option.safe) { + warning('ADsafe subscripting.'); + } + } + advance(']', that); + no_space(prevtoken, token); + that.first = left; + that.second = e; + return that; + }, true); + + prefix('[', function () { + this.first = []; + while (nexttoken.id !== '(end)') { + while (nexttoken.id === ',') { + warning("Extra comma."); + advance(','); + } + if (nexttoken.id === ']') { + break; + } + this.first.push(expression(10)); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.id === ']' && !option.es5) { + warning("Extra comma.", token); + break; + } + } else { + break; + } + } + advance(']', this); + return this; + }, 170); + + + function property_name() { + var id = optionalidentifier(true); + if (!id) { + if (nexttoken.id === '(string)') { + id = nexttoken.value; + if (option.adsafe && + (id.charAt(0) === '_' || + id.charAt(id.length - 1) === '_')) { + warning("Unexpected {a} in '{b}'.", token, + "dangling '_'", id); + } + advance(); + } else if (nexttoken.id === '(number)') { + id = nexttoken.value.toString(); + advance(); + } + } + return id; + } + + + function functionparams() { + var i, t = nexttoken, p = []; + advance('('); + no_space(); + if (nexttoken.id === ')') { + no_space(); + advance(')'); + return; + } + for (;;) { + i = identifier(); + p.push(i); + addlabel(i, 'parameter'); + if (nexttoken.id === ',') { + comma(); + } else { + no_space(); + advance(')', t); + return p; + } + } + } + + + function doFunction(func, name) { + var s = scope; + scope = Object.create(s); + funct = { + '(name)' : name || '"' + anonname + '"', + '(line)' : nexttoken.line, + '(context)' : funct, + '(breakage)' : 0, + '(loopage)' : 0, + '(scope)' : scope, + '(token)' : func + }; + token.funct = funct; + functions.push(funct); + if (name) { + addlabel(name, 'function'); + } + func.name = name || ''; + func.first = funct['(params)'] = functionparams(); + func.block = block(false); + + scope = s; + funct['(last)'] = token.line; + funct = funct['(context)']; + return func; + } + + + prefix('{', function () { + var get, i, j, name, p, set, seen = {}, t; + this.arity = 'prefix'; + this.first = []; + while (nexttoken.id !== '}') { + +// JSLint recognizes the ES5 extension for get/set in object literals, +// but requires that they be used in pairs. + + if (nexttoken.value === 'get' && peek().id !== ':') { + if (!option.es5) { + warning("get/set are ES5 features."); + } + get = nexttoken; + one_space_only(); + advance('get'); + name = nexttoken; + i = property_name(); + if (!i) { + error("Missing property name."); + } + doFunction(get, ''); + if (funct['(loopage)']) { + warning("Don't make functions within a loop.", t); + } + p = get.first; + if (p) { + warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); + } + comma(); + set = nexttoken; + spaces(); + advance('set'); + one_space_only(); + j = property_name(); + if (i !== j) { + error("Expected '{a}' and instead saw '{b}'.", token, i, j); + } + doFunction(set, ''); + p = set.first; + if (!p || p.length !== 1 || p[0] !== 'value') { + warning("Expected (value) in set {a} function.", t, i); + } + name.first = [get, set]; + } else { + name = nexttoken; + i = property_name(); + if (typeof i !== 'string') { + error("Missing property name."); + } + advance(':'); + spaces(); + name.first = expression(10); + } + this.first.push(name); + if (seen[i] === true) { + warning("Duplicate member '{a}'.", nexttoken, i); + } + seen[i] = true; + countMember(i); + if (nexttoken.id !== ',') { + break; + } + for (;;) { + comma(); + if (nexttoken.id !== ',') { + break; + } + warning("Extra comma."); + } + if (nexttoken.id === '}' && !option.es5) { + warning("Extra comma.", token); + } + } + advance('}', this); + return this; + }); + + stmt('{', function () { + warning("Expected to see a statement and instead saw a block."); + this.arity = 'statement'; + this.block = statements(); + this.disrupt = this.block.disrupt; + advance('}'); + return this; + }); + + + stmt('var', function () { + +// JavaScript does not have block scope. It only has function scope. So, +// declaring a variable in a block can have unexpected consequences. + +// var.first will contain an array, the array containing name tokens +// and assignment tokens. + + var assign, id, name; + + if (funct['(onevar)'] && option.onevar) { + warning("Too many var statements."); + } else if (!funct['(global)']) { + funct['(onevar)'] = true; + } + this.arity = 'statement'; + this.first = []; + for (;;) { + spaces(); + name = nexttoken; + id = identifier(); + if (funct['(global)'] && predefined[id] === false) { + warning("Redefinition of '{a}'.", token, id); + } + addlabel(id, 'unused'); + + if (nexttoken.id === '=') { + assign = nexttoken; + assign.first = name; + spaces(); + advance('='); + spaces(); + if (nexttoken.id === 'undefined') { + warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); + } + if (peek(0).id === '=' && nexttoken.identifier) { + error("Variable {a} was not declared correctly.", + nexttoken, nexttoken.value); + } + assign.second = expression(0); + assign.arity = 'infix'; + this.first.push(assign); + } else { + this.first.push(name); + } + if (nexttoken.id !== ',') { + break; + } + comma(); + } + return this; + }); + + stmt('function', function () { + one_space(); + if (inblock) { + warning( +"Function statements should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token); + } + var i = identifier(); + if (i) { + addlabel(i, 'unction'); + no_space_only(); + } + doFunction(this, i, true); + if (nexttoken.id === '(' && nexttoken.line === token.line) { + error( +"Function statements are not invocable. Wrap the whole function invocation in parens."); + } + this.arity = 'statement'; + return this; + }); + + prefix('function', function () { + one_space(); + var i = optionalidentifier(); + if (i) { + no_space_only(); + } + doFunction(this, i); + if (funct['(loopage)']) { + warning("Don't make functions within a loop."); + } + this.arity = 'function'; + return this; + }); + + stmt('if', function () { + var t = nexttoken; + one_space(); + advance('('); + no_space(); + this.arity = 'statement'; + this.first = expression(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + expression(20); + } + no_space(); + advance(')', t); + one_space_only(); + this.block = block(true); + if (nexttoken.id === 'else') { + one_space(); + advance('else'); + one_space_only(); + this['else'] = nexttoken.id === 'if' || nexttoken.id === 'switch' ? + statement(true) : block(true); + if (this['else'].disrupt && this.block.disrupt) { + this.disrupt = true; + } + } + return this; + }); + + stmt('try', function () { + +// try.first The catch variable +// try.second The catch clause +// try.third The finally clause +// try.block The try block + + var b, e, s, t; + if (option.adsafe) { + warning("ADsafe try violation.", this); + } + one_space_only(); + this.arity = 'statement'; + this.block = block(false); + if (nexttoken.id === 'catch') { + one_space(); + advance('catch'); + one_space(); + advance('('); + no_space_only(); + s = scope; + scope = Object.create(s); + e = nexttoken.value; + this.first = e; + if (nexttoken.type !== '(identifier)') { + warning("Expected an identifier and instead saw '{a}'.", + nexttoken, e); + } else { + addlabel(e, 'exception'); + } + advance(); + no_space_only(); + advance(')'); + one_space(); + this.second = block(false); + b = true; + scope = s; + } + if (nexttoken.id === 'finally') { + one_space(); + t = nexttoken; + advance('finally'); + one_space(); + this.third = block(false); + } else if (!b) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, 'catch', nexttoken.value); + } + return this; + }); + + + stmt('while', function () { + one_space(); + var t = nexttoken; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + no_space(); + this.arity = 'statement'; + this.first = expression(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + expression(20); + } + no_space(); + advance(')', t); + one_space_only(); + this.block = block(true); + if (this.block.disrupt) { + warning("Strange loop.", prevtoken); + } + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + reserve('with'); + + stmt('switch', function () { + +// switch.first the switch expression +// switch.second the array of cases. A case is 'case' or 'default' token: +// case.first the array of case expressions +// case.second the array of statements +// If all of the arrays of statements are disrupt, then the switch is disrupt. + + var b = true, + s, + t = nexttoken; + funct['(breakage)'] += 1; + one_space(); + advance('('); + no_space(); + this.arity = 'statement'; + this.first = expression(20); + no_space(); + advance(')', t); + one_space_only(); + advance('{'); + this.second = []; + while (nexttoken.id === 'case') { + t = nexttoken; + t.first = []; + do { + spaces(); + advance('case'); + one_space(); + t.first.push(expression(0)); + no_space_only(); + advance(':'); + } while (nexttoken.id === 'case'); + spaces(); + t.second = statements(); + if (t.second && t.second.length > 0) { + s = t.second[t.second.length - 1]; + if (s.disrupt) { + if (s.id === 'break') { + b = false; + } + } else { + warning("Missing break after case."); + } + } else { + warning("Empty case"); + } + this.second.push(t); + } + if (this.second.length === 0) { + warning("switch without cases."); + } + if (nexttoken.id === 'default') { + spaces(); + t = nexttoken; + advance('default'); + no_space_only(); + advance(':'); + spaces(); + t.second = statements(); + if (t.second && t.second.length > 0) { + s = t.second[t.second.length - 1]; + if (b && s.disrupt && s.id !== 'break') { + this.disrupt = true; + } + } + this.second.push(t); + } + funct['(breakage)'] -= 1; + spaces(); + advance('}'); + return this; + }); + + stmt('debugger', function () { + if (!option.debug) { + warning("All 'debugger' statements should be removed."); + } + this.arity = 'statement'; + return this; + }); + + stmt('do', function () { + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + one_space_only(); + this.arity = 'statement'; + this.block = block(true); + if (this.block.disrupt) { + warning("Strange loop.", prevtoken); + } + one_space(); + advance('while'); + var t = nexttoken; + one_space_only(); + advance('('); + no_space(); + this.first = expression(0); + if (this.first.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + } + no_space(); + advance(')', t); + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + stmt('for', function () { + var f = option.forin, i, s, t = nexttoken, v; + this.arity = 'statement'; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + spaces(this, t); + no_space(); + if (nexttoken.id === 'var') { + error("Move all 'var' declarations to the top of the function."); + } + if (peek(0).id === 'in') { + v = nexttoken; + switch (funct[v.value]) { + case 'unused': + funct[v.value] = 'var'; + break; + case 'var': + break; + default: + warning("Bad for in variable '{a}'.", v, v.value); + } + advance(); + i = nexttoken; + advance('in'); + i.first = v; + i.second = expression(20); + advance(')', t); + this.first = i; + s = block(true); + if (!f && (s.length > 1 || typeof s[0] !== 'object' || + s[0].value !== 'if')) { + warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this); + } + } else { + if (nexttoken.id !== ';') { + this.first = []; + for (;;) { + this.first.push(expression(0, 'for')); + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + semicolon(); + if (nexttoken.id !== ';') { + this.second = expression(20); + if (this.second.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + } + } + semicolon(token); + if (nexttoken.id === ';') { + error("Expected '{a}' and instead saw '{b}'.", nexttoken, ')', ';'); + } + if (nexttoken.id !== ')') { + this.third = []; + for (;;) { + this.third.push(expression(0, 'for')); + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + no_space(); + advance(')', t); + one_space_only(); + s = block(true); + } + if (s.disrupt) { + warning("Strange loop.", prevtoken); + } + this.block = s; + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + + disruptstmt('break', function () { + var v = nexttoken.value; + this.arity = 'statement'; + if (funct['(breakage)'] === 0) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + if (nexttoken.identifier && token.line === nexttoken.line) { + one_space_only(); + if (funct[v] !== 'label') { + warning("'{a}' is not a label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + return this; + }); + + + disruptstmt('continue', function () { + var v = nexttoken.value; + this.arity = 'statement'; + if (funct['(breakage)'] === 0) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + if (nexttoken.identifier && token.line === nexttoken.line) { + one_space_only(); + if (funct[v] !== 'label') { + warning("'{a}' is not a label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + return this; + }); + + + disruptstmt('return', function () { + this.arity = 'statement'; + if (nexttoken.id !== ';' && nexttoken.line === token.line) { + one_space_only(); + if (nexttoken.id === '/' || nexttoken.id === '(regexp)') { + warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); + } + this.first = expression(20); + } + return this; + }); + + + disruptstmt('throw', function () { + this.arity = 'statement'; + one_space_only(); + this.first = expression(20); + return this; + }); + + reserve('void'); + +// Superfluous reserved words + + reserve('class'); + reserve('const'); + reserve('enum'); + reserve('export'); + reserve('extends'); + reserve('import'); + reserve('super'); + +// Harmony reserved words + + reserve('let'); + reserve('yield'); + reserve('implements'); + reserve('interface'); + reserve('package'); + reserve('private'); + reserve('protected'); + reserve('public'); + reserve('static'); + + +// Parse JSON + + function jsonValue() { + + function jsonObject() { + var o = {}, t = nexttoken; + advance('{'); + if (nexttoken.id !== '}') { + for (;;) { + if (nexttoken.id === '(end)') { + error("Missing '}' to match '{' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === '}') { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ',') { + error("Unexpected comma.", nexttoken); + } else if (nexttoken.id !== '(string)') { + warning("Expected a string and instead saw {a}.", + nexttoken, nexttoken.value); + } + if (o[nexttoken.value] === true) { + warning("Duplicate key '{a}'.", + nexttoken, nexttoken.value); + } else if (nexttoken.value === '__proto__') { + warning("Stupid key '{a}'.", + nexttoken, nexttoken.value); + } else { + o[nexttoken.value] = true; + } + advance(); + advance(':'); + jsonValue(); + if (nexttoken.id !== ',') { + break; + } + advance(','); + } + } + advance('}'); + } + + function jsonArray() { + var t = nexttoken; + advance('['); + if (nexttoken.id !== ']') { + for (;;) { + if (nexttoken.id === '(end)') { + error("Missing ']' to match '[' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === ']') { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ',') { + error("Unexpected comma.", nexttoken); + } + jsonValue(); + if (nexttoken.id !== ',') { + break; + } + advance(','); + } + } + advance(']'); + } + + switch (nexttoken.id) { + case '{': + jsonObject(); + break; + case '[': + jsonArray(); + break; + case 'true': + case 'false': + case 'null': + case '(number)': + case '(string)': + advance(); + break; + case '-': + advance('-'); + if (token.thru !== nexttoken.from) { + warning("Unexpected space after '-'.", token); + } + no_space_only(); + advance('(number)'); + break; + default: + error("Expected a JSON value.", nexttoken); + } + } + + +// CSS parsing. + + function cssName() { + if (nexttoken.identifier) { + advance(); + return true; + } + } + + + function cssNumber() { + if (nexttoken.id === '-') { + advance('-'); + no_space_only(); + } + if (nexttoken.type === '(number)') { + advance('(number)'); + return true; + } + } + + + function cssString() { + if (nexttoken.type === '(string)') { + advance(); + return true; + } + } + + function cssColor() { + var i, number, value; + if (nexttoken.identifier) { + value = nexttoken.value; + if (value === 'rgb' || value === 'rgba') { + advance(); + advance('('); + for (i = 0; i < 3; i += 1) { + if (i) { + comma(); + } + number = nexttoken.value; + if (nexttoken.type !== '(number)' || number < 0) { + warning("Expected a positive number and instead saw '{a}'", + nexttoken, number); + advance(); + } else { + advance(); + if (nexttoken.id === '%') { + advance('%'); + if (number > 100) { + warning("Expected a percentage and instead saw '{a}'", + token, number); + } + } else { + if (number > 255) { + warning("Expected a small number and instead saw '{a}'", + token, number); + } + } + } + } + if (value === 'rgba') { + comma(); + number = +nexttoken.value; + if (nexttoken.type !== '(number)' || number < 0 || number > 1) { + warning("Expected a number between 0 and 1 and instead saw '{a}'", + nexttoken, number); + } + advance(); + if (nexttoken.id === '%') { + warning("Unexpected '%'."); + advance('%'); + } + } + advance(')'); + return true; + } else if (cssColorData[nexttoken.value] === true) { + advance(); + return true; + } + } else if (nexttoken.type === '(color)') { + advance(); + return true; + } + return false; + } + + + function cssLength() { + if (nexttoken.id === '-') { + advance('-'); + no_space_only(); + } + if (nexttoken.type === '(number)') { + advance(); + if (nexttoken.type !== '(string)' && + cssLengthData[nexttoken.value] === true) { + no_space_only(); + advance(); + } else if (+token.value !== 0) { + warning("Expected a linear unit and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + return true; + } + return false; + } + + + function cssLineHeight() { + if (nexttoken.id === '-') { + advance('-'); + no_space_only(); + } + if (nexttoken.type === '(number)') { + advance(); + if (nexttoken.type !== '(string)' && + cssLengthData[nexttoken.value] === true) { + no_space_only(); + advance(); + } + return true; + } + return false; + } + + + function cssWidth() { + if (nexttoken.identifier) { + switch (nexttoken.value) { + case 'thin': + case 'medium': + case 'thick': + advance(); + return true; + } + } else { + return cssLength(); + } + } + + + function cssMargin() { + if (nexttoken.identifier) { + if (nexttoken.value === 'auto') { + advance(); + return true; + } + } else { + return cssLength(); + } + } + + function cssAttr() { + if (nexttoken.identifier && nexttoken.value === 'attr') { + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + advance(')'); + return true; + } + return false; + } + + + function cssCommaList() { + while (nexttoken.id !== ';') { + if (!cssName() && !cssString()) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + if (nexttoken.id !== ',') { + return true; + } + comma(); + } + } + + + function cssCounter() { + if (nexttoken.identifier && nexttoken.value === 'counter') { + advance(); + advance('('); + advance(); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(')'); + return true; + } + if (nexttoken.identifier && nexttoken.value === 'counters') { + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(')'); + return true; + } + return false; + } + + + function cssShape() { + var i; + if (nexttoken.identifier && nexttoken.value === 'rect') { + advance(); + advance('('); + for (i = 0; i < 4; i += 1) { + if (!cssLength()) { + warning("Expected a number and instead saw '{a}'.", + nexttoken, nexttoken.value); + break; + } + } + advance(')'); + return true; + } + return false; + } + + + function cssUrl() { + var c, url; + if (nexttoken.identifier && nexttoken.value === 'url') { + nexttoken = lex.range('(', ')'); + url = nexttoken.value; + c = url.charAt(0); + if (c === '"' || c === '\'') { + if (url.slice(-1) !== c) { + warning("Bad url string."); + } else { + url = url.slice(1, -1); + if (url.indexOf(c) >= 0) { + warning("Bad url string."); + } + } + } + if (!url) { + warning("Missing url."); + } + advance(); + if (option.safe && ux.test(url)) { + error("ADsafe URL violation."); + } + urls.push(url); + return true; + } + return false; + } + + + cssAny = [cssUrl, function () { + for (;;) { + if (nexttoken.identifier) { + switch (nexttoken.value.toLowerCase()) { + case 'url': + cssUrl(); + break; + case 'expression': + warning("Unexpected expression '{a}'.", + nexttoken, nexttoken.value); + advance(); + break; + default: + advance(); + } + } else { + if (nexttoken.id === ';' || nexttoken.id === '!' || + nexttoken.id === '(end)' || nexttoken.id === '}') { + return true; + } + advance(); + } + } + }]; + + + cssBorderStyle = [ + 'none', 'dashed', 'dotted', 'double', 'groove', + 'hidden', 'inset', 'outset', 'ridge', 'solid' + ]; + + cssBreak = [ + 'auto', 'always', 'avoid', 'left', 'right' + ]; + + cssMedia = { + 'all': true, + 'braille': true, + 'embossed': true, + 'handheld': true, + 'print': true, + 'projection': true, + 'screen': true, + 'speech': true, + 'tty': true, + 'tv': true + }; + + cssOverflow = [ + 'auto', 'hidden', 'scroll', 'visible' + ]; + + cssAttributeData = { + background: [ + true, 'background-attachment', 'background-color', + 'background-image', 'background-position', 'background-repeat' + ], + 'background-attachment': ['scroll', 'fixed'], + 'background-color': ['transparent', cssColor], + 'background-image': ['none', cssUrl], + 'background-position': [ + 2, [cssLength, 'top', 'bottom', 'left', 'right', 'center'] + ], + 'background-repeat': [ + 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' + ], + 'border': [true, 'border-color', 'border-style', 'border-width'], + 'border-bottom': [ + true, 'border-bottom-color', 'border-bottom-style', + 'border-bottom-width' + ], + 'border-bottom-color': cssColor, + 'border-bottom-style': cssBorderStyle, + 'border-bottom-width': cssWidth, + 'border-collapse': ['collapse', 'separate'], + 'border-color': ['transparent', 4, cssColor], + 'border-left': [ + true, 'border-left-color', 'border-left-style', 'border-left-width' + ], + 'border-left-color': cssColor, + 'border-left-style': cssBorderStyle, + 'border-left-width': cssWidth, + 'border-right': [ + true, 'border-right-color', 'border-right-style', + 'border-right-width' + ], + 'border-right-color': cssColor, + 'border-right-style': cssBorderStyle, + 'border-right-width': cssWidth, + 'border-spacing': [2, cssLength], + 'border-style': [4, cssBorderStyle], + 'border-top': [ + true, 'border-top-color', 'border-top-style', 'border-top-width' + ], + 'border-top-color': cssColor, + 'border-top-style': cssBorderStyle, + 'border-top-width': cssWidth, + 'border-width': [4, cssWidth], + bottom: [cssLength, 'auto'], + 'caption-side' : ['bottom', 'left', 'right', 'top'], + clear: ['both', 'left', 'none', 'right'], + clip: [cssShape, 'auto'], + color: cssColor, + content: [ + 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', + cssString, cssUrl, cssCounter, cssAttr + ], + 'counter-increment': [ + cssName, 'none' + ], + 'counter-reset': [ + cssName, 'none' + ], + cursor: [ + cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', + 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', + 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait' + ], + direction: ['ltr', 'rtl'], + display: [ + 'block', 'compact', 'inline', 'inline-block', 'inline-table', + 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', + 'table-cell', 'table-column', 'table-column-group', + 'table-footer-group', 'table-header-group', 'table-row', + 'table-row-group' + ], + 'empty-cells': ['show', 'hide'], + 'float': ['left', 'none', 'right'], + font: [ + 'caption', 'icon', 'menu', 'message-box', 'small-caption', + 'status-bar', true, 'font-size', 'font-style', 'font-weight', + 'font-family' + ], + 'font-family': cssCommaList, + 'font-size': [ + 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', + 'xx-large', 'larger', 'smaller', cssLength + ], + 'font-size-adjust': ['none', cssNumber], + 'font-stretch': [ + 'normal', 'wider', 'narrower', 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', + 'semi-expanded', 'expanded', 'extra-expanded' + ], + 'font-style': [ + 'normal', 'italic', 'oblique' + ], + 'font-variant': [ + 'normal', 'small-caps' + ], + 'font-weight': [ + 'normal', 'bold', 'bolder', 'lighter', cssNumber + ], + height: [cssLength, 'auto'], + left: [cssLength, 'auto'], + 'letter-spacing': ['normal', cssLength], + 'line-height': ['normal', cssLineHeight], + 'list-style': [ + true, 'list-style-image', 'list-style-position', 'list-style-type' + ], + 'list-style-image': ['none', cssUrl], + 'list-style-position': ['inside', 'outside'], + 'list-style-type': [ + 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', + 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', + 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', + 'hiragana-iroha', 'katakana-oroha', 'none' + ], + margin: [4, cssMargin], + 'margin-bottom': cssMargin, + 'margin-left': cssMargin, + 'margin-right': cssMargin, + 'margin-top': cssMargin, + 'marker-offset': [cssLength, 'auto'], + 'max-height': [cssLength, 'none'], + 'max-width': [cssLength, 'none'], + 'min-height': cssLength, + 'min-width': cssLength, + opacity: cssNumber, + outline: [true, 'outline-color', 'outline-style', 'outline-width'], + 'outline-color': ['invert', cssColor], + 'outline-style': [ + 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', + 'outset', 'ridge', 'solid' + ], + 'outline-width': cssWidth, + overflow: cssOverflow, + 'overflow-x': cssOverflow, + 'overflow-y': cssOverflow, + padding: [4, cssLength], + 'padding-bottom': cssLength, + 'padding-left': cssLength, + 'padding-right': cssLength, + 'padding-top': cssLength, + 'page-break-after': cssBreak, + 'page-break-before': cssBreak, + position: ['absolute', 'fixed', 'relative', 'static'], + quotes: [8, cssString], + right: [cssLength, 'auto'], + 'table-layout': ['auto', 'fixed'], + 'text-align': ['center', 'justify', 'left', 'right'], + 'text-decoration': [ + 'none', 'underline', 'overline', 'line-through', 'blink' + ], + 'text-indent': cssLength, + 'text-shadow': ['none', 4, [cssColor, cssLength]], + 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], + top: [cssLength, 'auto'], + 'unicode-bidi': ['normal', 'embed', 'bidi-override'], + 'vertical-align': [ + 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', + 'text-bottom', cssLength + ], + visibility: ['visible', 'hidden', 'collapse'], + 'white-space': [ + 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' + ], + width: [cssLength, 'auto'], + 'word-spacing': ['normal', cssLength], + 'word-wrap': ['break-word', 'normal'], + 'z-index': ['auto', cssNumber] + }; + + function styleAttribute() { + var v; + while (nexttoken.id === '*' || nexttoken.id === '#' || + nexttoken.value === '_') { + if (!option.css) { + warning("Unexpected '{a}'.", nexttoken, nexttoken.value); + } + advance(); + } + if (nexttoken.id === '-') { + if (!option.css) { + warning("Unexpected '{a}'.", nexttoken, nexttoken.value); + } + advance('-'); + if (!nexttoken.identifier) { + warning( +"Expected a non-standard style attribute and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + return cssAny; + } else { + if (!nexttoken.identifier) { + warning("Excepted a style attribute, and instead saw '{a}'.", + nexttoken, nexttoken.value); + } else { + if (is_own(cssAttributeData, nexttoken.value)) { + v = cssAttributeData[nexttoken.value]; + } else { + v = cssAny; + if (!option.css) { + warning("Unrecognized style attribute '{a}'.", + nexttoken, nexttoken.value); + } + } + } + advance(); + return v; + } + } + + + function styleValue(v) { + var i = 0, + n, + once, + match, + round, + start = 0, + vi; + switch (typeof v) { + case 'function': + return v(); + case 'string': + if (nexttoken.identifier && nexttoken.value === v) { + advance(); + return true; + } + return false; + } + for (;;) { + if (i >= v.length) { + return false; + } + vi = v[i]; + i += 1; + if (vi === true) { + break; + } else if (typeof vi === 'number') { + n = vi; + vi = v[i]; + i += 1; + } else { + n = 1; + } + match = false; + while (n > 0) { + if (styleValue(vi)) { + match = true; + n -= 1; + } else { + break; + } + } + if (match) { + return true; + } + } + start = i; + once = []; + for (;;) { + round = false; + for (i = start; i < v.length; i += 1) { + if (!once[i]) { + if (styleValue(cssAttributeData[v[i]])) { + match = true; + round = true; + once[i] = true; + break; + } + } + } + if (!round) { + return match; + } + } + } + + function styleChild() { + if (nexttoken.id === '(number)') { + advance(); + if (nexttoken.value === 'n' && nexttoken.identifier) { + no_space_only(); + advance(); + if (nexttoken.id === '+') { + no_space_only(); + advance('+'); + no_space_only(); + advance('(number)'); + } + } + return; + } else { + if (nexttoken.identifier && + (nexttoken.value === 'odd' || nexttoken.value === 'even')) { + advance(); + return; + } + } + warning("Unexpected token '{a}'.", nexttoken, nexttoken.value); + } + + function substyle() { + var v; + for (;;) { + if (nexttoken.id === '}' || nexttoken.id === '(end)' || + xquote && nexttoken.id === xquote) { + return; + } + while (nexttoken.id === ';') { + warning("Misplaced ';'."); + advance(';'); + } + v = styleAttribute(); + advance(':'); + if (nexttoken.identifier && nexttoken.value === 'inherit') { + advance(); + } else { + if (!styleValue(v)) { + warning("Unexpected token '{a}'.", nexttoken, + nexttoken.value); + advance(); + } + } + if (nexttoken.id === '!') { + advance('!'); + no_space_only(); + if (nexttoken.identifier && nexttoken.value === 'important') { + advance(); + } else { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, 'important', nexttoken.value); + } + } + if (nexttoken.id === '}' || nexttoken.id === xquote) { + warning("Missing '{a}'.", nexttoken, ';'); + } else { + semicolon(); + } + } + } + + function styleSelector() { + if (nexttoken.identifier) { + if (!is_own(htmltag, option.cap ? + nexttoken.value.toLowerCase() : nexttoken.value)) { + warning("Expected a tagName, and instead saw {a}.", + nexttoken, nexttoken.value); + } + advance(); + } else { + switch (nexttoken.id) { + case '>': + case '+': + advance(); + styleSelector(); + break; + case ':': + advance(':'); + switch (nexttoken.value) { + case 'active': + case 'after': + case 'before': + case 'checked': + case 'disabled': + case 'empty': + case 'enabled': + case 'first-child': + case 'first-letter': + case 'first-line': + case 'first-of-type': + case 'focus': + case 'hover': + case 'last-child': + case 'last-of-type': + case 'link': + case 'only-of-type': + case 'root': + case 'target': + case 'visited': + advance(); + break; + case 'lang': + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a lang code, and instead saw :{a}.", + nexttoken, nexttoken.value); + } + advance(')'); + break; + case 'nth-child': + case 'nth-last-child': + case 'nth-last-of-type': + case 'nth-of-type': + advance(); + advance('('); + styleChild(); + advance(')'); + break; + case 'not': + advance(); + advance('('); + if (nexttoken.id === ':' && peek(0).value === 'not') { + warning("Nested not."); + } + styleSelector(); + advance(')'); + break; + default: + warning("Expected a pseudo, and instead saw :{a}.", + nexttoken, nexttoken.value); + } + break; + case '#': + advance('#'); + if (!nexttoken.identifier) { + warning("Expected an id, and instead saw #{a}.", + nexttoken, nexttoken.value); + } + advance(); + break; + case '*': + advance('*'); + break; + case '.': + advance('.'); + if (!nexttoken.identifier) { + warning("Expected a class, and instead saw #.{a}.", + nexttoken, nexttoken.value); + } + advance(); + break; + case '[': + advance('['); + if (!nexttoken.identifier) { + warning("Expected an attribute, and instead saw [{a}].", + nexttoken, nexttoken.value); + } + advance(); + if (nexttoken.id === '=' || nexttoken.value === '~=' || + nexttoken.value === '$=' || + nexttoken.value === '|=' || + nexttoken.id === '*=' || + nexttoken.id === '^=') { + advance(); + if (nexttoken.type !== '(string)') { + warning("Expected a string, and instead saw {a}.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(']'); + break; + default: + error("Expected a CSS selector, and instead saw {a}.", + nexttoken, nexttoken.value); + } + } + } + + function stylePattern() { + if (nexttoken.id === '{') { + warning("Expected a style pattern, and instead saw '{a}'.", nexttoken, + nexttoken.id); + } + for (;;) { + styleSelector(); + if (nexttoken.id === ' fragments and .js files.", token); + } + if (option.fragment) { + if (n !== 'div') { + error("ADsafe violation: Wrap the widget in a div.", token); + } + } else { + error("Use the fragment option.", token); + } + } + option.browser = true; + assume(); + } + + function doAttribute(n, a, v) { + var u, x; + if (a === 'id') { + u = typeof v === 'string' ? v.toUpperCase() : ''; + if (ids[u] === true) { + warning("Duplicate id='{a}'.", nexttoken, v); + } + if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) { + warning("Bad id: '{a}'.", nexttoken, v); + } else if (option.adsafe) { + if (adsafe_id) { + if (v.slice(0, adsafe_id.length) !== adsafe_id) { + warning("ADsafe violation: An id must have a '{a}' prefix", + nexttoken, adsafe_id); + } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } else { + adsafe_id = v; + if (!/^[A-Z]+_$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } + } + x = v.search(dx); + if (x >= 0) { + warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'class' || a === 'type' || a === 'name') { + x = v.search(qx); + if (x >= 0) { + warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'href' || a === 'background' || + a === 'content' || a === 'data' || + a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { + if (option.safe && ux.test(v)) { + error("ADsafe URL violation."); + } + urls.push(v); + } else if (a === 'for') { + if (option.adsafe) { + if (adsafe_id) { + if (v.slice(0, adsafe_id.length) !== adsafe_id) { + warning("ADsafe violation: An id must have a '{a}' prefix", + nexttoken, adsafe_id); + } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } else { + warning("ADSAFE violation: bad id."); + } + } + } else if (a === 'name') { + if (option.adsafe && v.indexOf('_') >= 0) { + warning("ADsafe name violation."); + } + } + } + + function doTag(n, a) { + var i, t = htmltag[n], x; + src = false; + if (!t) { + error("Unrecognized tag '<{a}>'.", + nexttoken, + n === n.toLowerCase() ? n : + n + ' (capitalization error)'); + } + if (stack.length > 0) { + if (n === 'html') { + error("Too many tags.", token); + } + x = t.parent; + if (x) { + if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { + error("A '<{a}>' must be within '<{b}>'.", + token, n, x); + } + } else if (!option.adsafe && !option.fragment) { + i = stack.length; + do { + if (i <= 0) { + error("A '<{a}>' must be within '<{b}>'.", + token, n, 'body'); + } + i -= 1; + } while (stack[i].name !== 'body'); + } + } + switch (n) { + case 'div': + if (option.adsafe && stack.length === 1 && !adsafe_id) { + warning("ADSAFE violation: missing ID_."); + } + break; + case 'script': + xmode = 'script'; + advance('>'); + ////indent = nexttoken.from; + if (a.lang) { + warning("lang is deprecated.", token); + } + if (option.adsafe && stack.length !== 1) { + warning("ADsafe script placement violation.", token); + } + if (a.src) { + if (option.adsafe && (!adsafe_may || !approved[a.src])) { + warning("ADsafe unapproved script source.", token); + } + if (a.type) { + warning("type is unnecessary.", token); + } + } else { + if (adsafe_went) { + error("ADsafe script violation.", token); + } + use_strict(); + statements('script'); + } + xmode = 'html'; + advance(''); + styles(); + xmode = 'html'; + advance(''; + } + + function html() { + var a, attributes, e, n, q, t, v, w = option.white, wmode; + xmode = 'html'; + xquote = ''; + stack = null; + for (;;) { + switch (nexttoken.value) { + case '<': + xmode = 'html'; + advance('<'); + attributes = {}; + t = nexttoken; + if (!t.identifier) { + warning("Bad identifier {a}.", t, t.value); + } + n = t.value; + if (option.cap) { + n = n.toLowerCase(); + } + t.name = n; + advance(); + if (!stack) { + stack = []; + doBegin(n); + } + v = htmltag[n]; + if (typeof v !== 'object') { + error("Unrecognized tag '<{a}>'.", t, n); + } + e = v.empty; + t.type = n; + for (;;) { + if (nexttoken.id === '/') { + advance('/'); + if (nexttoken.id !== '>') { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, '>', nexttoken.value); + } + break; + } + if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') { + break; + } + if (!nexttoken.identifier) { + if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { + error("Missing '>'.", nexttoken); + } + warning("Bad identifier."); + } + option.white = true; + spaces(); + a = nexttoken.value; + option.white = w; + advance(); + if (!option.cap && a !== a.toLowerCase()) { + warning("Attribute '{a}' not all lower case.", nexttoken, a); + } + a = a.toLowerCase(); + xquote = ''; + if (is_own(attributes, a)) { + warning("Attribute '{a}' repeated.", nexttoken, a); + } + if (a.slice(0, 2) === 'on') { + if (!option.on) { + warning("Avoid HTML event handlers."); + } + xmode = 'scriptstring'; + advance('='); + q = nexttoken.id; + if (q !== '"' && q !== "'") { + error("Missing quote."); + } + xquote = q; + wmode = option.white; + option.white = false; + advance(q); + use_strict(); + statements('on'); + option.white = wmode; + if (nexttoken.id !== q) { + error("Missing close quote on script attribute."); + } + xmode = 'html'; + xquote = ''; + advance(q); + v = false; + } else if (a === 'style') { + xmode = 'scriptstring'; + advance('='); + q = nexttoken.id; + if (q !== '"' && q !== "'") { + error("Missing quote."); + } + xmode = 'styleproperty'; + xquote = q; + advance(q); + substyle(); + xmode = 'html'; + xquote = ''; + advance(q); + v = false; + } else { + if (nexttoken.id === '=') { + advance('='); + v = nexttoken.value; + if (!nexttoken.identifier && + nexttoken.id !== '"' && + nexttoken.id !== '\'' && + nexttoken.type !== '(string)' && + nexttoken.type !== '(number)' && + nexttoken.type !== '(color)') { + warning("Expected an attribute value and instead saw '{a}'.", token, a); + } + advance(); + } else { + v = true; + } + } + attributes[a] = v; + doAttribute(n, a, v); + } + doTag(n, attributes); + if (!e) { + stack.push(t); + } + xmode = 'outer'; + advance('>'); + break; + case '') { + error("Missing '{a}'.", nexttoken, '>'); + } + xmode = 'outer'; + advance('>'); + break; + case '' || nexttoken.id === '(end)') { + break; + } + if (nexttoken.value.indexOf('--') >= 0) { + error("Unexpected --."); + } + if (nexttoken.value.indexOf('<') >= 0) { + error("Unexpected <."); + } + if (nexttoken.value.indexOf('>') >= 0) { + error("Unexpected >."); + } + } + xmode = 'outer'; + advance('>'); + break; + case '(end)': + return; + default: + if (nexttoken.id === '(end)') { + error("Missing '{a}'.", nexttoken, + ''); + } else { + advance(); + } + } + if (stack && stack.length === 0 && (option.adsafe || + !option.fragment || nexttoken.id === '(end)')) { + break; + } + } + if (nexttoken.id !== '(end)') { + error("Unexpected material after the end."); + } + } + + +// The actual JSLINT function itself. + + var itself = function (s, o) { + var a, i, k; + JSLINT.errors = []; + JSLINT.tree = ''; + predefined = Object.create(standard); + if (o) { + a = o.predef; + if (a) { + if (Array.isArray(a)) { + for (i = 0; i < a.length; i += 1) { + predefined[a[i]] = true; + } + } else if (typeof a === 'object') { + k = Object.keys(a); + for (i = 0; i < k.length; i += 1) { + predefined[k[i]] = !!a[k]; + } + } + } + if (o.adsafe) { + o.safe = true; + } + if (o.safe) { + o.browser = + o.css = + o.debug = + o.devel = + o.evil = + o.forin = + o.on = + o.rhino = + o.windows = + o.sub = + o.widget = false; + + o.nomen = + o.safe = + o.undef = true; + + predefined.Date = + predefined['eval'] = + predefined.Function = + predefined.Object = null; + + predefined.ADSAFE = + predefined.lib = false; + } + option = o; + } else { + option = {}; + } + option.indent = option.indent || 4; + option.maxerr = option.maxerr || 50; + adsafe_id = ''; + adsafe_may = false; + adsafe_went = false; + approved = {}; + if (option.approved) { + for (i = 0; i < option.approved.length; i += 1) { + approved[option.approved[i]] = option.approved[i]; + } + } else { + approved.test = 'test'; + } + tab = ''; + for (i = 0; i < option.indent; i += 1) { + tab += ' '; + } + global = Object.create(predefined); + scope = global; + funct = { + '(global)': true, + '(name)': '(global)', + '(scope)': scope, + '(breakage)': 0, + '(loopage)': 0 + }; + functions = [funct]; + ids = {}; + urls = []; + src = false; + xmode = false; + stack = null; + member = {}; + membersOnly = null; + implied = {}; + inblock = false; + lookahead = []; + jsonmode = false; + warnings = 0; + lex.init(s); + prereg = true; + strict_mode = false; + + prevtoken = token = nexttoken = syntax['(begin)']; + assume(); + + try { + advance(); + if (nexttoken.value.charAt(0) === '<') { + html(); + if (option.adsafe && !adsafe_went) { + warning("ADsafe violation: Missing ADSAFE.go.", this); + } + } else { + switch (nexttoken.id) { + case '{': + case '[': + jsonmode = true; + jsonValue(); + break; + case '@': + case '*': + case '#': + case '.': + case ':': + xmode = 'style'; + advance(); + if (token.id !== '@' || !nexttoken.identifier || + nexttoken.value !== 'charset' || token.line !== 1 || + token.from !== 1) { + error("A css file should begin with @charset 'UTF-8';"); + } + advance(); + if (nexttoken.type !== '(string)' && + nexttoken.value !== 'UTF-8') { + error("A css file should begin with @charset 'UTF-8';"); + } + advance(); + semicolon(); + styles(); + break; + + default: + if (option.adsafe && option.fragment) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, '
', nexttoken.value); + } + if (nexttoken.value === 'use strict') { + warning("Use the function form of \"use strict\"."); + use_strict(); + } + JSLINT.tree = statements('lib'); + if (JSLINT.tree.disrupt) { + warning("Weird program.", prevtoken); + } + } + } + advance('(end)'); + } catch (e) { + if (e) { + JSLINT.errors.push({ + reason : e.message, + line : e.line || nexttoken.line, + character : e.character || nexttoken.from + }, null); + } + } + return JSLINT.errors.length === 0; + }; + + +// Data summary. + + itself.data = function () { + + var data = {functions: []}, fu, globals, implieds = [], f, i, j, + members = [], n, unused = [], v; + if (itself.errors.length) { + data.errors = itself.errors; + } + + if (jsonmode) { + data.json = true; + } + + for (n in implied) { + if (is_own(implied, n)) { + implieds.push({ + name: n, + line: implied[n] + }); + } + } + if (implieds.length > 0) { + data.implieds = implieds; + } + + if (urls.length > 0) { + data.urls = urls; + } + + globals = Object.keys(scope); + if (globals.length > 0) { + data.globals = globals; + } + + for (i = 1; i < functions.length; i += 1) { + f = functions[i]; + fu = {}; + for (j = 0; j < functionicity.length; j += 1) { + fu[functionicity[j]] = []; + } + for (n in f) { + if (is_own(f, n) && n.charAt(0) !== '(') { + v = f[n]; + if (v === 'unction') { + v = 'unused'; + } + if (Array.isArray(fu[v])) { + fu[v].push(n); + if (v === 'unused') { + unused.push({ + name: n, + line: f['(line)'], + 'function': f['(name)'] + }); + } + } + } + } + for (j = 0; j < functionicity.length; j += 1) { + if (fu[functionicity[j]].length === 0) { + delete fu[functionicity[j]]; + } + } + fu.name = f['(name)']; + fu.param = f['(params)']; + fu.line = f['(line)']; + fu.last = f['(last)']; + data.functions.push(fu); + } + + if (unused.length > 0) { + data.unused = unused; + } + + members = []; + for (n in member) { + if (typeof member[n] === 'number') { + data.member = member; + break; + } + } + + return data; + }; + + itself.report = function (option) { + var data = itself.data(); + + var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s; + + function detail(h, array) { + var b, i, singularity; + if (array) { + o.push('
' + h + ' '); + array = array.sort(); + for (i = 0; i < array.length; i += 1) { + if (array[i] !== singularity) { + singularity = array[i]; + o.push((b ? ', ' : '') + singularity); + b = true; + } + } + o.push('
'); + } + } + + if (data.errors || data.implieds || data.unused) { + err = true; + o.push('
Error:'); + if (data.errors) { + for (i = 0; i < data.errors.length; i += 1) { + c = data.errors[i]; + if (c) { + e = c.evidence || ''; + o.push('

Problem' + (isFinite(c.line) ? ' at line ' + + c.line + ' character ' + c.character : '') + + ': ' + c.reason.entityify() + + '

' + + (e && (e.length > 80 ? e.slice(0, 77) + '...' : + e).entityify()) + '

'); + } + } + } + + if (data.implieds) { + s = []; + for (i = 0; i < data.implieds.length; i += 1) { + s[i] = '' + data.implieds[i].name + ' ' + + data.implieds[i].line + ''; + } + o.push('

Implied global: ' + s.join(', ') + '

'); + } + + if (data.unused) { + s = []; + for (i = 0; i < data.unused.length; i += 1) { + s[i] = '' + data.unused[i].name + ' ' + + data.unused[i].line + ' ' + + data.unused[i]['function'] + ''; + } + o.push('

Unused variable: ' + s.join(', ') + '

'); + } + if (data.json) { + o.push('

JSON: bad.

'); + } + o.push('
'); + } + + if (!option) { + + o.push('
'); + + if (data.urls) { + detail("URLs
", data.urls, '
'); + } + + if (xmode === 'style') { + o.push('

CSS.

'); + } else if (data.json && !err) { + o.push('

JSON: good.

'); + } else if (data.globals) { + o.push('
Global ' + + data.globals.sort().join(', ') + '
'); + } else { + o.push('
No new global variables introduced.
'); + } + + for (i = 0; i < data.functions.length; i += 1) { + f = data.functions[i]; + + o.push('
' + f.line + '-' + + f.last + ' ' + (f.name || '') + '(' + + (f.param ? f.param.join(', ') : '') + ')
'); + detail('Unused', f.unused); + detail('Closure', f.closure); + detail('Variable', f['var']); + detail('Exception', f.exception); + detail('Outer', f.outer); + detail('Global', f.global); + detail('Label', f.label); + } + + if (data.member) { + a = Object.keys(data.member); + if (a.length) { + a = a.sort(); + m = '
/*members ';
+                    l = 10;
+                    for (i = 0; i < a.length; i += 1) {
+                        k = a[i];
+                        n = k.name();
+                        if (l + n.length > 72) {
+                            o.push(m + '
'); + m = ' '; + l = 1; + } + l += n.length + 2; + if (data.member[k] === 1) { + n = '' + n + ''; + } + if (i < a.length - 1) { + n += ', '; + } + m += n; + } + o.push(m + '
*/
'); + } + o.push('
'); + } + } + return o.join(''); + }; + itself.jslint = itself; + + itself.edition = '2011-01-09'; + + return itself; + +}()); + +}); \ No newline at end of file From f804a62f1dae149f093a239bb2e84af165302b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Kleiman?= Date: Fri, 21 Jan 2011 16:18:25 +0800 Subject: [PATCH 06/60] Fix function documentation. --- lib/ace/document.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 20250ba7..6a6d2b98 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -300,7 +300,7 @@ var Document = function(text) { * Removes a range of full lines * * @param firstRow {Integer} The first row to be removed - * @param firstRow {Integer} The first row to be removed + * @param lastRow {Integer} The last row to be removed * @return {String[]} The removed lines */ this.removeLines = function(firstRow, lastRow) { From f6f97918f5207406eac23b270df4a1a2aa661ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Kleiman?= Date: Fri, 21 Jan 2011 16:26:11 +0800 Subject: [PATCH 07/60] Remove suspicious code that lacks side-effects. --- lib/ace/mode/xml_highlight_rules.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ace/mode/xml_highlight_rules.js b/lib/ace/mode/xml_highlight_rules.js index 807297c2..f8f6fe6c 100644 --- a/lib/ace/mode/xml_highlight_rules.js +++ b/lib/ace/mode/xml_highlight_rules.js @@ -110,8 +110,6 @@ var XmlHighlightRules = function() { }; }; -/fd/g - oop.inherits(XmlHighlightRules, TextHighlightRules); exports.XmlHighlightRules = XmlHighlightRules; From 6e524d5b8d0c1ffb7f723fac64dcbe10efa3b6f0 Mon Sep 17 00:00:00 2001 From: Tom Andrews Date: Fri, 21 Jan 2011 03:37:48 +0800 Subject: [PATCH 08/60] Removed reference to build folder, as that doesn't exist --- editor-build.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/editor-build.html b/editor-build.html index e6008471..da01e173 100644 --- a/editor-build.html +++ b/editor-build.html @@ -197,11 +197,11 @@ echo $output; - - + + - \ No newline at end of file + From dc42d5c49b155f51344b2b06ab711b3c568beea5 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 15:01:03 +0100 Subject: [PATCH 09/60] update pilot --- support/pilot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/pilot b/support/pilot index f0ba9df0..9ef10946 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit f0ba9df06853f733f5a0cd4d774facd1fc067c92 +Subproject commit 9ef10946d31d1b0e2bd63466fa5aba9c5269e5fd From 5f909a4f94d8a8fc97cbe79862558b230037afe7 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 15:22:52 +0100 Subject: [PATCH 10/60] uglify filter is broken --- Makefile.dryice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index d92c938e..d1900a5e 100644 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -137,7 +137,7 @@ copy({ // Create the compressed and uncompressed output files copy({ source: data, - filter: copy.filter.uglifyjs, + //filter: copy.filter.uglifyjs, dest: 'build/ace.js' }); copy({ From 730da2ee1ebe0b600c36de19d15f7649aa7256ba Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 15:36:14 +0100 Subject: [PATCH 11/60] define nodejs dependencies --- package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 01adf13f..386fb113 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ace", "description": "Ajax.org Code Editor is a full featured source code highlighting editor that powers the Cloud9 IDE", - "version": "0.1", + "version": "0.1.0", "homepage" : "http://github.com/ajaxorg/ace", "engines": {"node": ">= 0.2.0"}, "author": "Fabian Jakobs ", @@ -10,6 +10,10 @@ "type" : "git", "url" : "http://github.com/ajaxorg/ace.git" }, + "dependencies": { + "uglify-js": ">=0.0.2", + "step": ">=0.0.3" + }, "licenses": [{ "type": "MPL", "url": "http://www.mozilla.org/MPL/" From 9373f50ddb7e145e03f57d72754d75b6e224ace2 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 15:59:09 +0100 Subject: [PATCH 12/60] make Makefile executable --- Makefile.dryice.js | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 Makefile.dryice.js diff --git a/Makefile.dryice.js b/Makefile.dryice.js old mode 100644 new mode 100755 index d1900a5e..c60702a3 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * From 08c5c0bec7d47d9bdb6db64f5e36ad56a23ebf17 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 15:59:20 +0100 Subject: [PATCH 13/60] add more npm deps --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 386fb113..e1891036 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,10 @@ }, "dependencies": { "uglify-js": ">=0.0.2", - "step": ">=0.0.3" + "step": ">=0.0.3", + "asyncjs": ">=0.0.2", + "jsdom": ">=0.1.23", + "htmlparser": ">=1.7.2" }, "licenses": [{ "type": "MPL", From c99d9dd262753aee5475c1533331d781ba80945e Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 17:02:11 +0100 Subject: [PATCH 14/60] replace git submodules by npm dependencies --- .gitmodules | 23 +---------------------- package.json | 5 ++--- support/async | 1 - support/dryice | 1 - support/jsdom | 1 - support/node-htmlparser | 1 - support/requirejs | 1 - 7 files changed, 3 insertions(+), 30 deletions(-) delete mode 160000 support/async delete mode 160000 support/dryice delete mode 160000 support/jsdom delete mode 160000 support/node-htmlparser delete mode 160000 support/requirejs diff --git a/.gitmodules b/.gitmodules index b46e518b..aee8d549 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,30 +1,9 @@ -[submodule "tool/support/node-o3-xml"] - path = tool/support/node-o3-xml - url = git://github.com/ajaxorg/node-o3-xml.git -[submodule "tool/support/async"] - path = tool/support/async - url = git://github.com/fjakobs/async.js.git -[submodule "support/requirejs"] - path = support/requirejs - url = git://github.com/jrburke/requirejs.git [submodule "support/node-o3-xml"] path = support/node-o3-xml url = git://github.com/ajaxorg/node-o3-xml.git -[submodule "support/async"] - path = support/async - url = git://github.com/fjakobs/async.js.git -[submodule "support/jsdom"] - path = support/jsdom - url = git://github.com/tmpvar/jsdom.git -[submodule "support/node-htmlparser"] - path = support/node-htmlparser - url = git://github.com/tautologistics/node-htmlparser.git [submodule "support/cockpit"] path = support/cockpit url = git://github.com/ajaxorg/cockpit.git [submodule "support/pilot"] path = support/pilot - url = git://github.com/ajaxorg/pilot.git -[submodule "support/dryice"] - path = support/dryice - url = git://github.com/mozilla/dryice.git + url = git://github.com/ajaxorg/pilot.git \ No newline at end of file diff --git a/package.json b/package.json index e1891036..17b333bb 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,10 @@ "url" : "http://github.com/ajaxorg/ace.git" }, "dependencies": { - "uglify-js": ">=0.0.2", - "step": ">=0.0.3", "asyncjs": ">=0.0.2", "jsdom": ">=0.1.23", - "htmlparser": ">=1.7.2" + "htmlparser": ">=1.7.2", + "dryice": ">=0.1.0" }, "licenses": [{ "type": "MPL", diff --git a/support/async b/support/async deleted file mode 160000 index 6da80763..00000000 --- a/support/async +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6da8076355fc9f06191d39b8f5989159dc8a162c diff --git a/support/dryice b/support/dryice deleted file mode 160000 index 89f99bb6..00000000 --- a/support/dryice +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 89f99bb65c66d0c25d461ab4af5743ef1676a04d diff --git a/support/jsdom b/support/jsdom deleted file mode 160000 index 48675596..00000000 --- a/support/jsdom +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 486755962dc0b69c60bfd04869ff1e0093406bae diff --git a/support/node-htmlparser b/support/node-htmlparser deleted file mode 160000 index 60d64db4..00000000 --- a/support/node-htmlparser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 60d64db4a9a8b1a26bd099bc34f657bf096c4f39 diff --git a/support/requirejs b/support/requirejs deleted file mode 160000 index 819c4e7b..00000000 --- a/support/requirejs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 819c4e7b9b1e6e5f99696b5c9f2805891a9e7cfd From ad9056cce20ad509839792e82173a2648c3a2e5e Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 17:02:34 +0100 Subject: [PATCH 15/60] user async.js from npm --- lib/ace/test/all.js | 2 +- lib/ace/test/change_document_test.js | 2 +- lib/ace/test/document_test.js | 4 ++-- lib/ace/test/edit_session_test.js | 4 ++-- lib/ace/test/event_emitter_test.js | 2 +- lib/ace/test/mode/css_test.js | 2 +- lib/ace/test/mode/css_tokenizer_test.js | 2 +- lib/ace/test/mode/html_test.js | 2 +- lib/ace/test/mode/html_tokenizer_test.js | 2 +- lib/ace/test/mode/javascript_test.js | 2 +- lib/ace/test/mode/javascript_tokenizer_test.js | 2 +- lib/ace/test/mode/text_test.js | 2 +- lib/ace/test/mode/xml_test.js | 2 +- lib/ace/test/mode/xml_tokenizer_test.js | 2 +- lib/ace/test/navigation_test.js | 2 +- lib/ace/test/range_test.js | 2 +- lib/ace/test/search_test.js | 2 +- lib/ace/test/selection_test.js | 2 +- lib/ace/test/text_edit_test.js | 2 +- lib/ace/test/virtual_renderer_test.js | 2 +- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/ace/test/all.js b/lib/ace/test/all.js index 5e4dc4e0..00df7b59 100644 --- a/lib/ace/test/all.js +++ b/lib/ace/test/all.js @@ -37,7 +37,7 @@ require("../../../support/paths"); -var async = require("async"); +var async = require("asyncjs"); async.concat( require("./change_document_test"), diff --git a/lib/ace/test/change_document_test.js b/lib/ace/test/change_document_test.js index 4a91eeb1..d00c2af9 100644 --- a/lib/ace/test/change_document_test.js +++ b/lib/ace/test/change_document_test.js @@ -155,7 +155,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/document_test.js b/lib/ace/test/document_test.js index 2d8f9b33..c46602cd 100644 --- a/lib/ace/test/document_test.js +++ b/lib/ace/test/document_test.js @@ -38,7 +38,7 @@ var Document = require("../document").Document, Range = require("../range").Range, assert = require("./assertions"), - async = require("async"); + async = require("asyncjs"); var Test = { @@ -280,7 +280,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); if (module === require.main) { require("../../../support/paths"); diff --git a/lib/ace/test/edit_session_test.js b/lib/ace/test/edit_session_test.js index c5f76b9b..1cf721fa 100644 --- a/lib/ace/test/edit_session_test.js +++ b/lib/ace/test/edit_session_test.js @@ -42,7 +42,7 @@ var EditSession = require("ace/edit_session").EditSession, MockRenderer = require("./mockrenderer"), Range = require("ace/range").Range, assert = require("./assertions"), - async = require("async"); + async = require("asyncjs"); var Test = { @@ -266,7 +266,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/event_emitter_test.js b/lib/ace/test/event_emitter_test.js index 7a98cd9b..6ebe5638 100644 --- a/lib/ace/test/event_emitter_test.js +++ b/lib/ace/test/event_emitter_test.js @@ -60,7 +60,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test) +module.exports = require("asyncjs/test").testcase(Test) if (module === require.main) module.exports.exec() diff --git a/lib/ace/test/mode/css_test.js b/lib/ace/test/mode/css_test.js index 3b2932c0..654d5c83 100644 --- a/lib/ace/test/mode/css_test.js +++ b/lib/ace/test/mode/css_test.js @@ -70,7 +70,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/css_tokenizer_test.js b/lib/ace/test/mode/css_tokenizer_test.js index f26e82d2..0618edac 100644 --- a/lib/ace/test/mode/css_tokenizer_test.js +++ b/lib/ace/test/mode/css_tokenizer_test.js @@ -84,7 +84,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test, "css tokenizer"); +module.exports = require("asyncjs/test").testcase(Test, "css tokenizer"); }); diff --git a/lib/ace/test/mode/html_test.js b/lib/ace/test/mode/html_test.js index d9cf64e4..7184ffc2 100644 --- a/lib/ace/test/mode/html_test.js +++ b/lib/ace/test/mode/html_test.js @@ -62,7 +62,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/html_tokenizer_test.js b/lib/ace/test/mode/html_tokenizer_test.js index a14ece42..6ba8fdea 100644 --- a/lib/ace/test/mode/html_tokenizer_test.js +++ b/lib/ace/test/mode/html_tokenizer_test.js @@ -65,7 +65,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/javascript_test.js b/lib/ace/test/mode/javascript_test.js index 9fcb9c09..a12c4488 100644 --- a/lib/ace/test/mode/javascript_test.js +++ b/lib/ace/test/mode/javascript_test.js @@ -144,7 +144,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/javascript_tokenizer_test.js b/lib/ace/test/mode/javascript_tokenizer_test.js index ebe44c01..73e13f1e 100644 --- a/lib/ace/test/mode/javascript_tokenizer_test.js +++ b/lib/ace/test/mode/javascript_tokenizer_test.js @@ -101,7 +101,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/text_test.js b/lib/ace/test/mode/text_test.js index a39f7d5b..10ca9d13 100644 --- a/lib/ace/test/mode/text_test.js +++ b/lib/ace/test/mode/text_test.js @@ -59,7 +59,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/xml_test.js b/lib/ace/test/mode/xml_test.js index e2375afb..72bd56e1 100644 --- a/lib/ace/test/mode/xml_test.js +++ b/lib/ace/test/mode/xml_test.js @@ -70,7 +70,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/mode/xml_tokenizer_test.js b/lib/ace/test/mode/xml_tokenizer_test.js index 5905bc64..e63980ed 100644 --- a/lib/ace/test/mode/xml_tokenizer_test.js +++ b/lib/ace/test/mode/xml_tokenizer_test.js @@ -60,7 +60,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/navigation_test.js b/lib/ace/test/navigation_test.js index 44938b6f..c9bbdaf6 100644 --- a/lib/ace/test/navigation_test.js +++ b/lib/ace/test/navigation_test.js @@ -150,7 +150,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test) +module.exports = require("asyncjs/test").testcase(Test) }); if (module === require.main) { diff --git a/lib/ace/test/range_test.js b/lib/ace/test/range_test.js index bc5582ad..8415f135 100644 --- a/lib/ace/test/range_test.js +++ b/lib/ace/test/range_test.js @@ -161,7 +161,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/search_test.js b/lib/ace/test/search_test.js index b33a0d0f..1f42571c 100644 --- a/lib/ace/test/search_test.js +++ b/lib/ace/test/search_test.js @@ -344,7 +344,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test) +module.exports = require("asyncjs/test").testcase(Test) }); if (module === require.main) { diff --git a/lib/ace/test/selection_test.js b/lib/ace/test/selection_test.js index bccf0395..06858ec5 100644 --- a/lib/ace/test/selection_test.js +++ b/lib/ace/test/selection_test.js @@ -290,7 +290,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/text_edit_test.js b/lib/ace/test/text_edit_test.js index 3dbcc3b6..0ae2615e 100644 --- a/lib/ace/test/text_edit_test.js +++ b/lib/ace/test/text_edit_test.js @@ -438,7 +438,7 @@ var Test = { } }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { diff --git a/lib/ace/test/virtual_renderer_test.js b/lib/ace/test/virtual_renderer_test.js index 83e0351c..0d4fe8ed 100644 --- a/lib/ace/test/virtual_renderer_test.js +++ b/lib/ace/test/virtual_renderer_test.js @@ -72,7 +72,7 @@ var Test = { // change tab size after setDocument (for text layer) }; -module.exports = require("async/test").testcase(Test); +module.exports = require("asyncjs/test").testcase(Test); }); if (module === require.main) { From fa686c0c2429a78774f9f74130977f4dbf877d88 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 17:15:59 +0100 Subject: [PATCH 16/60] update readme --- Readme.md | 37 ++++++++++++++++++++++++++++++++++++- static.py | 2 +- 2 files changed, 37 insertions(+), 2 deletions(-) mode change 100644 => 100755 static.py diff --git a/Readme.md b/Readme.md index b4f633a5..8897212b 100644 --- a/Readme.md +++ b/Readme.md @@ -32,4 +32,39 @@ Getting the code Ace is a community project. We actively encourage and support contributions. The Ace source code is hosted on GitHub. It is released under the Mozilla tri-license (MPL/GPL/LGPL). This is the same license used by Firefox. This license is friendly to all kinds of projects, whether open source or not. Take charge of your editor and add your favorite language highlighting and keybindings! - git clone git://github.com/ajaxorg/ace.git \ No newline at end of file + git clone git://github.com/ajaxorg/ace.git + git submodule update --init --recursive + +Running Ace +----------- + +After the checkout Ace works out of the box. No build step is required. Simply open 'editor.html' in any browser except Google Chrome. Google Chrome doesn't allow XMLHTTPRequests from files loaded from disc (i.e. with a file:/// URL). To open the Ace in Chrome simply start the bundled mini HTTP server: + + ./static.py + +The editor can then be opened at http://localhost:9999/editor.html. + +Package Ace +----------- + +To package Ace we use the dryice build tool developed by the Mozilla Skywriter team. To install dryice and all its dependencies simply call: + + npm link . + +Afterwards Ace can by build by calling + + ./Makefile.dryice.js + +The packaged Ace will be put in the 'build' folder. + +Running the Unit Tests +---------------------- + +The Ace unit tests run on node.js. Before the first run a couple of node mudules have to be installed. The easiest way to do this is by using the node package manager (npm). In the Ace base directory simply call + + npm link . + +To run the tests call: + + node lib/ace/test/all.js + diff --git a/static.py b/static.py old mode 100644 new mode 100755 index 774cbb9f..8cac3b7a --- a/static.py +++ b/static.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.4 +#!/usr/bin/env python """static - A stupidly simple WSGI way to serve static (or mixed) content. (See the docstrings of the various functions and classes.) From 6abd5dae8e4e971bf3eb966f201e4589c98f72c6 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 17:17:40 +0100 Subject: [PATCH 17/60] cleanup paths.js --- support/paths.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/support/paths.js b/support/paths.js index 09568933..b081ec7a 100644 --- a/support/paths.js +++ b/support/paths.js @@ -1,8 +1,4 @@ require("./requireJS-node"); require.paths.unshift(__dirname + "/../lib"); -require.paths.unshift(__dirname + "/cockpit/lib"); require.paths.unshift(__dirname + "/pilot/lib"); -require.paths.unshift(__dirname + "/async/lib"); -require.paths.unshift(__dirname + "/node-htmlparser/lib"); -require.paths.unshift(__dirname + "/jsdom/lib"); require.paths.unshift(__dirname); \ No newline at end of file From 1e4ee57d6306f3841cd0936e754dbfb004450193 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 21:24:05 +0100 Subject: [PATCH 18/60] add jslint example --- demo/startup.js | 36 ++++++++++++++++-- lib/ace/css/editor.css | 4 ++ lib/ace/worker/host.js | 22 +++++------ lib/ace/worker/jslint.js | 10 ++--- lib/ace/worker/mirror.js | 38 +++++++++++++++++++ .../{WorkerClient.js => worker_client.js} | 16 +++++--- 6 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 lib/ace/worker/mirror.js rename lib/ace/worker/{WorkerClient.js => worker_client.js} (84%) diff --git a/demo/startup.js b/demo/startup.js index 419e2667..49581563 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -59,7 +59,7 @@ exports.launch = function(env) { var emacs = require("ace/keyboard/keybinding/emacs").Emacs; var HashHandler = require("ace/keyboard/hash_handler").HashHandler; - var WorkerClient = require("ace/worker/WorkerClient").WorkerClient; + var WorkerClient = require("ace/worker/worker_client").WorkerClient; var docs = {}; @@ -67,9 +67,37 @@ exports.launch = function(env) { docs.js.setMode(new JavaScriptMode()); docs.js.setUndoManager(new UndoManager()); - var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/demo", "Demo"); - worker.send("juhu"); - + var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/mirror", "Mirror"); + worker.call("setValue", [docs.js.getValue()]); + + docs.js.getDocument().on("change", function(e) { + e.range = { + start: e.data.range.start, + end: e.data.range.end + }; + worker.emit("change", e); + }); + + worker.on("jslint", function(results) { + console.log("jslint", results); + + var rows = []; + for (var i=0; i Date: Sat, 22 Jan 2011 21:24:52 +0100 Subject: [PATCH 19/60] make sure range is an instance of Range --- lib/ace/document.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 6a6d2b98..7de5cd9b 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -357,7 +357,7 @@ var Document = function(text) { this.applyDeltas = function(deltas) { for (var i=0; i=0; i--) { var delta = deltas[i]; - var range = delta.range; + var range = Range.fromPoints(delta.range.start, delta.range.end); if (delta.action == "insertLines") this.removeLines(range.start.row, range.end.row) From 2a65f1266f8625fcfe4d0e81fd80fa26c11a92cb Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sat, 22 Jan 2011 23:58:03 +0100 Subject: [PATCH 20/60] update jslint demo --- demo/startup.js | 17 ++++++++++------- lib/ace/css/editor.css | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/demo/startup.js b/demo/startup.js index 49581563..5778e6a0 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -79,17 +79,20 @@ exports.launch = function(env) { }); worker.on("jslint", function(results) { - console.log("jslint", results); - - var rows = []; + var errors = []; for (var i=0; i Date: Sun, 23 Jan 2011 00:01:29 +0100 Subject: [PATCH 21/60] add support for line annotations --- lib/ace/edit_session.js | 35 +++++++++++++++++++++++++++++++++++ lib/ace/editor.js | 9 +++++++++ lib/ace/layer/gutter.js | 36 ++++++++++++++++++++++++++++++++---- lib/ace/virtual_renderer.js | 5 +++++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index e9e2a66a..c0c3c6ad 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -198,6 +198,41 @@ var EditSession = function(text, mode) { this._dispatchEvent("changeBreakpoint", {}); }; + this.getBreakpoints = function() { + return this.$breakpoints; + }; + + /** + * Error: + * { + * row: 12, + * column: 2, //can be undefined + * text: "Missing argument", + * type: "error" // or "warning" or "info" + * } + */ + this.setAnnotations = function(annotations) { + this.$annotations = []; + for (var i=0; i", (i+1), "
"); html.push(""); } diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 687085fa..affce7fb 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -474,6 +474,11 @@ var VirtualRenderer = function(container, theme) { this.$loop.schedule(this.CHANGE_GUTTER); }; + this.setAnnotations = function(annotations) { + this.$gutterLayer.setAnnotations(annotations); + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.updateCursor = function(position, overwrite) { this.$cursorLayer.setCursor(position, overwrite); this.$loop.schedule(this.CHANGE_CURSOR); From b9e26f2b8dbcb6e308fe7bd88a3461cf0a7462f7 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sun, 23 Jan 2011 14:26:06 +0100 Subject: [PATCH 22/60] move css to an external file --- editor.html | 61 +---------------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/editor.html b/editor.html index f1b20bf5..30e67abb 100644 --- a/editor.html +++ b/editor.html @@ -6,66 +6,7 @@ Editor - - +
From 30975d5b0d567dcc03c51cea8bff06edc87b72b2 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sun, 23 Jan 2011 14:26:29 +0100 Subject: [PATCH 23/60] remove error decoration --- lib/ace/css/editor.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 2d79f9db..cd537ddd 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -19,10 +19,6 @@ height: 100%; } -.ace_gutter-cell.ace_error { - color: red; -} - .ace_editor .ace_sb { position: absolute; overflow-x: hidden; From 8238e36ca1a62278ed6b660ae19e53d56b4c3bc9 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sun, 23 Jan 2011 14:59:53 +0100 Subject: [PATCH 24/60] nicely integrate the worker code --- demo/startup.js | 62 ++++++++++++++----------------- lib/ace/edit_session.js | 14 ++++--- lib/ace/mode/javascript.js | 34 +++++++++++++++++ lib/ace/mode/javascript_worker.js | 24 ++++++++++++ lib/ace/mode/text.js | 4 ++ lib/ace/worker/Demo.js | 16 -------- lib/ace/worker/mirror.js | 17 ++++++--- lib/ace/worker/worker_client.js | 4 ++ 8 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 lib/ace/mode/javascript_worker.js delete mode 100644 lib/ace/worker/Demo.js diff --git a/demo/startup.js b/demo/startup.js index 5778e6a0..1403fd1b 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -59,48 +59,42 @@ exports.launch = function(env) { var emacs = require("ace/keyboard/keybinding/emacs").Emacs; var HashHandler = require("ace/keyboard/hash_handler").HashHandler; - var WorkerClient = require("ace/worker/worker_client").WorkerClient; - var docs = {}; docs.js = new EditSession(document.getElementById("jstext").innerHTML); docs.js.setMode(new JavaScriptMode()); docs.js.setUndoManager(new UndoManager()); - var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/mirror", "Mirror"); - worker.call("setValue", [docs.js.getValue()]); - - docs.js.getDocument().on("change", function(e) { - e.range = { - start: e.data.range.start, - end: e.data.range.end - }; - worker.emit("change", e); - }); + if (false && window.Worker) { + var worker = new WorkerClient("../..", ["ace", "pilot"], "ace/worker/mirror", "Mirror"); + worker.call("setValue", [docs.js.getValue()]); - worker.on("jslint", function(results) { - var errors = []; - for (var i=0; i Date: Sun, 23 Jan 2011 15:02:38 +0100 Subject: [PATCH 25/60] worker cleanup --- lib/ace/mode/javascript.js | 4 ++++ lib/ace/worker/worker_client.js | 1 + 2 files changed, 5 insertions(+) diff --git a/lib/ace/mode/javascript.js b/lib/ace/mode/javascript.js index 3d4d62d2..f18ec329 100644 --- a/lib/ace/mode/javascript.js +++ b/lib/ace/mode/javascript.js @@ -152,6 +152,10 @@ oop.inherits(Mode, TextMode); session.setAnnotations(errors) }); + worker.on("terminate", function() { + session.clearAnnotations(); + }); + return worker; }; diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 81a516a7..71d6505e 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -65,6 +65,7 @@ var WorkerClient = function(baseUrl, topLevelNamespaces, module, classname) { oop.implement(this, EventEmitter); this.terminate = function() { + this._dispatchEvent("terminate", {}); this.$worker.terminate(); }; From 6097f1cdfc73f2b8f19c5d2f72b70c0748e0104b Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sun, 23 Jan 2011 17:47:31 +0100 Subject: [PATCH 26/60] add some eclipse icons --- demo/icons/Readme.txt | 1 + demo/icons/epl.html | 260 +++++++++++++++++++++++++++++++++++++ demo/icons/error_obj.gif | Bin 0 -> 339 bytes demo/icons/warning_obj.gif | Bin 0 -> 324 bytes demo/styles.css | 62 +++++++++ 5 files changed, 323 insertions(+) create mode 100644 demo/icons/Readme.txt create mode 100644 demo/icons/epl.html create mode 100644 demo/icons/error_obj.gif create mode 100644 demo/icons/warning_obj.gif create mode 100644 demo/styles.css diff --git a/demo/icons/Readme.txt b/demo/icons/Readme.txt new file mode 100644 index 00000000..87a71c8d --- /dev/null +++ b/demo/icons/Readme.txt @@ -0,0 +1 @@ +The icons in this folder are from the Eclipse project and licensed under the Eclipse public license version 1.0 (EPL). \ No newline at end of file diff --git a/demo/icons/epl.html b/demo/icons/epl.html new file mode 100644 index 00000000..f57b834b --- /dev/null +++ b/demo/icons/epl.html @@ -0,0 +1,260 @@ + + + + +Eclipse Public License - Version 1.0 + + + + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;

+ +

iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and

+ +

iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), 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 OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.

+ + + + + \ No newline at end of file diff --git a/demo/icons/error_obj.gif b/demo/icons/error_obj.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bc60689c6dcbfb71770789a3c2e98f51f6eecd7 GIT binary patch literal 339 zcmZ?wbhEHb6krfwxXQrrHNWU4 z&?^&f5)>{J;LlXy?-r(U7vw8pD9sGYH>i^m7 z{}+n?Uupb*wdw!W=Kpu5|Gzi)|MMOH&*W4G3O9s`Hie5dhKhAYOSDHz^u$R`Ns&F; ztb4Lu|5%IOu~xma-9{(d4NrF&pY1liGsFJ=Y^MivoF6U?J>712zT5O-ui5SC4)jB<~Eh3Ck(|MR1tY+CYw%7@BZ0uHG65^MT;^$&kcave^ j77&vV;AWL^lhtKopEXNY&Ru%J!UdsAmM(L0WUvMR>~MP$ literal 0 HcmV?d00001 diff --git a/demo/styles.css b/demo/styles.css new file mode 100644 index 00000000..44ab6810 --- /dev/null +++ b/demo/styles.css @@ -0,0 +1,62 @@ + html { + height: 100%; + overflow: hidden; +} + +body { + overflow: hidden; + margin: 0; + padding: 0; + height: 100%; + width: 100%; + font-family: Arial, Helvetica, sans-serif, Tahoma, Verdana, sans-serif; + font-size: 12px; + background: rgb(14, 98, 165); + color: white; +} + +#editor { + top: 55px; + left: 0px; + background: white; +} + +.ace_gutter-cell.ace_error { + background-image: url("icons/error_obj.gif"); + background-repeat: no-repeat; + background-position: 4px center; +} + +#controls { + width: 100%; + height: 55px; +} + +#jump { + position: absolute; + width: 10px; + height: 10px; + border: 1px solid red; + z-index: 10000; + display: none; +} + +#cockpitInput { + position: absolute; + width: 100%; + bottom: 0; + + border: none; outline: none; + font-family: consolas, courier, monospace; + font-size: 120%; +} + +#cockpitOutput { + padding: 10px; + margin: 0 15px; + border: 1px solid #AAA; + -moz-border-radius-topleft: 10px; + -moz-border-radius-topright: 10px; + border-top-left-radius: 4px; border-top-right-radius: 4px; + background: #DDD; color: #000; +} \ No newline at end of file From 7d919c0516600017d0ff51d2e1c63079c1d2826c Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Sun, 23 Jan 2011 18:29:57 +0100 Subject: [PATCH 27/60] this timeout does more harm than it helps. Prevents iOS keyboard from opening and has problems in FF4 --- lib/ace/editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 6aeeee4b..26fbb1c2 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -57,7 +57,7 @@ var Editor =function(renderer, session) { this.keyBinding = new KeyBinding(this); var self = this; event.addListener(container, "mousedown", function(e) { - setTimeout(function() {self.focus();}); + self.focus(); return event.preventDefault(e); }); event.addListener(container, "selectstart", function(e) { From 22cc0e9a1cf0b3e14f6c6a9d1db1822c42a47aec Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 25 Jan 2011 02:58:15 +0100 Subject: [PATCH 28/60] Implementing CLI settings that are shown in the demo editor. --- demo/boot.js | 6 +- lib/ace/defaults.js | 51 +++++++++++++++ lib/ace/settings/default-settings.js | 97 ++++++++++++++++++++++++++++ support/pilot | 2 +- 4 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 lib/ace/defaults.js create mode 100644 lib/ace/settings/default-settings.js diff --git a/demo/boot.js b/demo/boot.js index e84bfe3f..c5ca3b28 100644 --- a/demo/boot.js +++ b/demo/boot.js @@ -36,7 +36,7 @@ * ***** END LICENSE BLOCK ***** */ var config = { - paths: { + paths: { demo: "../demo", ace: "../lib/ace", cockpit: "../support/cockpit/lib/cockpit", @@ -47,10 +47,12 @@ var config = { var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", "pilot/environment", "demo/startup" ]; +var plugins = [ "pilot/index", "cockpit/index", "ace/defaults" ]; + require(config); require(deps, function() { var catalog = require("pilot/plugin_manager").catalog; - catalog.registerPlugins([ "pilot/index", "cockpit/index" ]).then(function() { + catalog.registerPlugins(plugins).then(function() { var env = require("pilot/environment").create(); catalog.startupPlugins({ env: env }).then(function() { require("demo/startup").launch(env); diff --git a/lib/ace/defaults.js b/lib/ace/defaults.js new file mode 100644 index 00000000..bd217cd1 --- /dev/null +++ b/lib/ace/defaults.js @@ -0,0 +1,51 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (http://jeditoolkit.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var settings = require("ace/settings/default-settings") + +exports.startup = function startup(data, reason) { + settings.startup(data, reason) +} + +exports.shutdown = function shutdown(data, reason) { + settings.shutdown(data, reason) +} + +}) diff --git a/lib/ace/settings/default-settings.js b/lib/ace/settings/default-settings.js new file mode 100644 index 00000000..4e5e0fdf --- /dev/null +++ b/lib/ace/settings/default-settings.js @@ -0,0 +1,97 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (http://jeditoolkit.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var types = require('pilot/types') +var SelectionType = require('pilot/types/basic').SelectionType + +var env + +var settingTypes = { + selectionStyle: new SelectionType({ + data: [ 'line', 'text' ] + }) +} + +var settings = { + printMargin: { + description: 'Position of the print margin column.', + type: 'number', + defaultValue: 80, + onChange: function onChange(event) { + if (env.editor) env.editor.setPrintMarginColumn(event.value) + } + }, + showIvisibles: { + description: 'Whether or not to show invisible characters.', + type: 'bool', + defaultValue: false, + onChange: function onChange(event) { + if (env.editor) env.editor.setShowInvisibles(event.value) + } + }, + highlightActiveLine: { + description: 'Whether or not highlight active line.', + type: 'bool', + defaultValue: true, + onChange: function onChange(event) { + if (env.editor) env.editor.setHighlightActiveLine(event.value) + } + }, + selectionStyle: { + description: 'Type of text selection.', + type: 'selectionStyle', + defaultValue: 'line', + onChange: function onChange(event) { + if (env.editor) env.editor.setSelectionStyle(event.value) + } + } +} + +exports.startup = function startup(data, reason) { + env = data.env + types.registerTypes(settingTypes) + data.env.settings.addSettings(settings) +} + +exports.shutdown = function shutdown(data, reason) { + data.env.settings.removeSettings(settings) +} + +}) diff --git a/support/pilot b/support/pilot index 9ef10946..85ecce3f 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit 9ef10946d31d1b0e2bd63466fa5aba9c5269e5fd +Subproject commit 85ecce3f54711a5198ca7771bb000edbe71a0010 From 762dc52f861457e654b6ba4f4cf91cc80b5ef1be Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 25 Jan 2011 12:39:52 +0100 Subject: [PATCH 29/60] Allowing textmate like highlighting of right margin & indicator. Changed twilight style to demo this. --- lib/ace/css/editor.css | 24 +++++++++++++++++------- lib/ace/theme/twilight.js | 5 +++-- lib/ace/virtual_renderer.js | 11 ++++++++--- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index cd537ddd..adfa1511 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -32,13 +32,23 @@ left: 0px; } +.ace_editor .ace_print_margin_layer { + z-index: 0; + position: absolute; + overflow: hidden; + margin: 0px; + left: 0px; + height: 100%; + width: 100%; +} + .ace_editor .ace_print_margin { position: absolute; height: 100%; } .ace_layer { - z-index: 0; + z-index: 1; position: absolute; overflow: hidden; white-space: nowrap; @@ -56,7 +66,7 @@ } .ace_cursor { - z-index: 3; + z-index: 4; position: absolute; } @@ -69,20 +79,20 @@ .ace_marker-layer .ace_step { position: absolute; - z-index: 2; + z-index: 3; } .ace_marker-layer .ace_selection { position: absolute; - z-index: 3; + z-index: 4; } .ace_marker-layer .ace_bracket { position: absolute; - z-index: 4; + z-index: 5; } .ace_marker-layer .ace_active_line { position: absolute; - z-index: 1; -} \ No newline at end of file + z-index: 2; +} diff --git a/lib/ace/theme/twilight.js b/lib/ace/theme/twilight.js index 4d27f963..36fb8953 100644 --- a/lib/ace/theme/twilight.js +++ b/lib/ace/theme/twilight.js @@ -64,8 +64,9 @@ define(function(require, exports, module) { }\ \ .ace-twilight .ace_print_margin {\ - width: 1px;\ - background: #e8e8e8;\ + border-left: 1px solid #3C3C3C;\ + width: 100%;\ + background: #242424;\ }\ \ .ace-twilight .ace_scroller {\ diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 687085fa..e52cd2c0 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -20,6 +20,7 @@ * * Contributor(s): * Fabian Jakobs + * Irakli Gozalishvili (http://jeditoolkit.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -265,13 +266,17 @@ var VirtualRenderer = function(container, theme) { } this.$updatePrintMargin = function() { + var containerEl + if (!this.$showPrintMargin && !this.$printMarginEl) return; - if (!this.$printMarginEl) { - this.$printMarginEl = document.createElement("div"); - this.content.insertBefore(this.$printMarginEl, this.$textLayer.element); + containerEl = document.createElement("div"); + containerEl.className = "ace_print_margin_layer"; + this.$printMarginEl = document.createElement("div") this.$printMarginEl.className = "ace_print_margin"; + containerEl.appendChild(this.$printMarginEl); + this.content.insertBefore(containerEl, this.$textLayer.element); } var style = this.$printMarginEl.style; From 40627c28250363ec3aa482b5f0e905bb6cac7bc8 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 13:32:42 +0100 Subject: [PATCH 30/60] add warning style --- demo/styles.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/demo/styles.css b/demo/styles.css index 44ab6810..ff6a749f 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -27,6 +27,12 @@ body { background-position: 4px center; } +.ace_gutter-cell.ace_warning { + background-image: url("icons/warning_obj.gif"); + background-repeat: no-repeat; + background-position: 4px center; +} + #controls { width: 100%; height: 55px; From ef90df695a3a6fad3d61bbfc4f1a351d3f5ccd43 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 13:32:58 +0100 Subject: [PATCH 31/60] remove "mac" whitespace --- lib/ace/edit_session.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 701cb543..1177e245 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -531,9 +531,9 @@ var EditSession = function(text, mode) { var lastDelta = deltas[deltas.length-1]; this.selection.clearSelection(); - if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") + if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") this.selection.moveCursorToPosition(firstDelta.range.start); - if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") + if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") this.selection.setSelectionRange(Range.fromPoints(firstDelta.range.start, lastDelta.range.end)); }, @@ -550,9 +550,9 @@ var EditSession = function(text, mode) { var lastDelta = deltas[deltas.length-1]; this.selection.clearSelection(); - if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") + if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") this.selection.setSelectionRange(Range.fromPoints(firstDelta.range.start, lastDelta.range.end)); - if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") + if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") this.selection.moveCursorToPosition(firstDelta.range.start); }, From f9d03eed7b97d98f240cf12b6f9ca6a290c75d23 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 13:33:37 +0100 Subject: [PATCH 32/60] requireJS version of narcissus --- lib/ace/narcissus/jsdefs.js | 375 +++++++++ lib/ace/narcissus/jslex.js | 460 +++++++++++ lib/ace/narcissus/jsparse.js | 1432 ++++++++++++++++++++++++++++++++++ 3 files changed, 2267 insertions(+) create mode 100644 lib/ace/narcissus/jsdefs.js create mode 100644 lib/ace/narcissus/jslex.js create mode 100644 lib/ace/narcissus/jsparse.js diff --git a/lib/ace/narcissus/jsdefs.js b/lib/ace/narcissus/jsdefs.js new file mode 100644 index 00000000..09215364 --- /dev/null +++ b/lib/ace/narcissus/jsdefs.js @@ -0,0 +1,375 @@ +/* vim: set sw=4 ts=4 et tw=78: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Well-known constants and lookup tables. Many consts are generated from the + * tokens table via eval to minimize redundancy, so consumers must be compiled + * separately to take advantage of the simple switch-case constant propagation + * done by SpiderMonkey. + */ + +define(function(require, exports, module) { + +exports.options = { + version: 185, +}; + +(function() { + exports.hostGlobal = this +})(); + +var tokens = [ + // End of source. + "END", + + // Operators and punctuators. Some pair-wise order matters, e.g. (+, -) + // and (UNARY_PLUS, UNARY_MINUS). + "\n", ";", + ",", + "=", + "?", ":", "CONDITIONAL", + "||", + "&&", + "|", + "^", + "&", + "==", "!=", "===", "!==", + "<", "<=", ">=", ">", + "<<", ">>", ">>>", + "+", "-", + "*", "/", "%", + "!", "~", "UNARY_PLUS", "UNARY_MINUS", + "++", "--", + ".", + "[", "]", + "{", "}", + "(", ")", + + // Nonterminal tree node type codes. + "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", + "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER", + "GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL", + + // Terminals. + "IDENTIFIER", "NUMBER", "STRING", "REGEXP", + + // Keywords. + "break", + "case", "catch", "const", "continue", + "debugger", "default", "delete", "do", + "else", + "false", "finally", "for", "function", + "if", "in", "instanceof", + "let", + "new", "null", + "return", + "switch", + "this", "throw", "true", "try", "typeof", + "var", "void", + "yield", + "while", "with", +]; + +var statementStartTokens = [ + "break", + "const", "continue", + "debugger", "do", + "for", + "if", + "return", + "switch", + "throw", "try", + "var", + "yield", + "while", "with", +]; + +// Operator and punctuator mapping from token to tree node type name. +// NB: because the lexer doesn't backtrack, all token prefixes must themselves +// be valid tokens (e.g. !== is acceptable because its prefixes are the valid +// tokens != and !). +var opTypeNames = { + '\n': "NEWLINE", + ';': "SEMICOLON", + ',': "COMMA", + '?': "HOOK", + ':': "COLON", + '||': "OR", + '&&': "AND", + '|': "BITWISE_OR", + '^': "BITWISE_XOR", + '&': "BITWISE_AND", + '===': "STRICT_EQ", + '==': "EQ", + '=': "ASSIGN", + '!==': "STRICT_NE", + '!=': "NE", + '<<': "LSH", + '<=': "LE", + '<': "LT", + '>>>': "URSH", + '>>': "RSH", + '>=': "GE", + '>': "GT", + '++': "INCREMENT", + '--': "DECREMENT", + '+': "PLUS", + '-': "MINUS", + '*': "MUL", + '/': "DIV", + '%': "MOD", + '!': "NOT", + '~': "BITWISE_NOT", + '.': "DOT", + '[': "LEFT_BRACKET", + ']': "RIGHT_BRACKET", + '{': "LEFT_CURLY", + '}': "RIGHT_CURLY", + '(': "LEFT_PAREN", + ')': "RIGHT_PAREN" +}; + +// Hash of keyword identifier to tokens index. NB: we must null __proto__ to +// avoid toString, etc. namespace pollution. +var keywords = {__proto__: null}; + +// Define const END, etc., based on the token names. Also map name to index. +var tokenIds = {}; + +// Building up a string to be eval'd in different contexts. +var consts = "const "; +for (var i = 0, j = tokens.length; i < j; i++) { + if (i > 0) + consts += ", "; + var t = tokens[i]; + var name; + if (/^[a-z]/.test(t)) { + name = t.toUpperCase(); + keywords[t] = i; + } else { + name = (/^\W/.test(t) ? opTypeNames[t] : t); + } + consts += name + " = " + i; + tokenIds[name] = i; + tokens[t] = i; +} +consts += ";"; + +var isStatementStartCode = {__proto__: null}; +for (i = 0, j = statementStartTokens.length; i < j; i++) + isStatementStartCode[keywords[statementStartTokens[i]]] = true; + +// Map assignment operators to their indexes in the tokens array. +var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%']; + +for (i = 0, j = assignOps.length; i < j; i++) { + t = assignOps[i]; + assignOps[t] = tokens[t]; +} + +function defineGetter(obj, prop, fn, dontDelete, dontEnum) { + Object.defineProperty(obj, prop, + { get: fn, configurable: !dontDelete, enumerable: !dontEnum }); +} + +function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) { + Object.defineProperty(obj, prop, + { value: val, writable: !readOnly, configurable: !dontDelete, + enumerable: !dontEnum }); +} + +// Returns true if fn is a native function. (Note: SpiderMonkey specific.) +function isNativeCode(fn) { + // Relies on the toString method to identify native code. + return ((typeof fn) === "function") && fn.toString().match(/\[native code\]/); +} + +function getPropertyDescriptor(obj, name) { + while (obj) { + if (({}).hasOwnProperty.call(obj, name)) + return Object.getOwnPropertyDescriptor(obj, name); + obj = Object.getPrototypeOf(obj); + } +} + +function getOwnProperties(obj) { + var map = {}; + for (var name in Object.getOwnPropertyNames(obj)) + map[name] = Object.getOwnPropertyDescriptor(obj, name); + return map; +} + +function makePassthruHandler(obj) { + // Handler copied from + // http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy + return { + getOwnPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + + // a trapping proxy's properties must always be configurable + desc.configurable = true; + return desc; + }, + getPropertyDescriptor: function(name) { + var desc = getPropertyDescriptor(obj, name); + + // a trapping proxy's properties must always be configurable + desc.configurable = true; + return desc; + }, + getOwnPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + }, + defineProperty: function(name, desc) { + Object.defineProperty(obj, name, desc); + }, + "delete": function(name) { return delete obj[name]; }, + fix: function() { + if (Object.isFrozen(obj)) { + return getOwnProperties(obj); + } + + // As long as obj is not frozen, the proxy won't allow itself to be fixed. + return undefined; // will cause a TypeError to be thrown + }, + + has: function(name) { return name in obj; }, + hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); }, + get: function(receiver, name) { return obj[name]; }, + + // bad behavior when set fails in non-strict mode + set: function(receiver, name, val) { obj[name] = val; return true; }, + enumerate: function() { + var result = []; + for (name in obj) { result.push(name); }; + return result; + }, + keys: function() { return Object.keys(obj); } + }; +} + +// default function used when looking for a property in the global object +function noPropFound() { return undefined; } + +var hasOwnProperty = ({}).hasOwnProperty; + +function StringMap() { + this.table = Object.create(null, {}); + this.size = 0; +} + +StringMap.prototype = { + has: function(x) { return hasOwnProperty.call(this.table, x); }, + set: function(x, v) { + if (!hasOwnProperty.call(this.table, x)) + this.size++; + this.table[x] = v; + }, + get: function(x) { return this.table[x]; }, + getDef: function(x, thunk) { + if (!hasOwnProperty.call(this.table, x)) { + this.size++; + this.table[x] = thunk(); + } + return this.table[x]; + }, + forEach: function(f) { + var table = this.table; + for (var key in table) + f.call(this, key, table[key]); + }, + toString: function() { return "[object StringMap]" } +}; + +// non-destructive stack +function Stack(elts) { + this.elts = elts || null; +} + +Stack.prototype = { + push: function(x) { + return new Stack({ top: x, rest: this.elts }); + }, + top: function() { + if (!this.elts) + throw new Error("empty stack"); + return this.elts.top; + }, + isEmpty: function() { + return this.top === null; + }, + find: function(test) { + for (var elts = this.elts; elts; elts = elts.rest) { + if (test(elts.top)) + return elts.top; + } + return null; + }, + has: function(x) { + return Boolean(this.find(function(elt) { return elt === x })); + }, + forEach: function(f) { + for (var elts = this.elts; elts; elts = elts.rest) { + f(elts.top); + } + } +}; + +exports.tokens = tokens; +exports.opTypeNames = opTypeNames; +exports.keywords = keywords; +exports.isStatementStartCode = isStatementStartCode; +exports.tokenIds = tokenIds; +exports.consts = consts; +exports.assignOps = assignOps; +exports.defineGetter = defineGetter; +exports.defineProperty = defineProperty; +exports.isNativeCode = isNativeCode; +exports.makePassthruHandler = makePassthruHandler; +exports.noPropFound = noPropFound; +exports.StringMap = StringMap; +exports.Stack = Stack; + +}); \ No newline at end of file diff --git a/lib/ace/narcissus/jslex.js b/lib/ace/narcissus/jslex.js new file mode 100644 index 00000000..c551792f --- /dev/null +++ b/lib/ace/narcissus/jslex.js @@ -0,0 +1,460 @@ +/* vim: set sw=4 ts=4 et tw=78: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Lexical scanner. + */ + +define(function(require, exports, module) { + +var definitions = require("ace/narcissus/jsdefs"); + +// Set constants in the local scope. +eval(definitions.consts); + +// Build up a trie of operator tokens. +var opTokens = {}; +for (var op in definitions.opTypeNames) { + if (op === '\n' || op === '.') + continue; + + var node = opTokens; + for (var i = 0; i < op.length; i++) { + var ch = op[i]; + if (!(ch in node)) + node[ch] = {}; + node = node[ch]; + node.op = op; + } +} + +/* + * Tokenizer :: (source, filename, line number) -> Tokenizer + */ +function Tokenizer(s, f, l) { + this.cursor = 0; + this.source = String(s); + this.tokens = []; + this.tokenIndex = 0; + this.lookahead = 0; + this.scanNewlines = false; + this.unexpectedEOF = false; + this.filename = f || ""; + this.lineno = l || 1; +} + +Tokenizer.prototype = { + get done() { + // We need to set scanOperand to true here because the first thing + // might be a regexp. + return this.peek(true) === END; + }, + + get token() { + return this.tokens[this.tokenIndex]; + }, + + match: function (tt, scanOperand) { + return this.get(scanOperand) === tt || this.unget(); + }, + + mustMatch: function (tt) { + if (!this.match(tt)) { + throw this.newSyntaxError("Missing " + + definitions.tokens[tt].toLowerCase()); + } + return this.token; + }, + + peek: function (scanOperand) { + var tt, next; + if (this.lookahead) { + next = this.tokens[(this.tokenIndex + this.lookahead) & 3]; + tt = (this.scanNewlines && next.lineno !== this.lineno) + ? NEWLINE + : next.type; + } else { + tt = this.get(scanOperand); + this.unget(); + } + return tt; + }, + + peekOnSameLine: function (scanOperand) { + this.scanNewlines = true; + var tt = this.peek(scanOperand); + this.scanNewlines = false; + return tt; + }, + + // Eat comments and whitespace. + skip: function () { + var input = this.source; + for (;;) { + var ch = input[this.cursor++]; + var next = input[this.cursor]; + if (ch === '\n' && !this.scanNewlines) { + this.lineno++; + } else if (ch === '/' && next === '*') { + this.cursor++; + for (;;) { + ch = input[this.cursor++]; + if (ch === undefined) + throw this.newSyntaxError("Unterminated comment"); + + if (ch === '*') { + next = input[this.cursor]; + if (next === '/') { + this.cursor++; + break; + } + } else if (ch === '\n') { + this.lineno++; + } + } + } else if (ch === '/' && next === '/') { + this.cursor++; + for (;;) { + ch = input[this.cursor++]; + if (ch === undefined) + return; + + if (ch === '\n') { + this.lineno++; + break; + } + } + } else if (ch !== ' ' && ch !== '\t') { + this.cursor--; + return; + } + } + }, + + // Lex the exponential part of a number, if present. Return true iff an + // exponential part was found. + lexExponent: function() { + var input = this.source; + var next = input[this.cursor]; + if (next === 'e' || next === 'E') { + this.cursor++; + ch = input[this.cursor++]; + if (ch === '+' || ch === '-') + ch = input[this.cursor++]; + + if (ch < '0' || ch > '9') + throw this.newSyntaxError("Missing exponent"); + + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + return true; + } + + return false; + }, + + lexZeroNumber: function (ch) { + var token = this.token, input = this.source; + token.type = NUMBER; + + ch = input[this.cursor++]; + if (ch === '.') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + this.lexExponent(); + token.value = parseFloat(token.start, this.cursor); + } else if (ch === 'x' || ch === 'X') { + do { + ch = input[this.cursor++]; + } while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F')); + this.cursor--; + + token.value = parseInt(input.substring(token.start, this.cursor)); + } else if (ch >= '0' && ch <= '7') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '7'); + this.cursor--; + + token.value = parseInt(input.substring(token.start, this.cursor)); + } else { + this.cursor--; + this.lexExponent(); // 0E1, &c. + token.value = 0; + } + }, + + lexNumber: function (ch) { + var token = this.token, input = this.source; + token.type = NUMBER; + + var floating = false; + do { + ch = input[this.cursor++]; + if (ch === '.' && !floating) { + floating = true; + ch = input[this.cursor++]; + } + } while (ch >= '0' && ch <= '9'); + + this.cursor--; + + var exponent = this.lexExponent(); + floating = floating || exponent; + + var str = input.substring(token.start, this.cursor); + token.value = floating ? parseFloat(str) : parseInt(str); + }, + + lexDot: function (ch) { + var token = this.token, input = this.source; + var next = input[this.cursor]; + if (next >= '0' && next <= '9') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + this.lexExponent(); + + token.type = NUMBER; + token.value = parseFloat(token.start, this.cursor); + } else { + token.type = DOT; + token.assignOp = null; + token.value = '.'; + } + }, + + lexString: function (ch) { + var token = this.token, input = this.source; + token.type = STRING; + + var hasEscapes = false; + var delim = ch; + while ((ch = input[this.cursor++]) !== delim) { + if (this.cursor == input.length) + throw this.newSyntaxError("Unterminated string literal"); + if (ch === '\\') { + hasEscapes = true; + if (++this.cursor == input.length) + throw this.newSyntaxError("Unterminated string literal"); + } + } + + token.value = hasEscapes + ? eval(input.substring(token.start, this.cursor)) + : input.substring(token.start + 1, this.cursor - 1); + }, + + lexRegExp: function (ch) { + var token = this.token, input = this.source; + token.type = REGEXP; + + do { + ch = input[this.cursor++]; + if (ch === '\\') { + this.cursor++; + } else if (ch === '[') { + do { + if (ch === undefined) + throw this.newSyntaxError("Unterminated character class"); + + if (ch === '\\') + this.cursor++; + + ch = input[this.cursor++]; + } while (ch !== ']'); + } else if (ch === undefined) { + throw this.newSyntaxError("Unterminated regex"); + } + } while (ch !== '/'); + + do { + ch = input[this.cursor++]; + } while (ch >= 'a' && ch <= 'z'); + + this.cursor--; + + token.value = eval(input.substring(token.start, this.cursor)); + }, + + lexOp: function (ch) { + var token = this.token, input = this.source; + + // A bit ugly, but it seems wasteful to write a trie lookup routine + // for only 3 characters... + var node = opTokens[ch]; + var next = input[this.cursor]; + if (next in node) { + node = node[next]; + this.cursor++; + next = input[this.cursor]; + if (next in node) { + node = node[next]; + this.cursor++; + next = input[this.cursor]; + } + } + + var op = node.op; + if (definitions.assignOps[op] && input[this.cursor] === '=') { + this.cursor++; + token.type = ASSIGN; + token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]]; + op += '='; + } else { + token.type = definitions.tokenIds[definitions.opTypeNames[op]]; + token.assignOp = null; + } + + token.value = op; + }, + + // FIXME: Unicode escape sequences + // FIXME: Unicode identifiers + lexIdent: function (ch) { + var token = this.token, input = this.source; + + do { + ch = input[this.cursor++]; + } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch === '$' || ch === '_'); + + this.cursor--; // Put the non-word character back. + + var id = input.substring(token.start, this.cursor); + token.type = definitions.keywords[id] || IDENTIFIER; + token.value = id; + }, + + /* + * Tokenizer.get :: void -> token type + * + * Consume input *only* if there is no lookahead. + * Dispatch to the appropriate lexing function depending on the input. + */ + get: function (scanOperand) { + var token; + while (this.lookahead) { + --this.lookahead; + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (token.type !== NEWLINE || this.scanNewlines) + return token.type; + } + + this.skip(); + + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (!token) + this.tokens[this.tokenIndex] = token = {}; + + var input = this.source; + if (this.cursor === input.length) + return token.type = END; + + token.start = this.cursor; + token.lineno = this.lineno; + + var ch = input[this.cursor++]; + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '$' || ch === '_') { + this.lexIdent(ch); + } else if (scanOperand && ch === '/') { + this.lexRegExp(ch); + } else if (ch in opTokens) { + this.lexOp(ch); + } else if (ch === '.') { + this.lexDot(ch); + } else if (ch >= '1' && ch <= '9') { + this.lexNumber(ch); + } else if (ch === '0') { + this.lexZeroNumber(ch); + } else if (ch === '"' || ch === "'") { + this.lexString(ch); + } else if (this.scanNewlines && ch === '\n') { + token.type = NEWLINE; + token.value = '\n'; + this.lineno++; + } else { + throw this.newSyntaxError("Illegal token"); + } + + token.end = this.cursor; + return token.type; + }, + + /* + * Tokenizer.unget :: void -> undefined + * + * Match depends on unget returning undefined. + */ + unget: function () { + if (++this.lookahead === 4) throw "PANIC: too much lookahead!"; + this.tokenIndex = (this.tokenIndex - 1) & 3; + }, + + newSyntaxError: function (m) { + var e = new SyntaxError(m, this.filename, this.lineno); + e.source = this.source; + e.lineno = this.lineno; + e.cursor = this.lookahead + ? this.tokens[(this.tokenIndex + this.lookahead) & 3].start + : this.cursor; + return e; + }, +}; + +exports.Tokenizer = Tokenizer; + +}); \ No newline at end of file diff --git a/lib/ace/narcissus/jsparse.js b/lib/ace/narcissus/jsparse.js new file mode 100644 index 00000000..46b85ff3 --- /dev/null +++ b/lib/ace/narcissus/jsparse.js @@ -0,0 +1,1432 @@ +/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*- + * vim: set sw=4 ts=4 et tw=78: + * ***** BEGIN LICENSE BLOCK ***** + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Parser. + */ + +define(function(require, exports, module) { + +var lexer = require("ace/narcissus/jslex"); +var definitions = require("ace/narcissus/jsdefs"); + +const StringMap = definitions.StringMap; +const Stack = definitions.Stack; + +// Set constants in the local scope. +eval(definitions.consts); + +/* + * pushDestructuringVarDecls :: (node, hoisting node) -> void + * + * Recursively add all destructured declarations to varDecls. + */ +function pushDestructuringVarDecls(n, s) { + for (var i in n) { + var sub = n[i]; + if (sub.type === IDENTIFIER) { + s.varDecls.push(sub); + } else { + pushDestructuringVarDecls(sub, s); + } + } +} + +// NESTING_TOP: top-level +// NESTING_SHALLOW: nested within static forms such as { ... } or labeled statement +// NESTING_DEEP: nested within dynamic forms such as if, loops, etc. +const NESTING_TOP = 0, NESTING_SHALLOW = 1, NESTING_DEEP = 2; + +function StaticContext(parentScript, parentBlock, inFunction, inForLoopInit, nesting) { + this.parentScript = parentScript; + this.parentBlock = parentBlock; + this.inFunction = inFunction; + this.inForLoopInit = inForLoopInit; + this.nesting = nesting; + this.allLabels = new Stack(); + this.currentLabels = new Stack(); + this.labeledTargets = new Stack(); + this.defaultTarget = null; + definitions.options.ecma3OnlyMode && (this.ecma3OnlyMode = true); + definitions.options.parenFreeMode && (this.parenFreeMode = true); +} + +StaticContext.prototype = { + ecma3OnlyMode: false, + parenFreeMode: false, + // non-destructive update via prototype extension + update: function(ext) { + var desc = {}; + for (var key in ext) { + desc[key] = { + value: ext[key], + writable: true, + enumerable: true, + configurable: true + } + } + return Object.create(this, desc); + }, + pushLabel: function(label) { + return this.update({ currentLabels: this.currentLabels.push(label), + allLabels: this.allLabels.push(label) }); + }, + pushTarget: function(target) { + var isDefaultTarget = target.isLoop || target.type === SWITCH; + + if (this.currentLabels.isEmpty()) { + return isDefaultTarget + ? this.update({ defaultTarget: target }) + : this; + } + + target.labels = new StringMap(); + this.currentLabels.forEach(function(label) { + target.labels.set(label, true); + }); + return this.update({ currentLabels: new Stack(), + labeledTargets: this.labeledTargets.push(target), + defaultTarget: isDefaultTarget + ? target + : this.defaultTarget }); + }, + nest: function(atLeast) { + var nesting = Math.max(this.nesting, atLeast); + return (nesting !== this.nesting) + ? this.update({ nesting: nesting }) + : this; + } +}; + +/* + * Script :: (tokenizer, boolean) -> node + * + * Parses the toplevel and function bodies. + */ +function Script(t, inFunction) { + var n = new Node(t, scriptInit()); + var x = new StaticContext(n, n, inFunction, false, NESTING_TOP); + Statements(t, x, n); + return n; +} + +// We extend Array slightly with a top-of-stack method. +definitions.defineProperty(Array.prototype, "top", + function() { + return this.length && this[this.length-1]; + }, false, false, true); + +/* + * Node :: (tokenizer, optional init object) -> node + */ +function Node(t, init) { + var token = t.token; + if (token) { + // If init.type exists it will override token.type. + this.type = token.type; + this.value = token.value; + this.lineno = token.lineno; + + // Start and end are file positions for error handling. + this.start = token.start; + this.end = token.end; + } else { + this.lineno = t.lineno; + } + + // Node uses a tokenizer for debugging (getSource, filename getter). + this.tokenizer = t; + this.children = []; + + for (var prop in init) + this[prop] = init[prop]; +} + +var Np = Node.prototype = {}; +Np.constructor = Node; +Np.toSource = Object.prototype.toSource; + +// Always use push to add operands to an expression, to update start and end. +Np.push = function (kid) { + // kid can be null e.g. [1, , 2]. + if (kid !== null) { + if (kid.start < this.start) + this.start = kid.start; + if (this.end < kid.end) + this.end = kid.end; + } + return this.children.push(kid); +} + +Node.indentLevel = 0; + +function tokenString(tt) { + var t = definitions.tokens[tt]; + return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase(); +} + +Np.toString = function () { + var a = []; + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target') + a.push({id: i, value: this[i]}); + } + a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); + const INDENTATION = " "; + var n = ++Node.indentLevel; + var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenString(this.type); + for (i = 0; i < a.length; i++) + s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; + n = --Node.indentLevel; + s += "\n" + INDENTATION.repeat(n) + "}"; + return s; +} + +Np.getSource = function () { + return this.tokenizer.source.slice(this.start, this.end); +}; + +/* + * Helper init objects for common nodes. + */ + +const LOOP_INIT = { isLoop: true }; + +function blockInit() { + return { type: BLOCK, varDecls: [] }; +} + +function scriptInit() { + return { type: SCRIPT, + funDecls: [], + varDecls: [], + modDecls: [], + impDecls: [], + expDecls: [], + loadDeps: [], + hasEmptyReturn: false, + hasReturnWithValue: false, + isGenerator: false }; +} + +definitions.defineGetter(Np, "filename", + function() { + return this.tokenizer.filename; + }); + +definitions.defineProperty(String.prototype, "repeat", + function(n) { + var s = "", t = this + s; + while (--n >= 0) + s += t; + return s; + }, false, false, true); + +function MaybeLeftParen(t, x) { + if (x.parenFreeMode) + return t.match(LEFT_PAREN) ? LEFT_PAREN : END; + return t.mustMatch(LEFT_PAREN).type; +} + +function MaybeRightParen(t, p) { + if (p === LEFT_PAREN) + t.mustMatch(RIGHT_PAREN); +} + +/* + * Statements :: (tokenizer, compiler context, node) -> void + * + * Parses a sequence of Statements. + */ +function Statements(t, x, n) { + try { + while (!t.done && t.peek(true) !== RIGHT_CURLY) + n.push(Statement(t, x)); + } catch (e) { + if (t.done) + t.unexpectedEOF = true; + throw e; + } +} + +function Block(t, x) { + t.mustMatch(LEFT_CURLY); + var n = new Node(t, blockInit()); + Statements(t, x.update({ parentBlock: n }).pushTarget(n), n); + t.mustMatch(RIGHT_CURLY); + return n; +} + +const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2; + +/* + * Statement :: (tokenizer, compiler context) -> node + * + * Parses a Statement. + */ +function Statement(t, x) { + var i, label, n, n2, p, c, ss, tt = t.get(true), tt2, x2, x3; + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch (tt) { + case FUNCTION: + // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't. + return FunctionDefinition(t, x, true, + (x.nesting !== NESTING_TOP) + ? STATEMENT_FORM + : DECLARED_FORM); + + case LEFT_CURLY: + n = new Node(t, blockInit()); + Statements(t, x.update({ parentBlock: n }).pushTarget(n).nest(NESTING_SHALLOW), n); + t.mustMatch(RIGHT_CURLY); + return n; + + case IF: + n = new Node(t); + n.condition = HeadExpression(t, x); + x2 = x.pushTarget(n).nest(NESTING_DEEP); + n.thenPart = Statement(t, x2); + n.elsePart = t.match(ELSE) ? Statement(t, x2) : null; + return n; + + case SWITCH: + // This allows CASEs after a DEFAULT, which is in the standard. + n = new Node(t, { cases: [], defaultIndex: -1 }); + n.discriminant = HeadExpression(t, x); + x2 = x.pushTarget(n).nest(NESTING_DEEP); + t.mustMatch(LEFT_CURLY); + while ((tt = t.get()) !== RIGHT_CURLY) { + switch (tt) { + case DEFAULT: + if (n.defaultIndex >= 0) + throw t.newSyntaxError("More than one switch default"); + // FALL THROUGH + case CASE: + n2 = new Node(t); + if (tt === DEFAULT) + n.defaultIndex = n.cases.length; + else + n2.caseLabel = Expression(t, x2, COLON); + break; + + default: + throw t.newSyntaxError("Invalid switch case"); + } + t.mustMatch(COLON); + n2.statements = new Node(t, blockInit()); + while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT && + tt !== RIGHT_CURLY) + n2.statements.push(Statement(t, x2)); + n.cases.push(n2); + } + return n; + + case FOR: + n = new Node(t, LOOP_INIT); + if (t.match(IDENTIFIER)) { + if (t.token.value === "each") + n.isEach = true; + else + t.unget(); + } + if (!x.parenFreeMode) + t.mustMatch(LEFT_PAREN); + x2 = x.pushTarget(n).nest(NESTING_DEEP); + x3 = x.update({ inForLoopInit: true }); + if ((tt = t.peek()) !== SEMICOLON) { + if (tt === VAR || tt === CONST) { + t.get(); + n2 = Variables(t, x3); + } else if (tt === LET) { + t.get(); + if (t.peek() === LEFT_PAREN) { + n2 = LetBlock(t, x3, false); + } else { + // Let in for head, we need to add an implicit block + // around the rest of the for. + x3.parentBlock = n; + n.varDecls = []; + n2 = Variables(t, x3); + } + } else { + n2 = Expression(t, x3); + } + } + if (n2 && t.match(IN)) { + n.type = FOR_IN; + n.object = Expression(t, x3); + if (n2.type === VAR || n2.type === LET) { + c = n2.children; + + // Destructuring turns one decl into multiples, so either + // there must be only one destructuring or only one + // decl. + if (c.length !== 1 && n2.destructurings.length !== 1) { + throw new SyntaxError("Invalid for..in left-hand side", + t.filename, n2.lineno); + } + if (n2.destructurings.length > 0) { + n.iterator = n2.destructurings[0]; + } else { + n.iterator = c[0]; + } + n.varDecl = n2; + } else { + if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) { + n2.destructuredNames = checkDestructuring(t, x3, n2); + } + n.iterator = n2; + } + } else { + n.setup = n2; + t.mustMatch(SEMICOLON); + if (n.isEach) + throw t.newSyntaxError("Invalid for each..in loop"); + n.condition = (t.peek() === SEMICOLON) + ? null + : Expression(t, x3); + t.mustMatch(SEMICOLON); + tt2 = t.peek(); + n.update = (x.parenFreeMode + ? tt2 === LEFT_CURLY || definitions.isStatementStartCode[tt2] + : tt2 === RIGHT_PAREN) + ? null + : Expression(t, x3); + } + if (!x.parenFreeMode) + t.mustMatch(RIGHT_PAREN); + n.body = Statement(t, x2); + return n; + + case WHILE: + n = new Node(t, { isLoop: true }); + n.condition = HeadExpression(t, x); + n.body = Statement(t, x.pushTarget(n).nest(NESTING_DEEP)); + return n; + + case DO: + n = new Node(t, { isLoop: true }); + n.body = Statement(t, x.pushTarget(n).nest(NESTING_DEEP)); + t.mustMatch(WHILE); + n.condition = HeadExpression(t, x); + if (!x.ecmaStrictMode) { + // diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 1177e245..3d768a58 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -241,7 +241,7 @@ var EditSession = function(text, mode) { }; this.tokenRe = /^[\w\d]+/g; - this.nonTokenRe = /^[^\w\d]+/g; + this.nonTokenRe = /^[^\w\d|\x00-\xff]+/g; this.getWordRange = function(row, column) { var line = this.getLine(row); @@ -633,19 +633,23 @@ var EditSession = function(text, mode) { var screenColumn = 0; var remaining = docColumn; - - var line = this.getLine(row).split("\t"); - for (var i=0; i len) { - remaining -= (len + 1); - screenColumn += len + tabSize; - } - else { - screenColumn += remaining; - break; - } - } + var line = this.getLine(row); + + for(var i=0; i 0){ + remaining -= 1; + if(c == '\t'){ + screenColumn += tabSize; + } else if(/[^\x00-\xff]/.exec(c)){ + screenColumn += 2; + } else { + screenColumn += 1; + } + }else{ + break; + } + } return screenColumn; }; @@ -655,23 +659,24 @@ var EditSession = function(text, mode) { var docColumn = 0; var remaining = screenColumn; - - var line = this.getLine(row).split("\t"); - for (var i=0; i= len + tabSize) { - remaining -= (len + tabSize); - docColumn += (len + 1); - } - else if (remaining > len){ - docColumn += len; - break; - } - else { - docColumn += remaining; - break; - } - } + var line = this.getLine(row); + + for(var i=0; i 0){ + docColumn += 1; + if(c == '\t'){ + remaining -= tabSize; + } else if(/[^\x00-\xff]/.exec(c)){ + remaining -= 2; + } else { + remaining -= 1; + } + }else{ + break; + } + } + return docColumn; }; diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index ecaa6698..815f669a 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -274,6 +274,17 @@ var Text = function(parentEl) { .replace(/" + w + ""; + }else{ + buffer = buffer + w; + } + } + output = buffer; if (!this.$textToken[token.type]) { var classes = "ace_" + token.type.replace(/\./g, " ace_"); diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index affce7fb..0ae38f7d 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -591,12 +591,35 @@ var VirtualRenderer = function(container, theme) { }; this.showComposition = function(position) { + var cur = this.$cursorLayer.element.firstChild; + var scroll = this.scroller; + var top = (parseInt(cur.style.top) || 0) + (parseInt(scroll.style.top) || 0); + var left = (parseInt(cur.style.left) || 0) + (parseInt(scroll.style.left) || 0); + + var edit = document.getElementById("editor").lastChild; + edit.style.color = '#66ff33'; + edit.style.top = (top - 1) + "px"; + edit.style.left = (left + 3) + "px"; + edit.style.background = "transparent"; + edit.style.border = "none"; + edit.style.resize = "none"; + edit.style.outline = "none"; + edit.style.width = "10000px"; + edit.style.height = "10000px"; + edit.style.fontSize = "12px"; + + this.hideCursor(); }; this.setCompositionText = function(text) { }; this.hideComposition = function() { + this.showCursor(); + var edit = document.getElementById("editor").lastChild; + edit.className = ''; + edit.style.top = "-10000px"; + edit.style.left = "-10000px"; }; this.setTheme = function(theme) { diff --git a/static.py b/static.py index 8cac3b7a..801a70d7 100755 --- a/static.py +++ b/static.py @@ -231,7 +231,7 @@ def test(): app = Cling(getcwd()) try: print "Serving " + getcwd() + " to http://localhost:9999" - make_server('localhost', 9999, validator(app)).serve_forever() + make_server('0.0.0.0', 9999, validator(app)).serve_forever() except KeyboardInterrupt, ki: print "" print "Ciao, baby!" From b98a404d983fe115c05915526105d316069fe0a6 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 25 Jan 2011 15:47:56 +0100 Subject: [PATCH 38/60] Change git submodule to point to the latest pilot. --- support/pilot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/pilot b/support/pilot index 85ecce3f..b3a656ba 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit 85ecce3f54711a5198ca7771bb000edbe71a0010 +Subproject commit b3a656ba07e038bb8f84530c4f56c38f7029929d From 83a43bfd625cb18cef4c38d7040e9b53ffce39ab Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 25 Jan 2011 16:00:01 +0100 Subject: [PATCH 39/60] Removing unnecessary and dependency on heavy 'node-o3-xml'. --- .gitmodules | 3 --- support/node-o3-xml | 1 - 2 files changed, 4 deletions(-) delete mode 160000 support/node-o3-xml diff --git a/.gitmodules b/.gitmodules index aee8d549..6074430c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "support/node-o3-xml"] - path = support/node-o3-xml - url = git://github.com/ajaxorg/node-o3-xml.git [submodule "support/cockpit"] path = support/cockpit url = git://github.com/ajaxorg/cockpit.git diff --git a/support/node-o3-xml b/support/node-o3-xml deleted file mode 160000 index 563a2b10..00000000 --- a/support/node-o3-xml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 563a2b1091bd1b9a7d26a9f597aacb4341edb619 From 681dace8ce33e6b0177ce850b1540315097afd61 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 25 Jan 2011 23:00:01 +0800 Subject: [PATCH 40/60] Removing unnecessary and dependency on heavy 'node-o3-xml'. --- .gitmodules | 3 --- support/node-o3-xml | 1 - 2 files changed, 4 deletions(-) delete mode 160000 support/node-o3-xml diff --git a/.gitmodules b/.gitmodules index aee8d549..6074430c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "support/node-o3-xml"] - path = support/node-o3-xml - url = git://github.com/ajaxorg/node-o3-xml.git [submodule "support/cockpit"] path = support/cockpit url = git://github.com/ajaxorg/cockpit.git diff --git a/support/node-o3-xml b/support/node-o3-xml deleted file mode 160000 index 563a2b10..00000000 --- a/support/node-o3-xml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 563a2b1091bd1b9a7d26a9f597aacb4341edb619 From 383e493fa0f450f5c097a0f03d9d426aa78ee4f6 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:36:51 +0100 Subject: [PATCH 41/60] fire event when session changes --- lib/ace/editor.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 26fbb1c2..efa1cb9a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -122,6 +122,7 @@ var Editor =function(renderer, session) { if (this.session == session) return; if (this.session) { + var oldSession = this.session; this.session.removeEventListener("change", this.$onDocumentChange); this.session.removeEventListener("changeMode", this.$onDocumentModeChange); this.session.removeEventListener("changeTabSize", this.$onDocumentChangeTabSize); @@ -172,6 +173,11 @@ var Editor =function(renderer, session) { this.onDocumentChangeAnnotation(); this.renderer.scrollToRow(session.getScrollTopRow()); this.renderer.updateFull(); + + this._dispatchEvent("changeSession", { + session: session, + oldSession: oldSession + }); }; this.getSession = function() { From 56c97c992c544111b69684133076f49b978d10c6 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:37:19 +0100 Subject: [PATCH 42/60] move hidden text area over the cursor --- lib/ace/keyboard/textinput.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index 08812a0a..459f9e0e 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -43,11 +43,34 @@ var TextInput = function(parentNode, host) { var text = document.createElement("textarea"); var style = text.style; - style.position = "absolute"; + style.position = "fixed"; style.left = "-10000px"; style.top = "-10000px"; parentNode.appendChild(text); + // move text input over the cursor + // this is required for iOS and IME + style.left = "0px"; + style.top = "0px"; + style.zIndex = -1; + style.opacity = 0; + style.width = "10px"; + style.height = "30px"; + + var changeCursor = function() { + var cursor = host.getCursorPosition(); + var pos = host.renderer.textToScreenCoordinates(cursor.row, cursor.column); + var epos = parentNode.getBoundingClientRect(); + style.left = (pos.pageX - epos.left - 6) + "px"; + style.top = (pos.pageY - epos.top + 52) + "px"; + }; + + host.addEventListener("changeSession", function(e) { + if (e.oldSession) + e.oldSession.getSelection().removeEventListener("changeCursor", changeCursor); + e.session.getSelection().addEventListener("changeCursor", changeCursor); + }); + var PLACEHOLDER = String.fromCharCode(0); sendText(); From 149c7c3ff55b5ff41109358f393ec14f020842ca Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:37:56 +0100 Subject: [PATCH 43/60] finish cjk support --- lib/ace/css/editor.css | 20 ++++++++++++ lib/ace/edit_session.js | 64 ++++++++++++++++++++++++++----------- lib/ace/layer/text.js | 18 ++++------- lib/ace/virtual_renderer.js | 48 +++++++++++++++------------- 4 files changed, 97 insertions(+), 53 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index cd537ddd..0390dc5a 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -12,6 +12,20 @@ overflow-y: hidden; } +.ace_content { + position: absolute; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.ace_composition { + position: absolute; + background: #555; + color: #DDD; + z-index: 4; +} + .ace_gutter { position: absolute; overflow-x: hidden; @@ -51,8 +65,14 @@ color: black; } +.ace_cjk { + display: inline-block; + text-align: center; +} + .ace_cursor-layer { cursor: text; + pointer-events: none; } .ace_cursor { diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 3d768a58..5ae0998c 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -241,8 +241,7 @@ var EditSession = function(text, mode) { }; this.tokenRe = /^[\w\d]+/g; - this.nonTokenRe = /^[^\w\d|\x00-\xff]+/g; - + this.nonTokenRe = /^(?:[^\w\d|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF])+/g this.getWordRange = function(row, column) { var line = this.getLine(row); @@ -635,18 +634,27 @@ var EditSession = function(text, mode) { var remaining = docColumn; var line = this.getLine(row); - for(var i=0; i 0){ + for (var i=0; i 0) { remaining -= 1; - if(c == '\t'){ + // tab + if (c == 9) { screenColumn += tabSize; - } else if(/[^\x00-\xff]/.exec(c)){ - screenColumn += 2; + } + // CJK characters + else if ( + c >= 0x3040 && c <= 0x309F || // Hiragana + c >= 0x30A0 && c <= 0x30FF || // Katakana + c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs + c >= 0xF900 && c <= 0xFAFF || + c >= 0x3400 && c <= 0x4DBF + ) { + screenColumn += 2; } else { screenColumn += 1; } - }else{ + } else { break; } } @@ -661,22 +669,42 @@ var EditSession = function(text, mode) { var remaining = screenColumn; var line = this.getLine(row); - for(var i=0; i 0){ + for(var i=0; i 0) { docColumn += 1; - if(c == '\t'){ - remaining -= tabSize; - } else if(/[^\x00-\xff]/.exec(c)){ - remaining -= 2; + // tab + if (c == 9) { + if (remaining >= tabSize) { + remaining -= tabSize; + } else { + remaining = 0; + docColumn -= 1; + } + } + // CJK characters + else if ( + c >= 0x3040 && c <= 0x309F || // Hiragana + c >= 0x30A0 && c <= 0x30FF || // Katakana + c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs + c >= 0xF900 && c <= 0xFAFF || + c >= 0x3400 && c <= 0x4DBF + ) { + if (remaining >= 2) { + remaining -= 2; + } else { + remaining = 0; + docColumn -= 1; + } } else { remaining -= 1; } - }else{ + } else { break; } } - + return docColumn; }; diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 815f669a..568f1db9 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -266,6 +266,8 @@ var Text = function(parentEl) { var spaceReplace = " "; } + var characterWidth = this.config.characterWidth; + for ( var i = 0; i < tokens.length; i++) { var token = tokens[i]; @@ -273,19 +275,11 @@ var Text = function(parentEl) { .replace(/&/g, "&") .replace(/" + c + "" + }); - var buffer = ''; - for (var j=0;j" + w + ""; - }else{ - buffer = buffer + w; - } - } - output = buffer; - if (!this.$textToken[token.type]) { var classes = "ace_" + token.type.replace(/\./g, " ace_"); stringBuilder.push("", output, ""); diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 0ae38f7d..fba0aca1 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -67,8 +67,7 @@ var VirtualRenderer = function(container, theme) { this.container.appendChild(this.scroller); this.content = document.createElement("div"); - this.content.style.cssText = "position:absolute;box-sizing:border-box;" + - "-moz-box-sizing:border-box;-webkit-box-sizing:border-box"; + this.content.className = "ace_content"; this.scroller.appendChild(this.content); this.$gutterLayer = new GutterLayer(this.$gutter); @@ -591,35 +590,38 @@ var VirtualRenderer = function(container, theme) { }; this.showComposition = function(position) { - var cur = this.$cursorLayer.element.firstChild; - var scroll = this.scroller; - var top = (parseInt(cur.style.top) || 0) + (parseInt(scroll.style.top) || 0); - var left = (parseInt(cur.style.left) || 0) + (parseInt(scroll.style.left) || 0); - - var edit = document.getElementById("editor").lastChild; - edit.style.color = '#66ff33'; - edit.style.top = (top - 1) + "px"; - edit.style.left = (left + 3) + "px"; - edit.style.background = "transparent"; - edit.style.border = "none"; - edit.style.resize = "none"; - edit.style.outline = "none"; - edit.style.width = "10000px"; - edit.style.height = "10000px"; - edit.style.fontSize = "12px"; - + console.log("show composition") + if (!this.$composition) { + this.$composition = document.createElement("div"); + this.$composition.className = "ace_composition"; + this.content.appendChild(this.$composition); + } + + this.$composition.innerHTML = " "; + + var pos = this.$cursorLayer.getPixelPosition(); + var style = this.$composition.style; + style.top = pos.top + "px"; + style.left = (pos.left + this.$padding) + "px"; + style.height = this.lineHeight + "px"; + //style.width = this.characterWidth + "px"; + this.hideCursor(); }; this.setCompositionText = function(text) { + this.$composition.innerText = this.$composition.textContent = text; }; this.hideComposition = function() { this.showCursor(); - var edit = document.getElementById("editor").lastChild; - edit.className = ''; - edit.style.top = "-10000px"; - edit.style.left = "-10000px"; + + if (!this.$composition) + return; + + var style = this.$composition.style; + style.top = "-10000px"; + style.left = "-10000px"; }; this.setTheme = function(theme) { From 34278a590a497a43de9312003383bbf1f203b07a Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:38:13 +0100 Subject: [PATCH 44/60] add missing function to mock --- lib/ace/test/mockrenderer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ace/test/mockrenderer.js b/lib/ace/test/mockrenderer.js index be4cd520..412a418a 100644 --- a/lib/ace/test/mockrenderer.js +++ b/lib/ace/test/mockrenderer.js @@ -129,5 +129,8 @@ MockRenderer.prototype.showCursor = function() { MockRenderer.prototype.visualizeFocus = function() { }; +MockRenderer.prototype.setAnnotations = function() { +}; + return MockRenderer; }); From 2844d1d08dc6bc92680b9a1bcaff706fd261abcb Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:38:35 +0100 Subject: [PATCH 45/60] add box model property to the CSS mode --- lib/ace/mode/css_highlight_rules.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ace/mode/css_highlight_rules.js b/lib/ace/mode/css_highlight_rules.js index 1dc74e15..f13c821e 100644 --- a/lib/ace/mode/css_highlight_rules.js +++ b/lib/ace/mode/css_highlight_rules.js @@ -44,14 +44,14 @@ var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightR var CssHighlightRules = function() { var properties = lang.arrayToMap( - ("azimuth|background-attachment|background-color|background-image|" + + ("-moz-box-sizing|-webkit-box-sizing|azimuth|background-attachment|background-color|background-image|" + "background-position|background-repeat|background|border-bottom-color|" + "border-bottom-style|border-bottom-width|border-bottom|border-collapse|" + "border-color|border-left-color|border-left-style|border-left-width|" + "border-left|border-right-color|border-right-style|border-right-width|" + "border-right|border-spacing|border-style|border-top-color|" + "border-top-style|border-top-width|border-top|border-width|border|" + - "bottom|caption-side|clear|clip|color|content|counter-increment|" + + "bottom|box-sizing|caption-side|clear|clip|color|content|counter-increment|" + "counter-reset|cue-after|cue-before|cue|cursor|direction|display|" + "elevation|empty-cells|float|font-family|font-size-adjust|font-size|" + "font-stretch|font-style|font-variant|font-weight|font|height|left|" + @@ -76,8 +76,8 @@ var CssHighlightRules = function() { var constants = lang.arrayToMap( ("absolute|all-scroll|always|armenian|auto|baseline|below|bidi-override|" + - "block|bold|bolder|both|bottom|break-all|break-word|capitalize|center|" + - "char|circle|cjk-ideographic|col-resize|collapse|crosshair|dashed|" + + "block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|" + + "char|circle|cjk-ideographic|col-resize|collapse|content-box|crosshair|dashed|" + "decimal-leading-zero|decimal|default|disabled|disc|" + "distribute-all-lines|distribute-letter|distribute-space|" + "distribute|dotted|double|e-resize|ellipsis|fixed|georgian|groove|" + From c388ba2964cd3b971002b799263272c3b118db83 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:38:47 +0100 Subject: [PATCH 46/60] remove outdated build demo --- editor-build.html | 213 ---------------------------------------------- 1 file changed, 213 deletions(-) delete mode 100644 editor-build.html diff --git a/editor-build.html b/editor-build.html deleted file mode 100644 index b506decc..00000000 --- a/editor-build.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - Editor - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - -
- info -
-
- -
-
- - - - - - - - - - - - - - - - - - - From fb1cedcbc298fd763a9204afe76bd385e7bad8a5 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 25 Jan 2011 21:40:00 +0100 Subject: [PATCH 47/60] update pilot --- support/pilot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/pilot b/support/pilot index d1efe31c..af903446 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit d1efe31c7df29526e6ffd8fe294dbd54b8227e61 +Subproject commit af90344687c8486892b44d5decc4c2181df3a6cf From 275a34a1dc279cf1e428e8f213a270514d8bbc36 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 26 Jan 2011 00:35:31 +0100 Subject: [PATCH 48/60] Merge branch 'keyboard-handling' into vice --- lib/ace/keyboard/state_handler.js | 17 ++++++++++++----- support/pilot | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/ace/keyboard/state_handler.js b/lib/ace/keyboard/state_handler.js index ab50bc74..5861ad23 100644 --- a/lib/ace/keyboard/state_handler.js +++ b/lib/ace/keyboard/state_handler.js @@ -80,13 +80,13 @@ StateHandler.prototype = { } var keyArray = []; - if (hashId & 1) keyArray.push("Ctrl"); - if (hashId & 8) keyArray.push("Command"); - if (hashId & 2) keyArray.push("Option"); - if (hashId & 4) keyArray.push("Shift"); + if (hashId & 1) keyArray.push("ctrl"); + if (hashId & 8) keyArray.push("command"); + if (hashId & 2) keyArray.push("option"); + if (hashId & 4) keyArray.push("shift"); if (key) keyArray.push(key); - var symbolicName = keyArray.join("-").toLowerCase(); + var symbolicName = keyArray.join("-"); var bufferToUse = data.buffer + symbolicName; // Don't add the symbolic name to the key buffer if the alt_ key is @@ -194,6 +194,13 @@ StateHandler.prototype = { * This function is called by keyBinding. */ handleKeyboard: function(data, hashId, key) { + // If we pressed any command key but no other key, then ignore the input. + // Otherwise "shift-" is added to the buffer, and later on "shift-g" + // which results in "shift-shift-g" which doesn't make senese. + if (hashId != 0 && (key == "" || String.fromCharCode(0))) { + return null; + } + // Compute the current value of the keyboard input buffer. var r = this.$composeBuffer(data, hashId, key); var buffer = r.bufferToUse; diff --git a/support/pilot b/support/pilot index b3a656ba..af903446 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit b3a656ba07e038bb8f84530c4f56c38f7029929d +Subproject commit af90344687c8486892b44d5decc4c2181df3a6cf From 87ff762270239a605172bd02921afe43295cc74c Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 26 Jan 2011 07:34:46 +0800 Subject: [PATCH 49/60] Pulling in jviereck's fixes for keyboard handling. --- lib/ace/keyboard/state_handler.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/ace/keyboard/state_handler.js b/lib/ace/keyboard/state_handler.js index ab50bc74..5861ad23 100644 --- a/lib/ace/keyboard/state_handler.js +++ b/lib/ace/keyboard/state_handler.js @@ -80,13 +80,13 @@ StateHandler.prototype = { } var keyArray = []; - if (hashId & 1) keyArray.push("Ctrl"); - if (hashId & 8) keyArray.push("Command"); - if (hashId & 2) keyArray.push("Option"); - if (hashId & 4) keyArray.push("Shift"); + if (hashId & 1) keyArray.push("ctrl"); + if (hashId & 8) keyArray.push("command"); + if (hashId & 2) keyArray.push("option"); + if (hashId & 4) keyArray.push("shift"); if (key) keyArray.push(key); - var symbolicName = keyArray.join("-").toLowerCase(); + var symbolicName = keyArray.join("-"); var bufferToUse = data.buffer + symbolicName; // Don't add the symbolic name to the key buffer if the alt_ key is @@ -194,6 +194,13 @@ StateHandler.prototype = { * This function is called by keyBinding. */ handleKeyboard: function(data, hashId, key) { + // If we pressed any command key but no other key, then ignore the input. + // Otherwise "shift-" is added to the buffer, and later on "shift-g" + // which results in "shift-shift-g" which doesn't make senese. + if (hashId != 0 && (key == "" || String.fromCharCode(0))) { + return null; + } + // Compute the current value of the keyboard input buffer. var r = this.$composeBuffer(data, hashId, key); var buffer = r.bufferToUse; From ce9d9f2bf4faf10ab66589f0dc104912a9f85a7d Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 12:13:43 +0100 Subject: [PATCH 50/60] Implementing CLI settings that are shown in the demo editor. Conflicts: support/pilot --- demo/boot.js | 6 +- lib/ace/defaults.js | 51 +++++++++++++++ lib/ace/settings/default-settings.js | 97 ++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 lib/ace/defaults.js create mode 100644 lib/ace/settings/default-settings.js diff --git a/demo/boot.js b/demo/boot.js index e84bfe3f..c5ca3b28 100644 --- a/demo/boot.js +++ b/demo/boot.js @@ -36,7 +36,7 @@ * ***** END LICENSE BLOCK ***** */ var config = { - paths: { + paths: { demo: "../demo", ace: "../lib/ace", cockpit: "../support/cockpit/lib/cockpit", @@ -47,10 +47,12 @@ var config = { var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", "pilot/environment", "demo/startup" ]; +var plugins = [ "pilot/index", "cockpit/index", "ace/defaults" ]; + require(config); require(deps, function() { var catalog = require("pilot/plugin_manager").catalog; - catalog.registerPlugins([ "pilot/index", "cockpit/index" ]).then(function() { + catalog.registerPlugins(plugins).then(function() { var env = require("pilot/environment").create(); catalog.startupPlugins({ env: env }).then(function() { require("demo/startup").launch(env); diff --git a/lib/ace/defaults.js b/lib/ace/defaults.js new file mode 100644 index 00000000..bd217cd1 --- /dev/null +++ b/lib/ace/defaults.js @@ -0,0 +1,51 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (http://jeditoolkit.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var settings = require("ace/settings/default-settings") + +exports.startup = function startup(data, reason) { + settings.startup(data, reason) +} + +exports.shutdown = function shutdown(data, reason) { + settings.shutdown(data, reason) +} + +}) diff --git a/lib/ace/settings/default-settings.js b/lib/ace/settings/default-settings.js new file mode 100644 index 00000000..4e5e0fdf --- /dev/null +++ b/lib/ace/settings/default-settings.js @@ -0,0 +1,97 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (http://jeditoolkit.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var types = require('pilot/types') +var SelectionType = require('pilot/types/basic').SelectionType + +var env + +var settingTypes = { + selectionStyle: new SelectionType({ + data: [ 'line', 'text' ] + }) +} + +var settings = { + printMargin: { + description: 'Position of the print margin column.', + type: 'number', + defaultValue: 80, + onChange: function onChange(event) { + if (env.editor) env.editor.setPrintMarginColumn(event.value) + } + }, + showIvisibles: { + description: 'Whether or not to show invisible characters.', + type: 'bool', + defaultValue: false, + onChange: function onChange(event) { + if (env.editor) env.editor.setShowInvisibles(event.value) + } + }, + highlightActiveLine: { + description: 'Whether or not highlight active line.', + type: 'bool', + defaultValue: true, + onChange: function onChange(event) { + if (env.editor) env.editor.setHighlightActiveLine(event.value) + } + }, + selectionStyle: { + description: 'Type of text selection.', + type: 'selectionStyle', + defaultValue: 'line', + onChange: function onChange(event) { + if (env.editor) env.editor.setSelectionStyle(event.value) + } + } +} + +exports.startup = function startup(data, reason) { + env = data.env + types.registerTypes(settingTypes) + data.env.settings.addSettings(settings) +} + +exports.shutdown = function shutdown(data, reason) { + data.env.settings.removeSettings(settings) +} + +}) From 9b0d7a6443f8f95d3e925e3710a505260e7fa35e Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 12:15:01 +0100 Subject: [PATCH 51/60] update cockpit --- support/cockpit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/cockpit b/support/cockpit index dbc78536..1fa3516d 160000 --- a/support/cockpit +++ b/support/cockpit @@ -1 +1 @@ -Subproject commit dbc78536a4ea61e0f762067fb847213526500d9f +Subproject commit 1fa3516d4d553af9f6edd81c20023bcfab08c2a3 From 8508b43bb559b32754f244ae0ad415ce54d1e65f Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 17:08:13 +0100 Subject: [PATCH 52/60] interpret undefined as false --- Makefile.dryice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index c60702a3..7674c8c7 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -263,7 +263,7 @@ function runFilters(value, filter, reading, name) { return value; } - if (filter.onRead == reading) { + if ((!!filter.onRead) == reading) { return filter(value, name); } else { From 4af6988ece312c11f48270d650d5359eda126446 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 17:08:34 +0100 Subject: [PATCH 53/60] make build system worker compatible --- Makefile.dryice.js | 13 ++++- build_support/boot.js | 76 ++++++++++++------------ build_support/mini_require.js | 105 ++++++++++++++++++---------------- 3 files changed, 105 insertions(+), 89 deletions(-) diff --git a/Makefile.dryice.js b/Makefile.dryice.js index 7674c8c7..74694852 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -91,11 +91,12 @@ copy({ { root: aceHome + '/lib', include: /.*\.js$/, - exclude: /tests?\/|theme\/|mode\// + exclude: /tests?\/|theme\/|mode\/|ace\/worker\/host\.js/ }, { base: aceHome + '/lib/', path: 'ace/theme/textmate.js' }, { base: aceHome + '/lib/', path: 'ace/mode/text.js' }, { base: aceHome + '/lib/', path: 'ace/mode/javascript.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/javascript_worker.js' }, { base: aceHome + '/lib/', path: 'ace/mode/text_highlight_rules.js' }, { base: aceHome + '/lib/', path: 'ace/mode/javascript_highlight_rules.js' }, { base: aceHome + '/lib/', path: 'ace/mode/doc_comment_highlight_rules.js' }, @@ -121,7 +122,6 @@ copy({ source: [ 'build_support/mini_require.js', pilot, - // cockpit, ace, 'build_support/boot.js' ], @@ -146,7 +146,14 @@ copy({ dest: 'build/ace-uncompressed.js' }); - +// Create worker bootstrap code +copy({ + source: "lib/ace/worker/host.js", + filter: [function(data) { + return data + "\nimportScripts('ace-uncompressed.js')"; + }], + dest: 'build/host.js' +}); diff --git a/build_support/boot.js b/build_support/boot.js index f61e91d6..df2ab0ff 100644 --- a/build_support/boot.js +++ b/build_support/boot.js @@ -35,41 +35,45 @@ * * ***** END LICENSE BLOCK ***** */ -var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", - "pilot/environment" ]; - -require(deps, function() { - var catalog = require("pilot/plugin_manager").catalog; - catalog.registerPlugins([ "pilot/index" ]); -}); - -var ace = { - edit: function(el) { - if (typeof(el) == "string") { - el = document.getElementById(el); - } - var env = require("pilot/environment").create(); +// don't define it in a worker. +if (window.document) { + + var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", + "pilot/environment" ]; + + require(deps, function() { var catalog = require("pilot/plugin_manager").catalog; - catalog.startupPlugins({ env: env }).then(function() { - var EditSession = require("ace/edit_session").EditSession; - var JavaScriptMode = require("ace/mode/javascript").Mode; - var UndoManager = require("ace/undomanager").UndoManager; - var Editor = require("ace/editor").Editor; - var Renderer = require("ace/virtual_renderer").VirtualRenderer; - var theme = require("ace/theme/textmate"); - - var doc = new EditSession(el.innerHTML); - el.innerHTML = ''; - doc.setMode(new JavaScriptMode()); - doc.setUndoManager(new UndoManager()); - env.document = doc; - env.editor = new Editor(new Renderer(el, theme)); - env.editor.setSession(doc); - env.editor.resize(); - window.addEventListener("resize", function() { + catalog.registerPlugins([ "pilot/index" ]); + }); + + var ace = { + edit: function(el) { + if (typeof(el) == "string") { + el = document.getElementById(el); + } + var env = require("pilot/environment").create(); + var catalog = require("pilot/plugin_manager").catalog; + catalog.startupPlugins({ env: env }).then(function() { + var EditSession = require("ace/edit_session").EditSession; + var JavaScriptMode = require("ace/mode/javascript").Mode; + var UndoManager = require("ace/undomanager").UndoManager; + var Editor = require("ace/editor").Editor; + var Renderer = require("ace/virtual_renderer").VirtualRenderer; + var theme = require("ace/theme/textmate"); + + var doc = new EditSession(el.innerHTML); + el.innerHTML = ''; + doc.setMode(new JavaScriptMode()); + doc.setUndoManager(new UndoManager()); + env.document = doc; + env.editor = new Editor(new Renderer(el, theme)); + env.editor.setSession(doc); env.editor.resize(); - }, false); - el.env = env; - }); - } -}; + window.addEventListener("resize", function() { + env.editor.resize(); + }, false); + el.env = env; + }); + } + }; +} \ No newline at end of file diff --git a/build_support/mini_require.js b/build_support/mini_require.js index 9500fbc2..25069325 100644 --- a/build_support/mini_require.js +++ b/build_support/mini_require.js @@ -35,59 +35,64 @@ * * ***** END LICENSE BLOCK ***** */ -function require(module, callback) { +// don't define it in a worker. There we have a different implementation +if (window.document) { - if (Array.isArray(module)) { - var params = []; - module.forEach(function(m) { - params.push(require._lookup(m)); - }, this); - - if (callback) { - callback.apply(null, params); + window.require = function(module, callback) { + + if (Array.isArray(module)) { + var params = []; + module.forEach(function(m) { + params.push(require._lookup(m)); + }, this); + + if (callback) { + callback.apply(null, params); + } + } + + if (typeof module === 'string') { + payload = require._lookup(module); + if (callback) { + callback(); + } + return payload; } } - - if (typeof module === 'string') { - payload = require._lookup(module); - if (callback) { - callback(); + require.modules = {}; + require.packaged = true; + + require._lookup = function(moduleName) { + var payload = require.modules[moduleName]; + var module_name = moduleName; + if (payload == null) { + console.error('Missing module: ' + moduleName); + console.trace(); } + + if (typeof payload === 'function') { + var exports = {}; + var module = { + id: moduleName, + uri: '' + }; + payload(require, exports, module); + payload = exports; + // cache the resulting module object for next time + require.modules[module_name] = payload; + } + return payload; + }; + + window.define = function(module, payload) { + if (typeof module !== 'string') { + console.error('dropping module because define wasn\'t munged.'); + console.trace(); + return; + } + + // console.log('defining module: ' + module + ' as a ' + typeof payload); + require.modules[module] = payload; } -} -require.modules = {}; - -require._lookup = function(moduleName) { - var payload = require.modules[moduleName]; - var module_name = moduleName; - if (payload == null) { - console.error('Missing module: ' + moduleName); - console.trace(); - } - - if (typeof payload === 'function') { - var exports = {}; - var module = { - id: moduleName, - uri: '' - }; - payload(require, exports, module); - payload = exports; - // cache the resulting module object for next time - require.modules[module_name] = payload; - } - - return payload; -}; - -function define(module, payload) { - if (typeof module !== 'string') { - console.error('dropping module because define wasn\'t munged.'); - console.trace(); - return; - } - - // console.log('defining module: ' + module + ' as a ' + typeof payload); - require.modules[module] = payload; -} +} \ No newline at end of file From 23a2e368fc5be867f00fb84e18a9c5bbefa609b8 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 17:09:32 +0100 Subject: [PATCH 54/60] make sure the worker also works with the packages version --- lib/ace/worker/host.js | 50 ++++++++++++++++++++++----------- lib/ace/worker/worker_client.js | 18 ++++++++---- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/lib/ace/worker/host.js b/lib/ace/worker/host.js index 7e182a38..cb449ffd 100644 --- a/lib/ace/worker/host.js +++ b/lib/ace/worker/host.js @@ -7,33 +7,48 @@ var window = { console: console }; -var require = function(name) { - if (require.modules[name]) - return require.modules[name].exports; +var require = function(id) { + var module = require.modules[id]; + if (module) { + if (!module.initialized) { + module.exports = module.factory().exports; + module.initialized = true; + } + return module.exports; + } - var chunks = name.split("/"); + var chunks = id.split("/"); chunks[0] = require.tlns[chunks[0]] || chunks[0]; path = require.baseUrl + "/" + chunks.join("/") + ".js" - require.id = name; - importScripts(path); - return require.modules[name].exports; + require.id = id; + importScripts(path); + return require(id); }; + require.modules = {}; require.tlns = {}; require.baseUrl; -var define = function(factory) { - var module = { - exports: {} +var define = function(id, factory) { + if (!factory) { + factory = id; + id = require.id; + } + if (id.indexOf("text!") == 0) + return; + + require.modules[id] = { + factory: function() { + var module = { + exports: {} + }; + var returnExports = factory(require, module.exports, module); + if (returnExports) + module.exports = exports; + return module; + } }; - - var name = require.id; - var returnExports = factory(require, module.exports, module); - if (returnExports) - module.exports = exports; - - require.modules[name] = module; }; function initBaseUrls(baseUrl, topLevelNamespaces) { @@ -81,6 +96,7 @@ onmessage = function(e) { main[msg.command].apply(main, msg.args); else if (msg.init) { initBaseUrls(msg.base, msg.tlns); + require("pilot/fixoldbrowsers"); sender = initSender(); var clazz = require(msg.module)[msg.classname]; main = new clazz(sender); diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 71d6505e..5e8e3c28 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -14,13 +14,19 @@ var EventEmitter = require("pilot/event_emitter").EventEmitter; var WorkerClient = function(baseUrl, topLevelNamespaces, module, classname) { this.callbacks = []; - var workerUrl = require.nameToUrl("ace/worker/host", null, "_"); - var worker = this.$worker = new Worker(workerUrl); - var tlns = {}; - for (var i=0; i Date: Wed, 26 Jan 2011 17:15:53 +0100 Subject: [PATCH 55/60] focus immediately and in a timeout --- lib/ace/editor.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index efa1cb9a..6be9b875 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -221,6 +221,13 @@ var Editor =function(renderer, session) { }; this.focus = function() { + // Safari need the timeout + // iOS and Firefox need it called immediately + // to be on the save side we do both + var _self = this; + setTimeout(function() { + _self.textInput.focus(); + }); this.textInput.focus(); }; From c14c061a7b644ff8702c1b3172ab4c53bb81cd3f Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 17:19:34 +0100 Subject: [PATCH 56/60] embed icons in the CSS --- demo/styles.css | 12 ------------ lib/ace/css/editor.css | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/demo/styles.css b/demo/styles.css index ff6a749f..ce59595c 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -21,18 +21,6 @@ body { background: white; } -.ace_gutter-cell.ace_error { - background-image: url("icons/error_obj.gif"); - background-repeat: no-repeat; - background-position: 4px center; -} - -.ace_gutter-cell.ace_warning { - background-image: url("icons/warning_obj.gif"); - background-repeat: no-repeat; - background-position: 4px center; -} - #controls { width: 100%; height: 55px; diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index f59da076..b7d6eb23 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -33,6 +33,18 @@ height: 100%; } +.ace_gutter-cell.ace_error { + background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B"); + background-repeat: no-repeat; + background-position: 4px center; +} + +.ace_gutter-cell.ace_warning { + background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B"); + background-repeat: no-repeat; + background-position: 4px center; +} + .ace_editor .ace_sb { position: absolute; overflow-x: hidden; From 838db65610156e6700359bf626e13cb810c18a99 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 26 Jan 2011 18:01:40 +0100 Subject: [PATCH 57/60] Extending editor and twilight theme to allow styling of cursor form a vim plugin or any other that needs to visually indicate it's mode. --- lib/ace/css/editor.css | 1 + lib/ace/editor.js | 12 +++++++++++- lib/ace/theme/twilight.js | 4 ++++ lib/ace/virtual_renderer.js | 15 ++++++++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index f59da076..e610ca44 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -81,6 +81,7 @@ } .ace_cursor-layer { + z-index: 0; cursor: text; pointer-events: none; } diff --git a/lib/ace/editor.js b/lib/ace/editor.js index efa1cb9a..62b53138 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -20,6 +21,7 @@ * * Contributor(s): * Fabian Jakobs + * Irakli Gozalishvili (http://jeditoolkit.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -196,6 +198,14 @@ var Editor =function(renderer, session) { this.renderer.setTheme(theme); }; + this.setStyle = function(style) { + this.renderer.setStyle(style) + }; + + this.unsetStyle = function(style) { + this.renderer.unsetStyle(style) + } + this.$highlightBrackets = function() { if (this.$bracketHighlight) { this.renderer.removeMarker(this.$bracketHighlight); diff --git a/lib/ace/theme/twilight.js b/lib/ace/theme/twilight.js index 36fb8953..600ba519 100644 --- a/lib/ace/theme/twilight.js +++ b/lib/ace/theme/twilight.js @@ -85,6 +85,10 @@ define(function(require, exports, module) { .ace-twilight .ace_cursor.ace_overwrite {\ border-left: 0px;\ border-bottom: 1px solid #A7A7A7;\ +}\ +.ace-twilight.normal-mode .ace_cursor.ace_overwrite {\ + border: 1px solid #FFE300;\ + background: #766B13;\ }\ \ .ace-twilight .ace_marker-layer .ace_selection {\ diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index edcaa3d9..47444ee4 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -658,6 +659,18 @@ var VirtualRenderer = function(container, theme) { } }; + // Methods allows to add / remove CSS classnames to the editor element. + // This feature can be used by plug-ins to provide a visual indication of + // a certain mode that editor is in. + + this.setStyle = function setStyle(style) { + dom.addCssClass(this.container, style) + }; + + this.unsetStyle = function unsetStyle(style) { + dom.removeCssClass(this.container, style) + }; + }).call(VirtualRenderer.prototype); exports.VirtualRenderer = VirtualRenderer; From ffada5c7b2f97240e890e4c6a608a26df1fcd5d1 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Wed, 26 Jan 2011 19:48:00 +0100 Subject: [PATCH 58/60] make cursor visible again --- lib/ace/css/editor.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index f7250101..5b545728 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -93,7 +93,7 @@ } .ace_cursor-layer { - z-index: 0; + z-index: 4; cursor: text; pointer-events: none; } From 125539052ace7e934abdd280068fb4d9937fab9c Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 26 Jan 2011 19:13:54 +0800 Subject: [PATCH 59/60] setShowGutter must update width --- lib/ace/virtual_renderer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 47444ee4..134fe487 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -260,9 +260,14 @@ var VirtualRenderer = function(container, theme) { }; this.setShowGutter = function(show){ + if(this.showGutter === show) + return; this.$gutter.style.display = show ? "block" : "none"; this.showGutter = show; + // set fake width to make onResize work + this.$size.width = -1 this.onResize(); + this.$gutterLayer.update(this.layerConfig) } this.$updatePrintMargin = function() { From 6b0087c234ca5adfd947cad2b12ab78ad94270d9 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 26 Jan 2011 19:15:37 +0800 Subject: [PATCH 60/60] add toggle gutter to demo --- demo/startup.js | 10 ++++++++++ demo/styles.css | 16 ++++++++++++++++ editor.html | 4 +++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/demo/startup.js b/demo/startup.js index 1403fd1b..1b5a958f 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -277,6 +277,16 @@ exports.launch = function(env) { return event.preventDefault(e); }); + + // gutter + editor = env.editor + toggleGutter=function(){ + editor.renderer.setShowGutter(!editor.renderer.showGutter) + } + // + window.ace={ + editor: editor + } }; }); diff --git a/demo/styles.css b/demo/styles.css index ce59595c..325e6e82 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -53,4 +53,20 @@ body { -moz-border-radius-topright: 10px; border-top-left-radius: 4px; border-top-right-radius: 4px; background: #DDD; color: #000; +} + +#toggleGutter{ + height:8px; + width:20px; + background:lightblue; + bottom:0; + position:absolute; + z-index: 1000; + -moz-border-radius-topright: 20px; +} + +#toggleGutter:hover{ + -moz-box-shadow: 2px -1px 5px 3px #91B6D5; + border-right: 1px solid #A1A1CF; + border-top: 1px solid #A1A1CF; } \ No newline at end of file diff --git a/editor.html b/editor.html index 30e67abb..9849cbcb 100644 --- a/editor.html +++ b/editor.html @@ -64,8 +64,10 @@ -
+
+
+