| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695 |
- /* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Ajax.org B.V. nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
- define(function(require, exports, module) {
- "use strict";
- var oop = require("./lib/oop");
- var dom = require("./lib/dom");
- var useragent = require("./lib/useragent");
- var config = require("./config");
- var GutterLayer = require("./layer/gutter").Gutter;
- var MarkerLayer = require("./layer/marker").Marker;
- var TextLayer = require("./layer/text").Text;
- var CursorLayer = require("./layer/cursor").Cursor;
- var ScrollBarH = require("./scrollbar").ScrollBarH;
- var ScrollBarV = require("./scrollbar").ScrollBarV;
- var RenderLoop = require("./renderloop").RenderLoop;
- var EventEmitter = require("./lib/event_emitter").EventEmitter;
- var editorCss = require("./requirejs/text!./css/editor.css");
- dom.importCssString(editorCss, "ace_editor");
- /**
- * The class that is responsible for drawing everything you see on the screen!
- * @class VirtualRenderer
- **/
- /**
- * Constructs a new `VirtualRenderer` within the `container` specified, applying the given `theme`.
- * @param {DOMElement} container The root element of the editor
- * @param {String} theme The starting theme
- *
- * @constructor
- **/
- var VirtualRenderer = function(container, theme) {
- var _self = this;
- this.container = container || dom.createElement("div");
- // TODO: this breaks rendering in Cloud9 with multiple ace instances
- // // Imports CSS once per DOM document ('ace_editor' serves as an identifier).
- // dom.importCssString(editorCss, "ace_editor", container.ownerDocument);
- // in IE <= 9 the native cursor always shines through
- this.$keepTextAreaAtCursor = true;
- dom.addCssClass(this.container, "ace_editor");
- this.setTheme(theme);
- this.$gutter = dom.createElement("div");
- this.$gutter.className = "ace_gutter";
- this.container.appendChild(this.$gutter);
- this.scroller = dom.createElement("div");
- this.scroller.className = "ace_scroller";
- this.container.appendChild(this.scroller);
- this.content = dom.createElement("div");
- this.content.className = "ace_content";
- this.scroller.appendChild(this.content);
- this.$gutterLayer = new GutterLayer(this.$gutter);
- this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this));
- this.$markerBack = new MarkerLayer(this.content);
- var textLayer = this.$textLayer = new TextLayer(this.content);
- this.canvas = textLayer.element;
- this.$markerFront = new MarkerLayer(this.content);
- this.$cursorLayer = new CursorLayer(this.content);
- // Indicates whether the horizontal scrollbar is visible
- this.$horizScroll = false;
- this.$vScroll = false;
- this.scrollBar =
- this.scrollBarV = new ScrollBarV(this.container, this);
- this.scrollBarH = new ScrollBarH(this.container, this);
- this.scrollBarV.addEventListener("scroll", function(e) {
- if (!_self.$scrollAnimation)
- _self.session.setScrollTop(e.data - _self.scrollMargin.top);
- });
- this.scrollBarH.addEventListener("scroll", function(e) {
- if (!_self.$scrollAnimation)
- _self.session.setScrollLeft(e.data - _self.scrollMargin.left);
- });
- this.scrollTop = 0;
- this.scrollLeft = 0;
- this.cursorPos = {
- row : 0,
- column : 0
- };
- this.$textLayer.addEventListener("changeCharacterSize", function() {
- _self.updateCharacterSize();
- _self.onResize(true);
- _self._signal("changeCharacterSize");
- });
- this.$size = {
- width: 0,
- height: 0,
- scrollerHeight: 0,
- scrollerWidth: 0,
- $dirty: true
- };
- this.layerConfig = {
- width : 1,
- padding : 0,
- firstRow : 0,
- firstRowScreen: 0,
- lastRow : 0,
- lineHeight : 0,
- characterWidth : 0,
- minHeight : 1,
- maxHeight : 1,
- offset : 0,
- height : 1
- };
-
- this.scrollMargin = {
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
- v: 0,
- h: 0
- };
- this.$loop = new RenderLoop(
- this.$renderChanges.bind(this),
- this.container.ownerDocument.defaultView
- );
- this.$loop.schedule(this.CHANGE_FULL);
- this.updateCharacterSize();
- this.setPadding(4);
- config.resetOptions(this);
- config._emit("renderer", this);
- };
- (function() {
- this.CHANGE_CURSOR = 1;
- this.CHANGE_MARKER = 2;
- this.CHANGE_GUTTER = 4;
- this.CHANGE_SCROLL = 8;
- this.CHANGE_LINES = 16;
- this.CHANGE_TEXT = 32;
- this.CHANGE_SIZE = 64;
- this.CHANGE_MARKER_BACK = 128;
- this.CHANGE_MARKER_FRONT = 256;
- this.CHANGE_FULL = 512;
- this.CHANGE_H_SCROLL = 1024;
- // this.$logChanges = function(changes) {
- // var a = ""
- // if (changes & this.CHANGE_CURSOR) a += " cursor";
- // if (changes & this.CHANGE_MARKER) a += " marker";
- // if (changes & this.CHANGE_GUTTER) a += " gutter";
- // if (changes & this.CHANGE_SCROLL) a += " scroll";
- // if (changes & this.CHANGE_LINES) a += " lines";
- // if (changes & this.CHANGE_TEXT) a += " text";
- // if (changes & this.CHANGE_SIZE) a += " size";
- // if (changes & this.CHANGE_MARKER_BACK) a += " marker_back";
- // if (changes & this.CHANGE_MARKER_FRONT) a += " marker_front";
- // if (changes & this.CHANGE_FULL) a += " full";
- // if (changes & this.CHANGE_H_SCROLL) a += " h_scroll";
- // console.log(a.trim())
- // };
- oop.implement(this, EventEmitter);
- this.updateCharacterSize = function() {
- if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) {
- this.$allowBoldFonts = this.$textLayer.allowBoldFonts;
- this.setStyle("ace_nobold", !this.$allowBoldFonts);
- }
- this.layerConfig.characterWidth =
- this.characterWidth = this.$textLayer.getCharacterWidth();
- this.layerConfig.lineHeight =
- this.lineHeight = this.$textLayer.getLineHeight();
- this.$updatePrintMargin();
- };
- /**
- *
- * Associates the renderer with an [[EditSession `EditSession`]].
- **/
- this.setSession = function(session) {
- this.session = session;
-
- if (this.scrollMargin.top && session.getScrollTop() <= 0)
- session.setScrollTop(-this.scrollMargin.top);
- this.$cursorLayer.setSession(session);
- this.$markerBack.setSession(session);
- this.$markerFront.setSession(session);
- this.$gutterLayer.setSession(session);
- this.$textLayer.setSession(session);
- this.$loop.schedule(this.CHANGE_FULL);
- };
- /**
- * Triggers a partial update of the text, from the range given by the two parameters.
- * @param {Number} firstRow The first row to update
- * @param {Number} lastRow The last row to update
- *
- *
- **/
- this.updateLines = function(firstRow, lastRow) {
- if (lastRow === undefined)
- lastRow = Infinity;
- if (!this.$changedLines) {
- this.$changedLines = {
- firstRow: firstRow,
- lastRow: lastRow
- };
- }
- else {
- if (this.$changedLines.firstRow > firstRow)
- this.$changedLines.firstRow = firstRow;
- if (this.$changedLines.lastRow < lastRow)
- this.$changedLines.lastRow = lastRow;
- }
- if (this.$changedLines.firstRow > this.layerConfig.lastRow ||
- this.$changedLines.lastRow < this.layerConfig.firstRow)
- return;
- this.$loop.schedule(this.CHANGE_LINES);
- };
- this.onChangeTabSize = function() {
- this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER);
- this.$textLayer.onChangeTabSize();
- };
- /**
- * Triggers a full update of the text, for all the rows.
- **/
- this.updateText = function() {
- this.$loop.schedule(this.CHANGE_TEXT);
- };
- /**
- * Triggers a full update of all the layers, for all the rows.
- * @param {Boolean} force If `true`, forces the changes through
- *
- *
- **/
- this.updateFull = function(force) {
- if (force)
- this.$renderChanges(this.CHANGE_FULL, true);
- else
- this.$loop.schedule(this.CHANGE_FULL);
- };
- /**
- *
- * Updates the font size.
- **/
- this.updateFontSize = function() {
- this.$textLayer.checkForSizeChanges();
- };
- this.$changes = 0;
- this.$updateSizeAsync = function() {
- if (this.$loop.pending)
- this.$size.$dirty = true;
- else
- this.onResize();
- };
- /**
- * [Triggers a resize of the editor.]{: #VirtualRenderer.onResize}
- * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed
- * @param {Number} gutterWidth The width of the gutter in pixels
- * @param {Number} width The width of the editor in pixels
- * @param {Number} height The hiehgt of the editor, in pixels
- *
- *
- **/
- this.onResize = function(force, gutterWidth, width, height) {
- // if (force)
- // console.log("force resize requested", width, height)
- if (this.resizing > 2)
- return;
- else if (this.resizing > 0)
- this.resizing++;
- else
- this.resizing = force ? 1 : 0;
- // `|| el.scrollHeight` is required for outosizing editors on ie
- // where elements with clientHeight = 0 alsoe have clientWidth = 0
- var el = this.container;
- if (!height)
- height = el.clientHeight || el.scrollHeight;
- if (!width)
- width = el.clientWidth || el.scrollWidth;
- var changes = this.$updateCachedSize(force, gutterWidth, width, height);
-
- // console.log("resizing to", width, height, JSON.stringify(this.$size))
- // setTimeout(function() {
- // console.log("actual size ", this.container.clientWidth, this.container.clientHeight)
-
- // }.bind(this), 500)
-
- if (!this.$size.scrollerHeight || (!width && !height))
- return this.resizing = 0;
- if (force)
- this.$gutterLayer.$padding = null;
- if (force)
- this.$renderChanges(changes | this.$changes, true);
- else
- this.$loop.schedule(changes | this.$changes);
- if (this.resizing)
- this.resizing = 0;
- };
-
- this.$updateCachedSize = function(force, gutterWidth, width, height) {
- height -= (this.$extraHeight || 0);
- var changes = 0;
- var size = this.$size;
- var oldSize = {
- width: size.width,
- height: size.height,
- scrollerHeight: size.scrollerHeight,
- scrollerWidth: size.scrollerWidth
- };
- if (height && (force || size.height != height)) {
- size.height = height;
- changes = this.CHANGE_SIZE;
- size.scrollerHeight = size.height;
- if (this.$horizScroll)
- size.scrollerHeight -= this.scrollBarH.getHeight();
-
- // this.scrollBarV.setHeight(size.scrollerHeight);
- this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px";
- if (this.session) {
- //this.session.setScrollTop(this.getScrollTop());
- changes = changes | this.CHANGE_SCROLL;
- }
- }
- if (width && (force || size.width != width)) {
- changes = this.CHANGE_SIZE;
- size.width = width;
-
- if (gutterWidth == null)
- gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0;
-
- this.gutterWidth = gutterWidth;
-
- this.scrollBarH.element.style.left =
- this.scroller.style.left = gutterWidth + "px";
- size.scrollerWidth = Math.max(0, width - gutterWidth - this.scrollBarV.getWidth());
-
- this.scrollBarH.element.style.right =
- this.scroller.style.right = this.scrollBarV.getWidth() + "px";
- this.scroller.style.bottom = this.scrollBarH.getHeight() + "px";
-
- // this.scrollBarH.element.style.setWidth(size.scrollerWidth);
- if (this.session && this.session.getUseWrapMode() && this.adjustWrapLimit() || force)
- changes = changes | this.CHANGE_FULL;
- }
-
- size.$dirty = !width || !height;
- if (changes)
- this._signal("resize", oldSize);
- return changes;
- };
- this.onGutterResize = function() {
- var gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0;
- if (gutterWidth != this.gutterWidth)
- this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height);
- if (this.session.getUseWrapMode() && this.adjustWrapLimit()) {
- this.$loop.schedule(this.CHANGE_FULL);
- } else if (this.$size.$dirty) {
- this.$loop.schedule(this.CHANGE_FULL);
- } else {
- this.$computeLayerConfig();
- this.$loop.schedule(this.CHANGE_MARKER);
- }
- };
- /**
- * Adjusts the wrap limit, which is the number of characters that can fit within the width of the edit area on screen.
- **/
- this.adjustWrapLimit = function() {
- var availableWidth = this.$size.scrollerWidth - this.$padding * 2;
- var limit = Math.floor(availableWidth / this.characterWidth);
- return this.session.adjustWrapLimit(limit, this.$showPrintMargin && this.$printMarginColumn);
- };
- /**
- * Identifies whether you want to have an animated scroll or not.
- * @param {Boolean} shouldAnimate Set to `true` to show animated scrolls
- *
- **/
- this.setAnimatedScroll = function(shouldAnimate){
- this.setOption("animatedScroll", shouldAnimate);
- };
- /**
- * Returns whether an animated scroll happens or not.
- * @returns {Boolean}
- **/
- this.getAnimatedScroll = function() {
- return this.$animatedScroll;
- };
- /**
- * Identifies whether you want to show invisible characters or not.
- * @param {Boolean} showInvisibles Set to `true` to show invisibles
- *
- **/
- this.setShowInvisibles = function(showInvisibles) {
- this.setOption("showInvisibles", showInvisibles);
- };
- /**
- * Returns whether invisible characters are being shown or not.
- * @returns {Boolean}
- **/
- this.getShowInvisibles = function() {
- return this.getOption("showInvisibles");
- };
- this.getDisplayIndentGuides = function() {
- return this.getOption("displayIndentGuides");
- };
- this.setDisplayIndentGuides = function(display) {
- this.setOption("displayIndentGuides", display);
- };
- /**
- * Identifies whether you want to show the print margin or not.
- * @param {Boolean} showPrintMargin Set to `true` to show the print margin
- *
- **/
- this.setShowPrintMargin = function(showPrintMargin) {
- this.setOption("showPrintMargin", showPrintMargin);
- };
- /**
- * Returns whether the print margin is being shown or not.
- * @returns {Boolean}
- **/
- this.getShowPrintMargin = function() {
- return this.getOption("showPrintMargin");
- };
- /**
- * Identifies whether you want to show the print margin column or not.
- * @param {Boolean} showPrintMargin Set to `true` to show the print margin column
- *
- **/
- this.setPrintMarginColumn = function(showPrintMargin) {
- this.setOption("printMarginColumn", showPrintMargin);
- };
- /**
- * Returns whether the print margin column is being shown or not.
- * @returns {Boolean}
- **/
- this.getPrintMarginColumn = function() {
- return this.getOption("printMarginColumn");
- };
- /**
- * Returns `true` if the gutter is being shown.
- * @returns {Boolean}
- **/
- this.getShowGutter = function(){
- return this.getOption("showGutter");
- };
- /**
- * Identifies whether you want to show the gutter or not.
- * @param {Boolean} show Set to `true` to show the gutter
- *
- **/
- this.setShowGutter = function(show){
- return this.setOption("showGutter", show);
- };
- this.getFadeFoldWidgets = function(){
- return this.getOption("fadeFoldWidgets")
- };
- this.setFadeFoldWidgets = function(show) {
- this.setOption("fadeFoldWidgets", show);
- };
- this.setHighlightGutterLine = function(shouldHighlight) {
- this.setOption("highlightGutterLine", shouldHighlight);
- };
- this.getHighlightGutterLine = function() {
- return this.getOption("highlightGutterLine");
- };
- this.$updateGutterLineHighlight = function() {
- var pos = this.$cursorLayer.$pixelPos;
- var height = this.layerConfig.lineHeight;
- if (this.session.getUseWrapMode()) {
- var cursor = this.session.selection.getCursor();
- cursor.column = 0;
- pos = this.$cursorLayer.getPixelPosition(cursor, true);
- height *= this.session.getRowLength(cursor.row);
- }
- this.$gutterLineHighlight.style.top = pos.top - this.layerConfig.offset + "px";
- this.$gutterLineHighlight.style.height = height + "px";
- };
- this.$updatePrintMargin = function() {
- if (!this.$showPrintMargin && !this.$printMarginEl)
- return;
- if (!this.$printMarginEl) {
- var containerEl = dom.createElement("div");
- containerEl.className = "ace_layer ace_print-margin-layer";
- this.$printMarginEl = dom.createElement("div");
- this.$printMarginEl.className = "ace_print-margin";
- containerEl.appendChild(this.$printMarginEl);
- this.content.insertBefore(containerEl, this.content.firstChild);
- }
- var style = this.$printMarginEl.style;
- style.left = ((this.characterWidth * this.$printMarginColumn) + this.$padding) + "px";
- style.visibility = this.$showPrintMargin ? "visible" : "hidden";
-
- if (this.session && this.session.$wrap == -1)
- this.adjustWrapLimit();
- };
- /**
- *
- * Returns the root element containing this renderer.
- * @returns {DOMElement}
- **/
- this.getContainerElement = function() {
- return this.container;
- };
- /**
- *
- * Returns the element that the mouse events are attached to
- * @returns {DOMElement}
- **/
- this.getMouseEventTarget = function() {
- return this.content;
- };
- /**
- *
- * Returns the element to which the hidden text area is added.
- * @returns {DOMElement}
- **/
- this.getTextAreaContainer = function() {
- return this.container;
- };
- // move text input over the cursor
- // this is required for iOS and IME
- this.$moveTextAreaToCursor = function() {
- if (!this.$keepTextAreaAtCursor)
- return;
- var config = this.layerConfig;
- var posTop = this.$cursorLayer.$pixelPos.top;
- var posLeft = this.$cursorLayer.$pixelPos.left;
- posTop -= config.offset;
- var h = this.lineHeight;
- if (posTop < 0 || posTop > config.height - h)
- return;
- var w = this.characterWidth;
- if (this.$composition) {
- var val = this.textarea.value.replace(/^\x01+/, "");
- w *= (this.session.$getStringScreenWidth(val)[0]+2);
- h += 2;
- posTop -= 1;
- }
- posLeft -= this.scrollLeft;
- if (posLeft > this.$size.scrollerWidth - w)
- posLeft = this.$size.scrollerWidth - w;
- posLeft -= this.scrollBar.width;
- this.textarea.style.height = h + "px";
- this.textarea.style.width = w + "px";
- this.textarea.style.right = Math.max(0, this.$size.scrollerWidth - posLeft - w) + "px";
- this.textarea.style.bottom = Math.max(0, this.$size.height - posTop - h) + "px";
- };
- /**
- *
- * [Returns the index of the first visible row.]{: #VirtualRenderer.getFirstVisibleRow}
- * @returns {Number}
- **/
- this.getFirstVisibleRow = function() {
- return this.layerConfig.firstRow;
- };
- /**
- *
- * Returns the index of the first fully visible row. "Fully" here means that the characters in the row are not truncated; that the top and the bottom of the row are on the screen.
- * @returns {Number}
- **/
- this.getFirstFullyVisibleRow = function() {
- return this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1);
- };
- /**
- *
- * Returns the index of the last fully visible row. "Fully" here means that the characters in the row are not truncated; that the top and the bottom of the row are on the screen.
- * @returns {Number}
- **/
- this.getLastFullyVisibleRow = function() {
- var flint = Math.floor((this.layerConfig.height + this.layerConfig.offset) / this.layerConfig.lineHeight);
- return this.layerConfig.firstRow - 1 + flint;
- };
- /**
- *
- * [Returns the index of the last visible row.]{: #VirtualRenderer.getLastVisibleRow}
- * @returns {Number}
- **/
- this.getLastVisibleRow = function() {
- return this.layerConfig.lastRow;
- };
- this.$padding = null;
- /**
- * Sets the padding for all the layers.
- * @param {Number} padding A new padding value (in pixels)
- *
- *
- *
- **/
- this.setPadding = function(padding) {
- this.$padding = padding;
- this.$textLayer.setPadding(padding);
- this.$cursorLayer.setPadding(padding);
- this.$markerFront.setPadding(padding);
- this.$markerBack.setPadding(padding);
- this.$loop.schedule(this.CHANGE_FULL);
- this.$updatePrintMargin();
- };
-
- this.setScrollMargin = function(top, bottom, left, right) {
- var sm = this.scrollMargin;
- sm.top = top|0;
- sm.bottom = bottom|0;
- sm.right = right|0;
- sm.left = left|0;
- sm.v = sm.top + sm.bottom;
- sm.h = sm.left + sm.right;
- if (sm.top && this.scrollTop <= 0 && this.session)
- this.session.setScrollTop(sm.top);
- this.updateFull();
- };
- /**
- * Returns whether the horizontal scrollbar is set to be always visible.
- * @returns {Boolean}
- **/
- this.getHScrollBarAlwaysVisible = function() {
- return this.$hScrollBarAlwaysVisible;
- };
- /**
- * Identifies whether you want to show the horizontal scrollbar or not.
- * @param {Boolean} alwaysVisible Set to `true` to make the horizontal scroll bar visible
- **/
- this.setHScrollBarAlwaysVisible = function(alwaysVisible) {
- this.setOption("hScrollBarAlwaysVisible", alwaysVisible);
- };
- /**
- * Returns whether the horizontal scrollbar is set to be always visible.
- * @returns {Boolean}
- **/
- this.getVScrollBarAlwaysVisible = function() {
- return this.$hScrollBarAlwaysVisible;
- };
- /**
- * Identifies whether you want to show the horizontal scrollbar or not.
- * @param {Boolean} alwaysVisible Set to `true` to make the horizontal scroll bar visible
- **/
- this.setVScrollBarAlwaysVisible = function(alwaysVisible) {
- this.setOption("vScrollBarAlwaysVisible", alwaysVisible);
- };
- this.$updateScrollBarV = function() {
- this.scrollBarV.setInnerHeight(this.layerConfig.maxHeight + this.scrollMargin.v);
- this.scrollBarV.setScrollTop(this.scrollTop + this.scrollMargin.top);
- };
- this.$updateScrollBarH = function() {
- this.scrollBarH.setInnerWidth(this.layerConfig.width + 2 * this.$padding + this.scrollMargin.h);
- this.scrollBarH.setScrollLeft(this.scrollLeft + this.scrollMargin.left);
- };
-
- this.$frozen = false;
- this.freeze = function() {
- this.$frozen = true;
- };
-
- this.unfreeze = function() {
- this.$frozen = false;
- };
- this.$renderChanges = function(changes, force) {
- if (this.$changes) {
- changes |= this.$changes;
- this.$changes = 0;
- }
- if ((!this.session || !this.container.offsetWidth || this.$frozen) || (!changes && !force)) {
- this.$changes |= changes;
- return;
- }
- if (this.$size.$dirty) {
- this.$changes |= changes;
- return this.onResize(true);
- }
- if (!this.lineHeight) {
- this.$textLayer.checkForSizeChanges();
- }
- // this.$logChanges(changes);
-
- this._signal("beforeRender");
- var config = this.layerConfig;
- // text, scrolling and resize changes can cause the view port size to change
- if (changes & this.CHANGE_FULL ||
- changes & this.CHANGE_SIZE ||
- changes & this.CHANGE_TEXT ||
- changes & this.CHANGE_LINES ||
- changes & this.CHANGE_SCROLL ||
- changes & this.CHANGE_H_SCROLL
- ) {
- changes |= this.$computeLayerConfig();
- config = this.layerConfig;
- // update scrollbar first to not lose scroll position when gutter calls resize
- this.$updateScrollBarV();
- if (changes & this.CHANGE_H_SCROLL)
- this.$updateScrollBarH();
- this.$gutterLayer.element.style.marginTop = (-config.offset) + "px";
- this.content.style.marginTop = (-config.offset) + "px";
- this.content.style.width = config.width + 2 * this.$padding + "px";
- this.content.style.height = config.minHeight + "px";
- }
-
- // horizontal scrolling
- if (changes & this.CHANGE_H_SCROLL) {
- this.content.style.marginLeft = -this.scrollLeft + "px";
- this.scroller.className = this.scrollLeft <= 0 ? "ace_scroller" : "ace_scroller ace_scroll-left";
- }
- // full
- if (changes & this.CHANGE_FULL) {
- this.$textLayer.update(config);
- if (this.$showGutter)
- this.$gutterLayer.update(config);
- this.$markerBack.update(config);
- this.$markerFront.update(config);
- this.$cursorLayer.update(config);
- this.$moveTextAreaToCursor();
- this.$highlightGutterLine && this.$updateGutterLineHighlight();
- this._signal("afterRender");
- return;
- }
- // scrolling
- if (changes & this.CHANGE_SCROLL) {
- if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES)
- this.$textLayer.update(config);
- else
- this.$textLayer.scrollLines(config);
- if (this.$showGutter)
- this.$gutterLayer.update(config);
- this.$markerBack.update(config);
- this.$markerFront.update(config);
- this.$cursorLayer.update(config);
- this.$highlightGutterLine && this.$updateGutterLineHighlight();
- this.$moveTextAreaToCursor();
- this._signal("afterRender");
- return;
- }
- if (changes & this.CHANGE_TEXT) {
- this.$textLayer.update(config);
- if (this.$showGutter)
- this.$gutterLayer.update(config);
- }
- else if (changes & this.CHANGE_LINES) {
- if (this.$updateLines() || (changes & this.CHANGE_GUTTER) && this.$showGutter)
- this.$gutterLayer.update(config);
- }
- else if (changes & this.CHANGE_TEXT || changes & this.CHANGE_GUTTER) {
- if (this.$showGutter)
- this.$gutterLayer.update(config);
- }
- if (changes & this.CHANGE_CURSOR) {
- this.$cursorLayer.update(config);
- this.$moveTextAreaToCursor();
- this.$highlightGutterLine && this.$updateGutterLineHighlight();
- }
- if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) {
- this.$markerFront.update(config);
- }
- if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) {
- this.$markerBack.update(config);
- }
- this._signal("afterRender");
- };
-
- this.$autosize = function() {
- var height = this.session.getScreenLength() * this.lineHeight;
- var maxHeight = this.$maxLines * this.lineHeight;
- var desiredHeight = Math.max(
- (this.$minLines||1) * this.lineHeight,
- Math.min(maxHeight, height)
- ) + this.scrollMargin.v + (this.$extraHeight || 0);
- var vScroll = height > maxHeight;
-
- if (desiredHeight != this.desiredHeight ||
- this.$size.height != this.desiredHeight || vScroll != this.$vScroll) {
- if (vScroll != this.$vScroll) {
- this.$vScroll = vScroll;
- this.scrollBarV.setVisible(vScroll);
- }
-
- var w = this.container.clientWidth;
- this.container.style.height = desiredHeight + "px";
- this.$updateCachedSize(true, this.$gutterWidth, w, desiredHeight);
- // this.$loop.changes = 0;
- this.desiredHeight = desiredHeight;
- }
- };
-
- this.$computeLayerConfig = function() {
- if (this.$maxLines && this.lineHeight > 1)
- this.$autosize();
- var session = this.session;
-
- var hideScrollbars = this.$size.height <= 2 * this.lineHeight;
- var screenLines = this.session.getScreenLength();
- var maxHeight = screenLines * this.lineHeight;
- var offset = this.scrollTop % this.lineHeight;
- var minHeight = this.$size.scrollerHeight + this.lineHeight;
- var longestLine = this.$getLongestLine();
-
- var horizScroll = !hideScrollbars && (this.$hScrollBarAlwaysVisible ||
- this.$size.scrollerWidth - longestLine - 2 * this.$padding < 0);
- var hScrollChanged = this.$horizScroll !== horizScroll;
- if (hScrollChanged) {
- this.$horizScroll = horizScroll;
- this.scrollBarH.setVisible(horizScroll);
- }
-
- if (!this.$maxLines && this.$scrollPastEnd) {
- if (this.scrollTop > maxHeight - this.$size.scrollerHeight)
- maxHeight += Math.min(
- (this.$size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd,
- this.scrollTop - maxHeight + this.$size.scrollerHeight
- );
- }
-
- var vScroll = !hideScrollbars && (this.$vScrollBarAlwaysVisible ||
- this.$size.scrollerHeight - maxHeight < 0);
- var vScrollChanged = this.$vScroll !== vScroll;
- if (vScrollChanged) {
- this.$vScroll = vScroll;
- this.scrollBarV.setVisible(vScroll);
- }
-
- this.session.setScrollTop(Math.max(-this.scrollMargin.top,
- Math.min(this.scrollTop, maxHeight - this.$size.scrollerHeight + this.scrollMargin.v)));
- this.session.setScrollLeft(Math.max(-this.scrollMargin.left, Math.min(this.scrollLeft,
- longestLine + 2 * this.$padding - this.$size.scrollerWidth + this.scrollMargin.h)));
- var lineCount = Math.ceil(minHeight / this.lineHeight) - 1;
- var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));
- var lastRow = firstRow + lineCount;
- // Map lines on the screen to lines in the document.
- var firstRowScreen, firstRowHeight;
- var lineHeight = this.lineHeight;
- firstRow = session.screenToDocumentRow(firstRow, 0);
- // Check if firstRow is inside of a foldLine. If true, then use the first
- // row of the foldLine.
- var foldLine = session.getFoldLine(firstRow);
- if (foldLine) {
- firstRow = foldLine.start.row;
- }
- firstRowScreen = session.documentToScreenRow(firstRow, 0);
- firstRowHeight = session.getRowLength(firstRow) * lineHeight;
- lastRow = Math.min(session.screenToDocumentRow(lastRow, 0), session.getLength() - 1);
- minHeight = this.$size.scrollerHeight + session.getRowLength(lastRow) * lineHeight +
- firstRowHeight;
- offset = this.scrollTop - firstRowScreen * lineHeight;
- var changes = 0;
- // Horizontal scrollbar visibility may have changed, which changes
- // the client height of the scroller
- if (hScrollChanged || vScrollChanged) {
- changes = this.$updateCachedSize(true, this.gutterWidth, this.$size.width, this.$size.height);
- this._signal("scrollbarVisibilityChanged");
- if (vScrollChanged)
- longestLine = this.$getLongestLine();
- }
-
- this.layerConfig = {
- width : longestLine,
- padding : this.$padding,
- firstRow : firstRow,
- firstRowScreen: firstRowScreen,
- lastRow : lastRow,
- lineHeight : lineHeight,
- characterWidth : this.characterWidth,
- minHeight : minHeight,
- maxHeight : maxHeight,
- offset : offset,
- height : this.$size.scrollerHeight
- };
- // For debugging.
- // console.log(JSON.stringify(this.layerConfig));
- return changes;
- };
- this.$updateLines = function() {
- var firstRow = this.$changedLines.firstRow;
- var lastRow = this.$changedLines.lastRow;
- this.$changedLines = null;
- var layerConfig = this.layerConfig;
- if (firstRow > layerConfig.lastRow + 1) { return; }
- if (lastRow < layerConfig.firstRow) { return; }
- // if the last row is unknown -> redraw everything
- if (lastRow === Infinity) {
- if (this.$showGutter)
- this.$gutterLayer.update(layerConfig);
- this.$textLayer.update(layerConfig);
- return;
- }
- // else update only the changed rows
- this.$textLayer.updateLines(layerConfig, firstRow, lastRow);
- return true;
- };
- this.$getLongestLine = function() {
- var charCount = this.session.getScreenWidth();
- if (this.showInvisibles && !this.session.$useWrapMode)
- charCount += 1;
- return Math.max(this.$size.scrollerWidth - 2 * this.$padding, Math.round(charCount * this.characterWidth));
- };
- /**
- *
- * Schedules an update to all the front markers in the document.
- **/
- this.updateFrontMarkers = function() {
- this.$markerFront.setMarkers(this.session.getMarkers(true));
- this.$loop.schedule(this.CHANGE_MARKER_FRONT);
- };
- /**
- *
- * Schedules an update to all the back markers in the document.
- **/
- this.updateBackMarkers = function() {
- this.$markerBack.setMarkers(this.session.getMarkers());
- this.$loop.schedule(this.CHANGE_MARKER_BACK);
- };
- /**
- *
- * Deprecated; (moved to [[EditSession]])
- * @deprecated
- **/
- this.addGutterDecoration = function(row, className){
- this.$gutterLayer.addGutterDecoration(row, className);
- };
- /**
- * Deprecated; (moved to [[EditSession]])
- * @deprecated
- **/
- this.removeGutterDecoration = function(row, className){
- this.$gutterLayer.removeGutterDecoration(row, className);
- };
- /**
- *
- * Redraw breakpoints.
- **/
- this.updateBreakpoints = function(rows) {
- this.$loop.schedule(this.CHANGE_GUTTER);
- };
- /**
- *
- * Sets annotations for the gutter.
- * @param {Array} annotations An array containing annotations
- *
- *
- **/
- this.setAnnotations = function(annotations) {
- this.$gutterLayer.setAnnotations(annotations);
- this.$loop.schedule(this.CHANGE_GUTTER);
- };
- /**
- *
- * Updates the cursor icon.
- **/
- this.updateCursor = function() {
- this.$loop.schedule(this.CHANGE_CURSOR);
- };
- /**
- *
- * Hides the cursor icon.
- **/
- this.hideCursor = function() {
- this.$cursorLayer.hideCursor();
- };
- /**
- *
- * Shows the cursor icon.
- **/
- this.showCursor = function() {
- this.$cursorLayer.showCursor();
- };
- this.scrollSelectionIntoView = function(anchor, lead, offset) {
- // first scroll anchor into view then scroll lead into view
- this.scrollCursorIntoView(anchor, offset);
- this.scrollCursorIntoView(lead, offset);
- };
- /**
- *
- * Scrolls the cursor into the first visibile area of the editor
- **/
- this.scrollCursorIntoView = function(cursor, offset) {
- // the editor is not visible
- if (this.$size.scrollerHeight === 0)
- return;
- var pos = this.$cursorLayer.getPixelPosition(cursor);
- var left = pos.left;
- var top = pos.top;
-
- var scrollTop = this.$scrollAnimation ? this.session.getScrollTop() : this.scrollTop;
- if (scrollTop > top) {
- if (offset)
- top -= offset * this.$size.scrollerHeight;
- if (top == 0)
- top = - this.scrollMargin.top;
- else if (top == 0)
- top = + this.scrollMargin.bottom;
- this.session.setScrollTop(top);
- } else if (scrollTop + this.$size.scrollerHeight < top + this.lineHeight) {
- if (offset)
- top += offset * this.$size.scrollerHeight;
- this.session.setScrollTop(top + this.lineHeight - this.$size.scrollerHeight);
- }
- var scrollLeft = this.scrollLeft;
- if (scrollLeft > left) {
- if (left < this.$padding + 2 * this.layerConfig.characterWidth)
- left = -this.scrollMargin.left;
- this.session.setScrollLeft(left);
- } else if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {
- this.session.setScrollLeft(Math.round(left + this.characterWidth - this.$size.scrollerWidth));
- } else if (scrollLeft <= this.$padding && left - scrollLeft < this.characterWidth) {
- this.session.setScrollLeft(0);
- }
- };
- /**
- * {:EditSession.getScrollTop}
- * @related EditSession.getScrollTop
- * @returns {Number}
- **/
- this.getScrollTop = function() {
- return this.session.getScrollTop();
- };
- /**
- * {:EditSession.getScrollLeft}
- * @related EditSession.getScrollLeft
- * @returns {Number}
- **/
- this.getScrollLeft = function() {
- return this.session.getScrollLeft();
- };
- /**
- *
- * Returns the first visible row, regardless of whether it's fully visible or not.
- * @returns {Number}
- **/
- this.getScrollTopRow = function() {
- return this.scrollTop / this.lineHeight;
- };
- /**
- *
- * Returns the last visible row, regardless of whether it's fully visible or not.
- * @returns {Number}
- **/
- this.getScrollBottomRow = function() {
- return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1);
- };
- /**
- * Gracefully scrolls from the top of the editor to the row indicated.
- * @param {Number} row A row id
- *
- *
- * @related EditSession.setScrollTop
- **/
- this.scrollToRow = function(row) {
- this.session.setScrollTop(row * this.lineHeight);
- };
- this.alignCursor = function(cursor, alignment) {
- if (typeof cursor == "number")
- cursor = {row: cursor, column: 0};
- var pos = this.$cursorLayer.getPixelPosition(cursor);
- var h = this.$size.scrollerHeight - this.lineHeight;
- var offset = pos.top - h * (alignment || 0);
- this.session.setScrollTop(offset);
- return offset;
- };
- this.STEPS = 8;
- this.$calcSteps = function(fromValue, toValue){
- var i = 0;
- var l = this.STEPS;
- var steps = [];
- var func = function(t, x_min, dx) {
- return dx * (Math.pow(t - 1, 3) + 1) + x_min;
- };
- for (i = 0; i < l; ++i)
- steps.push(func(i / this.STEPS, fromValue, toValue - fromValue));
- return steps;
- };
- /**
- * Gracefully scrolls the editor to the row indicated.
- * @param {Number} line A line number
- * @param {Boolean} center If `true`, centers the editor the to indicated line
- * @param {Boolean} animate If `true` animates scrolling
- * @param {Function} callback Function to be called after the animation has finished
- *
- *
- **/
- this.scrollToLine = function(line, center, animate, callback) {
- var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0});
- var offset = pos.top;
- if (center)
- offset -= this.$size.scrollerHeight / 2;
- var initialScroll = this.scrollTop;
- this.session.setScrollTop(offset);
- if (animate !== false)
- this.animateScrolling(initialScroll, callback);
- };
- this.animateScrolling = function(fromValue, callback) {
- var toValue = this.scrollTop;
- if (!this.$animatedScroll)
- return;
- var _self = this;
-
- if (fromValue == toValue)
- return;
-
- if (this.$scrollAnimation) {
- var oldSteps = this.$scrollAnimation.steps;
- if (oldSteps.length) {
- fromValue = oldSteps[0];
- if (fromValue == toValue)
- return;
- }
- }
-
- var steps = _self.$calcSteps(fromValue, toValue);
- this.$scrollAnimation = {from: fromValue, to: toValue, steps: steps};
- clearInterval(this.$timer);
- _self.session.setScrollTop(steps.shift());
- this.$timer = setInterval(function() {
- if (steps.length) {
- _self.session.setScrollTop(steps.shift());
- // trick session to think it's already scrolled to not loose toValue
- _self.session.$scrollTop = toValue;
- } else if (toValue != null) {
- _self.session.$scrollTop = -1;
- _self.session.setScrollTop(toValue);
- toValue = null;
- } else {
- // do this on separate step to not get spurious scroll event from scrollbar
- _self.$timer = clearInterval(_self.$timer);
- _self.$scrollAnimation = null;
- callback && callback();
- }
- }, 10);
- };
- /**
- * Scrolls the editor to the y pixel indicated.
- * @param {Number} scrollTop The position to scroll to
- *
- *
- * @returns {Number}
- **/
- this.scrollToY = function(scrollTop) {
- // after calling scrollBar.setScrollTop
- // scrollbar sends us event with same scrollTop. ignore it
- if (this.scrollTop !== scrollTop) {
- this.$loop.schedule(this.CHANGE_SCROLL);
- this.scrollTop = scrollTop;
- }
- };
- /**
- * Scrolls the editor across the x-axis to the pixel indicated.
- * @param {Number} scrollLeft The position to scroll to
- *
- *
- * @returns {Number}
- **/
- this.scrollToX = function(scrollLeft) {
- if (this.scrollLeft !== scrollLeft)
- this.scrollLeft = scrollLeft;
- this.$loop.schedule(this.CHANGE_H_SCROLL);
- };
- /**
- * Scrolls the editor across both x- and y-axes.
- * @param {Number} x The x value to scroll to
- * @param {Number} y The y value to scroll to
- **/
- this.scrollTo = function(x, y) {
- this.session.setScrollTop(y);
- this.session.setScrollLeft(y);
- };
-
- /**
- * Scrolls the editor across both x- and y-axes.
- * @param {Number} deltaX The x value to scroll by
- * @param {Number} deltaY The y value to scroll by
- **/
- this.scrollBy = function(deltaX, deltaY) {
- deltaY && this.session.setScrollTop(this.session.getScrollTop() + deltaY);
- deltaX && this.session.setScrollLeft(this.session.getScrollLeft() + deltaX);
- };
- /**
- * Returns `true` if you can still scroll by either parameter; in other words, you haven't reached the end of the file or line.
- * @param {Number} deltaX The x value to scroll by
- * @param {Number} deltaY The y value to scroll by
- *
- *
- * @returns {Boolean}
- **/
- this.isScrollableBy = function(deltaX, deltaY) {
- if (deltaY < 0 && this.session.getScrollTop() >= 1 - this.scrollMargin.top)
- return true;
- if (deltaY > 0 && this.session.getScrollTop() + this.$size.scrollerHeight
- - this.layerConfig.maxHeight - (this.$size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd
- < -1 + this.scrollMargin.bottom)
- return true;
- if (deltaX < 0 && this.session.getScrollLeft() >= 1 - this.scrollMargin.left)
- return true;
- if (deltaX > 0 && this.session.getScrollLeft() + this.$size.scrollerWidth
- - this.layerConfig.width < -1 + this.scrollMargin.right)
- return true;
- };
- this.pixelToScreenCoordinates = function(x, y) {
- var canvasPos = this.scroller.getBoundingClientRect();
- var offset = (x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth;
- var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight);
- var col = Math.round(offset);
- return {row: row, column: col, side: offset - col > 0 ? 1 : -1};
- };
- this.screenToTextCoordinates = function(x, y) {
- var canvasPos = this.scroller.getBoundingClientRect();
- var col = Math.round(
- (x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth
- );
- var row = (y + this.scrollTop - canvasPos.top) / this.lineHeight;
- return this.session.screenToDocumentPosition(row, Math.max(col, 0));
- };
- /**
- * Returns an object containing the `pageX` and `pageY` coordinates of the document position.
- * @param {Number} row The document row position
- * @param {Number} column The document column position
- *
- *
- *
- * @returns {Object}
- **/
- this.textToScreenCoordinates = function(row, column) {
- var canvasPos = this.scroller.getBoundingClientRect();
- var pos = this.session.documentToScreenPosition(row, column);
- var x = this.$padding + Math.round(pos.column * this.characterWidth);
- var y = pos.row * this.lineHeight;
- return {
- pageX: canvasPos.left + x - this.scrollLeft,
- pageY: canvasPos.top + y - this.scrollTop
- };
- };
- /**
- *
- * Focuses the current container.
- **/
- this.visualizeFocus = function() {
- dom.addCssClass(this.container, "ace_focus");
- };
- /**
- *
- * Blurs the current container.
- **/
- this.visualizeBlur = function() {
- dom.removeCssClass(this.container, "ace_focus");
- };
- /**
- * @param {Number} position
- *
- * @private
- **/
- this.showComposition = function(position) {
- if (!this.$composition)
- this.$composition = {
- keepTextAreaAtCursor: this.$keepTextAreaAtCursor,
- cssText: this.textarea.style.cssText
- };
- this.$keepTextAreaAtCursor = true;
- dom.addCssClass(this.textarea, "ace_composition");
- this.textarea.style.cssText = "";
- this.$moveTextAreaToCursor();
- };
- /**
- * @param {String} text A string of text to use
- *
- * Sets the inner text of the current composition to `text`.
- **/
- this.setCompositionText = function(text) {
- this.$moveTextAreaToCursor();
- };
- /**
- *
- * Hides the current composition.
- **/
- this.hideComposition = function() {
- if (!this.$composition)
- return;
- dom.removeCssClass(this.textarea, "ace_composition");
- this.$keepTextAreaAtCursor = this.$composition.keepTextAreaAtCursor;
- this.textarea.style.cssText = this.$composition.cssText;
- this.$composition = null;
- };
- /**
- * [Sets a new theme for the editor. `theme` should exist, and be a directory path, like `ace/theme/textmate`.]{: #VirtualRenderer.setTheme}
- * @param {String} theme The path to a theme
- * @param {Function} cb optional callback
- *
- **/
- this.setTheme = function(theme, cb) {
- var _self = this;
- this.$themeValue = theme;
- _self._dispatchEvent('themeChange',{theme:theme});
- if (!theme || typeof theme == "string") {
- var moduleName = theme || "ace/theme/textmate";
- config.loadModule(["theme", moduleName], afterLoad);
- } else {
- afterLoad(theme);
- }
- function afterLoad(module) {
- if (_self.$themeValue != theme)
- return cb && cb();
- if (!module.cssClass)
- return;
- dom.importCssString(
- module.cssText,
- module.cssClass,
- _self.container.ownerDocument
- );
- if (_self.theme)
- dom.removeCssClass(_self.container, _self.theme.cssClass);
- // this is kept only for backwards compatibility
- _self.$theme = module.cssClass;
- _self.theme = module;
- dom.addCssClass(_self.container, module.cssClass);
- dom.setCssClass(_self.container, "ace_dark", module.isDark);
- var padding = "padding" in module ? module.padding : 4;
- if (_self.$padding && padding != _self.$padding)
- _self.setPadding(padding);
- // force re-measure of the gutter width
- if (_self.$size) {
- _self.$size.width = 0;
- _self.onResize();
- }
- _self._dispatchEvent('themeLoaded', {theme:module});
- cb && cb();
- }
- };
- /**
- * [Returns the path of the current theme.]{: #VirtualRenderer.getTheme}
- * @returns {String}
- **/
- this.getTheme = function() {
- return this.$themeValue;
- };
- // Methods allows to add / remove CSS classnames to the editor element.
- // This feature can be used by plug-ins to provide a visual indication of
- // a certain mode that editor is in.
- /**
- * [Adds a new class, `style`, to the editor.]{: #VirtualRenderer.setStyle}
- * @param {String} style A class name
- *
- **/
- this.setStyle = function(style, include) {
- dom.setCssClass(this.container, style, include != false);
- };
- /**
- * [Removes the class `style` from the editor.]{: #VirtualRenderer.unsetStyle}
- * @param {String} style A class name
- *
- **/
- this.unsetStyle = function(style) {
- dom.removeCssClass(this.container, style);
- };
- /**
- * @param {String} cursorStyle A css cursor style
- *
- **/
- this.setMouseCursor = function(cursorStyle) {
- this.content.style.cursor = cursorStyle;
- };
- /**
- * Destroys the text and cursor layers for this renderer.
- **/
- this.destroy = function() {
- this.$textLayer.destroy();
- this.$cursorLayer.destroy();
- };
- }).call(VirtualRenderer.prototype);
- config.defineOptions(VirtualRenderer.prototype, "renderer", {
- animatedScroll: {initialValue: false},
- showInvisibles: {
- set: function(value) {
- if (this.$textLayer.setShowInvisibles(value))
- this.$loop.schedule(this.CHANGE_TEXT);
- },
- initialValue: false
- },
- showPrintMargin: {
- set: function() { this.$updatePrintMargin(); },
- initialValue: true
- },
- printMarginColumn: {
- set: function() { this.$updatePrintMargin(); },
- initialValue: 80
- },
- printMargin: {
- set: function(val) {
- if (typeof val == "number")
- this.$printMarginColumn = val;
- this.$showPrintMargin = !!val;
- this.$updatePrintMargin();
- },
- get: function() {
- return this.$showPrintMargin && this.$printMarginColumn;
- }
- },
- showGutter: {
- set: function(show){
- this.$gutter.style.display = show ? "block" : "none";
- this.onGutterResize();
- },
- initialValue: true
- },
- fadeFoldWidgets: {
- set: function(show) {
- dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show);
- },
- initialValue: false
- },
- showFoldWidgets: {
- set: function(show) {this.$gutterLayer.setShowFoldWidgets(show)},
- initialValue: true
- },
- displayIndentGuides: {
- set: function(show) {
- if (this.$textLayer.setDisplayIndentGuides(show))
- this.$loop.schedule(this.CHANGE_TEXT);
- },
- initialValue: true
- },
- highlightGutterLine: {
- set: function(shouldHighlight) {
- if (!this.$gutterLineHighlight) {
- this.$gutterLineHighlight = dom.createElement("div");
- this.$gutterLineHighlight.className = "ace_gutter-active-line";
- this.$gutter.appendChild(this.$gutterLineHighlight);
- return;
- }
- this.$gutterLineHighlight.style.display = shouldHighlight ? "" : "none";
- // if cursorlayer have never been updated there's nothing on screen to update
- if (this.$cursorLayer.$pixelPos)
- this.$updateGutterLineHighlight();
- },
- initialValue: false,
- value: true
- },
- hScrollBarAlwaysVisible: {
- set: function(val) {
- if (!this.$hScrollBarAlwaysVisible || !this.$horizScroll)
- this.$loop.schedule(this.CHANGE_SCROLL);
- },
- initialValue: false
- },
- vScrollBarAlwaysVisible: {
- set: function(val) {
- if (!this.$vScrollBarAlwaysVisible || !this.$vScroll)
- this.$loop.schedule(this.CHANGE_SCROLL);
- },
- initialValue: false
- },
- fontSize: {
- set: function(size) {
- if (typeof size == "number")
- size = size + "px";
- this.container.style.fontSize = size;
- this.updateFontSize();
- },
- initialValue: 12
- },
- fontFamily: {
- set: function(name) {
- this.container.style.fontFamily = name;
- this.updateFontSize();
- }
- },
- maxLines: {
- set: function(val) {
- this.updateFull();
- }
- },
- minLines: {
- set: function(val) {
- this.updateFull();
- }
- },
- scrollPastEnd: {
- set: function(val) {
- val = +val || 0;
- if (this.$scrollPastEnd == val)
- return;
- this.$scrollPastEnd = val;
- this.$loop.schedule(this.CHANGE_SCROLL);
- },
- initialValue: 0,
- handlesSet: true
- },
- fixedWidthGutter: {
- set: function(val) {
- this.$gutterLayer.$fixedWidth = !!val;
- this.$loop.schedule(this.CHANGE_GUTTER);
- }
- }
- });
- exports.VirtualRenderer = VirtualRenderer;
- });
|