Index: Source/web/resources/listPicker.js |
diff --git a/Source/web/resources/listPicker.js b/Source/web/resources/listPicker.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7fc66c510a21194a80ec6723362e6642c7d9f9fe |
--- /dev/null |
+++ b/Source/web/resources/listPicker.js |
@@ -0,0 +1,250 @@ |
+"use strict"; |
+/* |
tkent
2014/12/15 09:26:05
Use the 3-line header.
keishi
2014/12/16 03:53:24
Done.
|
+ * Copyright (C) 2012 Google Inc. All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions |
+ * are met: |
+ * 1. Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * 2. Redistributions in binary form must reproduce the above copyright |
+ * notice, this list of conditions and the following disclaimer in the |
+ * documentation and/or other materials provided with the distribution. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+var global = { |
+ argumentsReceived: false, |
+ params: null, |
+ picker: null |
+}; |
+ |
+/** |
+ * @param {Event} event |
+ */ |
+function handleMessage(event) { |
+ window.removeEventListener("message", handleMessage, false); |
+ initialize(JSON.parse(event.data)); |
+ global.argumentsReceived = true; |
+} |
+ |
+/** |
+ * @param {!Object} args |
+ */ |
+function initialize(args) { |
+ global.params = args; |
+ var main = $("main"); |
+ main.innerHTML = ""; |
+ global.picker = new ListPicker(main, args); |
+} |
+ |
+function handleArgumentsTimeout() { |
+ if (global.argumentsReceived) |
+ return; |
+ var args = { |
+ }; |
+ initialize(args); |
+} |
+ |
+/** |
+ * @constructor |
+ * @param {!Element} element |
+ * @param {!Object} config |
+ */ |
+function ListPicker(element, config) { |
+ Picker.call(this, element, config); |
+ this._selectElement = createElement("select"); |
+ this._element.appendChild(this._selectElement); |
+ this._layout(); |
+ this._selectElement.focus(); |
+ this._selectElement.addEventListener("mouseover", this._handleMouseOver.bind(this), false); |
+ this._selectElement.addEventListener("mouseup", this._handleMouseUp.bind(this), false); |
+ this._selectElement.addEventListener("keydown", this._handleKeyDown.bind(this), false); |
+ this._selectElement.addEventListener("input", this._handleInput.bind(this), false); |
+ window.addEventListener("message", this._handleWindowMessage.bind(this), false); |
+ window.addEventListener("mousemove", this._handleWindowMouseMove.bind(this), false); |
+ window.addEventListener("mousemove", this._handleWindowMouseMove.bind(this), false); |
+ this.lastMousePositionX = Infinity; |
+ this.lastMousePositionY = Infinity; |
+ |
+ // Not sure why but we need to delay this call so that offsetHeight is |
+ // accurate. We wait for the window to resize to work around an issue |
+ // of immediate resize requests getting mixed up. |
+ this._handleWindowDidHideBound = this._handleWindowDidHide.bind(this); |
+ window.addEventListener("didHide", this._handleWindowDidHideBound, false); |
+ hideWindow(); |
+} |
+ListPicker.prototype = Object.create(Picker.prototype); |
+ |
+ListPicker.prototype._handleWindowDidHide = function() { |
+ this._fixWindowSize(); |
+ var selectedOption = this._selectElement.options[this._selectElement.selectedIndex]; |
+ selectedOption.scrollIntoView(false); |
+ window.removeEventListener("didHide", this._handleWindowDidHideBound, false); |
+}; |
+ |
+ListPicker.prototype._handleWindowMessage = function(event) { |
+ eval(event.data); |
+ if (window.updateData.type === "update") |
+ this._update(window.updateData); |
+ delete window.updateData; |
+}; |
+ |
+ListPicker.prototype._handleWindowMouseMove = function (event) { |
+ this.lastMousePositionX = event.clientX; |
+ this.lastMousePositionY = event.clientY; |
+}; |
+ |
+ListPicker.prototype._handleMouseOver = function(event) { |
+ if (event.toElement.tagName !== "OPTION") |
+ return; |
+ var savedScrollTop = this._selectElement.scrollTop; |
+ event.toElement.selected = true; |
+ this._selectElement.scrollTop = savedScrollTop; |
+}; |
+ |
+ListPicker.prototype._handleMouseUp = function(event) { |
+ if (event.target.tagName !== "OPTION") |
+ return; |
+ window.pagePopupController.setValueAndClosePopup(0, this._selectElement.value); |
+}; |
+ |
+ListPicker.prototype._handleInput = function(event) { |
+ window.pagePopupController.setValue(this._selectElement.value); |
+}; |
+ |
+ListPicker.prototype._handleKeyDown = function(event) { |
+ var key = event.keyIdentifier; |
+ if (key === "U+001B") { // ESC |
+ window.pagePopupController.closePopup(); |
+ event.preventDefault(); |
+ } else if (key === "Enter") { |
+ window.pagePopupController.setValueAndClosePopup(0, this._selectElement.value); |
+ event.preventDefault(); |
+ } else if (event.altKey && (key === "Down" || key === "Up")) { |
+ // FIXME: We need to add a delay here because, if we do it immediately |
+ // the key press event will be handled by HTMLSelectElement and this |
+ // popup will be reopened. |
+ setTimeout(function () { |
+ window.pagePopupController.closePopup(); |
+ }, 0); |
+ event.preventDefault(); |
+ } |
+}; |
+ |
+ListPicker.prototype._fixWindowSize = function() { |
+ this._selectElement.style.height = ""; |
+ this._selectElement.size = 20; |
+ var maxHeight = this._selectElement.offsetHeight; |
+ this._selectElement.style.height = "0"; |
+ var heightOutsideOfContent = this._selectElement.offsetHeight - this._selectElement.clientHeight; |
+ var desiredWindowHeight = this._selectElement.scrollHeight + heightOutsideOfContent; |
+ this._selectElement.style.height = desiredWindowHeight + "px"; |
+ // FIXME: scrollHeight returns floored value so we needed this check. |
+ if (this._hasVerticalScrollbar()) |
+ desiredWindowHeight += 1; |
+ if (desiredWindowHeight > maxHeight) |
+ desiredWindowHeight = maxHeight; |
+ var desiredWindowWidth = Math.max(this._config.anchorRectInScreen.width, this._selectElement.offsetWidth); |
+ var windowRect = adjustWindowRect(desiredWindowWidth, desiredWindowHeight, this._selectElement.offsetWidth, 0); |
+ this._selectElement.style.width = windowRect.width + "px"; |
+ this._selectElement.style.height = windowRect.height + "px"; |
+ this._element.style.height = windowRect.height + "px"; |
+ setWindowRect(windowRect); |
+}; |
+ |
+ListPicker.prototype._hasVerticalScrollbar = function() { |
+ return this._selectElement.scrollWidth > this._selectElement.clientWidth; |
+}; |
+ |
+ListPicker.prototype._listItemCount = function() { |
+ return this._selectElement.querySelectorAll("option,optgroup,hr").length; |
+}; |
+ |
+ListPicker.prototype._layout = function() { |
+ for (var i = 0; i < this._config.children.length; ++i) { |
+ this._selectElement.appendChild(this._createItemElement(this._config.children[i])); |
+ } |
+ this._selectElement.value = this._config.selectedIndex; |
+}; |
+ |
+ListPicker.prototype._update = function(data) { |
+ this._config.children = data.children; |
+ var oldValue = this._selectElement.value; |
+ while (this._selectElement.firstChild) { |
+ this._selectElement.removeChild(this._selectElement.firstChild); |
+ } |
+ for (var i = 0; i < this._config.children.length; ++i) { |
+ this._selectElement.appendChild(this._createItemElement(this._config.children[i])); |
+ } |
+ this._selectElement.value = this._config.selectedIndex; |
+ var elementUnderMouse = document.elementFromPoint(this.lastMousePositionX, this.lastMousePositionY); |
+ var optionUnderMouse = elementUnderMouse && elementUnderMouse.closest("option"); |
+ if (optionUnderMouse) |
+ optionUnderMouse.selected = true; |
+ else |
+ this._selectElement.value = oldValue; |
+ this._fixWindowSize(); |
+}; |
+ |
+ListPicker.prototype._createItemElement = function(config) { |
+ if (config.type === "option") { |
+ var option = createElement("option"); |
+ option.appendChild(document.createTextNode(config.label)); |
+ option.value = config.value; |
+ option.title = config.title; |
+ option.disabled = config.disabled; |
+ option.setAttribute("aria-label", config.ariaLabel); |
+ this._applyItemStyle(option, config.style); |
+ this._selectElement.appendChild(option); |
+ return option; |
+ } else if (config.type === "optgroup") { |
+ var optgroup = createElement("optgroup"); |
+ optgroup.label = config.label; |
+ optgroup.title = config.title; |
+ optgroup.disabled = config.disabled; |
+ optgroup.setAttribute("aria-label", config.ariaLabel); |
+ this._applyItemStyle(optgroup, config.style); |
+ for (var i = 0; i < config.children.length; ++i) { |
+ optgroup.appendChild(this._createItemElement(config.children[i])); |
+ } |
+ this._selectElement.appendChild(optgroup); |
+ return optgroup; |
+ } else if (config.type === "separator") { |
+ var hr = createElement("hr"); |
+ hr.title = config.title; |
+ hr.disabled = config.disabled; |
+ hr.setAttribute("aria-label", config.ariaLabel); |
+ this._applyItemStyle(hr, config.style); |
+ return hr; |
+ } |
+}; |
+ |
+ListPicker.prototype._applyItemStyle = function(element, styleConfig) { |
+ element.style.color = styleConfig.color; |
+ element.style.backgroundColor = styleConfig.backgroundColor; |
+ element.style.fontSize = styleConfig.fontSize + "px"; |
+ element.style.fontWeight = styleConfig.fontWeight; |
+ element.style.fontFamily = styleConfig.fontFamily.join(","); |
+ element.style.visibility = styleConfig.visibility; |
+ element.style.display = styleConfig.display; |
+ element.style.direction = styleConfig.direction; |
+ element.style.unicodeBidi = styleConfig.unicodeBidi; |
+}; |
+ |
+if (window.dialogArguments) { |
+ initialize(dialogArguments); |
+} else { |
+ window.addEventListener("message", handleMessage, false); |
+ window.setTimeout(handleArgumentsTimeout, 1000); |
+} |