| 1 |
joko |
1.1 |
// +-----------------------------------------------------------------------+ |
| 2 |
|
|
// | Copyright (c) 2002-2003, Richard Heyes, Harald Radi | |
| 3 |
|
|
// | All rights reserved. | |
| 4 |
|
|
// | | |
| 5 |
|
|
// | Redistribution and use in source and binary forms, with or without | |
| 6 |
|
|
// | modification, are permitted provided that the following conditions | |
| 7 |
|
|
// | are met: | |
| 8 |
|
|
// | | |
| 9 |
|
|
// | o Redistributions of source code must retain the above copyright | |
| 10 |
|
|
// | notice, this list of conditions and the following disclaimer. | |
| 11 |
|
|
// | o Redistributions in binary form must reproduce the above copyright | |
| 12 |
|
|
// | notice, this list of conditions and the following disclaimer in the | |
| 13 |
|
|
// | documentation and/or other materials provided with the distribution.| |
| 14 |
|
|
// | o The names of the authors may not be used to endorse or promote | |
| 15 |
|
|
// | products derived from this software without specific prior written | |
| 16 |
|
|
// | permission. | |
| 17 |
|
|
// | | |
| 18 |
|
|
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 |
|
|
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 |
|
|
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 |
|
|
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 |
|
|
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 |
|
|
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 |
|
|
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 |
|
|
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 |
|
|
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 |
|
|
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 |
|
|
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 |
|
|
// | | |
| 30 |
|
|
// +-----------------------------------------------------------------------+ |
| 31 |
|
|
// | Author: Richard Heyes <richard@phpguru.org> | |
| 32 |
|
|
// | Harald Radi <harald.radi@nme.at> | |
| 33 |
|
|
// +-----------------------------------------------------------------------+ |
| 34 |
|
|
// |
| 35 |
|
|
// Id: TreeMenu.js,v 1.13 2003/02/19 18:30:46 richard Exp |
| 36 |
|
|
// $Id: TreeMenu.js,v 1.13 2003/02/19 18:30:46 richard Exp $ |
| 37 |
|
|
|
| 38 |
|
|
|
| 39 |
|
|
/** |
| 40 |
|
|
* TreeMenu class |
| 41 |
|
|
*/ |
| 42 |
|
|
function TreeMenu(iconpath, myname, linkTarget, defaultClass, usePersistence, noTopLevelImages) |
| 43 |
|
|
{ |
| 44 |
|
|
// Properties |
| 45 |
|
|
this.iconpath = iconpath; |
| 46 |
|
|
this.myname = myname; |
| 47 |
|
|
this.linkTarget = linkTarget; |
| 48 |
|
|
this.defaultClass = defaultClass; |
| 49 |
|
|
this.usePersistence = usePersistence; |
| 50 |
|
|
this.noTopLevelImages = noTopLevelImages; |
| 51 |
|
|
this.n = new Array(); |
| 52 |
|
|
|
| 53 |
|
|
this.nodeRefs = new Array(); |
| 54 |
|
|
this.branches = new Array(); |
| 55 |
|
|
this.branchStatus = new Array(); |
| 56 |
|
|
this.layerRelations = new Array(); |
| 57 |
|
|
this.childParents = new Array(); |
| 58 |
|
|
this.cookieStatuses = new Array(); |
| 59 |
|
|
|
| 60 |
|
|
this.preloadImages(); |
| 61 |
|
|
} |
| 62 |
|
|
|
| 63 |
|
|
/** |
| 64 |
|
|
* Adds a node to the tree |
| 65 |
|
|
*/ |
| 66 |
|
|
TreeMenu.prototype.addItem = function (newNode) |
| 67 |
|
|
{ |
| 68 |
|
|
newIndex = this.n.length; |
| 69 |
|
|
this.n[newIndex] = newNode; |
| 70 |
|
|
|
| 71 |
|
|
return this.n[newIndex]; |
| 72 |
|
|
} |
| 73 |
|
|
|
| 74 |
|
|
/** |
| 75 |
|
|
* Preload images hack for Mozilla |
| 76 |
|
|
*/ |
| 77 |
|
|
TreeMenu.prototype.preloadImages = function () |
| 78 |
|
|
{ |
| 79 |
|
|
var plustop = new Image; plustop.src = this.iconpath + '/plustop.gif'; |
| 80 |
|
|
var plusbottom = new Image; plusbottom.src = this.iconpath + '/plusbottom.gif'; |
| 81 |
|
|
var plus = new Image; plus.src = this.iconpath + '/plus.gif'; |
| 82 |
|
|
|
| 83 |
|
|
var minustop = new Image; minustop.src = this.iconpath + '/minustop.gif'; |
| 84 |
|
|
var minusbottom = new Image; minusbottom.src = this.iconpath + '/minusbottom.gif'; |
| 85 |
|
|
var minus = new Image; minus.src = this.iconpath + '/minus.gif'; |
| 86 |
|
|
|
| 87 |
|
|
var branchtop = new Image; branchtop.src = this.iconpath + '/branchtop.gif'; |
| 88 |
|
|
var branchbottom = new Image; branchbottom.src = this.iconpath + '/branchbottom.gif'; |
| 89 |
|
|
var branch = new Image; branch.src = this.iconpath + '/branch.gif'; |
| 90 |
|
|
|
| 91 |
|
|
var linebottom = new Image; linebottom.src = this.iconpath + '/linebottom.gif'; |
| 92 |
|
|
var line = new Image; line.src = this.iconpath + '/line.gif'; |
| 93 |
|
|
} |
| 94 |
|
|
|
| 95 |
|
|
/** |
| 96 |
|
|
* Main function that draws the menu and assigns it |
| 97 |
|
|
* to the layer (or document.write()s it) |
| 98 |
|
|
*/ |
| 99 |
|
|
TreeMenu.prototype.drawMenu = function ()// OPTIONAL ARGS: nodes = [], level = [], prepend = '', expanded = false, visbility = 'inline', parentLayerID = null |
| 100 |
|
|
{ |
| 101 |
|
|
/** |
| 102 |
|
|
* Necessary variables |
| 103 |
|
|
*/ |
| 104 |
|
|
var output = ''; |
| 105 |
|
|
var modifier = ''; |
| 106 |
|
|
var layerID = ''; |
| 107 |
|
|
var parentLayerID = ''; |
| 108 |
|
|
|
| 109 |
|
|
/** |
| 110 |
|
|
* Parse any optional arguments |
| 111 |
|
|
*/ |
| 112 |
|
|
var nodes = arguments[0] ? arguments[0] : this.n |
| 113 |
|
|
var level = arguments[1] ? arguments[1] : []; |
| 114 |
|
|
var prepend = arguments[2] ? arguments[2] : ''; |
| 115 |
|
|
var expanded = arguments[3] ? arguments[3] : false; |
| 116 |
|
|
var visibility = arguments[4] ? arguments[4] : 'inline'; |
| 117 |
|
|
var parentLayerID = arguments[5] ? arguments[5] : null; |
| 118 |
|
|
|
| 119 |
|
|
var currentlevel = level.length; |
| 120 |
|
|
|
| 121 |
|
|
for (var i=0; i<nodes.length; i++) { |
| 122 |
|
|
|
| 123 |
|
|
level[currentlevel] = i+1; |
| 124 |
|
|
layerID = this.myname + '_' + 'node_' + this.implode('_', level); |
| 125 |
|
|
|
| 126 |
|
|
/** |
| 127 |
|
|
* Store this object in the nodeRefs array |
| 128 |
|
|
*/ |
| 129 |
|
|
this.nodeRefs[layerID] = nodes[i]; |
| 130 |
|
|
|
| 131 |
|
|
/** |
| 132 |
|
|
* Store the child/parent relationship |
| 133 |
|
|
*/ |
| 134 |
|
|
this.childParents[layerID] = parentLayerID; |
| 135 |
|
|
|
| 136 |
|
|
/** |
| 137 |
|
|
* Gif modifier |
| 138 |
|
|
*/ |
| 139 |
|
|
if (i == 0 && parentLayerID == null) { |
| 140 |
|
|
modifier = nodes.length > 1 ? "top" : 'single'; |
| 141 |
|
|
} else if(i == (nodes.length-1)) { |
| 142 |
|
|
modifier = "bottom"; |
| 143 |
|
|
} else { |
| 144 |
|
|
modifier = ""; |
| 145 |
|
|
} |
| 146 |
|
|
|
| 147 |
|
|
/** |
| 148 |
|
|
* Single root branch is always expanded |
| 149 |
|
|
*/ |
| 150 |
|
|
if (!this.doesMenu() || (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages))) { |
| 151 |
|
|
expanded = true; |
| 152 |
|
|
|
| 153 |
|
|
} else if (nodes[i].expanded) { |
| 154 |
|
|
expanded = true; |
| 155 |
|
|
|
| 156 |
|
|
} else { |
| 157 |
|
|
expanded = false; |
| 158 |
|
|
} |
| 159 |
|
|
|
| 160 |
|
|
/** |
| 161 |
|
|
* Make sure visibility is correct based on parent status |
| 162 |
|
|
*/ |
| 163 |
|
|
visibility = this.checkParentVisibility(layerID) ? visibility : 'none'; |
| 164 |
|
|
|
| 165 |
|
|
/** |
| 166 |
|
|
* Setup branch status and build an indexed array |
| 167 |
|
|
* of branch layer ids |
| 168 |
|
|
*/ |
| 169 |
|
|
if (nodes[i].n.length > 0) { |
| 170 |
|
|
this.branchStatus[layerID] = expanded; |
| 171 |
|
|
this.branches[this.branches.length] = layerID; |
| 172 |
|
|
} |
| 173 |
|
|
|
| 174 |
|
|
/** |
| 175 |
|
|
* Setup toggle relationship |
| 176 |
|
|
*/ |
| 177 |
|
|
if (!this.layerRelations[parentLayerID]) { |
| 178 |
|
|
this.layerRelations[parentLayerID] = new Array(); |
| 179 |
|
|
} |
| 180 |
|
|
this.layerRelations[parentLayerID][this.layerRelations[parentLayerID].length] = layerID; |
| 181 |
|
|
|
| 182 |
|
|
/** |
| 183 |
|
|
* Branch images |
| 184 |
|
|
*/ |
| 185 |
|
|
var gifname = nodes[i].n.length && this.doesMenu() && nodes[i].isDynamic ? (expanded ? 'minus' : 'plus') : 'branch'; |
| 186 |
|
|
var iconimg = nodes[i].icon ? this.stringFormat('<img src="{0}/{1}" width="20" height="20" align="top" id="icon_{2}">', this.iconpath, nodes[i].icon, layerID) : ''; |
| 187 |
|
|
|
| 188 |
|
|
/** |
| 189 |
|
|
* Add event handlers |
| 190 |
|
|
*/ |
| 191 |
|
|
var eventHandlers = ""; |
| 192 |
|
|
for (j in nodes[i].events) { |
| 193 |
|
|
eventHandlers += this.stringFormat('{0}="{1}" ', j, nodes[i].events[j]); |
| 194 |
|
|
} |
| 195 |
|
|
|
| 196 |
|
|
/** |
| 197 |
|
|
* Build the html to write to the document |
| 198 |
|
|
* IMPORTANT: |
| 199 |
|
|
* document.write()ing the string: '<div style="display:...' will screw up nn4.x |
| 200 |
|
|
*/ |
| 201 |
|
|
var layerTag = this.doesMenu() ? this.stringFormat('<div id="{0}" style="display: {1}" class="{2}">', layerID, visibility, (nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass)) : this.stringFormat('<div class="{0}">', nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass); |
| 202 |
|
|
var onMDown = this.doesMenu() && nodes[i].n.length && nodes[i].isDynamic ? this.stringFormat('onmousedown="{0}.toggleBranch(\'{1}\', true)" style="cursor: pointer; cursor: hand"', this.myname, layerID) : ''; |
| 203 |
|
|
var imgTag = this.stringFormat('<img src="{0}/{1}{2}.gif" width="20" height="20" align="top" border="0" name="img_{3}" {4}>', this.iconpath, gifname, modifier, layerID, onMDown); |
| 204 |
|
|
var linkTarget= nodes[i].linkTarget ? nodes[i].linkTarget : this.linkTarget; |
| 205 |
|
|
var linkStart = nodes[i].link ? this.stringFormat('<a href="{0}" target="{1}">', nodes[i].link, linkTarget) : ''; |
| 206 |
|
|
var linkEnd = nodes[i].link ? '</a>' : ''; |
| 207 |
|
|
|
| 208 |
|
|
output = this.stringFormat('{0}<nobr>{1}{2}{3}{4}<span {5}>{6}</span>{7}</nobr><br></div>', |
| 209 |
|
|
layerTag, |
| 210 |
|
|
prepend, |
| 211 |
|
|
parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages) ? '' : imgTag, |
| 212 |
|
|
iconimg, |
| 213 |
|
|
linkStart, |
| 214 |
|
|
eventHandlers, |
| 215 |
|
|
nodes[i].title, |
| 216 |
|
|
linkEnd); |
| 217 |
|
|
|
| 218 |
|
|
/** |
| 219 |
|
|
* Write out the HTML. Uses document.write for speed over layers and |
| 220 |
|
|
* innerHTML. This however means no dynamic adding/removing nodes on |
| 221 |
|
|
* the client side. This could be conditional I guess if dynamic |
| 222 |
|
|
* adding/removing is required. |
| 223 |
|
|
*/ |
| 224 |
|
|
document.write(output + "\r\n"); |
| 225 |
|
|
|
| 226 |
|
|
/** |
| 227 |
|
|
* Traverse sub nodes ? |
| 228 |
|
|
*/ |
| 229 |
|
|
if (nodes[i].n.length) { |
| 230 |
|
|
/** |
| 231 |
|
|
* Determine what to prepend. If there is only one root |
| 232 |
|
|
* node then the prepend to pass to children is nothing. |
| 233 |
|
|
* Otherwise it depends on where we are in the tree. |
| 234 |
|
|
*/ |
| 235 |
|
|
if (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages)) { |
| 236 |
|
|
var newPrepend = ''; |
| 237 |
|
|
|
| 238 |
|
|
} else if (i < (nodes.length - 1)) { |
| 239 |
|
|
var newPrepend = prepend + this.stringFormat('<img src="{0}/line.gif" width="20" height="20" align="top">', this.iconpath); |
| 240 |
|
|
|
| 241 |
|
|
} else { |
| 242 |
|
|
var newPrepend = prepend + this.stringFormat('<img src="{0}/linebottom.gif" width="20" height="20" align="top">', this.iconpath); |
| 243 |
|
|
} |
| 244 |
|
|
|
| 245 |
|
|
this.drawMenu(nodes[i].n, |
| 246 |
|
|
level, |
| 247 |
|
|
newPrepend, |
| 248 |
|
|
nodes[i].expanded, |
| 249 |
|
|
expanded ? 'inline' : 'none', |
| 250 |
|
|
layerID); |
| 251 |
|
|
} |
| 252 |
|
|
} |
| 253 |
|
|
} |
| 254 |
|
|
|
| 255 |
|
|
/** |
| 256 |
|
|
* Toggles a branches visible status. Called from resetBranches() |
| 257 |
|
|
* and also when a +/- graphic is clicked. |
| 258 |
|
|
*/ |
| 259 |
|
|
TreeMenu.prototype.toggleBranch = function (layerID, updateStatus) // OPTIONAL ARGS: fireEvents = true |
| 260 |
|
|
{ |
| 261 |
|
|
var currentDisplay = this.getLayer(layerID).style.display; |
| 262 |
|
|
var newDisplay = (this.branchStatus[layerID] && currentDisplay == 'inline') ? 'none' : 'inline'; |
| 263 |
|
|
var fireEvents = arguments[2] != null ? arguments[2] : true; |
| 264 |
|
|
|
| 265 |
|
|
for (var i=0; i<this.layerRelations[layerID].length; i++) { |
| 266 |
|
|
|
| 267 |
|
|
if (this.branchStatus[this.layerRelations[layerID][i]]) { |
| 268 |
|
|
this.toggleBranch(this.layerRelations[layerID][i], false); |
| 269 |
|
|
} |
| 270 |
|
|
|
| 271 |
|
|
this.getLayer(this.layerRelations[layerID][i]).style.display = newDisplay; |
| 272 |
|
|
} |
| 273 |
|
|
|
| 274 |
|
|
if (updateStatus) { |
| 275 |
|
|
this.branchStatus[layerID] = !this.branchStatus[layerID]; |
| 276 |
|
|
|
| 277 |
|
|
/** |
| 278 |
|
|
* Persistence |
| 279 |
|
|
*/ |
| 280 |
|
|
if (this.doesPersistence() && !arguments[2] && this.usePersistence) { |
| 281 |
|
|
this.setExpandedStatusForCookie(layerID, this.branchStatus[layerID]); |
| 282 |
|
|
} |
| 283 |
|
|
|
| 284 |
|
|
/** |
| 285 |
|
|
* Fire custom events |
| 286 |
|
|
*/ |
| 287 |
|
|
if (fireEvents) { |
| 288 |
|
|
nodeObject = this.nodeRefs[layerID]; |
| 289 |
|
|
|
| 290 |
|
|
if (nodeObject.ontoggle != null) { |
| 291 |
|
|
eval(nodeObject.ontoggle); |
| 292 |
|
|
} |
| 293 |
|
|
|
| 294 |
|
|
if (newDisplay == 'none' && nodeObject.oncollapse != null) { |
| 295 |
|
|
eval(nodeObject.oncollapse); |
| 296 |
|
|
} else if (newDisplay == 'inline' && nodeObject.onexpand != null){ |
| 297 |
|
|
eval(nodeObject.onexpand); |
| 298 |
|
|
} |
| 299 |
|
|
} |
| 300 |
|
|
|
| 301 |
|
|
// Swap image |
| 302 |
|
|
this.swapImage(layerID); |
| 303 |
|
|
} |
| 304 |
|
|
|
| 305 |
|
|
// Swap icon |
| 306 |
|
|
this.swapIcon(layerID); |
| 307 |
|
|
} |
| 308 |
|
|
|
| 309 |
|
|
/** |
| 310 |
|
|
* Swaps the plus/minus branch images |
| 311 |
|
|
*/ |
| 312 |
|
|
TreeMenu.prototype.swapImage = function (layerID) |
| 313 |
|
|
{ |
| 314 |
|
|
var imgSrc = document.images['img_' + layerID].src; |
| 315 |
|
|
|
| 316 |
|
|
var re = /^(.*)(plus|minus)(bottom|top|single)?.gif$/ |
| 317 |
|
|
if (matches = imgSrc.match(re)) { |
| 318 |
|
|
|
| 319 |
|
|
document.images['img_' + layerID].src = this.stringFormat('{0}{1}{2}{3}', |
| 320 |
|
|
matches[1], |
| 321 |
|
|
matches[2] == 'plus' ? 'minus' : 'plus', |
| 322 |
|
|
matches[3] ? matches[3] : '', |
| 323 |
|
|
'.gif'); |
| 324 |
|
|
} |
| 325 |
|
|
} |
| 326 |
|
|
|
| 327 |
|
|
/** |
| 328 |
|
|
* Swaps the icon for the expanded icon if one |
| 329 |
|
|
* has been supplied. |
| 330 |
|
|
*/ |
| 331 |
|
|
TreeMenu.prototype.swapIcon = function (layerID) |
| 332 |
|
|
{ |
| 333 |
|
|
if (document.images['icon_' + layerID]) { |
| 334 |
|
|
var imgSrc = document.images['icon_' + layerID].src; |
| 335 |
|
|
|
| 336 |
|
|
if (this.nodeRefs[layerID].icon && this.nodeRefs[layerID].expandedIcon) { |
| 337 |
|
|
//alert(imgSrc.indexOf(this.nodeRefs[layerID].expandedIcon)); |
| 338 |
|
|
var newSrc = (imgSrc.indexOf(this.nodeRefs[layerID].expandedIcon) == -1 ? this.nodeRefs[layerID].expandedIcon : this.nodeRefs[layerID].icon); |
| 339 |
|
|
|
| 340 |
|
|
document.images['icon_' + layerID].src = this.iconpath + '/' + newSrc; |
| 341 |
|
|
} |
| 342 |
|
|
} |
| 343 |
|
|
} |
| 344 |
|
|
|
| 345 |
|
|
/** |
| 346 |
|
|
* Can the browser handle the dynamic menu? |
| 347 |
|
|
*/ |
| 348 |
|
|
TreeMenu.prototype.doesMenu = function () |
| 349 |
|
|
{ |
| 350 |
|
|
return (is_ie4up || is_nav6up || is_gecko); |
| 351 |
|
|
} |
| 352 |
|
|
|
| 353 |
|
|
/** |
| 354 |
|
|
* Can the browser handle save the branch status |
| 355 |
|
|
*/ |
| 356 |
|
|
TreeMenu.prototype.doesPersistence = function () |
| 357 |
|
|
{ |
| 358 |
|
|
return (is_ie4up || is_gecko || is_nav6up); |
| 359 |
|
|
} |
| 360 |
|
|
|
| 361 |
|
|
/** |
| 362 |
|
|
* Returns the appropriate layer accessor |
| 363 |
|
|
*/ |
| 364 |
|
|
TreeMenu.prototype.getLayer = function (layerID) |
| 365 |
|
|
{ |
| 366 |
|
|
if (is_ie4) { |
| 367 |
|
|
return document.all(layerID); |
| 368 |
|
|
|
| 369 |
|
|
} else if (document.getElementById(layerID)) { |
| 370 |
|
|
return document.getElementById(layerID); |
| 371 |
|
|
|
| 372 |
|
|
} else if (document.all(layerID)) { |
| 373 |
|
|
return document.all(layerID); |
| 374 |
|
|
} |
| 375 |
|
|
} |
| 376 |
|
|
|
| 377 |
|
|
/** |
| 378 |
|
|
* Save the status of the layer |
| 379 |
|
|
*/ |
| 380 |
|
|
TreeMenu.prototype.setExpandedStatusForCookie = function (layerID, expanded) |
| 381 |
|
|
{ |
| 382 |
|
|
this.cookieStatuses[layerID] = expanded; |
| 383 |
|
|
this.saveCookie(); |
| 384 |
|
|
} |
| 385 |
|
|
|
| 386 |
|
|
/** |
| 387 |
|
|
* Load the status of the layer |
| 388 |
|
|
*/ |
| 389 |
|
|
TreeMenu.prototype.getExpandedStatusFromCookie = function (layerID) |
| 390 |
|
|
{ |
| 391 |
|
|
if (this.cookieStatuses[layerID]) { |
| 392 |
|
|
return this.cookieStatuses[layerID]; |
| 393 |
|
|
} |
| 394 |
|
|
|
| 395 |
|
|
return false; |
| 396 |
|
|
} |
| 397 |
|
|
|
| 398 |
|
|
/** |
| 399 |
|
|
* Saves the cookie that holds which branches are expanded. |
| 400 |
|
|
* Only saves the details of the branches which are expanded. |
| 401 |
|
|
*/ |
| 402 |
|
|
TreeMenu.prototype.saveCookie = function () |
| 403 |
|
|
{ |
| 404 |
|
|
var cookieString = new Array(); |
| 405 |
|
|
|
| 406 |
|
|
for (var i in this.cookieStatuses) { |
| 407 |
|
|
if (this.cookieStatuses[i] == true) { |
| 408 |
|
|
cookieString[cookieString.length] = i; |
| 409 |
|
|
} |
| 410 |
|
|
} |
| 411 |
|
|
|
| 412 |
|
|
document.cookie = 'TreeMenuBranchStatus=' + cookieString.join(':'); |
| 413 |
|
|
} |
| 414 |
|
|
|
| 415 |
|
|
/** |
| 416 |
|
|
* Reads cookie parses it for status info and |
| 417 |
|
|
* stores that info in the class member. |
| 418 |
|
|
*/ |
| 419 |
|
|
TreeMenu.prototype.loadCookie = function () |
| 420 |
|
|
{ |
| 421 |
|
|
var cookie = document.cookie.split('; '); |
| 422 |
|
|
|
| 423 |
|
|
for (var i=0; i < cookie.length; i++) { |
| 424 |
|
|
var crumb = cookie[i].split('='); |
| 425 |
|
|
if ('TreeMenuBranchStatus' == crumb[0] && crumb[1]) { |
| 426 |
|
|
var expandedBranches = crumb[1].split(':'); |
| 427 |
|
|
for (var j=0; j<expandedBranches.length; j++) { |
| 428 |
|
|
this.cookieStatuses[expandedBranches[j]] = true; |
| 429 |
|
|
} |
| 430 |
|
|
} |
| 431 |
|
|
} |
| 432 |
|
|
} |
| 433 |
|
|
|
| 434 |
|
|
/** |
| 435 |
|
|
* Reset branch status |
| 436 |
|
|
*/ |
| 437 |
|
|
TreeMenu.prototype.resetBranches = function () |
| 438 |
|
|
{ |
| 439 |
|
|
if (!this.doesPersistence()) { |
| 440 |
|
|
return false; |
| 441 |
|
|
} |
| 442 |
|
|
|
| 443 |
|
|
this.loadCookie(); |
| 444 |
|
|
|
| 445 |
|
|
for (var i=0; i<this.branches.length; i++) { |
| 446 |
|
|
var status = this.getExpandedStatusFromCookie(this.branches[i]); |
| 447 |
|
|
// Only update if it's supposed to be expanded and it's not already |
| 448 |
|
|
if (status == true && this.branchStatus[this.branches[i]] != true) { |
| 449 |
|
|
if (this.checkParentVisibility(this.branches[i])) { |
| 450 |
|
|
this.toggleBranch(this.branches[i], true, false); |
| 451 |
|
|
} else { |
| 452 |
|
|
this.branchStatus[this.branches[i]] = true; |
| 453 |
|
|
this.swapImage(this.branches[i]); |
| 454 |
|
|
} |
| 455 |
|
|
} |
| 456 |
|
|
} |
| 457 |
|
|
} |
| 458 |
|
|
|
| 459 |
|
|
/** |
| 460 |
|
|
* Checks whether a branch should be open |
| 461 |
|
|
* or not based on its parents' status |
| 462 |
|
|
*/ |
| 463 |
|
|
TreeMenu.prototype.checkParentVisibility = function (layerID) |
| 464 |
|
|
{ |
| 465 |
|
|
if (this.in_array(this.childParents[layerID], this.branches) |
| 466 |
|
|
&& this.branchStatus[this.childParents[layerID]] |
| 467 |
|
|
&& this.checkParentVisibility(this.childParents[layerID]) ) { |
| 468 |
|
|
|
| 469 |
|
|
return true; |
| 470 |
|
|
|
| 471 |
|
|
} else if (this.childParents[layerID] == null) { |
| 472 |
|
|
return true; |
| 473 |
|
|
} |
| 474 |
|
|
|
| 475 |
|
|
return false; |
| 476 |
|
|
} |
| 477 |
|
|
|
| 478 |
|
|
/** |
| 479 |
|
|
* New C# style string formatter |
| 480 |
|
|
*/ |
| 481 |
|
|
TreeMenu.prototype.stringFormat = function (strInput) |
| 482 |
|
|
{ |
| 483 |
|
|
var idx = 0; |
| 484 |
|
|
|
| 485 |
|
|
for (var i=1; i<arguments.length; i++) { |
| 486 |
|
|
while ((idx = strInput.indexOf('{' + (i - 1) + '}', idx)) != -1) { |
| 487 |
|
|
strInput = strInput.substring(0, idx) + arguments[i] + strInput.substr(idx + 3); |
| 488 |
|
|
} |
| 489 |
|
|
} |
| 490 |
|
|
|
| 491 |
|
|
return strInput; |
| 492 |
|
|
} |
| 493 |
|
|
|
| 494 |
|
|
/** |
| 495 |
|
|
* Also much adored, the PHP implode() function |
| 496 |
|
|
*/ |
| 497 |
|
|
TreeMenu.prototype.implode = function (seperator, input) |
| 498 |
|
|
{ |
| 499 |
|
|
var output = ''; |
| 500 |
|
|
|
| 501 |
|
|
for (var i=0; i<input.length; i++) { |
| 502 |
|
|
if (i == 0) { |
| 503 |
|
|
output += input[i]; |
| 504 |
|
|
} else { |
| 505 |
|
|
output += seperator + input[i]; |
| 506 |
|
|
} |
| 507 |
|
|
} |
| 508 |
|
|
|
| 509 |
|
|
return output; |
| 510 |
|
|
} |
| 511 |
|
|
|
| 512 |
|
|
/** |
| 513 |
|
|
* Aah, all the old favourites are coming out... |
| 514 |
|
|
*/ |
| 515 |
|
|
TreeMenu.prototype.in_array = function (item, arr) |
| 516 |
|
|
{ |
| 517 |
|
|
for (var i=0; i<arr.length; i++) { |
| 518 |
|
|
if (arr[i] == item) { |
| 519 |
|
|
return true; |
| 520 |
|
|
} |
| 521 |
|
|
} |
| 522 |
|
|
|
| 523 |
|
|
return false; |
| 524 |
|
|
} |
| 525 |
|
|
|
| 526 |
|
|
/** |
| 527 |
|
|
* TreeNode Class |
| 528 |
|
|
*/ |
| 529 |
|
|
function TreeNode(title, icon, link, expanded, isDynamic, cssClass, linkTarget, expandedIcon) |
| 530 |
|
|
{ |
| 531 |
|
|
this.title = title; |
| 532 |
|
|
this.icon = icon; |
| 533 |
|
|
this.expandedIcon = expandedIcon; |
| 534 |
|
|
this.link = link; |
| 535 |
|
|
this.expanded = expanded; |
| 536 |
|
|
this.isDynamic = isDynamic; |
| 537 |
|
|
this.cssClass = cssClass; |
| 538 |
|
|
this.linkTarget = linkTarget; |
| 539 |
|
|
this.n = new Array(); |
| 540 |
|
|
this.events = new Array(); |
| 541 |
|
|
this.handlers = null; |
| 542 |
|
|
this.oncollapse = null; |
| 543 |
|
|
this.onexpand = null; |
| 544 |
|
|
this.ontoggle = null; |
| 545 |
|
|
} |
| 546 |
|
|
|
| 547 |
|
|
/** |
| 548 |
|
|
* Adds a node to an already existing node |
| 549 |
|
|
*/ |
| 550 |
|
|
TreeNode.prototype.addItem = function (newNode) |
| 551 |
|
|
{ |
| 552 |
|
|
newIndex = this.n.length; |
| 553 |
|
|
this.n[newIndex] = newNode; |
| 554 |
|
|
|
| 555 |
|
|
return this.n[newIndex]; |
| 556 |
|
|
} |
| 557 |
|
|
|
| 558 |
|
|
/** |
| 559 |
|
|
* Sets an event for this particular node |
| 560 |
|
|
*/ |
| 561 |
|
|
TreeNode.prototype.setEvent = function (eventName, eventHandler) |
| 562 |
|
|
{ |
| 563 |
|
|
switch (eventName.toLowerCase()) { |
| 564 |
|
|
case 'onexpand': |
| 565 |
|
|
this.onexpand = eventHandler; |
| 566 |
|
|
break; |
| 567 |
|
|
|
| 568 |
|
|
case 'oncollapse': |
| 569 |
|
|
this.oncollapse = eventHandler; |
| 570 |
|
|
break; |
| 571 |
|
|
|
| 572 |
|
|
case 'ontoggle': |
| 573 |
|
|
this.ontoggle = eventHandler; |
| 574 |
|
|
break; |
| 575 |
|
|
|
| 576 |
|
|
default: |
| 577 |
|
|
this.events[eventName] = eventHandler; |
| 578 |
|
|
} |
| 579 |
|
|
} |
| 580 |
|
|
|
| 581 |
|
|
/** |
| 582 |
|
|
* That's the end of the tree classes. What follows is |
| 583 |
|
|
* the browser detection code. |
| 584 |
|
|
*/ |
| 585 |
|
|
|
| 586 |
|
|
|
| 587 |
|
|
//<!-- |
| 588 |
|
|
// Ultimate client-side JavaScript client sniff. Version 3.03 |
| 589 |
|
|
// (C) Netscape Communications 1999-2001. Permission granted to reuse and distribute. |
| 590 |
|
|
// Revised 17 May 99 to add is_nav5up and is_ie5up (see below). |
| 591 |
|
|
// Revised 20 Dec 00 to add is_gecko and change is_nav5up to is_nav6up |
| 592 |
|
|
// also added support for IE5.5 Opera4&5 HotJava3 AOLTV |
| 593 |
|
|
// Revised 22 Feb 01 to correct Javascript Detection for IE 5.x, Opera 4, |
| 594 |
|
|
// correct Opera 5 detection |
| 595 |
|
|
// add support for winME and win2k |
| 596 |
|
|
// synch with browser-type-oo.js |
| 597 |
|
|
// Revised 26 Mar 01 to correct Opera detection |
| 598 |
|
|
// Revised 02 Oct 01 to add IE6 detection |
| 599 |
|
|
|
| 600 |
|
|
// Everything you always wanted to know about your JavaScript client |
| 601 |
|
|
// but were afraid to ask. Creates "is_" variables indicating: |
| 602 |
|
|
// (1) browser vendor: |
| 603 |
|
|
// is_nav, is_ie, is_opera, is_hotjava, is_webtv, is_TVNavigator, is_AOLTV |
| 604 |
|
|
// (2) browser version number: |
| 605 |
|
|
// is_major (integer indicating major version number: 2, 3, 4 ...) |
| 606 |
|
|
// is_minor (float indicating full version number: 2.02, 3.01, 4.04 ...) |
| 607 |
|
|
// (3) browser vendor AND major version number |
| 608 |
|
|
// is_nav2, is_nav3, is_nav4, is_nav4up, is_nav6, is_nav6up, is_gecko, is_ie3, |
| 609 |
|
|
// is_ie4, is_ie4up, is_ie5, is_ie5up, is_ie5_5, is_ie5_5up, is_ie6, is_ie6up, is_hotjava3, is_hotjava3up, |
| 610 |
|
|
// is_opera2, is_opera3, is_opera4, is_opera5, is_opera5up |
| 611 |
|
|
// (4) JavaScript version number: |
| 612 |
|
|
// is_js (float indicating full JavaScript version number: 1, 1.1, 1.2 ...) |
| 613 |
|
|
// (5) OS platform and version: |
| 614 |
|
|
// is_win, is_win16, is_win32, is_win31, is_win95, is_winnt, is_win98, is_winme, is_win2k |
| 615 |
|
|
// is_os2 |
| 616 |
|
|
// is_mac, is_mac68k, is_macppc |
| 617 |
|
|
// is_unix |
| 618 |
|
|
// is_sun, is_sun4, is_sun5, is_suni86 |
| 619 |
|
|
// is_irix, is_irix5, is_irix6 |
| 620 |
|
|
// is_hpux, is_hpux9, is_hpux10 |
| 621 |
|
|
// is_aix, is_aix1, is_aix2, is_aix3, is_aix4 |
| 622 |
|
|
// is_linux, is_sco, is_unixware, is_mpras, is_reliant |
| 623 |
|
|
// is_dec, is_sinix, is_freebsd, is_bsd |
| 624 |
|
|
// is_vms |
| 625 |
|
|
// |
| 626 |
|
|
// See http://www.it97.de/JavaScript/JS_tutorial/bstat/navobj.html and |
| 627 |
|
|
// http://www.it97.de/JavaScript/JS_tutorial/bstat/Browseraol.html |
| 628 |
|
|
// for detailed lists of userAgent strings. |
| 629 |
|
|
// |
| 630 |
|
|
// Note: you don't want your Nav4 or IE4 code to "turn off" or |
| 631 |
|
|
// stop working when new versions of browsers are released, so |
| 632 |
|
|
// in conditional code forks, use is_ie5up ("IE 5.0 or greater") |
| 633 |
|
|
// is_opera5up ("Opera 5.0 or greater") instead of is_ie5 or is_opera5 |
| 634 |
|
|
// to check version in code which you want to work on future |
| 635 |
|
|
// versions. |
| 636 |
|
|
|
| 637 |
|
|
/** |
| 638 |
|
|
* Severly curtailed all this as only certain elements |
| 639 |
|
|
* are required by TreeMenu, specifically: |
| 640 |
|
|
* o is_ie4up |
| 641 |
|
|
* o is_nav6up |
| 642 |
|
|
* o is_gecko |
| 643 |
|
|
*/ |
| 644 |
|
|
|
| 645 |
|
|
// convert all characters to lowercase to simplify testing |
| 646 |
|
|
var agt=navigator.userAgent.toLowerCase(); |
| 647 |
|
|
|
| 648 |
|
|
// *** BROWSER VERSION *** |
| 649 |
|
|
// Note: On IE5, these return 4, so use is_ie5up to detect IE5. |
| 650 |
|
|
var is_major = parseInt(navigator.appVersion); |
| 651 |
|
|
var is_minor = parseFloat(navigator.appVersion); |
| 652 |
|
|
|
| 653 |
|
|
// Note: Opera and WebTV spoof Navigator. We do strict client detection. |
| 654 |
|
|
// If you want to allow spoofing, take out the tests for opera and webtv. |
| 655 |
|
|
var is_nav = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1) |
| 656 |
|
|
&& (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1) |
| 657 |
|
|
&& (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1)); |
| 658 |
|
|
var is_nav6up = (is_nav && (is_major >= 5)); |
| 659 |
|
|
var is_gecko = (agt.indexOf('gecko') != -1); |
| 660 |
|
|
|
| 661 |
|
|
|
| 662 |
|
|
var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)); |
| 663 |
|
|
var is_ie4 = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) ); |
| 664 |
|
|
var is_ie4up = (is_ie && (is_major >= 4)); |
| 665 |
|
|
//--> end hide JavaScript |