ext-arrows.js 6.85 KB
/*
 * ext-arrows.js
 *
 * Licensed under the Apache License, Version 2
 *
 * Copyright(c) 2010 Alexis Deveria
 *
 */

 
svgEditor.addExtension("Arrows", function(S) {
		var svgcontent = S.svgcontent,
			addElem = S.addSvgElementFromJson,
			nonce = S.nonce,
			randomize_ids = S.randomize_ids,
			selElems;

		svgCanvas.bind('setnonce', setArrowNonce);
		svgCanvas.bind('unsetnonce', unsetArrowNonce);
			
		var lang_list = {
			"en":[
				{"id": "arrow_none", "textContent": "No arrow" }
			],
			"fr":[
				{"id": "arrow_none", "textContent": "Sans flèche" }
			]
		};
		
		var prefix = 'se_arrow_';
		if (randomize_ids) {
		  var arrowprefix = prefix + nonce + '_';
		} else {
		  var arrowprefix = prefix;
		}

		var pathdata = {
			fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8,  id: arrowprefix + 'fw'},
			bk: {d:"m10,0l-10,5l10,5l-5,-5l5,-5z", refx:2, id: arrowprefix + 'bk'}
		}
		
		function setArrowNonce(window, n) {
		    randomize_ids = true;
		    arrowprefix = prefix + n + '_';
 			pathdata.fw.id = arrowprefix + 'fw';
			pathdata.bk.id = arrowprefix + 'bk';
		}

		function unsetArrowNonce(window) {
		    randomize_ids = false;
		    arrowprefix = prefix;
 			pathdata.fw.id = arrowprefix + 'fw';
			pathdata.bk.id = arrowprefix + 'bk';
		}

		function getLinked(elem, attr) {
			var str = elem.getAttribute(attr);
			if(!str) return null;
			var m = str.match(/\(\#(.*)\)/);
			if(!m || m.length !== 2) {
				return null;
			}
			return S.getElem(m[1]);
		}
		
		function showPanel(on) {
			$('#arrow_panel').toggle(on);
			
			if(on) {
				var el = selElems[0];
				var end = el.getAttribute("marker-end");
				var start = el.getAttribute("marker-start");
				var mid = el.getAttribute("marker-mid");
				var val;
				
				if(end && start) {
					val = "both";
				} else if(end) {
					val = "end";
				} else if(start) {
					val = "start";
				} else if(mid) {
					val = "mid";
					if(mid.indexOf("bk") != -1) {
						val = "mid_bk";
					}
				}
				
				if(!start && !mid && !end) {
					val = "none";
				}
				
				$("#arrow_list").val(val);
			}
		}
		
		function resetMarker() {
			var el = selElems[0];
			el.removeAttribute("marker-start");
			el.removeAttribute("marker-mid");
			el.removeAttribute("marker-end");
		}
		
		function addMarker(dir, type, id) {
			// TODO: Make marker (or use?) per arrow type, since refX can be different
			id = id || arrowprefix + dir;
			
			var marker = S.getElem(id);

			var data = pathdata[dir];
			
			if(type == "mid") {
				data.refx = 5;
			}

			if(!marker) {
				marker = addElem({
					"element": "marker",
					"attr": {
						"viewBox": "0 0 10 10",
						"id": id,
						"refY": 5,
						"markerUnits": "strokeWidth",
						"markerWidth": 5,
						"markerHeight": 5,
						"orient": "auto",
						"style": "pointer-events:none" // Currently needed for Opera
					}
				});
				var arrow = addElem({
					"element": "path",
					"attr": {
						"d": data.d,
						"fill": "#000000"
					}
				});
				marker.appendChild(arrow);
				S.findDefs().appendChild(marker);
			} 
			
			marker.setAttribute('refX', data.refx);
			
			return marker;
		}
		
		function setArrow() {
			var type = this.value;
			resetMarker();
		
			if(type == "none") {
				return;
			}
		
			// Set marker on element
			var dir = "fw";
			if(type == "mid_bk") {
				type = "mid";
				dir = "bk";
			} else if(type == "both") {
				addMarker("bk", type);
				svgCanvas.changeSelectedAttribute("marker-start", "url(#" + pathdata.bk.id + ")");
				type = "end";
				dir = "fw";
			} else if (type == "start") {
				dir = "bk";
			}
			
			addMarker(dir, type);
			svgCanvas.changeSelectedAttribute("marker-"+type, "url(#" + pathdata[dir].id + ")");
			S.call("changed", selElems);
		}
		
		function colorChanged(elem) {
			var color = elem.getAttribute('stroke');
			
			var mtypes = ['start','mid','end'];
			var defs = S.findDefs();
			
			$.each(mtypes, function(i, type) {
				var marker = getLinked(elem, 'marker-'+type);
				if(!marker) return;
				
				var cur_color = $(marker).children().attr('fill');
				var cur_d = $(marker).children().attr('d');
				var new_marker = null;
				if(cur_color === color) return;
				
				var all_markers = $(defs).find('marker');
				// Different color, check if already made
				all_markers.each(function() {
					var attrs = $(this).children().attr(['fill', 'd']);
					if(attrs.fill === color && attrs.d === cur_d) {
						// Found another marker with this color and this path
						new_marker = this;
					}
				});
				
				if(!new_marker) {
					// Create a new marker with this color
					var last_id = marker.id;
					var dir = last_id.indexOf('_fw') !== -1?'fw':'bk';
					
					new_marker = addMarker(dir, type, arrowprefix + dir + all_markers.length);

					$(new_marker).children().attr('fill', color);
				}
				
				$(elem).attr('marker-'+type, "url(#" + new_marker.id + ")");
				
				// Check if last marker can be removed
				var remove = true;
				$(S.svgcontent).find('line, polyline, path, polygon').each(function() {
					var elem = this;
					$.each(mtypes, function(j, mtype) {
						if($(elem).attr('marker-' + mtype) === "url(#" + marker.id + ")") {
							return remove = false;
						}
					});
					if(!remove) return false;
				});
				
				// Not found, so can safely remove
				if(remove) {
					$(marker).remove();
				}

			});
			
		}
		
		return {
			name: "Arrows",
			context_tools: [{
				type: "select",
				panel: "arrow_panel",
				title: "Select arrow type",
				id: "arrow_list",
				options: {
					none: "No arrow",
					end: "---->",
					start: "<----",
					both: "<--->",
					mid: "-->--",
					mid_bk: "--<--"
				},
				defval: "none",
				events: {
					change: setArrow
				}
			}],
			callback: function() {
				$('#arrow_panel').hide();
				// Set ID so it can be translated in locale file
				$('#arrow_list option')[0].id = 'connector_no_arrow';
			},
			addLangData: function(lang) {
				return {
					data: lang_list[lang]
				};
			},
			selectedChanged: function(opts) {
				
				// Use this to update the current selected elements
				selElems = opts.elems;
				
				var i = selElems.length;
				var marker_elems = ['line','path','polyline','polygon'];
				
				while(i--) {
					var elem = selElems[i];
					if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
						if(opts.selectedElement && !opts.multiselected) {
							showPanel(true);
						} else {
							showPanel(false);
						}
					} else {
						showPanel(false);
					}
				}
			},
			elementChanged: function(opts) {
				var elem = opts.elems[0];
				if(elem && (
					elem.getAttribute("marker-start") ||
					elem.getAttribute("marker-mid") ||
					elem.getAttribute("marker-end")
				)) {
	// 								var start = elem.getAttribute("marker-start");
	// 								var mid = elem.getAttribute("marker-mid");
	// 								var end = elem.getAttribute("marker-end");
					// Has marker, so see if it should match color
					colorChanged(elem);
				}
				
			}
		};
});