Initial check-in
[dygraphs.git] / mochikit_v14 / examples / sortable_tables / sortable_tables.js
1 /*
2
3 On page load, the SortableManager:
4
5 - Finds the table by its id (sortable_table).
6 - Parses its thead for columns with a "mochi:format" attribute.
7 - Parses the data out of the tbody based upon information given in the
8 "mochi:format" attribute, and clones the tr elements for later re-use.
9 - Clones the column header th elements for use as a template when drawing
10 sort arrow columns.
11 - Stores away a reference to the tbody, as it will be replaced on each sort.
12 - Performs the first sort.
13
14
15 On sort request:
16
17 - Sorts the data based on the given key and direction
18 - Creates a new tbody from the rows in the new ordering
19 - Replaces the column header th elements with clickable versions, adding an
20 indicator (↑ or ↓) to the most recently sorted column.
21
22 */
23
24 SortableManager = function () {
25 this.thead = null;
26 this.tbody = null;
27 this.columns = [];
28 this.rows = [];
29 this.sortState = {};
30 this.sortkey = 0;
31 };
32
33 mouseOverFunc = function () {
34 addElementClass(this, "over");
35 };
36
37 mouseOutFunc = function () {
38 removeElementClass(this, "over");
39 };
40
41 ignoreEvent = function (ev) {
42 if (ev && ev.preventDefault) {
43 ev.preventDefault();
44 ev.stopPropagation();
45 } else if (typeof(event) != 'undefined') {
46 event.cancelBubble = false;
47 event.returnValue = false;
48 }
49 };
50
51
52 update(SortableManager.prototype, {
53
54 "initWithTable": function (table) {
55 /***
56
57 Initialize the SortableManager with a table object
58
59 ***/
60 // Ensure that it's a DOM element
61 table = getElement(table);
62 // Find the thead
63 this.thead = table.getElementsByTagName('thead')[0];
64 // get the mochi:format key and contents for each column header
65 var cols = this.thead.getElementsByTagName('th');
66 for (var i = 0; i < cols.length; i++) {
67 var node = cols[i];
68 var attr = null;
69 try {
70 attr = node.getAttribute("mochi:format");
71 } catch (err) {
72 // pass
73 }
74 var o = node.childNodes;
75 this.columns.push({
76 "format": attr,
77 "element": node,
78 "proto": node.cloneNode(true)
79 });
80 }
81 // scrape the tbody for data
82 this.tbody = table.getElementsByTagName('tbody')[0];
83 // every row
84 var rows = this.tbody.getElementsByTagName('tr');
85 for (var i = 0; i < rows.length; i++) {
86 // every cell
87 var row = rows[i];
88 var cols = row.getElementsByTagName('td');
89 var rowData = [];
90 for (var j = 0; j < cols.length; j++) {
91 // scrape the text and build the appropriate object out of it
92 var cell = cols[j];
93 var obj = scrapeText(cell);
94 switch (this.columns[j].format) {
95 case 'isodate':
96 obj = isoDate(obj);
97 break;
98 case 'str':
99 break;
100 case 'istr':
101 obj = obj.toLowerCase();
102 break;
103 // cases for numbers, etc. could be here
104 default:
105 break;
106 }
107 rowData.push(obj);
108 }
109 // stow away a reference to the TR and save it
110 rowData.row = row.cloneNode(true);
111 this.rows.push(rowData);
112
113 }
114
115 // do initial sort on first column
116 this.drawSortedRows(this.sortkey, true, false);
117
118 },
119
120 "onSortClick": function (name) {
121 /***
122
123 Return a sort function for click events
124
125 ***/
126 return method(this, function () {
127 log('onSortClick', name);
128 var order = this.sortState[name];
129 if (order == null) {
130 order = true;
131 } else if (name == this.sortkey) {
132 order = !order;
133 }
134 this.drawSortedRows(name, order, true);
135 });
136 },
137
138 "drawSortedRows": function (key, forward, clicked) {
139 /***
140
141 Draw the new sorted table body, and modify the column headers
142 if appropriate
143
144 ***/
145 log('drawSortedRows', key, forward);
146 this.sortkey = key;
147 // sort based on the state given (forward or reverse)
148 var cmp = (forward ? keyComparator : reverseKeyComparator);
149 this.rows.sort(cmp(key));
150 // save it so we can flip next time
151 this.sortState[key] = forward;
152 // get every "row" element from this.rows and make a new tbody
153 var newBody = TBODY(null, map(itemgetter("row"), this.rows));
154 // swap in the new tbody
155 this.tbody = swapDOM(this.tbody, newBody);
156 for (var i = 0; i < this.columns.length; i++) {
157 var col = this.columns[i];
158 var node = col.proto.cloneNode(true);
159 // remove the existing events to minimize IE leaks
160 col.element.onclick = null;
161 col.element.onmousedown = null;
162 col.element.onmouseover = null;
163 col.element.onmouseout = null;
164 // set new events for the new node
165 node.onclick = this.onSortClick(i);
166 node.onmousedown = ignoreEvent;
167 node.onmouseover = mouseOverFunc;
168 node.onmouseout = mouseOutFunc;
169 // if this is the sorted column
170 if (key == i) {
171 // \u2193 is down arrow, \u2191 is up arrow
172 // forward sorts mean the rows get bigger going down
173 var arrow = (forward ? "\u2193" : "\u2191");
174 // add the character to the column header
175 node.appendChild(SPAN(null, arrow));
176 if (clicked) {
177 node.onmouseover();
178 }
179 }
180
181 // swap in the new th
182 col.element = swapDOM(col.element, node);
183 }
184 }
185 });
186
187 sortableManager = new SortableManager();
188
189 addLoadEvent(function () {
190 sortableManager.initWithTable('sortable_table');
191 });
192
193 // rewrite the view-source links
194 addLoadEvent(function () {
195 var elems = getElementsByTagAndClassName("A", "view-source");
196 var page = "sortable_tables/";
197 for (var i = 0; i < elems.length; i++) {
198 var elem = elems[i];
199 var href = elem.href.split(/\//).pop();
200 elem.target = "_blank";
201 elem.href = "../view-source/view-source.html#" + page + href;
202 }
203 });