~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/ui/common/html_dialog.js

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2007, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without 
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
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.
 
14
//
 
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.
 
25
 
 
26
/**
 
27
 * Initialize the base functionality of the dialog.
 
28
 */
 
29
function initDialog() {
 
30
  if (!browser.ie_mobile) {
 
31
    dom.addEvent(document, "keyup", handleKeyUp);
 
32
  }
 
33
 
 
34
  if (browser.wince && browser.opera) {
 
35
    initWinCEButtons();
 
36
  }
 
37
 
 
38
  if (browser.ie_mobile) {
 
39
    if (!window.pie_dialog.IsSmartPhone()) {
 
40
      initWinCEButtons();
 
41
    }
 
42
 
 
43
    window.pie_dialog.SetScriptContext(window);
 
44
    window.pie_dialog.ResizeDialog();
 
45
  }
 
46
}
 
47
 
 
48
/**
 
49
 * Show the WinCE Buttons for IE Mobile and Opera Mobile.
 
50
 */
 
51
function initWinCEButtons() {
 
52
  var buttonRowElem = dom.getElementById("buttons-wince");
 
53
  if (buttonRowElem) {
 
54
    buttonRowElem.style.display = 'block';
 
55
  }
 
56
}
 
57
 
 
58
/**
 
59
 * Set the label of an input button, and optionally, its accesskey.
 
60
 */
 
61
function setButtonLabel(textID, elemID, accessKeyID) {
 
62
  var textElem = dom.getElementById(textID);
 
63
  var buttonElem = dom.getElementById(elemID);
 
64
  if (textElem == null || buttonElem == null) {
 
65
    return;
 
66
  }
 
67
 
 
68
  var accessKeyElem = null;
 
69
  if (isDefined(typeof accessKeyID)) {
 
70
    accessKeyElem = dom.getElementById(accessKeyID);
 
71
  }
 
72
 
 
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();
 
84
    }
 
85
  } else if (buttonElem.tagName.toLowerCase() == "button") {
 
86
    var text = dom.getTextContent(textElem).trim();
 
87
 
 
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;
 
92
 
 
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>");
 
97
    }
 
98
 
 
99
    buttonElem.innerHTML = text;
 
100
  } else {
 
101
    throw new Error("Unexpected button tag name: " + buttonElem.tagName);
 
102
  }
 
103
}
 
104
 
 
105
/**
 
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.
 
109
 */
 
110
function initCustomLayout(layoutFunction) {
 
111
  function doLayout() {
 
112
    layoutFunction(getContentHeight());
 
113
  }
 
114
 
 
115
  doLayout();
 
116
 
 
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);
 
121
 
 
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) {
 
128
    var lastHeight = -1;
 
129
 
 
130
    function maybeDoLayout() {
 
131
      var currentHeight = dom.getWindowInnerHeight();
 
132
      if (currentHeight != lastHeight) {
 
133
        lastHeight = currentHeight;
 
134
        doLayout();
 
135
      }
 
136
    }
 
137
 
 
138
    window.setInterval(maybeDoLayout, 30);
 
139
  }
 
140
}
 
141
 
 
142
/**
 
143
 * Get the JSON-formatted arguments that were passed by the caller.
 
144
 */
 
145
function getArguments() {
 
146
  var argsString;
 
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;
 
161
  }
 
162
 
 
163
  if (typeof argsString == "string" && argsString.length > 0) {
 
164
    return JSON.parse(argsString);
 
165
  } else {
 
166
    return null;
 
167
  }
 
168
}
 
169
 
 
170
/**
 
171
 * Helper used by getArguments(). Getting the arguments in the right type is
 
172
 * more involved in Firefox.
 
173
 */
 
174
function getFirefoxArguments(windowArguments) {
 
175
  var Ci = Components.interfaces;
 
176
  windowArguments.QueryInterface(Ci.nsIProperties);
 
177
  var supportsString = 
 
178
    windowArguments.get("dialogArguments", Ci.nsISupportsString);
 
179
  return supportsString.data;
 
180
}
 
181
 
 
182
/**
 
183
 * Close the dialog, sending the specified result back to the caller.
 
184
 */
 
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);
 
193
    window.close();
 
194
  } else if (browser.safari) {
 
195
    window.gears_dialog.setResults(resultString);
 
196
  } else if (browser.opera) {
 
197
    window.returnValue = resultString;
 
198
    window.close();
 
199
  } else if (browser.android) {
 
200
    window.bridge.closeDialog(resultString);
 
201
  } else if (browser.chrome) {
 
202
    saveChromeResults(resultString);
 
203
    window.close();
 
204
  }
 
205
}
 
206
 
 
207
function saveChromeResults(resultString) {
 
208
  chrome.send("DialogClose", [resultString]);
 
209
}
 
210
 
 
211
/**
 
212
 * Helper used by endDialog() for Firefox.
 
213
 */
 
214
function saveFirefoxResults(resultString) {
 
215
  var Cc = Components.classes;
 
216
  var Ci = Components.interfaces;
 
217
  
 
218
  var props = window.arguments[0].QueryInterface(Ci.nsIProperties);
 
219
  var supportsString = Cc["@mozilla.org/supports-string;1"]
 
220
      .createInstance(Ci.nsISupportsString);
 
221
      
 
222
  supportsString.data = resultString;
 
223
  props.set("dialogResult", supportsString);
 
224
}
 
225
 
 
226
/**
 
227
 * Returns the height of the content area of the dialog.
 
228
 */
 
229
function getContentHeight() {
 
230
  var head = dom.getElementById("head");
 
231
  var foot = dom.getElementById("foot");
 
232
  return dom.getWindowInnerHeight() - head.offsetHeight - foot.offsetHeight;
 
233
}
 
234
 
 
235
/**
 
236
 * For some reason ESC doesn't work on HTML dialogs in either Firefox or IE. So
 
237
 * we implement it manually.
 
238
 */
 
239
function handleKeyUp(e) {
 
240
  var ESC_KEY_CODE = 27;
 
241
  
 
242
  if (e.keyCode == ESC_KEY_CODE) {
 
243
    saveAndClose(null);
 
244
  }
 
245
}
 
246
 
 
247
/**
 
248
 * Disables a button in the right way, whether it is normal or custom.
 
249
 */
 
250
function disableButton(buttonElem) {
 
251
  buttonElem.disabled = true;
 
252
 
 
253
  if (browser.ie_mobile) {
 
254
    window.pie_dialog.SetButtonEnabled(false);
 
255
  }
 
256
 
 
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");
 
262
  } else {
 
263
    throw new Error("Unexpected tag name: " + buttonElem.tagName);
 
264
  }
 
265
}
 
266
 
 
267
/**
 
268
 * Enables a button in the right way, whether it is normal or custom.
 
269
 */
 
270
function enableButton(buttonElem) {
 
271
  buttonElem.disabled = false;
 
272
 
 
273
  if (browser.ie_mobile) {
 
274
    window.pie_dialog.SetButtonEnabled(true);
 
275
  }
 
276
  
 
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");
 
282
  } else {
 
283
    throw new Error("Unexpected tag name: " + buttonElem.tagName);
 
284
  }
 
285
}
 
286
 
 
287
/**
 
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 '.' -> '\\.'
 
291
 */
 
292
function breakString(str, searchString, escapedSearchString) {
 
293
  if (!escapedSearchString) {
 
294
    escapedSearchString = searchString;
 
295
  }
 
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 &#8203;. 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 &#8203;, so we use a zero-sized
 
303
    // image. (Note that this does not work on desktop IE).
 
304
    optionalLineBreak = '<img width="0px" height="0px">';
 
305
  }
 
306
  var regex = new RegExp(escapedSearchString, 'g');
 
307
  return str.replace(regex, searchString + optionalLineBreak);
 
308
}
 
309
 
 
310
/**
 
311
 * Resizes the dialog to fit the content size.
 
312
 */
 
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();
 
317
    return;
 
318
  }
 
319
  // window.resize() is not working on Android, we bail out.
 
320
  if (browser.android) {
 
321
    return;
 
322
  }
 
323
 
 
324
  // Resize the window to fit
 
325
  var contentDiv = dom.getElementById("content");
 
326
  var contentHeightProvided = getContentHeight();
 
327
  var contentHeightDesired = contentDiv.offsetHeight;
 
328
 
 
329
  var dialogHeightProvided = dom.getWindowInnerHeight();
 
330
  var dialogHeightDesired =
 
331
      dialogHeightProvided + (contentHeightDesired - contentHeightProvided);
 
332
 
 
333
  var minDialogHeight = opt_minDialogInnerHeight || 0;
 
334
  var maxDialogHeight = 400; // arbitrary max height for a dialog to resize to
 
335
  
 
336
  var targetHeight = Math.max(dialogHeightDesired, minDialogHeight);
 
337
  targetHeight = Math.min(dialogHeightDesired, maxDialogHeight);
 
338
 
 
339
  if (targetHeight != dialogHeightProvided) {
 
340
    var dy = targetHeight - dialogHeightProvided;
 
341
    window.resizeBy(0, dy);
 
342
  }
 
343
}
 
344
 
 
345
/**
 
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
 
348
 * limitation.
 
349
 */
 
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,
 
354
                                             position.left,
 
355
                                             position.width,
 
356
                                             position.height);
 
357
  } else {
 
358
    elem.src = image_url;
 
359
  }
 
360
}