Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 5 | function inspect(data) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 6 | chrome.send('inspect', [data]); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 7 | } |
| 8 | |
| 9 | function terminate(data) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 10 | chrome.send('terminate', [data]); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 11 | } |
| 12 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 13 | function reload(data) { |
| 14 | chrome.send('reload', [data]); |
| 15 | } |
| 16 | |
| 17 | function open(browserId, url) { |
| 18 | chrome.send('open', [browserId, url]); |
| 19 | } |
| 20 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 21 | function removeChildren(element_id) { |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 22 | var element = $(element_id); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 23 | element.textContent = ''; |
| 24 | } |
| 25 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 26 | function onload() { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 27 | var tabContents = document.querySelectorAll('#content > div'); |
| 28 | for (var i = 0; i != tabContents.length; i++) { |
| 29 | var tabContent = tabContents[i]; |
| 30 | var tabName = tabContent.querySelector('.content-header').textContent; |
| 31 | |
| 32 | var tabHeader = document.createElement('div'); |
| 33 | tabHeader.className = 'tab-header'; |
| 34 | var button = document.createElement('button'); |
| 35 | button.textContent = tabName; |
| 36 | tabHeader.appendChild(button); |
| 37 | tabHeader.addEventListener('click', selectTab.bind(null, tabContent.id)); |
| 38 | $('navigation').appendChild(tabHeader); |
| 39 | } |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 40 | var selectedTabName = window.location.hash.slice(1) || 'devices'; |
| 41 | selectTab(selectedTabName + '-tab'); |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 42 | initPortForwarding(); |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 43 | chrome.send('init-ui'); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 44 | } |
| 45 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 46 | function selectTab(id) { |
| 47 | var tabContents = document.querySelectorAll('#content > div'); |
| 48 | var tabHeaders = $('navigation').querySelectorAll('.tab-header'); |
| 49 | for (var i = 0; i != tabContents.length; i++) { |
| 50 | var tabContent = tabContents[i]; |
| 51 | var tabHeader = tabHeaders[i]; |
| 52 | if (tabContent.id == id) { |
| 53 | tabContent.classList.add('selected'); |
| 54 | tabHeader.classList.add('selected'); |
| 55 | } else { |
| 56 | tabContent.classList.remove('selected'); |
| 57 | tabHeader.classList.remove('selected'); |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 62 | function populateLists(data) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 63 | removeChildren('pages'); |
| 64 | removeChildren('extensions'); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 65 | removeChildren('apps'); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 66 | removeChildren('others'); |
| 67 | |
| 68 | for (var i = 0; i < data.length; i++) { |
| 69 | if (data[i].type === 'page') |
| 70 | addToPagesList(data[i]); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 71 | else if (data[i].type === 'extension') |
| 72 | addToExtensionsList(data[i]); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 73 | else if (data[i].type === 'app') |
| 74 | addToAppsList(data[i]); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 75 | else |
| 76 | addToOthersList(data[i]); |
| 77 | } |
| 78 | } |
| 79 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 80 | function populateWorkersList(data) { |
| 81 | removeChildren('workers'); |
| 82 | |
| 83 | for (var i = 0; i < data.length; i++) |
| 84 | addToWorkersList(data[i]); |
| 85 | } |
| 86 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 87 | function populateDeviceLists(devices) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 88 | if (!devices) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 89 | return; |
Ben Murdoch | 9ab5563 | 2013-07-18 11:57:30 +0100 | [diff] [blame] | 90 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 91 | function alreadyDisplayed(element, data) { |
| 92 | var json = JSON.stringify(data); |
| 93 | if (element.cachedJSON == json) |
| 94 | return true; |
| 95 | element.cachedJSON = json; |
| 96 | return false; |
| 97 | } |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 98 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 99 | var deviceList = $('devices'); |
| 100 | if (alreadyDisplayed(deviceList, devices)) |
| 101 | return; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 102 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 103 | function removeObsolete(validIds, section) { |
| 104 | if (validIds.indexOf(section.id) < 0) |
| 105 | section.remove(); |
| 106 | } |
| 107 | |
| 108 | var newDeviceIds = devices.map(function(d) { return d.adbGlobalId }); |
| 109 | Array.prototype.forEach.call( |
| 110 | deviceList.querySelectorAll('.device'), |
| 111 | removeObsolete.bind(null, newDeviceIds)); |
| 112 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 113 | for (var d = 0; d < devices.length; d++) { |
| 114 | var device = devices[d]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 115 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 116 | var browserList; |
| 117 | var deviceSection = $(device.adbGlobalId); |
| 118 | if (deviceSection) { |
| 119 | browserList = deviceSection.querySelector('.browsers'); |
| 120 | } else { |
| 121 | deviceSection = document.createElement('div'); |
| 122 | deviceSection.id = device.adbGlobalId; |
| 123 | deviceSection.className = 'device list'; |
| 124 | deviceList.appendChild(deviceSection); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 125 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 126 | var deviceHeader = document.createElement('div'); |
| 127 | deviceHeader.className = 'section'; |
| 128 | deviceHeader.textContent = device.adbModel; |
| 129 | deviceSection.appendChild(deviceHeader); |
| 130 | |
| 131 | browserList = document.createElement('div'); |
| 132 | browserList.className = 'browsers'; |
| 133 | deviceSection.appendChild(browserList); |
| 134 | } |
| 135 | |
| 136 | if (alreadyDisplayed(deviceSection, device)) |
| 137 | continue; |
| 138 | |
| 139 | var newBrowserIds = |
| 140 | device.browsers.map(function(b) { return b.adbGlobalId }); |
| 141 | Array.prototype.forEach.call( |
| 142 | browserList.querySelectorAll('.browser'), |
| 143 | removeObsolete.bind(null, newBrowserIds)); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 144 | |
| 145 | for (var b = 0; b < device.browsers.length; b++) { |
| 146 | var browser = device.browsers[b]; |
| 147 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 148 | var pageList; |
| 149 | var browserSection = $(browser.adbGlobalId); |
| 150 | if (browserSection) { |
| 151 | pageList = browserSection.querySelector('.pages'); |
| 152 | pageList.textContent = ''; |
| 153 | } else { |
| 154 | browserSection = document.createElement('div'); |
| 155 | browserSection.id = browser.adbGlobalId; |
| 156 | browserSection.className = 'browser'; |
| 157 | browserList.appendChild(browserSection); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 158 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 159 | var browserHeader = document.createElement('div'); |
| 160 | browserHeader.className = 'small-section'; |
| 161 | browserHeader.textContent = browser.adbBrowserName; |
| 162 | browserSection.appendChild(browserHeader); |
| 163 | |
| 164 | var newPage = document.createElement('div'); |
| 165 | newPage.className = 'open'; |
| 166 | |
| 167 | var newPageUrl = document.createElement('input'); |
| 168 | newPageUrl.type = 'text'; |
| 169 | newPageUrl.placeholder = 'Open tab with url'; |
| 170 | newPage.appendChild(newPageUrl); |
| 171 | |
| 172 | var openHandler = function(browserId, input) { |
| 173 | open(browserId, input.value || 'about:blank'); |
| 174 | input.value = ''; |
| 175 | }.bind(null, browser.adbGlobalId, newPageUrl); |
| 176 | newPageUrl.addEventListener('keyup', function(handler, event) { |
| 177 | if (event.keyIdentifier == 'Enter' && event.target.value) |
| 178 | handler(); |
| 179 | }.bind(null, openHandler), true); |
| 180 | |
| 181 | var newPageButton = document.createElement('button'); |
| 182 | newPageButton.textContent = 'Open'; |
| 183 | newPage.appendChild(newPageButton); |
| 184 | newPageButton.addEventListener('click', openHandler, true); |
| 185 | |
| 186 | browserSection.appendChild(newPage); |
| 187 | |
| 188 | pageList = document.createElement('div'); |
| 189 | pageList.className = 'list pages'; |
| 190 | browserSection.appendChild(pageList); |
| 191 | } |
| 192 | |
| 193 | if (alreadyDisplayed(browserSection, browser)) |
| 194 | continue; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 195 | |
| 196 | for (var p = 0; p < browser.pages.length; p++) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 197 | var page = browser.pages[p]; |
| 198 | var row = addTargetToList( |
| 199 | page, pageList, ['faviconUrl', 'name', 'url']); |
| 200 | row.appendChild(createActionLink( |
| 201 | 'reload', reload.bind(null, page), page.attached)); |
| 202 | row.appendChild(createActionLink( |
| 203 | 'close', terminate.bind(null, page), page.attached)); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 204 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 205 | } |
| 206 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 207 | } |
| 208 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 209 | function addToPagesList(data) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 210 | addTargetToList(data, $('pages'), ['faviconUrl', 'name', 'url']); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 211 | } |
| 212 | |
| 213 | function addToExtensionsList(data) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 214 | addTargetToList(data, $('extensions'), ['name', 'url']); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 215 | } |
| 216 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 217 | function addToAppsList(data) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 218 | addTargetToList(data, $('apps'), ['name', 'url']); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 219 | } |
| 220 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 221 | function addToWorkersList(data) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 222 | var row = addTargetToList(data, $('workers'), ['name', 'url', 'pid']); |
| 223 | row.appendChild(createActionLink( |
| 224 | 'terminate', terminate.bind(null, data), data.attached)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | function addToOthersList(data) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 228 | addTargetToList(data, $('others'), ['url']); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | function formatValue(data, property) { |
| 232 | var value = data[property]; |
| 233 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 234 | if (property == 'faviconUrl') { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 235 | var faviconElement = document.createElement('img'); |
| 236 | if (value) |
| 237 | faviconElement.src = value; |
| 238 | return faviconElement; |
| 239 | } |
| 240 | |
| 241 | var text = value ? String(value) : ''; |
| 242 | if (text.length > 100) |
| 243 | text = text.substring(0, 100) + '\u2026'; |
| 244 | |
| 245 | if (property == 'pid') |
| 246 | text = 'Pid:' + text; |
| 247 | |
| 248 | var span = document.createElement('span'); |
| 249 | span.textContent = ' ' + text + ' '; |
| 250 | span.className = property; |
| 251 | return span; |
| 252 | } |
| 253 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 254 | function addTargetToList(data, list, properties) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 255 | var row = document.createElement('div'); |
| 256 | row.className = 'row'; |
| 257 | for (var j = 0; j < properties.length; j++) |
| 258 | row.appendChild(formatValue(data, properties[j])); |
| 259 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 260 | row.appendChild(createActionLink('inspect', inspect.bind(null, data))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 261 | |
| 262 | row.processId = data.processId; |
| 263 | row.routeId = data.routeId; |
| 264 | |
| 265 | list.appendChild(row); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 266 | return row; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 267 | } |
| 268 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 269 | function createActionLink(text, handler, opt_disabled) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 270 | var link = document.createElement('a'); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 271 | if (opt_disabled) |
| 272 | link.classList.add('disabled'); |
| 273 | else |
| 274 | link.classList.remove('disabled'); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 275 | |
| 276 | link.setAttribute('href', '#'); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 277 | link.textContent = text; |
| 278 | link.addEventListener('click', handler, true); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 279 | return link; |
| 280 | } |
| 281 | |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 282 | |
| 283 | function initPortForwarding() { |
| 284 | $('port-forwarding-enable').addEventListener('change', enablePortForwarding); |
| 285 | |
| 286 | $('port-forwarding-config-open').addEventListener( |
| 287 | 'click', openPortForwardingConfig); |
| 288 | $('port-forwarding-config-close').addEventListener( |
| 289 | 'click', closePortForwardingConfig); |
| 290 | $('port-forwarding-config-done').addEventListener( |
| 291 | 'click', commitPortForwardingConfig); |
| 292 | } |
| 293 | |
| 294 | function enablePortForwarding(event) { |
| 295 | chrome.send('set-port-forwarding-enabled', [event.target.checked]); |
| 296 | } |
| 297 | |
| 298 | function handleKey(event) { |
| 299 | switch (event.keyCode) { |
| 300 | case 13: // Enter |
| 301 | if (event.target.nodeName == 'INPUT') { |
| 302 | var line = event.target.parentNode; |
| 303 | if (!line.classList.contains('fresh') || |
| 304 | line.classList.contains('empty')) |
| 305 | commitPortForwardingConfig(); |
| 306 | else |
| 307 | commitFreshLineIfValid(true /* select new line */); |
| 308 | } else { |
| 309 | commitPortForwardingConfig(); |
| 310 | } |
| 311 | break; |
| 312 | |
| 313 | case 27: |
| 314 | closePortForwardingConfig(); |
| 315 | break; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | function openPortForwardingConfig() { |
| 320 | loadPortForwardingConfig(window.portForwardingConfig); |
| 321 | |
| 322 | $('port-forwarding-overlay').classList.add('open'); |
| 323 | document.addEventListener('keyup', handleKey); |
| 324 | |
| 325 | var freshPort = document.querySelector('.fresh .port'); |
| 326 | if (freshPort) |
| 327 | freshPort.focus(); |
| 328 | else |
| 329 | $('port-forwarding-config-done').focus(); |
| 330 | } |
| 331 | |
| 332 | function closePortForwardingConfig() { |
| 333 | $('port-forwarding-overlay').classList.remove('open'); |
| 334 | document.removeEventListener('keyup', handleKey); |
| 335 | } |
| 336 | |
| 337 | function loadPortForwardingConfig(config) { |
| 338 | var list = $('port-forwarding-config-list'); |
| 339 | list.textContent = ''; |
| 340 | for (var port in config) |
| 341 | list.appendChild(createConfigLine(port, config[port])); |
| 342 | list.appendChild(createEmptyConfigLine()); |
| 343 | } |
| 344 | |
| 345 | function commitPortForwardingConfig() { |
| 346 | if (document.querySelector( |
| 347 | '.port-forwarding-pair:not(.fresh) input.invalid')) |
| 348 | return; |
| 349 | |
| 350 | if (document.querySelector( |
| 351 | '.port-forwarding-pair.fresh:not(.empty) input.invalid')) |
| 352 | return; |
| 353 | |
| 354 | closePortForwardingConfig(); |
| 355 | commitFreshLineIfValid(); |
| 356 | var lines = document.querySelectorAll('.port-forwarding-pair'); |
| 357 | var config = {}; |
| 358 | for (var i = 0; i != lines.length; i++) { |
| 359 | var line = lines[i]; |
| 360 | var portInput = line.querySelector('.port:not(.invalid)'); |
| 361 | var locationInput = line.querySelector('.location:not(.invalid)'); |
| 362 | if (portInput && locationInput) |
| 363 | config[portInput.value] = locationInput.value; |
| 364 | } |
| 365 | chrome.send('set-port-forwarding-config', [config]); |
| 366 | } |
| 367 | |
| 368 | function updatePortForwardingEnabled(enabled) { |
| 369 | var checkbox = $('port-forwarding-enable'); |
| 370 | checkbox.checked = !!enabled; |
| 371 | checkbox.disabled = false; |
| 372 | } |
| 373 | |
| 374 | function updatePortForwardingConfig(config) { |
| 375 | window.portForwardingConfig = config; |
| 376 | $('port-forwarding-config-open').disabled = !config; |
| 377 | } |
| 378 | |
| 379 | function createConfigLine(port, location) { |
| 380 | var line = document.createElement('div'); |
| 381 | line.className = 'port-forwarding-pair'; |
| 382 | |
| 383 | var portInput = createConfigField(port, 'port', 'Port', validatePort); |
| 384 | line.appendChild(portInput); |
| 385 | |
| 386 | var locationInput = createConfigField( |
| 387 | location, 'location', 'IP address and port', validateLocation); |
| 388 | line.appendChild(locationInput); |
| 389 | locationInput.addEventListener('keydown', function(e) { |
| 390 | if (e.keyIdentifier == 'U+0009' && // Tab |
| 391 | !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && |
| 392 | line.classList.contains('fresh') && |
| 393 | !line.classList.contains('empty')) { |
| 394 | // Tabbing forward on the fresh line, try create a new empty one. |
| 395 | commitFreshLineIfValid(true); |
| 396 | e.preventDefault(); |
| 397 | } |
| 398 | }); |
| 399 | |
| 400 | var lineDelete = document.createElement('div'); |
| 401 | lineDelete.className = 'close-button'; |
| 402 | lineDelete.addEventListener('click', function() { |
| 403 | var newSelection = line.nextElementSibling; |
| 404 | line.parentNode.removeChild(line); |
| 405 | selectLine(newSelection); |
| 406 | }); |
| 407 | line.appendChild(lineDelete); |
| 408 | |
| 409 | line.addEventListener('click', selectLine.bind(null, line)); |
| 410 | line.addEventListener('focus', selectLine.bind(null, line)); |
| 411 | |
| 412 | checkEmptyLine(line); |
| 413 | |
| 414 | return line; |
| 415 | } |
| 416 | |
| 417 | function validatePort(input) { |
| 418 | var match = input.value.match(/^(\d+)$/); |
| 419 | if (!match) |
| 420 | return false; |
| 421 | var port = parseInt(match[1]); |
| 422 | if (port < 5000 || 10000 < port) |
| 423 | return false; |
| 424 | |
| 425 | var inputs = document.querySelectorAll('input.port:not(.invalid)'); |
| 426 | for (var i = 0; i != inputs.length; ++i) { |
| 427 | if (inputs[i] == input) |
| 428 | break; |
| 429 | if (parseInt(inputs[i].value) == port) |
| 430 | return false; |
| 431 | } |
| 432 | return true; |
| 433 | } |
| 434 | |
| 435 | function validateLocation(input) { |
| 436 | var match = input.value.match(/^([a-zA-Z0-9\.]+):(\d+)$/); |
| 437 | if (!match) |
| 438 | return false; |
| 439 | var port = parseInt(match[2]); |
| 440 | return port <= 10000; |
| 441 | } |
| 442 | |
| 443 | function createEmptyConfigLine() { |
| 444 | var line = createConfigLine('', ''); |
| 445 | line.classList.add('fresh'); |
| 446 | return line; |
| 447 | } |
| 448 | |
| 449 | function createConfigField(value, className, hint, validate) { |
| 450 | var input = document.createElement('input'); |
| 451 | input.className = className; |
| 452 | input.type = 'text'; |
| 453 | input.placeholder = hint; |
| 454 | input.value = value; |
| 455 | |
| 456 | function checkInput() { |
| 457 | if (validate(input)) |
| 458 | input.classList.remove('invalid'); |
| 459 | else |
| 460 | input.classList.add('invalid'); |
| 461 | if (input.parentNode) |
| 462 | checkEmptyLine(input.parentNode); |
| 463 | } |
| 464 | checkInput(); |
| 465 | |
| 466 | input.addEventListener('keyup', checkInput); |
| 467 | input.addEventListener('focus', function() { |
| 468 | selectLine(input.parentNode); |
| 469 | }); |
| 470 | |
| 471 | return input; |
| 472 | } |
| 473 | |
| 474 | function checkEmptyLine(line) { |
| 475 | var inputs = line.querySelectorAll('input'); |
| 476 | var empty = true; |
| 477 | for (var i = 0; i != inputs.length; i++) { |
| 478 | if (inputs[i].value != '') |
| 479 | empty = false; |
| 480 | } |
| 481 | if (empty) |
| 482 | line.classList.add('empty'); |
| 483 | else |
| 484 | line.classList.remove('empty'); |
| 485 | } |
| 486 | |
| 487 | function selectLine(line) { |
| 488 | if (line.classList.contains('selected')) |
| 489 | return; |
| 490 | unselectLine(); |
| 491 | line.classList.add('selected'); |
| 492 | } |
| 493 | |
| 494 | function unselectLine() { |
| 495 | var line = document.querySelector('.port-forwarding-pair.selected'); |
| 496 | if (!line) |
| 497 | return; |
| 498 | line.classList.remove('selected'); |
| 499 | commitFreshLineIfValid(); |
| 500 | } |
| 501 | |
| 502 | function commitFreshLineIfValid(opt_selectNew) { |
| 503 | var line = document.querySelector('.port-forwarding-pair.fresh'); |
| 504 | if (line.querySelector('.invalid')) |
| 505 | return; |
| 506 | line.classList.remove('fresh'); |
| 507 | var freshLine = createEmptyConfigLine(); |
| 508 | line.parentNode.appendChild(freshLine); |
| 509 | if (opt_selectNew) |
| 510 | freshLine.querySelector('.port').focus(); |
| 511 | } |
| 512 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 513 | document.addEventListener('DOMContentLoaded', onload); |