FloatingPane.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.layout.FloatingPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.FloatingPane"] = true;
  8. dojo.provide("dojox.layout.FloatingPane");
  9. dojo.experimental("dojox.layout.FloatingPane");
  10. dojo.require("dojo.window");
  11. dojo.require("dijit._Templated");
  12. dojo.require("dijit._Widget");
  13. dojo.require("dojo.dnd.Moveable");
  14. dojo.require("dojox.layout.ContentPane");
  15. dojo.require("dojox.layout.ResizeHandle");
  16. dojo.declare("dojox.layout.FloatingPane",
  17. [ dojox.layout.ContentPane, dijit._Templated ],
  18. {
  19. // summary:
  20. // A non-modal Floating window.
  21. //
  22. // description:
  23. // Makes a `dojox.layout.ContentPane` float and draggable by it's title [similar to TitlePane]
  24. // and over-rides onClick to onDblClick for wipeIn/Out of containerNode
  25. // provides minimize(dock) / show() and hide() methods, and resize [almost]
  26. //
  27. // closable: Boolean
  28. // Allow closure of this Node
  29. closable: true,
  30. // dockable: Boolean
  31. // Allow minimizing of pane if true
  32. dockable: true,
  33. // resizable: Boolean
  34. // Allow resizing of pane true if true
  35. resizable: false,
  36. // maxable: Boolean
  37. // Horrible param name for "Can you maximize this floating pane?"
  38. maxable: false,
  39. // resizeAxis: String
  40. // One of: x | xy | y to limit pane's sizing direction
  41. resizeAxis: "xy",
  42. // title: String
  43. // Title to use in the header
  44. title: "",
  45. // dockTo: DomNode?
  46. // if empty, will create private layout.Dock that scrolls with viewport
  47. // on bottom span of viewport.
  48. dockTo: "",
  49. // duration: Integer
  50. // Time is MS to spend toggling in/out node
  51. duration: 400,
  52. /*=====
  53. // iconSrc: String
  54. // [not implemented yet] will be either icon in titlepane to left
  55. // of Title, and/or icon show when docked in a fisheye-like dock
  56. // or maybe dockIcon would be better?
  57. iconSrc: null,
  58. =====*/
  59. // contentClass: String
  60. // The className to give to the inner node which has the content
  61. contentClass: "dojoxFloatingPaneContent",
  62. // animation holders for toggle
  63. _showAnim: null,
  64. _hideAnim: null,
  65. // node in the dock (if docked)
  66. _dockNode: null,
  67. // privates:
  68. _restoreState: {},
  69. _allFPs: [],
  70. _startZ: 100,
  71. templateString: dojo.cache("dojox.layout", "resources/FloatingPane.html", "<div class=\"dojoxFloatingPane\" id=\"${id}\">\n\t<div tabindex=\"0\" role=\"button\" class=\"dojoxFloatingPaneTitle\" dojoAttachPoint=\"focusNode\">\n\t\t<span dojoAttachPoint=\"closeNode\" dojoAttachEvent=\"onclick: close\" class=\"dojoxFloatingCloseIcon\"></span>\n\t\t<span dojoAttachPoint=\"maxNode\" dojoAttachEvent=\"onclick: maximize\" class=\"dojoxFloatingMaximizeIcon\">&thinsp;</span>\n\t\t<span dojoAttachPoint=\"restoreNode\" dojoAttachEvent=\"onclick: _restore\" class=\"dojoxFloatingRestoreIcon\">&thinsp;</span>\t\n\t\t<span dojoAttachPoint=\"dockNode\" dojoAttachEvent=\"onclick: minimize\" class=\"dojoxFloatingMinimizeIcon\">&thinsp;</span>\n\t\t<span dojoAttachPoint=\"titleNode\" class=\"dijitInline dijitTitleNode\"></span>\n\t</div>\n\t<div dojoAttachPoint=\"canvas\" class=\"dojoxFloatingPaneCanvas\">\n\t\t<div dojoAttachPoint=\"containerNode\" role=\"region\" tabindex=\"-1\" class=\"${contentClass}\">\n\t\t</div>\n\t\t<span dojoAttachPoint=\"resizeHandle\" class=\"dojoxFloatingResizeHandle\"></span>\n\t</div>\n</div>\n"),
  72. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  73. title: { type:"innerHTML", node:"titleNode" }
  74. }),
  75. postCreate: function(){
  76. this.inherited(arguments);
  77. new dojo.dnd.Moveable(this.domNode,{ handle: this.focusNode });
  78. //this._listener = dojo.subscribe("/dnd/move/start",this,"bringToTop");
  79. if(!this.dockable){ this.dockNode.style.display = "none"; }
  80. if(!this.closable){ this.closeNode.style.display = "none"; }
  81. if(!this.maxable){
  82. this.maxNode.style.display = "none";
  83. this.restoreNode.style.display = "none";
  84. }
  85. if(!this.resizable){
  86. this.resizeHandle.style.display = "none";
  87. }else{
  88. this.domNode.style.width = dojo.marginBox(this.domNode).w + "px";
  89. }
  90. this._allFPs.push(this);
  91. this.domNode.style.position = "absolute";
  92. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  93. this._naturalState = dojo.coords(this.domNode);
  94. },
  95. startup: function(){
  96. if(this._started){ return; }
  97. this.inherited(arguments);
  98. if(this.resizable){
  99. if(dojo.isIE){
  100. this.canvas.style.overflow = "auto";
  101. }else{
  102. this.containerNode.style.overflow = "auto";
  103. }
  104. this._resizeHandle = new dojox.layout.ResizeHandle({
  105. targetId: this.id,
  106. resizeAxis: this.resizeAxis
  107. },this.resizeHandle);
  108. }
  109. if(this.dockable){
  110. // FIXME: argh.
  111. var tmpName = this.dockTo;
  112. if(this.dockTo){
  113. this.dockTo = dijit.byId(this.dockTo);
  114. }else{
  115. this.dockTo = dijit.byId('dojoxGlobalFloatingDock');
  116. }
  117. if(!this.dockTo){
  118. var tmpId, tmpNode;
  119. // we need to make our dock node, and position it against
  120. // .dojoxDockDefault .. this is a lot. either dockto="node"
  121. // and fail if node doesn't exist or make the global one
  122. // once, and use it on empty OR invalid dockTo="" node?
  123. if(tmpName){
  124. tmpId = tmpName;
  125. tmpNode = dojo.byId(tmpName);
  126. }else{
  127. tmpNode = dojo.create('div', null, dojo.body());
  128. dojo.addClass(tmpNode,"dojoxFloatingDockDefault");
  129. tmpId = 'dojoxGlobalFloatingDock';
  130. }
  131. this.dockTo = new dojox.layout.Dock({ id: tmpId, autoPosition: "south" }, tmpNode);
  132. this.dockTo.startup();
  133. }
  134. if((this.domNode.style.display == "none")||(this.domNode.style.visibility == "hidden")){
  135. // If the FP is created dockable and non-visible, start up docked.
  136. this.minimize();
  137. }
  138. }
  139. this.connect(this.focusNode,"onmousedown","bringToTop");
  140. this.connect(this.domNode, "onmousedown","bringToTop");
  141. // Initial resize to give child the opportunity to lay itself out
  142. this.resize(dojo.coords(this.domNode));
  143. this._started = true;
  144. },
  145. setTitle: function(/* String */ title){
  146. // summary: Update the Title bar with a new string
  147. dojo.deprecated("pane.setTitle", "Use pane.set('title', someTitle)", "2.0");
  148. this.set("title", title);
  149. // this.setTitle = dojo.hitch(this, "setTitle") ??
  150. },
  151. close: function(){
  152. // summary: Close and destroy this widget
  153. if(!this.closable){ return; }
  154. dojo.unsubscribe(this._listener);
  155. this.hide(dojo.hitch(this,function(){
  156. this.destroyRecursive();
  157. }));
  158. },
  159. hide: function(/* Function? */ callback){
  160. // summary: Close, but do not destroy this FloatingPane
  161. dojo.fadeOut({
  162. node:this.domNode,
  163. duration:this.duration,
  164. onEnd: dojo.hitch(this,function() {
  165. this.domNode.style.display = "none";
  166. this.domNode.style.visibility = "hidden";
  167. if(this.dockTo && this.dockable){
  168. this.dockTo._positionDock(null);
  169. }
  170. if(callback){
  171. callback();
  172. }
  173. })
  174. }).play();
  175. },
  176. show: function(/* Function? */callback){
  177. // summary: Show the FloatingPane
  178. var anim = dojo.fadeIn({node:this.domNode, duration:this.duration,
  179. beforeBegin: dojo.hitch(this,function(){
  180. this.domNode.style.display = "";
  181. this.domNode.style.visibility = "visible";
  182. if (this.dockTo && this.dockable) { this.dockTo._positionDock(null); }
  183. if (typeof callback == "function") { callback(); }
  184. this._isDocked = false;
  185. if (this._dockNode) {
  186. this._dockNode.destroy();
  187. this._dockNode = null;
  188. }
  189. })
  190. }).play();
  191. this.resize(dojo.coords(this.domNode));
  192. this._onShow(); // lazy load trigger
  193. },
  194. minimize: function(){
  195. // summary: Hide and dock the FloatingPane
  196. if(!this._isDocked){ this.hide(dojo.hitch(this,"_dock")); }
  197. },
  198. maximize: function(){
  199. // summary: Make this FloatingPane full-screen (viewport)
  200. if(this._maximized){ return; }
  201. this._naturalState = dojo.position(this.domNode);
  202. if(this._isDocked){
  203. this.show();
  204. setTimeout(dojo.hitch(this,"maximize"),this.duration);
  205. }
  206. dojo.addClass(this.focusNode,"floatingPaneMaximized");
  207. this.resize(dojo.window.getBox());
  208. this._maximized = true;
  209. },
  210. _restore: function(){
  211. if(this._maximized){
  212. this.resize(this._naturalState);
  213. dojo.removeClass(this.focusNode,"floatingPaneMaximized");
  214. this._maximized = false;
  215. }
  216. },
  217. _dock: function(){
  218. if(!this._isDocked && this.dockable){
  219. this._dockNode = this.dockTo.addNode(this);
  220. this._isDocked = true;
  221. }
  222. },
  223. resize: function(/* Object */dim){
  224. // summary: Size the FloatingPane and place accordingly
  225. dim = dim || this._naturalState;
  226. this._naturalState = dim;
  227. // From the ResizeHandle we only get width and height information
  228. var dns = this.domNode.style;
  229. if("t" in dim){ dns.top = dim.t + "px"; }
  230. if("l" in dim){ dns.left = dim.l + "px"; }
  231. dns.width = dim.w + "px";
  232. dns.height = dim.h + "px";
  233. // Now resize canvas
  234. var mbCanvas = { l: 0, t: 0, w: dim.w, h: (dim.h - this.focusNode.offsetHeight) };
  235. dojo.marginBox(this.canvas, mbCanvas);
  236. // If the single child can resize, forward resize event to it so it can
  237. // fit itself properly into the content area
  238. this._checkIfSingleChild();
  239. if(this._singleChild && this._singleChild.resize){
  240. this._singleChild.resize(mbCanvas);
  241. }
  242. },
  243. bringToTop: function(){
  244. // summary: bring this FloatingPane above all other panes
  245. var windows = dojo.filter(
  246. this._allFPs,
  247. function(i){
  248. return i !== this;
  249. },
  250. this);
  251. windows.sort(function(a, b){
  252. return a.domNode.style.zIndex - b.domNode.style.zIndex;
  253. });
  254. windows.push(this);
  255. dojo.forEach(windows, function(w, x){
  256. w.domNode.style.zIndex = this._startZ + (x * 2);
  257. dojo.removeClass(w.domNode, "dojoxFloatingPaneFg");
  258. }, this);
  259. dojo.addClass(this.domNode, "dojoxFloatingPaneFg");
  260. },
  261. destroy: function(){
  262. // summary: Destroy this FloatingPane completely
  263. this._allFPs.splice(dojo.indexOf(this._allFPs, this), 1);
  264. if(this._resizeHandle){
  265. this._resizeHandle.destroy();
  266. }
  267. this.inherited(arguments);
  268. }
  269. });
  270. dojo.declare("dojox.layout.Dock",
  271. [dijit._Widget,dijit._Templated],
  272. {
  273. // summary:
  274. // A widget that attaches to a node and keeps track of incoming / outgoing FloatingPanes
  275. // and handles layout
  276. templateString: '<div class="dojoxDock"><ul dojoAttachPoint="containerNode" class="dojoxDockList"></ul></div>',
  277. // private _docked: array of panes currently in our dock
  278. _docked: [],
  279. _inPositioning: false,
  280. autoPosition: false,
  281. addNode: function(refNode){
  282. // summary: Instert a dockNode refernce into the dock
  283. var div = dojo.create('li', null, this.containerNode),
  284. node = new dojox.layout._DockNode({
  285. title: refNode.title,
  286. paneRef: refNode
  287. }, div)
  288. ;
  289. node.startup();
  290. return node;
  291. },
  292. startup: function(){
  293. if (this.id == "dojoxGlobalFloatingDock" || this.isFixedDock) {
  294. // attach window.onScroll, and a position like in presentation/dialog
  295. this.connect(window, 'onresize', "_positionDock");
  296. this.connect(window, 'onscroll', "_positionDock");
  297. if(dojo.isIE){
  298. this.connect(this.domNode, "onresize", "_positionDock");
  299. }
  300. }
  301. this._positionDock(null);
  302. this.inherited(arguments);
  303. },
  304. _positionDock: function(/* Event? */e){
  305. if(!this._inPositioning){
  306. if(this.autoPosition == "south"){
  307. // Give some time for scrollbars to appear/disappear
  308. setTimeout(dojo.hitch(this, function() {
  309. this._inPositiononing = true;
  310. var viewport = dojo.window.getBox();
  311. var s = this.domNode.style;
  312. s.left = viewport.l + "px";
  313. s.width = (viewport.w-2) + "px";
  314. s.top = (viewport.h + viewport.t) - this.domNode.offsetHeight + "px";
  315. this._inPositioning = false;
  316. }), 125);
  317. }
  318. }
  319. }
  320. });
  321. dojo.declare("dojox.layout._DockNode",
  322. [dijit._Widget,dijit._Templated],
  323. {
  324. // summary:
  325. // dojox.layout._DockNode is a private widget used to keep track of
  326. // which pane is docked.
  327. //
  328. // title: String
  329. // Shown in dock icon. should read parent iconSrc?
  330. title: "",
  331. // paneRef: Widget
  332. // reference to the FloatingPane we reprasent in any given dock
  333. paneRef: null,
  334. templateString:
  335. '<li dojoAttachEvent="onclick: restore" class="dojoxDockNode">'+
  336. '<span dojoAttachPoint="restoreNode" class="dojoxDockRestoreButton" dojoAttachEvent="onclick: restore"></span>'+
  337. '<span class="dojoxDockTitleNode" dojoAttachPoint="titleNode">${title}</span>'+
  338. '</li>',
  339. restore: function(){
  340. // summary: remove this dock item from parent dock, and call show() on reffed floatingpane
  341. this.paneRef.show();
  342. this.paneRef.bringToTop();
  343. this.destroy();
  344. }
  345. });
  346. }