/* * This file is part of Canviz. See http://www.canviz.org/ * $Id: canviz.js 265 2009-05-19 13:35:13Z ryandesign.com $ */ var CanvizTokenizer = Class.create({ initialize: function(str) { this.str = str; }, takeChars: function(num) { if (!num) { num = 1; } var tokens = new Array(); while (num--) { var matches = this.str.match(/^(\S+)\s*/); if (matches) { this.str = this.str.substr(matches[0].length); tokens.push(matches[1]); } else { tokens.push(false); } } if (1 == tokens.length) { return tokens[0]; } else { return tokens; } }, takeNumber: function(num) { if (!num) { num = 1; } if (1 == num) { return Number(this.takeChars()); } else { var tokens = this.takeChars(num); while (num--) { tokens[num] = Number(tokens[num]); } return tokens; } }, takeString: function() { var byteCount = Number(this.takeChars()), charCount = 0, charCode; if ('-' != this.str.charAt(0)) { return false; } while (0 < byteCount) { ++charCount; charCode = this.str.charCodeAt(charCount); if (0x80 > charCode) { --byteCount; } else if (0x800 > charCode) { byteCount -= 2; } else { byteCount -= 3; } } var str = this.str.substr(1, charCount); this.str = this.str.substr(1 + charCount).replace(/^\s+/, ''); return str; } }); var CanvizEntity = Class.create({ initialize: function(defaultAttrHashName, name, canviz, rootGraph, parentGraph, immediateGraph) { this.defaultAttrHashName = defaultAttrHashName; this.name = name; this.canviz = canviz; this.rootGraph = rootGraph; this.parentGraph = parentGraph; this.immediateGraph = immediateGraph; this.attrs = $H(); this.drawAttrs = $H(); }, initBB: function() { var matches = this.getAttr('pos').match(/([0-9.]+),([0-9.]+)/); var x = Math.round(matches[1]); var y = Math.round(this.canviz.height - matches[2]); this.bbRect = new Rect(x, y, x, y); }, getAttr: function(attrName, escString) { if (Object.isUndefined(escString)) escString = false; var attrValue = this.attrs.get(attrName); if (Object.isUndefined(attrValue)) { var graph = this.parentGraph; while (!Object.isUndefined(graph)) { attrValue = graph[this.defaultAttrHashName].get(attrName); if (Object.isUndefined(attrValue)) { graph = graph.parentGraph; } else { break; } } } if (attrValue && escString) { attrValue = attrValue.replace(this.escStringMatchRe, function(match, p1) { switch (p1) { case 'N': // fall through case 'E': return this.name; case 'T': return this.tailNode; case 'H': return this.headNode; case 'G': return this.immediateGraph.name; case 'L': return this.getAttr('label', true); } return match; }.bind(this)); } return attrValue; }, draw: function(ctx, ctxScale, redrawCanvasOnly) { var i, tokens, fillColor, strokeColor, label; var urls, href_index, href_count; if (!redrawCanvasOnly) { this.initBB(); var bbDiv = new Element('div'); this.canviz.elements.appendChild(bbDiv); } label = this.getAttr('label', true); if ( label && label.match(']*>.*') ) { var h; // debug('Got a table'); urls = new Array(); var hrefl = label.split('href="'); for(h=1; h this.versionCompare(this.maxXdotVersion, attrHash.get('xdotversion'))) { debug('unsupported xdotversion ' + attrHash.get('xdotversion') + '; this script currently supports up to xdotversion ' + this.maxXdotVersion); } break; } } } else { debug('can\'t read attributes for entity ' + entityName + ' from ' + attrs); } } while (matches); } } } } /* if (this.maxWidth && this.maxHeight) { if (this.width > this.maxWidth || this.height > this.maxHeight || this.bbEnlarge) { this.bbScale = Math.min(this.maxWidth / this.width, this.maxHeight / this.height); this.width = Math.round(this.width * this.bbScale); this.height = Math.round(this.height * this.bbScale); } } */ // debug('done'); this.draw(); }, draw: function(redrawCanvasOnly) { if (Object.isUndefined(redrawCanvasOnly)) redrawCanvasOnly = false; var ctxScale = this.scale * this.dpi / 72; var width = Math.round(ctxScale * this.width + 2 * this.padding); var height = Math.round(ctxScale * this.height + 2 * this.padding); if (!redrawCanvasOnly) { this.canvas.width = width; this.canvas.height = height; this.canvas.setStyle({ width: width + 'px', height: height + 'px' }); this.container.setStyle({ width: width + 'px' }); while (this.elements.firstChild) { this.elements.removeChild(this.elements.firstChild); } } this.ctx.save(); this.ctx.lineCap = 'round'; this.ctx.fillStyle = this.bgcolor.canvasColor; this.ctx.fillRect(0, 0, width, height); this.ctx.translate(this.padding, this.padding); this.ctx.scale(ctxScale, ctxScale); this.graphs[0].draw(this.ctx, ctxScale, redrawCanvasOnly); this.ctx.restore(); }, drawPath: function(ctx, path, filled, dashStyle) { if (filled) { ctx.beginPath(); path.makePath(ctx); ctx.fill(); } if (ctx.fillStyle != ctx.strokeStyle || !filled) { switch (dashStyle) { case 'dashed': ctx.beginPath(); path.makeDashedPath(ctx, this.dashLength); break; case 'dotted': var oldLineWidth = ctx.lineWidth; ctx.lineWidth *= 2; ctx.beginPath(); path.makeDottedPath(ctx, this.dotSpacing); break; case 'solid': default: if (!filled) { ctx.beginPath(); path.makePath(ctx); } } ctx.stroke(); if (oldLineWidth) ctx.lineWidth = oldLineWidth; } }, unescape: function(str) { var matches = str.match(/^"(.*)"$/); if (matches) { return matches[1].replace(/\\"/g, '"'); } else { return str; } }, parseHexColor: function(color) { var matches = color.match(/^#([0-9a-f]{2})\s*([0-9a-f]{2})\s*([0-9a-f]{2})\s*([0-9a-f]{2})?$/i); if (matches) { var canvasColor, textColor = '#' + matches[1] + matches[2] + matches[3], opacity = 1; if (matches[4]) { // rgba opacity = parseInt(matches[4], 16) / 255; canvasColor = 'rgba(' + parseInt(matches[1], 16) + ',' + parseInt(matches[2], 16) + ',' + parseInt(matches[3], 16) + ',' + opacity + ')'; } else { // rgb canvasColor = textColor; } } return {canvasColor: canvasColor, textColor: textColor, opacity: opacity}; }, hsvToRgbColor: function(h, s, v) { var i, f, p, q, t, r, g, b; h *= 360; i = Math.floor(h / 60) % 6; f = h / 60 - i; p = v * (1 - s); q = v * (1 - f * s); t = v * (1 - (1 - f) * s); switch (i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } return 'rgb(' + Math.round(255 * r) + ',' + Math.round(255 * g) + ',' + Math.round(255 * b) + ')'; }, versionCompare: function(a, b) { a = a.split('.'); b = b.split('.'); var a1, b1; while (a.length || b.length) { a1 = a.length ? a.shift() : 0; b1 = b.length ? b.shift() : 0; if (a1 < b1) return -1; if (a1 > b1) return 1; } return 0; }, // an alphanumeric string or a number or a double-quoted string or an HTML string idMatch: '([a-zA-Z\u0080-\uFFFF_][0-9a-zA-Z\u0080-\uFFFF_]*|-?(?:\\.\\d+|\\d+(?:\\.\\d*)?)|"(?:\\\\"|[^"])*"|<(?:<[^>]*>|[^<>]+?)+>)' }); Object.extend(Canviz.prototype, { // ID or ID:port or ID:compassPoint or ID:port:compassPoint nodeIdMatch: Canviz.prototype.idMatch + '(?::' + Canviz.prototype.idMatch + ')?(?::' + Canviz.prototype.idMatch + ')?' }); Object.extend(Canviz.prototype, { graphMatchRe: new RegExp('^(strict\\s+)?(graph|digraph)(?:\\s+' + Canviz.prototype.idMatch + ')?\\s*{$', 'i'), subgraphMatchRe: new RegExp('^(?:subgraph\\s+)?' + Canviz.prototype.idMatch + '?\\s*{$', 'i'), nodeMatchRe: new RegExp('^(' + Canviz.prototype.nodeIdMatch + ')\\s+\\[(.+)\\];$'), edgeMatchRe: new RegExp('^(' + Canviz.prototype.nodeIdMatch + '\\s*-[->]\\s*' + Canviz.prototype.nodeIdMatch + ')\\s+\\[(.+)\\];$'), attrMatchRe: new RegExp('^' + Canviz.prototype.idMatch + '=' + Canviz.prototype.idMatch + '(?:[,\\s]+|$)') }); var CanvizImage = Class.create({ initialize: function(canviz, src) { this.canviz = canviz; ++this.canviz.numImages; this.finished = this.loaded = false; this.img = new Image(); this.img.onload = this.onLoad.bind(this); this.img.onerror = this.onFinish.bind(this); this.img.onabort = this.onFinish.bind(this); this.img.src = this.canviz.imagePath + src; }, onLoad: function() { this.loaded = true; this.onFinish(); }, onFinish: function() { this.finished = true; ++this.canviz.numImagesFinished; if (this.canviz.numImages == this.canviz.numImagesFinished) { this.canviz.draw(true); } }, draw: function(ctx, l, t, w, h) { if (this.finished) { if (this.loaded) { ctx.drawImage(this.img, l, t, w, h); } else { debug('can\'t load image ' + this.img.src); this.drawBrokenImage(ctx, l, t, w, h); } } }, drawBrokenImage: function(ctx, l, t, w, h) { ctx.save(); ctx.beginPath(); new Rect(l, t, l + w, t + w).draw(ctx); ctx.moveTo(l, t); ctx.lineTo(l + w, t + w); ctx.moveTo(l + w, t); ctx.lineTo(l, t + h); ctx.strokeStyle = '#f00'; ctx.lineWidth = 1; ctx.stroke(); ctx.restore(); } }); function debug(str, escape) { str = String(str); if (Object.isUndefined(escape)) { escape = true; } if (escape) { str = str.escapeHTML(); } $('debug_output').innerHTML += '»' + str + '«
'; }