discourse-legacysite-perl/site/slowtwitch.com/www/articles/static/treecats.js
2024-06-17 22:27:49 +10:00

859 lines
32 KiB
JavaScript

/*
* =================================================================
* Gossamer Links - enhanced directory management system
*
* Website : http://gossamer-threads.com/
* Support : http://gossamer-threads.com/scripts/support/
* Revision : $Id: treecats.js,v 1.29 2007/11/15 01:20:18 brewt Exp $
*
* Copyright (c) 2006 Gossamer Threads Inc. All Rights Reserved.
* Redistribution in part or in whole strictly prohibited. Please
* see LICENSE file for full details.
* =================================================================
*/
/*
<!-- this.objects.workspace -->
<div id="treecats">
<!-- this.objects.selection -->
<div class="treecats-selection/treecats-selection-summary treecats-selection-single/treecats-selection-multiple">
<!-- this.objects.selectionList -->
<ul>
<li class="treecats-selection-current"><input type="hidden" name="[this.config.inputName]" value="<id>" /><span>Full/Category</span><a class="treecats-selection-change">[this.lang.change]</a><a class="treecats-selection-remove">[this.lang.remove]</a><a class="treecats-selection-done">[this.lang.done]<a/><a class="treecats-selection-cancel">[this.lang.cancel]</a></li>
...
</ul>
<!-- this.objects.selectionListAdd -->
<a class="treecats-selection-add">[this.lang.add]</a>
</div>
<!-- this.objects.tree -->
<div class="treecats-tree">
<div id="tc-c[id]" class="treecats-category">
<div class="treecats-category-info treecats-selected">
<a href="javascript:tc.navigate([id])"><img /></a> <span title="Full/Category"><a href="javascript:tc.select(<id>)">Category Name</a></span>
</div>
<div class="treecats-children">
<div id="tc-c[id]" class="treecats-category">...</div>
...
<ul class="treecats-links">
<li class="treecats-selected"><a href="javascript:tc.selectLink([id])" title="[URL]">Link Title</a></li>
...
</ul>
</div>
</div>
</div>
</div>
TODO
where to append the div's to (for absolute positioning to work)
scroll to the selection (perhaps this should be added to _resize)
*/
function treecats(config, lang) {
this.config = {
// The method to use to browse categories.
// "normal"
// "collapsed" - when viewing a category, collapse all categories on the same level
browseMode : 'normal',
// This controls whether or not multiple categories may be selected or if links
// are to be selected. It may be either: "single", "multiple", or "link".
selectionMode : 'single',
// When selectionMode is 'multiple', this is the maximum number of categories
// to allow. Set this to 0 for an unlimited number of categories.
multipleMax : 0,
// Whether or not a selection is required. If a selection is not required, a
// root entry will be displayed (using lang.rootText), allowing the user to
// unselect the current selection. Note that this option does not force the
// user to make a selection. This option is only valid if selectionMode is set
// to 'single'.
selectionRequired : true,
// Whether or not selecting a category will expand its children.
selectionExpands : true,
// The URL to treecats.cgi.
cgiURL : '',
// Extra query string to add to the url.
cgiQueryString : '',
// The URL to the expand.gif, collapse.gif, root.gif and loading.gif images.
imageURL : '',
// The name of the treecats object.
objName : 'tc',
// The id of the area that treecats will use.
workspace : 'treecats',
// The maximum height (in pixels) of the tree area before a scrollbar is
// enabled. Set this to 0 for no maximum height.
maxHeight : 500,
// The form input name.
inputName : 'CatLinks.CategoryID'
};
this.lang = {
noSelection : 'No category selected',
noSelectionLink : 'No link selected',
rootText : 'All Categories',
expand : 'Expand',
collapse : 'Collapse',
change : '[Change]',
remove : '[Remove]',
done : '[Done]',
cancel : '[Cancel]',
add : '[Add]',
loading : 'Loading',
duplicate : 'You have already selected this category.'
};
if (config) {
for (var key in config)
this.config[key] = config[key];
}
if (lang) {
for (var key in lang)
this.lang[key] = lang[key];
}
this.objects = {
workspace : null,
selection : null,
selectionList : null,
selectionListAdd : null,
tree : null
};
// A backup of the selection to allow cancel
this.selectionBackup = null;
// The ID of the selection item currently being used
this.currentSelectionItem = 0;
// The ID of the category/link that is currently selected
this.currentSelection = null;
// The links (object) of the previously selected category
this.links = null;
// Mapping of link ID to category ID
this.linkToCategory = {};
// Constants for _traverse()
this.SINGLE = 0;
this.DOWN = 1;
this.UP = 2;
// Constants for navigate()
this.AUTO = 0;
this.EXPAND = 1;
this.COLLAPSE = 2;
}
treecats.prototype.load = function () {
if (this.config.selectionMode != 'single')
this.config.selectionRequired = true;
// Accept pre-selected categories to be either inputs already on the page, or
// as arguments to load(). If you use inputs already on the page, then you
// must call load() after all the inputs have been loaded.
var fetch = [];
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].name == this.config.inputName) {
if (inputs[i].value > 0)
fetch.push(inputs[i].value);
inputs[i].parentNode.removeChild(inputs[i]);
i--;
}
}
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > 0)
fetch.push(arguments[i]);
}
this.objects.workspace = document.getElementById(this.config.workspace);
this.objects.selection = document.createElement('div');
this.objects.selection.className = 'treecats-selection-summary ' + (this.config.selectionMode == 'multiple' ? 'treecats-selection-multiple' : 'treecats-selection-single');
this.objects.workspace.appendChild(this.objects.selection);
this.objects.selectionList = document.createElement('ul');
this.objects.selection.appendChild(this.objects.selectionList);
if (this.config.selectionMode == 'multiple') {
this.objects.selectionListAdd = document.createElement('a');
this.objects.selectionListAdd.href = 'javascript:' + this.config.objName + '.add()';
this.objects.selectionListAdd.className = 'treecats-selection-add';
this.objects.selectionListAdd.style.display = 'none';
this.objects.selectionListAdd.appendChild(document.createTextNode(this.lang.add));
this.objects.selection.appendChild(this.objects.selectionListAdd);
}
this.objects.tree = document.createElement('div');
this.objects.tree.className = 'treecats-tree';
this.objects.tree.style.display = 'none';
this.objects.workspace.appendChild(this.objects.tree);
if (this.config.cgiURL)
this.config.cgiURL = this.config.cgiURL.replace(/^https?:\/\/[^\/]+\//, '/');
if (fetch.length == 0) {
this._createSelection();
fetch = [0];
}
var type = this.config.selectionMode == 'link' ? 'lid=' : 'cid=';
this._asyncReq(this._treeHandler, this.config.cgiURL + '/treecats.cgi', type + fetch.join('&' + type) + (this.config.cgiQueryString ? '&' + this.config.cgiQueryString : ''));
}
// Toggle the summary mode display. Pass in a true value for the cancel
// argument if you wish to revert to the original selection.
treecats.prototype.toggle = function (id, cancel) {
if (!id)
return;
var selected = document.getElementById(this._sid(id));
// Show the tree
if (this.objects.tree.style.display == 'none') {
this.currentSelectionItem = id;
// Save the current selection (so the user can cancel)
this.selectionBackup = { title : selected.childNodes[1].firstChild.nodeValue, value : selected.firstChild.value };
for (var i = 0; i < this.objects.selectionList.childNodes.length; i++) {
var curr = this.objects.selectionList.childNodes[i];
// Show the Done/Cancel links on the current selection item
if (curr == selected)
curr.childNodes[4].style.display = curr.childNodes[5].style.display = '';
// Hide the Change/Remove links
curr.childNodes[2].style.display = curr.childNodes[3].style.display = 'none';
}
this.collapseAll();
var sid = selected.firstChild.value || 0;
if (this.config.selectionMode != 'link') {
this.expandTo(sid);
this.select(sid);
}
else {
this.expandTo(this.linkToCategory[sid], true);
this.selectLink(sid);
}
selected.childNodes[1].className = 'treecats-selection-current';
this.objects.tree.style.display = '';
this.objects.selection.className = this.objects.selection.className.replace(/treecats-selection-summary /, 'treecats-selection ');
this._resize();
}
else {
// Check to see that the user's selection isn't a duplicate
if (!cancel && this.config.selectionMode == 'multiple' && selected.firstChild.value > 0) {
for (var i = 0; i < this.objects.selectionList.childNodes.length; i++) {
var curr = this.objects.selectionList.childNodes[i];
if (curr == selected)
continue;
if (curr.firstChild.value == selected.firstChild.value) {
alert(this.lang.duplicate);
return;
}
}
}
// Restore the previous selection
if (cancel)
this._updateSelection(this.selectionBackup.title, this.selectionBackup.value);
for (var i = 0; i < this.objects.selectionList.childNodes.length; i++) {
var curr = this.objects.selectionList.childNodes[i];
// Hide the Done/Cancel links
if (curr == selected)
curr.childNodes[4].style.display = curr.childNodes[5].style.display = 'none';
// Show the Change/Remove links
curr.childNodes[2].style.display = '';
if (this.objects.selectionList.childNodes.length > 1)
curr.childNodes[3].style.display = '';
}
selected.childNodes[1].className = '';
this.objects.tree.style.display = 'none';
this.objects.selection.className = this.objects.selection.className.replace(/treecats-selection /, 'treecats-selection-summary ');
}
this._updateListAdd();
}
// Select a Category
treecats.prototype.select = function (id) {
if (this.config.selectionMode == 'link')
return;
// Unselect the previously selected category
var already_selected = this.currentSelection == id;
if (!already_selected) {
var prevCat = document.getElementById(this._id(this.currentSelection));
if (prevCat)
prevCat.firstChild.className = prevCat.firstChild.className.replace(/ ?treecats-selected/, '');
this.currentSelection = id;
}
var cat = document.getElementById(this._id(id));
if (!cat)
return;
if (!already_selected) {
this._updateSelection(id > 0 ? cat.firstChild.lastChild.title : this.lang.rootText, id);
// Set the selected class on the selection
cat.firstChild.className += ' treecats-selected';
}
// Expand the category's children on selection
if (this.config.selectionExpands && this.objects.tree.style.display != 'none' && id != 0 &&
(already_selected || cat.childNodes[1].style.display == 'none'))
this.navigate(id);
}
// Select a link
treecats.prototype.selectLink = function (id) {
if (this.config.selectionMode != 'link')
return;
var link = document.getElementById(this._lid(id));
if (!link)
return;
var already_selected = this.currentSelection == id;
if (already_selected)
return;
// The link may be in multiple categories
var ext = '';
var count = 0;
var unselect;
while (unselect = document.getElementById(this._lid(this.currentSelection + ext))) {
unselect.className = unselect.className.replace(/ ?treecats-selected/, '');
count++;
ext = '-' + count;
}
this.currentSelection = id;
this._updateSelection(link.firstChild.firstChild.nodeValue, id);
ext = '';
count = 0;
var select;
while (select = document.getElementById(this._lid(id + ext))) {
select.className += ' treecats-selected';
count++;
ext = '-' + count;
}
}
// Expand or collapse a category.
// state: 0 = auto (this.AUTO), 1 = force expand (this.EXPAND), 2 = force collapse (this.COLLAPSE)
treecats.prototype.navigate = function (id, state) {
var cat = document.getElementById(this._id(id));
if (!cat)
return;
var me = this;
// Collapse categories on the same level as the current category
var collapseSiblings = function () {
if (me.config.browseMode != 'collapsed')
return;
var siblings = cat.parentNode.childNodes;
for (var i = 0; i < siblings.length; i++) {
if (siblings[i] == cat || !siblings[i].className.match(/treecats-category/))
continue;
if (siblings[i].childNodes[1].style.display != 'none')
me.navigate(me._getId(siblings[i]));
}
}
// Can't expand a category if it doesn't have children, but if browseMode is
// collapsed, then siblings may need to be collapsed
if (cat.firstChild.firstChild.tagName.toLowerCase() != 'a') {
collapseSiblings();
return;
}
// Collapse/Expand a category
var toggle = function (c) {
var collapse = c.childNodes[1].style.display == 'none';
if (state == me.EXPAND)
collapse = 1;
else if (state == me.COLLAPSE)
collapse = 0;
c.firstChild.firstChild.firstChild.src = me.config.imageURL + (collapse ? '/collapse.gif' : '/expand.gif');
c.firstChild.firstChild.firstChild.alt = collapse ? '[-]' : '[+]';
c.firstChild.firstChild.firstChild.title = collapse ? me.lang.collapse : me.lang.expand;
c.childNodes[1].style.display = collapse ? '' : 'none';
};
var toggleChildren = function () {
// Collapse any open categories on the same level
collapseSiblings();
toggle(cat);
};
var toggleLinks = function () {
if (me.config.selectionMode != 'link')
return;
var links = cat.childNodes[1].lastChild;
if (!links)
return;
// Hide the previous category's links if a different category has been selected
if (me.links && (me.links != links || links.className != 'treecats-links')) {
me.links.style.display = 'none';
// There are only links in this category, so the category needs to be collapsed
if (me.links.parentNode.childNodes.length == 1 && me.links.parentNode.style.display != 'none')
toggle(me.links.parentNode.parentNode);
}
if (links.className == 'treecats-links') {
me.links = links;
links.style.display = '';
}
else
me.links = null;
};
// The category has already been fetched
if (cat.childNodes[1].hasChildNodes() || state == this.COLLAPSE) {
// In link mode, only one category's links are shown at a time, so selecting a
// category that is already expanded shouldn't always collapse it.
// Also, in link mode, a category that has no links should collapse immediately.
if (this.config.selectionMode != 'link' || cat.childNodes[1].style.display == 'none' || state == this.COLLAPSE ||
(this.links && this.links == cat.childNodes[1].lastChild) ||
cat.childNodes[1].lastChild.className != 'treecats-links')
toggleChildren();
toggleLinks();
this._resize();
}
else {
cat.firstChild.firstChild.firstChild.src = this.config.imageURL + '/loading.gif';
cat.firstChild.firstChild.firstChild.title = this.lang.loading;
this._asyncReq(function (me, req) {
toggleChildren();
me._treeHandler(me, req);
toggleLinks();
}, this.config.cgiURL + '/treecats.cgi?id=' + id + (this.config.selectionMode == 'link' ? ';links=1' : '') + (this.config.cgiQueryString ? ';' + this.config.cgiQueryString : ''));
}
}
// Traverse the category tree.
// dir: 0 = just that one (this.SINGLE), 1 = down (this.DOWN), 2 = up (this.UP)
treecats.prototype._traverse = function (id, func, dir) {
// When selectionRequired is true, there is no category with an ID of 0
if (id == 0 && this.config.selectionRequired) {
if (dir == this.UP)
return;
for (var i = 0; i < this.objects.tree.childNodes.length; i++)
this._traverse(this._getId(this.objects.tree.childNodes[i]), func, dir);
return;
}
var cat = document.getElementById(this._id(id));
if (!cat)
return;
func(cat, id);
// Traverse down the tree
if (dir == 1) {
for (var i = 0; i < cat.lastChild.childNodes.length; i++) {
if (cat.lastChild.childNodes[i].className.match(/treecats-category/))
this._traverse(this._getId(cat.lastChild.childNodes[i]), func, dir);
}
}
// Traverse up the tree
else if (dir == 2) {
if (cat.parentNode.className.match(/treecats-children/))
this._traverse(this._getId(cat.parentNode.parentNode), func, dir);
}
}
// Expand categories from the category up to the root. Pass in true for
// inclusive if you wish to expand the category itself.
treecats.prototype.expandTo = function (id, inclusive) {
if (id == 0 || id == undefined)
return;
var start;
if (inclusive)
start = id;
else {
var cat = document.getElementById(this._id(id));
if (!cat || !cat.parentNode.className.match(/treecats-children/))
return;
start = this._getId(cat.parentNode.parentNode);
}
// Expand the categories from the category's parent and up
var me = this;
this._traverse(start, function (cat, id) { me.navigate(id, me.EXPAND); }, this.UP);
// Since we traverse up the tree, the side effect is that in link mode, the
// root node ends up having its links shown.
if (this.config.selectionMode == 'link')
this.navigate(id, this.EXPAND);
}
// Collapse all categories
treecats.prototype.collapseAll = function () {
var me = this;
this._traverse(0, function (cat, id) { me.navigate(id, me.COLLAPSE); }, this.DOWN);
}
// Add a new category selection item
treecats.prototype.add = function () {
if (this.config.selectionMode != 'multiple')
return;
var sid = this._createSelection();
// Automatically go into selection mode
this.toggle(sid);
}
// Remove a category selection item that has the supplied id
treecats.prototype.remove = function (id) {
// Don't allow the removal of the last item
if (this.objects.selectionList.childNodes.length <= 1)
return;
var selection = document.getElementById(this._sid(id));
if (!selection)
return;
this.objects.selectionList.removeChild(selection);
// Hide the Remove link
if (this.objects.selectionList.childNodes.length == 1)
this.objects.selectionList.firstChild.childNodes[3].style.display = 'none';
this._updateListAdd();
}
// Create a new category selection item. If no title is specified, then
// this.lang.noSelection[Link] will be used.
treecats.prototype._createSelection = function (title, value) {
if (!title)
title = this.config.selectionRequired ? (this.config.selectionMode != 'link' ? this.lang.noSelection : this.lang.noSelectionLink) : this.lang.rootText;
var selection = document.createElement('li');
// Find an unused selection item ID
var sid = 1;
while (document.getElementById(this._sid(sid)))
sid++;
selection.id = this._sid(sid);
var input = document.createElement('input');
input.type = 'hidden';
input.name = this.config.inputName;
input.value = value > 0 ? value : '';
selection.appendChild(input);
var span = document.createElement('span');
span.appendChild(document.createTextNode(title));
selection.appendChild(span);
var change = document.createElement('a');
change.href = 'javascript:' + this.config.objName + '.toggle(' + sid + ')';
change.className = 'treecats-selection-change';
change.appendChild(document.createTextNode(this.lang.change));
selection.appendChild(change);
var remove = document.createElement('a');
remove.href = 'javascript:' + this.config.objName + '.remove(' + sid + ')';
remove.className = 'treecats-selection-remove';
if (this.objects.selectionList.childNodes.length == 0)
remove.style.display = 'none';
remove.appendChild(document.createTextNode(this.lang.remove));
selection.appendChild(remove);
var done = document.createElement('a');
done.href = 'javascript:' + this.config.objName + '.toggle(' + sid + ')';
done.className = 'treecats-selection-done';
done.style.display = 'none';
done.appendChild(document.createTextNode(this.lang.done));
selection.appendChild(done);
var cancel = document.createElement('a');
cancel.href = 'javascript:' + this.config.objName + '.toggle(' + sid + ',1)';
cancel.className = 'treecats-selection-cancel';
cancel.style.display = 'none';
cancel.appendChild(document.createTextNode(this.lang.cancel));
selection.appendChild(cancel);
this.objects.selectionList.appendChild(selection);
// Show the Remove link on all selections
if (this.objects.selectionList.childNodes.length > 1) {
for (var i = 0; i < this.objects.selectionList.childNodes.length; i++)
this.objects.selectionList.childNodes[i].childNodes[3].style.display = '';
}
return sid;
}
// Update the current category selection. If no selectionItem is selected, a
// new selectionItem will be created.
treecats.prototype._updateSelection = function (title, value) {
if (!title)
title = this.config.selectionRequired ? (this.config.selectionMode != 'link' ? this.lang.noSelection : this.lang.noSelectionLink) : this.lang.rootText;
// When pre-selecting categories/links (this.currentSelectionItem == 0), the
// selection items need to be created.
if (this.currentSelectionItem > 0)
var selected = document.getElementById(this._sid(this.currentSelectionItem));
else
var selected = document.getElementById(this._sid(this._createSelection()));
selected.firstChild.value = value > 0 ? value : '';
selected.childNodes[1].replaceChild(document.createTextNode(title), selected.childNodes[1].firstChild);
}
treecats.prototype._treeHandler = function (me, req) {
var cats = req.responseXML.getElementsByTagName('category');
var father;
var children;
var error = req.responseXML.getElementsByTagName('error');
if (error.length) {
alert('treecats error: ' + error[0].firstChild.nodeValue);
return;
}
// Create a special root entry if needed
var root = document.getElementById(me._id(0));
if (!me.config.selectionRequired && !root) {
var xmldoc = cats[0].ownerDocument;
var root = xmldoc.createElement('category');
root.setAttribute('id', 0);
root.setAttribute('fatherid', -1);
root.setAttribute('children', -1);
root.setAttribute('selected', 0);
root.setAttribute('links', 0);
var name = xmldoc.createElement('name');
name.appendChild(xmldoc.createTextNode(me.lang.rootText));
root.appendChild(name);
var fname = xmldoc.createElement('fullname');
fname.appendChild(xmldoc.createTextNode(''));
root.appendChild(fname);
cats[0].parentNode.insertBefore(root, cats[0]);
// IE doesn't update the cats array like other browsers do, so refetch results
cats = cats[0].parentNode.getElementsByTagName('category');
}
for (var i = 0; i < cats.length; i++) {
var id = cats[i].getAttribute('id');
var fid = cats[i].getAttribute('fatherid');
var childcount = parseInt(cats[i].getAttribute('children')) || 0;
var linkcount = parseInt(cats[i].getAttribute('links')) || 0;
var selected = cats[i].getAttribute('selected') > 0 ? true : false;
var expandable = childcount;
if (me.config.selectionMode == 'link') {
expandable += linkcount;
}
if (document.getElementById(me._id(id)))
continue;
var cat = document.createElement('div');
cat.id = me._id(id);
cat.className = 'treecats-category';
var cat_info = document.createElement('div');
cat_info.className = 'treecats-category-info';
cat.appendChild(cat_info);
var img = document.createElement('img');
img.src = me.config.imageURL + (expandable > 0 ? '/expand.gif' : expandable < 0 ? '/root.gif' : '/blank.gif');
if (expandable > 0) {
img.alt = '[+]';
img.title = me.lang.expand;
var img_link = document.createElement('a');
img_link.href = 'javascript:' + me.config.objName + '.navigate(' + id + ')';
img_link.appendChild(img);
cat_info.appendChild(img_link);
}
else
cat_info.appendChild(img);
var cat_span = document.createElement('span');
cat_span.title = cats[i].getElementsByTagName('fullname')[0].firstChild.nodeValue;
cat_info.appendChild(cat_span);
var cat_name = document.createTextNode(cats[i].getElementsByTagName('name')[0].firstChild.nodeValue);
// If we're in link selectionMode, then only make the Category linkable if it
// has Links or sub-categories. Also navigate() instead of select().
if ((me.config.selectionMode == 'link' && expandable) || me.config.selectionMode != 'link') {
var cat_link = document.createElement('a');
cat_link.href = 'javascript:' + me.config.objName + (me.config.selectionMode != 'link' ? '.select' : '.navigate') + '(' + id + ')';
cat_link.appendChild(cat_name);
cat_span.appendChild(cat_link);
}
else
cat_span.appendChild(cat_name);
var cat_child = document.createElement('div');
cat_child.className = 'treecats-children';
if (id != 0)
cat_child.style.display = 'none';
cat.appendChild(cat_child);
if (!father || father.id != me._id(fid)) {
if ((fid == 0 && me.config.selectionRequired) || (id == 0 && !me.config.selectionRequired))
father = me.objects.tree;
else
father = document.getElementById(me._id(fid));
if (!father) {
alert('treecats error: Father id (' + fid + ') hasn\'t been created yet!');
continue;
}
if ((fid == 0 && me.config.selectionRequired) || (id == 0 && !me.config.selectionRequired))
children = father;
else
children = father.childNodes[1];
}
children.appendChild(cat);
if (selected && me.config.selectionMode != 'link')
me._updateSelection(cats[i].getElementsByTagName('fullname')[0].firstChild.nodeValue, id);
}
if (me.config.selectionMode == 'link') {
var links = req.responseXML.getElementsByTagName('link');
if (links.length) {
for (var i = 0; i < links.length; i++) {
var linkid = links[i].getAttribute('id');
var catid = links[i].getAttribute('catid');
var selected = links[i].getAttribute('selected') > 0 ? true : false;
var cat = document.getElementById(me._id(catid));
var cat_child = cat.lastChild;
var ul = cat_child.lastChild;
if (!ul || !ul.className.match(/treecats-links/)) {
ul = document.createElement('ul');
ul.className = 'treecats-links';
ul.style.display = 'none';
cat_child.appendChild(ul);
}
var li = document.createElement('li');
// Give links which are in multiple categories a different ID
var ext = '';
var count = 0;
var dupe;
while (dupe = document.getElementById(me._lid(linkid + ext))) {
// Select this link if it's already selected (from another category)
if (li.className != dupe.className)
li.className = dupe.className;
count++;
ext = '-' + count;
}
li.id = me._lid(linkid + ext);
var link = document.createElement('a');
link.href = 'javascript:' + me.config.objName + '.selectLink(' + linkid + ')';
link.title = links[i].getElementsByTagName('url')[0].firstChild.nodeValue;
link.appendChild(document.createTextNode(links[i].getElementsByTagName('name')[0].firstChild.nodeValue));
li.appendChild(link);
ul.appendChild(li);
// Check that the link hasn't already been added
if (selected && !me.linkToCategory[linkid])
me._updateSelection(links[i].getElementsByTagName('name')[0].firstChild.nodeValue, linkid);
// Save where the link is so we can expand to the category later on
me.linkToCategory[linkid] = catid;
}
}
}
me._updateListAdd();
me._resize();
}
// Update the visibility of the Add link
treecats.prototype._updateListAdd = function () {
if (this.config.selectionMode != 'multiple')
return;
if (this.config.multipleMax > 0 && this.objects.selectionList.childNodes.length >= this.config.multipleMax)
this.objects.selectionListAdd.style.display = 'none';
else
this.objects.selectionListAdd.style.display = (this.objects.tree.style.display == 'none' && this.objects.selectionList.lastChild.firstChild.value > 0) ? '' : 'none';
}
// Check to see if scrollbars need to be added or removed
treecats.prototype._resize = function () {
if (this.config.maxHeight > 0) {
if (this.objects.tree.scrollHeight > this.config.maxHeight) {
this.objects.tree.style.height = this.config.maxHeight + 'px';
this.objects.tree.style.overflow = 'scroll';
}
else {
this.objects.tree.style.height = '';
this.objects.tree.style.overflow = '';
}
}
}
// Get the ID from an object
treecats.prototype._getId = function (obj) {
return obj.id.replace(/^.*?(\d+)$/, '$1');
}
// Create a Category HTML ID
treecats.prototype._id = function (id) {
return this.config.objName + '-c' + id;
}
// Create a Link HTML ID
treecats.prototype._lid = function (id) {
return this.config.objName + '-l' + id;
}
// Create a Selection HTML ID
treecats.prototype._sid = function (id) {
return this.config.objName + '-s' + id;
}
// Perfom an asynchronous request
treecats.prototype._asyncReq = function (handler, url, post_data) {
var req;
if (window.XMLHttpRequest)
req = new XMLHttpRequest();
else if (window.ActiveXObject) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {}
}
}
if (!req) {
alert('treecats error: Your browser does not support XMLHttpRequest');
return;
}
var me = this;
req.onreadystatechange = function (e) {
if (req.readyState == 4) {
if (req.status == 200)
handler(me, req);
else
alert('treecats error: A ' + req.status + " error occured while retrieving the XML data (" + url + "):\n" + req.statusText);
}
};
try {
req.open(post_data ? "POST" : "GET", url, true);
}
catch (e) {
alert('treecats error: ' + e + ".\nPerhaps cgiURL does not match the current URL.");
return;
}
if (post_data)
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send(post_data);
}