1/* DOKUWIKI:include_once raphael.js */ 2/* DOKUWIKI:include_once jquery.qtip.js */ 3 4jQuery(function () { 5 console.log('---------------------'); 6 console.log('ADVRack Plugin v0.0.2'); 7 console.log('jQuery ' + jQuery().jquery); 8 console.log('---------------------'); 9 10 jQuery('div.advrack').each(function () { 11 console.log('ADVRack: parsing advrack stanza'); 12 drawRack(this); 13 }); 14}); 15 16// define a few global variables 17var drawing_wscale = 2.5, drawing_hscale = 1.5; 18var h_regexp = /^(\d{1,2})([u|in|cm])*/i, w_regexp = /^(\d{1,2})([in|cm])/i; 19 20Raphael.fn.roundedRectangle = function (x, y, w, h, r1, r2, r3, r4){ 21 var array = []; 22 array = array.concat(["M",x,r1+y, "Q",x,y, x+r1,y]); //A 23 array = array.concat(["L",x+w-r2,y, "Q",x+w,y, x+w,y+r2]); //B 24 array = array.concat(["L",x+w,y+h-r3, "Q",x+w,y+h, x+w-r3,y+h]); //C 25 array = array.concat(["L",x+r4,y+h, "Q",x,y+h, x,y+h-r4, "Z"]); //D 26 27 return this.path(array); 28}; 29 30function drawRack (el) { 31 var racks = jQuery.parseJSON(jQuery.text(el)); 32 el.innerHTML = ""; // hide syntax from page. 33 34 35 // define the Raphael paper object 36 var paper = Raphael(el,0,0); 37 paper.setViewBox(0,0,400,300,true); 38 paper.setSize('100%', '100%'); 39 40 // iterate over how many racks we have defined. 41 jQuery.each(racks, function(idx, rack) { 42 if (! rack['options']['height']) { 43 drawError({'paper': paper, msg: "No height property found at rack index: " + rack['options']['index']}); 44 } 45 46 var rack_height = rack['options']['height'].match(h_regexp); 47 var rack_width = rack['options']['width'].match(w_regexp); 48 49 // calculate total height 50 var servers_height = calcHeight(rack['servers']); 51 var cages_height = calcHeight(rack['cages']); 52 53 if ((servers_height['errors'].length > 0) || (cages_height['errors'].length > 0)) { 54 var errors = new Array(); 55 jQuery.each(servers_height['errors'], function(idx, server_err) { 56 errors.push({"msg": server_err['error']}); 57 }); 58 59 jQuery.each(cages_height['errors'], function(idx,cage_err) { 60 errors.push({"msg": cage_err['error']}); 61 }); 62 63 if (! jQuery.isEmptyObject(errors)) 64 drawError(errors); 65 } 66 67 if ((servers_height['height'] + cages_height['height']) <= rack_height[1] * 4.445) { 68 // draw the rack first: we're scaling 1:1 cm to pixel, we're also converting in to cm for the width and u to cm for the height 69 var raph_rack = paper.rect(6,6, rack_width[1] * 2.54 * drawing_wscale, rack_height[1] * 4.445 * drawing_hscale); 70 raph_rack.attr({"stroke-width": 4}); 71 72 // index the equipment from top to bottom 73 var equipment = new Array(Number(rack_height[1])); 74 e_length = equipment.length; 75 for (var i = 0; i < e_length; i++) { 76 equipment[i] = {'type': 'FILLER', 'index': 1+i, 'height': '1U'}; 77 } 78 79 jQuery.each(rack['servers'], function(idx, server) { 80 server['type'] = 'SERVER'; // stamp equipment as type: 'SERVER' 81 equipment = deleteFillerPanels(server, equipment); 82 }); 83 84 jQuery.each(rack['cages'], function(idx, cage) { 85 cage['type'] = 'CAGE'; // stamp equipment as type: 'CAGE' 86 equipment = deleteFillerPanels(cage, equipment); 87 }); 88 89 // draw each equipment 90 jQuery.each(equipment, function(idx, item) { 91 if (item) 92 drawEquipment(paper, raph_rack, item); 93 }); 94 } else { 95 drawError({'paper': paper, 'msg': 'Sum of servers and cages height is more than rack ' + rack['options']['index'] + '\'s U space!'}); 96 } 97 }); 98} 99 100function drawEquipment(paper, rack, equipment) { 101 if (equipment['type']) { 102 switch (equipment['type']) { 103 case "SERVER": 104 var boundaries = rack.getBBox(); 105 var h = equipment['height'].match(h_regexp); 106 var equipment_height = h[1] * 4.445 * drawing_hscale; 107 var server = paper.set(); 108 var server_block = paper.rect(boundaries.x, boundaries.y + ((equipment['index']-1) * 4.445 * drawing_hscale), boundaries.width, equipment_height, 2.0); 109 // set server attributes 110 var attributes = {'fill': 'white'}; 111 if (equipment['fill-color']) 112 attributes['fill'] = equipment['fill-color']; 113 server_block.attr(attributes); 114 115 // create cosmetic effects 116 setupGlowEffect(server_block); 117 118 // set server description 119 if (equipment['tooltip']) 120 setupTooltip(server_block, equipment['tooltip']); 121 122 // bundle stuff into one raphael object 123 server.push(server_block); 124 125 // set server label 126 if (equipment['label']) { 127 var boundaries = server_block.getBBox(); 128 var text = paper.text(boundaries.width/2+6, boundaries.y + boundaries.height/2, equipment['label']).attr({'font-size': 4, 'fill': 'black', 'text-anchor': 'middle'}); 129 server.push(text); 130 } 131 break; 132 case "CAGE": 133 var boundaries = rack.getBBox(); 134 var h = equipment['height'].match(h_regexp); 135 var equipment_height = h[1] * 4.445 * drawing_hscale; 136 var cage = paper.set(); 137 var cage_block = paper.rect(boundaries.x, boundaries.y + ((equipment['index']-1) * 4.445 * drawing_hscale), boundaries.width, equipment_height, 2.0); 138 // set cage attributes 139 var attributes = {'fill': 'white'}; 140 if (equipment['fill-color']) 141 attributes['fill'] = equipment['fill-color']; 142 cage_block.attr(attributes); 143 144 // set cage label: might be overwritten by modules 145 if (equipment['label']) { 146 var boundaries = cage_block.getBBox(); 147 var text = paper.text(boundaries.width/2+6, boundaries.y + boundaries.height/2, equipment['label']).attr({'font-size': 4, 'fill': 'black', 'text-anchor': 'middle'}); 148 cage.push(text); 149 } 150 151 // draw cage decks 152 if (equipment['decks']) { 153 var previous_deck_height = 0; 154 var dl = equipment['decks']; 155 for (var d = 0; d < dl.length; d++) { 156 var deck = equipment['decks'][d]; 157 var boundaries = cage_block.getBBox(); 158 if (! deck['height']) 159 drawError({'paper': paper, 'msg': 'Height not defined in one of cage ' + equipment['index'] + ' decks'}); 160 var h = deck['height'].match(h_regexp); 161 var deck_height = h[1] * 4.445 * drawing_hscale; 162 var raph_deck = paper.set(); 163 var deck_block, corner = [0,0,0,0]; 164 if (d == 0) { 165 deck_block = paper.roundedRectangle(boundaries.x, boundaries.y + previous_deck_height, boundaries.width, deck_height, 2,2,0,0); 166 // set cornors for later use within sub-modules 167 corner = [2,2,0,0]; 168 } else if (d < dl.length - 1) { 169 deck_block = paper.roundedRectangle(boundaries.x, boundaries.y + previous_deck_height, boundaries.width, deck_height, 0,0,0,0); 170 // set cornors for later use within sub-modules 171 corner = [0,0,0,0]; 172 } else { 173 deck_block = paper.roundedRectangle(boundaries.x, boundaries.y + previous_deck_height, boundaries.width, deck_height, 0,0,2,2); 174 // set cornors for later use within sub-modules 175 corner = [0,0,2,2]; 176 } 177 178 // fill the deck with white so mouse hover would have the correct effect 179 deck_block.attr('fill', 'white'); 180 181 // setup the deck's tooltip 182 if (deck['tooltip']) 183 setupTooltip(deck_block, deck['tooltip']); 184 185 // set the deck's label 186 if (deck['label']) { 187 var boundaries = deck_block.getBBox(); 188 var text = paper.text(boundaries.width/2+6, boundaries.y + boundaries.height/2, deck['label']).attr({'font-size': 4, 'fill': 'black', 'text-anchor': 'middle'}); 189 raph_deck.push(text); 190 } 191 192 // create cosmetic effects 193 setupGlowEffect(deck_block); 194 195 // draw blocks and blades 196 if (deck['type'] === 'block') { 197 if (deck['blocks']) 198 drawDeckModules(paper, boundaries, corner, previous_deck_height, deck_height, deck['blocks'], deck, equipment); 199 } else if (deck['type'] === 'blade') { 200 if (deck['blades']) 201 drawDeckModules(paper, boundaries, corner, previous_deck_height, deck_height, deck['blades'], deck, equipment); 202 } else { 203 drawError({'paper': paper, 'msg': 'Invalid equipment type! Decks can have type \'block\' or \'blade\' only.'}); 204 } 205 206 raph_deck.push(deck_block); 207 previous_deck_height += deck_height; 208 } 209 } 210 211 // create cosmetic effects 212 setupGlowEffect(cage_block); 213 214 cage.push(cage_block); 215 break; 216 case "FILLER": 217 var boundaries = rack.getBBox(); 218 var h = equipment['height'].match(h_regexp); 219 var equipment_height = h[1] * 4.445 * drawing_hscale; 220 var panel = paper.set(); 221 var filler = paper.rect(boundaries.x, boundaries.y + ((equipment['index']-1) * 4.445 * drawing_hscale), boundaries.width, equipment_height * 0.95).attr('fill','grey'); 222 boundaries = filler.getBBox(); 223 var text1 = paper.text(boundaries.x + boundaries.width - 6, boundaries.y + equipment_height/2, equipment['index']).attr({'text-anchor': 'start', 'font-size': 4, 'fill': '#ffffff'}); 224 var text2 = paper.text(boundaries.x + 2, boundaries.y + boundaries.height/2, 'filler panel').attr({'text-anchor': 'start', 'font-size': 4, 'fill': '#ffffff'}); 225 panel.push(filler, text1, text2); 226 break; 227 default: 228 drawError({'paper': paper, 'msg': 'Invalid equipment type! This is a possible bug in the ADVRack plugin.'}); 229 } 230 } else { 231 drawError({'paper': paper, 'msg': 'No equipment type specified!'}); 232 } 233} 234 235function calcHeight (items) { 236 var height = 0; 237 var errors = new Array(); 238 jQuery.each(items, function(idx, item) { 239 if (! item['height']) { 240 errors.push({"error": "No height property found in item index: " + item['index']}); 241 } else if (! h_regexp.test(item['height'])) { 242 errors.push({"error": "No height value specified or incorrect value. Item index: " + item['index']}); 243 } else { 244 var h = item['height'].match(h_regexp); 245 switch (h[2].toLowerCase()) { 246 case "in": 247 h[1] *= 2.54; // convert to Centimeters 248 break; 249 case "cm": 250 break; // cm is the default measurement, we use 1:1 cm to px scale. 251 case "u": 252 h[1] *= 4.445; // convert from rack units to centimeters 253 break; 254 default: 255 // we'll assume rack units if nothing is defined 256 h[1] *= 4.445; 257 } 258 height += Number(h[1]); 259 } 260 }); 261 return {"errors": errors, "height": height}; 262} 263 264function deleteFillerPanels(unit, equipment) { 265 var unit_height = unit['height'].match(h_regexp); 266 unit_height = Number(unit_height[1]); 267 if (unit_height > 1) { 268 for (var h = (unit['index']-1); h < (unit['index'] - 1 + unit_height); h++) { 269 delete equipment[h]; 270 } 271 equipment[unit['index']-1] = unit; 272 } else { 273 equipment[unit['index']-1] = unit; 274 } 275 return equipment; 276} 277 278function setupGlowEffect(unit) { 279 unit.hover(function(){ // hover in 280 this.glow_effect = this.glow({ width: 1, color: "blue" }); 281 this.glow_effect.toFront(); 282 }, function() { // hover out 283 this.glow_effect.remove(); 284 }); 285} 286 287function setupTooltip(raph_obj, tooltip) { 288 if (tooltip) 289 jQuery(raph_obj.node).qtip(tooltip); 290} 291 292function drawDeckModules(paper, rack_boundaries, corner, previous_deck_height, deck_height, modules, deck, equipment) { 293 // calculate module width 294 var module_width = (rack_boundaries.width)/(modules.length); 295 var previous_module_width = 0; 296 for (var b = 0; b < modules.length; b++) { 297 var module; 298 if (b == 0) { 299 module = paper.roundedRectangle(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height, corner[0],0,0,corner[3]); 300 // draw a thin border around the module; useful to define our module edges if it has a fill color. 301 paper.roundedRectangle(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height, corner[0],0,0,corner[3]).attr({'stroke-width': 0.2, 'stroke': 'black'}); 302 } else if (b < modules.length - 1) { 303 module = paper.rect(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height, 0,0,0,0); 304 // draw a thin border around the module; useful to define our module edges if it has a fill color. 305 paper.roundedRectangle(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height,0,0,0,0).attr({'stroke-width': 0.2, 'stroke': 'black'}); 306 } else { 307 module = paper.roundedRectangle(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height, 0,corner[1],corner[2],0); 308 // draw a thin border around the module; useful to define our module edges if it has a fill color. 309 paper.roundedRectangle(rack_boundaries.x + previous_module_width, rack_boundaries.y + previous_deck_height, module_width, deck_height,0,corner[1],corner[2],0).attr({'stroke-width': 0.2, 'stroke': 'black'}); 310 } 311 312 // a few cosmetic touches 313 if (modules[b]['fill-color']) { 314 module.attr('fill', modules[b]['fill-color']); 315 } else { 316 module.attr('fill', 'white'); 317 } 318 if (modules[b]['tooltip']) { 319 setupTooltip(module, modules[b]['tooltip']); 320 } else if (deck['tooltip']) { 321 setupTooltip(module, deck['tooltip']); 322 } else { 323 if (equipment['tooltip']) 324 setupTooltip(module, equipment['tooltip']); 325 } 326 327 // set the deck's label 328 if (modules[b]['label']) { 329 330 var boundaries = module.getBBox(); 331 var text = paper.text(boundaries.x + boundaries.width/2, boundaries.y + boundaries.height/2, modules[b]['label']).attr({'font-size': 4, 'fill': 'black', 'text-anchor': 'middle'}); 332 if (deck['type'] === 'blade') 333 text.transform('r-90'); 334 } 335 336 // add glow effect to modules 337 setupGlowEffect(module); 338 339 previous_module_width += module_width; 340 } 341} 342 343function drawError (error) { 344 // a function to show error messasge, instead of rack drawing 345 var paper = error.paper; 346 paper.clear(); 347 var msg = error.msg; 348 paper.text(10,10, 'ERROR: ' + msg).attr('text-anchor', 'start'); 349 exit(); 350} 351