1/* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.Object} 34 * @param {!Element} element 35 */ 36WebInspector.StatusBarItem = function(element) 37{ 38 this.element = element; 39 this._enabled = true; 40} 41 42WebInspector.StatusBarItem.prototype = { 43 /** 44 * @param {boolean} value 45 */ 46 setEnabled: function(value) 47 { 48 if (this._enabled === value) 49 return; 50 this._enabled = value; 51 this._applyEnabledState(); 52 }, 53 54 /** 55 * @protected 56 */ 57 _applyEnabledState: function() 58 { 59 this.element.disabled = !this._enabled; 60 }, 61 62 __proto__: WebInspector.Object.prototype 63} 64 65/** 66 * @constructor 67 * @extends {WebInspector.StatusBarItem} 68 * @param {string} text 69 * @param {string=} className 70 */ 71WebInspector.StatusBarText = function(text, className) 72{ 73 WebInspector.StatusBarItem.call(this, document.createElement("span")); 74 this.element.className = "status-bar-item status-bar-text"; 75 if (className) 76 this.element.classList.add(className); 77 this.element.textContent = text; 78} 79 80WebInspector.StatusBarText.prototype = { 81 /** 82 * @param {string} text 83 */ 84 setText: function(text) 85 { 86 this.element.textContent = text; 87 }, 88 89 __proto__: WebInspector.StatusBarItem.prototype 90} 91 92 93/** 94 * @constructor 95 * @extends {WebInspector.StatusBarItem} 96 * @param {string} title 97 * @param {string} className 98 * @param {number=} states 99 */ 100WebInspector.StatusBarButton = function(title, className, states) 101{ 102 WebInspector.StatusBarItem.call(this, document.createElement("button")); 103 this.element.className = className + " status-bar-item"; 104 this.element.addEventListener("click", this._clicked.bind(this), false); 105 106 this.glyph = document.createElement("div"); 107 this.glyph.className = "glyph"; 108 this.element.appendChild(this.glyph); 109 110 this.glyphShadow = document.createElement("div"); 111 this.glyphShadow.className = "glyph shadow"; 112 this.element.appendChild(this.glyphShadow); 113 114 this.states = states; 115 if (!states) 116 this.states = 2; 117 118 if (states == 2) 119 this._state = false; 120 else 121 this._state = 0; 122 123 this.title = title; 124 this.className = className; 125 this._visible = true; 126} 127 128WebInspector.StatusBarButton.prototype = { 129 _clicked: function() 130 { 131 this.dispatchEventToListeners("click"); 132 if (this._longClickInterval) { 133 clearInterval(this._longClickInterval); 134 delete this._longClickInterval; 135 } 136 }, 137 138 /** 139 * @override 140 */ 141 _applyEnabledState: function() 142 { 143 this.element.disabled = !this._enabled; 144 if (this._longClickInterval) { 145 clearInterval(this._longClickInterval); 146 delete this._longClickInterval; 147 } 148 }, 149 150 /** 151 * @return {boolean} 152 */ 153 enabled: function() 154 { 155 return this._enabled; 156 }, 157 158 get title() 159 { 160 return this._title; 161 }, 162 163 set title(x) 164 { 165 if (this._title === x) 166 return; 167 this._title = x; 168 this.element.title = x; 169 }, 170 171 get state() 172 { 173 return this._state; 174 }, 175 176 set state(x) 177 { 178 if (this._state === x) 179 return; 180 181 if (this.states === 2) 182 this.element.enableStyleClass("toggled-on", x); 183 else { 184 this.element.classList.remove("toggled-" + this._state); 185 if (x !== 0) 186 this.element.classList.add("toggled-" + x); 187 } 188 this._state = x; 189 }, 190 191 get toggled() 192 { 193 if (this.states !== 2) 194 throw("Only used toggled when there are 2 states, otherwise, use state"); 195 return this.state; 196 }, 197 198 set toggled(x) 199 { 200 if (this.states !== 2) 201 throw("Only used toggled when there are 2 states, otherwise, use state"); 202 this.state = x; 203 }, 204 205 get visible() 206 { 207 return this._visible; 208 }, 209 210 set visible(x) 211 { 212 if (this._visible === x) 213 return; 214 215 this.element.enableStyleClass("hidden", !x); 216 this._visible = x; 217 }, 218 219 makeLongClickEnabled: function() 220 { 221 var boundMouseDown = mouseDown.bind(this); 222 var boundMouseUp = mouseUp.bind(this); 223 224 this.element.addEventListener("mousedown", boundMouseDown, false); 225 this.element.addEventListener("mouseout", boundMouseUp, false); 226 this.element.addEventListener("mouseup", boundMouseUp, false); 227 228 var longClicks = 0; 229 230 this._longClickData = { mouseUp: boundMouseUp, mouseDown: boundMouseDown }; 231 232 /** 233 * @param {?Event} e 234 * @this {WebInspector.StatusBarButton} 235 */ 236 function mouseDown(e) 237 { 238 if (e.which !== 1) 239 return; 240 longClicks = 0; 241 this._longClickInterval = setInterval(longClicked.bind(this), 200); 242 } 243 244 /** 245 * @param {?Event} e 246 * @this {WebInspector.StatusBarButton} 247 */ 248 function mouseUp(e) 249 { 250 if (e.which !== 1) 251 return; 252 if (this._longClickInterval) { 253 clearInterval(this._longClickInterval); 254 delete this._longClickInterval; 255 } 256 } 257 258 /** 259 * @this {WebInspector.StatusBarButton} 260 */ 261 function longClicked() 262 { 263 ++longClicks; 264 this.dispatchEventToListeners(longClicks === 1 ? "longClickDown" : "longClickPress"); 265 } 266 }, 267 268 unmakeLongClickEnabled: function() 269 { 270 if (!this._longClickData) 271 return; 272 this.element.removeEventListener("mousedown", this._longClickData.mouseDown, false); 273 this.element.removeEventListener("mouseout", this._longClickData.mouseUp, false); 274 this.element.removeEventListener("mouseup", this._longClickData.mouseUp, false); 275 delete this._longClickData; 276 }, 277 278 /** 279 * @param {?function():!Array.<!WebInspector.StatusBarButton>} buttonsProvider 280 */ 281 setLongClickOptionsEnabled: function(buttonsProvider) 282 { 283 if (buttonsProvider) { 284 if (!this._longClickOptionsData) { 285 this.makeLongClickEnabled(); 286 287 this.longClickGlyph = document.createElement("div"); 288 this.longClickGlyph.className = "fill long-click-glyph"; 289 this.element.appendChild(this.longClickGlyph); 290 291 this.longClickGlyphShadow = document.createElement("div"); 292 this.longClickGlyphShadow.className = "fill long-click-glyph shadow"; 293 this.element.appendChild(this.longClickGlyphShadow); 294 295 var longClickDownListener = this._showOptions.bind(this); 296 this.addEventListener("longClickDown", longClickDownListener, this); 297 298 this._longClickOptionsData = { 299 glyphElement: this.longClickGlyph, 300 glyphShadowElement: this.longClickGlyphShadow, 301 longClickDownListener: longClickDownListener 302 }; 303 } 304 this._longClickOptionsData.buttonsProvider = buttonsProvider; 305 } else { 306 if (!this._longClickOptionsData) 307 return; 308 this.element.removeChild(this._longClickOptionsData.glyphElement); 309 this.element.removeChild(this._longClickOptionsData.glyphShadowElement); 310 311 this.removeEventListener("longClickDown", this._longClickOptionsData.longClickDownListener, this); 312 delete this._longClickOptionsData; 313 314 this.unmakeLongClickEnabled(); 315 } 316 }, 317 318 _showOptions: function() 319 { 320 var buttons = this._longClickOptionsData.buttonsProvider(); 321 var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states); 322 mainButtonClone.addEventListener("click", this._clicked, this); 323 mainButtonClone.state = this.state; 324 buttons.push(mainButtonClone); 325 326 var mouseUpListener = mouseUp.bind(this); 327 document.documentElement.addEventListener("mouseup", mouseUpListener, false); 328 329 var optionsGlassPane = new WebInspector.GlassPane(); 330 var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar"); 331 const buttonHeight = 23; 332 333 var hostButtonPosition = this.element.totalOffset(); 334 335 var topNotBottom = hostButtonPosition.top + buttonHeight * buttons.length < document.documentElement.offsetHeight; 336 337 if (topNotBottom) 338 buttons = buttons.reverse(); 339 340 optionsBarElement.style.height = (buttonHeight * buttons.length) + "px"; 341 if (topNotBottom) 342 optionsBarElement.style.top = (hostButtonPosition.top + 1) + "px"; 343 else 344 optionsBarElement.style.top = (hostButtonPosition.top - (buttonHeight * (buttons.length - 1))) + "px"; 345 optionsBarElement.style.left = (hostButtonPosition.left + 1) + "px"; 346 347 var boundMouseOver = mouseOver.bind(this); 348 var boundMouseOut = mouseOut.bind(this); 349 for (var i = 0; i < buttons.length; ++i) { 350 buttons[i].element.addEventListener("mousemove", boundMouseOver, false); 351 buttons[i].element.addEventListener("mouseout", boundMouseOut, false); 352 optionsBarElement.appendChild(buttons[i].element); 353 } 354 var hostButtonIndex = topNotBottom ? 0 : buttons.length - 1; 355 buttons[hostButtonIndex].element.classList.add("emulate-active"); 356 357 function mouseOver(e) 358 { 359 if (e.which !== 1) 360 return; 361 var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item"); 362 buttonElement.classList.add("emulate-active"); 363 } 364 365 function mouseOut(e) 366 { 367 if (e.which !== 1) 368 return; 369 var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item"); 370 buttonElement.classList.remove("emulate-active"); 371 } 372 373 function mouseUp(e) 374 { 375 if (e.which !== 1) 376 return; 377 optionsGlassPane.dispose(); 378 document.documentElement.removeEventListener("mouseup", mouseUpListener, false); 379 380 for (var i = 0; i < buttons.length; ++i) { 381 if (buttons[i].element.classList.contains("emulate-active")) { 382 buttons[i].element.classList.remove("emulate-active"); 383 buttons[i]._clicked(); 384 break; 385 } 386 } 387 } 388 }, 389 390 __proto__: WebInspector.StatusBarItem.prototype 391} 392 393/** 394 * @constructor 395 * @extends {WebInspector.StatusBarItem} 396 * @param {?function(!Event)} changeHandler 397 * @param {string=} className 398 */ 399WebInspector.StatusBarComboBox = function(changeHandler, className) 400{ 401 WebInspector.StatusBarItem.call(this, document.createElement("span")); 402 this.element.className = "status-bar-select-container"; 403 404 this._selectElement = this.element.createChild("select", "status-bar-item"); 405 this.element.createChild("div", "status-bar-select-arrow"); 406 if (changeHandler) 407 this._selectElement.addEventListener("change", changeHandler, false); 408 if (className) 409 this._selectElement.classList.add(className); 410} 411 412WebInspector.StatusBarComboBox.prototype = { 413 /** 414 * @return {!Element} 415 */ 416 selectElement: function() 417 { 418 return this._selectElement; 419 }, 420 421 /** 422 * @return {number} 423 */ 424 size: function() 425 { 426 return this._selectElement.childElementCount; 427 }, 428 429 /** 430 * @param {!Element} option 431 */ 432 addOption: function(option) 433 { 434 this._selectElement.appendChild(option); 435 }, 436 437 /** 438 * @param {string} label 439 * @param {string=} title 440 * @param {string=} value 441 * @return {!Element} 442 */ 443 createOption: function(label, title, value) 444 { 445 var option = this._selectElement.createChild("option"); 446 option.text = label; 447 if (title) 448 option.title = title; 449 if (typeof value !== "undefined") 450 option.value = value; 451 return option; 452 }, 453 454 /** 455 * @override 456 */ 457 _applyEnabledState: function() 458 { 459 this._selectElement.disabled = !this._enabled; 460 }, 461 462 /** 463 * @param {!Element} option 464 */ 465 removeOption: function(option) 466 { 467 this._selectElement.removeChild(option); 468 }, 469 470 removeOptions: function() 471 { 472 this._selectElement.removeChildren(); 473 }, 474 475 /** 476 * @return {?Element} 477 */ 478 selectedOption: function() 479 { 480 if (this._selectElement.selectedIndex >= 0) 481 return this._selectElement[this._selectElement.selectedIndex]; 482 return null; 483 }, 484 485 /** 486 * @param {!Element} option 487 */ 488 select: function(option) 489 { 490 this._selectElement.selectedIndex = Array.prototype.indexOf.call(this._selectElement, option); 491 }, 492 493 /** 494 * @param {number} index 495 */ 496 setSelectedIndex: function(index) 497 { 498 this._selectElement.selectedIndex = index; 499 }, 500 501 /** 502 * @return {number} 503 */ 504 selectedIndex: function() 505 { 506 return this._selectElement.selectedIndex; 507 }, 508 509 __proto__: WebInspector.StatusBarItem.prototype 510} 511 512/** 513 * @constructor 514 * @extends {WebInspector.StatusBarItem} 515 * @param {string} title 516 */ 517WebInspector.StatusBarCheckbox = function(title) 518{ 519 WebInspector.StatusBarItem.call(this, document.createElement("label")); 520 this.element.classList.add("status-bar-item", "checkbox"); 521 this._checkbox = this.element.createChild("input"); 522 this._checkbox.type = "checkbox"; 523 this.element.createTextChild(title); 524} 525 526WebInspector.StatusBarCheckbox.prototype = { 527 /** 528 * @return {boolean} 529 */ 530 checked: function() 531 { 532 return this._checkbox.checked; 533 }, 534 535 __proto__: WebInspector.StatusBarItem.prototype 536} 537