aboutsummaryrefslogtreecommitdiff
path: root/includes/js/dojox/widget/SortList.js
blob: e50ec2b88b23d8bb4eaa82b5dc51ba6bfa43c7de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
if(!dojo._hasResource["dojox.widget.SortList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.widget.SortList"] = true;
dojo.provide("dojox.widget.SortList");
dojo.experimental("dojox.widget.SortList"); // level: prototype, designed for dijit.chat.demo

dojo.require("dijit.layout._LayoutWidget");
dojo.require("dijit._Templated");

dojo.declare("dojox.widget.SortList",
	[dijit.layout._LayoutWidget, dijit._Templated],
	{
	// summary: A sortable unordered-list with a fixed header for use in dijit.demos.chat
	//		for demonstration purposes only for now. feel free to make API suggestions
	//		or fixes. 
	//
	// title: String 
	//		The title in the header
	title: "",
	
	// heading: String
	//		In the event a parent container is expecting a title="" attribute, set it for the parent 
	//		via title, and the title of this widget via heading="" ... assuming you want different 
	//		titles for each. eg: TabContainer, AccordionContainer, etc. 
	heading: "",

	// descending: Boolean
	//		Toggle sort order based on this value. 
	descending: true,

	// selected: Array
	//		A list of the selected <li> nodes at any given time.
	selected: null,

	// sortable: Boolean
	//	toggle to enable/disable sorting
	sortable: true,

	// FIXME: this is really simple store support
	store: "",
	key: "name",

	templateString:"<div class=\"sortList\" id=\"${id}\">\n\t\t<div class=\"sortListTitle\" dojoAttachPoint=\"titleNode\">\n\t\t<div class=\"sortListIcon\"></div>\n\t\t<span dojoAttachPoint=\"focusNode\">${title}</span>\n\t\t</div>\n\t\t<div class=\"sortListBodyWrapper\" dojoAttachEvent=\"onmouseover: _set, onmouseout: _unset, onclick:_handleClick\" dojoAttachPoint=\"bodyWrapper\">\n\t\t<ul dojoAttachPoint=\"containerNode\" class=\"sortListBody\"></ul>\n\t</div>\n</div>\n",

	_addItem: function(item){
		var node = dojo.doc.createElement("li");
		var text = this.store.getValue(item,this.key);
		node.innerHTML = text;
		this.containerNode.appendChild(node);
	},

	postCreate: function(){
		if(this.store){
			this.store = dojo.getObject(this.store);
			var props = {
				onItem: dojo.hitch(this,"_addItem"),
				onComplete: dojo.hitch(this,"onSort")
			};
			this.store.fetch(props);	
		}else{ this.onSort(); }
		this.inherited(arguments);
	},

	startup: function(){
		this.inherited(arguments);
		if(this.heading){ 
			this.setTitle(this.heading); this.title=this.heading; 
		}
		// we cheat, and give the browser just enough time so we know our height
		setTimeout(dojo.hitch(this,"resize"),5);
		if (this.sortable){ this.connect(this.titleNode,"onclick", "onSort"); }
	},

	resize: function(){
		// summary: do our additional calculations when resize() is called by or in a parent
		this.inherited(arguments);
		// FIXME: 
		// the 10 comes from the difference between the contentBox and calculated height
		// because of badding and border extents. this shouldn't be done this way, a theme change will 
		// break it: but we also don't want to run getComputedStyle or dojo.coords() every time resize() 
		// is fired.
		var offset = ((this._contentBox.h) - (dojo.style(this.titleNode,"height")))-10;
		this.bodyWrapper.style.height = Math.abs(offset) + "px"; 
	},
	
	onSort: function(/* Event */e){
		// summary: sort the data, and style the nodes.

		var arr = dojo.query("li",this.domNode);
		if (this.sortable){
			this.descending = !this.descending;
			dojo.addClass(this.titleNode,((this.descending)?"sortListDesc":"sortListAsc"));
			dojo.removeClass(this.titleNode,((this.descending)?"sortListAsc":"sortListDesc"));
			arr.sort(this._sorter);
			if(this.descending){ arr.reverse(); }
		}
		var i=0;
		dojo.forEach(arr,function(item){
			dojo[(i++)%2 === 0 ? "addClass" : "removeClass"](item,"sortListItemOdd");
			this.containerNode.appendChild(item); 
		},this);
	},
	
	_set: function(/* Event */e){
		// summary: set hover state 
		if(e.target !== this.bodyWrapper){
			dojo.addClass(e.target,"sortListItemHover");
		}
	},

	_unset: function(/* Event */e){
		// summary: remove hover state (FIXME: combine with _set?) 
		dojo.removeClass(e.target,"sortListItemHover"); 
	},

	_handleClick: function(/* Event */e){
		// summary: click listener for data portion of widget. toggle selected state
		//	of node, and update this.selected array accordingly
		dojo.toggleClass(e.target,"sortListItemSelected");
		e.target.focus();
		this._updateValues(e.target.innerHTML);
	},

	_updateValues: function(){
		this._selected = dojo.query("li.sortListItemSelected",this.containerNode);
		this.selected = [];
		dojo.forEach(this._selected,function(node){
			this.selected.push(node.innerHTML);
		},this);
		this.onChanged(arguments);
	},

	_sorter: function(a,b){
		// summary: a basic sort function, use query sort, or keep this?
		var aStr = a.innerHTML;
		var bStr = b.innerHTML;
		if(aStr>bStr){ return 1; }
		if(aStr<bStr){ return -1; }
		return 0;
	},

	setTitle: function(/* String */title){
		// summary: Sets the widget title to a String
		this.focusNode.innerHTML = this.title = title;
	},

	onChanged: function(){
		// summary: stub function, passes the last changed item, and is fired after current state 
	}
});

}