summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/markup/utils.js
blob: 06609676a50c514b2bfc4de542bfd16fe536f08d (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* 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";

/**
 * Apply a 'flashed' background and foreground color to elements. Intended
 * to be used with flashElementOff as a way of drawing attention to an element.
 *
 * @param  {Node} backgroundElt
 *         The element to set the highlighted background color on.
 * @param  {Object} options
 * @param  {Node} options.foregroundElt
 *         The element to set the matching foreground color on. This will equal
 *         backgroundElt if not set.
 * @param  {String} options.backgroundClass
 *         The background highlight color class to set on the element.
 */
function flashElementOn(
  backgroundElt,
  { foregroundElt = backgroundElt, backgroundClass = "theme-bg-contrast" } = {}
) {
  if (!backgroundElt || !foregroundElt) {
    return;
  }

  // Make sure the animation class is not here
  backgroundElt.classList.remove("flash-out");

  // Change the background
  backgroundElt.classList.add(backgroundClass);

  foregroundElt.classList.add("theme-fg-contrast");
  [].forEach.call(
    foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
    span => span.classList.add("theme-fg-contrast")
  );
}

/**
 * Remove a 'flashed' background and foreground color to elements.
 * See flashElementOn.
 *
 * @param  {Node} backgroundElt
 *         The element to remove the highlighted background color on.
 * @param  {Object} options
 * @param  {Node} options.foregroundElt
 *         The element to remove the matching foreground color on. This will equal
 *         backgroundElt if not set.
 * @param  {String} options.backgroundClass
 *         The background highlight color class to remove on the element.
 */
function flashElementOff(
  backgroundElt,
  { foregroundElt = backgroundElt, backgroundClass = "theme-bg-contrast" } = {}
) {
  if (!backgroundElt || !foregroundElt) {
    return;
  }

  // Add the animation class to smoothly remove the background
  backgroundElt.classList.add("flash-out");

  // Remove the background
  backgroundElt.classList.remove(backgroundClass);

  foregroundElt.classList.remove("theme-fg-contrast");
  // Make sure the foreground animation class is removed
  foregroundElt.classList.remove("flash-out");
  [].forEach.call(
    foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
    span => span.classList.remove("theme-fg-contrast")
  );
}

/**
 * Retrieve the available width between a provided element left edge and a container right
 * edge. This used can be used as a max-width for inplace-editor (autocomplete) widgets
 * replacing Editor elements of the the markup-view;
 */
function getAutocompleteMaxWidth(element, container) {
  const elementRect = element.getBoundingClientRect();
  const containerRect = container.getBoundingClientRect();
  return containerRect.right - elementRect.left - 2;
}

/**
 * Parse attribute names and values from a string.
 *
 * @param  {String} attr
 *         The input string for which names/values are to be parsed.
 * @param  {HTMLDocument} doc
 *         A document that can be used to test valid attributes.
 * @return {Array}
 *         An array of attribute names and their values.
 */
function parseAttributeValues(attr, doc) {
  attr = attr.trim();

  const parseAndGetNode = str => {
    return new DOMParser().parseFromString(str, "text/html").body.childNodes[0];
  };

  // Handle bad user inputs by appending a " or ' if it fails to parse without
  // them. Also note that a SVG tag is used to make sure the HTML parser
  // preserves mixed-case attributes
  const el =
    parseAndGetNode("<svg " + attr + "></svg>") ||
    parseAndGetNode("<svg " + attr + '"></svg>') ||
    parseAndGetNode("<svg " + attr + "'></svg>");

  const div = doc.createElement("div");
  const attributes = [];
  for (const { name, value } of el.attributes) {
    // Try to set on an element in the document, throws exception on bad input.
    // Prevents InvalidCharacterError - "String contains an invalid character".
    try {
      div.setAttribute(name, value);
      attributes.push({ name, value });
    } catch (e) {
      // This may throw exceptions on bad input.
      // Prevents InvalidCharacterError - "String contains an invalid
      // character".
    }
  }

  return attributes;
}

module.exports = {
  flashElementOn,
  flashElementOff,
  getAutocompleteMaxWidth,
  parseAttributeValues,
};