| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 | 
							- /*
 
- 	Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
 
- 	Available via Academic Free License >= 2.1 OR the modified BSD license.
 
- 	see: http://dojotoolkit.org/license for details
 
- */
 
- if(!dojo._hasResource["dojox.drawing.manager.Stencil"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
 
- dojo._hasResource["dojox.drawing.manager.Stencil"] = true;
 
- dojo.provide("dojox.drawing.manager.Stencil");
 
- (function(){
 
- 	var surface, surfaceNode;
 
- 	dojox.drawing.manager.Stencil = dojox.drawing.util.oo.declare(
 
- 		// summary:
 
- 		//		The main class for tracking Stencils that are cretaed, added,
 
- 		//		selected, or deleted. Also handles selections, multiple
 
- 		//		selections, adding and removing from selections, and dragging
 
- 		//		selections. It's this class that triggers the anchors to
 
- 		//		appear on a Stencil and whther there are anchor on a multiple
 
- 		//		select or not (currently not)
 
- 		//
 
- 		function(options){
 
- 			//
 
- 			// TODO: mixin props
 
- 			//
 
- 			surface = options.surface;
 
- 			this.canvas = options.canvas;
 
- 			
 
- 			this.defaults = dojox.drawing.defaults.copy();
 
- 			this.undo = options.undo;
 
- 			this.mouse = options.mouse;
 
- 			this.keys = options.keys;
 
- 			this.anchors = options.anchors;
 
- 			this.stencils = {};
 
- 			this.selectedStencils = {};
 
- 			this._mouseHandle = this.mouse.register(this);
 
- 			
 
- 			dojo.connect(this.keys, "onArrow", this, "onArrow");
 
- 			dojo.connect(this.keys, "onEsc", this, "deselect");
 
- 			dojo.connect(this.keys, "onDelete", this, "onDelete");
 
- 			
 
- 		},
 
- 		{
 
- 			_dragBegun: false,
 
- 			_wasDragged:false,
 
- 			_secondClick:false,
 
- 			_isBusy:false,
 
- 			
 
- 			setRecentStencil: function(stencil){
 
- 				// summary:
 
- 				//		Keeps track of the most recent stencil interacted
 
- 				//		with, whether created or selected.
 
- 				this.recent = stencil;
 
- 			},
 
- 			
 
- 			getRecentStencil: function(){
 
- 				// summary:
 
- 				//		Returns the stencil most recently interacted
 
- 				//		with whether it's last created or last selected
 
- 				return this.recent;
 
- 			},
 
- 			
 
- 			register: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Key method for adding Stencils. Stencils
 
- 				//		can be added to the canvas without adding
 
- 				//		them to this, but they won't have selection
 
- 				//		or drag ability.
 
- 				//
 
- 				console.log("Selection.register ::::::", stencil.id);
 
- 				if(stencil.isText && !stencil.editMode && stencil.deleteEmptyCreate && !stencil.getText()){
 
- 					// created empty text field
 
- 					// defaults say to delete
 
- 					console.warn("EMPTY CREATE DELETE", stencil);
 
- 					stencil.destroy();
 
- 					return false;
 
- 				}
 
- 				
 
- 				this.stencils[stencil.id] = stencil;
 
- 				this.setRecentStencil(stencil);
 
- 				
 
- 				if(stencil.execText){
 
- 					if(stencil._text && !stencil.editMode){
 
- 						console.log("select text");
 
- 						this.selectItem(stencil);
 
- 					}
 
- 					stencil.connect("execText", this, function(){
 
- 						if(stencil.isText && stencil.deleteEmptyModify && !stencil.getText()){
 
- 							console.warn("EMPTY MOD DELETE", stencil);
 
- 							// text deleted
 
- 							// defaults say to delete
 
- 							this.deleteItem(stencil);
 
- 						}else if(stencil.selectOnExec){
 
- 							this.selectItem(stencil);
 
- 						}
 
- 					});
 
- 				}
 
- 				
 
- 				stencil.connect("deselect", this, function(){
 
- 					if(!this._isBusy && this.isSelected(stencil)){
 
- 						// called from within stencil. do action.
 
- 						this.deselectItem(stencil);
 
- 					}
 
- 				});
 
- 				
 
- 				stencil.connect("select", this, function(){
 
- 					if(!this._isBusy && !this.isSelected(stencil)){
 
- 						// called from within stencil. do action.
 
- 						this.selectItem(stencil);
 
- 					}
 
- 				});
 
- 				
 
- 				return stencil;
 
- 			},
 
- 			unregister: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Method for removing Stencils from the manager.
 
- 				//		This doesn't delete them, only removes them from
 
- 				// 		the list.
 
- 				//
 
- 				console.log("Selection.unregister ::::::", stencil.id, "sel:", stencil.selected);
 
- 				if(stencil){
 
- 					stencil.selected && this.onDeselect(stencil);
 
- 					delete this.stencils[stencil.id];
 
- 				}
 
- 			},
 
- 			
 
- 			onArrow: function(/*Key Event*/evt){
 
- 				// summary:
 
- 				// 		Moves selection based on keyboard arrow keys
 
- 				//
 
- 				// FIXME: Check constraints
 
- 				if(this.hasSelected()){
 
- 					this.saveThrottledState();
 
- 					this.group.applyTransform({dx:evt.x, dy: evt.y});
 
- 				}
 
- 			},
 
- 			
 
- 			_throttleVrl:null,
 
- 			_throttle: false,
 
- 			throttleTime:400,
 
- 			_lastmxx:-1,
 
- 			_lastmxy:-1,
 
- 			saveMoveState: function(){
 
- 				// summary:
 
- 				//		Internal. Used for the prototype undo stack.
 
- 				// 		Saves selection position.
 
- 				//
 
- 				var mx = this.group.getTransform();
 
- 				if(mx.dx == this._lastmxx && mx.dy == this._lastmxy){ return; }
 
- 				this._lastmxx = mx.dx;
 
- 				this._lastmxy = mx.dy;
 
- 				//console.warn("SAVE MOVE!", mx.dx, mx.dy);
 
- 				this.undo.add({
 
- 					before:dojo.hitch(this.group, "setTransform", mx)
 
- 				});
 
- 			},
 
- 			
 
- 			saveThrottledState: function(){
 
- 				// summary:
 
- 				//		Internal. Used for the prototype undo stack.
 
- 				//		Prevents an undo point on every mouse move.
 
- 				//		Only does a point when the mouse hesitates.
 
- 				//
 
- 				clearTimeout(this._throttleVrl);
 
- 				clearInterval(this._throttleVrl);
 
- 				this._throttleVrl = setTimeout(dojo.hitch(this, function(){
 
- 					this._throttle = false;
 
- 					this.saveMoveState();
 
- 				}), this.throttleTime);
 
- 				if(this._throttle){ return; }
 
- 				this._throttle = true;
 
- 				
 
- 				this.saveMoveState();
 
- 				
 
- 			},
 
- 			unDelete: function(/*Array*/stencils){
 
- 				// summary:
 
- 				//		Undeletes a stencil. Used in undo stack.
 
- 				//
 
- 				console.log("unDelete:", stencils);
 
- 				for(var s in stencils){
 
- 					stencils[s].render();
 
- 					this.onSelect(stencils[s]);
 
- 				}
 
- 			},
 
- 			onDelete: function(/*Boolean*/noundo){
 
- 				// summary:
 
- 				//		Event fired on deletion of a stencil
 
- 				//
 
- 				console.log("Stencil onDelete", noundo);
 
- 				if(noundo!==true){
 
- 					this.undo.add({
 
- 						before:dojo.hitch(this, "unDelete", this.selectedStencils),
 
- 						after:dojo.hitch(this, "onDelete", true)
 
- 					});
 
- 				}
 
- 				this.withSelected(function(m){
 
- 					this.anchors.remove(m);
 
- 					var id = m.id;
 
- 					console.log("delete:", m);
 
- 					m.destroy();
 
- 					delete this.stencils[id];
 
- 				});
 
- 				this.selectedStencils = {};
 
- 			},
 
- 			
 
- 			deleteItem: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Deletes a stencil.
 
- 				//		NOTE: supports limited undo.
 
- 				//
 
- 				// manipulating the selection to fire onDelete properly
 
- 				if(this.hasSelected()){
 
- 					// there is a selection
 
- 					var sids = [];
 
- 					for(var m in this.selectedStencils){
 
- 						if(this.selectedStencils.id == stencil.id){
 
- 							if(this.hasSelected()==1){
 
- 								// the deleting stencil is the only one selected
 
- 								this.onDelete();
 
- 								return;
 
- 							}
 
- 						}else{
 
- 							sids.push(this.selectedStencils.id);
 
- 						}
 
- 					}
 
- 					// remove selection, delete, restore selection
 
- 					this.deselect();
 
- 					this.selectItem(stencil);
 
- 					this.onDelete();
 
- 					dojo.forEach(sids, function(id){
 
- 						this.selectItem(id);
 
- 					}, this);
 
- 				}else{
 
- 					// there is not a selection. select it, delete it
 
- 					this.selectItem(stencil);
 
- 					// now delete selection
 
- 					this.onDelete();
 
- 				}
 
- 			},
 
- 			
 
- 			removeAll: function(){
 
- 				// summary:
 
- 				//		Deletes all Stencils on the canvas.
 
- 				
 
- 				this.selectAll();
 
- 				this._isBusy = true;
 
- 				this.onDelete();
 
- 				this.stencils = {};
 
- 				this._isBusy = false;
 
- 			},
 
- 			
 
- 			setSelectionGroup: function(){
 
- 				// summary:
 
- 				//		Internal. Creates a new selection group
 
- 				//		used to hold selected stencils.
 
- 				//
 
- 				this.withSelected(function(m){
 
- 					this.onDeselect(m, true);
 
- 				});
 
- 				
 
- 				if(this.group){
 
- 					surface.remove(this.group);
 
- 					this.group.removeShape();
 
- 				}
 
- 				this.group = surface.createGroup();
 
- 				this.group.setTransform({dx:0, dy: 0});
 
- 				
 
- 				this.withSelected(function(m){
 
- 					this.group.add(m.container);
 
- 					m.select();
 
- 				});
 
- 			},
 
- 			
 
- 			setConstraint: function(){
 
- 				// summary:
 
- 				//		Internal. Gets all selected stencils' coordinates
 
- 				//		and determines how far left and up the selection
 
- 				//		can go without going below zero
 
- 				//
 
- 				var t = Infinity, l = Infinity;
 
- 				this.withSelected(function(m){
 
- 					var o = m.getBounds();
 
- 					t = Math.min(o.y1, t);
 
- 					l = Math.min(o.x1, l);
 
- 				});
 
- 				this.constrain = {l:-l, t:-t};
 
- 			},
 
- 			
 
- 			
 
- 			
 
- 			onDeselect: function(stencil, keepObject){
 
- 				// summary:
 
- 				//		Event fired on deselection of a stencil
 
- 				//
 
- 				if(!keepObject){
 
- 					delete this.selectedStencils[stencil.id];
 
- 				}
 
- 				//console.log('onDeselect, keep:', keepObject, "stencil:", stencil.type)
 
- 				
 
- 				this.anchors.remove(stencil);
 
- 				
 
- 				surface.add(stencil.container);
 
- 				stencil.selected && stencil.deselect();
 
- 				stencil.applyTransform(this.group.getTransform());
 
- 			},
 
- 			
 
- 			deselectItem: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Deselect passed stencil
 
- 				//
 
- 				// note: just keeping with standardized methods
 
- 				this.onDeselect(stencil);
 
- 			},
 
- 			
 
- 			deselect: function(){ // all stencils
 
- 				// summary:
 
- 				//		Deselect all stencils
 
- 				//
 
- 				this.withSelected(function(m){
 
- 					this.onDeselect(m);
 
- 				});
 
- 				this._dragBegun = false;
 
- 				this._wasDragged = false;
 
- 			},
 
- 			
 
- 			onSelect: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Event fired on selection of a stencil
 
- 				//
 
- 				//console.log("stencil.onSelect", stencil);
 
- 				if(!stencil){
 
- 					console.error("null stencil is not selected:", this.stencils)
 
- 				}
 
- 				if(this.selectedStencils[stencil.id]){ return; }
 
- 				this.selectedStencils[stencil.id] = stencil;
 
- 				this.group.add(stencil.container);
 
- 				stencil.select();
 
- 				if(this.hasSelected()==1){
 
- 					this.anchors.add(stencil, this.group);
 
- 				}
 
- 			},
 
- 			
 
- 			selectAll: function(){
 
- 				// summary:
 
- 				//		Selects all items
 
- 				this._isBusy = true;
 
- 				for(var m in this.stencils){
 
- 					//if(!this.stencils[m].selected){
 
- 						this.selectItem(m);
 
- 					//}
 
- 				}
 
- 				this._isBusy = false;
 
- 			},
 
- 			
 
- 			selectItem: function(/*String|Object*/ idOrItem){
 
- 				// summary:
 
- 				//		Method used to select a stencil.
 
- 				//
 
- 				var id = typeof(idOrItem)=="string" ? idOrItem : idOrItem.id;
 
- 				var stencil = this.stencils[id];
 
- 				this.setSelectionGroup();
 
- 				this.onSelect(stencil);
 
- 				this.group.moveToFront();
 
- 				this.setConstraint();
 
- 			},
 
- 			
 
- 			onLabelDoubleClick: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event to connect a textbox to
 
- 				//		for label edits
 
- 				console.info("mgr.onLabelDoubleClick:", obj);
 
- 				if(this.selectedStencils[obj.id]){
 
- 					this.deselect();
 
- 				}
 
- 			},
 
- 			
 
- 			onStencilDoubleClick: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired on the double-click of a stencil
 
- 				//
 
- 				console.info("mgr.onStencilDoubleClick:", obj);
 
- 				if(this.selectedStencils[obj.id]){
 
- 					if(this.selectedStencils[obj.id].edit){
 
- 						console.info("Mgr Stencil Edit -> ", this.selectedStencils[obj.id]);
 
- 						var m = this.selectedStencils[obj.id];
 
- 						// deselect must happen first to set the transform
 
- 						// then edit knows where to set the text box
 
- 						m.editMode = true;
 
- 						this.deselect();
 
- 						m.edit();
 
- 					}
 
- 				}
 
- 				
 
- 			},
 
- 			
 
- 			onAnchorUp: function(){
 
- 				// summary:
 
- 				//		Event fire on mouseup off of an anchor point
 
- 				this.setConstraint();
 
- 			},
 
- 			
 
- 			onStencilDown: function(/*EventObject*/obj, evt){
 
- 				// summary:
 
- 				//		Event fired on mousedown on a stencil
 
- 				//
 
- 				console.info(" >>> onStencilDown:", obj.id, this.keys.meta);
 
- 				if(!this.stencils[obj.id]){ return; }
 
- 				this.setRecentStencil(this.stencils[obj.id]);
 
- 				this._isBusy = true;
 
- 				
 
- 				
 
- 				if(this.selectedStencils[obj.id] && this.keys.meta){
 
- 					if(dojo.isMac && this.keys.cmmd){
 
- 						// block context menu
 
- 						
 
- 					}
 
- 					console.log("    shift remove");
 
- 					this.onDeselect(this.selectedStencils[obj.id]);
 
- 					if(this.hasSelected()==1){
 
- 						this.withSelected(function(m){
 
- 							this.anchors.add(m, this.group);
 
- 						});
 
- 					}
 
- 					this.group.moveToFront();
 
- 					this.setConstraint();
 
- 					return;
 
- 				
 
- 				}else if(this.selectedStencils[obj.id]){
 
- 					console.log("    clicked on selected");
 
- 					// clicking on same selected item(s)
 
- 					// RESET OFFSETS
 
- 					var mx = this.group.getTransform();
 
- 					this._offx = obj.x - mx.dx;
 
- 					this._offy = obj.y - mx.dy;
 
- 					return;
 
- 				
 
- 				}else if(!this.keys.meta){
 
- 					
 
- 					console.log("    deselect all");
 
- 					this.deselect();
 
- 				
 
- 				}else{
 
- 					// meta-key add
 
- 					//console.log("reset sel and add stencil")
 
- 				}
 
- 				console.log("    add stencil to selection");
 
- 				// add a stencil
 
- 				this.selectItem(obj.id);
 
- 				
 
- 				mx = this.group.getTransform();
 
- 				this._offx = obj.x - mx.dx;
 
- 				this._offy = obj.y - mx.dx;
 
- 				
 
- 				this.orgx = obj.x;
 
- 				this.orgy = obj.y;
 
- 				
 
- 				this._isBusy = false;
 
- 				
 
- 				// TODO:
 
- 				//  dojo.style(surfaceNode, "cursor", "pointer");
 
- 				
 
- 				// TODO:
 
- 				this.undo.add({
 
- 					before:function(){
 
- 						
 
- 					},
 
- 					after: function(){
 
- 						
 
- 					}
 
- 				});
 
- 			},
 
- 			
 
- 			onLabelDown: function(/*EventObject*/obj, evt){
 
- 				// summary:
 
- 				//		Event fired on mousedown of a stencil's label
 
- 				//		Because it's an annotation the id will be the
 
- 				//		master stencil.
 
- 				//console.info("===============>>>Label click: ",obj, " evt: ",evt);
 
- 				this.onStencilDown(obj,evt);
 
- 			},
 
- 			
 
- 			onStencilUp: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired on mouseup off of a stencil
 
- 				//
 
- 			},
 
- 			
 
- 			onLabelUp: function(/*EventObject*/obj){
 
- 				this.onStencilUp(obj);
 
- 			},
 
- 			
 
- 			onStencilDrag: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired on every mousemove of a stencil drag
 
- 				//
 
- 				if(!this._dragBegun){
 
- 					// bug, in FF anyway - first mouse move shows x=0
 
- 					// the 'else' fixes it
 
- 					this.onBeginDrag(obj);
 
- 					this._dragBegun = true;
 
- 				}else{
 
- 					this.saveThrottledState();
 
- 					
 
- 					var x = obj.x - obj.last.x,
 
- 						y = obj.y - obj.last.y,
 
- 						c = this.constrain,
 
- 						mz = this.defaults.anchors.marginZero;
 
- 					
 
- 					
 
- 					x = obj.x - this._offx;
 
- 					y = obj.y - this._offy;
 
- 					
 
- 					if(x < c.l + mz){
 
- 						x = c.l + mz;
 
- 					}
 
- 					if(y < c.t + mz){
 
- 						y = c.t + mz;
 
- 					}
 
- 					
 
- 					this.group.setTransform({
 
- 						dx: x,
 
- 						dy: y
 
- 					});
 
- 					
 
- 					
 
- 				}
 
- 			},
 
- 			
 
- 			onLabelDrag: function(/*EventObject*/obj){
 
- 				this.onStencilDrag(obj);
 
- 			},
 
- 			
 
- 			onDragEnd: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired at the end of a stencil drag
 
- 				//
 
- 				this._dragBegun = false;
 
- 			},
 
- 			onBeginDrag: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired at the beginning of a stencil drag
 
- 				//
 
- 				this._wasDragged = true;
 
- 			},
 
- 			
 
- 			onDown: function(/*EventObject*/obj){
 
- 				// summary:
 
- 				//		Event fired on mousedown on the canvas
 
- 				//
 
- 				this.deselect();
 
- 			},
 
- 						
 
- 			
 
- 			onStencilOver: function(obj){
 
- 				// summary:
 
- 				//		This changes the cursor when hovering over
 
- 				//		a selectable stencil.
 
- 				//console.log("OVER")
 
- 				dojo.style(obj.id, "cursor", "move");
 
- 			},
 
- 			onStencilOut: function(obj){
 
- 				// summary:
 
- 				//		This restores the cursor.
 
- 				//console.log("OUT")
 
- 				dojo.style(obj.id, "cursor", "crosshair");
 
- 			},
 
- 			
 
- 			exporter: function(){
 
- 				// summary:
 
- 				//		Collects all Stencil data and returns an
 
- 				//		Array of objects.
 
- 				var items = [];
 
- 				for(var m in this.stencils){
 
- 					this.stencils[m].enabled && items.push(this.stencils[m].exporter());
 
- 				}
 
- 				return items; // Array
 
- 			},
 
- 			
 
- 			listStencils: function(){
 
- 				return this.stencils;
 
- 			},
 
- 			
 
- 			toSelected: function(/*String*/func){
 
- 				// summary:
 
- 				//		Convenience function calls function *within*
 
- 				//		all selected stencils
 
- 				var args = Array.prototype.slice.call(arguments).splice(1);
 
- 				for(var m in this.selectedStencils){
 
- 					var item = this.selectedStencils[m];
 
- 					item[func].apply(item, args);
 
- 				}
 
- 			},
 
- 			
 
- 			withSelected: function(/*Function*/func){
 
- 				// summary:
 
- 				//		Convenience function calls function on
 
- 				//		all selected stencils
 
- 				var f = dojo.hitch(this, func);
 
- 				for(var m in this.selectedStencils){
 
- 					f(this.selectedStencils[m]);
 
- 				}
 
- 			},
 
- 			
 
- 			withUnselected: function(/*Function*/func){
 
- 				// summary:
 
- 				//		Convenience function calls function on
 
- 				//		all stencils that are not selected
 
- 				var f = dojo.hitch(this, func);
 
- 				for(var m in this.stencils){
 
- 					!this.stencils[m].selected && f(this.stencils[m]);
 
- 				}
 
- 			},
 
- 			
 
- 			withStencils: function(/*Function*/func){
 
- 				// summary:
 
- 				//		Convenience function calls function on
 
- 				//		all stencils
 
- 				var f = dojo.hitch(this, func);
 
- 				for(var m in this.stencils){
 
- 					f(this.stencils[m]);
 
- 				}
 
- 			},
 
- 			
 
- 			hasSelected: function(){
 
- 				// summary:
 
- 				// 		Returns number of selected (generally used
 
- 				//		as truthy or falsey)
 
- 				//
 
- 				// FIXME: should be areSelected?
 
- 				var ln = 0;
 
- 				for(var m in this.selectedStencils){ ln++; }
 
- 				return ln; // Number
 
- 			},
 
- 			
 
- 			isSelected: function(/*Object*/stencil){
 
- 				// summary:
 
- 				//		Returns if passed stencil is selected or not
 
- 				//		based on internal collection, not on stencil
 
- 				//		boolean
 
- 				return !!this.selectedStencils[stencil.id]; // Boolean
 
- 			}
 
- 		}
 
- 		
 
- 	);
 
- })();
 
- }
 
 
  |