3 * Copyright (C) 2012 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
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 ANY
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 ON
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.
30 return document.getElementById(id);
34 * @param {!string} tagName
35 * @param {string=} opt_class
36 * @param {string=} opt_text
39 function createElement(tagName, opt_class, opt_text) {
40 var element = document.createElement(tagName);
42 element.setAttribute("class", opt_class);
44 element.appendChild(document.createTextNode(opt_text));
50 * @param {!number|Rectangle|Object} xOrRect
52 * @param {!number} width
53 * @param {!number} height
55 function Rectangle(xOrRect, y, width, height) {
56 if (typeof xOrRect === "object") {
58 width = xOrRect.width;
59 height = xOrRect.height;
68 Rectangle.prototype = {
69 get maxX() { return this.x + this.width; },
70 get maxY() { return this.y + this.height; },
71 toString: function() { return "Rectangle(" + this.x + "," + this.y + "," + this.width + "," + this.height + ")"; }
75 * @param {!Rectangle} rect1
76 * @param {!Rectangle} rect2
77 * @return {?Rectangle}
79 Rectangle.intersection = function(rect1, rect2) {
80 var x = Math.max(rect1.x, rect2.x);
81 var maxX = Math.min(rect1.maxX, rect2.maxX);
82 var y = Math.max(rect1.y, rect2.y);
83 var maxY = Math.min(rect1.maxY, rect2.maxY);
85 var height = maxY - y;
86 if (width < 0 || height < 0)
88 return new Rectangle(x, y, width, height);
92 * @param {!number} width
93 * @param {!number} height
95 function resizeWindow(width, height) {
96 setWindowRect(adjustWindowRect(width, height, width, height));
100 * @param {!number} width
101 * @param {!number} height
102 * @param {?number} minWidth
103 * @param {?number} minHeight
104 * @return {!Rectangle}
106 function adjustWindowRect(width, height, minWidth, minHeight) {
107 if (typeof minWidth !== "number")
109 if (typeof minHeight !== "number")
112 var windowRect = new Rectangle(0, 0, width, height);
114 if (!global.params.anchorRectInScreen)
117 var anchorRect = new Rectangle(global.params.anchorRectInScreen);
118 var availRect = new Rectangle(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
120 _adjustWindowRectVertically(windowRect, availRect, anchorRect, minHeight);
121 _adjustWindowRectHorizontally(windowRect, availRect, anchorRect, minWidth);
126 function _adjustWindowRectVertically(windowRect, availRect, anchorRect, minHeight) {
127 var availableSpaceAbove = anchorRect.y - availRect.y;
128 availableSpaceAbove = Math.max(0, Math.min(availRect.height, availableSpaceAbove));
130 var availableSpaceBelow = availRect.maxY - anchorRect.maxY;
131 availableSpaceBelow = Math.max(0, Math.min(availRect.height, availableSpaceBelow));
132 if (windowRect.height > availableSpaceBelow && availableSpaceBelow < availableSpaceAbove) {
133 windowRect.height = Math.min(windowRect.height, availableSpaceAbove);
134 windowRect.height = Math.max(windowRect.height, minHeight);
135 windowRect.y = anchorRect.y - windowRect.height;
137 windowRect.height = Math.min(windowRect.height, availableSpaceBelow);
138 windowRect.height = Math.max(windowRect.height, minHeight);
139 windowRect.y = anchorRect.maxY;
143 function _adjustWindowRectHorizontally(windowRect, availRect, anchorRect, minWidth) {
144 windowRect.width = Math.min(windowRect.width, availRect.width);
145 windowRect.width = Math.max(windowRect.width, minWidth);
146 windowRect.x = anchorRect.x;
147 // If we are getting clipped, we want to switch alignment to the right side
148 // of the anchor rect as long as doing so will make the popup not clipped.
149 var rightAlignedX = windowRect.x + anchorRect.width - windowRect.width;
150 if (rightAlignedX >= availRect.x && (windowRect.maxX > availRect.maxX || global.params.isRTL))
151 windowRect.x = rightAlignedX;
155 * @param {!Rectangle} rect
157 function setWindowRect(rect) {
158 if (window.frameElement) {
159 window.frameElement.style.width = rect.width + "px";
160 window.frameElement.style.height = rect.height + "px";
162 window.pagePopupController.setWindowRect(rect.x, rect.y, rect.width, rect.height);
166 function hideWindow() {
173 function isWindowHidden() {
174 return window.innerWidth === 1 && window.innerHeight === 1;
177 window.addEventListener("resize", function() {
178 if (isWindowHidden())
179 window.dispatchEvent(new CustomEvent("didHide"));
181 window.dispatchEvent(new CustomEvent("didOpenPicker"));
187 function getScrollbarWidth() {
188 if (typeof window.scrollbarWidth === "undefined") {
189 var scrollDiv = document.createElement("div");
190 scrollDiv.style.opacity = "0";
191 scrollDiv.style.overflow = "scroll";
192 scrollDiv.style.width = "50px";
193 scrollDiv.style.height = "50px";
194 document.body.appendChild(scrollDiv);
195 window.scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
196 scrollDiv.parentNode.removeChild(scrollDiv);
198 return window.scrollbarWidth;
202 * @param {!string} className
205 function enclosingNodeOrSelfWithClass(selfNode, className)
207 for (var node = selfNode; node && node !== selfNode.ownerDocument; node = node.parentNode) {
208 if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains(className))
217 function EventEmitter() {
221 * @param {!string} type
222 * @param {!function({...*})} callback
224 EventEmitter.prototype.on = function(type, callback) {
225 console.assert(callback instanceof Function);
226 if (!this._callbacks)
227 this._callbacks = {};
228 if (!this._callbacks[type])
229 this._callbacks[type] = [];
230 this._callbacks[type].push(callback);
233 EventEmitter.prototype.hasListener = function(type) {
234 if (!this._callbacks)
236 var callbacksForType = this._callbacks[type];
237 if (!callbacksForType)
239 return callbacksForType.length > 0;
243 * @param {!string} type
244 * @param {!function(Object)} callback
246 EventEmitter.prototype.removeListener = function(type, callback) {
247 if (!this._callbacks)
249 var callbacksForType = this._callbacks[type];
250 if (!callbacksForType)
252 callbacksForType.splice(callbacksForType.indexOf(callback), 1);
253 if (callbacksForType.length === 0)
254 delete this._callbacks[type];
258 * @param {!string} type
259 * @param {...*} var_args
261 EventEmitter.prototype.dispatchEvent = function(type) {
262 if (!this._callbacks)
264 var callbacksForType = this._callbacks[type];
265 if (!callbacksForType)
267 callbacksForType = callbacksForType.slice(0);
268 for (var i = 0; i < callbacksForType.length; ++i) {
269 callbacksForType[i].apply(this, Array.prototype.slice.call(arguments, 1));
275 * @extends EventEmitter
276 * @param {!Element} element
277 * @param {!Object} config
279 function Picker(element, config) {
280 this._element = element;
281 this._config = config;
284 Picker.prototype = Object.create(EventEmitter.prototype);
296 * @param {!string} value
298 Picker.prototype.submitValue = function(value) {
299 window.pagePopupController.setValue(value);
300 window.pagePopupController.closePopup();
303 Picker.prototype.handleCancel = function() {
304 window.pagePopupController.closePopup();
307 Picker.prototype.chooseOtherColor = function() {
308 window.pagePopupController.setValueAndClosePopup(Picker.Actions.ChooseOtherColor, "");
311 Picker.prototype.cleanup = function() {};
313 window.addEventListener("keyup", function(event) {
314 // JAWS dispatches extra Alt events and unless we handle them they move the
315 // focus and close the popup.
316 if (event.keyIdentifier === "Alt")
317 event.preventDefault();