• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openwisp / netjsongraph.js / 16582217787

28 Jul 2025 10:59PM UTC coverage: 80.832% (+2.0%) from 78.786%
16582217787

push

github

web-flow
[change] Uniform geojson map rendering #395

Closes #395

449 of 599 branches covered (74.96%)

Branch coverage included in aggregate %.

140 of 187 new or added lines in 7 files covered. (74.87%)

4 existing lines in 3 files now uncovered.

795 of 940 relevant lines covered (84.57%)

7.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.58
/src/js/netjsongraph.gui.js
1
class NetJSONGraphGUI {
2
  constructor(_this) {
3
    this.self = _this;
17✔
4
    this.renderModeSelector = null;
17✔
5
    this.controls = null;
17✔
6
    this.sideBar = null;
17✔
7
    this.metaInfoContainer = null;
17✔
8
    this.nodeLinkInfoContainer = null;
17✔
9
  }
10

11
  createControls() {
12
    const controls = document.createElement("div");
13✔
13
    controls.setAttribute("class", "njg-controls");
13✔
14
    this.self.el.appendChild(controls);
13✔
15
    return controls;
13✔
16
  }
17

18
  createRenderModeSelector() {
19
    const selectIconContainer = document.createElement("div");
13✔
20
    const iconEye = document.createElement("span");
13✔
21
    iconEye.setAttribute("class", "iconfont icon-eye");
13✔
22
    selectIconContainer.setAttribute("class", "njg-selectIcon");
13✔
23
    selectIconContainer.appendChild(iconEye);
13✔
24
    this.controls.appendChild(selectIconContainer);
13✔
25
    return selectIconContainer;
13✔
26
  }
27

28
  createSideBar() {
29
    const sideBar = document.createElement("div");
16✔
30
    sideBar.setAttribute("class", "njg-sideBar");
16✔
31
    sideBar.classList.add("hidden");
16✔
32
    const button = document.createElement("button");
16✔
33
    sideBar.appendChild(button);
16✔
34
    button.classList.add("sideBarHandle");
16✔
35
    button.onclick = () => {
16✔
36
      sideBar.classList.toggle("hidden");
2✔
37
      const metaInfo = document.querySelector(".njg-metaInfoContainer");
2✔
38
      if (
2!
39
        (this.self.config.showMetaOnNarrowScreens ||
5✔
40
          this.self.el.clientWidth > 850) &&
41
        metaInfo
42
      ) {
43
        metaInfo.style.display = "flex";
2✔
44
      }
45
    };
46
    this.self.el.appendChild(sideBar);
16✔
47
    return sideBar;
16✔
48
  }
49

50
  hideInfoOnNarrowScreen() {
51
    if (
13✔
52
      !this.self.config.showMetaOnNarrowScreens &&
17✔
53
      this.self.el.clientWidth < 850
54
    ) {
55
      this.metaInfoContainer.style.display = "none";
4✔
56
    }
57

58
    if (
13✔
59
      this.metaInfoContainer.style.display === "none" &&
17✔
60
      this.nodeLinkInfoContainer.style.display === "none"
61
    ) {
62
      this.sideBar.classList.add("hidden");
4✔
63
    }
64
  }
65

66
  createMetaInfoContainer() {
67
    const metaInfoContainer = document.createElement("div");
13✔
68
    const header = document.createElement("h2");
13✔
69
    const metadataContainer = document.createElement("div");
13✔
70

71
    metadataContainer.classList.add("njg-metaData");
13✔
72
    metaInfoContainer.classList.add("njg-metaInfoContainer");
13✔
73
    const closeButton = document.createElement("span");
13✔
74
    closeButton.classList.add("njg-closeButton");
13✔
75
    header.innerHTML = "Info";
13✔
76
    closeButton.innerHTML = " &#x2715;";
13✔
77
    header.appendChild(closeButton);
13✔
78
    metaInfoContainer.appendChild(header);
13✔
79
    metaInfoContainer.appendChild(metadataContainer);
13✔
80
    this.metaInfoContainer = metaInfoContainer;
13✔
81
    this.sideBar.appendChild(metaInfoContainer);
13✔
82
    this.nodeLinkInfoContainer = this.createNodeLinkInfoContainer();
13✔
83
    this.hideInfoOnNarrowScreen();
13✔
84
    window.addEventListener("resize", this.hideInfoOnNarrowScreen.bind(this));
13✔
85

86
    closeButton.onclick = () => {
13✔
87
      this.metaInfoContainer.style.display = "none";
2✔
88
      if (this.nodeLinkInfoContainer.style.display === "none") {
2!
89
        this.sideBar.classList.add("hidden");
2✔
90
      }
91
    };
92

93
    return metaInfoContainer;
13✔
94
  }
95

96
  createNodeLinkInfoContainer() {
97
    const nodeLinkInfoContainer = document.createElement("div");
18✔
98
    nodeLinkInfoContainer.classList.add("njg-nodeLinkInfoContainer");
18✔
99
    nodeLinkInfoContainer.style.display = "none";
18✔
100
    this.sideBar.appendChild(nodeLinkInfoContainer);
18✔
101
    return nodeLinkInfoContainer;
18✔
102
  }
103

104
  getNodeLinkInfo(type, data) {
105
    const nodeLinkInfoChildren =
106
      document.querySelectorAll(".njg-infoContainer");
7✔
107
    const headerInfoChildren = document.querySelectorAll(
7✔
108
      ".njg-headerContainer",
109
    );
110
    for (let i = 0; i < nodeLinkInfoChildren.length; i += 1) {
7✔
111
      nodeLinkInfoChildren[i].remove();
6✔
112
    }
113

114
    for (let i = 0; i < headerInfoChildren.length; i += 1) {
7✔
115
      headerInfoChildren[i].remove();
6✔
116
    }
117

118
    const headerContainer = document.createElement("div");
7✔
119
    const infoContainer = document.createElement("div");
7✔
120
    const header = document.createElement("h2");
7✔
121
    const closeButton = document.createElement("span");
7✔
122

123
    infoContainer.classList.add("njg-infoContainer");
7✔
124
    headerContainer.classList.add("njg-headerContainer");
7✔
125
    closeButton.classList.add("njg-closeButton");
7✔
126
    this.nodeLinkInfoContainer.style.display = "flex";
7✔
127
    header.innerHTML = `${type} Info`;
7✔
128
    closeButton.innerHTML = " &#x2715;";
7✔
129

130
    Object.keys(data).forEach((key) => {
7✔
131
      const val = data[key];
14✔
132

133
      // Hide keys whose value is not provided or is explicitly undefined/null/empty
134
      if (
14!
135
        val === undefined ||
62!
136
        val === null ||
137
        (typeof val === "string" &&
138
          (val.trim() === "" || /^(undefined|null)$/i.test(val.trim())) &&
139
          val !== "0")
140
      ) {
NEW
141
        return;
×
142
      }
143

144
      const infoItems = document.createElement("div");
14✔
145
      infoItems.classList.add("njg-infoItems");
14✔
146
      const keyLabel = document.createElement("span");
14✔
147
      keyLabel.setAttribute("class", "njg-keyLabel");
14✔
148
      const valueLabel = document.createElement("span");
14✔
149
      valueLabel.setAttribute("class", "njg-valueLabel");
14✔
150
      if (key === "location") {
14✔
151
        keyLabel.innerHTML = "Location";
1✔
152
        valueLabel.innerHTML = `${Math.round(data[key].lat * 1000) / 1000}, ${
1✔
153
          Math.round(data[key].lng * 1000) / 1000
154
        }`;
155
      } else if (key === "localAddresses") {
13✔
156
        keyLabel.innerHTML = "Local Addresses";
1✔
157
        valueLabel.innerHTML = data[key].join("<br/>");
1✔
158
      } else {
159
        keyLabel.innerHTML = key;
12✔
160
        // Preserve multiline values
161
        const displayVal =
162
          typeof val === "string" ? val.replace(/\n/g, "<br/>") : val;
12✔
163
        valueLabel.innerHTML = displayVal;
12✔
164
      }
165

166
      infoItems.appendChild(keyLabel);
14✔
167
      infoItems.appendChild(valueLabel);
14✔
168
      infoContainer.appendChild(infoItems);
14✔
169
    });
170
    headerContainer.appendChild(header);
7✔
171
    headerContainer.appendChild(closeButton);
7✔
172
    this.nodeLinkInfoContainer.appendChild(headerContainer);
7✔
173
    this.nodeLinkInfoContainer.appendChild(infoContainer);
7✔
174

175
    closeButton.onclick = () => {
7✔
176
      this.nodeLinkInfoContainer.style.display = "none";
3✔
177
      if (
3✔
178
        this.metaInfoContainer === null ||
6✔
179
        this.metaInfoContainer.style.display === "none"
180
      ) {
181
        this.sideBar.classList.add("hidden");
1✔
182
      }
183
    };
184
  }
185

186
  init() {
187
    this.sideBar = this.createSideBar();
10✔
188
    if (this.self.config.switchMode) {
10✔
189
      this.controls = this.createControls();
9✔
190
      this.renderModeSelector = this.createRenderModeSelector();
9✔
191
    }
192
  }
193
}
194

195
export default NetJSONGraphGUI;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc