Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / web / resources / pickerCommon.js
blobf0051ee9b5e05b5d7e5c334ebfe3ac2f196d49ff
1 "use strict";
2 /*
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 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.
24  */
26 /**
27  * @param {!string} id
28  */
29 function $(id) {
30     return document.getElementById(id);
33 /**
34  * @param {!string} tagName
35  * @param {string=} opt_class
36  * @param {string=} opt_text
37  * @return {!Element}
38  */
39 function createElement(tagName, opt_class, opt_text) {
40     var element = document.createElement(tagName);
41     if (opt_class)
42         element.setAttribute("class", opt_class);
43     if (opt_text)
44         element.appendChild(document.createTextNode(opt_text));
45     return element;
48 /**
49  * @constructor
50  * @param {!number|Rectangle|Object} xOrRect
51  * @param {!number} y
52  * @param {!number} width
53  * @param {!number} height
54  */
55 function Rectangle(xOrRect, y, width, height) {
56     if (typeof xOrRect === "object") {
57         y = xOrRect.y;
58         width = xOrRect.width;
59         height = xOrRect.height;
60         xOrRect = xOrRect.x;
61     }
62     this.x = xOrRect;
63     this.y = y;
64     this.width = width;
65     this.height = 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 + ")"; }
74 /**
75  * @param {!Rectangle} rect1
76  * @param {!Rectangle} rect2
77  * @return {?Rectangle}
78  */
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);
84     var width = maxX - x;
85     var height = maxY - y;
86     if (width < 0 || height < 0)
87         return null;
88     return new Rectangle(x, y, width, height);
91 /**
92  * @param {!number} width
93  * @param {!number} height
94  */
95 function resizeWindow(width, height) {
96     setWindowRect(adjustWindowRect(width, height, width, height));
99 /**
100  * @param {!number} width
101  * @param {!number} height
102  * @param {?number} minWidth
103  * @param {?number} minHeight
104  * @return {!Rectangle}
105  */
106 function adjustWindowRect(width, height, minWidth, minHeight) {
107     if (typeof minWidth !== "number")
108         minWidth = 0;
109     if (typeof minHeight !== "number")
110         minHeight = 0;
112     var windowRect = new Rectangle(0, 0, width, height);
114     if (!global.params.anchorRectInScreen)
115         return windowRect;
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);
123     return windowRect;
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;
136     } else {
137         windowRect.height = Math.min(windowRect.height, availableSpaceBelow);
138         windowRect.height = Math.max(windowRect.height, minHeight);
139         windowRect.y = anchorRect.maxY;
140     }
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
156  */
157 function setWindowRect(rect) {
158     if (window.frameElement) {
159         window.frameElement.style.width = rect.width + "px";
160         window.frameElement.style.height = rect.height + "px";
161     } else {
162         window.pagePopupController.setWindowRect(rect.x, rect.y, rect.width, rect.height);
163     }
166 function hideWindow() {
167     resizeWindow(1, 1);
171  * @return {!boolean}
172  */
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"));
180     else
181         window.dispatchEvent(new CustomEvent("didOpenPicker"));
182 }, false);
185  * @return {!number}
186  */
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);
197     }
198     return window.scrollbarWidth;
202  * @param {!string} className
203  * @return {?Element}
204  */
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))
209             return node;
210     }
211     return null;
215  * @constructor
216  */
217 function EventEmitter() {
221  * @param {!string} type
222  * @param {!function({...*})} callback
223  */
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)
235         return false;
236     var callbacksForType = this._callbacks[type];
237     if (!callbacksForType)
238         return false;
239     return callbacksForType.length > 0;
243  * @param {!string} type
244  * @param {!function(Object)} callback
245  */
246 EventEmitter.prototype.removeListener = function(type, callback) {
247     if (!this._callbacks)
248         return;
249     var callbacksForType = this._callbacks[type];
250     if (!callbacksForType)
251         return;
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
260  */
261 EventEmitter.prototype.dispatchEvent = function(type) {
262     if (!this._callbacks)
263         return;
264     var callbacksForType = this._callbacks[type];
265     if (!callbacksForType)
266         return;
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));
270     }
274  * @constructor
275  * @extends EventEmitter
276  * @param {!Element} element
277  * @param {!Object} config
278  */
279 function Picker(element, config) {
280     this._element = element;
281     this._config = config;
284 Picker.prototype = Object.create(EventEmitter.prototype);
287  * @enum {number}
288  */
289 Picker.Actions = {
290     SetValue: 0,
291     Cancel: -1,
292     ChooseOtherColor: -2
296  * @param {!string} value
297  */
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();
318 }, true);