Portlet.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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.widget.Portlet"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.widget.Portlet"] = true;
  8. dojo.experimental("dojox.widget.Portlet");
  9. dojo.provide("dojox.widget.Portlet");
  10. dojo.require("dijit.TitlePane");
  11. dojo.require("dojo.fx");
  12. dojo.declare("dojox.widget.Portlet", [dijit.TitlePane, dijit._Container],{
  13. // summary: A container widget that is designed to be contained
  14. // in a dojox.layout.GridContainer. Child widgets can insert
  15. // an icon into the title bar of the Portlet, which when
  16. // clicked, executes the "toggle" method of the child widget.
  17. // A child widget must specify the attribute
  18. // "portletIconClass", and the optional class
  19. // "portletIconHoverClass", as well as the
  20. // "toggle" function.
  21. // resizeChildren: Boolean
  22. // If true, when the Portlet is resized, any child widgets
  23. // with a 'resize' method have that method called.
  24. resizeChildren: true,
  25. // closable: Boolean
  26. // If true, a close button is placed in the title bar,
  27. // and the Portlet can be hidden. If false, the Portlet
  28. // cannot be closed.
  29. closable: true,
  30. // _parents: Array
  31. // An array of all the StackContainer widgets that this Portlet
  32. // is contained in. These are used to determine if the portlet
  33. // is visible or not.
  34. _parents: null,
  35. // _size: Object
  36. // Cache of the previous size of the portlet, used to determine
  37. // if the size has changed and if the child widgets should be
  38. // resized.
  39. _size: null,
  40. // dragRestriction: Boolean
  41. // To remove the drag capability.
  42. dragRestriction : false,
  43. buildRendering: function(){
  44. this.inherited(arguments);
  45. // Hide the portlet until it is fully constructed.
  46. dojo.style(this.domNode, "visibility", "hidden");
  47. },
  48. postCreate: function(){
  49. this.inherited(arguments);
  50. // Add the portlet classes
  51. dojo.addClass(this.domNode, "dojoxPortlet");
  52. dojo.removeClass(this.arrowNode, "dijitArrowNode");
  53. dojo.addClass(this.arrowNode, "dojoxPortletIcon dojoxArrowDown");
  54. dojo.addClass(this.titleBarNode, "dojoxPortletTitle");
  55. dojo.addClass(this.hideNode, "dojoxPortletContentOuter");
  56. // Choose the class to add depending on if the portlet is draggable or not.
  57. dojo.addClass(this.domNode, "dojoxPortlet-" + (!this.dragRestriction ? "movable" : "nonmovable"));
  58. var _this = this;
  59. if(this.resizeChildren){
  60. // If children should be resized when the portlet size changes,
  61. // listen for items being dropped, when the window is resized,
  62. // or when another portlet's size changes.
  63. this.subscribe("/dnd/drop", function(){_this._updateSize();});
  64. this.subscribe("/Portlet/sizechange", function(widget){_this.onSizeChange(widget);});
  65. this.connect(window, "onresize", function(){_this._updateSize();});
  66. // Subscribe to all possible child-selection events that could affect this
  67. // portlet
  68. var doSelectSubscribe = dojo.hitch(this, function(id, lastId){
  69. var widget = dijit.byId(id);
  70. if(widget.selectChild){
  71. var s = this.subscribe(id + "-selectChild", function(child){
  72. var n = _this.domNode.parentNode;
  73. while(n){
  74. if(n == child.domNode){
  75. // Only fire this once, as the widget is now visible
  76. // at least once, so child measurements should be accurate.
  77. _this.unsubscribe(s);
  78. _this._updateSize();
  79. break;
  80. }
  81. n = n.parentNode;
  82. }
  83. });
  84. // Record the StackContainer and child widget that this portlet
  85. // is in, so it can figure out whether or not it is visible.
  86. // If it is not visible, it will not update it's size dynamically.
  87. var child = dijit.byId(lastId);
  88. if(widget && child){
  89. _this._parents.push({parent: widget, child: child});
  90. }
  91. }
  92. });
  93. var lastId;
  94. this._parents = [];
  95. // Find all parent widgets, and if they are StackContainers,
  96. // subscribe to their selectChild method calls.
  97. for(var p = this.domNode.parentNode; p != null; p = p.parentNode){
  98. var id = p.getAttribute ? p.getAttribute("widgetId") : null;
  99. if(id){
  100. doSelectSubscribe(id, lastId);
  101. lastId = id;
  102. }
  103. }
  104. }
  105. // Prevent clicks on icons from causing a drag to start.
  106. this.connect(this.titleBarNode, "onmousedown", function(evt){
  107. if (dojo.hasClass(evt.target, "dojoxPortletIcon")) {
  108. dojo.stopEvent(evt);
  109. return false;
  110. }
  111. return true;
  112. });
  113. // Inform all portlets that the size of this one has changed,
  114. // and therefore perhaps they have too
  115. this.connect(this._wipeOut, "onEnd", function(){_this._publish();});
  116. this.connect(this._wipeIn, "onEnd", function(){_this._publish();});
  117. if(this.closable){
  118. this.closeIcon = this._createIcon("dojoxCloseNode", "dojoxCloseNodeHover", dojo.hitch(this, "onClose"));
  119. dojo.style(this.closeIcon, "display", "");
  120. }
  121. },
  122. startup: function(){
  123. if(this._started){return;}
  124. var children = this.getChildren();
  125. this._placeSettingsWidgets();
  126. // Start up the children
  127. dojo.forEach(children, function(child){
  128. try{
  129. if(!child.started && !child._started){
  130. child.startup()
  131. }
  132. }
  133. catch(e){
  134. console.log(this.id + ":" + this.declaredClass, e);
  135. }
  136. });
  137. this.inherited(arguments);
  138. //this._updateSize();
  139. dojo.style(this.domNode, "visibility", "visible");
  140. },
  141. _placeSettingsWidgets: function(){
  142. // summary: Checks all the children to see if they are instances
  143. // of dojox.widget.PortletSettings. If they are,
  144. // create an icon for them in the title bar which when clicked,
  145. // calls their toggle() method.
  146. dojo.forEach(this.getChildren(), dojo.hitch(this, function(child){
  147. if(child.portletIconClass && child.toggle && !child.attr("portlet")){
  148. this._createIcon(child.portletIconClass, child.portletIconHoverClass, dojo.hitch(child, "toggle"));
  149. dojo.place(child.domNode, this.containerNode, "before");
  150. child.attr("portlet", this);
  151. this._settingsWidget = child;
  152. }
  153. }));
  154. },
  155. _createIcon: function(clazz, hoverClazz, fn){
  156. // summary:
  157. // creates an icon in the title bar.
  158. var icon = dojo.create("div",{
  159. "class": "dojoxPortletIcon " + clazz,
  160. "waiRole": "presentation"
  161. });
  162. dojo.place(icon, this.arrowNode, "before");
  163. this.connect(icon, "onclick", fn);
  164. if(hoverClazz){
  165. this.connect(icon, "onmouseover", function(){
  166. dojo.addClass(icon, hoverClazz);
  167. });
  168. this.connect(icon, "onmouseout", function(){
  169. dojo.removeClass(icon, hoverClazz);
  170. });
  171. }
  172. return icon;
  173. },
  174. onClose: function(evt){
  175. // summary:
  176. // Hides the portlet. Note that it does not
  177. // persist this, so it is up to the client to
  178. // listen to this method and persist the closed state
  179. // in their own way.
  180. dojo.style(this.domNode, "display", "none");
  181. },
  182. onSizeChange: function(widget){
  183. // summary:
  184. // Updates the Portlet size if any other Portlet
  185. // changes its size.
  186. if(widget == this){
  187. return;
  188. }
  189. this._updateSize();
  190. },
  191. _updateSize: function(){
  192. // summary:
  193. // Updates the size of all child widgets.
  194. if(!this.open || !this._started || !this.resizeChildren){
  195. return;
  196. }
  197. if(this._timer){
  198. clearTimeout(this._timer);
  199. }
  200. // Delay applying the size change in case the size
  201. // changes very frequently, for performance reasons.
  202. this._timer = setTimeout(dojo.hitch(this, function(){
  203. var size ={
  204. w: dojo.style(this.domNode, "width"),
  205. h: dojo.style(this.domNode, "height")
  206. };
  207. // If the Portlet is in a StackWidget, and it is not
  208. // visible, do not update the size, as it could
  209. // make child widgets miscalculate.
  210. for(var i = 0; i < this._parents.length; i++){
  211. var p = this._parents[i];
  212. var sel = p.parent.selectedChildWidget
  213. if(sel && sel != p.child){
  214. return;
  215. }
  216. }
  217. if(this._size){
  218. // If the size of the portlet hasn't changed, don't
  219. // resize the children, as this can be expensive
  220. if(this._size.w == size.w && this._size.h == size.h){
  221. return;
  222. }
  223. }
  224. this._size = size;
  225. var fns = ["resize", "layout"];
  226. this._timer = null;
  227. var kids = this.getChildren();
  228. dojo.forEach(kids, function(child){
  229. for(var i = 0; i < fns.length; i++){
  230. if(dojo.isFunction(child[fns[i]])){
  231. try{
  232. child[fns[i]]();
  233. } catch(e){
  234. console.log(e);
  235. }
  236. break;
  237. }
  238. }
  239. });
  240. this.onUpdateSize();
  241. }), 100);
  242. },
  243. onUpdateSize: function(){
  244. // summary:
  245. // Stub function called when the size is changed.
  246. },
  247. _publish: function(){
  248. // summary: Publishes an event that all other portlets listen to.
  249. // This causes them to update their child widgets if their
  250. // size has changed.
  251. dojo.publish("/Portlet/sizechange",[this]);
  252. },
  253. _onTitleClick: function(evt){
  254. if(evt.target == this.arrowNode){
  255. this.inherited(arguments);
  256. }
  257. },
  258. addChild: function(child){
  259. // summary:
  260. // Adds a child widget to the portlet.
  261. this._size = null;
  262. this.inherited(arguments);
  263. if(this._started){
  264. this._placeSettingsWidgets();
  265. this._updateSize();
  266. }
  267. if(this._started && !child.started && !child._started){
  268. child.startup();
  269. }
  270. },
  271. destroyDescendants: function(/*Boolean*/ preserveDom){
  272. this.inherited(arguments);
  273. if(this._settingsWidget){
  274. this._settingsWidget.destroyRecursive(preserveDom);
  275. }
  276. },
  277. destroy: function(){
  278. if(this._timer){
  279. clearTimeout(this._timer);
  280. }
  281. this.inherited(arguments);
  282. },
  283. _setCss: function(){
  284. this.inherited(arguments);
  285. dojo.style(this.arrowNode, "display", this.toggleable ? "":"none");
  286. }
  287. });
  288. dojo.declare("dojox.widget.PortletSettings", [dijit._Container, dijit.layout.ContentPane],{
  289. // summary:
  290. // A settings widget to be used with a dojox.widget.Portlet.
  291. // description:
  292. // This widget should be placed inside a dojox.widget.Portlet widget.
  293. // It is used to set some preferences for that Portlet. It is essentially
  294. // a ContentPane, and should contain other widgets and DOM nodes that
  295. // do the real work of setting preferences for the portlet.
  296. // portletIconClass: String
  297. // The CSS class to apply to the icon in the Portlet title bar that is used
  298. // to toggle the visibility of this widget.
  299. portletIconClass: "dojoxPortletSettingsIcon",
  300. // portletIconHoverClass: String
  301. // The CSS class to apply to the icon in the Portlet title bar that is used
  302. // to toggle the visibility of this widget when the mouse hovers over it.
  303. portletIconHoverClass: "dojoxPortletSettingsIconHover",
  304. postCreate: function(){
  305. // summary:
  306. // Sets the require CSS classes on the widget.
  307. // Start the PortletSettings widget hidden, always.
  308. dojo.style(this.domNode, "display", "none");
  309. dojo.addClass(this.domNode, "dojoxPortletSettingsContainer");
  310. // Remove the unwanted content pane class.
  311. dojo.removeClass(this.domNode, "dijitContentPane");
  312. },
  313. _setPortletAttr: function(portlet){
  314. // summary:
  315. // Sets the portlet that encloses this widget.
  316. this.portlet = portlet;
  317. },
  318. toggle: function(){
  319. // summary:
  320. // Toggles the visibility of this widget.
  321. var n = this.domNode;
  322. if(dojo.style(n, "display") == "none"){
  323. dojo.style(n,{
  324. "display": "block",
  325. "height": "1px",
  326. "width": "auto"
  327. });
  328. dojo.fx.wipeIn({
  329. node: n
  330. }).play();
  331. }else{
  332. dojo.fx.wipeOut({
  333. node: n,
  334. onEnd: dojo.hitch(this, function(){
  335. dojo.style(n,{"display": "none", "height": "", "width":""});
  336. }
  337. )}).play();
  338. }
  339. }
  340. });
  341. dojo.declare("dojox.widget.PortletDialogSettings",
  342. dojox.widget.PortletSettings,{
  343. // summary:
  344. // A settings widget to be used with a dojox.widget.Portlet, which displays
  345. // the contents of this widget in a dijit.Dialog box.
  346. // dimensions: Array
  347. // The size of the dialog to display. This defaults to [300, 300]
  348. dimensions: null,
  349. constructor: function(props, node){
  350. this.dimensions = props.dimensions || [300, 100];
  351. },
  352. toggle: function(){
  353. // summary:
  354. // Shows and hides the Dialog box.
  355. if(!this.dialog){
  356. dojo["require"]("dijit.Dialog");
  357. this.dialog = new dijit.Dialog({title: this.title});
  358. dojo.body().appendChild(this.dialog.domNode);
  359. // Move this widget inside the dialog
  360. this.dialog.containerNode.appendChild(this.domNode);
  361. dojo.style(this.dialog.domNode,{
  362. "width" : this.dimensions[0] + "px",
  363. "height" : this.dimensions[1] + "px"
  364. });
  365. dojo.style(this.domNode, "display", "");
  366. }
  367. if(this.dialog.open){
  368. this.dialog.hide();
  369. }else{
  370. this.dialog.show(this.domNode);
  371. }
  372. }
  373. });
  374. }