mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-04 18:42:57 -05:00
332 lines
11 KiB
JavaScript
332 lines
11 KiB
JavaScript
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Distributed under the BSD license:
|
||
|
*
|
||
|
* Copyright (c) 2012, 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 EditSession = require("../edit_session").EditSession;
|
||
|
var Renderer = require("../virtual_renderer").VirtualRenderer;
|
||
|
var Editor = require("../editor").Editor;
|
||
|
var Range = require("../range").Range;
|
||
|
var event = require("../lib/event");
|
||
|
var lang = require("../lib/lang");
|
||
|
var dom = require("../lib/dom");
|
||
|
|
||
|
var $singleLineEditor = function(el) {
|
||
|
var renderer = new Renderer(el);
|
||
|
|
||
|
renderer.$maxLines = 4;
|
||
|
|
||
|
var editor = new Editor(renderer);
|
||
|
|
||
|
editor.setHighlightActiveLine(false);
|
||
|
editor.setShowPrintMargin(false);
|
||
|
editor.renderer.setShowGutter(false);
|
||
|
editor.renderer.setHighlightGutterLine(false);
|
||
|
|
||
|
editor.$mouseHandler.$focusWaitTimout = 0;
|
||
|
|
||
|
return editor;
|
||
|
};
|
||
|
|
||
|
var AcePopup = function(parentNode) {
|
||
|
var el = dom.createElement("div");
|
||
|
var popup = new $singleLineEditor(el);
|
||
|
|
||
|
if (parentNode)
|
||
|
parentNode.appendChild(el);
|
||
|
el.style.display = "none";
|
||
|
popup.renderer.content.style.cursor = "default";
|
||
|
popup.renderer.setStyle("ace_autocomplete");
|
||
|
|
||
|
popup.setOption("displayIndentGuides", false);
|
||
|
|
||
|
var noop = function(){};
|
||
|
|
||
|
popup.focus = noop;
|
||
|
popup.$isFocused = true;
|
||
|
|
||
|
popup.renderer.$cursorLayer.restartTimer = noop;
|
||
|
popup.renderer.$cursorLayer.element.style.opacity = 0;
|
||
|
|
||
|
popup.renderer.$maxLines = 8;
|
||
|
popup.renderer.$keepTextAreaAtCursor = false;
|
||
|
|
||
|
popup.setHighlightActiveLine(false);
|
||
|
// set default highlight color
|
||
|
popup.session.highlight("");
|
||
|
popup.session.$searchHighlight.clazz = "ace_highlight-marker";
|
||
|
|
||
|
popup.on("mousedown", function(e) {
|
||
|
var pos = e.getDocumentPosition();
|
||
|
popup.moveCursorToPosition(pos);
|
||
|
popup.selection.clearSelection();
|
||
|
selectionMarker.start.row = selectionMarker.end.row = pos.row;
|
||
|
e.stop();
|
||
|
});
|
||
|
|
||
|
var lastMouseEvent;
|
||
|
var hoverMarker = new Range(-1,0,-1,Infinity);
|
||
|
var selectionMarker = new Range(-1,0,-1,Infinity);
|
||
|
selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
|
||
|
popup.setSelectOnHover = function(val) {
|
||
|
if (!val) {
|
||
|
hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
|
||
|
} else if (hoverMarker.id) {
|
||
|
popup.session.removeMarker(hoverMarker.id);
|
||
|
hoverMarker.id = null;
|
||
|
}
|
||
|
}
|
||
|
popup.setSelectOnHover(false);
|
||
|
popup.on("mousemove", function(e) {
|
||
|
if (!lastMouseEvent) {
|
||
|
lastMouseEvent = e;
|
||
|
return;
|
||
|
}
|
||
|
if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
|
||
|
return;
|
||
|
}
|
||
|
lastMouseEvent = e;
|
||
|
lastMouseEvent.scrollTop = popup.renderer.scrollTop;
|
||
|
var row = lastMouseEvent.getDocumentPosition().row;
|
||
|
if (hoverMarker.start.row != row) {
|
||
|
if (!hoverMarker.id)
|
||
|
popup.setRow(row);
|
||
|
setHoverMarker(row);
|
||
|
}
|
||
|
});
|
||
|
popup.renderer.on("beforeRender", function() {
|
||
|
if (lastMouseEvent && hoverMarker.start.row != -1) {
|
||
|
lastMouseEvent.$pos = null;
|
||
|
var row = lastMouseEvent.getDocumentPosition().row;
|
||
|
if (!hoverMarker.id)
|
||
|
popup.setRow(row);
|
||
|
setHoverMarker(row, true);
|
||
|
}
|
||
|
});
|
||
|
popup.renderer.on("afterRender", function() {
|
||
|
var row = popup.getRow();
|
||
|
var t = popup.renderer.$textLayer;
|
||
|
var selected = t.element.childNodes[row - t.config.firstRow];
|
||
|
if (selected == t.selectedNode)
|
||
|
return;
|
||
|
if (t.selectedNode)
|
||
|
dom.removeCssClass(t.selectedNode, "ace_selected");
|
||
|
t.selectedNode = selected;
|
||
|
if (selected)
|
||
|
dom.addCssClass(selected, "ace_selected");
|
||
|
});
|
||
|
var hideHoverMarker = function() { setHoverMarker(-1) };
|
||
|
var setHoverMarker = function(row, suppressRedraw) {
|
||
|
if (row !== hoverMarker.start.row) {
|
||
|
hoverMarker.start.row = hoverMarker.end.row = row;
|
||
|
if (!suppressRedraw)
|
||
|
popup.session._emit("changeBackMarker");
|
||
|
popup._emit("changeHoverMarker");
|
||
|
}
|
||
|
};
|
||
|
popup.getHoveredRow = function() {
|
||
|
return hoverMarker.start.row;
|
||
|
};
|
||
|
|
||
|
event.addListener(popup.container, "mouseout", hideHoverMarker);
|
||
|
popup.on("hide", hideHoverMarker);
|
||
|
popup.on("changeSelection", hideHoverMarker);
|
||
|
|
||
|
popup.session.doc.getLength = function() {
|
||
|
return popup.data.length;
|
||
|
};
|
||
|
popup.session.doc.getLine = function(i) {
|
||
|
var data = popup.data[i];
|
||
|
if (typeof data == "string")
|
||
|
return data;
|
||
|
return (data && data.value) || "";
|
||
|
};
|
||
|
|
||
|
var bgTokenizer = popup.session.bgTokenizer;
|
||
|
bgTokenizer.$tokenizeRow = function(i) {
|
||
|
var data = popup.data[i];
|
||
|
var tokens = [];
|
||
|
if (!data)
|
||
|
return tokens;
|
||
|
if (typeof data == "string")
|
||
|
data = {value: data};
|
||
|
if (!data.caption)
|
||
|
data.caption = data.value;
|
||
|
|
||
|
var last = -1;
|
||
|
var flag, c;
|
||
|
for (var i = 0; i < data.caption.length; i++) {
|
||
|
c = data.caption[i];
|
||
|
flag = data.matchMask & (1 << i) ? 1 : 0;
|
||
|
if (last !== flag) {
|
||
|
tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
|
||
|
last = flag;
|
||
|
} else {
|
||
|
tokens[tokens.length - 1].value += c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (data.meta) {
|
||
|
var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
|
||
|
if (data.meta.length + data.caption.length < maxW - 2)
|
||
|
tokens.push({type: "rightAlignedText", value: data.meta});
|
||
|
}
|
||
|
return tokens;
|
||
|
};
|
||
|
bgTokenizer.$updateOnChange = noop;
|
||
|
bgTokenizer.start = noop;
|
||
|
|
||
|
popup.session.$computeWidth = function() {
|
||
|
return this.screenWidth = 0;
|
||
|
}
|
||
|
|
||
|
// public
|
||
|
popup.isOpen = false;
|
||
|
popup.isTopdown = false;
|
||
|
|
||
|
popup.data = [];
|
||
|
popup.setData = function(list) {
|
||
|
popup.data = list || [];
|
||
|
popup.setValue(lang.stringRepeat("\n", list.length), -1);
|
||
|
popup.setRow(0);
|
||
|
};
|
||
|
popup.getData = function(row) {
|
||
|
return popup.data[row];
|
||
|
};
|
||
|
|
||
|
popup.getRow = function() {
|
||
|
return selectionMarker.start.row;
|
||
|
};
|
||
|
popup.setRow = function(line) {
|
||
|
line = Math.max(-1, Math.min(this.data.length, line));
|
||
|
if (selectionMarker.start.row != line) {
|
||
|
popup.selection.clearSelection();
|
||
|
selectionMarker.start.row = selectionMarker.end.row = line || 0;
|
||
|
popup.session._emit("changeBackMarker");
|
||
|
popup.moveCursorTo(line || 0, 0);
|
||
|
if (popup.isOpen)
|
||
|
popup._signal("select");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
popup.on("changeSelection", function() {
|
||
|
if (popup.isOpen)
|
||
|
popup.setRow(popup.selection.lead.row);
|
||
|
});
|
||
|
|
||
|
popup.hide = function() {
|
||
|
this.container.style.display = "none";
|
||
|
this._signal("hide");
|
||
|
popup.isOpen = false;
|
||
|
};
|
||
|
popup.show = function(pos, lineHeight, topdownOnly) {
|
||
|
var el = this.container;
|
||
|
var screenHeight = window.innerHeight;
|
||
|
var screenWidth = window.innerWidth;
|
||
|
var renderer = this.renderer;
|
||
|
// var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
|
||
|
var maxH = renderer.$maxLines * lineHeight * 1.4;
|
||
|
var top = pos.top + this.$borderSize;
|
||
|
if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
|
||
|
el.style.top = "";
|
||
|
el.style.bottom = screenHeight - top + "px";
|
||
|
popup.isTopdown = false;
|
||
|
} else {
|
||
|
top += lineHeight;
|
||
|
el.style.top = top + "px";
|
||
|
el.style.bottom = "";
|
||
|
popup.isTopdown = true;
|
||
|
}
|
||
|
|
||
|
el.style.display = "";
|
||
|
this.renderer.$textLayer.checkForSizeChanges();
|
||
|
|
||
|
var left = pos.left;
|
||
|
if (left + el.offsetWidth > screenWidth)
|
||
|
left = screenWidth - el.offsetWidth;
|
||
|
|
||
|
el.style.left = left + "px";
|
||
|
|
||
|
this._signal("show");
|
||
|
lastMouseEvent = null;
|
||
|
popup.isOpen = true;
|
||
|
};
|
||
|
|
||
|
popup.getTextLeftOffset = function() {
|
||
|
return this.$borderSize + this.renderer.$padding + this.$imageSize;
|
||
|
};
|
||
|
|
||
|
popup.$imageSize = 0;
|
||
|
popup.$borderSize = 1;
|
||
|
|
||
|
return popup;
|
||
|
};
|
||
|
|
||
|
dom.importCssString("\
|
||
|
.ace_autocomplete.ace-tm .ace_marker-layer .ace_active-line {\
|
||
|
background-color: #CAD6FA;\
|
||
|
z-index: 1;\
|
||
|
}\
|
||
|
.ace_autocomplete.ace-tm .ace_line-hover {\
|
||
|
border: 1px solid #abbffe;\
|
||
|
margin-top: -1px;\
|
||
|
background: rgba(233,233,253,0.4);\
|
||
|
}\
|
||
|
.ace_autocomplete .ace_line-hover {\
|
||
|
position: absolute;\
|
||
|
z-index: 2;\
|
||
|
}\
|
||
|
.ace_rightAlignedText {\
|
||
|
color: gray;\
|
||
|
display: inline-block;\
|
||
|
position: absolute;\
|
||
|
right: 4px;\
|
||
|
text-align: right;\
|
||
|
z-index: -1;\
|
||
|
}\
|
||
|
.ace_autocomplete .ace_completion-highlight{\
|
||
|
color: #000;\
|
||
|
text-shadow: 0 0 0.01em;\
|
||
|
}\
|
||
|
.ace_autocomplete {\
|
||
|
width: 280px;\
|
||
|
z-index: 200000;\
|
||
|
background: #fbfbfb;\
|
||
|
color: #444;\
|
||
|
border: 1px lightgray solid;\
|
||
|
position: fixed;\
|
||
|
box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
|
||
|
line-height: 1.4;\
|
||
|
}");
|
||
|
|
||
|
exports.AcePopup = AcePopup;
|
||
|
|
||
|
});
|