/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/editor/InlineTextEditor.js
346 строк
13 KB
ficristo
Move most inline jslint directives to config files
12 авг 2016, 10:22
12 авг 2016, 10:22
074382a
Код
Авторство
О чём код?
/* * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ // FUTURE: Merge part (or all) of this class with MultiRangeInlineEditor define(function (require, exports, module) { "use strict"; // Load dependent modules var CodeMirror = require("thirdparty/CodeMirror/lib/codemirror"), EventDispatcher = require("utils/EventDispatcher"), DocumentManager = require("document/DocumentManager"), EditorManager = require("editor/EditorManager"), CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), InlineWidget = require("editor/InlineWidget").InlineWidget, KeyEvent = require("utils/KeyEvent"); /** * Shows or hides the dirty indicator * @private */ function _showDirtyIndicator($indicatorDiv, isDirty) { // Show or hide the dirty indicator by adjusting // the width of the div. $indicatorDiv.css("width", isDirty ? 16 : 0); } /** * Respond to dirty flag change event. If the dirty flag is associated with an inline editor, * show (or hide) the dirty indicator. * @private */ function _dirtyFlagChangeHandler(event, doc) { var $dirtyIndicators = $(".inline-text-editor .dirty-indicator"), $indicator; $dirtyIndicators.each(function (index, indicator) { $indicator = $(this); if ($indicator.data("fullPath") === doc.file.fullPath) { _showDirtyIndicator($indicator, doc.isDirty); } }); } /** * @constructor * @extends {InlineWidget} */ function InlineTextEditor() { InlineWidget.call(this); this.editor = null; // We need to set this as a capture handler so CodeMirror doesn't handle Esc before we see it. this.handleKeyDown = this.handleKeyDown.bind(this); this.htmlContent.addEventListener("keydown", this.handleKeyDown, true); } InlineTextEditor.prototype = Object.create(InlineWidget.prototype); InlineTextEditor.prototype.constructor = InlineTextEditor; InlineTextEditor.prototype.parentClass = InlineWidget.prototype; InlineTextEditor.prototype.$wrapper = null; /** @type {Editor} */ InlineTextEditor.prototype.editor = null; InlineTextEditor.prototype.$editorHolder = null; InlineTextEditor.prototype.$header = null; InlineTextEditor.prototype.$filename = null; /** * Given a host editor and its inline editors, find the widest gutter and make all the others match * @param {!Editor} hostEditor Host editor containing all the inline editors to sync * @private */ function _syncGutterWidths(hostEditor) { var allHostedEditors = EditorManager.getInlineEditors(hostEditor); // add the host itself to the list too allHostedEditors.push(hostEditor); var maxWidth = 0; allHostedEditors.forEach(function (editor) { var $gutter = $(editor._codeMirror.getGutterElement()).find(".CodeMirror-linenumbers"); $gutter.css("min-width", ""); var curWidth = $gutter.width(); if (curWidth > maxWidth) { maxWidth = curWidth; } }); if (allHostedEditors.length === 1) { //There's only the host, just refresh the gutter allHostedEditors[0]._codeMirror.setOption("gutters", allHostedEditors[0]._codeMirror.getOption("gutters")); return; } maxWidth = maxWidth + "px"; allHostedEditors.forEach(function (editor) { $(editor._codeMirror.getGutterElement()).find(".CodeMirror-linenumbers").css("min-width", maxWidth); // Force CodeMirror to refresh the gutter editor._codeMirror.setOption("gutters", editor._codeMirror.getOption("gutters")); }); } /** * Called any time inline was closed, whether manually (via close()) or automatically */ InlineTextEditor.prototype.onClosed = function () { InlineTextEditor.prototype.parentClass.onClosed.apply(this, arguments); _syncGutterWidths(this.hostEditor); // Destroy the inline editor. this.setInlineContent(null); this.htmlContent.removeEventListener("keydown", this.handleKeyDown, true); }; /** * Update the inline editor's height when the number of lines change. The * base implementation of this method does nothing. */ InlineTextEditor.prototype.sizeInlineWidgetToContents = function () { // brackets_codemirror_overrides.css adds height:auto to CodeMirror // Inline editors themselves do not need to be sized, but layouts like // the one used in CSSInlineEditor do need some manual layout. }; /** * Some tasks have to wait until we've been parented into the outer editor * @param {string} the inline ID that is generated by CodeMirror after the widget that holds the inline * editor is constructed and added to the DOM */ InlineTextEditor.prototype.onAdded = function () { var self = this; InlineTextEditor.prototype.parentClass.onAdded.apply(this, arguments); if (this.editor) { this.editor.refresh(); } // Update display of inline editors when the hostEditor signals a redraw CodeMirror.on(this.info, "redraw", function () { // At the point where we get the redraw, CodeMirror might not yet have actually // re-added the widget to the DOM. This is filed as https://github.com/codemirror/CodeMirror/issues/1226. // For now, we can work around it by doing the refresh on a setTimeout(). window.setTimeout(function () { if (self.editor) { self.editor.refresh(); } }, 0); }); _syncGutterWidths(this.hostEditor); if (this.editor) { this.editor.focus(); } }; /** * @return {?Editor} If an Editor within this inline editor has focus, returns it. Otherwise returns null. */ InlineTextEditor.prototype.getFocusedEditor = function () { if (this.editor && this.editor.hasFocus()) { return this.editor; } return null; }; /** * @private * Make sure that if we want to handle Esc to cancel a multiple selection, we don't let it bubble * up to InlineWidget, which will close the edit. */ InlineTextEditor.prototype.handleKeyDown = function (e) { if (e.keyCode === KeyEvent.DOM_VK_ESCAPE && this.editor && this.editor.getSelections().length > 1) { CodeMirror.commands.singleSelection(this.editor._codeMirror); e.stopImmediatePropagation(); } }; /** * Sets the document and range to show in the inline editor, or null to destroy the current editor and leave * the content blank. * @param {Document} doc The document to show, or null to show nothing * @param {number} startLine The first line of text in `doc` to show in inline editor. Ignored if doc is null. * @param {number} endLine The last line of text in `doc` to show in inline editor. Ignored if doc is null. */ InlineTextEditor.prototype.setInlineContent = function (doc, startLine, endLine) { var self = this; // Destroy the previous editor if we had one and clear out the filename info. if (this.editor) { this.editor.off(".InlineTextEditor"); this.editor.destroy(); // remove from DOM and release ref on Document this.editor = null; this.$filename.off(".InlineTextEditor") .removeAttr("title"); this.$filename.html(""); } if (!doc) { return; } var range = { startLine: startLine, endLine: endLine }; // dirty indicator, with file path stored on it var $dirtyIndicatorDiv = $("<div/>") .addClass("dirty-indicator") .html("•") .width(0); // initialize indicator as hidden $dirtyIndicatorDiv.data("fullPath", doc.file.fullPath); this.$lineNumber = $("<span class='line-number'/>"); // update contents of filename link this.$filename.append($dirtyIndicatorDiv) .append(doc.file.name + " : ") .append(this.$lineNumber) .attr("title", doc.file.fullPath); // clicking filename jumps to full editor view this.$filename.on("click.InlineTextEditor", function () { CommandManager.execute(Commands.FILE_OPEN, { fullPath: doc.file.fullPath }) .done(function () { EditorManager.getCurrentFullEditor().setCursorPos(startLine, 0, true); }); }); var inlineInfo = EditorManager.createInlineEditorForDocument(doc, range, this.$editorHolder.get(0)); this.editor = inlineInfo.editor; // Init line number display this._updateLineRange(inlineInfo.editor); // Always update the widget height when an inline editor completes a // display update this.editor.on("update.InlineTextEditor", function (event, editor) { self.sizeInlineWidgetToContents(); }); // Size editor to content whenever text changes (via edits here or any // other view of the doc: Editor fires "change" any time its text // changes, regardless of origin) this.editor.on("change.InlineTextEditor", function (event, editor) { if (self.hostEditor.isFullyVisible()) { self.sizeInlineWidgetToContents(); self._updateLineRange(editor); } }); // If Document's file is deleted, or Editor loses sync with Document, delegate to this._onLostContent() this.editor.on("lostContent.InlineTextEditor", function () { self._onLostContent.apply(self, arguments); }); // set dirty indicator state _showDirtyIndicator($dirtyIndicatorDiv, doc.isDirty); }; /** * Updates start line display. * @param {Editor} editor */ InlineTextEditor.prototype._updateLineRange = function (editor) { this._startLine = editor.getFirstVisibleLine(); this._endLine = editor.getLastVisibleLine(); this._lineCount = this._endLine - this._startLine; this.$lineNumber.text(this._startLine + 1); }; /** * @param {Editor} hostEditor */ InlineTextEditor.prototype.load = function (hostEditor) { InlineTextEditor.prototype.parentClass.load.apply(this, arguments); // We don't create the actual editor here--that will happen the first time // setInlineContent() is called. this.$wrapper = $("<div/>").addClass("inline-text-editor").appendTo(this.$htmlContent); this.$header = $("<div/>").addClass("inline-editor-header").appendTo(this.$wrapper); this.$filename = $("<a/>").addClass("filename").appendTo(this.$header); this.$editorHolder = $("<div/>").addClass("inline-editor-holder").appendTo(this.$wrapper); }; /** * Called when the editor containing the inline is made visible. */ InlineTextEditor.prototype.onParentShown = function () { InlineTextEditor.prototype.parentClass.onParentShown.apply(this, arguments); // Refresh line number display and codemirror line number gutter if (this.editor) { this._updateLineRange(this.editor); this.editor.refresh(); } // We need to call this explicitly whenever the host editor is reshown this.sizeInlineWidgetToContents(); }; /** * If Document's file is deleted, or Editor loses sync with Document, just close */ InlineTextEditor.prototype._onLostContent = function () { // Note: this closes the entire inline widget if any one Editor loses sync. This seems // better than leaving it open but suddenly removing one rule from the result list. this.close(); }; // Consolidate all dirty document updates // Due to circular dependencies, not safe to call on() directly EventDispatcher.on_duringInit(DocumentManager, "dirtyFlagChange", _dirtyFlagChangeHandler); exports.InlineTextEditor = InlineTextEditor; });