1/*
2 * Copyright (C) 2012 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 * @param {Element} relativeToElement
34 * @param {WebInspector.DialogDelegate} delegate
35 */
36WebInspector.Dialog = function(relativeToElement, delegate)
37{
38    this._delegate = delegate;
39    this._relativeToElement = relativeToElement;
40
41    this._glassPane = new WebInspector.GlassPane();
42    // Install glass pane capturing events.
43    this._glassPane.element.tabIndex = 0;
44    this._glassPane.element.addEventListener("focus", this._onGlassPaneFocus.bind(this), false);
45
46    this._element = this._glassPane.element.createChild("div");
47    this._element.tabIndex = 0;
48    this._element.addEventListener("focus", this._onFocus.bind(this), false);
49    this._element.addEventListener("keydown", this._onKeyDown.bind(this), false);
50    this._closeKeys = [
51        WebInspector.KeyboardShortcut.Keys.Enter.code,
52        WebInspector.KeyboardShortcut.Keys.Esc.code,
53    ];
54
55    delegate.show(this._element);
56
57    this._position();
58    this._windowResizeHandler = this._position.bind(this);
59    window.addEventListener("resize", this._windowResizeHandler, true);
60    this._delegate.focus();
61}
62
63/**
64 * @return {WebInspector.Dialog}
65 */
66WebInspector.Dialog.currentInstance = function()
67{
68    return WebInspector.Dialog._instance;
69}
70
71/**
72 * @param {Element} relativeToElement
73 * @param {WebInspector.DialogDelegate} delegate
74 */
75WebInspector.Dialog.show = function(relativeToElement, delegate)
76{
77    if (WebInspector.Dialog._instance)
78        return;
79    WebInspector.Dialog._instance = new WebInspector.Dialog(relativeToElement, delegate);
80}
81
82WebInspector.Dialog.hide = function()
83{
84    if (!WebInspector.Dialog._instance)
85        return;
86    WebInspector.Dialog._instance._hide();
87}
88
89WebInspector.Dialog.prototype = {
90    _hide: function()
91    {
92        if (this._isHiding)
93            return;
94        this._isHiding = true;
95
96        this._delegate.willHide();
97
98        delete WebInspector.Dialog._instance;
99        this._glassPane.dispose();
100        window.removeEventListener("resize", this._windowResizeHandler, true);
101    },
102
103    _onGlassPaneFocus: function(event)
104    {
105        this._hide();
106    },
107
108    _onFocus: function(event)
109    {
110        this._delegate.focus();
111    },
112
113    _position: function()
114    {
115        this._delegate.position(this._element, this._relativeToElement);
116    },
117
118    _onKeyDown: function(event)
119    {
120        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) {
121            event.preventDefault();
122            return;
123        }
124
125        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code)
126            this._delegate.onEnter();
127
128        if (this._closeKeys.indexOf(event.keyCode) >= 0) {
129            this._hide();
130            event.consume(true);
131        }
132    }
133};
134
135/**
136 * @constructor
137 * @extends {WebInspector.Object}
138 */
139WebInspector.DialogDelegate = function()
140{
141}
142
143WebInspector.DialogDelegate.prototype = {
144    /**
145     * @param {Element} element
146     */
147    show: function(element)
148    {
149        element.appendChild(this.element);
150        this.element.addStyleClass("dialog-contents");
151        element.addStyleClass("dialog");
152    },
153
154    /**
155     * @param {Element} element
156     * @param {Element} relativeToElement
157     */
158    position: function(element, relativeToElement)
159    {
160        var offset = relativeToElement.offsetRelativeToWindow(window);
161
162        var positionX = offset.x + (relativeToElement.offsetWidth - element.offsetWidth) / 2;
163        positionX = Number.constrain(positionX, 0, window.innerWidth - element.offsetWidth);
164
165        var positionY = offset.y + (relativeToElement.offsetHeight - element.offsetHeight) / 2;
166        positionY = Number.constrain(positionY, 0, window.innerHeight - element.offsetHeight);
167
168        element.style.left = positionX + "px";
169        element.style.top = positionY + "px";
170    },
171
172    focus: function() { },
173
174    onEnter: function() { },
175
176    willHide: function() { },
177
178    __proto__: WebInspector.Object.prototype
179}
180
181