source: other-projects/nz-flag-design/trunk/design-2d/Original editor.method.ac/method-draw/extensions/ext-connector.js@ 29468

Last change on this file since 29468 was 29468, checked in by sjs49, 9 years ago

Initial commit for editor.method.ac for flag design

  • Property svn:executable set to *
File size: 15.5 KB
Line 
1/*
2 * ext-connector.js
3 *
4 * Licensed under the Apache License, Version 2
5 *
6 * Copyright(c) 2010 Alexis Deveria
7 *
8 */
9
10methodDraw.addExtension("Connector", function(S) {
11 var svgcontent = S.svgcontent,
12 svgroot = S.svgroot,
13 getNextId = S.getNextId,
14 getElem = S.getElem,
15 addElem = S.addSvgElementFromJson,
16 selManager = S.selectorManager,
17 curConfig = methodDraw.curConfig,
18 started = false,
19 start_x,
20 start_y,
21 cur_line,
22 start_elem,
23 end_elem,
24 connections = [],
25 conn_sel = ".se_connector",
26 se_ns,
27// connect_str = "-SE_CONNECT-",
28 selElems = [];
29
30 elData = $.data;
31
32 var lang_list = {
33 "en":[
34 {"id": "mode_connect", "title": "Connect two objects" }
35 ],
36 "fr":[
37 {"id": "mode_connect", "title": "Connecter deux objets"}
38 ]
39 };
40
41 function getOffset(side, line) {
42 var give_offset = !!line.getAttribute('marker-' + side);
43// var give_offset = $(line).data(side+'_off');
44
45 // TODO: Make this number (5) be based on marker width/height
46 var size = line.getAttribute('stroke-width') * 5;
47 return give_offset ? size : 0;
48 }
49
50 function showPanel(on) {
51 var conn_rules = $('#connector_rules');
52 if(!conn_rules.length) {
53 conn_rules = $('<style id="connector_rules"><\/style>').appendTo('head');
54 }
55 conn_rules.text(!on?"":"#tool_clone, #tool_topath, #tool_angle, #xy_panel { display: none !important; }");
56 $('#connector_panel').toggle(on);
57 }
58
59 function setPoint(elem, pos, x, y, setMid) {
60 var pts = elem.points;
61 var pt = svgroot.createSVGPoint();
62 pt.x = x;
63 pt.y = y;
64 if(pos === 'end') pos = pts.numberOfItems-1;
65 // TODO: Test for this on init, then use alt only if needed
66 try {
67 pts.replaceItem(pt, pos);
68 } catch(err) {
69 // Should only occur in FF which formats points attr as "n,n n,n", so just split
70 var pt_arr = elem.getAttribute("points").split(" ");
71 for(var i=0; i< pt_arr.length; i++) {
72 if(i == pos) {
73 pt_arr[i] = x + ',' + y;
74 }
75 }
76 elem.setAttribute("points",pt_arr.join(" "));
77 }
78
79 if(setMid) {
80 // Add center point
81 var pt_start = pts.getItem(0);
82 var pt_end = pts.getItem(pts.numberOfItems-1);
83 setPoint(elem, 1, (pt_end.x + pt_start.x)/2, (pt_end.y + pt_start.y)/2);
84 }
85 }
86
87 function updateLine(diff_x, diff_y) {
88 // Update line with element
89 var i = connections.length;
90 while(i--) {
91 var conn = connections[i];
92 var line = conn.connector;
93 var elem = conn.elem;
94
95 var pre = conn.is_start?'start':'end';
96// var sw = line.getAttribute('stroke-width') * 5;
97
98 // Update bbox for this element
99 var bb = elData(line, pre+'_bb');
100 bb.x = conn.start_x + diff_x;
101 bb.y = conn.start_y + diff_y;
102 elData(line, pre+'_bb', bb);
103
104 var alt_pre = conn.is_start?'end':'start';
105
106 // Get center pt of connected element
107 var bb2 = elData(line, alt_pre+'_bb');
108 var src_x = bb2.x + bb2.width/2;
109 var src_y = bb2.y + bb2.height/2;
110
111 // Set point of element being moved
112 var pt = getBBintersect(src_x, src_y, bb, getOffset(pre, line)); // $(line).data(pre+'_off')?sw:0
113 setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
114
115 // Set point of connected element
116 var pt2 = getBBintersect(pt.x, pt.y, elData(line, alt_pre + '_bb'), getOffset(alt_pre, line));
117 setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
118
119 }
120 }
121
122 function findConnectors(elems) {
123 if(!elems) elems = selElems;
124 var connectors = $(svgcontent).find(conn_sel);
125 connections = [];
126
127 // Loop through connectors to see if one is connected to the element
128 connectors.each(function() {
129 var start = elData(this, "c_start");
130 var end = elData(this, "c_end");
131
132 var parts = [getElem(start), getElem(end)];
133 for(var i=0; i<2; i++) {
134 var c_elem = parts[i];
135 var add_this = false;
136 // The connected element might be part of a selected group
137 $(c_elem).parents().each(function() {
138 if($.inArray(this, elems) !== -1) {
139 // Pretend this element is selected
140 add_this = true;
141 }
142 });
143
144 if(!c_elem || !c_elem.parentNode) {
145 $(this).remove();
146 continue;
147 }
148 if($.inArray(c_elem, elems) !== -1 || add_this) {
149 var bb = svgCanvas.getStrokedBBox([c_elem]);
150 connections.push({
151 elem: c_elem,
152 connector: this,
153 is_start: (i === 0),
154 start_x: bb.x,
155 start_y: bb.y
156 });
157 }
158 }
159 });
160 }
161
162 function updateConnectors(elems) {
163 // Updates connector lines based on selected elements
164 // Is not used on mousemove, as it runs getStrokedBBox every time,
165 // which isn't necessary there.
166 findConnectors(elems);
167 if(connections.length) {
168 // Update line with element
169 var i = connections.length;
170 while(i--) {
171 var conn = connections[i];
172 var line = conn.connector;
173 var elem = conn.elem;
174
175 var sw = line.getAttribute('stroke-width') * 5;
176 var pre = conn.is_start?'start':'end';
177
178 // Update bbox for this element
179 var bb = svgCanvas.getStrokedBBox([elem]);
180 bb.x = conn.start_x;
181 bb.y = conn.start_y;
182 elData(line, pre+'_bb', bb);
183 var add_offset = elData(line, pre+'_off');
184
185 var alt_pre = conn.is_start?'end':'start';
186
187 // Get center pt of connected element
188 var bb2 = elData(line, alt_pre+'_bb');
189 var src_x = bb2.x + bb2.width/2;
190 var src_y = bb2.y + bb2.height/2;
191
192 // Set point of element being moved
193 var pt = getBBintersect(src_x, src_y, bb, getOffset(pre, line));
194 setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
195
196 // Set point of connected element
197 var pt2 = getBBintersect(pt.x, pt.y, elData(line, alt_pre + '_bb'), getOffset(alt_pre, line));
198 setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
199
200 // Update points attribute manually for webkit
201 if(navigator.userAgent.indexOf('AppleWebKit') != -1) {
202 var pts = line.points;
203 var len = pts.numberOfItems;
204 var pt_arr = Array(len);
205 for(var j=0; j< len; j++) {
206 var pt = pts.getItem(j);
207 pt_arr[j] = pt.x + ',' + pt.y;
208 }
209 line.setAttribute("points",pt_arr.join(" "));
210 }
211
212 }
213 }
214 }
215
216 function getBBintersect(x, y, bb, offset) {
217 if(offset) {
218 offset -= 0;
219 bb = $.extend({}, bb);
220 bb.width += offset;
221 bb.height += offset;
222 bb.x -= offset/2;
223 bb.y -= offset/2;
224 }
225
226 var mid_x = bb.x + bb.width/2;
227 var mid_y = bb.y + bb.height/2;
228 var len_x = x - mid_x;
229 var len_y = y - mid_y;
230
231 var slope = Math.abs(len_y/len_x);
232
233 var ratio;
234
235 if(slope < bb.height/bb.width) {
236 ratio = (bb.width/2) / Math.abs(len_x);
237 } else {
238 ratio = (bb.height/2) / Math.abs(len_y);
239 }
240
241
242 return {
243 x: mid_x + len_x * ratio,
244 y: mid_y + len_y * ratio
245 }
246 }
247
248 // Do once
249 (function() {
250 var gse = svgCanvas.groupSelectedElements;
251
252 svgCanvas.groupSelectedElements = function() {
253 svgCanvas.removeFromSelection($(conn_sel).toArray());
254 return gse.apply(this, arguments);
255 }
256
257 var mse = svgCanvas.moveSelectedElements;
258
259 svgCanvas.moveSelectedElements = function() {
260 svgCanvas.removeFromSelection($(conn_sel).toArray());
261 var cmd = mse.apply(this, arguments);
262 updateConnectors();
263 return cmd;
264 }
265
266 se_ns = svgCanvas.getEditorNS();
267 }());
268
269 // Do on reset
270 function init() {
271 // Make sure all connectors have data set
272 $(svgcontent).find('*').each(function() {
273 var conn = this.getAttributeNS(se_ns, "connector");
274 if(conn) {
275 this.setAttribute('class', conn_sel.substr(1));
276 var conn_data = conn.split(' ');
277 var sbb = svgCanvas.getStrokedBBox([getElem(conn_data[0])]);
278 var ebb = svgCanvas.getStrokedBBox([getElem(conn_data[1])]);
279 $(this).data('c_start',conn_data[0])
280 .data('c_end',conn_data[1])
281 .data('start_bb', sbb)
282 .data('end_bb', ebb);
283 svgCanvas.getEditorNS(true);
284 }
285 });
286// updateConnectors();
287 }
288
289// $(svgroot).parent().mousemove(function(e) {
290// // if(started
291// // || svgCanvas.getMode() != "connector"
292// // || e.target.parentNode.parentNode != svgcontent) return;
293//
294// console.log('y')
295// // if(e.target.parentNode.parentNode === svgcontent) {
296// //
297// // }
298// });
299
300 return {
301 name: "Connector",
302 svgicons: "images/conn.svg",
303 buttons: [{
304 id: "mode_connect",
305 type: "mode",
306 icon: "images/cut.png",
307 title: "Connect two objects",
308 includeWith: {
309 button: '#tool_line',
310 isDefault: false,
311 position: 1
312 },
313 events: {
314 'click': function() {
315 svgCanvas.setMode("connector");
316 }
317 }
318 }],
319 addLangData: function(lang) {
320 return {
321 data: lang_list[lang]
322 };
323 },
324 mouseDown: function(opts) {
325 var e = opts.event;
326 start_x = opts.start_x,
327 start_y = opts.start_y;
328 var mode = svgCanvas.getMode();
329
330 if(mode == "connector") {
331
332 if(started) return;
333
334 var mouse_target = e.target;
335
336 var parents = $(mouse_target).parents();
337
338 if($.inArray(svgcontent, parents) != -1) {
339 // Connectable element
340
341 // If child of foreignObject, use parent
342 var fo = $(mouse_target).closest("foreignObject");
343 start_elem = fo.length ? fo[0] : mouse_target;
344
345 // Get center of source element
346 var bb = svgCanvas.getStrokedBBox([start_elem]);
347 var x = bb.x + bb.width/2;
348 var y = bb.y + bb.height/2;
349
350 started = true;
351 cur_line = addElem({
352 "element": "polyline",
353 "attr": {
354 "id": getNextId(),
355 "points": (x+','+y+' '+x+','+y+' '+start_x+','+start_y),
356 "stroke": '#' + curConfig.initStroke.color,
357 "stroke-width": (!start_elem.stroke_width || start_elem.stroke_width == 0) ? curConfig.initStroke.width : start_elem.stroke_width,
358 "fill": "none",
359 "opacity": curConfig.initStroke.opacity,
360 "style": "pointer-events:none"
361 }
362 });
363 elData(cur_line, 'start_bb', bb);
364 }
365 return {
366 started: true
367 };
368 } else if(mode == "select") {
369 findConnectors();
370 }
371 },
372 mouseMove: function(opts) {
373 var zoom = svgCanvas.getZoom();
374 var e = opts.event;
375 var x = opts.mouse_x/zoom;
376 var y = opts.mouse_y/zoom;
377
378 var diff_x = x - start_x,
379 diff_y = y - start_y;
380
381 var mode = svgCanvas.getMode();
382
383 if(mode == "connector" && started) {
384
385 var sw = cur_line.getAttribute('stroke-width') * 3;
386 // Set start point (adjusts based on bb)
387 var pt = getBBintersect(x, y, elData(cur_line, 'start_bb'), getOffset('start', cur_line));
388 start_x = pt.x;
389 start_y = pt.y;
390
391 setPoint(cur_line, 0, pt.x, pt.y, true);
392
393 // Set end point
394 setPoint(cur_line, 'end', x, y, true);
395 } else if(mode == "select") {
396 var slen = selElems.length;
397
398 while(slen--) {
399 var elem = selElems[slen];
400 // Look for selected connector elements
401 if(elem && elData(elem, 'c_start')) {
402 // Remove the "translate" transform given to move
403 svgCanvas.removeFromSelection([elem]);
404 svgCanvas.getTransformList(elem).clear();
405
406 }
407 }
408 if(connections.length) {
409 updateLine(diff_x, diff_y);
410
411
412 }
413 }
414 },
415 mouseUp: function(opts) {
416 var zoom = svgCanvas.getZoom();
417 var e = opts.event,
418 x = opts.mouse_x/zoom,
419 y = opts.mouse_y/zoom,
420 mouse_target = e.target;
421
422 if(svgCanvas.getMode() == "connector") {
423 var fo = $(mouse_target).closest("foreignObject");
424 if(fo.length) mouse_target = fo[0];
425
426 var parents = $(mouse_target).parents();
427
428 if(mouse_target == start_elem) {
429 // Start line through click
430 started = true;
431 return {
432 keep: true,
433 element: null,
434 started: started
435 }
436 } else if($.inArray(svgcontent, parents) === -1) {
437 // Not a valid target element, so remove line
438 $(cur_line).remove();
439 started = false;
440 return {
441 keep: false,
442 element: null,
443 started: started
444 }
445 } else {
446 // Valid end element
447 end_elem = mouse_target;
448
449 var start_id = start_elem.id, end_id = end_elem.id;
450 var conn_str = start_id + " " + end_id;
451 var alt_str = end_id + " " + start_id;
452 // Don't create connector if one already exists
453 var dupe = $(svgcontent).find(conn_sel).filter(function() {
454 var conn = this.getAttributeNS(se_ns, "connector");
455 if(conn == conn_str || conn == alt_str) return true;
456 });
457 if(dupe.length) {
458 $(cur_line).remove();
459 return {
460 keep: false,
461 element: null,
462 started: false
463 }
464 }
465
466 var bb = svgCanvas.getStrokedBBox([end_elem]);
467
468 var pt = getBBintersect(start_x, start_y, bb, getOffset('start', cur_line));
469 setPoint(cur_line, 'end', pt.x, pt.y, true);
470 $(cur_line)
471 .data("c_start", start_id)
472 .data("c_end", end_id)
473 .data("end_bb", bb);
474 se_ns = svgCanvas.getEditorNS(true);
475 cur_line.setAttributeNS(se_ns, "se:connector", conn_str);
476 cur_line.setAttribute('class', conn_sel.substr(1));
477 cur_line.setAttribute('opacity', 1);
478 svgCanvas.addToSelection([cur_line]);
479 svgCanvas.moveToBottomSelectedElement();
480 selManager.requestSelector(cur_line).showGrips(false);
481 started = false;
482 return {
483 keep: true,
484 element: cur_line,
485 started: started
486 }
487 }
488 }
489 },
490 selectedChanged: function(opts) {
491 // TODO: Find better way to skip operations if no connectors are in use
492 if(!$(svgcontent).find(conn_sel).length) return;
493
494 if(svgCanvas.getMode() == 'connector') {
495 svgCanvas.setMode('select');
496 }
497
498 // Use this to update the current selected elements
499 selElems = opts.elems;
500
501 var i = selElems.length;
502
503 while(i--) {
504 var elem = selElems[i];
505 if(elem && elData(elem, 'c_start')) {
506 selManager.requestSelector(elem).showGrips(false);
507 if(opts.selectedElement && !opts.multiselected) {
508 // TODO: Set up context tools and hide most regular line tools
509 showPanel(true);
510 } else {
511 showPanel(false);
512 }
513 } else {
514 showPanel(false);
515 }
516 }
517 updateConnectors();
518 },
519 elementChanged: function(opts) {
520 var elem = opts.elems[0];
521 if (elem && elem.tagName == 'svg' && elem.id == "svgcontent") {
522 // Update svgcontent (can change on import)
523 svgcontent = elem;
524 init();
525 }
526
527 // Has marker, so change offset
528 if(elem && (
529 elem.getAttribute("marker-start") ||
530 elem.getAttribute("marker-mid") ||
531 elem.getAttribute("marker-end")
532 )) {
533 var start = elem.getAttribute("marker-start");
534 var mid = elem.getAttribute("marker-mid");
535 var end = elem.getAttribute("marker-end");
536 cur_line = elem;
537 $(elem)
538 .data("start_off", !!start)
539 .data("end_off", !!end);
540
541 if(elem.tagName == "line" && mid) {
542 // Convert to polyline to accept mid-arrow
543
544 var x1 = elem.getAttribute('x1')-0;
545 var x2 = elem.getAttribute('x2')-0;
546 var y1 = elem.getAttribute('y1')-0;
547 var y2 = elem.getAttribute('y2')-0;
548 var id = elem.id;
549
550 var mid_pt = (' '+((x1+x2)/2)+','+((y1+y2)/2) + ' ');
551 var pline = addElem({
552 "element": "polyline",
553 "attr": {
554 "points": (x1+','+y1+ mid_pt +x2+','+y2),
555 "stroke": elem.getAttribute('stroke'),
556 "stroke-width": elem.getAttribute('stroke-width'),
557 "marker-mid": mid,
558 "fill": "none",
559 "opacity": elem.getAttribute('opacity') || 1
560 }
561 });
562 $(elem).after(pline).remove();
563 svgCanvas.clearSelection();
564 pline.id = id;
565 svgCanvas.addToSelection([pline]);
566 elem = pline;
567 }
568 }
569 // Update line if it's a connector
570 if(elem.getAttribute('class') == conn_sel.substr(1)) {
571 var start = getElem(elData(elem, 'c_start'));
572 updateConnectors([start]);
573 } else {
574 updateConnectors();
575 }
576 },
577 toolButtonStateUpdate: function(opts) {
578 if(opts.nostroke) {
579 if ($('#mode_connect').hasClass('tool_button_current')) {
580 clickSelect();
581 }
582 }
583 $('#mode_connect')
584 .toggleClass('disabled',opts.nostroke);
585 }
586 };
587});
Note: See TracBrowser for help on using the repository browser.