1 /*----------------------------------------------------------------------------\
2 | Selectable Elements 1.02 |
3 |-----------------------------------------------------------------------------|
4 | Created by Erik Arvidsson |
5 | (http://webfx.eae.net/contact.html#erik) |
6 | For WebFX (http://webfx.eae.net/) |
7 |-----------------------------------------------------------------------------|
8 | A script that allows children of any element to be selected |
9 |-----------------------------------------------------------------------------|
10 | Copyright (c) 1999 - 2004 Erik Arvidsson |
11 |-----------------------------------------------------------------------------|
12 | This software is provided "as is", without warranty of any kind, express or |
13 | implied, including but not limited to the warranties of merchantability, |
14 | fitness for a particular purpose and noninfringement. In no event shall the |
15 | authors or copyright holders be liable for any claim, damages or other |
16 | liability, whether in an action of contract, tort or otherwise, arising |
17 | from, out of or in connection with the software or the use or other |
18 | dealings in the software. |
19 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
20 | This software is available under the three different licenses mentioned |
21 | below. To use this software you must chose, and qualify, for one of those. |
22 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
23 | The WebFX Non-Commercial License http://webfx.eae.net/license.html |
24 | Permits anyone the right to use the software in a non-commercial context |
26 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
27 | The WebFX Commercial license http://webfx.eae.net/commercial.html |
28 | Permits the license holder the right to use the software in a commercial |
29 | context. Such license must be specifically obtained, however it's valid for |
30 | any number of implementations of the licensed software. |
31 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
32 | GPL - The GNU General Public License http://www.gnu.org/licenses/gpl.txt |
33 | Permits anyone the right to use and modify the software without limitations |
34 | as long as proper credits are given and the original and modified source |
35 | code are included. Requires that the final product, software derivate from |
36 | the original source or any software utilizing a GPL component, such as |
37 | this, is also licensed under the GPL license. |
38 |-----------------------------------------------------------------------------|
39 | 2002-09-19 | Original Version Posted. |
40 | 2002-09-27 | Fixed a bug in IE when mouse down and up occured on different |
42 | 2003-02-11 | Minor problem with addClassName and removeClassName that |
43 | | triggered a bug in Opera 7. Added destroy method |
44 |-----------------------------------------------------------------------------|
45 | Created 2002-09-04 | All changes are in the log above. | Updated 2003-02-11 |
46 \----------------------------------------------------------------------------*/
48 function SelectableElements(oElement, bMultiple) {
52 this._htmlElement = oElement;
53 this._multiple = Boolean(bMultiple);
55 this._selectedItems = [];
56 this._fireChange = true;
59 this._onclick = function (e) {
60 if (e == null) e = oElement.ownerDocument.parentWindow.event;
64 if (oElement.addEventListener)
65 oElement.addEventListener("click", this._onclick, false);
66 else if (oElement.attachEvent)
67 oElement.attachEvent("onclick", this._onclick);
70 SelectableElements.prototype.setItemSelected = function (oEl, bSelected) {
71 if (!this._multiple) {
73 var old = this._selectedItems[0]
77 this.setItemSelectedUi(old, false);
78 this.setItemSelectedUi(oEl, true);
79 this._selectedItems = [oEl];
83 if (this._selectedItems[0] == oEl) {
84 this.setItemSelectedUi(oEl, false);
85 this._selectedItems = [];
90 if (Boolean(oEl._selected) == Boolean(bSelected))
93 this.setItemSelectedUi(oEl, bSelected);
96 this._selectedItems[this._selectedItems.length] = oEl;
101 for (var i = 0; i < this._selectedItems.length; i++) {
102 if (this._selectedItems[i] != oEl)
103 tmp[j++] = this._selectedItems[i];
105 this._selectedItems = tmp;
111 // This method updates the UI of the item
112 SelectableElements.prototype.setItemSelectedUi = function (oEl, bSelected) {
114 addClassName(oEl, "selected");
116 removeClassName(oEl, "selected");
118 oEl._selected = bSelected;
121 SelectableElements.prototype.getItemSelected = function (oEl) {
122 return Boolean(oEl._selected);
125 SelectableElements.prototype.fireChange = function () {
126 if (!this._fireChange)
128 if (typeof this.onchange == "string")
129 this.onchange = new Function(this.onchange);
130 if (typeof this.onchange == "function")
135 SelectableElements.prototype.click = function (e) {
136 var oldFireChange = this._fireChange;
137 this._fireChange = false;
139 // create a copy to compare with after changes
140 var selectedBefore = this.getSelectedItems(); // is a cloned array
143 var el = e.target != null ? e.target : e.srcElement;
144 while (el != null && !this.isItem(el))
147 if (el == null) { // happens in IE when down and up occur on different items
148 this._fireChange = oldFireChange;
153 var aIndex = this._anchorIndex;
155 // test whether the current row should be the anchor
156 if (this._selectedItems.length == 0 || (e.ctrlKey && !e.shiftKey && this._multiple)) {
157 aIndex = this._anchorIndex = rIndex;
160 if (!e.ctrlKey && !e.shiftKey || !this._multiple) {
162 var items = this._selectedItems;
163 for (var i = items.length - 1; i >= 0; i--) {
164 if (items[i]._selected && items[i] != el)
165 this.setItemSelectedUi(items[i], false);
167 this._anchorIndex = rIndex;
169 this.setItemSelectedUi(el, true);
171 this._selectedItems = [el];
175 else if (this._multiple && e.ctrlKey && !e.shiftKey) {
176 this.setItemSelected(el, !el._selected);
177 this._anchorIndex = rIndex;
181 else if (this._multiple && e.ctrlKey && e.shiftKey) {
183 var dirUp = this.isBefore(rIndex, aIndex);
186 while (item != null && item != rIndex) {
187 if (!item._selected && item != el)
188 this.setItemSelected(item, true);
189 item = dirUp ? this.getPrevious(item) : this.getNext(item);
193 this.setItemSelected(el, true);
197 else if (this._multiple && !e.ctrlKey && e.shiftKey) {
199 var dirUp = this.isBefore(rIndex, aIndex);
202 var items = this._selectedItems;
203 for (var i = items.length - 1; i >= 0; i--)
204 this.setItemSelectedUi(items[i], false);
205 this._selectedItems = [];
207 // select items in range
209 while (item != null) {
210 this.setItemSelected(item, true);
213 item = dirUp ? this.getPrevious(item) : this.getNext(item);
219 var changed = selectedBefore.length != this._selectedItems.length;
221 for (var i = 0; i < selectedBefore.length; i++) {
223 for (var j = 0; j < this._selectedItems.length; j++) {
224 if (selectedBefore[i] == this._selectedItems[j]) {
236 this._fireChange = oldFireChange;
237 if (changed && this._fireChange)
241 SelectableElements.prototype.getSelectedItems = function () {
243 var items = this._selectedItems;
244 var l = items.length;
245 var tmp = new Array(l);
246 for (var i = 0; i < l; i++)
251 SelectableElements.prototype.isItem = function (node) {
252 return node != null && node.nodeType == 1 && node.parentNode == this._htmlElement;
255 SelectableElements.prototype.destroy = function () {
256 if (this._htmlElement.removeEventListener)
257 this._htmlElement.removeEventListener("click", this._onclick, false);
258 else if (this._htmlElement.detachEvent)
259 this._htmlElement.detachEvent("onclick", this._onclick);
261 this._htmlElement = null;
262 this._onclick = null;
263 this._selectedItems = null;
266 /* Traversable Collection Interface */
268 SelectableElements.prototype.getNext = function (el) {
269 var n = el.nextSibling;
270 if (n == null || this.isItem(n))
272 return this.getNext(n);
275 SelectableElements.prototype.getPrevious = function (el) {
276 var p = el.previousSibling;
277 if (p == null || this.isItem(p))
279 return this.getPrevious(p);
282 SelectableElements.prototype.isBefore = function (n1, n2) {
283 var next = this.getNext(n1);
284 while (next != null) {
287 next = this.getNext(next);
292 /* End Traversable Collection Interface */
294 /* Indexable Collection Interface */
296 SelectableElements.prototype.getItems = function () {
299 var cs = this._htmlElement.childNodes;
301 for (var i = 0; i < l; i++) {
302 if (cs[i].nodeType == 1)
308 SelectableElements.prototype.getItem = function (nIndex) {
310 var cs = this._htmlElement.childNodes;
312 for (var i = 0; i < l; i++) {
313 if (cs[i].nodeType == 1) {
322 SelectableElements.prototype.getSelectedIndexes = function () {
323 var items = this.getSelectedItems();
324 var l = items.length;
325 var tmp = new Array(l);
326 for (var i = 0; i < l; i++)
327 tmp[i] = this.getItemIndex(items[i]);
332 SelectableElements.prototype.getItemIndex = function (el) {
334 var cs = this._htmlElement.childNodes;
336 for (var i = 0; i < l; i++) {
339 if (cs[i].nodeType == 1)
345 /* End Indexable Collection Interface */
349 function addClassName(el, sClassName) {
350 var s = el.className;
351 var p = s.split(" ");
352 if (p.length == 1 && p[0] == "")
356 for (var i = 0; i < l; i++) {
357 if (p[i] == sClassName)
360 p[p.length] = sClassName;
361 el.className = p.join(" ");
364 function removeClassName(el, sClassName) {
365 var s = el.className;
366 var p = s.split(" ");
370 for (var i = 0; i < l; i++) {
371 if (p[i] != sClassName)
374 el.className = np.join(" ");