diff --git a/Readme.md b/Readme.md index 63d028da..5e9e3107 100644 --- a/Readme.md +++ b/Readme.md @@ -163,6 +163,6 @@ Continuous Integration status ----------------------------- This project is tested with [Travis CI](http://travis-ci.org) -[![Build Status](https://secure.travis-ci.org/ajaxorg/ace.png)](http://travis-ci.org/ajaxorg/ace) +[![Build Status](https://secure.travis-ci.org/ajaxorg/ace.png?branch=master)](http://travis-ci.org/ajaxorg/ace) diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index ce5d57ad..93859a61 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -46,8 +46,6 @@ var theme = require("ace/theme/textmate"); var EditSession = require("ace/edit_session").EditSession; var UndoManager = require("ace/undomanager").UndoManager; -var vim = require("ace/keyboard/vim").handler; -var emacs = require("ace/keyboard/emacs").handler; var HashHandler = require("ace/keyboard/hash_handler").HashHandler; var Renderer = require("ace/virtual_renderer").VirtualRenderer; @@ -162,11 +160,10 @@ commands.addCommand({ exec: function() {alert("Fake Save File");} }); -var keybindings = { - // Null = use "default" keymapping - ace: null, - vim: vim, - emacs: emacs, +var keybindings = { + ace: null, // Null = use "default" keymapping + vim: require("ace/keyboard/vim").handler, + emacs: "ace/keyboard/emacs", // This is a way to define simple keyboard remappings custom: new HashHandler({ "gotoright": "Tab", diff --git a/kitchen-sink.html b/kitchen-sink.html index ac241fa1..858514f9 100644 --- a/kitchen-sink.html +++ b/kitchen-sink.html @@ -13,13 +13,13 @@ - + @@ -280,4 +280,4 @@ PACKAGE--> - \ No newline at end of file + diff --git a/lib/ace/config.js b/lib/ace/config.js index 5a1b0e47..d2d2aea7 100644 --- a/lib/ace/config.js +++ b/lib/ace/config.js @@ -32,6 +32,8 @@ define(function(require, exports, module) { "no use strict"; var lang = require("./lib/lang"); +var oop = require("./lib/oop"); +var EventEmitter = require("./lib/event_emitter").EventEmitter; var global = (function() { return this; @@ -65,6 +67,9 @@ exports.all = function() { return lang.copyObject(options); }; +// module loading +oop.implement(exports, EventEmitter); + exports.moduleUrl = function(name, component) { if (options.$moduleUrls[name]) return options.$moduleUrls[name]; @@ -87,6 +92,32 @@ exports.setModuleUrl = function(name, subst) { return options.$moduleUrls[name] = subst; }; +exports.loadModule = function(moduleName, onLoad) { + var module, moduleType; + if (Array.isArray(moduleName)) { + moduleType = moduleName[0]; + moduleName = moduleName[1]; + } + try { + module = require(moduleName); + } catch (e) {}; + if (module) + return onLoad(module); + + var afterLoad = function() { + require([moduleName], function(module) { + exports._emit("load.module", {name: moduleName, module: module}); + onLoad(module); + }); + }; + + if (!exports.get("packaged")) + return afterLoad(); + net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); +}; + + +// initialization exports.init = function() { options.packaged = require.packaged || module.packaged || (global.define && define.packaged); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index ae16984b..bf0c0a51 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -898,69 +898,6 @@ var EditSession = function(text, mode) { }; this.$modes = {}; - this._loadMode = function(mode, callback) { - if (!this.$modes["null"]) - this.$modes["null"] = this.$modes["ace/mode/text"] = new TextMode(); - - if (this.$modes[mode]) - return callback(this.$modes[mode]); - - var _self = this; - var module; - try { - module = require(mode); - } catch (e) {}; - // sometimes require returns empty object (this bug is present in requirejs 2 as well) - if (module && module.Mode) - return done(module); - - // set mode to text until loading is finished - if (!this.$mode) - this.$setModePlaceholder(); - - fetch(mode, function() { - require([mode], done); - }); - - function done(module) { - if (_self.$modes[mode]) - return callback(_self.$modes[mode]); - - _self.$modes[mode] = new module.Mode(); - _self.$modes[mode].$id = mode; - _self._emit("loadmode", { - name: mode, - mode: _self.$modes[mode] - }); - callback(_self.$modes[mode]); - } - - function fetch(name, callback) { - if (!config.get("packaged")) - return callback(); - - net.loadScript(config.moduleUrl(name, "mode"), callback); - } - }; - - this.$setModePlaceholder = function() { - this.$mode = this.$modes["null"]; - var tokenizer = this.$mode.getTokenizer(); - - if (!this.bgTokenizer) { - this.bgTokenizer = new BackgroundTokenizer(tokenizer); - var _self = this; - this.bgTokenizer.addEventListener("update", function(e) { - _self._emit("tokenizerUpdate", e); - }); - } else { - this.bgTokenizer.setTokenizer(tokenizer); - } - this.bgTokenizer.setDocument(this.getDocument()); - - this.tokenRe = this.$mode.tokenRe; - this.nonTokenRe = this.$mode.nonTokenRe; - }; /** * Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted. @@ -970,23 +907,45 @@ var EditSession = function(text, mode) { this.$mode = null; this.$modeId = null; this.setMode = function(mode) { - mode = mode || "null"; - // load on demand - if (typeof mode === "string") { - if (this.$modeId == mode) - return; - - this.$modeId = mode; - var _self = this; - this._loadMode(mode, function(module) { - if (_self.$modeId !== mode) - return; - - _self.setMode(module); - }); - return; + if (mode && typeof mode === "object") { + if (mode.getTokenizer) + return this.$onChangeMode(mode); + var options = mode; + var path = options.path; + } else { + path = mode || "ace/mode/text"; } + // this is needed if ace isn't on require path (e.g tests in node) + if (!this.$modes["ace/mode/text"]) + this.$modes["ace/mode/text"] = new TextMode(); + + if (this.$modes[path] && !options) + return this.$onChangeMode(this.$modes[path]); + + // load on demand + this.$modeId = path; + config.loadModule(["mode", path], function(m) { + if (this.$modeId !== path) + return; + if (this.$modes[path] && !options) + return this.$onChangeMode(this.$modes[path]); + if (m && m.Mode) { + m = new m.Mode(options); + if (!options) { + this.$modes[path] = m; + m.$id = path; + } + this.$onChangeMode(m) + } + }.bind(this)); + + // set mode to text until loading is finished + if (!this.$mode) + this.$onChangeMode(this.$modes["ace/mode/text"], true); + }; + + this.$onChangeMode = function(mode, $isPlaceholder) { if (this.$mode === mode) return; this.$mode = mode; this.$modeId = mode.$id; @@ -1014,14 +973,16 @@ var EditSession = function(text, mode) { } this.bgTokenizer.setDocument(this.getDocument()); - this.bgTokenizer.start(0); this.tokenRe = mode.tokenRe; this.nonTokenRe = mode.nonTokenRe; - this.$setFolding(mode.foldingRules); - this._emit("changeMode"); + if (!$isPlaceholder) { + this.$setFolding(mode.foldingRules); + this._emit("changeMode"); + this.bgTokenizer.start(0); + } }; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 865de51f..44bfb36a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -46,6 +46,7 @@ var Range = require("./range").Range; var EventEmitter = require("./lib/event_emitter").EventEmitter; var CommandManager = require("./commands/command_manager").CommandManager; var defaultCommands = require("./commands/default_commands").commands; +var config = require("./config"); /** * @@ -100,7 +101,17 @@ var Editor = function(renderer, session) { * **/ this.setKeyboardHandler = function(keyboardHandler) { - this.keyBinding.setKeyboardHandler(keyboardHandler); + if (typeof keyboardHandler == "string" && keyboardHandler) { + this.$keybindingId = keyboardHandler; + var _self = this; + config.loadModule(["keybinding", keyboardHandler], function(module) { + if (_self.$keybindingId == keyboardHandler) + _self.keyBinding.setKeyboardHandler(module && module.handler); + }); + } else { + delete this.$keybindingId; + this.keyBinding.setKeyboardHandler(keyboardHandler); + } }; /** @@ -2149,4 +2160,4 @@ var Editor = function(renderer, session) { exports.Editor = Editor; -}); \ No newline at end of file +}); diff --git a/lib/ace/ext/textarea.js b/lib/ace/ext/textarea.js index a8f6317f..a36db850 100644 --- a/lib/ace/ext/textarea.js +++ b/lib/ace/ext/textarea.js @@ -324,6 +324,19 @@ function setupApi(editor, editorDiv, settingDiv, ace, options, loader) { editorDiv.style.fontSize = value; break; + case "keybindings": + switch (value) { + case "vim": + editor.setKeyboardHandler("ace/keyboard/vim"); + break; + case "emacs": + editor.setKeyboardHandler("ace/keyboard/emacs"); + break; + default: + editor.setKeyboardHandler(null); + } + break; + case "softWrap": switch (value) { case "off": @@ -391,6 +404,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) { theme: "Theme:", fontSize: "Font Size:", softWrap: "Soft Wrap:", + keybindings: "Keyboard", showPrintMargin: "Show Print Margin:", useSoftTabs: "Use Soft Tabs:", showInvisibles: "Show Invisibles" @@ -456,6 +470,11 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) { 80: "80", free: "Free" }, + keybindings: { + ace: "ace", + vim: "vim", + emacs: "emacs" + }, showPrintMargin: BOOL, useSoftTabs: BOOL, showInvisibles: BOOL @@ -518,6 +537,7 @@ exports.options = { gutter: "false", fontSize: "12px", softWrap: "off", + keybindings: "ace", showPrintMargin: "false", useSoftTabs: "true", showInvisibles: "true" diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js index 8fc3aeeb..2952b80a 100644 --- a/lib/ace/keyboard/emacs.js +++ b/lib/ace/keyboard/emacs.js @@ -33,14 +33,14 @@ define(function(require, exports, module) { var dom = require("../lib/dom"); -var screenToTextBlockCoordinates = function(pageX, pageY) { +var screenToTextBlockCoordinates = function(x, y) { var canvasPos = this.scroller.getBoundingClientRect(); var col = Math.floor( - (pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth + (x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth ); var row = Math.floor( - (pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight + (y + this.scrollTop - canvasPos.top) / this.lineHeight ); return this.session.screenToDocumentPosition(row, col); diff --git a/lib/ace/mode/php.js b/lib/ace/mode/php.js index a73f3e84..6312840d 100644 --- a/lib/ace/mode/php.js +++ b/lib/ace/mode/php.js @@ -35,14 +35,17 @@ var oop = require("../lib/oop"); var TextMode = require("./text").Mode; var Tokenizer = require("../tokenizer").Tokenizer; var PhpHighlightRules = require("./php_highlight_rules").PhpHighlightRules; +var PhpLangHighlightRules = require("./php_highlight_rules").PhpLangHighlightRules; var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent; var Range = require("../range").Range; var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; var CStyleFoldMode = require("./folding/cstyle").FoldMode; var unicode = require("../unicode"); -var Mode = function() { - this.$tokenizer = new Tokenizer(new PhpHighlightRules().getRules()); +var Mode = function(opts) { + var inline = opts && opts.inline; + var HighlightRules = inline ? PhpLangHighlightRules : PhpHighlightRules; + this.$tokenizer = new Tokenizer(new HighlightRules().getRules()); this.$outdent = new MatchingBraceOutdent(); this.$behaviour = new CstyleBehaviour(); this.foldingRules = new CStyleFoldMode(); diff --git a/lib/ace/mode/php_highlight_rules.js b/lib/ace/mode/php_highlight_rules.js index dc08198d..eac9ecea 100644 --- a/lib/ace/mode/php_highlight_rules.js +++ b/lib/ace/mode/php_highlight_rules.js @@ -1062,4 +1062,5 @@ var PhpHighlightRules = function() { oop.inherits(PhpHighlightRules, HtmlHighlightRules); exports.PhpHighlightRules = PhpHighlightRules; +exports.PhpLangHighlightRules = PhpLangHighlightRules; }); diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 4661768b..8f169def 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -69,16 +69,16 @@ dom.importCssString(editorCss, "ace_editor"); var VirtualRenderer = function(container, theme) { var _self = this; - this.container = container; + this.container = container || dom.createElement("div"); // TODO: this breaks rendering in Cloud9 with multiple ace instances -// // Imports CSS once per DOM document ('ace_editor' serves as an identifier). -// dom.importCssString(editorCss, "ace_editor", container.ownerDocument); + // // Imports CSS once per DOM document ('ace_editor' serves as an identifier). + // dom.importCssString(editorCss, "ace_editor", container.ownerDocument); // in IE <= 9 the native cursor always shines through this.$keepTextAreaAtCursor = !useragent.isIE; - dom.addCssClass(container, "ace_editor"); + dom.addCssClass(this.container, "ace_editor"); this.setTheme(theme); @@ -113,7 +113,7 @@ var VirtualRenderer = function(container, theme) { this.$animatedScroll = false; - this.scrollBar = new ScrollBar(container); + this.scrollBar = new ScrollBar(this.container); this.scrollBar.addEventListener("scroll", function(e) { if (!_self.$inScrollAnimation) _self.session.setScrollTop(e.data); @@ -1278,10 +1278,7 @@ var VirtualRenderer = function(container, theme) { }; this._loadTheme = function(name, callback) { - if (!config.get("packaged")) - return callback(); - - net.loadScript(config.moduleUrl(name, "theme"), callback); + }; /** @@ -1292,28 +1289,12 @@ var VirtualRenderer = function(container, theme) { **/ this.setTheme = function(theme) { var _self = this; - this.$themeValue = theme; _self._dispatchEvent('themeChange',{theme:theme}); if (!theme || typeof theme == "string") { var moduleName = theme || "ace/theme/textmate"; - - var module; - try { - module = require(moduleName); - } catch (e) {}; - if (module) - return afterLoad(module); - - _self._loadTheme(moduleName, function() { - require([moduleName], function(module) { - if (_self.$themeValue !== theme) - return; - - afterLoad(module); - }); - }); + config.loadModule(["theme", moduleName], afterLoad); } else { afterLoad(theme); }