Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Switch to 2-space tabs.

  • Loading branch information...
commit c62fd3d34128f0411a6790cc71bb934118b48bc2 1 parent 6699723
@kennethkufluk authored
Showing with 665 additions and 665 deletions.
  1. +15 −15 index.html
  2. +62 −62 js-mindmap.css
  3. +535 −535 js-mindmap.js
  4. +39 −39 script.js
  5. +14 −14 style.css
View
30 index.html
@@ -1,22 +1,22 @@
<!DOCTYPE html>
<html>
<head>
- <!-- Kenneth Kufluk 2008/09/10 -->
- <title>js-mindmap demo - JavaScript Mindmap</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
- <link rel="stylesheet" type="text/css" href="js-mindmap.css" />
+ <!-- Kenneth Kufluk 2008/09/10 -->
+ <title>js-mindmap demo - JavaScript Mindmap</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="stylesheet" type="text/css" href="js-mindmap.css" />
<link href="style.css" type="text/css" rel="stylesheet"/>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<!-- UI, for draggable nodes -->
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.15/jquery-ui.min.js"></script>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.15/jquery-ui.min.js"></script>
<!-- Raphael for SVG support (won't work on android) -->
- <script type="text/javascript" src="raphael-min.js"></script>
+ <script type="text/javascript" src="raphael-min.js"></script>
<!-- Mindmap -->
- <script type="text/javascript" src="js-mindmap.js"></script>
+ <script type="text/javascript" src="js-mindmap.js"></script>
<!-- Kick everything off -->
<script src="script.js" type="text/javascript"></script>
@@ -33,17 +33,17 @@
<li><a href="http://kenneth.kufluk.com/blog/">Blog categories</a>
<ul>
- <li><a href="http://kenneth.kufluk.com/blog/blog/general/" title="View all posts filed under General">General</a></li>
- <li><a href="http://kenneth.kufluk.com/blog/blog/personal/" title="View all posts filed under Personal">Personal</a></li>
- <li><a href="http://kenneth.kufluk.com/blog/blog/physics/" title="View all posts filed under Physics &amp; Astronomy">Physics &amp; Astronomy</a></li>
- <li><a href="http://kenneth.kufluk.com/blog/blog/projects/" title="View all posts filed under Projects">Projects</a></li>
- <li><a href="http://kenneth.kufluk.com/blog/blog/rant/" title="View all posts filed under Ranting">Ranting</a></li>
- <li><a href="http://kenneth.kufluk.com/blog/blog/work/" title="View all posts filed under Work">Work</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/general/" title="View all posts filed under General">General</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/personal/" title="View all posts filed under Personal">Personal</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/physics/" title="View all posts filed under Physics &amp; Astronomy">Physics &amp; Astronomy</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/projects/" title="View all posts filed under Projects">Projects</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/rant/" title="View all posts filed under Ranting">Ranting</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/blog/work/" title="View all posts filed under Work">Work</a></li>
</ul>
</li>
<li><a href="http://kenneth.kufluk.com/blog/">Pages</a>
<ul>
- <li><a href="http://kenneth.kufluk.com/blog/about/" title="About Kenneth">About Kenneth</a></li>
+ <li><a href="http://kenneth.kufluk.com/blog/about/" title="About Kenneth">About Kenneth</a></li>
<li><a href="http://kenneth.kufluk.com/blog/work/" title="Employment">Employment</a></li>
<li><a href="http://kenneth.kufluk.com/blog/experiments/" title="Experiments">Experiments</a></li>
</ul>
@@ -59,7 +59,7 @@
<li><a href="http://simonwillison.net/" title="PHP, Python, CSS, XML and general web development.">Simon Willison</a></li>
</ul>
</li>
- </ul>
+ </ul>
</li>
</ul>
</body>
View
124 js-mindmap.css
@@ -1,62 +1,62 @@
- body {
- }
- #debug1 {
- display:block;
- }
- .js-mindmap-active h1 {
- display:none;
- }
- .js-mindmap-active section h1 {
- display:block;
- }
- .js-mindmap-active .node {
- position:absolute;
- top:0;
- left:0;
- font-family:verdana;
- font-size:11px;
- color:#003258;
- opacity:0.9;
- padding:0 7px;
- cursor:pointer;
- cursor:hand;
- z-index:100;
- list-style:none;
- }
- .js-mindmap-active a.node {
- font: 30px/34px Arial, sans-serif;
- font-size:1em;
- letter-spacing: 0;
- display:block;
- color:white;
- text-align:center;
- text-decoration:none;
- }
- .js-mindmap-active .node.active{
- font-size:1.5em;
- }
- .js-mindmap-active .node.active a{
- color:#003258;
- }
- .js-mindmap-active .node.activeparent a{
- color:#001228;
- }
- .js-mindmap-active img.line {
- position:absolute;
- width:200px;
- height:133px;
- top:0;
- left:0;
- display:block;
- z-index:0;
- }
- .ui-draggable {
- position:absolute;
- }
- .js-mindmap-active .node .node-action {
- position:absolute;
- right:-2em;
- bottom:-1px;
- text-align:center;
- vertical-align:super;
- }
+body {
+}
+#debug1 {
+ display:block;
+}
+.js-mindmap-active h1 {
+ display:none;
+}
+.js-mindmap-active section h1 {
+ display:block;
+}
+.js-mindmap-active .node {
+ position:absolute;
+ top:0;
+ left:0;
+ font-family:verdana;
+ font-size:11px;
+ color:#003258;
+ opacity:0.9;
+ padding:0 7px;
+ cursor:pointer;
+ cursor:hand;
+ z-index:100;
+ list-style:none;
+}
+.js-mindmap-active a.node {
+ font: 30px/34px Arial, sans-serif;
+ font-size:1em;
+ letter-spacing: 0;
+ display:block;
+ color:white;
+ text-align:center;
+ text-decoration:none;
+}
+.js-mindmap-active .node.active{
+ font-size:1.5em;
+}
+.js-mindmap-active .node.active a{
+ color:#003258;
+}
+.js-mindmap-active .node.activeparent a{
+ color:#001228;
+}
+.js-mindmap-active img.line {
+ position:absolute;
+ width:200px;
+ height:133px;
+ top:0;
+ left:0;
+ display:block;
+ z-index:0;
+}
+.ui-draggable {
+ position:absolute;
+}
+.js-mindmap-active .node .node-action {
+ position:absolute;
+ right:-2em;
+ bottom:-1px;
+ text-align:center;
+ vertical-align:super;
+}
View
1,070 js-mindmap.js
@@ -26,561 +26,561 @@
*/
/*
- Things to do:
- - remove Lines - NO - they seem harmless enough!
- - add better "make active" methods
- - remove the "root node" concept. Tie nodes to elements better, so we can check if a parent element is root
-
- - allow progressive exploration
- - allow easy supplying of an ajax param for loading new kids and a loader anim
- - allow easy exploration of a ul or ol to find nodes
- - limit to an area
- - allow more content (div instead of an a)
- - test multiple canvases
- - Hidden children should not be bounded
- - Layout children in circles
- - Add/Edit nodes
- - Resize event
- - incorporate widths into the forces, so left boundaries push on right boundaries
-
-
- Make demos:
- - amazon explore
- - directgov explore
- - thesaurus
- - themes
+ Things to do:
+ - remove Lines - NO - they seem harmless enough!
+ - add better "make active" methods
+ - remove the "root node" concept. Tie nodes to elements better, so we can check if a parent element is root
+
+ - allow progressive exploration
+ - allow easy supplying of an ajax param for loading new kids and a loader anim
+ - allow easy exploration of a ul or ol to find nodes
+ - limit to an area
+ - allow more content (div instead of an a)
+ - test multiple canvases
+ - Hidden children should not be bounded
+ - Layout children in circles
+ - Add/Edit nodes
+ - Resize event
+ - incorporate widths into the forces, so left boundaries push on right boundaries
+
+
+ Make demos:
+ - amazon explore
+ - directgov explore
+ - thesaurus
+ - themes
*/
(function ($) {
- 'use strict';
-
- var TIMEOUT = 4, // movement timeout in seconds
- CENTRE_FORCE = 3, // strength of attraction to the centre by the active node
- Node,
- Line;
-
- // Define all Node related functions.
- Node = function (obj, name, parent, opts) {
- this.obj = obj;
- this.options = obj.options;
-
- this.name = name;
- this.href = opts.href;
- if (opts.url) {
- this.url = opts.url;
- }
-
- // create the element for display
- this.el = $('<a href="' + this.href + '">' + this.name + '</a>').addClass('node');
- $('body').prepend(this.el);
-
- if (!parent) {
- obj.activeNode = this;
- this.el.addClass('active root');
- } else {
- obj.lines[obj.lines.length] = new Line(obj, this, parent);
- }
- this.parent = parent;
- this.children = [];
- if (this.parent) {
- this.parent.children.push(this);
+ 'use strict';
+
+ var TIMEOUT = 4, // movement timeout in seconds
+ CENTRE_FORCE = 3, // strength of attraction to the centre by the active node
+ Node,
+ Line;
+
+ // Define all Node related functions.
+ Node = function (obj, name, parent, opts) {
+ this.obj = obj;
+ this.options = obj.options;
+
+ this.name = name;
+ this.href = opts.href;
+ if (opts.url) {
+ this.url = opts.url;
+ }
+
+ // create the element for display
+ this.el = $('<a href="' + this.href + '">' + this.name + '</a>').addClass('node');
+ $('body').prepend(this.el);
+
+ if (!parent) {
+ obj.activeNode = this;
+ this.el.addClass('active root');
+ } else {
+ obj.lines[obj.lines.length] = new Line(obj, this, parent);
+ }
+ this.parent = parent;
+ this.children = [];
+ if (this.parent) {
+ this.parent.children.push(this);
+ }
+
+ // animation handling
+ this.moving = false;
+ this.moveTimer = 0;
+ this.obj.movementStopped = false;
+ this.visible = true;
+ this.x = 1;
+ this.y = 1;
+ this.dx = 0;
+ this.dy = 0;
+ this.hasPosition = false;
+
+ this.content = []; // array of content elements to display onclick;
+
+ this.el.css('position', 'absolute');
+
+ var thisnode = this;
+
+ this.el.draggable({
+ drag: function () {
+ obj.root.animateToStatic();
+ }
+ });
+
+ this.el.click(function () {
+ if (obj.activeNode) {
+ obj.activeNode.el.removeClass('active');
+ if (obj.activeNode.parent) {
+ obj.activeNode.parent.el.removeClass('activeparent');
}
-
- // animation handling
- this.moving = false;
- this.moveTimer = 0;
- this.obj.movementStopped = false;
+ }
+ if (typeof opts.onclick === 'function') {
+ opts.onclick(thisnode);
+ }
+ obj.activeNode = thisnode;
+ obj.activeNode.el.addClass('active');
+ if (obj.activeNode.parent) {
+ obj.activeNode.parent.el.addClass('activeparent');
+ }
+ obj.root.animateToStatic();
+ return false;
+ });
+
+ };
+
+ // ROOT NODE ONLY: control animation loop
+ Node.prototype.animateToStatic = function () {
+
+ clearTimeout(this.moveTimer);
+ // stop the movement after a certain time
+ var thisnode = this;
+ this.moveTimer = setTimeout(function () {
+ //stop the movement
+ thisnode.obj.movementStopped = true;
+ }, TIMEOUT * 1000);
+
+ if (this.moving) {
+ return;
+ }
+ this.moving = true;
+ this.obj.movementStopped = false;
+ this.animateLoop();
+ };
+
+ // ROOT NODE ONLY: animate all nodes (calls itself recursively)
+ Node.prototype.animateLoop = function () {
+ var i, len, mynode = this;
+ this.obj.canvas.clear();
+ for (i = 0, len = this.obj.lines.length; i < len; i++) {
+ this.obj.lines[i].updatePosition();
+ }
+ if (this.findEquilibrium() || this.obj.movementStopped) {
+ this.moving = false;
+ return;
+ }
+ setTimeout(function () {
+ mynode.animateLoop();
+ }, 10);
+ };
+
+ // find the right position for this node
+ Node.prototype.findEquilibrium = function () {
+ var i, len, stable = true;
+ stable = this.display() && stable;
+ for (i = 0, len = this.children.length; i < len; i++) {
+ stable = this.children[i].findEquilibrium() && stable;
+ }
+ return stable;
+ };
+
+ //Display this node, and its children
+ Node.prototype.display = function (depth) {
+ var parent = this,
+ stepAngle,
+ angle;
+
+ depth = depth || 0;
+
+ if (this.visible) {
+ // if: I'm not active AND my parent's not active AND my children aren't active ...
+ if (this.obj.activeNode !== this && this.obj.activeNode !== this.parent && this.obj.activeNode.parent !== this) {
+ // TODO hide me!
+ this.el.hide();
+ this.visible = false;
+ }
+ } else {
+ if (this.obj.activeNode === this || this.obj.activeNode === this.parent || this.obj.activeNode.parent === this) {
+ this.el.show();
this.visible = true;
- this.x = 1;
- this.y = 1;
- this.dx = 0;
- this.dy = 0;
- this.hasPosition = false;
-
- this.content = []; // array of content elements to display onclick;
-
- this.el.css('position', 'absolute');
-
- var thisnode = this;
-
- this.el.draggable({
- drag: function () {
- obj.root.animateToStatic();
- }
- });
-
- this.el.click(function () {
- if (obj.activeNode) {
- obj.activeNode.el.removeClass('active');
- if (obj.activeNode.parent) {
- obj.activeNode.parent.el.removeClass('activeparent');
- }
- }
- if (typeof opts.onclick === 'function') {
- opts.onclick(thisnode);
- }
- obj.activeNode = thisnode;
- obj.activeNode.el.addClass('active');
- if (obj.activeNode.parent) {
- obj.activeNode.parent.el.addClass('activeparent');
- }
- obj.root.animateToStatic();
- return false;
- });
-
- };
-
- // ROOT NODE ONLY: control animation loop
- Node.prototype.animateToStatic = function () {
-
- clearTimeout(this.moveTimer);
- // stop the movement after a certain time
- var thisnode = this;
- this.moveTimer = setTimeout(function () {
- //stop the movement
- thisnode.obj.movementStopped = true;
- }, TIMEOUT * 1000);
-
- if (this.moving) {
- return;
- }
- this.moving = true;
- this.obj.movementStopped = false;
- this.animateLoop();
- };
-
- // ROOT NODE ONLY: animate all nodes (calls itself recursively)
- Node.prototype.animateLoop = function () {
- var i, len, mynode = this;
- this.obj.canvas.clear();
- for (i = 0, len = this.obj.lines.length; i < len; i++) {
- this.obj.lines[i].updatePosition();
- }
- if (this.findEquilibrium() || this.obj.movementStopped) {
- this.moving = false;
- return;
- }
- setTimeout(function () {
- mynode.animateLoop();
- }, 10);
- };
-
- // find the right position for this node
- Node.prototype.findEquilibrium = function () {
- var i, len, stable = true;
- stable = this.display() && stable;
- for (i = 0, len = this.children.length; i < len; i++) {
- stable = this.children[i].findEquilibrium() && stable;
+ }
+ }
+ this.drawn = true;
+ // am I positioned? If not, position me.
+ if (!this.hasPosition) {
+ this.x = this.options.mapArea.x / 2;
+ this.y = this.options.mapArea.y / 2;
+ this.el.css({'left': this.x + "px", 'top': this.y + "px"});
+ this.hasPosition = true;
+ }
+ // are my children positioned? if not, lay out my children around me
+ stepAngle = Math.PI * 2 / this.children.length;
+ $.each(this.children, function (index) {
+ if (!this.hasPosition) {
+ if (!this.options.showProgressive || depth <= 1) {
+ angle = index * stepAngle;
+ this.x = (50 * Math.cos(angle)) + parent.x;
+ this.y = (50 * Math.sin(angle)) + parent.y;
+ this.hasPosition = true;
+ this.el.css({'left': this.x + "px", 'top': this.y + "px"});
}
- return stable;
- };
-
- //Display this node, and its children
- Node.prototype.display = function (depth) {
- var parent = this,
- stepAngle,
- angle;
-
- depth = depth || 0;
-
- if (this.visible) {
- // if: I'm not active AND my parent's not active AND my children aren't active ...
- if (this.obj.activeNode !== this && this.obj.activeNode !== this.parent && this.obj.activeNode.parent !== this) {
- // TODO hide me!
- this.el.hide();
- this.visible = false;
- }
+ }
+ });
+ // update my position
+ return this.updatePosition();
+ };
+
+ // updatePosition returns a boolean stating whether it's been static
+ Node.prototype.updatePosition = function () {
+ var forces, showx, showy;
+
+ if (this.el.hasClass("ui-draggable-dragging")) {
+ this.x = parseInt(this.el.css('left'), 10) + (this.el.width() / 2);
+ this.y = parseInt(this.el.css('top'), 10) + (this.el.height() / 2);
+ this.dx = 0;
+ this.dy = 0;
+ return false;
+ }
+
+ //apply accelerations
+ forces = this.getForceVector();
+ this.dx += forces.x * this.options.timeperiod;
+ this.dy += forces.y * this.options.timeperiod;
+
+ // damp the forces
+ this.dx = this.dx * this.options.damping;
+ this.dy = this.dy * this.options.damping;
+
+ //ADD MINIMUM SPEEDS
+ if (Math.abs(this.dx) < this.options.minSpeed) {
+ this.dx = 0;
+ }
+ if (Math.abs(this.dy) < this.options.minSpeed) {
+ this.dy = 0;
+ }
+ if (Math.abs(this.dx) + Math.abs(this.dy) === 0) {
+ return true;
+ }
+ //apply velocity vector
+ this.x += this.dx * this.options.timeperiod;
+ this.y += this.dy * this.options.timeperiod;
+ this.x = Math.min(this.options.mapArea.x, Math.max(1, this.x));
+ this.y = Math.min(this.options.mapArea.y, Math.max(1, this.y));
+ // display
+ showx = this.x - (this.el.width() / 2);
+ showy = this.y - (this.el.height() / 2) - 10;
+ this.el.css({'left': showx + "px", 'top': showy + "px"});
+ return false;
+ };
+
+ Node.prototype.getForceVector = function () {
+ var i, x1, y1, xsign, dist, theta, f,
+ xdist, rightdist, bottomdist, otherend,
+ fx = 0,
+ fy = 0,
+ nodes = this.obj.nodes,
+ lines = this.obj.lines;
+
+ // Calculate the repulsive force from every other node
+ for (i = 0; i < nodes.length; i++) {
+ if (nodes[i] === this) {
+ continue;
+ }
+ if (!nodes[i].visible) {
+ continue;
+ }
+ // Repulsive force (coulomb's law)
+ x1 = (nodes[i].x - this.x);
+ y1 = (nodes[i].y - this.y);
+ //adjust for variable node size
+// var nodewidths = (($(nodes[i]).width() + this.el.width())/2);
+ dist = Math.sqrt((x1 * x1) + (y1 * y1));
+// var myrepulse = this.options.repulse;
+// if (this.parent==nodes[i]) myrepulse=myrepulse*10; //parents stand further away
+ if (Math.abs(dist) < 500) {
+ if (x1 === 0) {
+ theta = Math.PI / 2;
+ xsign = 0;
} else {
- if (this.obj.activeNode === this || this.obj.activeNode === this.parent || this.obj.activeNode.parent === this) {
- this.el.show();
- this.visible = true;
- }
- }
- this.drawn = true;
- // am I positioned? If not, position me.
- if (!this.hasPosition) {
- this.x = this.options.mapArea.x / 2;
- this.y = this.options.mapArea.y / 2;
- this.el.css({'left': this.x + "px", 'top': this.y + "px"});
- this.hasPosition = true;
+ theta = Math.atan(y1 / x1);
+ xsign = x1 / Math.abs(x1);
}
- // are my children positioned? if not, lay out my children around me
- stepAngle = Math.PI * 2 / this.children.length;
- $.each(this.children, function (index) {
- if (!this.hasPosition) {
- if (!this.options.showProgressive || depth <= 1) {
- angle = index * stepAngle;
- this.x = (50 * Math.cos(angle)) + parent.x;
- this.y = (50 * Math.sin(angle)) + parent.y;
- this.hasPosition = true;
- this.el.css({'left': this.x + "px", 'top': this.y + "px"});
- }
- }
- });
- // update my position
- return this.updatePosition();
- };
-
- // updatePosition returns a boolean stating whether it's been static
- Node.prototype.updatePosition = function () {
- var forces, showx, showy;
-
- if (this.el.hasClass("ui-draggable-dragging")) {
- this.x = parseInt(this.el.css('left'), 10) + (this.el.width() / 2);
- this.y = parseInt(this.el.css('top'), 10) + (this.el.height() / 2);
- this.dx = 0;
- this.dy = 0;
- return false;
- }
-
- //apply accelerations
- forces = this.getForceVector();
- this.dx += forces.x * this.options.timeperiod;
- this.dy += forces.y * this.options.timeperiod;
-
- // damp the forces
- this.dx = this.dx * this.options.damping;
- this.dy = this.dy * this.options.damping;
-
- //ADD MINIMUM SPEEDS
- if (Math.abs(this.dx) < this.options.minSpeed) {
- this.dx = 0;
+ // force is based on radial distance
+ f = (this.options.repulse * 500) / (dist * dist);
+ fx += -f * Math.cos(theta) * xsign;
+ fy += -f * Math.sin(theta) * xsign;
+ }
+ }
+
+ // add repulsive force of the "walls"
+ //left wall
+ xdist = this.x + this.el.width();
+ f = (this.options.wallrepulse * 500) / (xdist * xdist);
+ fx += Math.min(2, f);
+ //right wall
+ rightdist = (this.options.mapArea.x - xdist);
+ f = -(this.options.wallrepulse * 500) / (rightdist * rightdist);
+ fx += Math.max(-2, f);
+ //top wall
+ f = (this.options.wallrepulse * 500) / (this.y * this.y);
+ fy += Math.min(2, f);
+ //bottom wall
+ bottomdist = (this.options.mapArea.y - this.y);
+ f = -(this.options.wallrepulse * 500) / (bottomdist * bottomdist);
+ fy += Math.max(-2, f);
+
+ // for each line, of which I'm a part, add an attractive force.
+ for (i = 0; i < lines.length; i++) {
+ otherend = null;
+ if (lines[i].start === this) {
+ otherend = lines[i].end;
+ } else if (lines[i].end === this) {
+ otherend = lines[i].start;
+ } else {
+ continue;
+ }
+ // Ignore the pull of hidden nodes
+ if (!otherend.visible) {
+ continue;
+ }
+ // Attractive force (hooke's law)
+ x1 = (otherend.x - this.x);
+ y1 = (otherend.y - this.y);
+ dist = Math.sqrt((x1 * x1) + (y1 * y1));
+ if (Math.abs(dist) > 0) {
+ if (x1 === 0) {
+ theta = Math.PI / 2;
+ xsign = 0;
}
- if (Math.abs(this.dy) < this.options.minSpeed) {
- this.dy = 0;
+ else {
+ theta = Math.atan(y1 / x1);
+ xsign = x1 / Math.abs(x1);
}
- if (Math.abs(this.dx) + Math.abs(this.dy) === 0) {
- return true;
+ // force is based on radial distance
+ f = (this.options.attract * dist) / 10000;
+ fx += f * Math.cos(theta) * xsign;
+ fy += f * Math.sin(theta) * xsign;
+ }
+ }
+
+ // if I'm active, attract me to the centre of the area
+ if (this.obj.activeNode === this) {
+ // Attractive force (hooke's law)
+ otherend = this.options.mapArea;
+ x1 = ((otherend.x / 2) - this.options.centreOffset - this.x);
+ y1 = ((otherend.y / 2) - this.y);
+ dist = Math.sqrt((x1 * x1) + (y1 * y1));
+ if (Math.abs(dist) > 0) {
+ if (x1 === 0) {
+ theta = Math.PI / 2;
+ xsign = 0;
+ } else {
+ xsign = x1 / Math.abs(x1);
+ theta = Math.atan(y1 / x1);
}
- //apply velocity vector
- this.x += this.dx * this.options.timeperiod;
- this.y += this.dy * this.options.timeperiod;
- this.x = Math.min(this.options.mapArea.x, Math.max(1, this.x));
- this.y = Math.min(this.options.mapArea.y, Math.max(1, this.y));
- // display
- showx = this.x - (this.el.width() / 2);
- showy = this.y - (this.el.height() / 2) - 10;
- this.el.css({'left': showx + "px", 'top': showy + "px"});
- return false;
+ // force is based on radial distance
+ f = (0.1 * this.options.attract * dist * CENTRE_FORCE) / 1000;
+ fx += f * Math.cos(theta) * xsign;
+ fy += f * Math.sin(theta) * xsign;
+ }
+ }
+
+ if (Math.abs(fx) > this.options.maxForce) {
+ fx = this.options.maxForce * (fx / Math.abs(fx));
+ }
+ if (Math.abs(fy) > this.options.maxForce) {
+ fy = this.options.maxForce * (fy / Math.abs(fy));
+ }
+ return {
+ x: fx,
+ y: fy
};
-
- Node.prototype.getForceVector = function () {
- var i, x1, y1, xsign, dist, theta, f,
- xdist, rightdist, bottomdist, otherend,
- fx = 0,
- fy = 0,
- nodes = this.obj.nodes,
- lines = this.obj.lines;
-
- // Calculate the repulsive force from every other node
- for (i = 0; i < nodes.length; i++) {
- if (nodes[i] === this) {
- continue;
- }
- if (!nodes[i].visible) {
- continue;
- }
- // Repulsive force (coulomb's law)
- x1 = (nodes[i].x - this.x);
- y1 = (nodes[i].y - this.y);
- //adjust for variable node size
-// var nodewidths = (($(nodes[i]).width() + this.el.width())/2);
- dist = Math.sqrt((x1 * x1) + (y1 * y1));
-// var myrepulse = this.options.repulse;
-// if (this.parent==nodes[i]) myrepulse=myrepulse*10; //parents stand further away
- if (Math.abs(dist) < 500) {
- if (x1 === 0) {
- theta = Math.PI / 2;
- xsign = 0;
- } else {
- theta = Math.atan(y1 / x1);
- xsign = x1 / Math.abs(x1);
- }
- // force is based on radial distance
- f = (this.options.repulse * 500) / (dist * dist);
- fx += -f * Math.cos(theta) * xsign;
- fy += -f * Math.sin(theta) * xsign;
- }
- }
-
- // add repulsive force of the "walls"
- //left wall
- xdist = this.x + this.el.width();
- f = (this.options.wallrepulse * 500) / (xdist * xdist);
- fx += Math.min(2, f);
- //right wall
- rightdist = (this.options.mapArea.x - xdist);
- f = -(this.options.wallrepulse * 500) / (rightdist * rightdist);
- fx += Math.max(-2, f);
- //top wall
- f = (this.options.wallrepulse * 500) / (this.y * this.y);
- fy += Math.min(2, f);
- //bottom wall
- bottomdist = (this.options.mapArea.y - this.y);
- f = -(this.options.wallrepulse * 500) / (bottomdist * bottomdist);
- fy += Math.max(-2, f);
-
- // for each line, of which I'm a part, add an attractive force.
- for (i = 0; i < lines.length; i++) {
- otherend = null;
- if (lines[i].start === this) {
- otherend = lines[i].end;
- } else if (lines[i].end === this) {
- otherend = lines[i].start;
+ };
+
+ Node.prototype.removeNode = function () {
+ var i,
+ oldnodes = this.obj.nodes,
+ oldlines = this.obj.lines;
+
+ for (i = 0; i < this.children.length; i++) {
+ this.children[i].removeNode();
+ }
+
+ this.obj.nodes = [];
+ for (i = 0; i < oldnodes.length; i++) {
+ if (oldnodes[i] === this) {
+ continue;
+ }
+ this.obj.nodes.push(oldnodes[i]);
+ }
+
+ this.obj.lines = [];
+ for (i = 0; i < oldlines.length; i++) {
+ if (oldlines[i].start === this) {
+ continue;
+ } else if (oldlines[i].end === this) {
+ continue;
+ }
+ this.obj.lines.push(oldlines[i]);
+ }
+
+ this.el.remove();
+ };
+
+
+
+ // Define all Line related functions.
+ Line = function (obj, startNode, endNode) {
+ this.obj = obj;
+ this.options = obj.options;
+ this.start = startNode;
+ this.colour = "blue";
+ this.size = "thick";
+ this.end = endNode;
+ };
+
+ Line.prototype.updatePosition = function () {
+ if (!this.options.showSublines && (!this.start.visible || !this.end.visible)) {
+ return;
+ }
+ this.size = (this.start.visible && this.end.visible) ? "thick" : "thin";
+ this.color = (this.obj.activeNode.parent === this.start || this.obj.activeNode.parent === this.end) ? "red" : "blue";
+ this.strokeStyle = "#FFF";
+
+ this.obj.canvas.path("M" + this.start.x + ' ' + this.start.y + "L" + this.end.x + ' ' + this.end.y).attr({'stroke': this.strokeStyle, 'opacity': 0.2, 'stroke-width': '5px'});
+ };
+
+ $.fn.addNode = function (parent, name, options) {
+ var obj = this[0],
+ node = obj.nodes[obj.nodes.length] = new Node(obj, name, parent, options);
+ console.log(obj.root);
+ obj.root.animateToStatic();
+ return node;
+ };
+
+ $.fn.addRootNode = function (name, opts) {
+ var node = this[0].nodes[0] = new Node(this[0], name, null, opts);
+ this[0].root = node;
+ return node;
+ };
+
+ $.fn.removeNode = function (name) {
+ return this.each(function () {
+// if (!!this.mindmapInit) return false;
+ //remove a node matching the anme
+// alert(name+' removed');
+ });
+ };
+
+ $.fn.mindmap = function (options) {
+ // Define default settings.
+ options = $.extend({
+ attract: 15,
+ repulse: 6,
+ damping: 0.55,
+ timeperiod: 10,
+ wallrepulse: 0.4,
+ mapArea: {
+ x: -1,
+ y: -1
+ },
+ canvasError: 'alert',
+ minSpeed: 0.05,
+ maxForce: 0.1,
+ showSublines: false,
+ updateIterationCount: 20,
+ showProgressive: true,
+ centreOffset: 100,
+ timer: 0
+ }, options);
+
+ var $window = $(window);
+
+ return this.each(function () {
+ var mindmap = this;
+
+ this.mindmapInit = true;
+ this.nodes = [];
+ this.lines = [];
+ this.activeNode = null;
+ this.options = options;
+ this.animateToStatic = function () {
+ this.root.animateToStatic();
+ };
+ $window.resize(function () {
+ mindmap.animateToStatic();
+ });
+
+ //canvas
+ if (options.mapArea.x === -1) {
+ options.mapArea.x = $window.width();
+ }
+ if (options.mapArea.y === -1) {
+ options.mapArea.y = $window.height();
+ }
+ //create drawing area
+ this.canvas = Raphael(0, 0, options.mapArea.x, options.mapArea.y);
+
+ // Add a class to the object, so that styles can be applied
+ $(this).addClass('js-mindmap-active');
+
+ // Add keyboard support (thanks to wadefs)
+ $(this).keyup(function (event) {
+ var newNode, i, activeParent = mindmap.activeNode.parent;
+ switch (event.which) {
+ case 33: // PgUp
+ case 38: // Up, move to parent
+ if (activeParent) {
+ activeParent.el.click();
+ }
+ break;
+ case 13: // Enter (change to insert a sibling)
+ case 34: // PgDn
+ case 40: // Down, move to first child
+ if (mindmap.activeNode.children.length) {
+ mindmap.activeNode.children[0].el.click();
+ }
+ break;
+ case 37: // Left, move to previous sibling
+ if (activeParent) {
+ newNode = null;
+ if (activeParent.children[0] === mindmap.activeNode) {
+ newNode = activeParent.children[activeParent.children.length - 1];
} else {
- continue;
- }
- // Ignore the pull of hidden nodes
- if (!otherend.visible) {
- continue;
- }
- // Attractive force (hooke's law)
- x1 = (otherend.x - this.x);
- y1 = (otherend.y - this.y);
- dist = Math.sqrt((x1 * x1) + (y1 * y1));
- if (Math.abs(dist) > 0) {
- if (x1 === 0) {
- theta = Math.PI / 2;
- xsign = 0;
- }
- else {
- theta = Math.atan(y1 / x1);
- xsign = x1 / Math.abs(x1);
+ for (i = 1; i < activeParent.children.length; i++) {
+ if (activeParent.children[i] === mindmap.activeNode) {
+ newNode = activeParent.children[i - 1];
}
- // force is based on radial distance
- f = (this.options.attract * dist) / 10000;
- fx += f * Math.cos(theta) * xsign;
- fy += f * Math.sin(theta) * xsign;
+ }
}
- }
-
- // if I'm active, attract me to the centre of the area
- if (this.obj.activeNode === this) {
- // Attractive force (hooke's law)
- otherend = this.options.mapArea;
- x1 = ((otherend.x / 2) - this.options.centreOffset - this.x);
- y1 = ((otherend.y / 2) - this.y);
- dist = Math.sqrt((x1 * x1) + (y1 * y1));
- if (Math.abs(dist) > 0) {
- if (x1 === 0) {
- theta = Math.PI / 2;
- xsign = 0;
- } else {
- xsign = x1 / Math.abs(x1);
- theta = Math.atan(y1 / x1);
- }
- // force is based on radial distance
- f = (0.1 * this.options.attract * dist * CENTRE_FORCE) / 1000;
- fx += f * Math.cos(theta) * xsign;
- fy += f * Math.sin(theta) * xsign;
+ if (newNode) {
+ newNode.el.click();
}
- }
-
- if (Math.abs(fx) > this.options.maxForce) {
- fx = this.options.maxForce * (fx / Math.abs(fx));
- }
- if (Math.abs(fy) > this.options.maxForce) {
- fy = this.options.maxForce * (fy / Math.abs(fy));
- }
- return {
- x: fx,
- y: fy
- };
- };
-
- Node.prototype.removeNode = function () {
- var i,
- oldnodes = this.obj.nodes,
- oldlines = this.obj.lines;
-
- for (i = 0; i < this.children.length; i++) {
- this.children[i].removeNode();
- }
-
- this.obj.nodes = [];
- for (i = 0; i < oldnodes.length; i++) {
- if (oldnodes[i] === this) {
- continue;
+ }
+ break;
+ case 39: // Right, move to next sibling
+ if (activeParent) {
+ newNode = null;
+ if (activeParent.children[activeParent.children.length - 1] === mindmap.activeNode) {
+ newNode = activeParent.children[0];
+ } else {
+ for (i = activeParent.children.length - 2; i >= 0; i--) {
+ if (activeParent.children[i] === mindmap.activeNode) {
+ newNode = activeParent.children[i + 1];
+ }
+ }
}
- this.obj.nodes.push(oldnodes[i]);
- }
-
- this.obj.lines = [];
- for (i = 0; i < oldlines.length; i++) {
- if (oldlines[i].start === this) {
- continue;
- } else if (oldlines[i].end === this) {
- continue;
+ if (newNode) {
+ newNode.el.click();
}
- this.obj.lines.push(oldlines[i]);
- }
-
- this.el.remove();
- };
-
-
-
- // Define all Line related functions.
- Line = function (obj, startNode, endNode) {
- this.obj = obj;
- this.options = obj.options;
- this.start = startNode;
- this.colour = "blue";
- this.size = "thick";
- this.end = endNode;
- };
-
- Line.prototype.updatePosition = function () {
- if (!this.options.showSublines && (!this.start.visible || !this.end.visible)) {
- return;
+ }
+ break;
+ case 45: // Ins, insert a child
+ break;
+ case 46: // Del, delete this node
+ break;
+ case 27: // Esc, cancel insert
+ break;
+ case 83: // 'S', save
+ break;
}
- this.size = (this.start.visible && this.end.visible) ? "thick" : "thin";
- this.color = (this.obj.activeNode.parent === this.start || this.obj.activeNode.parent === this.end) ? "red" : "blue";
- this.strokeStyle = "#FFF";
-
- this.obj.canvas.path("M" + this.start.x + ' ' + this.start.y + "L" + this.end.x + ' ' + this.end.y).attr({'stroke': this.strokeStyle, 'opacity': 0.2, 'stroke-width': '5px'});
- };
-
- $.fn.addNode = function (parent, name, options) {
- var obj = this[0],
- node = obj.nodes[obj.nodes.length] = new Node(obj, name, parent, options);
- console.log(obj.root);
- obj.root.animateToStatic();
- return node;
- };
-
- $.fn.addRootNode = function (name, opts) {
- var node = this[0].nodes[0] = new Node(this[0], name, null, opts);
- this[0].root = node;
- return node;
- };
-
- $.fn.removeNode = function (name) {
- return this.each(function () {
-// if (!!this.mindmapInit) return false;
- //remove a node matching the anme
-// alert(name+' removed');
- });
- };
-
- $.fn.mindmap = function (options) {
- // Define default settings.
- options = $.extend({
- attract: 15,
- repulse: 6,
- damping: 0.55,
- timeperiod: 10,
- wallrepulse: 0.4,
- mapArea: {
- x: -1,
- y: -1
- },
- canvasError: 'alert',
- minSpeed: 0.05,
- maxForce: 0.1,
- showSublines: false,
- updateIterationCount: 20,
- showProgressive: true,
- centreOffset: 100,
- timer: 0
- }, options);
-
- var $window = $(window);
-
- return this.each(function () {
- var mindmap = this;
-
- this.mindmapInit = true;
- this.nodes = [];
- this.lines = [];
- this.activeNode = null;
- this.options = options;
- this.animateToStatic = function () {
- this.root.animateToStatic();
- };
- $window.resize(function () {
- mindmap.animateToStatic();
- });
-
- //canvas
- if (options.mapArea.x === -1) {
- options.mapArea.x = $window.width();
- }
- if (options.mapArea.y === -1) {
- options.mapArea.y = $window.height();
- }
- //create drawing area
- this.canvas = Raphael(0, 0, options.mapArea.x, options.mapArea.y);
-
- // Add a class to the object, so that styles can be applied
- $(this).addClass('js-mindmap-active');
-
- // Add keyboard support (thanks to wadefs)
- $(this).keyup(function (event) {
- var newNode, i, activeParent = mindmap.activeNode.parent;
- switch (event.which) {
- case 33: // PgUp
- case 38: // Up, move to parent
- if (activeParent) {
- activeParent.el.click();
- }
- break;
- case 13: // Enter (change to insert a sibling)
- case 34: // PgDn
- case 40: // Down, move to first child
- if (mindmap.activeNode.children.length) {
- mindmap.activeNode.children[0].el.click();
- }
- break;
- case 37: // Left, move to previous sibling
- if (activeParent) {
- newNode = null;
- if (activeParent.children[0] === mindmap.activeNode) {
- newNode = activeParent.children[activeParent.children.length - 1];
- } else {
- for (i = 1; i < activeParent.children.length; i++) {
- if (activeParent.children[i] === mindmap.activeNode) {
- newNode = activeParent.children[i - 1];
- }
- }
- }
- if (newNode) {
- newNode.el.click();
- }
- }
- break;
- case 39: // Right, move to next sibling
- if (activeParent) {
- newNode = null;
- if (activeParent.children[activeParent.children.length - 1] === mindmap.activeNode) {
- newNode = activeParent.children[0];
- } else {
- for (i = activeParent.children.length - 2; i >= 0; i--) {
- if (activeParent.children[i] === mindmap.activeNode) {
- newNode = activeParent.children[i + 1];
- }
- }
- }
- if (newNode) {
- newNode.el.click();
- }
- }
- break;
- case 45: // Ins, insert a child
- break;
- case 46: // Del, delete this node
- break;
- case 27: // Esc, cancel insert
- break;
- case 83: // 'S', save
- break;
- }
- return false;
- });
+ return false;
+ });
- });
- };
+ });
+ };
}(jQuery));
-/*jslint devel: true, browser: true, continue: true, plusplus: true, indent: 4 */
+/*jslint devel: true, browser: true, continue: true, plusplus: true, indent: 2 */
View
78 script.js
@@ -1,41 +1,41 @@
// load the mindmap
- $(document).ready(function() {
- // enable the mindmap in the body
- $('body').mindmap();
+$(document).ready(function() {
+ // enable the mindmap in the body
+ $('body').mindmap();
- // add the data to the mindmap
- var root = $('body>ul>li').get(0).mynode = $('body').addRootNode($('body>ul>li>a').text(), {
- href:'/',
- url:'/',
- onclick:function(node) {
- $(node.obj.activeNode.content).each(function() {
- this.hide();
- });
- }
- });
- $('body>ul>li').hide();
- var addLI = function() {
- var parentnode = $(this).parents('li').get(0);
- if (typeof(parentnode)=='undefined') parentnode=root;
- else parentnode=parentnode.mynode;
-
- this.mynode = $('body').addNode(parentnode, $('a:eq(0)',this).text(), {
-// href:$('a:eq(0)',this).text().toLowerCase(),
- href:$('a:eq(0)',this).attr('href'),
- onclick:function(node) {
- $(node.obj.activeNode.content).each(function() {
- this.hide();
- });
- $(node.content).each(function() {
- this.show();
- });
- }
- });
- $(this).hide();
- $('>ul>li', this).each(addLI);
- };
- $('body>ul>li>ul').each(function() {
- $('>li', this).each(addLI);
- });
-
- });
+ // add the data to the mindmap
+ var root = $('body>ul>li').get(0).mynode = $('body').addRootNode($('body>ul>li>a').text(), {
+ href:'/',
+ url:'/',
+ onclick:function(node) {
+ $(node.obj.activeNode.content).each(function() {
+ this.hide();
+ });
+ }
+ });
+ $('body>ul>li').hide();
+ var addLI = function() {
+ var parentnode = $(this).parents('li').get(0);
+ if (typeof(parentnode)=='undefined') parentnode=root;
+ else parentnode=parentnode.mynode;
+
+ this.mynode = $('body').addNode(parentnode, $('a:eq(0)',this).text(), {
+// href:$('a:eq(0)',this).text().toLowerCase(),
+ href:$('a:eq(0)',this).attr('href'),
+ onclick:function(node) {
+ $(node.obj.activeNode.content).each(function() {
+ this.hide();
+ });
+ $(node.content).each(function() {
+ this.show();
+ });
+ }
+ });
+ $(this).hide();
+ $('>ul>li', this).each(addLI);
+ };
+ $('body>ul>li>ul').each(function() {
+ $('>li', this).each(addLI);
+ });
+
+});
View
28 style.css
@@ -1,22 +1,22 @@
body {
- background:green;
+ background:green;
}
.js-mindmap-active a.node {
- background:red;
- border: 2px solid white;
- -webkit-border-top-left-radius: 20px;
- -webkit-border-bottom-right-radius: 20px;
- -moz-border-radius-topleft: 20px;
- -moz-border-radius-bottomright: 20px;
- border-top-left-radius: 20px;
- border-bottom-right-radius: 20px;
+ background:red;
+ border: 2px solid white;
+ -webkit-border-top-left-radius: 20px;
+ -webkit-border-bottom-right-radius: 20px;
+ -moz-border-radius-topleft: 20px;
+ -moz-border-radius-bottomright: 20px;
+ border-top-left-radius: 20px;
+ border-bottom-right-radius: 20px;
}
.js-mindmap-active a.node.active {
- padding:5px 10px !important;
- border-width:5px !important;
+ padding:5px 10px !important;
+ border-width:5px !important;
}
.js-mindmap-active a.node.activeparent {
- padding:5px 10px !important;
- border-width:5px !important;
- background:#8B0000;
+ padding:5px 10px !important;
+ border-width:5px !important;
+ background:#8B0000;
}
Please sign in to comment.
Something went wrong with that request. Please try again.