if (!window.Selectables) var Selectables = { }; Object.extend(Selectables, { hash: new Hash(), add: function(id, value) { this.hash.set(id, value); }, toggleAll: function(id) { var select = this.hash.get(id); if(select) { select.toggleAll(); } }, get: function(id) { return this.hash.get(id); } }); Element.addMethods('TABLE', { toggleAll: function(element) { Selectables.toggleAll(element.identify()); return element; } }); var positioning = false; var SelectNode = Class.create( { initialize: function(number, element, id) { this.element = element; this.positionElement = element; this.number = number; this.selected = false; this.id = id; this.left = -1; this.right = -1; this.top = -1; this.bottom = -1; } }); var TreeNode = Class.create( { initialize: function(element, id) { this.element = element; this.childrenElement = element; this.children = []; this.opener; this.id = id; }, addChild: function(child) { this.children.push(child); if(this.element == null) { child.element.show(); } else if(!this.element.visible()) { child.element.hide(); } if(!child.element.visible()) { child.opener = this; } }, getDescendants: function() { var collected = []; this.findDescendants(collected); return collected; }, findDescendants: function(collected) { for(var i = 0; i < this.children.length; i++) { collected.push(this.children[i]); this.children[i].findDescendants(collected); } }, findDescendant: function(id) { if(id == this.id) { return this; } for(var i = 0; i < this.children.length; i++) { var node = this.children[i].findDescendant(id); if(node) { return node; } } } }); var Select = Class.create( { initialize: function(element, options) { this.element = $(element); this.name = this.element.identify(); this.selectors = []; this.lastSelection = null; this.lastState = true; this.selected = null; this.position = null; this.positionBegin = false; this.positionStartX; this.positionStartY; this.positionSource; this.positionTarget; this.positionType; this.positionArrow = null; this.positionCursor = null; this.options = { multiple: true, onDblClick: null, onSelect: null, onPosition: null, isTree: false, isLarge: false, singleListeners: [], listeners: [], selected: [], expanded: [] }; Object.extend(this.options, options || { }); // -------- this.element.setStyle({cursor : 'default'}); this.element.observe('mousedown', this.mouseDown.bindAsEventListener(this)); this.element.observe('mousemove', this.mouseMove.bindAsEventListener(this)); this.element.observe('dblclick', this.dblClick.bindAsEventListener(this)); $$('html').first().observe('mouseup', this.mouseUp.bindAsEventListener(this)); // -------- var descendants = this.element.descendants(); var currentNode = null; var number = 0; for(var i = 0; i < descendants.length; i++) { if(descendants[i].identify().startsWith(this.name + '_')) { var id = descendants[i].identify().substring(this.name.length + 1); currentNode = new SelectNode(number, descendants[i], id); this.selectors.push(currentNode); number++; } if(descendants[i].hasClassName('position') && currentNode) { currentNode.positionElement = descendants[i]; } } // ---------- this.element.insert({after: ""}); this.selected = $(this.name + "_selected"); var selectedIDs = this.options.selected; for(var i = 0; i < selectedIDs.length ; i++) { var selector = this.getSelector(selectedIDs[i]); if(selector) { selector.selected = true; } } this.updateSelection(); // --------- if(this.options.onPosition) { this.options.multiple = false; this.positionArrow = $('positionarrow'); if(!this.positionArrow) { $$('body').first().insert(""); this.positionArrow = $('positionarrow'); } this.positionCursor = $('positioncursor'); if(!this.positionCursor) { $$('body').first().insert(""); this.positionCursor = $('positioncursor'); } this.element.insert({after: ""}); this.position = $(this.name + "_position"); } Selectables.add(this.name, this); }, mouseDown: function(event) { var element = Event.element(event); if(element.name) { return; } var selected = this.getSelector(element); if(selected) { if(this.options.multiple && (event.shiftKey || event.ctrlKey || event.altKey)) { this.selectMultiple(event, selected); } else { this.selectSingle(event, selected); } if(this.options.onPosition) { this.startPositioning(event); } } Event.stop(event); }, mouseMove: function(event) { if(this.options.onPosition) { this.movePositioning(event); Event.stop(event); } }, mouseUp: function(event) { if(this.options.onPosition) { this.endPositioning(event); } }, dblClick: function(event) { if(this.options.onDblClick && this.getSelector(Event.element(event))) { this.options.onDblClick(); } }, getSelector: function(object) { if(Object.isElement(object)) { var element = this.getSelectorElement(object); for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i].element == element) { return this.selectors[i]; } } } for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i].element.identify() == object) { return this.selectors[i]; } else if(this.selectors[i].id == object) { return this.selectors[i]; } } }, getSelectorElement: function(element) { if(element.identify().startsWith(this.name + '_')) { return element; } var ancestors = element.ancestors(); for(var i = 0; i < ancestors.length; i++) { var item = ancestors[i]; if(item.identify().startsWith(this.name + '_')) { return item; } } }, selectSingle: function(event, selected) { for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i] == selected) { this.selectors[i].selected = true; this.lastSelection = selected; this.lastState = true; } else { this.selectors[i].selected = false; } } this.updateSelection(); }, selectMultiple: function(event, selected) { if(event.ctrlKey || event.altKey) { for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i] == selected) { this.selectors[i].selected = !this.selectors[i].selected; this.lastState = this.selectors[i].selected; break; } } this.lastSelection = selected; } if(event.shiftKey && this.lastSelection) { var start = this.lastSelection.number; var end = selected.number; if(start > end) { start = selected.number; end = this.lastSelection.number; } for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i].number >= start && this.selectors[i].number <= end) { this.selectors[i].selected = this.lastState; } } } this.updateSelection(); }, updateSelection: function() { var ids = []; for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i] && this.selectors[i].element) { if(this.selectors[i].selected) { ids.push(this.selectors[i].id); this.selectors[i].element.addClassName('tablehilite'); } else { this.selectors[i].element.removeClassName('tablehilite'); } } } this.selected.value = ids.join(","); var listeners = this.options.listeners; for(var i = 0; i < listeners.length; i++) { var listener = $(listeners[i]); if(listener) { listener.disabled = (ids.length < 1); } } listeners = this.options.singleListeners; for(var i = 0; i < listeners.length; i++) { listener = $(listeners[i]); if(listener) { listener.disabled = (ids.length != 1); } } if(this.options.onSelect) { this.options.onSelect(ids); } }, toggleAll: function() { var state = false; for(var i = 0; i < this.selectors.length; i++) { if(!this.selectors[i].selected) { state = true; break; } } for(var i = 0; i < this.selectors.length; i++) { this.selectors[i].selected = state; } this.updateSelection(); }, selectedIDs: function() { var ids = []; for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i]) { if(this.selectors[i].selected) { ids.push(this.selectors[i].id); } } } return ids; }, resetPositioning: function() { this.positionStartX = -1; this.positionStartY = -1; this.positionBegin = false; this.positionSource = null; this.positionTarget = null; this.positionType = null; positioning = false; }, updatePositions: function() { var lastSelector = null; for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i].element.visible()) { var left = 0; var top = 0; var node = this.selectors[i].positionElement; while(node) { if(node.nodeName == "TR") { left += node.cells[0].offsetLeft; top += node.cells[0].offsetTop; } else { left += node.offsetLeft; top += node.offsetTop; } node = node.offsetParent; } var dimensions = this.selectors[i].positionElement.getDimensions(); this.selectors[i].left = left; this.selectors[i].right = left + dimensions.width; this.selectors[i].top = top; this.selectors[i].bottom = top + dimensions.height; if(lastSelector) { if((this.selectors[i].top - lastSelector.bottom) < 16) { var average = Math.round((this.selectors[i].top + lastSelector.bottom) / 2); this.selectors[i].top = average; lastSelector.bottom = average; } } lastSelector = this.selectors[i]; } } }, startPositioning: function(event) { this.resetPositioning(); this.hideCursor(); this.updatePositions(); this.positionSource = this.getSelector(Event.element(event)); this.positionStartX = Event.pointerX(event); this.positionStartY = Event.pointerY(event); positioning = true; }, movePositioning: function(event) { var x = Event.pointerX(event); var y = Event.pointerY(event); if(this.positionBegin) { if(this.positionSource) { var position = this.getSelector(Event.element(event)); if(position) { this.positionTarget = position; if(this.options.isTree) { var topQuarter = position.top + (position.bottom - position.top) / 4; var bottomQuarter = position.top + (position.bottom - position.top) * 3 / 4; if(y < topQuarter) { this.positionType = "before"; } else if(y >= bottomQuarter) { this.positionType = "after"; } else { this.positionType = "in"; } } else { var middle = (position.top + position.bottom) / 2; if(y < middle) { this.positionType = "before"; } else { this.positionType = "after"; } } this.hideCursor(); this.updateCursor(); } } else { this.resetPositioning(); } } else { if(this.positionSource) { if(Math.abs(x - this.positionStartX) > 1 || Math.abs(y - this.positionStartY) > 4) { this.positionBegin = true; } } } }, endPositioning: function(event) { this.hideCursor(); if(this.positionTarget && this.positionTarget != this.positionSource) { this.position.value = this.positionSource.id + " " + this.positionType + " " + this.positionTarget.id; this.options.onPosition(); } this.resetPositioning(); }, hideCursor: function() { this.positionArrow.hide(); this.positionCursor.hide(); for(var i = 0; i < this.selectors.length; i++) { if(this.options.isTree) { this.selectors[i].positionElement.removeClassName('tablehilite'); } } }, updateCursor: function() { var hoffset = 12; var voffset = 5; var size = 3; if(this.positionType == "before") { this.positionArrow.setStyle( { left: this.positionTarget.left - hoffset, top: this.positionTarget.top - voffset }); this.positionCursor.setStyle( { left: this.positionTarget.left, width: this.positionTarget.right - this.positionTarget.left, top: this.positionTarget.top - 1, height: size }); this.positionArrow.show(); this.positionCursor.show(); } else if(this.positionType == "after") { this.positionArrow.setStyle( { left: this.positionTarget.left - hoffset, top: this.positionTarget.bottom - voffset }); this.positionCursor.setStyle( { left: this.positionTarget.left, width: this.positionTarget.right - this.positionTarget.left, top: this.positionTarget.bottom - 1, height: size }); this.positionArrow.show(); this.positionCursor.show(); } else if(this.positionType == "in") { this.positionTarget.positionElement.addClassName('tablehilite'); this.positionArrow.setStyle( { left: this.positionTarget.left - hoffset, top: ((this.positionTarget.top + this.positionTarget.bottom) / 2) - voffset }); this.positionArrow.show(); } } }); var Table = Class.create(Select, { initialize: function($super, element, options) { $super(element, options); } }); var ToggleTable = Class.create(Select, { initialize: function($super, element, options) { if(!options) { options = {}; } if(!options.expandedURL) { options.expandedURL = '../images/expanded.html'; } if(!options.collapsedURL) { options.collapsedURL = '../images/collapsed.html'; } $super(element, options); // --------- this.element.observe('click', this.click.bindAsEventListener(this)); // -------- var descendants = this.element.descendants(); for(var i = 0; i < descendants.length; i++) { if(descendants[i].identify().startsWith('parent_' + this.name + '_')) { var toggle = $('toggle_' + descendants[i].identify().substring(7)); if(toggle) { if(descendants[i].visible()) { toggle.src = this.options.expandedURL; } else { toggle.src = this.options.collapsedURL; } } } } }, mouseDown: function($super, event) { var toggle = Event.element(event); if(toggle.identify().startsWith('toggle_' + this.name + '_')) { return; } $super(event); }, click: function(event) { var toggle = Event.element(event); if(toggle.identify().startsWith('toggle_' + this.name + '_')) { var element = $('parent_' + toggle.identify().substring(7)); if(element) { element.toggle(); if(element.visible()) { toggle.src = this.options.expandedURL; if(this.options.onExpand) { this.options.onExpand(); } } else { toggle.src = this.options.collapsedURL; if(this.options.onCollapse) { this.options.onCollapse(); } } } } }, getSelectorElement: function($super, element) { var result = $super(element); if(result) { return result; } if(element.identify().startsWith('parent_' + this.name + '_')) { return $(element.identify().substring(7)); } var ancestors = element.ancestors(); for(var i = 0; i < ancestors.length; i++) { var item = ancestors[i]; if(item.identify().startsWith('parent_' + this.name + '_')) { return $(item.identify().substring(7)); } } }, updateSelection: function($super) { $super(); var ids = []; for(var i = 0; i < this.selectors.length; i++) { if(this.selectors[i]) { var element = $('parent_' + this.name + '_' + this.selectors[i].id) if(element) { if(this.selectors[i].selected) { element.addClassName('tablehilite'); } else { element.removeClassName('tablehilite'); } } } } } }); var Tree = Class.create(Select, { nodes: new Hash(), initialize: function($super, element, options) { if(!options) { options = {}; } if(!options.expandedURL) { options.expandedURL = '../images/expanded.html'; } if(!options.collapsedURL) { options.collapsedURL = '../images/collapsed.html'; } options.isTree = true; $super(element, options); this.root = new TreeNode(null); // -------- this.element.observe('click', this.click.bindAsEventListener(this)); // -------- this.nodes = new Hash(); var currentNode = null; var descendants = this.element.descendants(); for(var i = 0; i < descendants.length; i++) { if(descendants[i].identify().startsWith(this.name + '_')) { var id = descendants[i].identify().substring(this.name.length + 1); var parentid = ''; var classNames = $w(descendants[i].className); for(var j = 0; j < classNames.length; j++) { if(classNames[j].startsWith('parent_' + this.name + '_')) { parentid = classNames[j].substring(this.name.length + 8); } } var parent = this.nodes.get(parentid); if(parent == null) { parent = this.root; } currentNode = new TreeNode(descendants[i], id); this.nodes.set(id, currentNode); parent.addChild(currentNode); } if(descendants[i].hasClassName('children') && currentNode) { currentNode.childrenElement = descendants[i]; } } // --------- var nodes = this.root.getDescendants(); for(var i = 0; i < nodes.length; i++) { var toggle = $('toggle_' + nodes[i].element.identify()) if(toggle) { if(this.isExpanded(nodes[i])) { toggle.src = this.options.expandedURL; } else { toggle.src = this.options.collapsedURL; } } } // ------- this.element.insert({after: ""}); this.expanded = $(this.name + "_expanded"); // ------- this.updateExpansions(); }, mouseDown: function($super, event) { var toggle = Event.element(event); if(toggle.identify().startsWith('toggle_' + this.name + '_')) { return; } $super(event); }, click: function(event) { var toggle = Event.element(event); if(toggle.identify().startsWith('toggle_' + this.name + '_')) { var element = $(toggle.identify().substring(7)); if(element) { var id = element.identify().substring(this.name.length + 1); if(this.options.isLarge) { var expansions = []; var collapsed = false; for(var i = 0; i < this.options.expanded.length; i++) { if(this.options.expanded[i] == id) { collapsed = true; continue; } expansions.push(this.options.expanded[i]); } if(!collapsed) { expansions.push(id); } this.expanded.value = expansions.join(","); if(this.options.persistant) { setCookie(this.options.persistant, this.expanded.value, 0, "/"); } var ancestors = this.element.ancestors(); var submittedForm = false; for(var i = 0; i < ancestors.length; i++) { var item = ancestors[i]; if(item.tagName == "FORM") { submittedForm = true; item.submit(); } } if(!submittedForm) { window.location.reload(true); } } else { var node = this.nodes.get(id); if(this.options.isTable) { var descendants = node.getDescendants(); for(var i = 0; i < descendants.length; i++) { if(descendants[i].opener == null) { descendants[i].opener = node; descendants[i].element.hide(); } else if(descendants[i].opener == node) { descendants[i].opener = null; descendants[i].element.show(); } } } else { node.childrenElement.toggle(); } if(this.isExpanded(node)) { toggle.src = this.options.expandedURL; if(this.options.onExpanded) { this.options.onExpanded(); } } else { toggle.src = this.options.collapsedURL; var descendants = node.getDescendants(); for(var i = 0; i < descendants.length; i++) { var selector = this.getSelector(descendants[i].id); if(selector) { selector.selected = false; } } this.updateSelection(); if(this.options.onCollapssd) { this.options.onCollapssd(); } } this.updateExpansions(); } } } }, isExpanded: function(node) { if(this.options.isTable) { return(node.children.length > 0 && node.children[0].element.visible()); } else { return node.childrenElement.visible(); } }, updateExpansions: function(id, isExpanding) { var expansions = []; var nodes = this.root.getDescendants(); for(var i = 0; i < nodes.length; i++) { if(nodes[i].id && nodes[i].children.length > 0 && this.isExpanded(nodes[i])) { expansions.push(nodes[i].id); } } this.expanded.value = expansions.join(","); if(this.options.persistant) { setCookie(this.options.persistant, this.expanded.value, 0, "/"); } } }); var TreeTable = Class.create(Tree, { initialize: function($super, element, options) { if(!options) { options = {}; } options.isTable = true; $super(element, options); } }); function disableSelect() { if(positioning) { return false; } } function restoreSelect() { return true; } document.onselectstart = disableSelect; document.onmousedown = disableSelect; document.onclick = restoreSelect;