1
// Copyright 2007, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
* Initialize the base functionality of the dialog.
29
function initDialog() {
30
if (!browser.ie_mobile) {
31
dom.addEvent(document, "keyup", handleKeyUp);
34
if (browser.wince && browser.opera) {
38
if (browser.ie_mobile) {
39
if (!window.pie_dialog.IsSmartPhone()) {
43
window.pie_dialog.SetScriptContext(window);
44
window.pie_dialog.ResizeDialog();
49
* Show the WinCE Buttons for IE Mobile and Opera Mobile.
51
function initWinCEButtons() {
52
var buttonRowElem = dom.getElementById("buttons-wince");
54
buttonRowElem.style.display = 'block';
59
* Set the label of an input button, and optionally, its accesskey.
61
function setButtonLabel(textID, elemID, accessKeyID) {
62
var textElem = dom.getElementById(textID);
63
var buttonElem = dom.getElementById(elemID);
64
if (textElem == null || buttonElem == null) {
68
var accessKeyElem = null;
69
if (isDefined(typeof accessKeyID)) {
70
accessKeyElem = dom.getElementById(accessKeyID);
73
// This function works for two different kinds of buttons. Simple buttons
74
// based on the <input type="button"> tag, and custom buttons based on a
75
// <button> tag with the css class "custom".
76
// Note that on Windows Mobile 5, the tagName property is not defined.
77
// We can therefore safely assume that the converse is also true:
78
// if tagName is not defined, then the platform must be Windows Mobile 5.
79
if (!isDefined(typeof buttonElem.tagName) ||
80
buttonElem.tagName.toLowerCase() == "input") {
81
buttonElem.value = dom.getTextContent(textElem).trim();
82
if (accessKeyElem != null) {
83
buttonElem.accessKey = dom.getTextContent(accessKeyElem).trim();
85
} else if (buttonElem.tagName.toLowerCase() == "button") {
86
var text = dom.getTextContent(textElem).trim();
88
if (accessKeyElem != null) {
89
// Some browsers use the accessKey attribute of the the anchor tag.
90
var accessKey = dom.getTextContent(accessKeyElem).trim();
91
buttonElem.accessKey = accessKey;
93
// Find the first matching character in the text and mark it.
94
// Note: this form of String.replace() only replaces the first occurence.
95
text = text.replace(accessKey,
96
"<span class='accesskey'>" + accessKey + "</span>");
99
buttonElem.innerHTML = text;
101
throw new Error("Unexpected button tag name: " + buttonElem.tagName);
106
* Allows a dialog to do custom layout when the window changes sizes. The
107
* provided function will be called with the height of the content area when the
108
* dialog should relayout.
110
function initCustomLayout(layoutFunction) {
111
function doLayout() {
112
layoutFunction(getContentHeight());
117
// We do an additional layout in onload because sometimes things aren't
118
// stabilized when the first doLayout() is called above.
119
dom.addEvent(window, "load", doLayout);
120
dom.addEvent(window, "resize", doLayout);
122
// Mozilla doesn't fire continuous events during resize, so if we want to get
123
// somewhat smooth resizing, we need to run our own timer loop. This still
124
// doesn't look perfect, because the timer goes off out of sync with the
125
// browser's internal reflow, but I think it's better than nothing.
126
// TODO(aa): Keep looking for a way to get an event for each reflow, like IE.
127
if (browser.mozilla) {
130
function maybeDoLayout() {
131
var currentHeight = dom.getWindowInnerHeight();
132
if (currentHeight != lastHeight) {
133
lastHeight = currentHeight;
138
window.setInterval(maybeDoLayout, 30);
143
* Get the JSON-formatted arguments that were passed by the caller.
145
function getArguments() {
147
if (browser.ie_mobile) {
148
argsString = window.pie_dialog.GetDialogArguments();
149
} else if (browser.ie) {
150
argsString = window.external.GetDialogArguments();
151
} else if (browser.mozilla) {
152
argsString = getFirefoxArguments(window.arguments[0]);
153
} else if (browser.safari) {
154
argsString = window.gears_dialogArguments;
155
} else if (browser.opera) {
156
argsString = window.dialogArguments;
157
} else if (browser.android) {
158
argsString = "" + window.bridge.getDialogArguments();
159
} else if (browser.chrome) {
160
argsString = chrome.dialogArguments;
163
if (typeof argsString == "string" && argsString.length > 0) {
164
return JSON.parse(argsString);
171
* Helper used by getArguments(). Getting the arguments in the right type is
172
* more involved in Firefox.
174
function getFirefoxArguments(windowArguments) {
175
var Ci = Components.interfaces;
176
windowArguments.QueryInterface(Ci.nsIProperties);
178
windowArguments.get("dialogArguments", Ci.nsISupportsString);
179
return supportsString.data;
183
* Close the dialog, sending the specified result back to the caller.
185
function saveAndClose(resultObject) {
186
var resultString = JSON.stringify(resultObject);
187
if (browser.ie_mobile) {
188
window.pie_dialog.CloseDialog(resultString);
189
} else if (browser.ie) {
190
window.external.CloseDialog(resultString);
191
} else if (browser.mozilla) {
192
saveFirefoxResults(resultString);
194
} else if (browser.safari) {
195
window.gears_dialog.setResults(resultString);
196
} else if (browser.opera) {
197
window.returnValue = resultString;
199
} else if (browser.android) {
200
window.bridge.closeDialog(resultString);
201
} else if (browser.chrome) {
202
saveChromeResults(resultString);
207
function saveChromeResults(resultString) {
208
chrome.send("DialogClose", [resultString]);
212
* Helper used by endDialog() for Firefox.
214
function saveFirefoxResults(resultString) {
215
var Cc = Components.classes;
216
var Ci = Components.interfaces;
218
var props = window.arguments[0].QueryInterface(Ci.nsIProperties);
219
var supportsString = Cc["@mozilla.org/supports-string;1"]
220
.createInstance(Ci.nsISupportsString);
222
supportsString.data = resultString;
223
props.set("dialogResult", supportsString);
227
* Returns the height of the content area of the dialog.
229
function getContentHeight() {
230
var head = dom.getElementById("head");
231
var foot = dom.getElementById("foot");
232
return dom.getWindowInnerHeight() - head.offsetHeight - foot.offsetHeight;
236
* For some reason ESC doesn't work on HTML dialogs in either Firefox or IE. So
237
* we implement it manually.
239
function handleKeyUp(e) {
240
var ESC_KEY_CODE = 27;
242
if (e.keyCode == ESC_KEY_CODE) {
248
* Disables a button in the right way, whether it is normal or custom.
250
function disableButton(buttonElem) {
251
buttonElem.disabled = true;
253
if (browser.ie_mobile) {
254
window.pie_dialog.SetButtonEnabled(false);
257
if (!isDefined(typeof buttonElem.tagName) ||
258
buttonElem.tagName.toLowerCase() == "input") {
259
buttonElem.style.color = "gray";
260
} else if (buttonElem.tagName.toLowerCase() == "button") {
261
dom.addClass(buttonElem, "disabled");
263
throw new Error("Unexpected tag name: " + buttonElem.tagName);
268
* Enables a button in the right way, whether it is normal or custom.
270
function enableButton(buttonElem) {
271
buttonElem.disabled = false;
273
if (browser.ie_mobile) {
274
window.pie_dialog.SetButtonEnabled(true);
277
if (!isDefined(typeof buttonElem.tagName) ||
278
buttonElem.tagName.toLowerCase() == "input") {
279
buttonElem.style.color = "black";
280
} else if (buttonElem.tagName.toLowerCase() == "button") {
281
dom.removeClass(buttonElem, "disabled");
283
throw new Error("Unexpected tag name: " + buttonElem.tagName);
288
* Modifies a string to allow line breaks after each occurence of the specified
289
* string. escapedSearchString is the string escaped for use with regex, if
290
* required. eg '.' -> '\\.'
292
function breakString(str, searchString, escapedSearchString) {
293
if (!escapedSearchString) {
294
escapedSearchString = searchString;
296
// The <wbr> tag is not standard HTML, but is supported by IE, FF and Chrome.
297
// Opera and Safari do not support <wbr>, so we use HTML entity ​. This
298
// is placed after the <wbr> tag using CSS. See
299
// http://www.quirksmode.org/oddsandends/wbr.html for details.
300
var optionalLineBreak = '<wbr>';
301
if (browser.ie_mobile) {
302
// IE Mobile does not support <wbr> or ​, so we use a zero-sized
303
// image. (Note that this does not work on desktop IE).
304
optionalLineBreak = '<img width="0px" height="0px">';
306
var regex = new RegExp(escapedSearchString, 'g');
307
return str.replace(regex, searchString + optionalLineBreak);
311
* Resizes the dialog to fit the content size.
313
function resizeDialogToFitContent(opt_minDialogInnerHeight) {
314
// This does not work on PIE (no height measurement)
315
if (browser.ie_mobile) {
316
window.pie_dialog.ResizeDialog();
319
// window.resize() is not working on Android, we bail out.
320
if (browser.android) {
324
// Resize the window to fit
325
var contentDiv = dom.getElementById("content");
326
var contentHeightProvided = getContentHeight();
327
var contentHeightDesired = contentDiv.offsetHeight;
329
var dialogHeightProvided = dom.getWindowInnerHeight();
330
var dialogHeightDesired =
331
dialogHeightProvided + (contentHeightDesired - contentHeightProvided);
333
var minDialogHeight = opt_minDialogInnerHeight || 0;
334
var maxDialogHeight = 400; // arbitrary max height for a dialog to resize to
336
var targetHeight = Math.max(dialogHeightDesired, minDialogHeight);
337
targetHeight = Math.min(dialogHeightDesired, maxDialogHeight);
339
if (targetHeight != dialogHeightProvided) {
340
var dy = targetHeight - dialogHeightProvided;
341
window.resizeBy(0, dy);
346
* Safari running on OSX 10.4 can't load images dynamically from the network,
347
* this helper function calls gears-specific code to work around this
350
function loadImage(elem, image_url) {
351
if (browser.safari) {
352
var position = dom.getPosition(elem);
353
window.gears_dialog.loadImageIntoElement(image_url, position.top,
358
elem.src = image_url;