796 lines
23 KiB
JavaScript
796 lines
23 KiB
JavaScript
define("dojox/treemap/TreeMap", ["dojo/_base/array", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/event", "dojo/_base/Color", "dojo/touch",
|
|
"dojo/when", "dojo/on", "dojo/query", "dojo/dom-construct", "dojo/dom-geometry", "dojo/dom-class", "dojo/dom-style",
|
|
"./_utils", "dijit/_WidgetBase", "dojox/widget/_Invalidating", "dojox/widget/Selection",
|
|
"dojo/_base/sniff", "dojo/uacss"],
|
|
function(arr, lang, declare, event, Color, touch, when, on, query, domConstruct, domGeom, domClass, domStyle,
|
|
utils, _WidgetBase, _Invalidating, Selection, has){
|
|
|
|
return declare("dojox.treemap.TreeMap", [_WidgetBase, _Invalidating, Selection], {
|
|
// summary:
|
|
// A treemap widget.
|
|
|
|
baseClass: "dojoxTreeMap",
|
|
|
|
// store: dojo/store/api/Store
|
|
// The store that contains the items to display.
|
|
store: null,
|
|
|
|
// query: Object
|
|
// A query that can be passed to when querying the store.
|
|
query: {},
|
|
|
|
// itemToRenderer: [protected] Object
|
|
// The associated array item to renderer list.
|
|
itemToRenderer: null,
|
|
|
|
// Data
|
|
_dataChanged: false,
|
|
|
|
// rootItem: Object
|
|
// The root item of the treemap, that is the first visible item.
|
|
// If null the entire treemap hierarchy is shown.
|
|
// Default is null.
|
|
rootItem: null,
|
|
_rootItemChanged: false,
|
|
|
|
// tooltipAttr: String
|
|
// The attribute of the store item that contains the tooltip text of a treemap cell.
|
|
// Default is "".
|
|
tooltipAttr: "",
|
|
|
|
// areaAttr: String
|
|
// The attribute of the store item that contains the data used to compute the area of a treemap cell.
|
|
// Default is "".
|
|
areaAttr: "",
|
|
_areaChanged: false,
|
|
|
|
// labelAttr: String
|
|
// The attribute of the store item that contains the label of a treemap cell.
|
|
// Default is "label".
|
|
labelAttr: "label",
|
|
|
|
// labelThreshold: Number
|
|
// The starting depth level at which the labels are not displayed anymore on cells.
|
|
// If NaN no threshold is applied. The depth is the visual depth of the items on the screen not
|
|
// in the data (i.e. after drill down the depth of an item might change).
|
|
// Default is NaN.
|
|
labelThreshold: NaN,
|
|
|
|
// colorAttr: String
|
|
// The attribute of the store item that contains the data used to compute the color of a treemap cell.
|
|
// Default is "".
|
|
colorAttr: "",
|
|
// colorModel: dojox/color/api/ColorModel
|
|
// The optional color model that converts data to color.
|
|
// Default is null.
|
|
colorModel: null,
|
|
_coloringChanged: false,
|
|
|
|
// groupAttrs: Array
|
|
// An array of data attributes used to group data in the treemap.
|
|
// Default is [].
|
|
groupAttrs: [],
|
|
|
|
// groupFuncs: Array
|
|
// An array of grouping functions used to group data in the treemap.
|
|
// When null, groupAttrs is to compute grouping functions.
|
|
// Default is null.
|
|
groupFuncs: null,
|
|
|
|
_groupFuncs: null,
|
|
_groupingChanged: false,
|
|
|
|
constructor: function(){
|
|
this.itemToRenderer = {};
|
|
this.invalidatingProperties = [ "colorModel", "groupAttrs", "groupFuncs", "areaAttr", "areaFunc",
|
|
"labelAttr", "labelFunc", "labelThreshold", "tooltipAttr", "tooltipFunc",
|
|
"colorAttr", "colorFunc", "rootItem" ];
|
|
},
|
|
|
|
getIdentity: function(item){
|
|
return item.__treeID?item.__treeID:this.store.getIdentity(item);
|
|
},
|
|
|
|
resize: function(box){
|
|
if(box){
|
|
domGeom.setMarginBox(this.domNode, box);
|
|
this.invalidateRendering();
|
|
}
|
|
},
|
|
|
|
postCreate: function(){
|
|
this.inherited(arguments);
|
|
this.connect(this.domNode, "mouseover", this._onMouseOver);
|
|
this.connect(this.domNode, "mouseout", this._onMouseOut);
|
|
this.connect(this.domNode, touch.release, this._onMouseUp);
|
|
this.domNode.setAttribute("role", "presentation");
|
|
this.domNode.setAttribute("aria-label", "treemap");
|
|
},
|
|
|
|
buildRendering: function(){
|
|
this.inherited(arguments);
|
|
this.refreshRendering();
|
|
},
|
|
|
|
refreshRendering: function(){
|
|
var forceCreate = false;
|
|
|
|
if(this._dataChanged){
|
|
this._dataChanged = false;
|
|
this._groupingChanged = true;
|
|
this._coloringChanged = true;
|
|
}
|
|
|
|
if(this._groupingChanged){
|
|
this._groupingChanged = false;
|
|
this._set("rootItem", null);
|
|
this._updateTreeMapHierarchy();
|
|
forceCreate = true;
|
|
}
|
|
|
|
if(this._rootItemChanged){
|
|
this._rootItemChanged = false;
|
|
forceCreate = true;
|
|
}
|
|
|
|
if(this._coloringChanged){
|
|
this._coloringChanged = false;
|
|
if(this.colorModel != null && this._data != null && this.colorModel.initialize){
|
|
this.colorModel.initialize(this._data, lang.hitch(this, function(item){
|
|
return this.colorFunc(item, this.store);
|
|
}));
|
|
}
|
|
}
|
|
|
|
if(this._areaChanged){
|
|
this._areaChanged = false;
|
|
this._removeAreaForGroup();
|
|
}
|
|
|
|
if(this.domNode == undefined || this._items == null){
|
|
return;
|
|
}
|
|
|
|
if(forceCreate){
|
|
domConstruct.empty(this.domNode);
|
|
}
|
|
|
|
var rootItem = this.rootItem;
|
|
|
|
if(rootItem != null){
|
|
if(this._isLeaf(rootItem)){
|
|
rootItem = this._getRenderer(rootItem).parentItem;
|
|
}
|
|
}
|
|
|
|
var box = domGeom.getMarginBox(this.domNode);
|
|
if(rootItem != null){
|
|
this._buildRenderer(this.domNode, null, rootItem, {
|
|
x: box.l, y: box.t, w: box.w, h: box.h
|
|
}, 0, forceCreate);
|
|
}else{
|
|
this._buildChildrenRenderers(this.domNode, rootItem?rootItem:{ __treeRoot: true, children : this._items },
|
|
0, forceCreate, box);
|
|
}
|
|
},
|
|
|
|
_setRootItemAttr: function(value){
|
|
this._rootItemChanged = true;
|
|
this._set("rootItem", value);
|
|
},
|
|
|
|
_setStoreAttr: function(value){
|
|
var r;
|
|
if(value != null){
|
|
var results = value.query(this.query);
|
|
if(results.observe){
|
|
// user asked us to observe the store
|
|
results.observe(lang.hitch(this, this._updateItem), true);
|
|
}
|
|
r = when(results, lang.hitch(this, this._initItems));
|
|
}else{
|
|
r = this._initItems([]);
|
|
}
|
|
this._set("store", value);
|
|
return r;
|
|
},
|
|
|
|
_initItems: function(items){
|
|
this._dataChanged = true;
|
|
this._data = items;
|
|
this.invalidateRendering();
|
|
return items;
|
|
},
|
|
|
|
_updateItem: function(item, previousIndex, newIndex){
|
|
if(previousIndex!=-1){
|
|
if(newIndex!=previousIndex){
|
|
// this is a remove or a move
|
|
this._data.splice(previousIndex, 1);
|
|
}else{
|
|
// this is a put, previous and new index identical
|
|
// we don't know what has change exactly with store API
|
|
this._data[newIndex] = item;
|
|
}
|
|
}else if(newIndex!=-1){
|
|
// this is a add
|
|
this._data.splice(newIndex, 0, item);
|
|
}
|
|
// as we have no details let's refresh everything...
|
|
this._dataChanged = true;
|
|
this.invalidateRendering();
|
|
},
|
|
|
|
_setGroupAttrsAttr: function(value){
|
|
this._groupingChanged = true;
|
|
if(this.groupFuncs == null){
|
|
if(value !=null){
|
|
this._groupFuncs = arr.map(value, function(attr){
|
|
return function(item){
|
|
return item[attr];
|
|
};
|
|
});
|
|
}else{
|
|
this._groupFuncs = null;
|
|
}
|
|
}
|
|
this._set("groupAttrs", value);
|
|
},
|
|
|
|
_setGroupFuncsAttr: function(value){
|
|
this._groupingChanged = true;
|
|
this._set("groupFuncs", this._groupFuncs = value);
|
|
if(value == null && this.groupAttrs != null){
|
|
this._groupFuncs = arr.map(this.groupAttrs, function(attr){
|
|
return function(item){
|
|
return item[attr];
|
|
};
|
|
});
|
|
}
|
|
},
|
|
|
|
_setAreaAttrAttr: function(value){
|
|
this._areaChanged = true;
|
|
this._set("areaAttr", value);
|
|
},
|
|
|
|
// areaFunc: Function
|
|
// A function that returns the value use to compute the area of cell from a store item.
|
|
// Default implementation is using areaAttr.
|
|
areaFunc: function(/*Object*/ item, /*dojo/store/api/Store*/ store){
|
|
return (this.areaAttr && this.areaAttr.length > 0)?parseFloat(item[this.areaAttr]):1;
|
|
},
|
|
|
|
_setAreaFuncAttr: function(value){
|
|
this._areaChanged = true;
|
|
this._set("areaFunc", value);
|
|
},
|
|
|
|
// labelFunc: Function
|
|
// A function that returns the label of cell from a store item.
|
|
// Default implementation is using labelAttr.
|
|
labelFunc: function(/*Object*/ item, /*dojo/store/api/Store*/ store){
|
|
var label = (this.labelAttr && this.labelAttr.length > 0)?item[this.labelAttr]:null;
|
|
return label?label.toString():null;
|
|
},
|
|
|
|
// tooltipFunc: Function
|
|
// A function that returns the tooltip of cell from a store item.
|
|
// Default implementation is using tooltipAttr.
|
|
tooltipFunc: function(/*Object*/ item, /*dojo/store/api/Store*/ store){
|
|
var tooltip = (this.tooltipAttr && this.tooltipAttr.length > 0)?item[this.tooltipAttr]:null;
|
|
return tooltip?tooltip.toString():null;
|
|
},
|
|
|
|
_setColorModelAttr: function(value){
|
|
this._coloringChanged = true;
|
|
this._set("colorModel", value);
|
|
},
|
|
|
|
_setColorAttrAttr: function(value){
|
|
this._coloringChanged = true;
|
|
this._set("colorAttr", value);
|
|
},
|
|
|
|
// colorFunc: Function
|
|
// A function that returns from a store item the color value of cell or the value used by the
|
|
// ColorModel to compute the cell color. If a color must be returned it must be in form accepted by the
|
|
// dojo/_base/Color constructor. If a value must be returned it must be a Number.
|
|
// Default implementation is using colorAttr.
|
|
colorFunc: function(/*Object*/ item, /*dojo/store/api/Store*/ store){
|
|
var color = (this.colorAttr && this.colorAttr.length > 0)?item[this.colorAttr]:0;
|
|
if(color == null){
|
|
color = 0;
|
|
}
|
|
return parseFloat(color);
|
|
},
|
|
|
|
_setColorFuncAttr: function(value){
|
|
this._coloringChanged = true;
|
|
this._set("colorFunc", value);
|
|
},
|
|
|
|
createRenderer: function(item, level, kind){
|
|
// summary:
|
|
// Creates an item renderer of the specified kind. This is called only when the treemap
|
|
// is created. Default implementation always create div nodes. It also sets overflow
|
|
// to hidden and position to absolute on non-header renderers.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// kind: String
|
|
// The specified kind. This can either be "leaf", "group", "header" or "content".
|
|
// returns: DomNode
|
|
// The renderer use for the specified kind.
|
|
// tags:
|
|
// protected
|
|
var div = domConstruct.create("div");
|
|
if(kind != "header"){
|
|
domStyle.set(div, "overflow", "hidden");
|
|
domStyle.set(div, "position", "absolute");
|
|
}
|
|
return div;
|
|
},
|
|
|
|
styleRenderer: function(renderer, item, level, kind){
|
|
// summary:
|
|
// Style the item renderer. This is called each time the treemap is refreshed.
|
|
// For leaf items it colors them with the color computed from the color model.
|
|
// For other items it does nothing.
|
|
// renderer: DomNode
|
|
// The item renderer.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// kind: String
|
|
// The specified kind. This can either be "leaf", "group", "header" or "content".
|
|
// tags:
|
|
// protected
|
|
switch(kind){
|
|
case "leaf":
|
|
domStyle.set(renderer, "background", this.getColorForItem(item).toHex());
|
|
case "header":
|
|
var label = this.getLabelForItem(item);
|
|
if(label && (isNaN(this.labelThreshold) || level < this.labelThreshold)){
|
|
renderer.innerHTML = label;
|
|
}else{
|
|
domConstruct.empty(renderer);
|
|
}
|
|
break;
|
|
default:
|
|
|
|
}
|
|
},
|
|
|
|
_updateTreeMapHierarchy: function(){
|
|
if(this._data == null){
|
|
return;
|
|
}
|
|
if(this._groupFuncs != null && this._groupFuncs.length > 0){
|
|
this._items = utils.group(this._data, this._groupFuncs, lang.hitch(this, this._getAreaForItem)).children;
|
|
}else{
|
|
this._items = this._data;
|
|
}
|
|
},
|
|
|
|
_removeAreaForGroup: function(item){
|
|
var children;
|
|
if(item != null){
|
|
if(item.__treeValue){
|
|
delete item.__treeValue;
|
|
children = item.children;
|
|
}else{
|
|
// not a grouping item
|
|
return;
|
|
}
|
|
}else{
|
|
children = this._items;
|
|
}
|
|
if(children){
|
|
for(var i = 0; i < children.length; ++i){
|
|
this._removeAreaForGroup(children[i]);
|
|
}
|
|
}
|
|
},
|
|
|
|
_getAreaForItem: function(item){
|
|
var area = this.areaFunc(item, this.store);
|
|
return isNaN(area) ? 0 : area;
|
|
},
|
|
|
|
_computeAreaForItem: function(item){
|
|
var value;
|
|
if(item.__treeID){ // group
|
|
value = item.__treeValue;
|
|
if(!value){
|
|
value = 0;
|
|
var children = item.children;
|
|
for(var i = 0; i < children.length; ++i){
|
|
value += this._computeAreaForItem(children[i]);
|
|
}
|
|
item.__treeValue = value;
|
|
}
|
|
}else{
|
|
value = this._getAreaForItem(item);
|
|
}
|
|
return value;
|
|
},
|
|
|
|
getColorForItem: function(item){
|
|
// summary:
|
|
// Returns the color for a given item. This either use the colorModel if not null
|
|
// or just the result of the colorFunc.
|
|
// item: Object
|
|
// The data item.
|
|
// tags:
|
|
// protected
|
|
var value = this.colorFunc(item, this.store);
|
|
if(this.colorModel != null){
|
|
return this.colorModel.getColor(value);
|
|
}else{
|
|
return new Color(value);
|
|
}
|
|
},
|
|
|
|
getLabelForItem: function(item){
|
|
// summary:
|
|
// Returns the label for a given item.
|
|
// item: Object
|
|
// The data item.
|
|
// tags:
|
|
// protected
|
|
return item.__treeName?item.__treeName:this.labelFunc(item, this.store);
|
|
},
|
|
|
|
_buildChildrenRenderers: function(domNode, item, level, forceCreate, delta, anim){
|
|
var children = item.children;
|
|
var box = domGeom.getMarginBox(domNode);
|
|
|
|
var solution = utils.solve(children, box.w, box.h, lang.hitch(this,
|
|
this._computeAreaForItem), !this.isLeftToRight());
|
|
|
|
var rectangles = solution.rectangles;
|
|
|
|
if(delta){
|
|
rectangles = arr.map(rectangles, function(item){
|
|
item.x += delta.l;
|
|
item.y += delta.t;
|
|
return item;
|
|
});
|
|
}
|
|
|
|
var rectangle;
|
|
for(var j = 0; j < children.length; ++j){
|
|
rectangle = rectangles[j];
|
|
this._buildRenderer(domNode, item, children[j], rectangle, level, forceCreate, anim);
|
|
}
|
|
},
|
|
|
|
_isLeaf: function(item){
|
|
return !item.children;
|
|
},
|
|
|
|
_isRoot: function(item){
|
|
return item.__treeRoot;
|
|
},
|
|
|
|
_getRenderer: function(item, anim, parent){
|
|
if(anim){
|
|
// while animating we do that on a copy of the subtree
|
|
// so we can use our hash object to get to the renderer
|
|
for(var i = 0; i < parent.children.length; ++i){
|
|
if(parent.children[i].item == item){
|
|
return parent.children[i];
|
|
}
|
|
}
|
|
}
|
|
return this.itemToRenderer[this.getIdentity(item)];
|
|
},
|
|
|
|
_buildRenderer: function(container, parent, child, rect, level, forceCreate, anim){
|
|
var isLeaf = this._isLeaf(child);
|
|
var renderer = !forceCreate ? this._getRenderer(child, anim, container) : null;
|
|
renderer = isLeaf ? this._updateLeafRenderer(renderer, child, level) : this._updateGroupRenderer(renderer,
|
|
child, level);
|
|
if(forceCreate){
|
|
renderer.level = level;
|
|
renderer.item = child;
|
|
renderer.parentItem = parent;
|
|
this.itemToRenderer[this.getIdentity(child)] = renderer;
|
|
// update its selection status
|
|
this.updateRenderers(child);
|
|
}
|
|
|
|
// in some cases the computation might be slightly incorrect (0.0000...1)
|
|
// and due to the floor this will cause 1px gaps
|
|
|
|
var x = Math.floor(rect.x);
|
|
var y = Math.floor(rect.y);
|
|
var w = Math.floor(rect.x + rect.w + 0.00000000001) - x;
|
|
var h = Math.floor(rect.y + rect.h + 0.00000000001) - y;
|
|
|
|
// before sizing put the item inside its parent so that styling
|
|
// is applied and taken into account
|
|
if(forceCreate){
|
|
domConstruct.place(renderer, container);
|
|
}
|
|
|
|
domGeom.setMarginBox(renderer, {
|
|
l: x, t: y, w: w, h: h
|
|
});
|
|
|
|
if(!isLeaf){
|
|
var box = domGeom.getContentBox(renderer);
|
|
this._layoutGroupContent(renderer, box.w, box.h, level + 1, forceCreate, anim);
|
|
}
|
|
|
|
this.onRendererUpdated({ renderer: renderer, item: child, kind: isLeaf?"leaf":"group", level: level });
|
|
},
|
|
|
|
_layoutGroupContent: function(renderer, width, height, level, forceCreate, anim){
|
|
var header = query(".dojoxTreeMapHeader", renderer)[0];
|
|
var content = query(".dojoxTreeMapGroupContent", renderer)[0];
|
|
if(header == null || content == null){
|
|
return;
|
|
}
|
|
|
|
var box = domGeom.getMarginBox(header);
|
|
|
|
// If the header is too high, reduce its area
|
|
// and don't show the children..
|
|
if(box.h > height){
|
|
// TODO: this might cause pb when coming back to visibility later
|
|
// as the getMarginBox of the header will keep that value?
|
|
box.h = height;
|
|
domStyle.set(content, "display", "none");
|
|
}else{
|
|
domStyle.set(content, "display", "block");
|
|
domGeom.setMarginBox(content, {
|
|
l: 0, t: box.h, w: width, h: (height - box.h)
|
|
});
|
|
this._buildChildrenRenderers(content, renderer.item, level, forceCreate, null, anim);
|
|
}
|
|
|
|
domGeom.setMarginBox(header, {
|
|
l: 0, t: 0, w: width, h: box.h
|
|
});
|
|
},
|
|
|
|
_updateGroupRenderer: function(renderer, item, level){
|
|
// summary:
|
|
// Update a group renderer. This creates the renderer if not already created,
|
|
// call styleRender for it and recurse into children.
|
|
// renderer: DomNode
|
|
// The item renderer.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// tags:
|
|
// private
|
|
var forceCreate = renderer == null;
|
|
if(renderer == null){
|
|
renderer = this.createRenderer("div", level, "group");
|
|
domClass.add(renderer, "dojoxTreeMapGroup");
|
|
}
|
|
this.styleRenderer(renderer, item, level, "group");
|
|
var header = query(".dojoxTreeMapHeader", renderer)[0];
|
|
header = this._updateHeaderRenderer(header, item, level);
|
|
if(forceCreate){
|
|
domConstruct.place(header, renderer);
|
|
}
|
|
var content = query(".dojoxTreeMapGroupContent", renderer)[0];
|
|
content = this._updateGroupContentRenderer(content, item, level);
|
|
if(forceCreate){
|
|
domConstruct.place(content, renderer);
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_updateHeaderRenderer: function(renderer, item, level){
|
|
// summary:
|
|
// Update a leaf renderer. This creates the renderer if not already created,
|
|
// call styleRender for it and set the label as its innerHTML.
|
|
// renderer: DomNode
|
|
// The item renderer.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// tags:
|
|
// private
|
|
if(renderer == null){
|
|
renderer = this.createRenderer(item, level, "header");
|
|
domClass.add(renderer, "dojoxTreeMapHeader");
|
|
domClass.add(renderer, "dojoxTreeMapHeader_" + level);
|
|
}
|
|
this.styleRenderer(renderer, item, level, "header");
|
|
return renderer;
|
|
},
|
|
|
|
_updateLeafRenderer: function(renderer, item, level){
|
|
// summary:
|
|
// Update a leaf renderer. This creates the renderer if not already created,
|
|
// call styleRender for it and set the label as its innerHTML.
|
|
// renderer: DomNode
|
|
// The item renderer.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// tags:
|
|
// private
|
|
if(renderer == null){
|
|
renderer = this.createRenderer(item, level, "leaf");
|
|
domClass.add(renderer, "dojoxTreeMapLeaf");
|
|
domClass.add(renderer, "dojoxTreeMapLeaf_" + level);
|
|
}
|
|
this.styleRenderer(renderer, item, level, "leaf");
|
|
var tooltip = this.tooltipFunc(item, this.store);
|
|
if(tooltip){
|
|
renderer.title = tooltip;
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_updateGroupContentRenderer: function(renderer, item, level){
|
|
// summary:
|
|
// Update a group content renderer. This creates the renderer if not already created,
|
|
// and call styleRender for it.
|
|
// renderer:
|
|
// The item renderer.
|
|
// item: Object
|
|
// The data item.
|
|
// level: Number
|
|
// The item depth level.
|
|
// tags:
|
|
// private
|
|
if(renderer == null){
|
|
renderer = this.createRenderer(item, level, "content");
|
|
domClass.add(renderer, "dojoxTreeMapGroupContent");
|
|
domClass.add(renderer, "dojoxTreeMapGroupContent_" + level);
|
|
}
|
|
this.styleRenderer(renderer, item, level, "content");
|
|
return renderer;
|
|
},
|
|
|
|
_getRendererFromTarget: function(target){
|
|
var renderer = target;
|
|
while(renderer != this.domNode && !renderer.item){
|
|
renderer = renderer.parentNode;
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_onMouseOver: function(e){
|
|
var renderer = this._getRendererFromTarget(e.target);
|
|
if(renderer.item){
|
|
var item = renderer.item;
|
|
this._hoveredItem = item;
|
|
this.updateRenderers(item);
|
|
this.onItemRollOver({renderer: renderer, item : item, triggerEvent: e});
|
|
}
|
|
},
|
|
|
|
_onMouseOut: function(e){
|
|
var renderer = this._getRendererFromTarget(e.target);
|
|
if(renderer.item){
|
|
var item = renderer.item;
|
|
this._hoveredItem = null;
|
|
this.updateRenderers(item);
|
|
this.onItemRollOut({renderer: renderer, item : item, triggerEvent: e});
|
|
}
|
|
},
|
|
|
|
_onMouseUp: function(e){
|
|
var renderer = this._getRendererFromTarget(e.target);
|
|
if(renderer.item){
|
|
this.selectFromEvent(e, renderer.item, e.currentTarget, true);
|
|
//event.stop(e);
|
|
}
|
|
},
|
|
|
|
onRendererUpdated: function(){
|
|
// summary:
|
|
// Called when a renderer has been updated. This is called after creation, styling and sizing for
|
|
// each group and leaf renderers. For group renders this is also called after creation of children
|
|
// renderers.
|
|
// tags:
|
|
// callback
|
|
},
|
|
|
|
onItemRollOver: function(){
|
|
// summary:
|
|
// Called when an item renderer has been hovered.
|
|
// tags:
|
|
// callback
|
|
},
|
|
|
|
onItemRollOut: function(){
|
|
// summary:
|
|
// Called when an item renderer has been rolled out.
|
|
// tags:
|
|
// callback
|
|
},
|
|
|
|
updateRenderers: function(items){
|
|
// summary:
|
|
// Updates the renderer(s) that represent the specified item(s).
|
|
// item: Object|Array
|
|
// The item(s).
|
|
if(!items){
|
|
return;
|
|
}
|
|
if(!lang.isArray(items)){
|
|
items = [items];
|
|
}
|
|
for(var i=0; i<items.length;i++){
|
|
var item = items[i];
|
|
var renderer = this._getRenderer(item);
|
|
// at init time the renderer might not be ready
|
|
if(!renderer){
|
|
continue;
|
|
}
|
|
var selected = this.isItemSelected(item);
|
|
var ie = has("ie");
|
|
var div;
|
|
if(selected){
|
|
domClass.add(renderer, "dojoxTreeMapSelected");
|
|
if(ie && (has("quirks") || ie < 9)){
|
|
// let's do all of this only if not already done
|
|
div = renderer.previousSibling;
|
|
var rStyle = domStyle.get(renderer);
|
|
if(!div || !domClass.contains(div, "dojoxTreeMapIEHack")){
|
|
div = this.createRenderer(item, -10, "group");
|
|
domClass.add(div, "dojoxTreeMapIEHack");
|
|
domClass.add(div, "dojoxTreeMapSelected");
|
|
domStyle.set(div, {
|
|
position: "absolute",
|
|
overflow: "hidden"
|
|
});
|
|
domConstruct.place(div, renderer, "before");
|
|
}
|
|
// TODO: might fail if different border widths for different sides
|
|
var bWidth = 2*parseInt(domStyle.get(div, "border-width"));
|
|
if(this._isLeaf(item)){
|
|
bWidth -= 1;
|
|
}else{
|
|
bWidth += 1;
|
|
}
|
|
// if we just drill down some renders might not be laid out?
|
|
if(rStyle["left"] != "auto"){
|
|
domStyle.set(div, {
|
|
left: (parseInt(rStyle["left"])+1)+"px",
|
|
top: (parseInt(rStyle["top"])+1)+"px",
|
|
width: (parseInt(rStyle["width"])-bWidth)+"px",
|
|
height: (parseInt(rStyle["height"])-bWidth)+"px"
|
|
});
|
|
}
|
|
}
|
|
}else{
|
|
if(ie && (has("quirks") || ie < 9)){
|
|
div = renderer.previousSibling;
|
|
if(div && domClass.contains(div, "dojoxTreeMapIEHack")){
|
|
div.parentNode.removeChild(div);
|
|
}
|
|
}
|
|
domClass.remove(renderer, "dojoxTreeMapSelected");
|
|
|
|
}
|
|
if(this._hoveredItem == item){
|
|
domClass.add(renderer, "dojoxTreeMapHovered");
|
|
}else{
|
|
domClass.remove(renderer, "dojoxTreeMapHovered");
|
|
}
|
|
if(selected || this._hoveredItem == item){
|
|
domStyle.set(renderer, "zIndex", 20);
|
|
}else{
|
|
domStyle.set(renderer, "zIndex", (has("ie")<=7)?0:"auto");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|