As a user, I want to be able to draw segments
1. Pressing the left mouse button marks the beginning of the segment.
2. Mouse movement after clicking with the left mouse button draws an intermediate result.
3. After the button is released, the end of the segment is marked.
4. Data is sent to the server.
myDrawingBoard .once(“mousedown”, setStartingPoint) .any(“mousemove”, drawLine) .once(“mouseup”, setEndingPoint) .atLast(saveFigure)
var BaseEvent = function (type, element, callback, context) { this.element = element; this.callback = callback; this.context = context; this.id = GuidFactory.create(); this.name = "me_" + this.id; if (type instanceof Object) { for (var key in type) { this._codes = type[key] instanceof Array ? type[key] : [type[key]]; type = key; this.element = $(document); break; } } this.type = type; this._uniqueType = type + "." + this.id; this._handlers = []; }; BaseEvent.prototype = { on: function (callback, context) { this._handlers.push({callback: callback, context: context || this}); return this; }, trigger: function () { for (var i = 0; i < this._handlers.length; ++i) { var obj = this._handlers[i]; obj.callback.apply(obj.context, arguments); } }, init: function () { var _this = this; this.element.on(this._uniqueType, function (evt) { if (!_this._codes || _this._codes.indexOf(evt.keyCode) >= 0) { _this.trigger(evt); } }) }, dispose: function () { this.element.off(this._uniqueType); } };
var MetaEvent = function () { this._events = []; this._closingEvent = null; this._currentEvent = null; this.closed = false; this.id = GuidFactory.create(); this.name = "me_" + this.id; }; MetaEvent.prototype = { push: function (evt) { if (this.closed) throw new Error("Cannot push event to closed MetaEvent"); this._events.push(evt); }, close: function (evt) { if (this.closed) throw new Error("Cannot close already closed MetaEvent"); this._closingEvent = evt; this.closed = true; }, init: function (stateMachine) { this._createEventIndex(); this._stateMachine = stateMachine; for (var id in this._eventIndex) { this._initEvent(this._eventIndex[id]); } }, dispose: function () { for (var id in this._eventIndex) { this._eventIndex[id].dispose(); } }, _initEvent: function (evt) { var _this = this; evt.init(); evt.on(function (evt) { if (this.id === _this._closingEvent.id && this.callback.apply(this.context || this.element, [evt]) !== false) { _this._stateMachine[_this.name](); } else if (this.id === _this._currentEvent.id) { this.callback.apply(this.context || this.element, [evt]); } else if (this.type !== _this._currentEvent.type && this.callback.apply(this.context || this.element, [evt]) !== false) { _this._disposePreviousEvents(this.id); _this._currentEvent = _this._eventIndex[this.id]; } }); }, _createEventIndex: function () { this._eventIndex = {}; for (var i = 0; i < this._events.length; ++i) { var evt = this._events[i]; this._eventIndex[evt.id] = evt; } this._eventIndex[this._closingEvent.id] = this._closingEvent; this._currentEvent = this._events[0] || this._closingEvent; }, _disposePreviousEvents: function (eventId) { for (var i = 0; i < this._events.length; ++i) { var evt = this._events[i]; if (evt.id !== eventId) { evt.dispose(); } else { break; } } } };
var EventChain = function (element) { this._element = $(element); this._metaEvents = []; this._atLast = null; }; EventChain.prototype = { _lastEvent: function () { return this._metaEvents.length > 0 ? this._metaEvents[this._metaEvents.length - 1] : null; }, _createEventIndex: function () { this._eventIndex = {}; for (var i = 0; i < this._metaEvents.length; ++i) { var evt = this._metaEvents[i]; this._eventIndex[evt.id] = evt; } }, _createEvents: function () { return this._metaEvents.map(function (evt, index, metaEvents) { return { name: evt.name, from: evt.id, to: index + 1 < metaEvents.length ? metaEvents[index + 1].id : "atLast" } }); }, _createCallbacks: function () { var result = {}, _this = this; for (var i in this._eventIndex) { result["onenter" + this._eventIndex[i].id] = function (evt, from, to, data) { _this._eventIndex[to].init(this); } result["onleave" + this._eventIndex[i].id] = function (evt, from, to, data) { if (_this._eventIndex[from]) { _this._eventIndex[from].dispose(); } } } result["onatLast"] = function (evt, from, to) { if (_this._eventIndex[from]) { _this._eventIndex[from].dispose(); } if (_this._atLastCallback) { _this._atLastCallback.apply( _this._atLastContext || _this._element, arguments); } }; return result; }, // add event that will be handled only once once: function (type, element, callback, context) { if (element instanceof Function) { context = callback; callback = element; element = this._element; } var lastEvent = this._lastEvent(); if (lastEvent && !lastEvent.closed) { lastEvent.close(new BaseEvent(type, element, callback, context)); } else { var evt = new MetaEvent(); evt.close(new BaseEvent(type, element, callback, context)); this._metaEvents.push(evt); } return this; }, // add event that will be handled twice twice: function (type, element, callback, context) { return this .once(type, element, callback, context) .once(type, element, callback, context); }, // add event that will be repeated any times any: function (type, element, callback, context) { if (element instanceof Function) { context = callback; callback = element; element = this._element; } var lastEvent = this._lastEvent(); if (lastEvent && !lastEvent.closed) { lastEvent.push(new BaseEvent(type, element, callback, context)); } else { var evt = new MetaEvent(); evt.push(new BaseEvent(type, element, callback, context)); this._metaEvents.push(evt); } return this; }, // add event that will be repeated at least once onceAndMore: function (type, element, callback, context) { return this .once(type, element, callback, context) .any(type, element, callback, context); }, // set function that will be called after queue is done atLast: function (callback, context) { this._atLastCallback = callback; this._atLastContext = context; return this; }, // set event that will cancel queue immediately cancel: function (type, element, callback, context) { var _this = this; if (element instanceof Function) { context = callback; callback = element; element = this._element; } new BaseEvent(type, element, callback, context) .on("caught", function (evt) { if (this.callback.apply(this.context || this.element, [evt]) !== false) { _this.dispose(); } }) .init(); return this; }, // initialize state machine init: function () { this._createEventIndex(); var callbacks = this._createCallbacks(), events = this._createEvents(), stateMachine = StateMachine.create({ initial: this._metaEvents[0].id, final: "atLast", events: events, callbacks: callbacks }); return this; }, dispose: function () { for (var i = 0; i < this._metaEvents.length; ++i) { this._metaEvents[i].dispose(); } } };
jQuery.fn.eventChain = function () { return new EventChain(this); };
var LineDrawer = new (ConcreteDrawer.extend({ __type: "line", __draw: function (data) { return new SmartPath(data).draw(); }, __startDrawing: function (data) { return Board.EventLayer.eventChain() .once("mousedown", this._placeStartPoint, this) .any("mousemove", this.__drawTemporaryFigure, this) .once("mouseup", this._placeEndPoint, this) .cancel({"keydown": 27}, this.cancelDrawing, this) .atLast(this.__saveFigure, this) .init(); }, _placeStartPoint: function (evt) { this.__figureData.x1 = Board.EventLayer.pageX(evt); this.__figureData.y1 = Board.EventLayer.pageY(evt); }, __drawTemporaryFigure: function (evt) { this._placeEndPoint(evt); this.base(); }, _placeEndPoint: function (evt) { this.__figureData.x2 = Board.EventLayer.pageX(evt); this.__figureData.y2 = Board.EventLayer.pageY(evt); } }))();
As a user, I want to be able to draw broken lines.
1. Pressing the left mouse button marks the beginning of the segment.
2. The movement of the mouse shows an intermediate result.
3. Pressing the space bar marks the top of the polyline.
4. Repeat steps 2 and 3 until the user releases the mouse button, then save the last intermediate result.
return Board.EventLayer.eventChain() .once("mousedown", this._placeStartPoint, this) .any(function (chain) { return chain .any("mousemove", this.__drawTemporaryFigure, this) .once({"keydown": 32}, this._placePolygonePoint, this); }, this) .once("mouseup", this._placePolygonePoint, this) .cancel({"keydown": 27}, this.cancelDrawing, this) .atLast(this.__saveFigure, this) .init();
var CycleEvent = Base.extend({ constructor: function (cycle, element, context) { this._cycle = cycle; this._element = element; this._context = context; this.callback = function () {}; this.id = GuidFactory.create(); this.name = "me_" + this.id; this.type = "cycle_" + this.id; }, init: function () { this._cycleChain = this._cycle .apply(this._context || this, [this._element.eventChain()]) .atLast(this._restartCycle, this); this._cycleChain.init(); return this._cycleChain; }, dispose: function () { this._cycleChain.dispose(); }, _restartCycle: function () { this.dispose(); this.init(); this.trigger("caught"); } });
any: function (type, element, callback, context) { if (type instanceof Function) { return this._cycle(type, element) } else if (element instanceof Function) { context = callback; callback = element; element = this._element; } var lastEvent = this._lastEvent(); if (lastEvent && !lastEvent.closed) { lastEvent.push(new BaseEvent(type, element, callback, context)); } else { var evt = new MetaEvent(); evt.push(new BaseEvent(type, element, callback, context)); this._metaEvents.push(evt); } return this; }, // add cycle of events with same syntax _cycle: function (cycle, context) { var lastEvent = this._lastEvent(); if (lastEvent && !lastEvent.closed) { lastEvent.push(new CycleEvent(cycle, this._element, context)); } else { var evt = new MetaEvent(); evt.push(new CycleEvent(cycle, this._element, context)); this._metaEvents.push(evt); } return this; }
Source: https://habr.com/ru/post/245269/
All Articles