| changeset 199 | aff7eb118f7d |
| parent 198 | d3737b90a66b |
| child 207 | 583dc519c5fa |
| 198:d3737b90a66b | 199:aff7eb118f7d |
|---|---|
6 |
6 |
7 var Rkns = root.Rkns, |
7 var Rkns = root.Rkns, |
8 _ = Rkns._, |
8 _ = Rkns._, |
9 $ = Rkns.$; |
9 $ = Rkns.$; |
10 |
10 |
11 /* Rkns.Renderer Object */ |
|
12 |
|
13 /* This object contains constants, utility functions and classes for Renkan's Graph Manipulation GUI */ |
|
14 |
|
11 var Renderer = Rkns.Renderer = {}, |
15 var Renderer = Rkns.Renderer = {}, |
16 /* The minimum distance (in pixels) the mouse has to move to consider an element was dragged */ |
|
12 _MIN_DRAG_DISTANCE = 2, |
17 _MIN_DRAG_DISTANCE = 2, |
18 /* Distance between the inner and outer radius of buttons that appear when hovering on a node */ |
|
13 _NODE_BUTTON_WIDTH = 40, |
19 _NODE_BUTTON_WIDTH = 40, |
14 _EDGE_BUTTON_INNER = 2, |
20 _EDGE_BUTTON_INNER = 2, |
15 _EDGE_BUTTON_OUTER = 40, |
21 _EDGE_BUTTON_OUTER = 40, |
22 /* Constants used to know if a specific action is to be performed when clicking on the canvas */ |
|
16 _CLICKMODE_ADDNODE = 1, |
23 _CLICKMODE_ADDNODE = 1, |
17 _CLICKMODE_STARTEDGE = 2, |
24 _CLICKMODE_STARTEDGE = 2, |
18 _CLICKMODE_ENDEDGE = 3, |
25 _CLICKMODE_ENDEDGE = 3, |
26 /* Node size step: Used to calculate the size change when clicking the +/- buttons */ |
|
19 _NODE_SIZE_STEP = Math.LN2/4, |
27 _NODE_SIZE_STEP = Math.LN2/4, |
20 _MIN_SCALE = 1/20, |
28 _MIN_SCALE = 1/20, |
21 _MAX_SCALE = 20, |
29 _MAX_SCALE = 20, |
22 _MOUSEMOVE_RATE = 80, |
30 _MOUSEMOVE_RATE = 80, |
23 _DOUBLETAP_DELAY = 800, |
31 _DOUBLETAP_DELAY = 800, |
32 /* Maximum distance in pixels (squared, to reduce calculations) |
|
33 * between two taps when double-tapping on a touch terminal */ |
|
24 _DOUBLETAP_DISTANCE = 20*20, |
34 _DOUBLETAP_DISTANCE = 20*20, |
35 /* A placeholder so a default colour is displayed when a node has a null value for its user property */ |
|
25 _USER_PLACEHOLDER = function(_renkan) { |
36 _USER_PLACEHOLDER = function(_renkan) { |
26 return { |
37 return { |
27 color: _renkan.options.default_user_color, |
38 color: _renkan.options.default_user_color, |
28 title: _renkan.translate("(unknown user)"), |
39 title: _renkan.translate("(unknown user)"), |
29 get: function(attr) { |
40 get: function(attr) { |
30 return this[attr] || false; |
41 return this[attr] || false; |
31 } |
42 } |
32 }; |
43 }; |
33 }, |
44 }, |
45 /* The code for the "Drag and Add Bookmarklet", slightly minified and with whitespaces removed, though |
|
46 * it doesn't seem that it's still a requirement in newer browsers (i.e. the ones compatibles with canvas drawing) |
|
47 */ |
|
34 _BOOKMARKLET_CODE = function(_renkan) { |
48 _BOOKMARKLET_CODE = function(_renkan) { |
35 return "(function(a,b,c,d,e,f,h,i,j,k,l,m,n,o,p,q,r){a=document;b=a.body;c=a.location.href;j='draggable';m='text/x-iri-';d=a.createElement('div');d.innerHTML='<p_style=\"position:fixed;top:0;right:0;font:bold_18px_sans-serif;color:#fff;background:#909;padding:10px;z-index:100000;\">" |
49 return "(function(a,b,c,d,e,f,h,i,j,k,l,m,n,o,p,q,r){a=document;b=a.body;c=a.location.href;j='draggable';m='text/x-iri-';d=a.createElement('div');d.innerHTML='<p_style=\"position:fixed;top:0;right:0;font:bold_18px_sans-serif;color:#fff;background:#909;padding:10px;z-index:100000;\">" |
36 + _renkan.translate("Drag items from this website, drop them in Renkan").replace(/ /g,"_") |
50 + _renkan.translate("Drag items from this website, drop them in Renkan").replace(/ /g,"_") |
37 + "</p>'.replace(/_/g,String.fromCharCode(32));b.appendChild(d);e=[{r:/https?:\\/\\/[^\\/]*twitter\\.com\\//,s:'.tweet',n:'twitter'},{r:/https?:\\/\\/[^\\/]*google\\.[^\\/]+\\//,s:'.g',n:'google'},{r:/https?:\\/\\/[^\\/]*lemonde\\.fr\\//,s:'[data-vr-contentbox]',n:'lemonde'}];f=false;e.forEach(function(g){if(g.r.test(c)){f=g;}});if(f){h=function(){Array.prototype.forEach.call(a.querySelectorAll(f.s),function(i){i[j]=true;k=i.style;k.borderWidth='2px';k.borderColor='#909';k.borderStyle='solid';k.backgroundColor='rgba(200,0,180,.1)';})};window.setInterval(h,500);h();};a.addEventListener('dragstart',function(k){l=k.dataTransfer;l.setData(m+'source-uri',c);l.setData(m+'source-title',a.title);n=k.target;if(f){o=n;while(!o.attributes[j]){o=o.parentNode;if(o==b){break;}}}if(f&&o.attributes[j]){p=o.cloneNode(true);l.setData(m+'specific-site',f.n)}else{q=a.getSelection();if(q.type==='Range'||!q.type){p=q.getRangeAt(0).cloneContents();}else{p=n.cloneNode();}}r=a.createElement('div');r.appendChild(p);l.setData('text/x-iri-selected-text',r.textContent.trim());l.setData('text/x-iri-selected-html',r.innerHTML);},false);})();"; |
51 + "</p>'.replace(/_/g,String.fromCharCode(32));b.appendChild(d);e=[{r:/https?:\\/\\/[^\\/]*twitter\\.com\\//,s:'.tweet',n:'twitter'},{r:/https?:\\/\\/[^\\/]*google\\.[^\\/]+\\//,s:'.g',n:'google'},{r:/https?:\\/\\/[^\\/]*lemonde\\.fr\\//,s:'[data-vr-contentbox]',n:'lemonde'}];f=false;e.forEach(function(g){if(g.r.test(c)){f=g;}});if(f){h=function(){Array.prototype.forEach.call(a.querySelectorAll(f.s),function(i){i[j]=true;k=i.style;k.borderWidth='2px';k.borderColor='#909';k.borderStyle='solid';k.backgroundColor='rgba(200,0,180,.1)';})};window.setInterval(h,500);h();};a.addEventListener('dragstart',function(k){l=k.dataTransfer;l.setData(m+'source-uri',c);l.setData(m+'source-title',a.title);n=k.target;if(f){o=n;while(!o.attributes[j]){o=o.parentNode;if(o==b){break;}}}if(f&&o.attributes[j]){p=o.cloneNode(true);l.setData(m+'specific-site',f.n)}else{q=a.getSelection();if(q.type==='Range'||!q.type){p=q.getRangeAt(0).cloneContents();}else{p=n.cloneNode();}}r=a.createElement('div');r.appendChild(p);l.setData('text/x-iri-selected-text',r.textContent.trim());l.setData('text/x-iri-selected-html',r.innerHTML);},false);})();"; |
38 }, |
52 }, |
53 /* Shortens text to the required length then adds ellipsis */ |
|
39 shortenText = function(_text, _maxlength) { |
54 shortenText = function(_text, _maxlength) { |
40 return (_text.length > _maxlength ? (_text.substr(0,_maxlength) + '…') : _text); |
55 return (_text.length > _maxlength ? (_text.substr(0,_maxlength) + '…') : _text); |
41 }, |
56 }, |
57 /* Drawing an edit box with an arrow and positioning the edit box according to the position of the node/edge being edited |
|
58 * Called by Rkns.Renderer.NodeEditor and Rkns.Renderer.EdgeEditor */ |
|
42 drawEditBox = function(_options, _coords, _path, _xmargin, _selector) { |
59 drawEditBox = function(_options, _coords, _path, _xmargin, _selector) { |
43 _selector.css({ |
60 _selector.css({ |
44 width: ( _options.tooltip_width - 2* _options.tooltip_padding ), |
61 width: ( _options.tooltip_width - 2* _options.tooltip_padding ), |
45 }); |
62 }); |
46 var _height = _selector.outerHeight() + 2* _options.tooltip_padding, |
63 var _height = _selector.outerHeight() + 2* _options.tooltip_padding, |
81 top: (_options.tooltip_padding + _top) |
98 top: (_options.tooltip_padding + _top) |
82 }); |
99 }); |
83 return _path; |
100 return _path; |
84 }; |
101 }; |
85 |
102 |
103 /* Rkns.Renderer._BaseRepresentation Class */ |
|
104 |
|
105 /* In Renkan, a "Representation" is a sort of ViewModel (in the MVVM paradigm) and bridges the gap between |
|
106 * models (written with Backbone.js) and the view (written with Paper.js) |
|
107 * Renkan's representations all inherit from Rkns.Renderer._BaseRepresentation '*/ |
|
108 |
|
86 var _BaseRepresentation = Renderer._BaseRepresentation = function(_renderer, _model) { |
109 var _BaseRepresentation = Renderer._BaseRepresentation = function(_renderer, _model) { |
87 if (typeof _renderer !== "undefined") { |
110 if (typeof _renderer !== "undefined") { |
88 this.renderer = _renderer; |
111 this.renderer = _renderer; |
89 this.renkan = _renderer.renkan; |
112 this.renkan = _renderer.renkan; |
90 this.project = _renderer.renkan.project; |
113 this.project = _renderer.renkan.project; |
113 this.model.on("unselect", this._unselectBinding ); |
136 this.model.on("unselect", this._unselectBinding ); |
114 } |
137 } |
115 } |
138 } |
116 }; |
139 }; |
117 |
140 |
141 /* Rkns.Renderer._BaseRepresentation Methods */ |
|
142 |
|
118 _(_BaseRepresentation.prototype).extend({ |
143 _(_BaseRepresentation.prototype).extend({ |
119 _super: function(_func) { |
144 _super: function(_func) { |
120 return _BaseRepresentation.prototype[_func].apply(this, Array.prototype.slice.call(arguments, 1)); |
145 return _BaseRepresentation.prototype[_func].apply(this, Array.prototype.slice.call(arguments, 1)); |
121 }, |
146 }, |
122 redraw: function() {}, |
147 redraw: function() {}, |
149 this.model.off("unselect", this._unselectBinding ); |
174 this.model.off("unselect", this._unselectBinding ); |
150 } |
175 } |
151 } |
176 } |
152 }); |
177 }); |
153 |
178 |
154 /* */ |
179 /* End of Rkns.Renderer._BaseRepresentation Class */ |
180 |
|
181 /* Rkns.Renderer._BaseButton Class */ |
|
182 |
|
183 /* BaseButton is extended by contextual buttons that appear when hovering on nodes and edges */ |
|
155 |
184 |
156 var _BaseButton = Renderer._BaseButton = Rkns.Utils.inherit(_BaseRepresentation); |
185 var _BaseButton = Renderer._BaseButton = Rkns.Utils.inherit(_BaseRepresentation); |
157 |
186 |
158 _(_BaseButton.prototype).extend({ |
187 _(_BaseButton.prototype).extend({ |
159 moveTo: function(_pos) { |
188 moveTo: function(_pos) { |
177 destroy: function() { |
206 destroy: function() { |
178 this.sector.destroy(); |
207 this.sector.destroy(); |
179 } |
208 } |
180 }); |
209 }); |
181 |
210 |
182 /* */ |
211 /* End of Rkns.Renderer._BaseButton Class */ |
212 |
|
213 /* Rkns.Renderer.Node Class */ |
|
214 |
|
215 /* The representation for the node : A circle, with an image inside and a text label underneath. |
|
216 * The circle and the image are drawn on canvas and managed by Paper.js. |
|
217 * The text label is an HTML node, managed by jQuery. */ |
|
183 |
218 |
184 var NodeRepr = Renderer.Node = Rkns.Utils.inherit(_BaseRepresentation); |
219 var NodeRepr = Renderer.Node = Rkns.Utils.inherit(_BaseRepresentation); |
185 |
220 |
186 _(NodeRepr.prototype).extend({ |
221 _(NodeRepr.prototype).extend({ |
187 _init: function() { |
222 _init: function() { |
383 } |
418 } |
384 return res; |
419 return res; |
385 }); |
420 }); |
386 lastCoords = newCoords.slice(-2); |
421 lastCoords = newCoords.slice(-2); |
387 return newCoords; |
422 return newCoords; |
388 } |
423 }; |
389 |
424 |
390 instructions.forEach(function(instr) { |
425 instructions.forEach(function(instr) { |
391 var coords = instr.match(/([a-z]|[0-9.-]+)/ig) || [""]; |
426 var coords = instr.match(/([a-z]|[0-9.-]+)/ig) || [""]; |
392 switch(coords[0]) { |
427 switch(coords[0]) { |
393 case "M": |
428 case "M": |
1084 _model.set("size", _newsize); |
1119 _model.set("size", _newsize); |
1085 paper.view.draw(); |
1120 paper.view.draw(); |
1086 } else { |
1121 } else { |
1087 closeEditor(); |
1122 closeEditor(); |
1088 } |
1123 } |
1089 } |
1124 }; |
1090 |
1125 |
1091 this.editor_$.find(".Rk-Edit-Size-Down").click(function() { |
1126 this.editor_$.find(".Rk-Edit-Size-Down").click(function() { |
1092 shiftSize(-1); |
1127 shiftSize(-1); |
1093 return false; |
1128 return false; |
1094 }); |
1129 }); |
1099 } else { |
1134 } else { |
1100 if (typeof this.source_representation.highlighted === "object") { |
1135 if (typeof this.source_representation.highlighted === "object") { |
1101 var titlehtml = this.source_representation.highlighted.replace(_(_model.get("title")).escape(),'<span class="Rk-Highlighted">$1</span>'); |
1136 var titlehtml = this.source_representation.highlighted.replace(_(_model.get("title")).escape(),'<span class="Rk-Highlighted">$1</span>'); |
1102 this.editor_$.find(".Rk-Display-Title" + (_model.get("uri") ? " a" : "")).html(titlehtml); |
1137 this.editor_$.find(".Rk-Display-Title" + (_model.get("uri") ? " a" : "")).html(titlehtml); |
1103 if (this.options.show_node_tooltip_description) { |
1138 if (this.options.show_node_tooltip_description) { |
1104 this.editor_$.find(".Rk-Display-Description").html(this.source_representation.highlighted.replace(_(_model.get("description")).escape(),'<span class="Rk-Highlighted">$1</span>')) |
1139 this.editor_$.find(".Rk-Display-Description").html(this.source_representation.highlighted.replace(_(_model.get("description")).escape(),'<span class="Rk-Highlighted">$1</span>')); |
1105 } |
1140 } |
1106 } |
1141 } |
1107 } |
1142 } |
1108 this.editor_$.find("img").load(function() { |
1143 this.editor_$.find("img").load(function() { |
1109 _this.redraw(); |
1144 _this.redraw(); |
1746 var bindClick = function(selector, fname) { |
1781 var bindClick = function(selector, fname) { |
1747 _this.$.find(selector).click(function(evt) { |
1782 _this.$.find(selector).click(function(evt) { |
1748 _this[fname](evt); |
1783 _this[fname](evt); |
1749 return false; |
1784 return false; |
1750 }); |
1785 }); |
1751 } |
1786 }; |
1752 |
1787 |
1753 bindClick(".Rk-ZoomOut", "zoomOut"); |
1788 bindClick(".Rk-ZoomOut", "zoomOut"); |
1754 bindClick(".Rk-ZoomIn", "zoomIn"); |
1789 bindClick(".Rk-ZoomIn", "zoomIn"); |
1755 this.$.find(".Rk-CurrentUser").mouseenter( |
1790 this.$.find(".Rk-CurrentUser").mouseenter( |
1756 function() { _this.$.find(".Rk-UserList").slideDown(); } |
1791 function() { _this.$.find(".Rk-UserList").slideDown(); } |