diff --git a/Makefile.dryice.textarea.js b/Makefile.dryice.textarea.js new file mode 100755 index 00000000..d7c337bc --- /dev/null +++ b/Makefile.dryice.textarea.js @@ -0,0 +1,194 @@ +#!/usr/bin/env node +/* ***** 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 + * Julian Viereck + * + * 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 ***** */ + +var copy = require('dryice').copy; + +var aceHome = __dirname; + +function shadow(input) { + console.log("shadow input"); + if (typeof input !== 'string') { + input = input.toString(); + } + + return input.replace(/define\(/g, "__ace_shadowed__.define("); +} + +console.log('# ace ---------'); + +var project = copy.createCommonJsProject([ + aceHome + '/support/pilot/lib', + aceHome + '/lib' +]); + +copy({ + source: "build_support/editor_textarea.html", + dest: 'build/editor.html' +}); + +var ace = copy.createDataObject(); +copy({ + source: [ + 'build_support/mini_require_textarea.js' + ], + dest: ace +}); +copy({ + source: [ + copy.source.commonjs({ + project: project, + require: [ + "pilot/fixoldbrowsers", + "pilot/index", + "pilot/plugin_manager", + "pilot/environment", + "ace/editor", + "ace/edit_session", + "ace/undomanager", + "ace/theme/textmate", + "ace/mode/text", + "ace/mode/matching_brace_outdent", + "ace/virtual_renderer" + ] + }) + ], + filter: [ copy.filter.moduleDefines ], + dest: ace +}); +copy({ + source: { + root: project, + include: /.*\.css$|.*\.html$/, + exclude: /tests?\// + }, + filter: [ copy.filter.addDefines ], + dest: ace +}); +copy({ + source: { + root: project, + include: /.*\.png$|.*\.gif$/, + exclude: /tests?\// + }, + filter: [ copy.filter.base64 ], + dest: ace +}); +copy({ + source: [ + 'build_support/boot_textarea.js' + ], + dest: ace +}); + +// Create the compressed and uncompressed output files +copy({ + source: ace, + filter: [ + shadow, + copy.filter.uglifyjs + ], + dest: 'build/src/ace.js' +}); +copy({ + source: ace, + filter: [ + shadow, + ], + dest: 'build/src/ace-uncompressed.js' +}); + +console.log('# ace modes ---------'); + +// create modes +project.assumeAllFilesLoaded(); +["css", "html", "javascript", "php", "python", "xml", "ruby", "java", "c_cpp", "coffee"].forEach(function(mode) { + console.log("mode " + mode); + copy({ + source: [ + copy.source.commonjs({ + project: project.clone(), + require: [ 'ace/mode/' + mode ] + }) + ], + filter: [ + copy.filter.moduleDefines, + shadow, + copy.filter.uglifyjs + ], + dest: "build/src/mode-" + mode + ".js" + }); +}); + +console.log('# ace themes ---------'); + +// create themes +[ + "clouds", "clouds_midnight", "cobalt", "dawn", "idle_fingers", "kr_theme", + "mono_industrial", "monokai", "pastel_on_dark", "twilight" +].forEach(function(theme) { + console.log("theme " + theme); + copy({ + source: [{ + root: aceHome + '/lib', + include: "ace/theme/" + theme + ".js" + }], + filter: [ + copy.filter.moduleDefines, + shadow, + copy.filter.uglifyjs + ], + dest: "build/src/theme-" + theme + ".js" + }); +}); + +console.log('# License | Readme | Changelog ---------'); + +// copy text files +copy({ + source: aceHome + "/LICENSE", + dest: 'build/LICENSE' +}); +copy({ + source: aceHome + "/Readme.md", + dest: 'build/Readme.md' +}); +copy({ + source: aceHome + "/ChangeLog.txt", + dest: 'build/ChangeLog.txt' +}); diff --git a/build_support/boot_textarea.js b/build_support/boot_textarea.js new file mode 100644 index 00000000..56e272df --- /dev/null +++ b/build_support/boot_textarea.js @@ -0,0 +1,459 @@ +/* ***** 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): + * Kevin Dangoor (kdangoor@mozilla.com) + * Julian Viereck + * + * 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 ***** */ + +(function() { + +var require = window.__ace_shadowed__.require; +var deps = [ + "pilot/fixoldbrowsers", + "pilot/index", + "pilot/plugin_manager", + "pilot/environment", + "ace/editor", + "ace/edit_session", + "ace/virtual_renderer", + "ace/undomanager", + "ace/theme/textmate" +]; + +require(deps, function() { + var catalog = require("pilot/plugin_manager").catalog; + catalog.registerPlugins([ "pilot/index" ]); + + var Dom = require("pilot/dom"); + var Event = require("pilot/event"); + + var Editor = require("ace/editor").Editor; + var EditSession = require("ace/edit_session").EditSession; + var UndoManager = require("ace/undomanager").UndoManager; + var Renderer = require("ace/virtual_renderer").VirtualRenderer; + + window.__ace_shadowed__.edit = function(el) { + if (typeof(el) == "string") { + el = document.getElementById(el); + } + + var doc = new EditSession(Dom.getInnerText(el)); + doc.setUndoManager(new UndoManager()); + el.innerHTML = ''; + + var editor = new Editor(new Renderer(el, "ace/theme/textmate")); + editor.setSession(doc); + + var env = require("pilot/environment").create(); + catalog.startupPlugins({ env: env }).then(function() { + env.document = doc; + env.editor = env; + editor.resize(); + Event.addListener(window, "resize", function() { + editor.resize(); + }); + el.env = env; + }); + return editor; + } + + if (window.__ace_shadowed_loaded__) { + window.__ace_shadowed_loaded__(); + } +}); + +/** + * Returns the CSS property of element. + * 1) If the CSS property is on the style object of the element, use it, OR + * 2) Compute the CSS property + * + * If the property can't get computed, is 'auto' or 'intrinsic', the former + * calculated property is uesd (this can happen in cases where the textarea + * is hidden and has no dimension styles). + */ +var getCSSProperty = function(element, container, property) { + var ret = element.style[property] + || document.defaultView.getComputedStyle(element, ''). + getPropertyValue(property); + + if (!ret || ret == 'auto' || ret == 'intrinsic') { + ret = container.style[property]; + } + return ret; +}; + +function applyStyles(elm, styles) { + for (style in styles) { + elm.style[style] = styles[style]; + } +} + +function setupContainer(element, getValue) { + if (element.type != 'textarea') { + throw "Textarea required!"; + } + + var parentNode = element.parentNode; + + // This will hold the Bespin editor. + var container = document.createElement('div'); + + // To put Bespin in the place of the textarea, we have to copy a + // few of the textarea's style attributes to the div container. + // + // The problem is, that the properties have to get computed (they + // might be defined by a CSS file on the page - you can't access + // such rules that apply to an element via elm.style). Computed + // properties are converted to pixels although the dimension might + // be given as percentage. When the window resizes, the dimensions + // defined by percentages changes, so the properties have to get + // recomputed to get the new/true pixels. + var resizeEvent = function() { + var style = 'position:relative;'; + [ + 'margin-top', 'margin-left', 'margin-right', 'margin-bottom' + ].forEach(function(item) { + style += item + ':' + + getCSSProperty(element, container, item) + ';'; + }); + + // Calculating the width/height of the textarea is somewhat + // tricky. To do it right, you have to include the paddings + // to the sides as well (eg. width = width + padding-left, -right). + // This works well, as long as the width of the element is not + // set or given in pixels. In this case and after the textarea + // is hidden, getCSSProperty(element, container, 'width') will + // still return pixel value. If the element has realtiv dimensions + // (e.g. width='95') getCSSProperty(...) will return pixel values + // only as long as the textarea is visible. After it is hidden + // getCSSProperty will return the relativ dimensions as they + // are set on the element (in the case of width, 95). + // Making the sum of pixel vaules (e.g. padding) and realtive + // values (e.g. ) is not possible. As such the padding styles + // are ignored. + + // The complete width is the width of the textarea + the padding + // to the left and right. + var width = getCSSProperty(element, container, 'width'); + var height = getCSSProperty(element, container, 'height'); + style += 'height:' + height + ';width:' + width + ';'; + + // Set the display property to 'inline-block'. + style += 'display:inline-block;'; + container.setAttribute('style', style); + }; + window.addEventListener('resize', resizeEvent, false); + + // Call the resizeEvent once, so that the size of the container is + // calculated. + resizeEvent(); + + // Insert the div container after the element. + if (element.nextSibling) { + parentNode.insertBefore(container, element.nextSibling); + } else { + parentNode.appendChild(container); + } + + // Override the forms onsubmit function. Set the innerHTML and value + // of the textarea before submitting. + while (parentNode !== document) { + if (parentNode.tagName.toUpperCase() === 'FORM') { + var oldSumit = parentNode.onsubmit; + // Override the onsubmit function of the form. + parentNode.onsubmit = function(evt) { + element.value = getValue(); + element.innerHTML = getValue(); + // If there is a onsubmit function already, then call + // it with the current context and pass the event. + if (oldSumit) { + oldSumit.call(this, evt); + } + } + break; + } + parentNode = parentNode.parentNode; + } + return container; +} + +window.__ace_shadowed__.transformTextarea = function(element) { + var session; + var container = setupContainer(element, function() { + return session.getValue(); + }); + + // Hide the element. + element.style.display = 'none'; + container.style.background = 'white'; + + // + var editorDiv = document.createElement("div"); + applyStyles(editorDiv, { + top: "0px", + left: "0px", + right: "0px", + bottom: "0px" + }); + container.appendChild(editorDiv); + + var settingOpener = document.createElement("div"); + applyStyles(settingOpener, { + position: "absolute", + width: "15px", + right: "0px", + bottom: "0px", + background: "red", + cursor: "pointer", + textAlign: "center", + fontSize: "12px" + }); + settingOpener.innerHTML = "I"; + + var settingDiv = document.createElement("div"); + applyStyles(settingDiv, { + top: "0px", + left: "0px", + right: "0px", + bottom: "0px", + position: "absolute", + padding: "5px", + background: "rgba(0, 0, 0, 0.6)", + zIndex: 100, + color: "white", + display: "none" + }); + container.appendChild(settingDiv); + + // Power up ace on the textarea: + var ace = window.__ace_shadowed__; + var require = ace.require; + var define = ace.define; + var options = {}; + + var editor = ace.edit(editorDiv); + session = editor.getSession(); + + session.setValue(element.value || element.innerHTML); + editor.focus(); + + // Add the settingPanel opener to the editor's div. + editorDiv.appendChild(settingOpener); + + // Create the API. + var api = setupApi(editor, editorDiv, settingDiv, ace, options) + + // Create the setting's panel. + setupSettingPanel(settingDiv, settingOpener, api, options); + + return api; +} + +function setupApi(editor, editorDiv, settingDiv, ace, options) { + var load = ace.load; + var session = editor.getSession(); + var renderer = editor.renderer; + + function toBool(value) { + return value == "true"; + } + + var ret = { + setDisplaySettings: function(display) { + settingDiv.style.display = display ? "block" : "none"; + }, + + setOption: function(key, value) { + if (options[key] == value) return; + + switch (key) { + case "gutter": + renderer.setShowGutter(toBool(value)); + break; + + case "mode": + if (value != "text") { + // Load the required mode file. Files get loaded only once. + load("mode-" + value + ".js", function() { + var aceMode = require("ace/mode/" + value).Mode; + session.setMode(new aceMode()); + }); + } else { + session.setMode(new (require("ace/mode/text").Mode)); + } + break; + + case "theme": + if (value != "textmate") { + // Load the required theme file. Files get loaded only once. + load("theme-" + value + ".js", function() { + editor.setTheme("ace/theme/" + value); + }); + } else { + editor.setTheme("ace/theme/textmate"); + } + break; + + case "fontSize": + editorDiv.style.fontSize = value; + break; + } + + options[key] = value; + }, + + getOption: function(key) { + return options[key]; + }, + + getOptions: function() { + return options; + } + } + + for (option in ace.options) { + ret.setOption(option, ace.options[option]); + } + + return ret; +} + +function setupSettingPanel(settingDiv, settingOpener, api, options) { + var BOOL = { + "true": true, + "false": false + } + + var desc = { + mode: "Mode:", + gutter: "Display Gutter:", + theme: "Theme:", + fontSize: "Font Size:" + } + + var optionValues = { + mode: { + text: "Plain", + javascript: "JavaScript", + coffee: "CoffeeScript", + html: "HTML", + css: "CSS", + c_cpp: "C++", + php: "PHP", + ruby: "Ruby", + python: "Python" + + }, + theme: { + textmate: "Textmate", + eclipse: "Eclipse", + clouds: "Clouds", + clouds_midnight: "Clouds Midnight", + cobalt: "Cobalt", + dawn: "Dawn", + idle_fingers: "Idle Fingers", + kr_theme: "Kr Theme", + mono_industrial: "Mono Industrial", + monokai: "Monokai", + pastel_on_dark: "Pastel On Dark", + twilight: "Twilight", + }, + gutter: BOOL, + fontSize: { + "10px": "10px", + "12px": "12px", + "14px": "14px", + "16px": "16px" + } + } + + var table = []; + table.push(""); + + function renderOption(builder, option, obj, cValue) { + builder.push("") + } + + for (var option in options) { + table.push(""); + table.push(""); + } + table.push("
SettingValue
", desc[option], ""); + renderOption(table, option, optionValues[option], options[option]); + table.push("
"); + settingDiv.innerHTML = table.join(""); + + var selects = settingDiv.querySelectorAll("select"); + for (var i = 0; i < selects.length; i++) { + selects[i].onchange = function(e) { + var option = e.target.title; + var value = e.target.value; + api.setOption(option, value); + } + } + + var button = document.createElement("input"); + button.type = "button"; + button.value = "Hide"; + button.onclick = function() { + api.setDisplaySettings(false); + } + settingDiv.appendChild(button); + + settingOpener.onclick = function() { + api.setDisplaySettings(true); + } +} + +// Default startup options. +window.__ace_shadowed__.options = { + mode: "text", + theme: "textmate", + gutter: "false", + fontSize: "12px" +} + +})() diff --git a/build_support/editor_textarea.html b/build_support/editor_textarea.html new file mode 100644 index 00000000..1a5bf4a1 --- /dev/null +++ b/build_support/editor_textarea.html @@ -0,0 +1,95 @@ + + + + + + Editor + + + +
+SourceUrl: + +
+ + + + + diff --git a/build_support/mini_require_textarea.js b/build_support/mini_require_textarea.js new file mode 100644 index 00000000..9161bcf6 --- /dev/null +++ b/build_support/mini_require_textarea.js @@ -0,0 +1,128 @@ +/* ***** 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 + * Julian Viereck + * + * 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 a module along with a payload + * @param module a name for the payload + * @param payload a function to call with (require, exports, module) params + */ + +(function() { + +var _define = function(module, payload) { + if (typeof module !== 'string') { + if (_define.original) + _define.original.apply(window, arguments); + else { + console.error('dropping module because define wasn\'t a string.'); + console.trace(); + } + return; + } + + if (!_define.modules) + _define.modules = {}; + + _define.modules[module] = payload; +}; + +/** + * Get at functionality define()ed using the function above + */ +var _require = function(module, callback) { + if (Object.prototype.toString.call(module) === "[object Array]") { + var params = []; + for (var i = 0, l = module.length; i < l; ++i) { + var dep = lookup(module[i]); + if (!dep && _require.original) + return _require.original.apply(window, arguments); + params.push(dep); + }; + if (callback) { + callback.apply(null, params); + } + } + + if (typeof module === 'string') { + var payload = lookup(module); + if (!payload && _require.original) + return _require.original.apply(window, arguments); + + if (callback) { + callback(); + } + + return payload; + }; +} + +_require.packaged = true; +_require.noWorker = true; + +/** + * Internal function to lookup moduleNames and resolve them by calling the + * definition function if needed. + */ +var lookup = function(moduleName) { + var module = _define.modules[moduleName]; + if (module == null) { + console.error('Missing module: ' + moduleName); + return null; + } + + if (typeof module === 'function') { + var exports = {}; + module(_require, exports, { id: moduleName, uri: '' }); + // cache the resulting module object for next time + _define.modules[moduleName] = exports; + return exports; + } + + return module; +}; + +/** + * Expose as "shadowed" object to the outside world. + */ + +window.__ace_shadowed__ = { + require: _require, + define: _define +}; + +})(); \ No newline at end of file diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 2d62f826..6526d0fd 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -122,6 +122,7 @@ } .ace_marker-layer { + cursor: text; } .ace_marker-layer .ace_step { diff --git a/lib/ace/document.js b/lib/ace/document.js index f5d6b63a..490ee225 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -67,7 +67,7 @@ var Document = function(text) { }; this.getValue = function() { - return this.$lines.join(this.getNewLineCharacter()); + return this.getAllLines().join(this.getNewLineCharacter()); }; // check for IE split bug @@ -123,11 +123,11 @@ var Document = function(text) { * Get a verbatim copy of the given line as it is in the document */ this.getLine = function(row) { - return this.$lines[row] || ""; + return this.getLines(row, row + 1)[0] || ""; }; this.getLines = function(firstRow, lastRow) { - return this.$lines.slice(firstRow, lastRow+1); + return this.$lines.slice(firstRow, lastRow + 1); }; /** @@ -135,7 +135,7 @@ var Document = function(text) { * should not modify this array! */ this.getAllLines = function() { - return this.$lines; + return this.getLines(0, this.getLength()); }; this.getLength = function() { @@ -183,14 +183,29 @@ var Document = function(text) { var end = this.insertInLine(position, text); } else { - var end = this.insertInLine(position, newLines[0]); - this.insertNewLine(end); - if (newLines.length > 2) - this.insertLines(position.row+1, newLines.slice(1, newLines.length-1)); - - var end = this.insertInLine({row: position.row + newLines.length - 1, column: 0}, newLines[newLines.length-1]); + if (newLines[0].length > 0) { + var end = this.insertInLine(position, newLines[0]); + this.insertNewLine(end); + } + // If we are inserting at the end of the document, we don't need to + // use insertInLine (concorde depends on this optimization!) + if (position.row + 1 == this.getLength()) { + this.insertLines(position.row + 1, + newLines.slice(1, newLines.length)); + var end = { + row: position.row + newLines.length - 1, + column: position.column + newLines[newLines.length - 1].length + }; + } else { + if (newLines.length > 2) + this.insertLines(position.row + 1, + newLines.slice(1, newLines.length - 1)); + var end = this.insertInLine({ + row: position.row + newLines.length - 1, + column: 0 + }, newLines[newLines.length - 1]); + } } - return end; }; @@ -278,7 +293,7 @@ var Document = function(text) { this.removeLines(firstFullRow, lastFullRow); if (firstFullRow != firstRow) { - this.removeInLine(firstRow, range.start.column, this.$lines[firstRow].length); + this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length); this.removeNewLine(range.start.row); } } diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index eeb9feef..f7ffd251 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -209,7 +209,7 @@ var EditSession = function(text, mode) { this.addMarker = function(range, clazz, type, inFront) { var id = this.$markerId++; - + var marker = { range : range, type : type || "line", @@ -217,7 +217,7 @@ var EditSession = function(text, mode) { clazz : clazz, inFront: !!inFront } - + if (inFront) { this.$frontMarkers[id] = marker; this._dispatchEvent("changeFrontMarker") @@ -225,26 +225,26 @@ var EditSession = function(text, mode) { this.$backMarkers[id] = marker; this._dispatchEvent("changeBackMarker") } - + return id; }; - + this.removeMarker = function(markerId) { var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId]; if (!marker) return; - + var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers; if (marker) { delete (markers[markerId]); this._dispatchEvent(marker.inFront ? "changeFrontMarker" : "changeBackMarker"); } }; - + this.getMarkers = function(inFront) { return inFront ? this.$frontMarkers : this.$backMarkers; }; - + /** * Error: * { @@ -334,7 +334,7 @@ var EditSession = function(text, mode) { if (this.$worker) this.$worker.terminate(); - if (window.Worker) + if (window.Worker && !require.noWorker) this.$worker = mode.createWorker(this); else this.$worker = null; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index b101c173..3d43f07d 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -824,68 +824,66 @@ var Editor =function(renderer, session) { return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); }; - this.getVisibleRowCount = function() { - return this.getLastVisibleRow() - this.getFirstVisibleRow() + 1; + this.$getVisibleRowCount = function() { + return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1; }; - this.getPageDownRow = function() { - return this.renderer.getLastVisibleRow() - 1; + this.$getPageDownRow = function() { + return this.renderer.getScrollBottomRow(); }; - this.getPageUpRow = function() { - var firstRow = this.renderer.getFirstVisibleRow(); - var lastRow = this.renderer.getLastVisibleRow(); + this.$getPageUpRow = function() { + var firstRow = this.renderer.getScrollTopRow(); + var lastRow = this.renderer.getScrollBottomRow(); - return firstRow - (lastRow - firstRow) + 1; + return firstRow - (lastRow - firstRow); }; this.selectPageDown = function() { - var row = this.getPageDownRow() + Math.floor(this.getVisibleRowCount() / 2); + var row = this.$getPageDownRow() + Math.floor(this.$getVisibleRowCount() / 2); this.scrollPageDown(); var selection = this.getSelection(); - selection.$moveSelection(function() { - selection.moveCursorTo(row, selection.getSelectionLead().column); - }); + var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead()); + var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column); + selection.selectTo(dest.row, dest.column); }; this.selectPageUp = function() { - var visibleRows = this.getLastVisibleRow() - this.getFirstVisibleRow(); - var row = this.getPageUpRow() + Math.round(visibleRows / 2); + var visibleRows = this.renderer.getScrollTopRow() - this.renderer.getScrollBottomRow(); + var row = this.$getPageUpRow() + Math.round(visibleRows / 2); this.scrollPageUp(); var selection = this.getSelection(); - selection.$moveSelection(function() { - selection.moveCursorTo(row, selection.getSelectionLead().column); - }); + var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead()); + var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column); + selection.selectTo(dest.row, dest.column); }; this.gotoPageDown = function() { - var row = this.getPageDownRow(), - column = Math.min(this.getCursorPosition().column, - this.session.getLine(row).length); + var row = this.$getPageDownRow(); + var column = this.getCursorPositionScreen().column; this.scrollToRow(row); - this.getSelection().moveCursorTo(row, column); + this.getSelection().moveCursorToScreen(row, column); }; this.gotoPageUp = function() { - var row = this.getPageUpRow(), - column = Math.min(this.getCursorPosition().column, - this.session.getLine(row).length); + var row = this.$getPageUpRow(); + var column = this.getCursorPositionScreen().column; this.scrollToRow(row); - this.getSelection().moveCursorTo(row, column); + this.getSelection().moveCursorToScreen(row, column); }; this.scrollPageDown = function() { - this.scrollToRow(this.getPageDownRow()); + this.scrollToRow(this.$getPageDownRow()); }; this.scrollPageUp = function() { - this.renderer.scrollToRow(this.getPageUpRow()); + this.renderer.scrollToRow(this.$getPageUpRow()); }; this.scrollToRow = function(row) { @@ -906,6 +904,10 @@ var Editor =function(renderer, session) { return this.selection.getCursor(); }; + this.getCursorPositionScreen = function() { + return this.session.documentToScreenPosition(this.getCursorPosition()); + } + this.getSelectionRange = function() { return this.selection.getRange(); }; diff --git a/lib/ace/selection.js b/lib/ace/selection.js index a130228d..408f044a 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -408,6 +408,15 @@ var Selection = function(session) { this.$updateDesiredColumn(this.selectionLead.column); }; + this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) { + if (this.session.getUseWrapMode()) { + var pos = this.session.screenToDocumentPosition(row, column); + row = pos.row; + column = pos.column; + } + this.moveCursorTo(row, column, preventUpdateDesiredColumn); + }; + }).call(Selection.prototype); exports.Selection = Selection; diff --git a/lib/ace/test/all.js b/lib/ace/test/all.js index 81140835..d591534e 100644 --- a/lib/ace/test/all.js +++ b/lib/ace/test/all.js @@ -37,13 +37,14 @@ require("../../../support/paths"); -require("./mockdom"); +// require("./mockdom"); var async = require("asyncjs"); async.concat( - require("./anchor_test"), - require("./change_document_test"), - require("./document_test"), + // require("./anchor_test"), + // require("./change_document_test"), + require("./document_test") + /* require("./edit_session_test"), require("./event_emitter_test"), require("./navigation_test"), @@ -60,4 +61,5 @@ async.concat( require("./mode/text_test"), require("./mode/xml_test"), require("./mode/xml_tokenizer_test") + */ ).exec(); diff --git a/lib/ace/test/document_test.js b/lib/ace/test/document_test.js index b9249e8f..c4566331 100644 --- a/lib/ace/test/document_test.js +++ b/lib/ace/test/document_test.js @@ -38,7 +38,8 @@ define(function(require, exports, module) { -var Document = require("../document").Document, +var Document = require("../../../../../lib/concorde/Document"), + AceAdapter = require("../../../../../lib/concorde/AceAdapter"), Range = require("../range").Range, assert = require("./assertions"), async = require("asyncjs"); @@ -46,7 +47,7 @@ var Document = require("../document").Document, var Test = { "test: insert text in line" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -63,7 +64,7 @@ var Test = { }, "test: insert new line" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -80,7 +81,7 @@ var Test = { }, "test: insert lines at the beginning" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -97,7 +98,7 @@ var Test = { }, "test: insert lines at the end" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -107,7 +108,7 @@ var Test = { }, "test: insert lines in the middle" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -124,7 +125,7 @@ var Test = { }, "test: insert multi line string at the start" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -141,7 +142,7 @@ var Test = { }, "test: insert multi line string at the end" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -158,7 +159,7 @@ var Test = { }, "test: insert multi line string in the middle" : function() { - var doc = new Document(["12", "34"]); + var doc = new AceAdapter(new Document(["12", "34"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -175,7 +176,7 @@ var Test = { }, "test: delete in line" : function() { - var doc = new Document(["1234", "5678"]); + var doc = new AceAdapter(new Document(["1234", "5678"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -192,7 +193,7 @@ var Test = { }, "test: delete new line" : function() { - var doc = new Document(["1234", "5678"]); + var doc = new AceAdapter(new Document(["1234", "5678"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -209,7 +210,7 @@ var Test = { }, "test: delete multi line range line" : function() { - var doc = new Document(["1234", "5678", "abcd"]); + var doc = new AceAdapter(new Document(["1234", "5678", "abcd"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -220,13 +221,13 @@ var Test = { var d = deltas.concat(); doc.revertDeltas(d); assert.equal(doc.getValue(), ["1234", "5678", "abcd"].join("\n")); - + doc.applyDeltas(d); assert.equal(doc.getValue(), ["12cd"].join("\n")); }, "test: delete full lines" : function() { - var doc = new Document(["1234", "5678", "abcd"]); + var doc = new AceAdapter(new Document(["1234", "5678", "abcd"])); var deltas = []; doc.on("change", function(e) { deltas.push(e.data); }); @@ -236,44 +237,44 @@ var Test = { }, "test: remove lines should return the removed lines" : function() { - var doc = new Document(["1234", "5678", "abcd"]); + var doc = new AceAdapter(new Document(["1234", "5678", "abcd"])); var removed = doc.removeLines(1, 2); assert.equal(removed.join("\n"), ["5678", "abcd"].join("\n")); }, "test: should handle unix style new lines" : function() { - var doc = new Document(["1", "2", "3"]); + var doc = new AceAdapter(new Document(["1", "2", "3"])); assert.equal(doc.getValue(), ["1", "2", "3"].join("\n")); }, "test: should handle windows style new lines" : function() { - var doc = new Document(["1", "2", "3"].join("\r\n")); + var doc = new AceAdapter(new Document(["1", "2", "3"].join("\r\n"))); doc.setNewLineMode("unix"); assert.equal(doc.getValue(), ["1", "2", "3"].join("\n")); }, "test: set new line mode to 'windows' should use '\r\n' as new lines": function() { - var doc = new Document(["1", "2", "3"].join("\n")); + var doc = new AceAdapter(new Document(["1", "2", "3"].join("\n"))); doc.setNewLineMode("windows"); assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n")); }, "test: set new line mode to 'unix' should use '\n' as new lines": function() { - var doc = new Document(["1", "2", "3"].join("\r\n")); + var doc = new AceAdapter(new Document(["1", "2", "3"].join("\r\n"))); doc.setNewLineMode("unix"); assert.equal(doc.getValue(), ["1", "2", "3"].join("\n")); }, "test: set new line mode to 'auto' should detect the incoming nl type": function() { - var doc = new Document(["1", "2", "3"].join("\n")); + var doc = new AceAdapter(new Document(["1", "2", "3"].join("\n"))); doc.setNewLineMode("auto"); assert.equal(doc.getValue(), ["1", "2", "3"].join("\n")); - var doc = new Document(["1", "2", "3"].join("\r\n")); + var doc = new AceAdapter(new Document(["1", "2", "3"].join("\r\n"))); doc.setNewLineMode("auto"); assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n")); @@ -283,13 +284,13 @@ var Test = { }, "test: set value": function() { - var doc = new Document("1"); + var doc = new AceAdapter(new Document("1")); assert.equal("1", doc.getValue()); doc.setValue(doc.getValue()); assert.equal("1", doc.getValue()); - var doc = new Document("1\n2"); + var doc = new AceAdapter(new Document("1\n2")); assert.equal("1\n2", doc.getValue()); doc.setValue(doc.getValue()); @@ -297,8 +298,8 @@ var Test = { }, "test: empty document has to contain one line": function() { - var doc = new Document(""); - assert.equal(doc.$lines.length, 1); + var doc = new AceAdapter(new Document("")); + assert.equal(doc.getLength(), 1); } }; diff --git a/lib/ace/test/mockrenderer.js b/lib/ace/test/mockrenderer.js index 8d931483..9eb2b5c1 100644 --- a/lib/ace/test/mockrenderer.js +++ b/lib/ace/test/mockrenderer.js @@ -94,7 +94,17 @@ MockRenderer.prototype.updateCursor = function(position) { this.cursor.column = position.column; }; -MockRenderer.prototype.scrollToLine = function(row) { +MockRenderer.prototype.scrollToLine = function(line, center) { + var lineHeight = { lineHeight: 16 }; + var row = 0; + for (var l = 1; l < line; l++) { + row += this.session.getRowHeight(lineHeight, l-1) / lineHeight.lineHeight; + } + + if (center) { + row -= this.visibleRowCount / 2; + } + this.scrollToRow(row); }; MockRenderer.prototype.scrollCursorIntoView = function() { diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 573aaf58..6be72b88 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -597,6 +597,10 @@ var VirtualRenderer = function(container, theme) { return this.scrollTop / this.lineHeight; }; + this.getScrollBottomRow = function() { + return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1); + } + this.scrollToRow = function(row) { this.scrollToY(row * this.lineHeight); };