jquery.flot.events.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. define([
  2. 'jquery',
  3. 'lodash',
  4. 'angular',
  5. 'tether-drop',
  6. ],
  7. function ($, _, angular, Drop) {
  8. 'use strict';
  9. function createAnnotationToolip(element, event, plot) {
  10. var injector = angular.element(document).injector();
  11. var content = document.createElement('div');
  12. content.innerHTML = '<annotation-tooltip event="event" on-edit="onEdit()"></annotation-tooltip>';
  13. injector.invoke(["$compile", "$rootScope", function($compile, $rootScope) {
  14. var eventManager = plot.getOptions().events.manager;
  15. var tmpScope = $rootScope.$new(true);
  16. tmpScope.event = event;
  17. tmpScope.onEdit = function() {
  18. eventManager.editEvent(event);
  19. };
  20. $compile(content)(tmpScope);
  21. tmpScope.$digest();
  22. tmpScope.$destroy();
  23. var drop = new Drop({
  24. target: element[0],
  25. content: content,
  26. position: "bottom center",
  27. classes: 'drop-popover drop-popover--annotation',
  28. openOn: 'hover',
  29. hoverCloseDelay: 200,
  30. tetherOptions: {
  31. constraints: [{to: 'window', pin: true, attachment: "both"}]
  32. }
  33. });
  34. drop.open();
  35. drop.on('close', function() {
  36. setTimeout(function() {
  37. drop.destroy();
  38. });
  39. });
  40. }]);
  41. }
  42. var markerElementToAttachTo = null;
  43. function createEditPopover(element, event, plot) {
  44. var eventManager = plot.getOptions().events.manager;
  45. if (eventManager.editorOpen) {
  46. // update marker element to attach to (needed in case of legend on the right
  47. // when there is a double render pass and the inital marker element is removed)
  48. markerElementToAttachTo = element;
  49. return;
  50. }
  51. // mark as openend
  52. eventManager.editorOpened();
  53. // set marker elment to attache to
  54. markerElementToAttachTo = element;
  55. // wait for element to be attached and positioned
  56. setTimeout(function() {
  57. var injector = angular.element(document).injector();
  58. var content = document.createElement('div');
  59. content.innerHTML = '<event-editor panel-ctrl="panelCtrl" event="event" close="close()"></event-editor>';
  60. injector.invoke(["$compile", "$rootScope", function($compile, $rootScope) {
  61. var scope = $rootScope.$new(true);
  62. var drop;
  63. scope.event = event;
  64. scope.panelCtrl = eventManager.panelCtrl;
  65. scope.close = function() {
  66. drop.close();
  67. };
  68. $compile(content)(scope);
  69. scope.$digest();
  70. drop = new Drop({
  71. target: markerElementToAttachTo[0],
  72. content: content,
  73. position: "bottom center",
  74. classes: 'drop-popover drop-popover--form',
  75. openOn: 'click',
  76. tetherOptions: {
  77. constraints: [{to: 'window', pin: true, attachment: "both"}]
  78. }
  79. });
  80. drop.open();
  81. eventManager.editorOpened();
  82. drop.on('close', function() {
  83. // need timeout here in order call drop.destroy
  84. setTimeout(function() {
  85. eventManager.editorClosed();
  86. scope.$destroy();
  87. drop.destroy();
  88. });
  89. });
  90. }]);
  91. }, 100);
  92. }
  93. /*
  94. * jquery.flot.events
  95. *
  96. * description: Flot plugin for adding events/markers to the plot
  97. * version: 0.2.5
  98. * authors:
  99. * Alexander Wunschik <alex@wunschik.net>
  100. * Joel Oughton <joeloughton@gmail.com>
  101. * Nicolas Joseph <www.nicolasjoseph.com>
  102. *
  103. * website: https://github.com/mojoaxel/flot-events
  104. *
  105. * released under MIT License and GPLv2+
  106. */
  107. /**
  108. * A class that allows for the drawing an remove of some object
  109. */
  110. var DrawableEvent = function(object, drawFunc, clearFunc, moveFunc, left, top, width, height) {
  111. var _object = object;
  112. var _drawFunc = drawFunc;
  113. var _clearFunc = clearFunc;
  114. var _moveFunc = moveFunc;
  115. var _position = { left: left, top: top };
  116. var _width = width;
  117. var _height = height;
  118. this.width = function() { return _width; };
  119. this.height = function() { return _height; };
  120. this.position = function() { return _position; };
  121. this.draw = function() { _drawFunc(_object); };
  122. this.clear = function() { _clearFunc(_object); };
  123. this.getObject = function() { return _object; };
  124. this.moveTo = function(position) {
  125. _position = position;
  126. _moveFunc(_object, _position);
  127. };
  128. };
  129. /**
  130. * Event class that stores options (eventType, min, max, title, description) and the object to draw.
  131. */
  132. var VisualEvent = function(options, drawableEvent) {
  133. var _parent;
  134. var _options = options;
  135. var _drawableEvent = drawableEvent;
  136. var _hidden = false;
  137. this.visual = function() { return _drawableEvent; };
  138. this.getOptions = function() { return _options; };
  139. this.getParent = function() { return _parent; };
  140. this.isHidden = function() { return _hidden; };
  141. this.hide = function() { _hidden = true; };
  142. this.unhide = function() { _hidden = false; };
  143. };
  144. /**
  145. * A Class that handles the event-markers inside the given plot
  146. */
  147. var EventMarkers = function(plot) {
  148. var _events = [];
  149. this._types = [];
  150. this._plot = plot;
  151. this.eventsEnabled = false;
  152. this.getEvents = function() {
  153. return _events;
  154. };
  155. this.setTypes = function(types) {
  156. return this._types = types;
  157. };
  158. /**
  159. * create internal objects for the given events
  160. */
  161. this.setupEvents = function(events) {
  162. var that = this;
  163. var parts = _.partition(events, 'isRegion');
  164. var regions = parts[0];
  165. events = parts[1];
  166. $.each(events, function(index, event) {
  167. var ve = new VisualEvent(event, that._buildDiv(event));
  168. _events.push(ve);
  169. });
  170. $.each(regions, function (index, event) {
  171. var vre = new VisualEvent(event, that._buildRegDiv(event));
  172. _events.push(vre);
  173. });
  174. _events.sort(function(a, b) {
  175. var ao = a.getOptions(), bo = b.getOptions();
  176. if (ao.min > bo.min) { return 1; }
  177. if (ao.min < bo.min) { return -1; }
  178. return 0;
  179. });
  180. };
  181. /**
  182. * draw the events to the plot
  183. */
  184. this.drawEvents = function() {
  185. var that = this;
  186. // var o = this._plot.getPlotOffset();
  187. $.each(_events, function(index, event) {
  188. // check event is inside the graph range
  189. if (that._insidePlot(event.getOptions().min) && !event.isHidden()) {
  190. event.visual().draw();
  191. } else {
  192. event.visual().getObject().hide();
  193. }
  194. });
  195. };
  196. /**
  197. * update the position of the event-markers (e.g. after scrolling or zooming)
  198. */
  199. this.updateEvents = function() {
  200. var that = this;
  201. var o = this._plot.getPlotOffset(), left, top;
  202. var xaxis = this._plot.getXAxes()[this._plot.getOptions().events.xaxis - 1];
  203. $.each(_events, function(index, event) {
  204. top = o.top + that._plot.height() - event.visual().height();
  205. left = xaxis.p2c(event.getOptions().min) + o.left - event.visual().width() / 2;
  206. event.visual().moveTo({ top: top, left: left });
  207. });
  208. };
  209. /**
  210. * remove all events from the plot
  211. */
  212. this._clearEvents = function() {
  213. $.each(_events, function(index, val) {
  214. val.visual().clear();
  215. });
  216. _events = [];
  217. };
  218. /**
  219. * create a DOM element for the given event
  220. */
  221. this._buildDiv = function(event) {
  222. var that = this;
  223. var container = this._plot.getPlaceholder();
  224. var o = this._plot.getPlotOffset();
  225. var axes = this._plot.getAxes();
  226. var xaxis = this._plot.getXAxes()[this._plot.getOptions().events.xaxis - 1];
  227. var yaxis, top, left, color, markerSize, markerShow, lineStyle, lineWidth;
  228. var markerTooltip;
  229. // determine the y axis used
  230. if (axes.yaxis && axes.yaxis.used) { yaxis = axes.yaxis; }
  231. if (axes.yaxis2 && axes.yaxis2.used) { yaxis = axes.yaxis2; }
  232. // map the eventType to a types object
  233. var eventTypeId = event.eventType;
  234. if (this._types === null || !this._types[eventTypeId] || !this._types[eventTypeId].color) {
  235. color = '#666';
  236. } else {
  237. color = this._types[eventTypeId].color;
  238. }
  239. if (this._types === null || !this._types[eventTypeId] || !this._types[eventTypeId].markerSize) {
  240. markerSize = 8; //default marker size
  241. } else {
  242. markerSize = this._types[eventTypeId].markerSize;
  243. }
  244. if (this._types === null || !this._types[eventTypeId] || this._types[eventTypeId].markerShow === undefined) {
  245. markerShow = true;
  246. } else {
  247. markerShow = this._types[eventTypeId].markerShow;
  248. }
  249. if (this._types === null || !this._types[eventTypeId] || this._types[eventTypeId].markerTooltip === undefined) {
  250. markerTooltip = true;
  251. } else {
  252. markerTooltip = this._types[eventTypeId].markerTooltip;
  253. }
  254. if (this._types == null || !this._types[eventTypeId] || !this._types[eventTypeId].lineStyle) {
  255. lineStyle = 'dashed'; //default line style
  256. } else {
  257. lineStyle = this._types[eventTypeId].lineStyle.toLowerCase();
  258. }
  259. if (this._types == null || !this._types[eventTypeId] || this._types[eventTypeId].lineWidth === undefined) {
  260. lineWidth = 1; //default line width
  261. } else {
  262. lineWidth = this._types[eventTypeId].lineWidth;
  263. }
  264. var topOffset = xaxis.options.eventSectionHeight || 0;
  265. topOffset = topOffset / 3;
  266. top = o.top + this._plot.height() + topOffset;
  267. left = xaxis.p2c(event.min) + o.left;
  268. var line = $('<div class="events_line flot-temp-elem"></div>').css({
  269. "position": "absolute",
  270. "opacity": 0.8,
  271. "left": left + 'px',
  272. "top": 8,
  273. "width": lineWidth + "px",
  274. "height": this._plot.height() + topOffset * 0.8,
  275. "border-left-width": lineWidth + "px",
  276. "border-left-style": lineStyle,
  277. "border-left-color": color,
  278. "color": color
  279. })
  280. .appendTo(container);
  281. if (markerShow) {
  282. var marker = $('<div class="events_marker"></div>').css({
  283. "position": "absolute",
  284. "left": (-markerSize - Math.round(lineWidth / 2)) + "px",
  285. "font-size": 0,
  286. "line-height": 0,
  287. "width": 0,
  288. "height": 0,
  289. "border-left": markerSize+"px solid transparent",
  290. "border-right": markerSize+"px solid transparent"
  291. });
  292. marker.appendTo(line);
  293. if (this._types[eventTypeId] && this._types[eventTypeId].position && this._types[eventTypeId].position.toUpperCase() === 'BOTTOM') {
  294. marker.css({
  295. "top": top-markerSize-8 +"px",
  296. "border-top": "none",
  297. "border-bottom": markerSize+"px solid " + color
  298. });
  299. } else {
  300. marker.css({
  301. "top": "0px",
  302. "border-top": markerSize+"px solid " + color,
  303. "border-bottom": "none"
  304. });
  305. }
  306. marker.data({
  307. "event": event
  308. });
  309. var mouseenter = function() {
  310. createAnnotationToolip(marker, $(this).data("event"), that._plot);
  311. };
  312. if (event.editModel) {
  313. createEditPopover(marker, event.editModel, that._plot);
  314. }
  315. var mouseleave = function() {
  316. that._plot.clearSelection();
  317. };
  318. if (markerTooltip) {
  319. marker.css({ "cursor": "help" });
  320. marker.hover(mouseenter, mouseleave);
  321. }
  322. }
  323. var drawableEvent = new DrawableEvent(
  324. line,
  325. function drawFunc(obj) { obj.show(); },
  326. function(obj) { obj.remove(); },
  327. function(obj, position) {
  328. obj.css({
  329. top: position.top,
  330. left: position.left
  331. });
  332. },
  333. left,
  334. top,
  335. line.width(),
  336. line.height()
  337. );
  338. return drawableEvent;
  339. };
  340. /**
  341. * create a DOM element for the given region
  342. */
  343. this._buildRegDiv = function (event) {
  344. var that = this;
  345. var container = this._plot.getPlaceholder();
  346. var o = this._plot.getPlotOffset();
  347. var axes = this._plot.getAxes();
  348. var xaxis = this._plot.getXAxes()[this._plot.getOptions().events.xaxis - 1];
  349. var yaxis, top, left, lineWidth, regionWidth, lineStyle, color, markerTooltip;
  350. // determine the y axis used
  351. if (axes.yaxis && axes.yaxis.used) { yaxis = axes.yaxis; }
  352. if (axes.yaxis2 && axes.yaxis2.used) { yaxis = axes.yaxis2; }
  353. // map the eventType to a types object
  354. var eventTypeId = event.eventType;
  355. if (this._types === null || !this._types[eventTypeId] || !this._types[eventTypeId].color) {
  356. color = '#666';
  357. } else {
  358. color = this._types[eventTypeId].color;
  359. }
  360. if (this._types === null || !this._types[eventTypeId] || this._types[eventTypeId].markerTooltip === undefined) {
  361. markerTooltip = true;
  362. } else {
  363. markerTooltip = this._types[eventTypeId].markerTooltip;
  364. }
  365. if (this._types == null || !this._types[eventTypeId] || this._types[eventTypeId].lineWidth === undefined) {
  366. lineWidth = 1; //default line width
  367. } else {
  368. lineWidth = this._types[eventTypeId].lineWidth;
  369. }
  370. if (this._types == null || !this._types[eventTypeId] || !this._types[eventTypeId].lineStyle) {
  371. lineStyle = 'dashed'; //default line style
  372. } else {
  373. lineStyle = this._types[eventTypeId].lineStyle.toLowerCase();
  374. }
  375. var topOffset = 2;
  376. top = o.top + this._plot.height() + topOffset;
  377. var timeFrom = Math.min(event.min, event.timeEnd);
  378. var timeTo = Math.max(event.min, event.timeEnd);
  379. left = xaxis.p2c(timeFrom) + o.left;
  380. var right = xaxis.p2c(timeTo) + o.left;
  381. regionWidth = right - left;
  382. _.each([left, right], function(position) {
  383. var line = $('<div class="events_line flot-temp-elem"></div>').css({
  384. "position": "absolute",
  385. "opacity": 0.8,
  386. "left": position + 'px',
  387. "top": 8,
  388. "width": lineWidth + "px",
  389. "height": that._plot.height() + topOffset,
  390. "border-left-width": lineWidth + "px",
  391. "border-left-style": lineStyle,
  392. "border-left-color": color,
  393. "color": color
  394. });
  395. line.appendTo(container);
  396. });
  397. var region = $('<div class="events_marker region_marker flot-temp-elem"></div>').css({
  398. "position": "absolute",
  399. "opacity": 0.5,
  400. "left": left + 'px',
  401. "top": top,
  402. "width": Math.round(regionWidth + lineWidth) + "px",
  403. "height": "0.5rem",
  404. "border-left-color": color,
  405. "color": color,
  406. "background-color": color
  407. });
  408. region.appendTo(container);
  409. region.data({
  410. "event": event
  411. });
  412. var mouseenter = function () {
  413. createAnnotationToolip(region, $(this).data("event"), that._plot);
  414. };
  415. if (event.editModel) {
  416. createEditPopover(region, event.editModel, that._plot);
  417. }
  418. var mouseleave = function () {
  419. that._plot.clearSelection();
  420. };
  421. if (markerTooltip) {
  422. region.css({ "cursor": "help" });
  423. region.hover(mouseenter, mouseleave);
  424. }
  425. var drawableEvent = new DrawableEvent(
  426. region,
  427. function drawFunc(obj) { obj.show(); },
  428. function (obj) { obj.remove(); },
  429. function (obj, position) {
  430. obj.css({
  431. top: position.top,
  432. left: position.left
  433. });
  434. },
  435. left,
  436. top,
  437. region.width(),
  438. region.height()
  439. );
  440. return drawableEvent;
  441. };
  442. /**
  443. * check if the event is inside visible range
  444. */
  445. this._insidePlot = function(x) {
  446. var xaxis = this._plot.getXAxes()[this._plot.getOptions().events.xaxis - 1];
  447. var xc = xaxis.p2c(x);
  448. return xc > 0 && xc < xaxis.p2c(xaxis.max);
  449. };
  450. };
  451. /**
  452. * initialize the plugin for the given plot
  453. */
  454. function init(plot) {
  455. /*jshint validthis:true */
  456. var that = this;
  457. var eventMarkers = new EventMarkers(plot);
  458. plot.getEvents = function() {
  459. return eventMarkers._events;
  460. };
  461. plot.hideEvents = function() {
  462. $.each(eventMarkers._events, function(index, event) {
  463. event.visual().getObject().hide();
  464. });
  465. };
  466. plot.showEvents = function() {
  467. plot.hideEvents();
  468. $.each(eventMarkers._events, function(index, event) {
  469. event.hide();
  470. });
  471. that.eventMarkers.drawEvents();
  472. };
  473. // change events on an existing plot
  474. plot.setEvents = function(events) {
  475. if (eventMarkers.eventsEnabled) {
  476. eventMarkers.setupEvents(events);
  477. }
  478. };
  479. plot.hooks.processOptions.push(function(plot, options) {
  480. // enable the plugin
  481. if (options.events.data != null) {
  482. eventMarkers.eventsEnabled = true;
  483. }
  484. });
  485. plot.hooks.draw.push(function(plot) {
  486. var options = plot.getOptions();
  487. if (eventMarkers.eventsEnabled) {
  488. // check for first run
  489. if (eventMarkers.getEvents().length < 1) {
  490. eventMarkers.setTypes(options.events.types);
  491. eventMarkers.setupEvents(options.events.data);
  492. } else {
  493. eventMarkers.updateEvents();
  494. }
  495. }
  496. eventMarkers.drawEvents();
  497. });
  498. }
  499. var defaultOptions = {
  500. events: {
  501. data: null,
  502. types: null,
  503. xaxis: 1,
  504. position: 'BOTTOM'
  505. }
  506. };
  507. $.plot.plugins.push({
  508. init: init,
  509. options: defaultOptions,
  510. name: "events",
  511. version: "0.2.5"
  512. });
  513. });