dojox
Version:
Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.
349 lines (299 loc) • 10 kB
JavaScript
define([
"dojo/_base/declare",
"dojo/_base/kernel",
"dojo/_base/lang",
"dojo/_base/array",
"dojo/_base/event",
"dojo/_base/connect",
"dojo/dom-style",
"dojo/dom-class",
"dojo/dom-construct",
"dojo/fx",
"dijit/registry",
"dijit/TitlePane",
"dijit/_Container",
"./PortletSettings",
"./PortletDialogSettings"
], function(declare, kernel, lang, array, event, connect, domStyle, domClass, domConstruct, fx, registry,
TitlePane, _Container, PortletSettings, PortletDialogSettings){
kernel.experimental("dojox.widget.Portlet");
return declare("dojox.widget.Portlet", [TitlePane, _Container],{
// summary:
// A container widget that is designed to be contained
// in a dojox.layout.GridContainer. Child widgets can insert
// an icon into the title bar of the Portlet, which when
// clicked, executes the "toggle" method of the child widget.
// A child widget must specify the attribute
// "portletIconClass", and the optional class
// "portletIconHoverClass", as well as the
// "toggle" function.
// resizeChildren: Boolean
// If true, when the Portlet is resized, any child widgets
// with a 'resize' method have that method called.
resizeChildren: true,
// closable: Boolean
// If true, a close button is placed in the title bar,
// and the Portlet can be hidden. If false, the Portlet
// cannot be closed.
closable: true,
// _parents: Array
// An array of all the StackContainer widgets that this Portlet
// is contained in. These are used to determine if the portlet
// is visible or not.
_parents: null,
// _size: Object
// Cache of the previous size of the portlet, used to determine
// if the size has changed and if the child widgets should be
// resized.
_size: null,
// dragRestriction: Boolean
// To remove the drag capability.
dragRestriction : false,
buildRendering: function(){
this.inherited(arguments);
// Hide the portlet until it is fully constructed.
domStyle.set(this.domNode, "visibility", "hidden");
},
postCreate: function(){
this.inherited(arguments);
// Add the portlet classes
domClass.add(this.domNode, "dojoxPortlet");
domClass.remove(this.arrowNode, "dijitArrowNode");
domClass.add(this.arrowNode, "dojoxPortletIcon dojoxArrowDown");
domClass.add(this.titleBarNode, "dojoxPortletTitle");
domClass.add(this.hideNode, "dojoxPortletContentOuter");
// Choose the class to add depending on if the portlet is draggable or not.
domClass.add(this.domNode, "dojoxPortlet-" + (!this.dragRestriction ? "movable" : "nonmovable"));
var _this = this;
if(this.resizeChildren){
// If children should be resized when the portlet size changes,
// listen for items being dropped, when the window is resized,
// or when another portlet's size changes.
this.subscribe("/dnd/drop", function(){_this._updateSize();});
this.subscribe("/Portlet/sizechange", function(widget){_this.onSizeChange(widget);});
this.connect(window, "onresize", function(){_this._updateSize();});
// Subscribe to all possible child-selection events that could affect this
// portlet
var doSelectSubscribe = lang.hitch(this, function(id, lastId){
var widget = registry.byId(id);
if(widget.selectChild){
var s = this.subscribe(id + "-selectChild", function(child){
var n = _this.domNode.parentNode;
while(n){
if(n == child.domNode){
// Only fire this once, as the widget is now visible
// at least once, so child measurements should be accurate.
_this.unsubscribe(s);
_this._updateSize();
break;
}
n = n.parentNode;
}
});
// Record the StackContainer and child widget that this portlet
// is in, so it can figure out whether or not it is visible.
// If it is not visible, it will not update it's size dynamically.
var child = registry.byId(lastId);
if(widget && child){
_this._parents.push({parent: widget, child: child});
}
}
});
var lastId;
this._parents = [];
// Find all parent widgets, and if they are StackContainers,
// subscribe to their selectChild method calls.
for(var p = this.domNode.parentNode; p != null; p = p.parentNode){
var id = p.getAttribute ? p.getAttribute("widgetId") : null;
if(id){
doSelectSubscribe(id, lastId);
lastId = id;
}
}
}
// Prevent clicks on icons from causing a drag to start.
this.connect(this.titleBarNode, "onmousedown", function(evt){
if (domClass.contains(evt.target, "dojoxPortletIcon")) {
event.stop(evt);
return false;
}
return true;
});
// Inform all portlets that the size of this one has changed,
// and therefore perhaps they have too
this.connect(this._wipeOut, "onEnd", function(){_this._publish();});
this.connect(this._wipeIn, "onEnd", function(){_this._publish();});
if(this.closable){
this.closeIcon = this._createIcon("dojoxCloseNode", "dojoxCloseNodeHover", lang.hitch(this, "onClose"));
domStyle.set(this.closeIcon, "display", "");
}
},
startup: function(){
if(this._started){return;}
var children = this.getChildren();
this._placeSettingsWidgets();
// Start up the children
array.forEach(children, function(child){
try{
if(!child.started && !child._started){
child.startup()
}
}
catch(e){
console.log(this.id + ":" + this.declaredClass, e);
}
});
this.inherited(arguments);
//this._updateSize();
domStyle.set(this.domNode, "visibility", "visible");
},
_placeSettingsWidgets: function(){
// summary:
// Checks all the children to see if they are instances
// of dojox.widget.PortletSettings. If they are,
// create an icon for them in the title bar which when clicked,
// calls their toggle() method.
array.forEach(this.getChildren(), lang.hitch(this, function(child){
if(child.portletIconClass && child.toggle && !child.get("portlet")){
this._createIcon(child.portletIconClass, child.portletIconHoverClass, lang.hitch(child, "toggle"));
domConstruct.place(child.domNode, this.containerNode, "before");
child.set("portlet", this);
this._settingsWidget = child;
}
}));
},
_createIcon: function(clazz, hoverClazz, fn){
// summary:
// creates an icon in the title bar.
var icon = domConstruct.create("div",{
"class": "dojoxPortletIcon " + clazz,
"waiRole": "presentation"
});
domConstruct.place(icon, this.arrowNode, "before");
this.connect(icon, "onclick", fn);
if(hoverClazz){
this.connect(icon, "onmouseover", function(){
domClass.add(icon, hoverClazz);
});
this.connect(icon, "onmouseout", function(){
domClass.remove(icon, hoverClazz);
});
}
return icon;
},
onClose: function(evt){
// summary:
// Hides the portlet. Note that it does not
// persist this, so it is up to the client to
// listen to this method and persist the closed state
// in their own way.
domStyle.set(this.domNode, "display", "none");
},
onSizeChange: function(widget){
// summary:
// Updates the Portlet size if any other Portlet
// changes its size.
if(widget == this){
return;
}
this._updateSize();
},
_updateSize: function(){
// summary:
// Updates the size of all child widgets.
if(!this.open || !this._started || !this.resizeChildren){
return;
}
if(this._timer){
clearTimeout(this._timer);
}
// Delay applying the size change in case the size
// changes very frequently, for performance reasons.
this._timer = setTimeout(lang.hitch(this, function(){
var size ={
w: domStyle.get(this.domNode, "width"),
h: domStyle.get(this.domNode, "height")
};
// If the Portlet is in a StackWidget, and it is not
// visible, do not update the size, as it could
// make child widgets miscalculate.
for(var i = 0; i < this._parents.length; i++){
var p = this._parents[i];
var sel = p.parent.selectedChildWidget
if(sel && sel != p.child){
return;
}
}
if(this._size){
// If the size of the portlet hasn't changed, don't
// resize the children, as this can be expensive
if(this._size.w == size.w && this._size.h == size.h){
return;
}
}
this._size = size;
var fns = ["resize", "layout"];
this._timer = null;
var kids = this.getChildren();
array.forEach(kids, function(child){
for(var i = 0; i < fns.length; i++){
if(lang.isFunction(child[fns[i]])){
try{
child[fns[i]]();
} catch(e){
console.log(e);
}
break;
}
}
});
this.onUpdateSize();
}), 100);
},
onUpdateSize: function(){
// summary:
// Stub function called when the size is changed.
},
_publish: function(){
// summary:
// Publishes an event that all other portlets listen to.
// This causes them to update their child widgets if their
// size has changed.
connect.publish("/Portlet/sizechange",[this]);
},
_onTitleClick: function(evt){
if(evt.target == this.arrowNode){
this.inherited(arguments);
}
},
addChild: function(child){
// summary:
// Adds a child widget to the portlet.
this._size = null;
this.inherited(arguments);
if(this._started){
this._placeSettingsWidgets();
this._updateSize();
}
if(this._started && !child.started && !child._started){
child.startup();
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
this.inherited(arguments);
if(this._settingsWidget){
this._settingsWidget.destroyRecursive(preserveDom);
}
},
destroy: function(){
if(this._timer){
clearTimeout(this._timer);
}
this.inherited(arguments);
},
_setCss: function(){
this.inherited(arguments);
domStyle.set(this.arrowNode, "display", this.toggleable ? "":"none");
}
});
});