diff -r 67f3a24a7c01 -r fa8035885814 client/js/renderer/noderepr.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/js/renderer/noderepr.js Mon May 05 17:43:37 2014 +0200 @@ -0,0 +1,424 @@ +/* paper-renderer.js */ +"use strict"; + +define(['jquery', 'underscore', 'requtils', 'renderer/baserepresentation'], function ($, _, requtils, BaseRepresentation) { + + 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 = $('
').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 = .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 * .5 : (opacity - .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 : .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(),'$1')); + } 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 - .5 ) * height; + } else { + res = ( res - .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 = .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 = .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; + +}); +