summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/markup/views/text-editor.js
blob: f3c83ca8788d433cfb32587737dcbf86b0ee8a21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const {getAutocompleteMaxWidth} = require("devtools/client/inspector/markup/utils");
const {editableField} = require("devtools/client/shared/inplace-editor");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {LocalizationHelper} = require("devtools/shared/l10n");

const INSPECTOR_L10N =
      new LocalizationHelper("devtools/client/locales/inspector.properties");

/**
 * Creates a simple text editor node, used for TEXT and COMMENT
 * nodes.
 *
 * @param  {MarkupContainer} container
 *         The container owning this editor.
 * @param  {DOMNode} node
 *         The node being edited.
 * @param  {String} templateId
 *         The template id to use to build the editor.
 */
function TextEditor(container, node, templateId) {
  this.container = container;
  this.markup = this.container.markup;
  this.node = node;
  this.template = this.markup.template.bind(templateId);
  this._selected = false;

  this.markup.template(templateId, this);

  editableField({
    element: this.value,
    stopOnReturn: true,
    trigger: "dblclick",
    multiline: true,
    maxWidth: () => getAutocompleteMaxWidth(this.value, this.container.elt),
    trimOutput: false,
    done: (val, commit) => {
      if (!commit) {
        return;
      }
      this.node.getNodeValue().then(longstr => {
        longstr.string().then(oldValue => {
          longstr.release().then(null, console.error);

          this.container.undo.do(() => {
            this.node.setNodeValue(val);
          }, () => {
            this.node.setNodeValue(oldValue);
          });
        });
      });
    },
    cssProperties: getCssProperties(this.markup.toolbox),
    contextMenu: this.markup.inspector.onTextBoxContextMenu
  });

  this.update();
}

TextEditor.prototype = {
  get selected() {
    return this._selected;
  },

  set selected(value) {
    if (value === this._selected) {
      return;
    }
    this._selected = value;
    this.update();
  },

  update: function () {
    let longstr = null;
    this.node.getNodeValue().then(ret => {
      longstr = ret;
      return longstr.string();
    }).then(str => {
      longstr.release().then(null, console.error);
      this.value.textContent = str;

      let isWhitespace = !/[^\s]/.exec(str);
      this.value.classList.toggle("whitespace", isWhitespace);

      let chars = str.replace(/\n/g, "⏎")
                     .replace(/\t/g, "⇥")
                     .replace(/ /g, "◦");
      this.value.setAttribute("title", isWhitespace
        ? INSPECTOR_L10N.getFormatStr("markupView.whitespaceOnly", chars)
        : "");
    }).then(null, console.error);
  },

  destroy: function () {},

  /**
   * Stub method for consistency with ElementEditor.
   */
  getInfoAtNode: function () {
    return null;
  }
};

module.exports = TextEditor;