popup.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. if (!dojo._hasResource["dijit._base.popup"]) { // _hasResource checks added by
  2. // build. Do not use
  3. // _hasResource directly in your
  4. // code.
  5. dojo._hasResource["dijit._base.popup"] = true;
  6. dojo.provide("dijit._base.popup");
  7. dojo.require("dijit._base.focus");
  8. dojo.require("dijit._base.place");
  9. dojo.require("dijit._base.window");
  10. dijit.popup = new function() {
  11. // summary:
  12. // This class is used to show/hide widgets as popups.
  13. //
  14. var stack = [], beginZIndex = 1000, idGen = 1;
  15. this.open = function(/* Object */args) {
  16. // summary:
  17. // Popup the widget at the specified position
  18. //
  19. // args: Object
  20. // popup: Widget
  21. // widget to display,
  22. // parent: Widget
  23. // the button etc. that is displaying this popup
  24. // around: DomNode
  25. // DOM node (typically a button); place popup relative to this node
  26. // orient: Object
  27. // structure specifying possible positions of popup relative to
  28. // "around" node
  29. // onCancel: Function
  30. // callback when user has canceled the popup by
  31. // 1. hitting ESC or
  32. // 2. by using the popup widget's proprietary cancel mechanism (like
  33. // a cancel button in a dialog);
  34. // ie: whenever popupWidget.onCancel() is called, args.onCancel is
  35. // called
  36. // onClose: Function
  37. // callback whenever this popup is closed
  38. // onExecute: Function
  39. // callback when user "executed" on the popup/sub-popup by selecting
  40. // a menu choice, etc. (top menu only)
  41. //
  42. // examples:
  43. // 1. opening at the mouse position
  44. // dijit.popup.open({popup: menuWidget, x: evt.pageX, y:
  45. // evt.pageY});
  46. // 2. opening the widget as a dropdown
  47. // dijit.popup.open({parent: this, popup: menuWidget, around:
  48. // this.domNode, onClose: function(){...} });
  49. //
  50. // Note that whatever widget called dijit.popup.open() should also
  51. // listen to it's own _onBlur callback
  52. // (fired from _base/focus.js) to know that focus has moved
  53. // somewhere else and thus the popup should be closed.
  54. var widget = args.popup, orient = args.orient || {
  55. 'BL' : 'TL',
  56. 'TL' : 'BL'
  57. }, around = args.around, id = (args.around && args.around.id)
  58. ? (args.around.id + "_dropdown")
  59. : ("popup_" + idGen++);
  60. // make wrapper div to hold widget and possibly hold iframe behind
  61. // it.
  62. // we can't attach the iframe as a child of the widget.domNode
  63. // because
  64. // widget.domNode might be a <table>, <ul>, etc.
  65. var wrapper = dojo.doc.createElement("div");
  66. wrapper.id = id;
  67. wrapper.className = "dijitPopup";
  68. wrapper.style.zIndex = beginZIndex + stack.length;
  69. wrapper.style.visibility = "hidden";
  70. if (args.parent) {
  71. wrapper.dijitPopupParent = args.parent.id;
  72. }
  73. dojo.body().appendChild(wrapper);
  74. widget.domNode.style.display = "";
  75. wrapper.appendChild(widget.domNode);
  76. var iframe = new dijit.BackgroundIframe(wrapper);
  77. // position the wrapper node
  78. var best = around ? dijit.placeOnScreenAroundElement(wrapper,
  79. around, orient, widget.orient ? dojo
  80. .hitch(widget, "orient") : null) : dijit
  81. .placeOnScreen(wrapper, args, orient == 'R' ? ['TR', 'BR',
  82. 'TL', 'BL'] : ['TL', 'BL', 'TR', 'BR']);
  83. wrapper.style.visibility = "visible";
  84. // TODO: use effects to fade in wrapper
  85. var handlers = [];
  86. // Compute the closest ancestor popup that's *not* a child of
  87. // another popup.
  88. // Ex: For a TooltipDialog with a button that spawns a tree of
  89. // menus, find the popup of the button.
  90. function getTopPopup() {
  91. for (var pi = stack.length - 1; pi > 0
  92. && stack[pi].parent === stack[pi - 1].widget; pi--);
  93. return stack[pi];
  94. }
  95. // provide default escape and tab key handling
  96. // (this will work for any widget, not just menu)
  97. handlers.push(dojo.connect(wrapper, "onkeypress", this, function(
  98. evt) {
  99. if (evt.keyCode == dojo.keys.ESCAPE && args.onCancel) {
  100. args.onCancel();
  101. } else if (evt.keyCode == dojo.keys.TAB) {
  102. dojo.stopEvent(evt);
  103. var topPopup = getTopPopup();
  104. if (topPopup && topPopup.onCancel) {
  105. topPopup.onCancel();
  106. }
  107. }
  108. }));
  109. // watch for cancel/execute events on the popup and notify the
  110. // caller
  111. // (for a menu, "execute" means clicking an item)
  112. if (widget.onCancel) {
  113. handlers.push(dojo.connect(widget, "onCancel", null,
  114. args.onCancel));
  115. }
  116. handlers.push(dojo.connect(widget, widget.onExecute
  117. ? "onExecute"
  118. : "onChange", null, function() {
  119. var topPopup = getTopPopup();
  120. if (topPopup && topPopup.onExecute) {
  121. topPopup.onExecute();
  122. }
  123. }));
  124. stack.push({
  125. wrapper : wrapper,
  126. iframe : iframe,
  127. widget : widget,
  128. parent : args.parent,
  129. onExecute : args.onExecute,
  130. onCancel : args.onCancel,
  131. onClose : args.onClose,
  132. handlers : handlers
  133. });
  134. if (widget.onOpen) {
  135. widget.onOpen(best);
  136. }
  137. return best;
  138. };
  139. this.close = function(/* Widget */popup) {
  140. // summary:
  141. // Close specified popup and any popups that it parented
  142. while (dojo.some(stack, function(elem) {
  143. return elem.widget == popup;
  144. })) {
  145. var top = stack.pop(), wrapper = top.wrapper, iframe = top.iframe, widget = top.widget, onClose = top.onClose;
  146. if (widget.onClose) {
  147. widget.onClose();
  148. }
  149. dojo.forEach(top.handlers, dojo.disconnect);
  150. // #2685: check if the widget still has a domNode so ContentPane
  151. // can change its URL without getting an error
  152. if (!widget || !widget.domNode) {
  153. return;
  154. }
  155. dojo.style(widget.domNode, "display", "none");
  156. dojo.body().appendChild(widget.domNode);
  157. iframe.destroy();
  158. dojo._destroyElement(wrapper);
  159. if (onClose) {
  160. onClose();
  161. }
  162. }
  163. };
  164. }();
  165. dijit._frames = new function() {
  166. // summary: cache of iframes
  167. var queue = [];
  168. this.pop = function() {
  169. var iframe;
  170. if (queue.length) {
  171. iframe = queue.pop();
  172. iframe.style.display = "";
  173. } else {
  174. if (dojo.isIE) {
  175. var html = "<iframe src='javascript:\"\"'"
  176. + " style='position: absolute; left: 0px; top: 0px;"
  177. + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
  178. iframe = dojo.doc.createElement(html);
  179. } else {
  180. var iframe = dojo.doc.createElement("iframe");
  181. iframe.src = 'javascript:""';
  182. iframe.className = "dijitBackgroundIframe";
  183. }
  184. iframe.tabIndex = -1; // Magic to prevent iframe from getting
  185. // focus on tab keypress - as style
  186. // didnt work.
  187. dojo.body().appendChild(iframe);
  188. }
  189. return iframe;
  190. };
  191. this.push = function(iframe) {
  192. iframe.style.display = "";
  193. if (dojo.isIE) {
  194. iframe.style.removeExpression("width");
  195. iframe.style.removeExpression("height");
  196. }
  197. queue.push(iframe);
  198. }
  199. }();
  200. // fill the queue
  201. if (dojo.isIE && dojo.isIE < 7) {
  202. dojo.addOnLoad(function() {
  203. var f = dijit._frames;
  204. dojo.forEach([f.pop()], f.push);
  205. });
  206. }
  207. dijit.BackgroundIframe = function(/* DomNode */node) {
  208. // summary:
  209. // For IE z-index schenanigans. id attribute is required.
  210. //
  211. // description:
  212. // new dijit.BackgroundIframe(node)
  213. // Makes a background iframe as a child of node, that fills
  214. // area (and position) of node
  215. if (!node.id) {
  216. throw new Error("no id");
  217. }
  218. if ((dojo.isIE && dojo.isIE < 7)
  219. || (dojo.isFF && dojo.isFF < 3 && dojo.hasClass(dojo.body(),
  220. "dijit_a11y"))) {
  221. var iframe = dijit._frames.pop();
  222. node.appendChild(iframe);
  223. if (dojo.isIE) {
  224. iframe.style.setExpression("width", "document.getElementById('"
  225. + node.id + "').offsetWidth");
  226. iframe.style.setExpression("height",
  227. "document.getElementById('" + node.id
  228. + "').offsetHeight");
  229. }
  230. this.iframe = iframe;
  231. }
  232. };
  233. dojo.extend(dijit.BackgroundIframe, {
  234. destroy : function() {
  235. // summary: destroy the iframe
  236. if (this.iframe) {
  237. dijit._frames.push(this.iframe);
  238. delete this.iframe;
  239. }
  240. }
  241. });
  242. }