Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(447)

Side by Side Diff: Source/web/resources/listPicker.js

Issue 736883002: Implement <select> Popup Menu using PagePopup (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 "use strict";
2 /*
tkent 2014/12/15 09:26:05 Use the 3-line header.
keishi 2014/12/16 03:53:24 Done.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND AN Y
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR AN Y
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND O N
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 var global = {
27 argumentsReceived: false,
28 params: null,
29 picker: null
30 };
31
32 /**
33 * @param {Event} event
34 */
35 function handleMessage(event) {
36 window.removeEventListener("message", handleMessage, false);
37 initialize(JSON.parse(event.data));
38 global.argumentsReceived = true;
39 }
40
41 /**
42 * @param {!Object} args
43 */
44 function initialize(args) {
45 global.params = args;
46 var main = $("main");
47 main.innerHTML = "";
48 global.picker = new ListPicker(main, args);
49 }
50
51 function handleArgumentsTimeout() {
52 if (global.argumentsReceived)
53 return;
54 var args = {
55 };
56 initialize(args);
57 }
58
59 /**
60 * @constructor
61 * @param {!Element} element
62 * @param {!Object} config
63 */
64 function ListPicker(element, config) {
65 Picker.call(this, element, config);
66 this._selectElement = createElement("select");
67 this._element.appendChild(this._selectElement);
68 this._layout();
69 this._selectElement.focus();
70 this._selectElement.addEventListener("mouseover", this._handleMouseOver.bind (this), false);
71 this._selectElement.addEventListener("mouseup", this._handleMouseUp.bind(thi s), false);
72 this._selectElement.addEventListener("keydown", this._handleKeyDown.bind(thi s), false);
73 this._selectElement.addEventListener("input", this._handleInput.bind(this), false);
74 window.addEventListener("message", this._handleWindowMessage.bind(this), fal se);
75 window.addEventListener("mousemove", this._handleWindowMouseMove.bind(this), false);
76 window.addEventListener("mousemove", this._handleWindowMouseMove.bind(this), false);
77 this.lastMousePositionX = Infinity;
78 this.lastMousePositionY = Infinity;
79
80 // Not sure why but we need to delay this call so that offsetHeight is
81 // accurate. We wait for the window to resize to work around an issue
82 // of immediate resize requests getting mixed up.
83 this._handleWindowDidHideBound = this._handleWindowDidHide.bind(this);
84 window.addEventListener("didHide", this._handleWindowDidHideBound, false);
85 hideWindow();
86 }
87 ListPicker.prototype = Object.create(Picker.prototype);
88
89 ListPicker.prototype._handleWindowDidHide = function() {
90 this._fixWindowSize();
91 var selectedOption = this._selectElement.options[this._selectElement.selecte dIndex];
92 selectedOption.scrollIntoView(false);
93 window.removeEventListener("didHide", this._handleWindowDidHideBound, false) ;
94 };
95
96 ListPicker.prototype._handleWindowMessage = function(event) {
97 eval(event.data);
98 if (window.updateData.type === "update")
99 this._update(window.updateData);
100 delete window.updateData;
101 };
102
103 ListPicker.prototype._handleWindowMouseMove = function (event) {
104 this.lastMousePositionX = event.clientX;
105 this.lastMousePositionY = event.clientY;
106 };
107
108 ListPicker.prototype._handleMouseOver = function(event) {
109 if (event.toElement.tagName !== "OPTION")
110 return;
111 var savedScrollTop = this._selectElement.scrollTop;
112 event.toElement.selected = true;
113 this._selectElement.scrollTop = savedScrollTop;
114 };
115
116 ListPicker.prototype._handleMouseUp = function(event) {
117 if (event.target.tagName !== "OPTION")
118 return;
119 window.pagePopupController.setValueAndClosePopup(0, this._selectElement.valu e);
120 };
121
122 ListPicker.prototype._handleInput = function(event) {
123 window.pagePopupController.setValue(this._selectElement.value);
124 };
125
126 ListPicker.prototype._handleKeyDown = function(event) {
127 var key = event.keyIdentifier;
128 if (key === "U+001B") { // ESC
129 window.pagePopupController.closePopup();
130 event.preventDefault();
131 } else if (key === "Enter") {
132 window.pagePopupController.setValueAndClosePopup(0, this._selectElement. value);
133 event.preventDefault();
134 } else if (event.altKey && (key === "Down" || key === "Up")) {
135 // FIXME: We need to add a delay here because, if we do it immediately
136 // the key press event will be handled by HTMLSelectElement and this
137 // popup will be reopened.
138 setTimeout(function () {
139 window.pagePopupController.closePopup();
140 }, 0);
141 event.preventDefault();
142 }
143 };
144
145 ListPicker.prototype._fixWindowSize = function() {
146 this._selectElement.style.height = "";
147 this._selectElement.size = 20;
148 var maxHeight = this._selectElement.offsetHeight;
149 this._selectElement.style.height = "0";
150 var heightOutsideOfContent = this._selectElement.offsetHeight - this._select Element.clientHeight;
151 var desiredWindowHeight = this._selectElement.scrollHeight + heightOutsideOf Content;
152 this._selectElement.style.height = desiredWindowHeight + "px";
153 // FIXME: scrollHeight returns floored value so we needed this check.
154 if (this._hasVerticalScrollbar())
155 desiredWindowHeight += 1;
156 if (desiredWindowHeight > maxHeight)
157 desiredWindowHeight = maxHeight;
158 var desiredWindowWidth = Math.max(this._config.anchorRectInScreen.width, thi s._selectElement.offsetWidth);
159 var windowRect = adjustWindowRect(desiredWindowWidth, desiredWindowHeight, t his._selectElement.offsetWidth, 0);
160 this._selectElement.style.width = windowRect.width + "px";
161 this._selectElement.style.height = windowRect.height + "px";
162 this._element.style.height = windowRect.height + "px";
163 setWindowRect(windowRect);
164 };
165
166 ListPicker.prototype._hasVerticalScrollbar = function() {
167 return this._selectElement.scrollWidth > this._selectElement.clientWidth;
168 };
169
170 ListPicker.prototype._listItemCount = function() {
171 return this._selectElement.querySelectorAll("option,optgroup,hr").length;
172 };
173
174 ListPicker.prototype._layout = function() {
175 for (var i = 0; i < this._config.children.length; ++i) {
176 this._selectElement.appendChild(this._createItemElement(this._config.chi ldren[i]));
177 }
178 this._selectElement.value = this._config.selectedIndex;
179 };
180
181 ListPicker.prototype._update = function(data) {
182 this._config.children = data.children;
183 var oldValue = this._selectElement.value;
184 while (this._selectElement.firstChild) {
185 this._selectElement.removeChild(this._selectElement.firstChild);
186 }
187 for (var i = 0; i < this._config.children.length; ++i) {
188 this._selectElement.appendChild(this._createItemElement(this._config.chi ldren[i]));
189 }
190 this._selectElement.value = this._config.selectedIndex;
191 var elementUnderMouse = document.elementFromPoint(this.lastMousePositionX, t his.lastMousePositionY);
192 var optionUnderMouse = elementUnderMouse && elementUnderMouse.closest("optio n");
193 if (optionUnderMouse)
194 optionUnderMouse.selected = true;
195 else
196 this._selectElement.value = oldValue;
197 this._fixWindowSize();
198 };
199
200 ListPicker.prototype._createItemElement = function(config) {
201 if (config.type === "option") {
202 var option = createElement("option");
203 option.appendChild(document.createTextNode(config.label));
204 option.value = config.value;
205 option.title = config.title;
206 option.disabled = config.disabled;
207 option.setAttribute("aria-label", config.ariaLabel);
208 this._applyItemStyle(option, config.style);
209 this._selectElement.appendChild(option);
210 return option;
211 } else if (config.type === "optgroup") {
212 var optgroup = createElement("optgroup");
213 optgroup.label = config.label;
214 optgroup.title = config.title;
215 optgroup.disabled = config.disabled;
216 optgroup.setAttribute("aria-label", config.ariaLabel);
217 this._applyItemStyle(optgroup, config.style);
218 for (var i = 0; i < config.children.length; ++i) {
219 optgroup.appendChild(this._createItemElement(config.children[i]));
220 }
221 this._selectElement.appendChild(optgroup);
222 return optgroup;
223 } else if (config.type === "separator") {
224 var hr = createElement("hr");
225 hr.title = config.title;
226 hr.disabled = config.disabled;
227 hr.setAttribute("aria-label", config.ariaLabel);
228 this._applyItemStyle(hr, config.style);
229 return hr;
230 }
231 };
232
233 ListPicker.prototype._applyItemStyle = function(element, styleConfig) {
234 element.style.color = styleConfig.color;
235 element.style.backgroundColor = styleConfig.backgroundColor;
236 element.style.fontSize = styleConfig.fontSize + "px";
237 element.style.fontWeight = styleConfig.fontWeight;
238 element.style.fontFamily = styleConfig.fontFamily.join(",");
239 element.style.visibility = styleConfig.visibility;
240 element.style.display = styleConfig.display;
241 element.style.direction = styleConfig.direction;
242 element.style.unicodeBidi = styleConfig.unicodeBidi;
243 };
244
245 if (window.dialogArguments) {
246 initialize(dialogArguments);
247 } else {
248 window.addEventListener("message", handleMessage, false);
249 window.setTimeout(handleArgumentsTimeout, 1000);
250 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698