diff options
Diffstat (limited to 'apps/files_texteditor/js/aceeditor/ace-uncompressed.js')
-rwxr-xr-x | apps/files_texteditor/js/aceeditor/ace-uncompressed.js | 3210 |
1 files changed, 2810 insertions, 400 deletions
diff --git a/apps/files_texteditor/js/aceeditor/ace-uncompressed.js b/apps/files_texteditor/js/aceeditor/ace-uncompressed.js index 619baf49016..74bb44b16d8 100755 --- a/apps/files_texteditor/js/aceeditor/ace-uncompressed.js +++ b/apps/files_texteditor/js/aceeditor/ace-uncompressed.js @@ -40,7 +40,7 @@ * @param module a name for the payload * @param payload a function to call with (require, exports, module) params */ - + (function() { var ACE_NAMESPACE = ""; @@ -49,9 +49,6 @@ var global = (function() { return this; })(); -if (typeof requirejs !== "undefined") - return; - var _define = function(module, deps, payload) { if (typeof module !== 'string') { if (_define.original) @@ -68,7 +65,7 @@ var _define = function(module, deps, payload) { if (!_define.modules) _define.modules = {}; - + _define.modules[module] = payload; }; @@ -92,11 +89,11 @@ var _require = function(parentId, module, callback) { var payload = lookup(parentId, module); if (!payload && _require.original) return _require.original.apply(window, arguments); - + if (callback) { callback(); } - + return payload; } else { @@ -115,13 +112,13 @@ var normalizeModule = function(parentId, moduleName) { if (moduleName.charAt(0) == ".") { var base = parentId.split("/").slice(0, -1).join("/"); moduleName = base + "/" + moduleName; - + while(moduleName.indexOf(".") !== -1 && previous != moduleName) { var previous = moduleName; moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); } } - + return moduleName; }; @@ -141,19 +138,19 @@ var lookup = function(parentId, moduleName) { if (typeof module === 'function') { var exports = {}; var mod = { - id: moduleName, + id: moduleName, uri: '', exports: exports, packaged: true }; - + var req = function(module, callback) { return _require(moduleName, module, callback); }; - + var returnValue = module(req, exports, mod); exports = returnValue || mod.exports; - + // cache the resulting module object for next time _define.modules[moduleName] = exports; return exports; @@ -163,26 +160,45 @@ var lookup = function(parentId, moduleName) { }; function exportAce(ns) { + + if (typeof requirejs !== "undefined") { + + var define = global.define; + global.define = function(id, deps, callback) { + if (typeof callback !== "function") + return define.apply(this, arguments); + + return define(id, deps, function(require, exports, module) { + if (deps[2] == "module") + module.packaged = true; + return callback.apply(this, arguments); + }); + }; + global.define.packaged = true; + + return; + } + var require = function(module, callback) { return _require("", module, callback); }; require.packaged = true; - + var root = global; if (ns) { if (!global[ns]) global[ns] = {}; root = global[ns]; } - + if (root.define) _define.original = root.define; - + root.define = _define; if (root.require) _require.original = root.require; - + root.require = require; } @@ -225,7 +241,7 @@ exportAce(ACE_NAMESPACE); * * ***** END LICENSE BLOCK ***** */ -define('ace/ace', ['require', 'exports', 'module' , 'ace/lib/fixoldbrowsers', 'ace/lib/dom', 'ace/lib/event', 'ace/editor', 'ace/edit_session', 'ace/undomanager', 'ace/virtual_renderer', 'ace/theme/textmate'], function(require, exports, module) { +define('ace/ace', ['require', 'exports', 'module' , 'ace/lib/fixoldbrowsers', 'ace/lib/dom', 'ace/lib/event', 'ace/editor', 'ace/edit_session', 'ace/undomanager', 'ace/virtual_renderer', 'ace/multi_select', 'ace/worker/worker_client', 'ace/keyboard/hash_handler', 'ace/keyboard/state_handler', 'ace/placeholder', 'ace/config', 'ace/theme/textmate'], function(require, exports, module) { "use strict"; require("./lib/fixoldbrowsers"); @@ -237,6 +253,14 @@ var Editor = require("./editor").Editor; var EditSession = require("./edit_session").EditSession; var UndoManager = require("./undomanager").UndoManager; var Renderer = require("./virtual_renderer").VirtualRenderer; +var MultiSelect = require("./multi_select").MultiSelect; + +// The following require()s are for inclusion in the built ace file +require("./worker/worker_client"); +require("./keyboard/hash_handler"); +require("./keyboard/state_handler"); +require("./placeholder"); +require("./config").init(); exports.edit = function(el) { if (typeof(el) == "string") { @@ -248,6 +272,7 @@ exports.edit = function(el) { el.innerHTML = ''; var editor = new Editor(new Renderer(el, require("./theme/textmate"))); + new MultiSelect(editor); editor.setSession(doc); var env = {}; @@ -326,7 +351,7 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex RegExp.prototype.exec = function (str) { var match = real.exec.apply(this, arguments), name, r2; - if (match) { + if ( typeof(str) == 'string' && match) { // Fix browsers whose `exec` methods don't consistently return `undefined` for // nonparticipating capturing groups if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { @@ -391,7 +416,8 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex return -1; }; -});// vim: ts=4 sts=4 sw=4 expandtab +}); +// vim: ts=4 sts=4 sw=4 expandtab // -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project) // -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA @@ -1573,7 +1599,7 @@ exports.hasCssString = function(id, doc) { if (doc.createStyleSheet && (sheets = doc.styleSheets)) { while (index < sheets.length) - if (sheets[index++].title === id) return true; + if (sheets[index++].owningElement.id === id) return true; } else if ((sheets = doc.getElementsByTagName("style"))) { while (index < sheets.length) if (sheets[index++].id === id) return true; @@ -1594,7 +1620,7 @@ exports.importCssString = function importCssString(cssText, id, doc) { style = doc.createStyleSheet(); style.cssText = cssText; if (id) - style.title = id; + style.owningElement.id = id; } else { style = doc.createElementNS ? doc.createElementNS(XHTML_NS, "style") @@ -1740,8 +1766,7 @@ exports.getParentWindow = function(document) { return document.defaultView || document.parentWindow; }; -}); -/* ***** BEGIN LICENSE BLOCK ***** +});/* ***** 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 @@ -1917,20 +1942,9 @@ else { } exports.addMouseWheelListener = function(el, callback) { - var max = 0; + var factor = 8; var listener = function(e) { if (e.wheelDelta !== undefined) { - - // some versions of Safari (e.g. 5.0.5) report insanely high - // scroll values. These browsers require a higher factor - if (Math.abs(e.wheelDeltaY) > max) - max = Math.abs(e.wheelDeltaY); - - if (max > 5000) - var factor = 400; - else - var factor = 8; - if (e.wheelDeltaX !== undefined) { e.wheelX = -e.wheelDeltaX / factor; e.wheelY = -e.wheelDeltaY / factor; @@ -2029,7 +2043,7 @@ function normalizeCommandKeys(callback, e, keyCode) { exports.addCommandKeyListener = function(el, callback) { var addListener = exports.addListener; - if (useragent.isOldGecko) { + if (useragent.isOldGecko || useragent.isOpera) { // Old versions of Gecko aka. Firefox < 4.0 didn't repeat the keydown // event if the user pressed the key for a longer time. Instead, the // keydown event was fired once and later on only the keypress event. @@ -2050,18 +2064,6 @@ exports.addCommandKeyListener = function(el, callback) { lastDown = e.keyIdentifier || e.keyCode; return normalizeCommandKeys(callback, e, e.keyCode); }); - - // repeated keys are fired as keypress and not keydown events - if (useragent.isMac && useragent.isOpera) { - addListener(el, "keypress", function(e) { - var keyId = e.keyIdentifier || e.keyCode; - if (lastDown !== keyId) { - return normalizeCommandKeys(callback, e, lastDown); - } else { - lastDown = null; - } - }); - } } }; @@ -2446,7 +2448,7 @@ var Editor = function(renderer, session) { var container = renderer.getContainerElement(); this.container = container; this.renderer = renderer; - + this.textInput = new TextInput(renderer.getTextAreaContainer(), this); this.keyBinding = new KeyBinding(this); @@ -2558,7 +2560,10 @@ var Editor = function(renderer, session) { this.onChangeMode(); + this.$blockScrolling += 1; this.onCursorChange(); + this.$blockScrolling -= 1; + this.onScrollTopChange(); this.onScrollLeftChange(); this.onSelectionChange(); @@ -2605,6 +2610,7 @@ var Editor = function(renderer, session) { this.setFontSize = function(size) { this.container.style.fontSize = size; + this.renderer.updateFontSize(); }; this.$highlightBrackets = function() { @@ -2710,21 +2716,29 @@ var Editor = function(renderer, session) { this.$updateHighlightActiveLine = function() { var session = this.getSession(); - if (session.$highlightLineMarker) { + if (session.$highlightLineMarker) session.removeMarker(session.$highlightLineMarker); - } + if (typeof this.$lastrow == "number") + this.renderer.removeGutterDecoration(this.$lastrow, "ace_gutter_active_line"); + session.$highlightLineMarker = null; + this.$lastrow = null; - if (this.getHighlightActiveLine() && (this.getSelectionStyle() != "line" || !this.selection.isMultiLine())) { + if (this.getHighlightActiveLine()) { var cursor = this.getCursorPosition(), foldLine = this.session.getFoldLine(cursor.row); - var range; - if (foldLine) { - range = new Range(foldLine.start.row, 0, foldLine.end.row + 1, 0); - } else { - range = new Range(cursor.row, 0, cursor.row+1, 0); + + if ((this.getSelectionStyle() != "line" || !this.selection.isMultiLine())) { + var range; + if (foldLine) { + range = new Range(foldLine.start.row, 0, foldLine.end.row + 1, 0); + } else { + range = new Range(cursor.row, 0, cursor.row+1, 0); + } + session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "background"); } - session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "background"); + + this.renderer.addGutterDecoration(this.$lastrow = cursor.row, "ace_gutter_active_line"); } }; @@ -2794,16 +2808,7 @@ var Editor = function(renderer, session) { }; this.onCut = function() { - if (this.$readOnly) - return; - - var range = this.getSelectionRange(); - this._emit("cut", range); - - if (!this.selection.isEmpty()) { - this.session.remove(range); - this.clearSelection(); - } + this.commands.exec("cut", this); }; this.insert = function(text) { @@ -2978,6 +2983,14 @@ var Editor = function(renderer, session) { return this.$highlightSelectedWord; }; + this.setAnimatedScroll = function(shouldAnimate){ + this.renderer.setAnimatedScroll(shouldAnimate); + }; + + this.getAnimatedScroll = function(){ + return this.renderer.getAnimatedScroll(); + }; + this.setShowInvisibles = function(showInvisibles) { if (this.getShowInvisibles() == showInvisibles) return; @@ -3032,7 +3045,7 @@ var Editor = function(renderer, session) { this.$showFoldWidgets = show; this.renderer.updateFull(); }; - + this.getShowFoldWidgets = function() { return this.renderer.$gutterLayer.getShowFoldWidgets(); }; @@ -3249,7 +3262,7 @@ var Editor = function(renderer, session) { range.start.row += linesMoved; range.end.row += linesMoved; selection.setSelectionRange(range, reverse); - } + } else { selection.setSelectionAnchor(rows.last+linesMoved+1, 0); selection.$moveSelection(function() { @@ -3413,13 +3426,13 @@ var Editor = function(renderer, session) { cursor.column -= 2; pos = this.session.findMatchingBracket(cursor); } - + if (pos) { this.clearSelection(); this.moveCursorTo(pos.row, pos.column); } }; - + this.gotoLine = function(lineNumber, column) { this.selection.clearSelection(); this.session.unfold({row: lineNumber - 1, column: column || 0}); @@ -3511,12 +3524,19 @@ var Editor = function(renderer, session) { this.$search.set(options); var range = this.$search.find(this.session); + var replaced = 0; if (!range) - return; + return replaced; - this.$tryReplace(range, replacement); - if (range !== null) + if (this.$tryReplace(range, replacement)) { + replaced = 1; + } + if (range !== null) { this.selection.setSelectionRange(range); + this.renderer.scrollSelectionIntoView(range.start, range.end); + } + + return replaced; }; this.replaceAll = function(replacement, options) { @@ -3525,19 +3545,25 @@ var Editor = function(renderer, session) { } var ranges = this.$search.findAll(this.session); + var replaced = 0; if (!ranges.length) - return; + return replaced; var selection = this.getSelectionRange(); this.clearSelection(); this.selection.moveCursorTo(0, 0); this.$blockScrolling += 1; - for (var i = ranges.length - 1; i >= 0; --i) - this.$tryReplace(ranges[i], replacement); + for (var i = ranges.length - 1; i >= 0; --i) { + if(this.$tryReplace(ranges[i], replacement)) { + replaced++; + } + } this.selection.setSelectionRange(selection); this.$blockScrolling -= 1; + + return replaced; }; this.$tryReplace = function(range, replacement) { @@ -3589,8 +3615,23 @@ var Editor = function(renderer, session) { var range = this.$search.find(this.session); if (range) { this.session.unfold(range); - this.gotoLine(range.end.row+1, range.end.column); + + this.$blockScrolling += 1; this.selection.setSelectionRange(range); + this.$blockScrolling -= 1; + + if (this.getAnimatedScroll()) { + var cursor = this.getCursorPosition(); + if (!this.isRowFullyVisible(cursor.row)) + this.scrollToLine(cursor.row, true); + + //@todo scroll X + //if (!this.isColumnFullyVisible(cursor.column)) + //this.scrollToRow(cursor.column); + } + else { + this.renderer.scrollSelectionIntoView(range.start, range.end); + } } }; @@ -4298,7 +4339,7 @@ function DefaultHandlers(editor) { if (!editor.$mouseHandler.$clickSelection) { if (!dragCursor) { editor.moveCursorToPosition(pos); - editor.selection.clearSelection(pos.row, pos.column); + editor.selection.clearSelection(); } } @@ -4333,7 +4374,6 @@ function DefaultHandlers(editor) { if (distance > DRAG_OFFSET) { state = STATE_SELECT; var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY); - cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1)); onStartSelect(cursor); } else if ((time - mousedownTime) > editor.getDragDelay()) { @@ -4360,7 +4400,7 @@ function DefaultHandlers(editor) { else { if (!_self.$clickSelection) { editor.moveCursorToPosition(pos); - editor.selection.clearSelection(pos.row, pos.column); + editor.selection.clearSelection(); } } state = STATE_SELECT; @@ -4369,7 +4409,6 @@ function DefaultHandlers(editor) { var onUpdateSelectionInterval = function() { var anchor; var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY); - cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1)); if (_self.$clickSelection) { if (_self.$clickSelection.contains(cursor.row, cursor.column)) { @@ -4395,8 +4434,6 @@ function DefaultHandlers(editor) { var onDragSelectionInterval = function() { dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY); - dragCursor.row = Math.max(0, Math.min(dragCursor.row, editor.session.getLength() - 1)); - editor.moveCursorToPosition(dragCursor); }; @@ -4818,7 +4855,6 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) { var pageX = event.getDocumentX(this.domEvent); var pageY = event.getDocumentY(this.domEvent); this.$pos = this.editor.renderer.screenToTextCoordinates(pageX, pageY); - this.$pos.row = Math.max(0, Math.min(this.$pos.row, this.editor.session.getLength()-1)); return this.$pos; }; @@ -4918,7 +4954,7 @@ function FoldHandler(editor) { var position = e.getDocumentPosition(); var session = editor.session; - // If the user dclicked on a fold, then expand it. + // If the user clicked on a fold, then expand it. var fold = session.getFoldAt(position.row, position.column, 1); if (fold) { if (e.getAccelKey()) @@ -5198,91 +5234,109 @@ exports.commands = [{ name: "selectup", bindKey: bindKey("Shift-Up", "Shift-Up"), exec: function(editor) { editor.getSelection().selectUp(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "golineup", bindKey: bindKey("Up", "Up|Ctrl-P"), exec: function(editor, args) { editor.navigateUp(args.times); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selecttoend", bindKey: bindKey("Ctrl-Shift-End|Alt-Shift-Down", "Command-Shift-Down"), exec: function(editor) { editor.getSelection().selectFileEnd(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotoend", bindKey: bindKey("Ctrl-End|Ctrl-Down", "Command-End|Command-Down"), exec: function(editor) { editor.navigateFileEnd(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectdown", bindKey: bindKey("Shift-Down", "Shift-Down"), exec: function(editor) { editor.getSelection().selectDown(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "golinedown", bindKey: bindKey("Down", "Down|Ctrl-N"), exec: function(editor, args) { editor.navigateDown(args.times); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectwordleft", bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"), exec: function(editor) { editor.getSelection().selectWordLeft(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotowordleft", bindKey: bindKey("Ctrl-Left", "Option-Left"), exec: function(editor) { editor.navigateWordLeft(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selecttolinestart", bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left"), exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotolinestart", bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"), exec: function(editor) { editor.navigateLineStart(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectleft", bindKey: bindKey("Shift-Left", "Shift-Left"), exec: function(editor) { editor.getSelection().selectLeft(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotoleft", bindKey: bindKey("Left", "Left|Ctrl-B"), exec: function(editor, args) { editor.navigateLeft(args.times); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectwordright", bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"), exec: function(editor) { editor.getSelection().selectWordRight(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotowordright", bindKey: bindKey("Ctrl-Right", "Option-Right"), exec: function(editor) { editor.navigateWordRight(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selecttolineend", bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right"), exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotolineend", bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"), exec: function(editor) { editor.navigateLineEnd(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectright", bindKey: bindKey("Shift-Right", "Shift-Right"), exec: function(editor) { editor.getSelection().selectRight(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "gotoright", bindKey: bindKey("Right", "Right|Ctrl-F"), exec: function(editor, args) { editor.navigateRight(args.times); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectpagedown", @@ -5318,11 +5372,13 @@ exports.commands = [{ name: "selectlinestart", bindKey: bindKey("Shift-Home", "Shift-Home"), exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "selectlineend", bindKey: bindKey("Shift-End", "Shift-End"), exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", readOnly: true }, { name: "togglerecording", @@ -5334,17 +5390,37 @@ exports.commands = [{ bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"), exec: function(editor) { editor.commands.replay(editor); }, readOnly: true +}, { + name: "jumptomatching", + bindKey: bindKey("Ctrl-Shift-P", "Ctrl-Shift-P"), + exec: function(editor) { editor.jumpToMatching(); }, + multiSelectAction: "forEach", + readOnly: true }, // commands disabled in readOnly mode { + name: "cut", + exec: function(editor) { + var range = editor.getSelectionRange(); + editor._emit("cut", range); + + if (!editor.selection.isEmpty()) { + editor.session.remove(range); + editor.clearSelection(); + } + }, + multiSelectAction: "forEach" +}, { name: "removeline", bindKey: bindKey("Ctrl-D", "Command-D"), - exec: function(editor) { editor.removeLines(); } + exec: function(editor) { editor.removeLines(); }, + multiSelectAction: "forEach" }, { name: "togglecomment", bindKey: bindKey("Ctrl-7", "Command-7"), - exec: function(editor) { editor.toggleCommentLines(); } + exec: function(editor) { editor.toggleCommentLines(); }, + multiSelectAction: "forEach" }, { name: "replace", bindKey: bindKey("Ctrl-R", "Command-Option-F"), @@ -5396,66 +5472,76 @@ exports.commands = [{ }, { name: "del", bindKey: bindKey("Delete", "Delete|Ctrl-D"), - exec: function(editor) { editor.remove("right"); } + exec: function(editor) { editor.remove("right"); }, + multiSelectAction: "forEach" }, { name: "backspace", bindKey: bindKey( - "Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace", + "Command-Backspace|Option-Backspace|Shift-Backspace|Backspace", "Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H" ), - exec: function(editor) { editor.remove("left"); } + exec: function(editor) { editor.remove("left"); }, + multiSelectAction: "forEach" }, { name: "removetolinestart", - bindKey: bindKey("Alt-Backspace", "Option-Backspace"), - exec: function(editor) { editor.removeToLineStart(); } + bindKey: bindKey("Alt-Backspace", "Command-Backspace"), + exec: function(editor) { editor.removeToLineStart(); }, + multiSelectAction: "forEach" }, { name: "removetolineend", bindKey: bindKey("Alt-Delete", "Ctrl-K"), - exec: function(editor) { editor.removeToLineEnd(); } + exec: function(editor) { editor.removeToLineEnd(); }, + multiSelectAction: "forEach" }, { name: "removewordleft", bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"), - exec: function(editor) { editor.removeWordLeft(); } + exec: function(editor) { editor.removeWordLeft(); }, + multiSelectAction: "forEach" }, { name: "removewordright", bindKey: bindKey("Ctrl-Delete", "Alt-Delete"), - exec: function(editor) { editor.removeWordRight(); } + exec: function(editor) { editor.removeWordRight(); }, + multiSelectAction: "forEach" }, { name: "outdent", bindKey: bindKey("Shift-Tab", "Shift-Tab"), - exec: function(editor) { editor.blockOutdent(); } + exec: function(editor) { editor.blockOutdent(); }, + multiSelectAction: "forEach" }, { name: "indent", bindKey: bindKey("Tab", "Tab"), - exec: function(editor) { editor.indent(); } + exec: function(editor) { editor.indent(); }, + multiSelectAction: "forEach" }, { name: "insertstring", - exec: function(editor, str) { editor.insert(str); } + exec: function(editor, str) { editor.insert(str); }, + multiSelectAction: "forEach" }, { name: "inserttext", exec: function(editor, args) { editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); - } + }, + multiSelectAction: "forEach" }, { name: "splitline", bindKey: bindKey(null, "Ctrl-O"), - exec: function(editor) { editor.splitLine(); } + exec: function(editor) { editor.splitLine(); }, + multiSelectAction: "forEach" }, { name: "transposeletters", bindKey: bindKey("Ctrl-T", "Ctrl-T"), - exec: function(editor) { editor.transposeLetters(); } + exec: function(editor) { editor.transposeLetters(); }, + multiSelectAction: function(editor) {editor.transposeSelections(1); } }, { name: "touppercase", bindKey: bindKey("Ctrl-U", "Ctrl-U"), - exec: function(editor) { editor.toUpperCase(); } + exec: function(editor) { editor.toUpperCase(); }, + multiSelectAction: "forEach" }, { name: "tolowercase", bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"), - exec: function(editor) { editor.toLowerCase(); } -}, { - name: "jumptomatching", - bindKey: bindKey("Ctrl-Shift-P", "Ctrl-Shift-P"), - exec: function(editor) { editor.jumpToMatching(); } + exec: function(editor) { editor.toLowerCase(); }, + multiSelectAction: "forEach" }]; }); @@ -5499,11 +5585,13 @@ exports.commands = [{ * * ***** END LICENSE BLOCK ***** */ -define('ace/edit_session', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/lang', 'ace/lib/event_emitter', 'ace/selection', 'ace/mode/text', 'ace/range', 'ace/document', 'ace/background_tokenizer', 'ace/edit_session/folding', 'ace/edit_session/bracket_match'], function(require, exports, module) { +define('ace/edit_session', ['require', 'exports', 'module' , 'ace/config', 'ace/lib/oop', 'ace/lib/lang', 'ace/lib/net', 'ace/lib/event_emitter', 'ace/selection', 'ace/mode/text', 'ace/range', 'ace/document', 'ace/background_tokenizer', 'ace/edit_session/folding', 'ace/edit_session/bracket_match'], function(require, exports, module) { "use strict"; +var config = require("./config"); var oop = require("./lib/oop"); var lang = require("./lib/lang"); +var net = require("./lib/net"); var EventEmitter = require("./lib/event_emitter").EventEmitter; var Selection = require("./selection").Selection; var TextMode = require("./mode/text").Mode; @@ -5945,10 +6033,65 @@ var EditSession = function(text, mode) { this._emit("tokenizerUpdate", e); }; + this.$modes = {}; + this._loadMode = function(mode, callback) { + if (this.$modes[mode]) + return callback(this.$modes[mode]); + + var _self = this; + var module; + try { + module = require(mode); + } catch (e) {}; + if (module) + return done(module); + + fetch(function() { + require([mode], done); + }); + + function done(module) { + if (_self.$modes[mode]) + return callback(_self.$modes[mode]); + + _self.$modes[mode] = new module.Mode(); + _self._emit("loadmode", { + name: mode, + mode: _self.$modes[mode] + }); + callback(_self.$modes[mode]); + } + + function fetch(callback) { + if (!config.get("packaged")) + return callback(); + + var base = mode.split("/").pop(); + var filename = config.get("modePath") + "/mode-" + base + config.get("suffix"); + net.loadScript(filename, callback); + } + }; + this.$mode = null; + this.$origMode = null; this.setMode = function(mode) { + this.$origMode = mode; + + // load on demand + if (typeof mode === "string") { + var _self = this; + this._loadMode(mode, function(module) { + if (_self.$origMode !== mode) + return; + + _self.setMode(module); + }); + return; + } + if (this.$mode === mode) return; this.$mode = mode; + this.$stopWorker(); @@ -6847,8 +6990,8 @@ var EditSession = function(text, mode) { } this.getScreenLastRowColumn = function(screenRow) { - //return this.screenToDocumentColumn(screenRow, Number.MAX_VALUE / 10) - return this.documentToScreenColumn(screenRow, this.doc.getLine(screenRow).length); + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE) + return this.documentToScreenColumn(pos.row, pos.column); }; this.getDocumentLastRowColumn = function(docRow, docColumn) { @@ -6963,16 +7106,10 @@ var EditSession = function(text, mode) { docColumn += this.$getStringScreenWidth(line, screenColumn)[1]; - // Need to do some clamping action here. - if (this.$useWrapMode) { - if (docColumn >= column) { - // We remove one character at the end such that the docColumn - // position returned is not associated to the next row on the - // screen. - docColumn = column - 1; - } - } else { - docColumn = Math.min(docColumn, line.length); + // We remove one character at the end so that the docColumn + // position returned is not associated to the next row on the screen. + if (this.$useWrapMode && docColumn >= column) { + docColumn = column - 1; } if (foldLine) { @@ -7174,7 +7311,195 @@ require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype) exports.EditSession = EditSession; }); -/* ***** 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 + * 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): + * Fabian Jakobs <fabian AT ajax DOT org> + * + * 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('ace/config', ['require', 'exports', 'module' , 'ace/lib/lang'], function(require, exports, module) { +"no use strict"; + +var lang = require("./lib/lang"); + +var global = (function() { + return this; +})(); + +var options = { + packaged: false, + workerPath: "", + modePath: "", + themePath: "", + suffix: ".js" +}; + +exports.get = function(key) { + if (!options.hasOwnProperty(key)) + throw new Error("Unknown confik key: " + key); + + return options[key]; +}; + +exports.set = function(key, value) { + if (!options.hasOwnProperty(key)) + throw new Error("Unknown confik key: " + key); + + options[key] = value; +}; + +exports.all = function() { + return lang.copyObject(options); +}; + +exports.init = function() { + options.packaged = require.packaged || module.packaged || (global.define && define.packaged); + + if (!global.document) + return ""; + + var scriptOptions = {}; + var scriptUrl = ""; + var suffix; + + var scripts = document.getElementsByTagName("script"); + for (var i=0; i<scripts.length; i++) { + var script = scripts[i]; + + var src = script.src || script.getAttribute("src"); + if (!src) { + continue; + } + + var attributes = script.attributes; + for (var j=0, l=attributes.length; j < l; j++) { + var attr = attributes[j]; + if (attr.name.indexOf("data-ace-") === 0) { + scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = attr.value; + } + } + + var m = src.match(/^(?:(.*\/)ace\.js|(.*\/)ace((-uncompressed)?(-noconflict)?\.js))(?:\?|$)/); + if (m) { + scriptUrl = m[1] || m[2]; + suffix = m[3]; + } + } + + if (scriptUrl) { + scriptOptions.base = scriptOptions.base || scriptUrl; + scriptOptions.packaged = true; + } + + scriptOptions.suffix = scriptOptions.suffix || suffix; + scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base; + scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base; + scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base; + delete scriptOptions.base; + + for (var key in scriptOptions) + if (typeof scriptOptions[key] !== "undefined") + exports.set(key, scriptOptions[key]); +}; + +function deHyphenate(str) { + return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); }); +} + +});/** + * based on code from: + * + * @license RequireJS text 0.25.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ +define('ace/lib/net', ['require', 'exports', 'module' ], function(require, exports, module) { +"use strict"; + +exports.get = function (url, callback) { + var xhr = exports.createXhr(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function (evt) { + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); +}; + +var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; + +exports.createXhr = function() { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else { + for (i = 0; i < 3; i++) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + if (!xhr) { + throw new Error("createXhr(): XMLHttpRequest not available"); + } + + return xhr; +}; + +exports.loadScript = function(path, callback) { + var head = document.getElementsByTagName('head')[0]; + var s = document.createElement('script'); + + s.src = path; + head.appendChild(s); + + s.onload = callback; +}; + +});/* ***** 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 @@ -7235,18 +7560,18 @@ var Selection = function(session) { this.selectionLead = this.doc.createAnchor(0, 0); this.selectionAnchor = this.doc.createAnchor(0, 0); - var _self = this; + var self = this; this.selectionLead.on("change", function(e) { - _self._emit("changeCursor"); - if (!_self.$isEmpty) - _self._emit("changeSelection"); - if (!_self.$preventUpdateDesiredColumnOnChange && e.old.column != e.value.column) - _self.$updateDesiredColumn(); + self._emit("changeCursor"); + if (!self.$isEmpty) + self._emit("changeSelection"); + if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column) + self.$desiredColumn = null; }); this.selectionAnchor.on("change", function() { - if (!_self.$isEmpty) - _self._emit("changeSelection"); + if (!self.$isEmpty) + self._emit("changeSelection"); }); }; @@ -7356,12 +7681,7 @@ var Selection = function(session) { this.setSelectionAnchor(range.start.row, range.start.column); this.selectTo(range.end.row, range.end.column); } - this.$updateDesiredColumn(); - }; - - this.$updateDesiredColumn = function() { - var cursor = this.getCursor(); - this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column); + this.$desiredColumn = null; }; this.$moveSelection = function(mover) { @@ -7568,14 +7888,14 @@ var Selection = function(session) { this.moveCursorTo(fold.end.row, fold.end.column); return; } - + // first skip space if (match = this.session.nonTokenRe.exec(rightOfCursor)) { column += this.session.nonTokenRe.lastIndex; this.session.nonTokenRe.lastIndex = 0; rightOfCursor = line.substring(column); } - + // if at line end proceed with next line if (column >= line.length) { this.moveCursorTo(row, line.length); @@ -7584,7 +7904,7 @@ var Selection = function(session) { this.moveCursorWordRight(); return; } - + // advance to the end of the next token if (match = this.session.tokenRe.exec(rightOfCursor)) { column += this.session.tokenRe.lastIndex; @@ -7609,19 +7929,19 @@ var Selection = function(session) { if (str == null) { str = this.doc.getLine(row).substring(0, column) } - + var leftOfCursor = lang.stringReverse(str); var match; this.session.nonTokenRe.lastIndex = 0; this.session.tokenRe.lastIndex = 0; - + // skip whitespace if (match = this.session.nonTokenRe.exec(leftOfCursor)) { column -= this.session.nonTokenRe.lastIndex; leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); this.session.nonTokenRe.lastIndex = 0; } - + // if at begin of the line proceed in line above if (column <= 0) { this.moveCursorTo(row, 0); @@ -7646,8 +7966,14 @@ var Selection = function(session) { this.selectionLead.column ); - var screenCol = (chars === 0 && this.$desiredColumn) || screenPos.column; - var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol); + if (chars === 0) { + if (this.$desiredColumn) + screenPos.column = this.$desiredColumn; + else + this.$desiredColumn = screenPos.column; + } + + var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenPos.column); // move the cursor and update the desired column this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0); @@ -7657,7 +7983,7 @@ var Selection = function(session) { this.moveCursorTo(position.row, position.column); }; - this.moveCursorTo = function(row, column, preventUpdateDesiredColumn) { + this.moveCursorTo = function(row, column, keepDesiredColumn) { // Ensure the row/column is not inside of a fold. var fold = this.session.getFoldAt(row, column, 1); if (fold) { @@ -7665,21 +7991,47 @@ var Selection = function(session) { column = fold.start.column; } - this.$preventUpdateDesiredColumnOnChange = true; + this.$keepDesiredColumnOnChange = true; this.selectionLead.setPosition(row, column); - this.$preventUpdateDesiredColumnOnChange = false; + this.$keepDesiredColumnOnChange = false; - if (!preventUpdateDesiredColumn) - this.$updateDesiredColumn(this.selectionLead.column); + if (!keepDesiredColumn) + this.$desiredColumn = null; }; - this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) { + this.moveCursorToScreen = function(row, column, keepDesiredColumn) { var pos = this.session.screenToDocumentPosition(row, column); - row = pos.row; - column = pos.column; - this.moveCursorTo(row, column, preventUpdateDesiredColumn); + this.moveCursorTo(pos.row, pos.column, keepDesiredColumn); }; + // remove listeners from document + this.detach = function() { + this.selectionLead.detach(); + this.selectionAnchor.detach(); + this.session = this.doc = null; + } + + this.fromOrientedRange = function(range) { + this.setSelectionRange(range, range.cursor == range.start); + this.$desiredColumn = range.desiredColumn || this.$desiredColumn; + } + + this.toOrientedRange = function(range) { + var r = this.getRange(); + if (range) { + range.start.column = r.start.column; + range.start.row = r.start.row; + range.end.column = r.end.column; + range.end.row = r.end.row; + } else { + range = r; + } + + range.cursor = this.isBackwards() ? range.start : range.end; + range.desiredColumn = this.$desiredColumn; + return range; + } + }).call(Selection.prototype); exports.Selection = Selection; @@ -7737,7 +8089,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) { }; (function() { - this.isEequal = function(range) { + this.isEqual = function(range) { return this.start.row == range.start.row && this.end.row == range.end.row && this.start.column == range.start.column && @@ -7803,6 +8155,11 @@ var Range = function(startRow, startColumn, endRow, endColumn) { return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0; } + this.intersectsRange = function(range) { + var cmp = this.compareRange(range); + return (cmp == -1 || cmp == 0 || cmp == 1); + } + this.isEnd = function(row, column) { return this.end.row == row && this.end.column == column; } @@ -7962,6 +8319,21 @@ var Range = function(startRow, startColumn, endRow, endColumn) { return Range.fromPoints(start || this.start, end || this.end); }; + this.fixOrientation = function() { + if ( + this.start.row < this.end.row + || (this.start.row == this.end.row && this.start.column < this.end.column) + ) { + return false; + } + + var temp = this.start; + this.end = this.start; + this.start = temp; + return true; + }; + + this.isEmpty = function() { return (this.start.row == this.end.row && this.start.column == this.end.column); }; @@ -8275,23 +8647,30 @@ var Tokenizer = function(rules, flag) { var ruleRegExps = []; var matchTotal = 0; var mapping = this.matchMappings[key] = {}; - + for ( var i = 0; i < state.length; i++) { + + if (state[i].regex instanceof RegExp) + state[i].regex = state[i].regex.toString().slice(1, -1); + // Count number of matching groups. 2 extra groups from the full match // And the catch-all on the end (used to force a match); var matchcount = new RegExp("(?:(" + state[i].regex + ")|(.))").exec("a").length - 2; - + // Replace any backreferences and offset appropriately. var adjustedregex = state[i].regex.replace(/\\([0-9]+)/g, function (match, digit) { return "\\" + (parseInt(digit, 10) + matchTotal + 1); }); - + + if (matchcount > 1 && state[i].token.length !== matchcount-1) + throw new Error("Matching groups and length of the token array don't match in rule #" + i + " of state " + key); + mapping[matchTotal] = { rule: i, len: matchcount }; matchTotal += matchcount; - + ruleRegExps.push(adjustedregex); } @@ -8307,47 +8686,47 @@ var Tokenizer = function(rules, flag) { var mapping = this.matchMappings[currentState]; var re = this.regExps[currentState]; re.lastIndex = 0; - + var match, tokens = []; - + var lastIndex = 0; - + var token = { type: null, value: "" }; - + while (match = re.exec(line)) { var type = "text"; var rule = null; var value = [match[0]]; for (var i = 0; i < match.length-2; i++) { - if (match[i + 1] !== undefined) { - rule = state[mapping[i].rule]; - - if (mapping[i].len > 1) { - value = match.slice(i+2, i+1+mapping[i].len); - } - - // compute token type - if (typeof rule.token == "function") - type = rule.token.apply(this, value); - else - type = rule.token; + if (match[i + 1] === undefined) + continue; - var next = rule.next; - if (next && next !== currentState) { - currentState = next; - state = this.rules[currentState]; - mapping = this.matchMappings[currentState]; - lastIndex = re.lastIndex; + rule = state[mapping[i].rule]; - re = this.regExps[currentState]; - re.lastIndex = lastIndex; - } - break; + if (mapping[i].len > 1) + value = match.slice(i+2, i+1+mapping[i].len); + + // compute token type + if (typeof rule.token == "function") + type = rule.token.apply(this, value); + else + type = rule.token; + + var next = rule.next; + if (next && next !== currentState) { + currentState = next; + state = this.rules[currentState]; + mapping = this.matchMappings[currentState]; + lastIndex = re.lastIndex; + + re = this.regExps[currentState]; + re.lastIndex = lastIndex; } + break; } if (value[0]) { @@ -8356,13 +8735,15 @@ var Tokenizer = function(rules, flag) { type = [type]; } for (var i = 0; i < value.length; i++) { + if (!value[i]) + continue; + if ((!rule || rule.merge || type[i] === "text") && token.type === type[i]) { token.value += value[i]; } else { - if (token.type) { + if (token.type) tokens.push(token); - } - + token = { type: type[i], value: value[i] @@ -8370,10 +8751,10 @@ var Tokenizer = function(rules, flag) { } } } - + if (lastIndex == line.length) break; - + lastIndex = re.lastIndex; } @@ -8879,11 +9260,12 @@ var Document = function(text) { }; this.insert = function(position, text) { - if (text.length == 0) + if (!text || text.length === 0) return position; position = this.$clipPosition(position); + // only detect new lines if the document has no line break yet if (this.getLength() <= 1) this.$detectNewLine(text); @@ -10106,7 +10488,7 @@ function Folding() { this.foldAll = function(startRow, endRow) { var foldWidgets = this.foldWidgets; - endRow = endRow || foldWidgets.length; + endRow = endRow || this.getLength(); for (var row = startRow || 0; row < endRow; row++) { if (foldWidgets[row] == null) foldWidgets[row] = this.getFoldWidget(row); @@ -10195,7 +10577,7 @@ function Folding() { // sometimes singleline folds can be missed by the code above if (!range.isMultiLine()) { fold = this.getFoldAt(range.start.row, range.start.column, 1); - if (fold && range.isEequal(fold.range)) { + if (fold && range.isEqual(fold.range)) { this.removeFold(fold); return; } @@ -10586,7 +10968,7 @@ var Fold = exports.Fold = function(range, placeholder) { }; this.addSubFold = function(fold) { - if (this.range.isEequal(fold)) + if (this.range.isEqual(fold)) return this; if (!this.range.containsRange(fold)) @@ -11264,21 +11646,153 @@ Search.SELECTION = 2; exports.Search = Search; }); -define('ace/commands/command_manager', ['require', 'exports', 'module' , 'ace/lib/keys'], function(require, exports, module) { +define('ace/commands/command_manager', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/keyboard/hash_handler', 'ace/lib/event_emitter'], function(require, exports, module) { "use strict"; -var keyUtil = require("../lib/keys"); +var oop = require("../lib/oop"); +var HashHandler = require("../keyboard/hash_handler").HashHandler; +var EventEmitter = require("../lib/event_emitter").EventEmitter; var CommandManager = function(platform, commands) { - if (typeof platform !== "string") - throw new TypeError("'platform' argument must be either 'mac' or 'win'"); + this.platform = platform; + this.commands = {}; + this.commmandKeyBinding = {}; + + this.addCommands(commands); + + this.setDefaultHandler("exec", function(e) { + e.command.exec(e.editor, e.args || {}); + }); +}; + +oop.inherits(CommandManager, HashHandler); + +(function() { + + oop.implement(this, EventEmitter); + + this.exec = function(command, editor, args) { + if (typeof command === 'string') + command = this.commands[command]; + + if (!command) + return false; + + if (editor && editor.$readOnly && !command.readOnly) + return false; + + this._emit("exec", {editor: editor, command: command, args: args}); + return true; + }; + this.toggleRecording = function() { + if (this.$inReplay) + return; + if (this.recording) { + this.macro.pop(); + this.removeEventListener("exec", this.$addCommandToMacro); + + if (!this.macro.length) + this.macro = this.oldMacro; + + return this.recording = false; + } + if (!this.$addCommandToMacro) { + this.$addCommandToMacro = function(e) { + this.macro.push([e.command, e.args]); + }.bind(this); + } + + this.oldMacro = this.macro; + this.macro = []; + this.on("exec", this.$addCommandToMacro); + return this.recording = true; + }; + + this.replay = function(editor) { + if (this.$inReplay || !this.macro) + return; + + if (this.recording) + return this.toggleRecording(); + + try { + this.$inReplay = true; + this.macro.forEach(function(x) { + if (typeof x == "string") + this.exec(x, editor); + else + this.exec(x[0], editor, x[1]); + }, this); + } finally { + this.$inReplay = false; + } + }; + + this.trimMacro = function(m) { + return m.map(function(x){ + if (typeof x[0] != "string") + x[0] = x[0].name; + if (!x[1]) + x = x[0]; + return x; + }); + }; + +}).call(CommandManager.prototype); + +exports.CommandManager = CommandManager; + +}); +/* ***** 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 Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabian Jakobs <fabian AT ajax DOT org> + * Julian Viereck (julian.viereck@gmail.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('ace/keyboard/hash_handler', ['require', 'exports', 'module' , 'ace/lib/keys'], function(require, exports, module) { +"use strict"; + +var keyUtil = require("../lib/keys"); + +function HashHandler(config, platform) { this.platform = platform; this.commands = {}; this.commmandKeyBinding = {}; - if (commands) - commands.forEach(this.addCommand, this); + this.addCommands(config); }; (function() { @@ -11299,7 +11813,7 @@ var CommandManager = function(platform, commands) { command = this.commands[name]; delete this.commands[name]; - // exaustive search is brute force but since removeCommand is + // exhaustive search is brute force but since removeCommand is // not a performance critical operation this should be OK var ckb = this.commmandKeyBinding; for (var hashId in ckb) { @@ -11311,7 +11825,7 @@ var CommandManager = function(platform, commands) { }; this.addCommands = function(commands) { - Object.keys(commands).forEach(function(name) { + commands && Object.keys(commands).forEach(function(name) { var command = commands[name]; if (typeof command === "string") return this.bindKey(command, name); @@ -11357,14 +11871,14 @@ var CommandManager = function(platform, commands) { var key = typeof binding == "string" ? binding: binding[this.platform]; this.bindKey(key, command); - } + }; function parseKeys(keys, val, ret) { var key; var hashId = 0; - var parts = splitSafe(keys); + var parts = splitSafe(keys.toLowerCase()); - for (var i=0, l = parts.length; i < l; i++) { + for (var i = 0, l = parts.length; i < l; i++) { if (keyUtil.KEY_MODS[parts[i]]) hashId = hashId | keyUtil.KEY_MODS[parts[i]]; else @@ -11374,95 +11888,28 @@ var CommandManager = function(platform, commands) { return { key: key, hashId: hashId - } + }; } - function splitSafe(s, separator) { - return (s.toLowerCase() - .trim() + function splitSafe(s) { + return (s.trim() .split(new RegExp("[\\s ]*\\-[\\s ]*", "g"), 999)); } - this.findKeyCommand = function findKeyCommand(hashId, textOrKey) { - // Convert keyCode to the string representation. - if (typeof textOrKey == "number") { - textOrKey = keyUtil.keyCodeToString(textOrKey); - } - + this.findKeyCommand = function findKeyCommand(hashId, keyString) { var ckbr = this.commmandKeyBinding; - return ckbr[hashId] && ckbr[hashId][textOrKey.toLowerCase()]; + return ckbr[hashId] && ckbr[hashId][keyString.toLowerCase()]; } - this.exec = function(command, editor, args) { - if (typeof command === 'string') - command = this.commands[command]; - - if (!command) - return false; - - if (editor && editor.$readOnly && !command.readOnly) - return false; - - command.exec(editor, args || {}); - return true; - }; - - this.toggleRecording = function() { - if (this.$inReplay) - return; - if (this.recording) { - this.macro.pop(); - this.exec = this.normal_exec; - - if (!this.macro.length) - this.macro = this.oldMacro; - - return this.recording = false; - } - this.oldMacro = this.macro; - this.macro = []; - this.normal_exec = this.exec; - this.exec = function(command, editor, args) { - this.macro.push([command, args]); - return this.normal_exec(command, editor, args); + this.handleKeyboard = function(data, hashId, keyString, keyCode) { + return { + command: this.findKeyCommand(hashId, keyString) }; - return this.recording = true; }; - this.replay = function(editor) { - if (this.$inReplay || !this.macro) - return; - - if (this.recording) - return this.toggleRecording(); - - try { - this.$inReplay = true; - this.macro.forEach(function(x) { - if (typeof x == "string") - this.exec(x, editor); - else - this.exec(x[0], editor, x[1]); - }, this) - } finally { - this.$inReplay = false; - } - }; - - this.trimMacro = function(m) { - return m.map(function(x){ - if (typeof x[0] != "string") - x[0] = x[0].name; - if (!x[1]) - x = x[0]; - return x - }) - } - -}).call(CommandManager.prototype); - -exports.CommandManager = CommandManager; +}).call(HashHandler.prototype) +exports.HashHandler = HashHandler; }); /* vim:ts=4:sts=4:sw=4: * ***** BEGIN LICENSE BLOCK ***** @@ -11598,13 +12045,15 @@ exports.UndoManager = UndoManager; * * ***** END LICENSE BLOCK ***** */ -define('ace/virtual_renderer', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/dom', 'ace/lib/event', 'ace/lib/useragent', 'ace/layer/gutter', 'ace/layer/marker', 'ace/layer/text', 'ace/layer/cursor', 'ace/scrollbar', 'ace/renderloop', 'ace/lib/event_emitter', 'text!ace/css/editor.css'], function(require, exports, module) { +define('ace/virtual_renderer', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/dom', 'ace/lib/event', 'ace/lib/useragent', 'ace/config', 'ace/lib/net', 'ace/layer/gutter', 'ace/layer/marker', 'ace/layer/text', 'ace/layer/cursor', 'ace/scrollbar', 'ace/renderloop', 'ace/lib/event_emitter', 'text!ace/css/editor.css'], function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); var dom = require("./lib/dom"); var event = require("./lib/event"); var useragent = require("./lib/useragent"); +var config = require("./config"); +var net = require("./lib/net"); var GutterLayer = require("./layer/gutter").Gutter; var MarkerLayer = require("./layer/marker").Marker; var TextLayer = require("./layer/text").Text; @@ -11618,13 +12067,13 @@ dom.importCssString(editorCss, "ace_editor"); var VirtualRenderer = function(container, theme) { var _self = this; - + this.container = container; // 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); - + dom.addCssClass(container, "ace_editor"); this.setTheme(theme); @@ -11642,8 +12091,8 @@ var VirtualRenderer = function(container, theme) { this.scroller.appendChild(this.content); this.$gutterLayer = new GutterLayer(this.$gutter); - this.$gutterLayer.on("changeGutterWidth", this.onResize.bind(this, true)); - + this.$gutterLayer.on("changeGutterWidth", this.onResize.bind(this, true)); + this.$markerBack = new MarkerLayer(this.content); var textLayer = this.$textLayer = new TextLayer(this.content); @@ -11661,6 +12110,8 @@ var VirtualRenderer = function(container, theme) { this.$horizScroll = true; this.$horizScrollAlwaysVisible = true; + this.$animatedScroll = false; + this.scrollBar = new ScrollBar(container); this.scrollBar.addEventListener("scroll", function(e) { _self.session.setScrollTop(e.data); @@ -11668,9 +12119,18 @@ var VirtualRenderer = function(container, theme) { this.scrollTop = 0; this.scrollLeft = 0; - + event.addListener(this.scroller, "scroll", function() { - _self.session.setScrollLeft(_self.scroller.scrollLeft); + var scrollLeft = _self.scroller.scrollLeft; + _self.scrollLeft = scrollLeft; + _self.session.setScrollLeft(scrollLeft); + + if (scrollLeft == 0) { + _self.$gutter.className = "ace_gutter"; + } + else { + _self.$gutter.className = "ace_gutter horscroll"; + } }); this.cursorPos = { @@ -11678,7 +12138,7 @@ var VirtualRenderer = function(container, theme) { column : 0 }; - this.$textLayer.addEventListener("changeCharaterSize", function() { + this.$textLayer.addEventListener("changeCharacterSize", function() { _self.characterWidth = textLayer.getCharacterWidth(); _self.lineHeight = textLayer.getLineHeight(); _self.$updatePrintMargin(); @@ -11731,6 +12191,7 @@ var VirtualRenderer = function(container, theme) { this.CHANGE_MARKER_BACK = 128; this.CHANGE_MARKER_FRONT = 256; this.CHANGE_FULL = 512; + this.CHANGE_H_SCROLL = 1024; oop.implement(this, EventEmitter); @@ -11829,6 +12290,14 @@ var VirtualRenderer = function(container, theme) { return this.session.adjustWrapLimit(limit); }; + this.setAnimatedScroll = function(shouldAnimate){ + this.$animatedScroll = shouldAnimate; + }; + + this.getAnimatedScroll = function() { + return this.$animatedScroll; + }; + this.setShowInvisibles = function(showInvisibles) { if (this.$textLayer.setShowInvisibles(showInvisibles)) this.$loop.schedule(this.CHANGE_TEXT); @@ -11907,7 +12376,7 @@ var VirtualRenderer = function(container, theme) { // this persists in IE9 if (useragent.isIE) return; - + if (this.layerConfig.lastRow === 0) return; @@ -11968,7 +12437,7 @@ var VirtualRenderer = function(container, theme) { }; this.$renderChanges = function(changes) { - if (!changes || !this.session) + if (!changes || !this.session || !this.container.offsetWidth) return; // text, scrolling and resize changes can cause the view port size to change @@ -11980,20 +12449,33 @@ var VirtualRenderer = function(container, theme) { ) this.$computeLayerConfig(); + // horizontal scrolling + if (changes & this.CHANGE_H_SCROLL) { + this.scroller.scrollLeft = this.scrollLeft; + + // read the value after writing it since the value might get clipped + var scrollLeft = this.scroller.scrollLeft; + this.scrollLeft = scrollLeft; + this.session.setScrollLeft(scrollLeft); + } + // full if (changes & this.CHANGE_FULL) { + this.$textLayer.checkForSizeChanges(); + // update scrollbar first to not lose scroll position when gutter calls resize + this.$updateScrollBar(); this.$textLayer.update(this.layerConfig); if (this.showGutter) this.$gutterLayer.update(this.layerConfig); this.$markerBack.update(this.layerConfig); this.$markerFront.update(this.layerConfig); this.$cursorLayer.update(this.layerConfig); - this.$updateScrollBar(); return; } // scrolling if (changes & this.CHANGE_SCROLL) { + this.$updateScrollBar(); if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES) this.$textLayer.update(this.layerConfig); else @@ -12004,7 +12486,6 @@ var VirtualRenderer = function(container, theme) { this.$markerBack.update(this.layerConfig); this.$markerFront.update(this.layerConfig); this.$cursorLayer.update(this.layerConfig); - this.$updateScrollBar(); return; } @@ -12014,10 +12495,11 @@ var VirtualRenderer = function(container, theme) { this.$gutterLayer.update(this.layerConfig); } else if (changes & this.CHANGE_LINES) { - this.$updateLines(); - this.$updateScrollBar(); - if (this.showGutter) - this.$gutterLayer.update(this.layerConfig); + if (this.$updateLines()) { + this.$updateScrollBar(); + if (this.showGutter) + this.$gutterLayer.update(this.layerConfig); + } } else if (changes & this.CHANGE_GUTTER) { if (this.showGutter) this.$gutterLayer.update(this.layerConfig); @@ -12049,9 +12531,13 @@ var VirtualRenderer = function(container, theme) { var horizScroll = this.$horizScrollAlwaysVisible || this.$size.scrollerWidth - longestLine < 0; var horizScrollChanged = this.$horizScroll !== horizScroll; this.$horizScroll = horizScroll; - if (horizScrollChanged) + if (horizScrollChanged) { this.scroller.style.overflowX = horizScroll ? "scroll" : "hidden"; - + // when we hide scrollbar scroll event isn't emited + // leaving session with wrong scrollLeft value + if (!horizScroll) + this.session.setScrollLeft(0); + } var maxHeight = this.session.getScreenLength() * this.lineHeight; this.session.setScrollTop(Math.max(0, Math.min(this.scrollTop, maxHeight - this.$size.scrollerHeight))); @@ -12102,12 +12588,6 @@ var VirtualRenderer = function(container, theme) { this.content.style.width = longestLine + 2 * this.$padding + "px"; this.content.style.height = minHeight + "px"; - // scroller.scrollWidth was smaller than scrollLeft we needed - if (this.$desiredScrollLeft) { - this.scrollToX(this.$desiredScrollLeft); - this.$desiredScrollLeft = 0; - } - // Horizontal scrollbar visibility may have changed, which changes // the client height of the scroller if (horizScrollChanged) @@ -12138,6 +12618,7 @@ var VirtualRenderer = function(container, theme) { // else update only the changed rows this.$textLayer.updateLines(layerConfig, firstRow, lastRow); + return true; }; this.$getLongestLine = function() { @@ -12190,12 +12671,18 @@ var VirtualRenderer = function(container, theme) { this.$cursorLayer.showCursor(); }; - this.scrollCursorIntoView = function() { + this.scrollSelectionIntoView = function(anchor, lead) { + // first scroll anchor into view then scroll lead into view + this.scrollCursorIntoView(anchor); + this.scrollCursorIntoView(lead); + }; + + this.scrollCursorIntoView = function(cursor) { // the editor is not visible if (this.$size.scrollerHeight === 0) return; - var pos = this.$cursorLayer.getPixelPosition(); + var pos = this.$cursorLayer.getPixelPosition(cursor); var left = pos.left; var top = pos.top; @@ -12213,14 +12700,11 @@ var VirtualRenderer = function(container, theme) { if (scrollLeft > left) { if (left < this.$padding + 2 * this.layerConfig.characterWidth) left = 0; - this.scrollToX(left); + this.session.setScrollLeft(left); } if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) { - if (left > this.layerConfig.width + 2 * this.$padding) - this.$desiredScrollLeft = left; - else - this.scrollToX(Math.round(left + this.characterWidth - this.$size.scrollerWidth)); + this.session.setScrollLeft(Math.round(left + this.characterWidth - this.$size.scrollerWidth)); } }; @@ -12229,7 +12713,7 @@ var VirtualRenderer = function(container, theme) { }; this.getScrollLeft = function() { - return this.session.getScrollTop(); + return this.session.getScrollLeft(); }; this.getScrollTopRow = function() { @@ -12244,17 +12728,46 @@ var VirtualRenderer = function(container, theme) { this.session.setScrollTop(row * this.lineHeight); }; - this.scrollToLine = function(line, center) { - var lineHeight = { lineHeight: this.lineHeight }; - var offset = 0; - for (var l = 1; l < line; l++) { - offset += this.session.getRowHeight(lineHeight, l-1); - } + this.STEPS = 10; + this.$calcSteps = function(fromValue, toValue){ + var i = 0; + var l = this.STEPS; + var steps = []; - if (center) { + var func = function(t, x_min, dx) { + if ((t /= .5) < 1) + return dx / 2 * Math.pow(t, 3) + x_min; + return dx / 2 * (Math.pow(t - 2, 3) + 2) + x_min; + }; + + for (i = 0; i < l; ++i) + steps.push(func(i / this.STEPS, fromValue, toValue - fromValue)); + steps.push(toValue); + + return steps; + }; + + this.scrollToLine = function(line, center) { + var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0}); + var offset = pos.top; + if (center) offset -= this.$size.scrollerHeight / 2; + + if (this.$animatedScroll && Math.abs(offset - this.scrollTop) < 10000) { + var _self = this; + var steps = _self.$calcSteps(this.scrollTop, offset); + + clearInterval(this.$timer); + this.$timer = setInterval(function() { + _self.session.setScrollTop(steps.shift()); + + if (!steps.length) + clearInterval(_self.$timer); + }, 10); + } + else { + this.session.setScrollTop(offset); } - this.session.setScrollTop(offset); }; this.scrollToY = function(scrollTop) { @@ -12270,8 +12783,9 @@ var VirtualRenderer = function(container, theme) { if (scrollLeft <= this.$padding) scrollLeft = 0; - this.scroller.scrollLeft = scrollLeft; - scrollLeft = this.scroller.scrollLeft; + if (this.scrollLeft !== scrollLeft) + this.scrollLeft = scrollLeft; + this.$loop.schedule(this.CHANGE_H_SCROLL); }; this.scrollBy = function(deltaX, deltaY) { @@ -12287,16 +12801,27 @@ var VirtualRenderer = function(container, theme) { // todo: handle horizontal scrolling }; + this.pixelToScreenCoordinates = function(pageX, pageY) { + var canvasPos = this.scroller.getBoundingClientRect(); + + var col = Math.round( + (pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth + ); + var row = Math.floor( + (pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight + ); + + return {row: row, column: col}; + }; + this.screenToTextCoordinates = function(pageX, pageY) { var canvasPos = this.scroller.getBoundingClientRect(); var col = Math.round( - (pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) - / this.characterWidth + (pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth ); var row = Math.floor( - (pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) - / this.lineHeight + (pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight ); return this.session.screenToDocumentPosition(row, Math.max(col, 0)); @@ -12356,14 +12881,36 @@ var VirtualRenderer = function(container, theme) { style.left = "-10000px"; }; + this._loadTheme = function(name, callback) { + if (!config.get("packaged")) + return callback(); + + var base = name.split("/").pop(); + var filename = config.get("themePath") + "/theme-" + base + config.get("suffix"); + net.loadScript(filename, callback); + }; + this.setTheme = function(theme) { var _self = this; this.$themeValue = theme; if (!theme || typeof theme == "string") { - theme = theme || "ace/theme/textmate"; - require([theme], function(theme) { - afterLoad(theme); + 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); + }); }); } else { afterLoad(theme); @@ -12492,11 +13039,11 @@ var Gutter = function(parentEl) { this.addGutterDecoration = function(row, className){ if (!this.$decorations[row]) this.$decorations[row] = ""; - this.$decorations[row] += " ace_" + className; + this.$decorations[row] += " " + className; }; this.removeGutterDecoration = function(row, className){ - this.$decorations[row] = this.$decorations[row].replace(" ace_" + className, ""); + this.$decorations[row] = this.$decorations[row].replace(" " + className, ""); }; this.setBreakpoints = function(rows) { @@ -12723,7 +13270,7 @@ var Marker = function(parentEl) { }; /** - * Draws a marker, which spans a range of text in a single line + * Draws a marker, which spans a range of text on multiple lines */ this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) { // selection start @@ -12798,7 +13345,7 @@ var Marker = function(parentEl) { }; /** - * Draws a marker which covers one single full line + * Draws a marker which covers part or whole width of a single screen line */ this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength, type) { var padding = type === "background" ? 0 : this.$padding; @@ -12914,7 +13461,7 @@ var Text = function(parentEl) { var size = this.$measureSizes(); if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) { this.$characterSize = size; - this._emit("changeCharaterSize", {data: size}); + this._emit("changeCharacterSize", {data: size}); } }; @@ -12933,7 +13480,7 @@ var Text = function(parentEl) { lineHeight : 1 }; - this.$measureSizes = function() { + this.$measureSizes = useragent.isIE || useragent.isOldGecko ? function() { var n = 1000; if (!this.$measureNode) { var measureNode = this.$measureNode = dom.createElement("div"); @@ -12943,7 +13490,7 @@ var Text = function(parentEl) { style.left = style.top = (-n * 40) + "px"; style.visibility = "hidden"; - style.position = "absolute"; + style.position = "fixed"; style.overflow = "visible"; style.whiteSpace = "nowrap"; @@ -12960,8 +13507,12 @@ var Text = function(parentEl) { container = container.parentNode; container.appendChild(measureNode); } - } + + // Size and width can be null if the editor is not visible or + // detached from the document + if (!this.element.offsetWidth) + return null; var style = this.$measureNode.style; var computedStyle = dom.computedStyle(this.element); @@ -12975,7 +13526,46 @@ var Text = function(parentEl) { // Size and width can be null if the editor is not visible or // detached from the document - if (size.width == 0 && size.height == 0) + if (size.width == 0 || size.height == 0) + return null; + + return size; + } + : function() { + if (!this.$measureNode) { + var measureNode = this.$measureNode = dom.createElement("div"); + var style = measureNode.style; + + style.width = style.height = "auto"; + style.left = style.top = -100 + "px"; + + style.visibility = "hidden"; + style.position = "fixed"; + style.overflow = "visible"; + style.whiteSpace = "nowrap"; + + measureNode.innerHTML = "X"; + + var container = this.element.parentNode; + while (container && !dom.hasCssClass(container, "ace_editor")) + container = container.parentNode; + + if (!container) + return this.$measureNode = null; + + container.appendChild(measureNode); + } + + var rect = this.$measureNode.getBoundingClientRect(); + + var size = { + height: rect.height, + width: rect.width + }; + + // Size and width can be null if the editor is not visible or + // detached from the document + if (size.width == 0 || size.height == 0) return null; return size; @@ -13089,16 +13679,16 @@ var Text = function(parentEl) { }; this.$renderLinesFragment = function(config, firstRow, lastRow) { - var fragment = this.element.ownerDocument.createDocumentFragment(), - row = firstRow, - fold = this.session.getNextFoldLine(row), - foldStart = fold ?fold.start.row :Infinity; + var fragment = this.element.ownerDocument.createDocumentFragment(); + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; while (true) { if (row > foldStart) { - row = fold.end.row+1; - fold = this.session.getNextFoldLine(row, fold); - foldStart = fold ?fold.start.row :Infinity; + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; } if (row > lastRow) break; @@ -13137,15 +13727,15 @@ var Text = function(parentEl) { var html = []; var firstRow = config.firstRow, lastRow = config.lastRow; - var row = firstRow, - fold = this.session.getNextFoldLine(row), - foldStart = fold ?fold.start.row :Infinity; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; while (true) { if (row > foldStart) { - row = fold.end.row+1; - fold = this.session.getNextFoldLine(row, fold); - foldStart = fold ?fold.start.row :Infinity; + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row :Infinity; } if (row > lastRow) break; @@ -13446,11 +14036,10 @@ var Cursor = function(parentEl) { this.element.className = "ace_layer ace_cursor-layer"; parentEl.appendChild(this.element); - this.cursor = dom.createElement("div"); - this.cursor.className = "ace_cursor ace_hidden"; - this.element.appendChild(this.cursor); - this.isVisible = false; + + this.cursors = []; + this.cursor = this.addCursor(); }; (function() { @@ -13464,35 +14053,59 @@ var Cursor = function(parentEl) { this.session = session; }; + this.addCursor = function() { + var el = dom.createElement("div"); + var className = "ace_cursor"; + if (!this.isVisible) + className += " ace_hidden"; + if (this.overwrite) + className += " ace_overwrite"; + + el.className = className; + this.element.appendChild(el); + this.cursors.push(el); + return el; + }; + + this.removeCursor = function() { + if (this.cursors.length > 1) { + var el = this.cursors.pop(); + el.parentNode.removeChild(el); + return el; + } + }; + this.hideCursor = function() { this.isVisible = false; - dom.addCssClass(this.cursor, "ace_hidden"); + for (var i = this.cursors.length; i--; ) + dom.addCssClass(this.cursors[i], "ace_hidden"); clearInterval(this.blinkId); }; this.showCursor = function() { this.isVisible = true; - dom.removeCssClass(this.cursor, "ace_hidden"); - this.cursor.style.visibility = "visible"; + for (var i = this.cursors.length; i--; ) + dom.removeCssClass(this.cursors[i], "ace_hidden"); + + this.element.style.visibility = ""; this.restartTimer(); }; this.restartTimer = function() { clearInterval(this.blinkId); - if (!this.isVisible) { + if (!this.isVisible) return; - } - var cursor = this.cursor; + var element = this.element; this.blinkId = setInterval(function() { - cursor.style.visibility = "hidden"; + element.style.visibility = "hidden"; setTimeout(function() { - cursor.style.visibility = "visible"; + element.style.visibility = "visible"; }, 400); }, 1000); }; - this.getPixelPosition = function(onScreen) { + this.getPixelPosition = function(position, onScreen) { if (!this.config || !this.session) { return { left : 0, @@ -13500,7 +14113,8 @@ var Cursor = function(parentEl) { }; } - var position = this.session.selection.getCursor(); + if (!position) + position = this.session.selection.getCursor(); var pos = this.session.documentToScreenPosition(position); var cursorLeft = Math.round(this.$padding + pos.column * this.config.characterWidth); @@ -13516,25 +14130,53 @@ var Cursor = function(parentEl) { this.update = function(config) { this.config = config; - this.pixelPos = this.getPixelPosition(true); + if (this.session.selectionMarkerCount > 1) { + var selections = this.session.$selectionMarkers; + var i = 0, sel, cursorIndex = 0; - this.cursor.style.left = this.pixelPos.left + "px"; - this.cursor.style.top = this.pixelPos.top + "px"; - this.cursor.style.width = config.characterWidth + "px"; - this.cursor.style.height = config.lineHeight + "px"; + for (var i = selections.length; i--; ) { + sel = selections[i]; + var pixelPos = this.getPixelPosition(sel.cursor, true); - var overwrite = this.session.getOverwrite() - if (overwrite != this.overwrite) { - this.overwrite = overwrite; - if (overwrite) - dom.addCssClass(this.cursor, "ace_overwrite"); - else - dom.removeCssClass(this.cursor, "ace_overwrite"); + var style = (this.cursors[cursorIndex++] || this.addCursor()).style; + + style.left = pixelPos.left + "px"; + style.top = pixelPos.top + "px"; + style.width = config.characterWidth + "px"; + style.height = config.lineHeight + "px"; + } + if (cursorIndex > 1) + while (this.cursors.length > cursorIndex) + this.removeCursor(); + } else { + var pixelPos = this.getPixelPosition(null, true); + var style = this.cursor.style; + style.left = pixelPos.left + "px"; + style.top = pixelPos.top + "px"; + style.width = config.characterWidth + "px"; + style.height = config.lineHeight + "px"; + + while (this.cursors.length > 1) + this.removeCursor(); } + var overwrite = this.session.getOverwrite(); + if (overwrite != this.overwrite) + this.$setOverite(overwrite); + this.restartTimer(); }; + this.$setOverite = function(overwrite) { + this.overwrite = overwrite; + for (var i = this.cursors.length; i--; ) { + if (overwrite) + dom.addCssClass(this.cursors[i], "ace_overwrite"); + else + dom.removeCssClass(this.cursors[i], "ace_overwrite"); + } + }; + this.destroy = function() { clearInterval(this.blinkId); } @@ -13629,6 +14271,8 @@ var ScrollBar = function(parent) { this.inner.style.height = height + "px"; }; + // TODO: on chrome 17+ after for small zoom levels after this function + // this.element.scrollTop != scrollTop which makes page to scroll up. this.setScrollTop = function(scrollTop) { this.element.scrollTop = scrollTop; }; @@ -13713,11 +14357,10 @@ exports.RenderLoop = RenderLoop; }); define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);\n" + "\n" + - "\n" + ".ace_editor {\n" + " position: absolute;\n" + " overflow: hidden;\n" + - " font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Courier New', monospace;\n" + + " font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;\n" + " font-size: 12px;\n" + "}\n" + "\n" + @@ -13735,12 +14378,6 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa " cursor: text;\n" + "}\n" + "\n" + - "/* setting pointer-events: auto; on node under the mouse, which changes during scroll,\n" + - " will break mouse wheel scrolling in Safari */\n" + - ".ace_content * {\n" + - " pointer-events: none;\n" + - "}\n" + - "\n" + ".ace_composition {\n" + " position: absolute;\n" + " background: #555;\n" + @@ -13748,18 +14385,17 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa " z-index: 4;\n" + "}\n" + "\n" + - ".ace_gutter .ace_layer {\n" + - " position: relative;\n" + - " min-width: 54px;\n" + - " text-align: right;\n" + - "}\n" + - "\n" + ".ace_gutter {\n" + " position: absolute;\n" + " overflow : hidden;\n" + " height: 100%;\n" + " width: auto;\n" + " cursor: default;\n" + + " z-index: 1000;\n" + + "}\n" + + "\n" + + ".ace_gutter.horscroll {\n" + + " box-shadow: 0px 0px 20px rgba(0,0,0,0.4);\n" + "}\n" + "\n" + ".ace_gutter-cell {\n" + @@ -13838,6 +14474,16 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa " box-sizing: border-box;\n" + " -moz-box-sizing: border-box;\n" + " -webkit-box-sizing: border-box;\n" + + " /* setting pointer-events: auto; on node under the mouse, which changes\n" + + " during scroll, will break mouse wheel scrolling in Safari */\n" + + " pointer-events: none;\n" + + "}\n" + + "\n" + + ".ace_gutter .ace_layer {\n" + + " position: relative;\n" + + " min-width: 40px;\n" + + " text-align: right;\n" + + " pointer-events: auto;\n" + "}\n" + "\n" + ".ace_text-layer {\n" + @@ -13873,12 +14519,12 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa "\n" + ".ace_marker-layer .ace_selection {\n" + " position: absolute;\n" + - " z-index: 4;\n" + + " z-index: 5;\n" + "}\n" + "\n" + ".ace_marker-layer .ace_bracket {\n" + " position: absolute;\n" + - " z-index: 5;\n" + + " z-index: 6;\n" + "}\n" + "\n" + ".ace_marker-layer .ace_active_line {\n" + @@ -13886,9 +14532,13 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa " z-index: 2;\n" + "}\n" + "\n" + + ".ace_gutter .ace_gutter_active_line{\n" + + " background-color : #dcdcdc;\n" + + "}\n" + + "\n" + ".ace_marker-layer .ace_selected_word {\n" + " position: absolute;\n" + - " z-index: 6;\n" + + " z-index: 4;\n" + " box-sizing: border-box;\n" + " -moz-box-sizing: border-box;\n" + " -webkit-box-sizing: border-box;\n" + @@ -13935,7 +14585,7 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa " cursor: move;\n" + "}\n" + "\n" + - ".ace_folding-enabled .ace_gutter-cell {\n" + + ".ace_folding-enabled > .ace_gutter-cell {\n" + " padding-right: 13px;\n" + "}\n" + "\n" + @@ -13994,6 +14644,1761 @@ define("text!ace/css/editor.css", [], "@import url(//fonts.googleapis.com/css?fa "}\n" + ""); +/* 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): + * Harutyun Amirjanyan <amirjanyan AT gmail DOT 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('ace/multi_select', ['require', 'exports', 'module' , 'ace/range_list', 'ace/range', 'ace/selection', 'ace/mouse/multi_select_handler', 'ace/commands/multi_select_commands', 'ace/search', 'ace/edit_session', 'ace/editor'], function(require, exports, module) { + +var RangeList = require("./range_list").RangeList; +var Range = require("./range").Range; +var Selection = require("./selection").Selection; +var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; +exports.commands = require("./commands/multi_select_commands"); + +// Todo: session.find or editor.findVolatile that returns range +var Search = require("./search").Search; +var search = new Search(); + +function find(session, needle, dir) { + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session); +} + +// extend EditSession +var EditSession = require("./edit_session").EditSession; +(function() { + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; +}).call(EditSession.prototype); + +// extend Selection +(function() { + // list of ranges in reverse addition order + this.ranges = null; + + // automatically sorted list of ranges + this.rangeList = null; + + /** + * Selection.addRange(Range) -> Void + * + * adds a range to selection entering multiselect mode if necessary + **/ + this.addRange = function(range) { + if (!this.inMultiSelectMode && this.rangeCount == 0) { + var oldRange = this.toOrientedRange(); + if (!range || !range.isEqual(oldRange)) { + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + } + + if (!range) + return; + + if (!range.cursor) + range.cursor = range.end; + + var removed = this.rangeList.add(range); + + this.$onAddRange(range); + + if (removed.length) + this.$onRemoveRange(removed); + + if (this.rangeCount > 0 && !this.inMultiSelectMode) { + this._emit("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + }; + + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) + this.$onRemoveRange(removed); + + range && this.fromOrientedRange(range); + }; + + /** + * Selection.addRange(pos) -> Range + * pos: {row, column} + * + * removes range containing pos (if exists) + **/ + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0]; + } + }; + + /** + * Selection.mergeOverlappingRanges() -> Void + * + * merges overlapping ranges ensuring consistency after changes + **/ + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + if (removed.length) + this.$onRemoveRange(removed); + else if(this.ranges[0]) + this.fromOrientedRange(this.ranges[0]); + }; + + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this.fromOrientedRange(range); + this._emit("addRange", {range: range}); + }; + + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + var lastRange = this.rangeList.ranges.pop(); + removed.push(lastRange); + this.rangeCount = 0; + } + + for (var i = removed.length; i--; ) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + + this._emit("removeRange", {ranges: removed}); + + if (this.rangeCount == 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._emit("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + + lastRange = lastRange || this.ranges[0]; + if (lastRange && !lastRange.isEqual(this.getRange())) + this.fromOrientedRange(lastRange); + }; + + // adds multicursor support to selection + this.$initRangeList = function() { + if (this.rangeList) + return; + + this.rangeList = new RangeList(); + this.ranges = []; + this.rangeCount = 0; + }; + + this.getAllRanges = function() { + return this.rangeList.ranges.concat(); + }; + + this.splitIntoLines = function () { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var cursor = this.session.documentToScreenPosition(this.selectionLead); + var anchor = this.session.documentToScreenPosition(this.selectionAnchor); + + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + + /** + * Selection.rectangularRangeBlock(screenCursor, screenAnchor, includeEmptyLines) -> [Range] + * gets list of ranges composing rectangular block on the screen + * @includeEmptyLines if true includes ranges inside the block which + * are empty becuase of the clipping + */ + this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { + var rectSel = []; + + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + } + + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + + if (startColumn < 0) + startColumn = 0; + if (startRow < 0) + startRow = 0; + + if (startRow == endRow) + includeEmptyLines = true; + + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints( + this.session.screenToDocumentPosition(row, startColumn), + this.session.screenToDocumentPosition(row, endColumn) + ); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) + break; + var docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + + if (yBackwards) + rectSel.reverse(); + + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) + end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) + start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) + rectSel.splice(i, 1); + } + } + + return rectSel; + }; +}).call(Selection.prototype); + +// extend Editor +var Editor = require("./editor").Editor; +(function() { + /** + * Editor.updateSelectionMarkers() -> Void + * + * updates cursor and marker layers + **/ + this.updateSelectionMarkers = function() { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + /** + * Editor.addSelectionMarker(orientedRange) -> Range + * - orientedRange: range with cursor + * + * adds selection and cursor + **/ + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) + orientedRange.cursor = orientedRange.end; + + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); + + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; + + this.removeSelectionMarkers = function(ranges) { + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.marker) + continue; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) + this.session.$selectionMarkers.splice(index, 1); + } + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; + + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) + return; + this.inMultiSelectMode = true; + + this.setStyle("multiselect"); + this.keyBinding.addKeyboardHandler(exports.commands.keyboardHandler); + this.commands.on("exec", this.$onMultiSelectExec); + + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) + return; + this.inMultiSelectMode = false; + + this.unsetStyle("multiselect"); + this.keyBinding.removeKeyboardHandler(exports.commands.keyboardHandler); + + this.commands.removeEventListener("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onMultiSelectExec = function(e) { + var command = e.command; + var editor = e.editor; + if (!command.multiSelectAction) { + command.exec(editor, e.args || {}); + editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); + editor.multiSelect.mergeOverlappingRanges(); + } else if (command.multiSelectAction == "forEach") { + editor.forEachSelection(command, e.args); + } else if (command.multiSelectAction == "single") { + editor.exitMultiSelectMode(); + command.exec(editor, e.args || {}); + } else { + command.multiSelectAction(editor, e.args || {}); + } + e.preventDefault(); + }; + + /** + * Editor.forEachSelection(cmd, args) -> Void + * - cmd: command to execute + * - args: arguments to the command + * + * executes command for each selection range + **/ + this.forEachSelection = function(cmd, args) { + if (this.inVirtualSelectionMode) + return; + + var session = this.session; + var selection = this.selection; + var rangeList = selection.rangeList; + + var reg = selection._eventRegistry; + selection._eventRegistry = {}; + + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = rangeList.ranges.length; i--;) { + tmpSel.fromOrientedRange(rangeList.ranges[i]); + this.selection = session.selection = tmpSel; + cmd.exec(this, args || {}); + tmpSel.toOrientedRange(rangeList.ranges[i]); + } + tmpSel.detach(); + + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); + + this.onCursorChange(); + this.onSelectionChange(); + }; + + /** + * Editor.exitMultiSelectMode() -> Void + * + * removes all selections except the last added one. + **/ + this.exitMultiSelectMode = function() { + if (this.inVirtualSelectionMode) + return; + this.multiSelect.toSingleRange(); + }; + + this.getCopyText = function() { + var text = ""; + if (this.inMultiSelectMode) { + var ranges = this.multiSelect.rangeList.ranges; + text = []; + for (var i = 0; i < ranges.length; i++) { + text.push(this.session.getTextRange(ranges[i])); + } + text = text.join(this.session.getDocument().getNewLineCharacter()); + } else if (!this.selection.isEmpty()) { + text = this.session.getTextRange(this.getSelectionRange()); + } + + return text; + }; + + + // commands + /** + * Editor.selectMoreLines(dir, skip) -> Void + * - dir: -1 up, 1 down + * - skip: remove active selection range if true + * + * adds cursor above or bellow active cursor + **/ + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; + + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; + + var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); + + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); + var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); + } else { + var anchor = lead; + } + + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } + + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + if (skip) + var toRemove = range.cursor; + } + + this.selection.addRange(newRange); + if (toRemove) + this.selection.substractPoint(toRemove); + }; + + /** + * Editor.transposeSelections(dir) -> Void + * - dir: direction to rotate selections + * + * contents + * empty ranges are expanded to word + **/ + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; + + for (var i = all.length; i--; ) { + var range = all[i]; + if (range.isEmpty()) { + var tmp = session.getWordRange(range.start.row, range.start.column); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + range.end.row = tmp.end.row; + range.end.column = tmp.end.column; + } + } + sel.mergeOverlappingRanges(); + + var words = []; + for (var i = all.length; i--; ) { + var range = all[i]; + words.unshift(session.getTextRange(range)); + } + + if (dir < 0) + words.unshift(words.pop()); + else + words.push(words.shift()); + + for (var i = all.length; i--; ) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + } + + /** + * Editor.selectMore(dir, skip) -> Void + * - dir: 1 next, -1 previous + * - skip: remove active selection range if true + * + * finds next occurence of text in active selection + * and adds it to the selections + **/ + this.selectMore = function (dir, skip) { + var session = this.session; + var sel = session.multiSelect; + + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + var range = session.getWordRange(range.start.row, range.start.column); + range.cursor = range.end; + this.multiSelect.addRange(range); + } + var needle = session.getTextRange(range); + + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.multiSelect.addRange(newRange); + } + if (skip) + this.multiSelect.substractPoint(range.cursor); + }; +}).call(Editor.prototype); + + +function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; +} + +// patch +// adds multicursor support to a session +exports.onSessionChange = function(e) { + var session = e.session; + if (!session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } + this.multiSelect = session.multiSelect; + + var oldSession = e.oldSession; + if (oldSession) { + // todo use events + if (oldSession.multiSelect && oldSession.multiSelect.editor == this) + oldSession.multiSelect.editor = null; + + session.multiSelect.removeEventListener("addRange", this.$onAddRange); + session.multiSelect.removeEventListener("removeRange", this.$onRemoveRange); + session.multiSelect.removeEventListener("multiSelect", this.$onMultiSelect); + session.multiSelect.removeEventListener("singleSelect", this.$onSingleSelect); + } + + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + + // this.$onSelectionChange = this.onSelectionChange.bind(this); + + if (this.inMultiSelectMode != session.selection.inMultiSelectMode) { + if (session.selection.inMultiSelectMode) + this.$onMultiSelect(); + else + this.$onSingleSelect(); + } +}; + +/** + * MultiSelect(editor) -> Void + * + * adds multiple selection support to the editor + * (note: should be called only once for each editor instance) + **/ +function MultiSelect(editor) { + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + + exports.onSessionChange.call(editor, editor); + editor.on("changeSession", exports.onSessionChange.bind(editor)); + + editor.on("mousedown", onMouseDown); + editor.commands.addCommands(exports.commands.defaultCommands); +} + +exports.MultiSelect = MultiSelect; + +});/* ***** 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): + * Harutyun Amirjanyan <amirjanyan AT gmail DOT 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('ace/range_list', ['require', 'exports', 'module' ], function(require, exports, module) { +"use strict"; + + +var RangeList = function() { + this.ranges = []; +}; + +(function() { + this.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + + this.pointIndex = function(pos, startIndex) { + var list = this.ranges; + + for (var i = startIndex || 0; i < list.length; i++) { + var range = list[i]; + var cmp = this.comparePoints(pos, range.end); + + if (cmp > 0) + continue; + if (cmp == 0) + return i; + cmp = this.comparePoints(pos, range.start); + if (cmp >= 0) + return i; + + return -i-1; + } + return -i - 1; + }; + + this.add = function(range) { + var startIndex = this.pointIndex(range.start); + if (startIndex < 0) + startIndex = -startIndex - 1; + + var endIndex = this.pointIndex(range.end, startIndex); + + if (endIndex < 0) + endIndex = -endIndex - 1; + else + endIndex++; + + return this.ranges.splice(startIndex, endIndex - startIndex, range); + }; + + this.addList = function(list) { + var removed = []; + for (var i = list.length; i--; ) { + removed.push.call(removed, this.add(list[i])); + } + return removed; + }; + + this.substractPoint = function(pos) { + var i = this.pointIndex(pos); + + if (i >= 0) + return this.ranges.splice(i, 1); + }; + + // merge overlapping ranges + this.merge = function() { + var removed = []; + var list = this.ranges; + var next = list[0], range; + for (var i = 1; i < list.length; i++) { + range = next; + next = list[i]; + var cmp = this.comparePoints(range.end, next.start); + if (cmp < 0) + continue; + + if (cmp == 0 && !(range.isEmpty() || next.isEmpty())) + continue; + + if (this.comparePoints(range.end, next.end) < 0) { + range.end.row = next.end.row; + range.end.column = next.end.column; + } + + list.splice(i, 1); + removed.push(next); + next = range; + i--; + } + + return removed; + }; + + this.contains = function(row, column) { + return this.pointIndex({row: row, column: column}) >= 0; + }; + + this.containsPoint = function(pos) { + return this.pointIndex(pos) >= 0; + }; + + this.rangeAtPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) + return this.ranges[i]; + }; + + + this.clipRows = function(startRow, endRow) { + var list = this.ranges; + if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow) + return []; + + var startIndex = this.pointIndex({row: startRow, column: 0}); + if (startIndex < 0) + startIndex = -startIndex - 1; + var endIndex = this.pointIndex({row: endRow, column: 0}, startIndex); + if (endIndex < 0) + endIndex = -endIndex - 1; + + var clipped = []; + for (var i = startIndex; i < endIndex; i++) { + clipped.push(list[i]); + } + return clipped; + }; + + this.removeAll = function() { + return this.ranges.splice(0, this.ranges.length); + }; + + this.attach = function(session) { + if (this.session) + this.detach(); + + this.session = session; + this.onChange = this.$onChange.bind(this); + + this.session.on('change', this.onChange); + }; + + this.detach = function() { + if (!this.session) + return; + this.session.removeListener('change', this.onChange); + this.session = null; + }; + + this.$onChange = function(e) { + var changeRange = e.data.range; + if (e.data.action[0] == "i"){ + var start = changeRange.start; + var end = changeRange.end; + } else { + var end = changeRange.start; + var start = changeRange.end; + } + var startRow = start.row; + var endRow = end.row; + var lineDif = endRow - startRow; + + var colDiff = -start.column + end.column; + + var ranges = this.ranges; + + for (var i=0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row < startRow) + continue; + if (r.start.row > startRow) + break; + + if (r.start.row == startRow && r.start.column >= start.column ) { + r.start.column += colDiff; + r.start.row += lineDif; + } + if (r.end.row == startRow && r.end.column >= start.column) { + r.end.column += colDiff; + r.end.row += lineDif; + } + } + + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } + }; + +}).call(RangeList.prototype); + +exports.RangeList = RangeList; +}); +/* 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): + * Harutyun Amirjanyan <amirjanyan AT gmail DOT 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('ace/mouse/multi_select_handler', ['require', 'exports', 'module' , 'ace/lib/event'], function(require, exports, module) { + +var event = require("../lib/event"); + + +// mouse +function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; +} + +function onMouseDown(e) { + var ev = e.domEvent; + var alt = ev.altKey; + var shift = ev.shiftKey; + var ctrl = e.getAccelKey(); + var button = e.getButton(); + + if (!ctrl && !alt) { + if (e.editor.inMultiSelectMode) { + if (button == 0) { + e.editor.exitMultiSelectMode(); + } else if (button == 2) { + var editor = e.editor; + var selectionEmpty = editor.selection.isEmpty(); + editor.textInput.onContextMenu({x: e.clientX, y: e.clientY}, selectionEmpty); + event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose); + e.stop(); + } + } + return; + } + + var editor = e.editor; + var selection = editor.selection; + var isMultiSelect = editor.inMultiSelectMode; + var pos = e.getDocumentPosition(); + var cursor = selection.getCursor(); + var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + + + var mouseX = e.pageX, mouseY = e.pageY; + var onMouseSelection = function(e) { + mouseX = event.getDocumentX(e); + mouseY = event.getDocumentY(e); + }; + + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column); + + if (isSamePoint(screenCursor, newCursor) + && isSamePoint(cursor, selection.selectionLead)) + return; + screenCursor = newCursor; + + editor.selection.moveCursorToPosition(cursor); + editor.selection.clearSelection(); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + }; + + var session = editor.session; + var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var screenCursor = screenAnchor; + + + + if (ctrl && !shift && !alt && button == 0) { + if (!isMultiSelect && inSelection) + return; // dragging + + if (!isMultiSelect) + selection.addRange(selection.toOrientedRange()); + + + var oldRange = selection.rangeList.rangeAtPoint(pos); + + event.capture(editor.container, function(){}, function() { + var tmpSel = selection.toOrientedRange(); + + if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) + selection.substractPoint(tmpSel.cursor); + else + selection.addRange(tmpSel); + }); + + } else if (!shift && alt && button == 0) { + e.stop(); + + if (isMultiSelect && !ctrl) + selection.toSingleRange(); + else if (!isMultiSelect && ctrl) + selection.addRange(); + + selection.moveCursorToPosition(pos); + selection.clearSelection(); + + var rectSel = []; + + var onMouseSelectionEnd = function(e) { + clearInterval(timerId); + editor.removeSelectionMarkers(rectSel); + for (var i = 0; i < rectSel.length; i++) + selection.addRange(rectSel[i]); + }; + + var onSelectionInterval = blockSelect; + + event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); + var timerId = setInterval(function() {onSelectionInterval();}, 20); + + return e.preventDefault(); + } +} + + +exports.onMouseDown = onMouseDown; + +});/* 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): + * Harutyun Amirjanyan <amirjanyan AT gmail DOT 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('ace/commands/multi_select_commands', ['require', 'exports', 'module' , 'ace/keyboard/hash_handler'], function(require, exports, module) { + +// commands to enter multiselect mode +exports.defaultCommands = [{ + name: "addCursorAbove", + exec: function(editor) { editor.selectMoreLines(-1); }, + bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"}, + readonly: true +}, { + name: "addCursorBelow", + exec: function(editor) { editor.selectMoreLines(1); }, + bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"}, + readonly: true +}, { + name: "addCursorAboveSkipCurrent", + exec: function(editor) { editor.selectMoreLines(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"}, + readonly: true +}, { + name: "addCursorBelowSkipCurrent", + exec: function(editor) { editor.selectMoreLines(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"}, + readonly: true +}, { + name: "selectMoreBefore", + exec: function(editor) { editor.selectMore(-1); }, + bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"}, + readonly: true +}, { + name: "selectMoreAfter", + exec: function(editor) { editor.selectMore(1); }, + bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"}, + readonly: true +}, { + name: "selectNextBefore", + exec: function(editor) { editor.selectMore(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"}, + readonly: true +}, { + name: "selectNextAfter", + exec: function(editor) { editor.selectMore(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"}, + readonly: true +}, { + name: "splitIntoLines", + exec: function(editor) { editor.multiSelect.splitIntoLines(); }, + bindKey: {win: "Ctrl-Shift-L", mac: "Ctrl-Shift-L"}, + readonly: true +}]; + +// commands active in multiselect mode +exports.multiEditCommands = [{ + name: "singleSelection", + bindKey: "esc", + exec: function(editor) { editor.exitMultiSelectMode(); }, + readonly: true +}]; + +var HashHandler = require("../keyboard/hash_handler").HashHandler; +exports.keyboardHandler = new HashHandler(exports.multiEditCommands); + +});/* ***** 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): + * Fabian Jakobs <fabian AT ajax DOT org> + * + * 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('ace/worker/worker_client', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/config'], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var EventEmitter = require("../lib/event_emitter").EventEmitter; +var config = require("../config"); + +var WorkerClient = function(topLevelNamespaces, packagedJs, mod, classname) { + + this.changeListener = this.changeListener.bind(this); + + if (config.get("packaged")) { + this.$worker = new Worker(config.get("workerPath") + "/" + packagedJs); + } + else { + var workerUrl = this.$normalizePath(require.nameToUrl("ace/worker/worker", null, "_")); + this.$worker = new Worker(workerUrl); + + var tlns = {}; + for (var i=0; i<topLevelNamespaces.length; i++) { + var ns = topLevelNamespaces[i]; + var path = this.$normalizePath(require.nameToUrl(ns, null, "_").replace(/.js$/, "")); + + tlns[ns] = path; + } + } + + this.$worker.postMessage({ + init : true, + tlns: tlns, + module: mod, + classname: classname + }); + + this.callbackId = 1; + this.callbacks = {}; + + var _self = this; + this.$worker.onerror = function(e) { + window.console && console.log && console.log(e); + throw e; + }; + this.$worker.onmessage = function(e) { + var msg = e.data; + switch(msg.type) { + case "log": + window.console && console.log && console.log(msg.data); + break; + + case "event": + _self._emit(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, EventEmitter); + + this.$normalizePath = function(path) { + path = path.replace(/^[a-z]+:\/\/[^\/]+/, ""); // Remove domain name and rebuild it + path = location.protocol + "//" + location.host + // paths starting with a slash are relative to the root (host) + + (path.charAt(0) == "/" ? "" : location.pathname.replace(/\/[^\/]*$/, "")) + + "/" + path.replace(/^[\/]+/, ""); + return path; + }; + + this.terminate = function() { + this._emit("terminate", {}); + this.$worker.terminate(); + this.$worker = null; + this.$doc.removeEventListener("change", this.changeListener); + this.$doc = null; + }; + + this.send = function(cmd, args) { + this.$worker.postMessage({command: cmd, args: args}); + }; + + this.call = function(cmd, args, callback) { + if (callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + } + this.send(cmd, args); + }; + + this.emit = function(event, data) { + try { + // firefox refuses to clone objects which have function properties + // TODO: cleanup event + this.$worker.postMessage({event: event, data: {data: data.data}}); + } + catch(ex) {} + }; + + this.attachToDocument = function(doc) { + if(this.$doc) + this.terminate(); + + this.$doc = doc; + this.call("setValue", [doc.getValue()]); + doc.on("change", this.changeListener); + }; + + this.changeListener = function(e) { + e.range = { + start: e.data.range.start, + end: e.data.range.end + }; + this.emit("change", e); + }; + +}).call(WorkerClient.prototype); + +exports.WorkerClient = WorkerClient; + +}); +/* ***** 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 Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Julian Viereck (julian.viereck@gmail.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('ace/keyboard/state_handler', ['require', 'exports', 'module' ], function(require, exports, module) { +"use strict"; + +// If you're developing a new keymapping and want to get an idea what's going +// on, then enable debugging. +var DEBUG = false; + +function StateHandler(keymapping) { + this.keymapping = this.$buildKeymappingRegex(keymapping); +} + +StateHandler.prototype = { + /** + * Build the RegExp from the keymapping as RegExp can't stored directly + * in the metadata JSON and as the RegExp used to match the keys/buffer + * need to be adapted. + */ + $buildKeymappingRegex: function(keymapping) { + for (var state in keymapping) { + this.$buildBindingsRegex(keymapping[state]); + } + return keymapping; + }, + + $buildBindingsRegex: function(bindings) { + // Escape a given Regex string. + bindings.forEach(function(binding) { + if (binding.key) { + binding.key = new RegExp('^' + binding.key + '$'); + } else if (Array.isArray(binding.regex)) { + if (!('key' in binding)) + binding.key = new RegExp('^' + binding.regex[1] + '$'); + binding.regex = new RegExp(binding.regex.join('') + '$'); + } else if (binding.regex) { + binding.regex = new RegExp(binding.regex + '$'); + } + }); + }, + + $composeBuffer: function(data, hashId, key, e) { + // Initialize the data object. + if (data.state == null || data.buffer == null) { + data.state = "start"; + data.buffer = ""; + } + + 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 (key) keyArray.push(key); + + var symbolicName = keyArray.join("-"); + var bufferToUse = data.buffer + symbolicName; + + // Don't add the symbolic name to the key buffer if the alt_ key is + // part of the symbolic name. If it starts with alt_, this means + // that the user hit an alt keycombo and there will be a single, + // new character detected after this event, which then will be + // added to the buffer (e.g. alt_j will result in ∆). + // + // We test for 2 and not for & 2 as we only want to exclude the case where + // the option key is pressed alone. + if (hashId != 2) { + data.buffer = bufferToUse; + } + + var bufferObj = { + bufferToUse: bufferToUse, + symbolicName: symbolicName, + }; + + if (e) { + bufferObj.keyIdentifier = e.keyIdentifier + } + + return bufferObj; + }, + + $find: function(data, buffer, symbolicName, hashId, key, keyIdentifier) { + // Holds the command to execute and the args if a command matched. + var result = {}; + + // Loop over all the bindings of the keymap until a match is found. + this.keymapping[data.state].some(function(binding) { + var match; + + // Check if the key matches. + if (binding.key && !binding.key.test(symbolicName)) { + return false; + } + + // Check if the regex matches. + if (binding.regex && !(match = binding.regex.exec(buffer))) { + return false; + } + + // Check if the match function matches. + if (binding.match && !binding.match(buffer, hashId, key, symbolicName, keyIdentifier)) { + return false; + } + + // Check for disallowed matches. + if (binding.disallowMatches) { + for (var i = 0; i < binding.disallowMatches.length; i++) { + if (!!match[binding.disallowMatches[i]]) { + return false; + } + } + } + + // If there is a command to execute, then figure out the + // command and the arguments. + if (binding.exec) { + result.command = binding.exec; + + // Build the arguments. + if (binding.params) { + var value; + result.args = {}; + binding.params.forEach(function(param) { + if (param.match != null && match != null) { + value = match[param.match] || param.defaultValue; + } else { + value = param.defaultValue; + } + + if (param.type === 'number') { + value = parseInt(value); + } + + result.args[param.name] = value; + }); + } + data.buffer = ""; + } + + // Handle the 'then' property. + if (binding.then) { + data.state = binding.then; + data.buffer = ""; + } + + // If no command is set, then execute the "null" fake command. + if (result.command == null) { + result.command = "null"; + } + + if (DEBUG) { + console.log("KeyboardStateMapper#find", binding); + } + return true; + }); + + if (result.command) { + return result; + } else { + data.buffer = ""; + return false; + } + }, + + /** + * This function is called by keyBinding. + */ + handleKeyboard: function(data, hashId, key, keyCode, e) { + // 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 sense. + if (hashId != 0 && (key == "" || key == String.fromCharCode(0))) { + return null; + } + + // Compute the current value of the keyboard input buffer. + var r = this.$composeBuffer(data, hashId, key, e); + var buffer = r.bufferToUse; + var symbolicName = r.symbolicName; + var keyId = r.keyIdentifier; + + r = this.$find(data, buffer, symbolicName, hashId, key, keyId); + if (DEBUG) { + console.log("KeyboardStateMapper#match", buffer, symbolicName, r); + } + + return r; + } +} + +/** + * This is a useful matching function and therefore is defined here so that + * users of KeyboardStateMapper can use it. + * + * @return boolean + * If no command key (Command|Option|Shift|Ctrl) is pressed, it + * returns true. If the only the Shift key is pressed + a character + * true is returned as well. Otherwise, false is returned. + * Summing up, the function returns true whenever the user typed + * a normal character on the keyboard and no shortcut. + */ +exports.matchCharacterOnly = function(buffer, hashId, key, symbolicName) { + // If no command keys are pressed, then catch the input. + if (hashId == 0) { + return true; + } + // If only the shift key is pressed and a character key, then + // catch that input as well. + else if ((hashId == 4) && key.length == 1) { + return true; + } + // Otherwise, we let the input got through. + else { + return false; + } +}; + +exports.StateHandler = StateHandler; +}); +/* ***** 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): + * Zef Hemel <zef@c9.io> + * + * 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('ace/placeholder', ['require', 'exports', 'module' , 'ace/range', 'ace/lib/event_emitter', 'ace/lib/oop'], function(require, exports, module) { +"use strict"; + +var Range = require('./range').Range; +var EventEmitter = require("./lib/event_emitter").EventEmitter; +var oop = require("./lib/oop"); + +var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) { + var _self = this; + this.length = length; + this.session = session; + this.doc = session.getDocument(); + this.mainClass = mainClass; + this.othersClass = othersClass; + this.$onUpdate = this.onUpdate.bind(this); + this.doc.on("change", this.$onUpdate); + this.$others = others; + + this.$onCursorChange = function() { + setTimeout(function() { + _self.onCursorChange(); + }); + }; + + this.$pos = pos; + // Used for reset + var undoStack = session.getUndoManager().$undoStack || session.getUndoManager().$undostack || {length: -1}; + this.$undoStackDepth = undoStack.length; + this.setup(); + + session.selection.on("changeCursor", this.$onCursorChange); +}; + +(function() { + + oop.implement(this, EventEmitter); + + this.setup = function() { + var _self = this; + var doc = this.doc; + var session = this.session; + var pos = this.$pos; + + this.pos = doc.createAnchor(pos.row, pos.column); + this.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + this.length), this.mainClass, null, false); + this.pos.on("change", function(event) { + session.removeMarker(_self.markerId); + _self.markerId = session.addMarker(new Range(event.value.row, event.value.column, event.value.row, event.value.column+_self.length), _self.mainClass, null, false); + }); + this.others = []; + this.$others.forEach(function(other) { + var anchor = doc.createAnchor(other.row, other.column); + _self.others.push(anchor); + }); + session.setUndoSelect(false); + }; + + this.showOtherMarkers = function() { + if(this.othersActive) return; + var session = this.session; + var _self = this; + this.othersActive = true; + this.others.forEach(function(anchor) { + anchor.markerId = session.addMarker(new Range(anchor.row, anchor.column, anchor.row, anchor.column+_self.length), _self.othersClass, null, false); + anchor.on("change", function(event) { + session.removeMarker(anchor.markerId); + anchor.markerId = session.addMarker(new Range(event.value.row, event.value.column, event.value.row, event.value.column+_self.length), _self.othersClass, null, false); + }); + }); + }; + + this.hideOtherMarkers = function() { + if(!this.othersActive) return; + this.othersActive = false; + for (var i = 0; i < this.others.length; i++) { + this.session.removeMarker(this.others[i].markerId); + } + }; + + this.onUpdate = function(event) { + var delta = event.data; + var range = delta.range; + if(range.start.row !== range.end.row) return; + if(range.start.row !== this.pos.row) return; + if (this.$updating) return; + this.$updating = true; + var lengthDiff = delta.action === "insertText" ? range.end.column - range.start.column : range.start.column - range.end.column; + + if(range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1) { + var distanceFromStart = range.start.column - this.pos.column; + this.length += lengthDiff; + if(!this.session.$fromUndo) { + if(delta.action === "insertText") { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + if(otherPos.row === range.start.row && range.start.column < otherPos.column) + newPos.column += lengthDiff; + this.doc.insert(newPos, delta.text); + } + } else if(delta.action === "removeText") { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + if(otherPos.row === range.start.row && range.start.column < otherPos.column) + newPos.column += lengthDiff; + this.doc.remove(new Range(newPos.row, newPos.column, newPos.row, newPos.column - lengthDiff)); + } + } + // Special case: insert in beginning + if(range.start.column === this.pos.column && delta.action === "insertText") { + setTimeout(function() { + this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff); + for (var i = 0; i < this.others.length; i++) { + var other = this.others[i]; + var newPos = {row: other.row, column: other.column - lengthDiff}; + if(other.row === range.start.row && range.start.column < other.column) + newPos.column += lengthDiff; + other.setPosition(newPos.row, newPos.column); + } + }.bind(this), 0); + } + else if(range.start.column === this.pos.column && delta.action === "removeText") { + setTimeout(function() { + for (var i = 0; i < this.others.length; i++) { + var other = this.others[i]; + if(other.row === range.start.row && range.start.column < other.column) { + other.setPosition(other.row, other.column - lengthDiff); + } + } + }.bind(this), 0); + } + } + this.pos._emit("change", {value: this.pos}); + for (var i = 0; i < this.others.length; i++) { + this.others[i]._emit("change", {value: this.others[i]}); + } + } + this.$updating = false; + }; + + this.onCursorChange = function(event) { + if (this.$updating) return; + var pos = this.session.selection.getCursor(); + if(pos.row === this.pos.row && pos.column >= this.pos.column && pos.column <= this.pos.column + this.length) { + this.showOtherMarkers(); + this._emit("cursorEnter", event); + } else { + this.hideOtherMarkers(); + this._emit("cursorLeave", event); + } + }; + + this.detach = function() { + this.session.removeMarker(this.markerId); + this.hideOtherMarkers(); + this.doc.removeEventListener("change", this.$onUpdate); + this.session.selection.removeEventListener("changeCursor", this.$onCursorChange); + this.pos.detach(); + for (var i = 0; i < this.others.length; i++) { + this.others[i].detach(); + } + this.session.setUndoSelect(true); + }; + + this.cancel = function() { + if(this.$undoStackDepth === -1) + throw Error("Canceling placeholders only supported with undo manager attached to session."); + var undoManager = this.session.getUndoManager(); + var undosRequired = (undoManager.$undoStack || undoManager.$undostack).length - this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(true); + } + }; +}).call(PlaceHolder.prototype); + + +exports.PlaceHolder = PlaceHolder; +}); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -14055,7 +16460,7 @@ exports.cssText = ".ace-tm .ace_editor {\ }\ \ .ace-tm .ace_fold {\ - background-color: #0000A2;\ + background-color: #6B72E6;\ }\ \ .ace-tm .ace_text-layer {\ @@ -14063,7 +16468,7 @@ exports.cssText = ".ace-tm .ace_editor {\ }\ \ .ace-tm .ace_cursor {\ - border-left: 2px solid black;\ + border-left: 1px solid black;\ }\ \ .ace-tm .ace_cursor.ace_overwrite {\ @@ -14075,10 +16480,15 @@ exports.cssText = ".ace-tm .ace_editor {\ color: rgb(191, 191, 191);\ }\ \ +.ace-tm .ace_line .ace_storage,\ .ace-tm .ace_line .ace_keyword {\ color: blue;\ }\ \ +.ace-tm .ace_line .ace_constant {\ + color: rgb(197, 6, 11);\ +}\ +\ .ace-tm .ace_line .ace_constant.ace_buildin {\ color: rgb(88, 72, 246);\ }\ |