define(['jquery', 'underscore', 'requtils', 'renderer/baserepresentation'], function ($, _, requtils, BaseRepresentation) {
'use strict';
var Utils = requtils.getUtils();
/* Rkns.Renderer.Node Class */
/* The representation for the node : A circle, with an image inside and a text label underneath.
* The circle and the image are drawn on canvas and managed by Paper.js.
* The text label is an HTML node, managed by jQuery. */
//var NodeRepr = Renderer.Node = Utils.inherit(Renderer._BaseRepresentation);
var NodeRepr = Utils.inherit(BaseRepresentation);
_(NodeRepr.prototype).extend({
_init: function() {
this.renderer.node_layer.activate();
this.type = "Node";
this.circle = new paper.Path.Circle([0, 0], 1);
this.circle.__representation = this;
if (this.options.show_node_circles) {
this.circle.strokeWidth = this.options.node_stroke_width;
this.h_ratio = 1;
} else {
this.h_ratio = 0;
}
this.title = $('<div class="Rk-Label">').appendTo(this.renderer.labels_$);
if (this.options.editor_mode) {
var Renderer = requtils.getRenderer();
this.normal_buttons = [
new Renderer.NodeEditButton(this.renderer, null),
new Renderer.NodeRemoveButton(this.renderer, null),
new Renderer.NodeLinkButton(this.renderer, null),
new Renderer.NodeEnlargeButton(this.renderer, null),
new Renderer.NodeShrinkButton(this.renderer, null)
];
this.pending_delete_buttons = [
new Renderer.NodeRevertButton(this.renderer, null)
];
this.all_buttons = this.normal_buttons.concat(this.pending_delete_buttons);
for (var i = 0; i < this.all_buttons.length; i++) {
this.all_buttons[i].source_representation = this;
}
this.active_buttons = [];
} else {
this.active_buttons = this.all_buttons = [];
}
this.last_circle_radius = 1;
if (this.renderer.minimap) {
this.renderer.minimap.node_layer.activate();
this.minimap_circle = new paper.Path.Circle([0, 0], 1);
this.minimap_circle.__representation = this.renderer.minimap.miniframe.__representation;
this.renderer.minimap.node_group.addChild(this.minimap_circle);
}
},
redraw: function(_dontRedrawEdges) {
var _model_coords = new paper.Point(this.model.get("position")),
_baseRadius = this.options.node_size_base * Math.exp((this.model.get("size") || 0) * Utils._NODE_SIZE_STEP);
if (!this.is_dragging || !this.paper_coords) {
this.paper_coords = this.renderer.toPaperCoords(_model_coords);
}
this.circle_radius = _baseRadius * this.renderer.scale;
if (this.last_circle_radius !== this.circle_radius) {
this.all_buttons.forEach(function(b) {
b.setSectorSize();
});
this.circle.scale(this.circle_radius / this.last_circle_radius);
if (this.node_image) {
this.node_image.scale(this.circle_radius / this.last_circle_radius);
}
}
this.circle.position = this.paper_coords;
if (this.node_image) {
this.node_image.position = this.paper_coords.subtract(this.image_delta.multiply(this.circle_radius));
}
this.last_circle_radius = this.circle_radius;
var old_act_btn = this.active_buttons;
var opacity = 1;
if (this.model.get("delete_scheduled")) {
opacity = 0.5;
this.active_buttons = this.pending_delete_buttons;
this.circle.dashArray = [2,2];
} else {
opacity = 1;
this.active_buttons = this.normal_buttons;
this.circle.dashArray = null;
}
if (this.selected && this.renderer.isEditable()) {
if (old_act_btn !== this.active_buttons) {
old_act_btn.forEach(function(b) {
b.hide();
});
}
this.active_buttons.forEach(function(b) {
b.show();
});
}
if (this.node_image) {
this.node_image.opacity = this.highlighted ? opacity * 0.5 : (opacity - 0.01);
}
this.circle.fillColor = this.highlighted ? this.options.highlighted_node_fill_color : this.options.node_fill_color;
this.circle.opacity = this.options.show_node_circles ? opacity : 0.01;
var _text = this.model.get("title") || this.renkan.translate(this.options.label_untitled_nodes) || "";
_text = Utils.shortenText(_text, this.options.node_label_max_length);
if (typeof this.highlighted === "object") {
this.title.html(this.highlighted.replace(_(_text).escape(),'<span class="Rk-Highlighted">$1</span>'));
} else {
this.title.text(_text);
}
this.title.css({
left: this.paper_coords.x,
top: this.paper_coords.y + this.circle_radius * this.h_ratio + this.options.node_label_distance,
opacity: opacity
});
var _color = this.model.get("color") || (this.model.get("created_by") || Utils._USER_PLACEHOLDER(this.renkan)).get("color");
this.circle.strokeColor = _color;
var _pc = this.paper_coords;
this.all_buttons.forEach(function(b) {
b.moveTo(_pc);
});
var lastImage = this.img;
this.img = this.model.get("image");
if (this.img && this.img !== lastImage) {
this.showImage();
}
if (this.node_image && !this.img) {
this.node_image.remove();
delete this.node_image;
}
if (this.renderer.minimap) {
this.minimap_circle.fillColor = _color;
var minipos = this.renderer.toMinimapCoords(_model_coords),
miniradius = this.renderer.minimap.scale * _baseRadius,
minisize = new paper.Size([miniradius, miniradius]);
this.minimap_circle.fitBounds(minipos.subtract(minisize), minisize.multiply(2));
}
if (!_dontRedrawEdges) {
var _this = this;
_.each(
this.project.get("edges").filter(
function (ed) {
return ((ed.get("to") === _this.model) || (ed.get("from") === _this.model));
}
),
function(edge, index, list) {
var repr = _this.renderer.getRepresentationByModel(edge);
if (repr && typeof repr.from_representation !== "undefined" && typeof repr.from_representation.paper_coords !== "undefined" && typeof repr.to_representation !== "undefined" && typeof repr.to_representation.paper_coords !== "undefined") {
repr.redraw();
}
}
);
}
},
showImage: function() {
var _image = null;
if (typeof this.renderer.image_cache[this.img] === "undefined") {
_image = new Image();
this.renderer.image_cache[this.img] = _image;
_image.src = this.img;
} else {
_image = this.renderer.image_cache[this.img];
}
if (_image.width) {
if (this.node_image) {
this.node_image.remove();
}
this.renderer.node_layer.activate();
var width = _image.width,
height = _image.height,
clipPath = this.model.get("clip_path"),
hasClipPath = (typeof clipPath !== "undefined" && clipPath),
_clip = null,
baseRadius = null,
centerPoint = null;
if (hasClipPath) {
_clip = new paper.Path();
var instructions = clipPath.match(/[a-z][^a-z]+/gi) || [],
lastCoords = [0,0],
minX = Infinity,
minY = Infinity,
maxX = -Infinity,
maxY = -Infinity;
var transformCoords = function(tabc, relative) {
var newCoords = tabc.slice(1).map(function(v, k) {
var res = parseFloat(v),
isY = k % 2;
if (isY) {
res = ( res - 0.5 ) * height;
} else {
res = ( res - 0.5 ) * width;
}
if (relative) {
res += lastCoords[isY];
}
if (isY) {
minY = Math.min(minY, res);
maxY = Math.max(maxY, res);
} else {
minX = Math.min(minX, res);
maxX = Math.max(maxX, res);
}
return res;
});
lastCoords = newCoords.slice(-2);
return newCoords;
};
instructions.forEach(function(instr) {
var coords = instr.match(/([a-z]|[0-9.-]+)/ig) || [""];
switch(coords[0]) {
case "M":
_clip.moveTo(transformCoords(coords));
break;
case "m":
_clip.moveTo(transformCoords(coords, true));
break;
case "L":
_clip.lineTo(transformCoords(coords));
break;
case "l":
_clip.lineTo(transformCoords(coords, true));
break;
case "C":
_clip.cubicCurveTo(transformCoords(coords));
break;
case "c":
_clip.cubicCurveTo(transformCoords(coords, true));
break;
case "Q":
_clip.quadraticCurveTo(transformCoords(coords));
break;
case "q":
_clip.quadraticCurveTo(transformCoords(coords, true));
break;
}
});
baseRadius = Math[this.options.node_images_fill_mode ? "min" : "max"](maxX - minX, maxY - minY) / 2;
centerPoint = new paper.Point((maxX + minX) / 2, (maxY + minY) / 2);
if (!this.options.show_node_circles) {
this.h_ratio = (maxY - minY) / (2 * baseRadius);
}
} else {
baseRadius = Math[this.options.node_images_fill_mode ? "min" : "max"](width, height) / 2;
centerPoint = new paper.Point(0,0);
if (!this.options.show_node_circles) {
this.h_ratio = height / (2 * baseRadius);
}
}
var _raster = new paper.Raster(_image);
_raster.locked = true; // Disable mouse events on icon
if (hasClipPath) {
_raster = new paper.Group(_clip, _raster);
_raster.opacity = 0.99;
/* This is a workaround to allow clipping at group level
* If opacity was set to 1, paper.js would merge all clipping groups in one (known bug).
*/
_raster.clipped = true;
_clip.__representation = this;
}
if (this.options.clip_node_images) {
var _circleClip = new paper.Path.Circle(centerPoint, baseRadius);
_raster = new paper.Group(_circleClip, _raster);
_raster.opacity = 0.99;
_raster.clipped = true;
_circleClip.__representation = this;
}
this.image_delta = centerPoint.divide(baseRadius);
this.node_image = _raster;
this.node_image.__representation = _this;
this.node_image.scale(this.circle_radius / baseRadius);
this.node_image.position = this.paper_coords.subtract(this.image_delta.multiply(this.circle_radius));
this.redraw();
this.renderer.throttledPaperDraw();
} else {
var _this = this;
$(_image).on("load", function() {
_this.showImage();
});
}
},
paperShift: function(_delta) {
if (this.options.editor_mode) {
if (!this.renkan.read_only) {
this.is_dragging = true;
this.paper_coords = this.paper_coords.add(_delta);
this.redraw();
}
} else {
this.renderer.paperShift(_delta);
}
},
openEditor: function() {
this.renderer.removeRepresentationsOfType("editor");
var _editor = this.renderer.addRepresentation("NodeEditor",null);
_editor.source_representation = this;
_editor.draw();
},
select: function() {
this.selected = true;
this.circle.strokeWidth = this.options.selected_node_stroke_width;
if (this.renderer.isEditable()) {
this.active_buttons.forEach(function(b) {
b.show();
});
}
var _uri = this.model.get("uri");
if (_uri) {
$('.Rk-Bin-Item').each(function() {
var _el = $(this);
if (_el.attr("data-uri") === _uri) {
_el.addClass("selected");
}
});
}
if (!this.options.editor_mode) {
this.openEditor();
}
if (this.renderer.minimap) {
this.minimap_circle.strokeWidth = this.options.minimap_highlight_weight;
this.minimap_circle.strokeColor = this.options.minimap_highlight_color;
}
this._super("select");
},
unselect: function(_newTarget) {
if (!_newTarget || _newTarget.source_representation !== this) {
this.selected = false;
this.all_buttons.forEach(function(b) {
b.hide();
});
this.circle.strokeWidth = this.options.node_stroke_width;
$('.Rk-Bin-Item').removeClass("selected");
if (this.renderer.minimap) {
this.minimap_circle.strokeColor = undefined;
}
this._super("unselect");
}
},
highlight: function(textToReplace) {
var hlvalue = textToReplace || true;
if (this.highlighted === hlvalue) {
return;
}
this.highlighted = hlvalue;
this.redraw();
this.renderer.throttledPaperDraw();
},
unhighlight: function() {
if (!this.highlighted) {
return;
}
this.highlighted = false;
this.redraw();
this.renderer.throttledPaperDraw();
},
saveCoords: function() {
var _coords = this.renderer.toModelCoords(this.paper_coords),
_data = {
position: {
x: _coords.x,
y: _coords.y
}
};
if (this.renderer.isEditable()) {
this.model.set(_data);
}
},
mousedown: function(_event, _isTouch) {
if (_isTouch) {
this.renderer.unselectAll();
this.select();
}
},
mouseup: function(_event, _isTouch) {
if (this.renderer.is_dragging && this.renderer.isEditable()) {
this.saveCoords();
} else {
if (!_isTouch && !this.model.get("delete_scheduled")) {
this.openEditor();
}
this.model.trigger("clicked");
}
this.renderer.click_target = null;
this.renderer.is_dragging = false;
this.is_dragging = false;
},
destroy: function(_event) {
this._super("destroy");
this.all_buttons.forEach(function(b) {
b.destroy();
});
this.circle.remove();
this.title.remove();
if (this.renderer.minimap) {
this.minimap_circle.remove();
}
if (this.node_image) {
this.node_image.remove();
}
}
});
return NodeRepr;
});