123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- // wrapped by build app
- define("dojox/drawing/manager/Stencil", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){
- 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
- }
- }
-
- );
- })();
- });
|