diff --git a/CHANGELOG.md b/CHANGELOG.md index 090af032f30..f785ad5a0a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 1.9.0 (unreleased) +**Fixes** +- [Issue #1087](https://github.com/grafana/grafana/issues/1087). Panel: Fixed IE9 crash due to angular drag drop +- [Issue #1093](https://github.com/grafana/grafana/issues/1093). SingleStatPanel: Fixed position for drilldown link tooltip when dashboard requires scrolling +- [Issue #1095](https://github.com/grafana/grafana/issues/1095). DrilldownLink: template variables in params property was not interpolated + +# 1.9.0-rc1 (2014-11-17) + **UI Improvements* - [Issue #770](https://github.com/grafana/grafana/issues/770). UI: Panel dropdown menu replaced with a new panel menu @@ -14,6 +21,7 @@ - [Issue #951](https://github.com/grafana/grafana/issues/951). SingleStat: New singlestat panel **Misc** +- [Issue #864](https://github.com/grafana/grafana/issues/846). Panel: Share panel feature, get a link to panel with the current time range - [Issue #938](https://github.com/grafana/grafana/issues/938). Panel: Plugin panels now reside outside of app/panels directory - [Issue #952](https://github.com/grafana/grafana/issues/952). Help: Shortcut "?" to open help modal with list of all shortcuts - [Issue #991](https://github.com/grafana/grafana/issues/991). ScriptedDashboard: datasource services are now available in scripted dashboards, you can query datasource for metric keys, generate dashboards, and even save them in a scripted dashboard (see scripted_gen_and_save.js for example) diff --git a/latest.json b/latest.json index 30dd2d3127b..3573579f26f 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "version": "1.8.1", - "url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.1.tar.gz" + "version": "1.9.0-rc1", + "url": "http://grafanarel.s3.amazonaws.com/grafana-1.9.0-rc1.tar.gz" } diff --git a/package.json b/package.json index 90983011688..a6d7f0c905f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "1.9.0", + "version": "1.9.0-rc1", "repository": { "type": "git", "url": "http://github.com/torkelo/grafana.git" diff --git a/src/app/app.js b/src/app/app.js index 267d0081a90..c90c63b37c0 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -61,7 +61,7 @@ function (angular, $, _, appLevelRequire, config) { var apps_deps = [ 'ngRoute', '$strap.directives', - 'ngDragDrop', + 'ang-drag-drop', 'grafana', 'pasvaz.bindonce' ]; diff --git a/src/app/components/panelmeta.js b/src/app/components/panelmeta.js index 4fd97e9d02d..9b2b2d90104 100644 --- a/src/app/components/panelmeta.js +++ b/src/app/components/panelmeta.js @@ -12,12 +12,12 @@ function () { this.extendedMenu = []; if (options.fullscreen) { - this.addMenuItem('view', 'icon-eye-open', 'toggleFullscreen(false)'); + this.addMenuItem('view', 'icon-eye-open', 'toggleFullscreen(false); dismiss();'); } - this.addMenuItem('edit', 'icon-cog', 'editPanel()'); + this.addMenuItem('edit', 'icon-cog', 'editPanel(); dismiss();'); this.addMenuItem('duplicate', 'icon-copy', 'duplicatePanel()'); - this.addMenuItem('share', 'icon-share', 'sharePanel()'); + this.addMenuItem('share', 'icon-share', 'sharePanel(); dismiss();'); this.addEditorTab('General', 'app/partials/panelgeneral.html'); @@ -25,7 +25,7 @@ function () { this.addEditorTab('Metrics', 'app/partials/metrics.html'); } - this.addExtendedMenuItem('Panel JSON', '', 'editPanelJson()'); + this.addExtendedMenuItem('Panel JSON', '', 'editPanelJson(); dismiss();'); } PanelMeta.prototype.addMenuItem = function(text, icon, click) { diff --git a/src/app/controllers/row.js b/src/app/controllers/row.js index c92155d8d7e..dfd602751fb 100644 --- a/src/app/controllers/row.js +++ b/src/app/controllers/row.js @@ -107,7 +107,7 @@ function (angular, app, _) { var _as = 12 - $scope.dashboard.rowSpan($scope.row); $scope.panel = { - title: 'no title [click here]', + title: 'no title (click here)', error : false, span : _as < defaultSpan && _as > 0 ? _as : defaultSpan, editable: true, diff --git a/src/app/dashboards/scripted.js b/src/app/dashboards/scripted.js index f4a438b7854..8fca0248496 100644 --- a/src/app/dashboards/scripted.js +++ b/src/app/dashboards/scripted.js @@ -22,9 +22,6 @@ var dashboard, timspan; // All url parameters are available via the ARGS object var ARGS; -// Set a default timespan if one isn't specified -timspan = '1d'; - // Intialize a skeleton with nothing but a rows array and service object dashboard = { rows : [], @@ -32,8 +29,12 @@ dashboard = { // Set a title dashboard.title = 'Scripted dash'; + +// set default time +// time can be overriden in the url using from/to parameteres, but this is +// handled automatically in grafana core during dashboard initialization dashboard.time = { - from: "now-" + (ARGS.from || timspan), + from: 'now-6h', to: "now" }; diff --git a/src/app/dashboards/scripted_async.js b/src/app/dashboards/scripted_async.js index 31d23f2dde2..0a50aa44d5e 100644 --- a/src/app/dashboards/scripted_async.js +++ b/src/app/dashboards/scripted_async.js @@ -25,7 +25,7 @@ return function(callback) { var dashboard, timspan; // Set a default timespan if one isn't specified - timspan = '1d'; + timspan = ARGS.from || 'now-1d'; // Intialize a skeleton with nothing but a rows array and service object dashboard = { @@ -36,7 +36,7 @@ return function(callback) { // Set a title dashboard.title = 'Scripted dash'; dashboard.time = { - from: "now-" + (ARGS.from || timspan), + from: timspan, to: "now" }; diff --git a/src/app/dashboards/scripted_templated.js b/src/app/dashboards/scripted_templated.js index 0ca4ea1fde8..9ce145f4606 100644 --- a/src/app/dashboards/scripted_templated.js +++ b/src/app/dashboards/scripted_templated.js @@ -23,7 +23,7 @@ var dashboard, timspan; var ARGS; // Set a default timespan if one isn't specified -timspan = '1d'; +timspan = ARGS.from || 'now-1d'; // Intialize a skeleton with nothing but a rows array and service object dashboard = { @@ -33,7 +33,7 @@ dashboard = { // Set a title dashboard.title = 'Scripted dash'; dashboard.time = { - from: "now-" + (ARGS.from || timspan), + from: timspan, to: "now" }; dashboard.templating = { diff --git a/src/app/directives/graphiteFuncEditor.js b/src/app/directives/graphiteFuncEditor.js index dff8003f54f..2deef5b5246 100644 --- a/src/app/directives/graphiteFuncEditor.js +++ b/src/app/directives/graphiteFuncEditor.js @@ -206,6 +206,7 @@ function (angular, _, $) { if ($target.hasClass('icon-arrow-left')) { $scope.$apply(function() { _.move($scope.functions, $scope.$index, $scope.$index - 1); + $scope.targetChanged(); }); return; } @@ -213,6 +214,7 @@ function (angular, _, $) { if ($target.hasClass('icon-arrow-right')) { $scope.$apply(function() { _.move($scope.functions, $scope.$index, $scope.$index + 1); + $scope.targetChanged(); }); return; } diff --git a/src/app/directives/panelMenu.js b/src/app/directives/panelMenu.js index a4eb3bd210d..4b3970fba39 100644 --- a/src/app/directives/panelMenu.js +++ b/src/app/directives/panelMenu.js @@ -72,7 +72,7 @@ function (angular, $, _) { $link.toggleClass('has-panel-links', showIcon); }); - function dismiss(time) { + function dismiss(time, force) { clearTimeout(timeout); timeout = null; @@ -82,9 +82,11 @@ function (angular, $, _) { } // if hovering or draging pospone close - if ($menu.is(':hover') || $scope.dashboard.$$panelDragging) { - dismiss(2500); - return; + if (force !== true) { + if ($menu.is(':hover') || $scope.dashboard.$$panelDragging) { + dismiss(2200); + return; + } } if (menuScope) { @@ -97,7 +99,12 @@ function (angular, $, _) { } } - var showMenu = function() { + var showMenu = function(e) { + // if menu item is clicked and menu was just removed from dom ignore this event + if (!$.contains(document, e.target)) { + return; + } + if ($menu) { dismiss(); return; @@ -124,6 +131,9 @@ function (angular, $, _) { menuScope = $scope.$new(); menuScope.extendedMenu = getExtendedMenu($scope); + menuScope.dismiss = function() { + dismiss(null, true); + }; $('.panel-menu').remove(); elem.append($menu); @@ -134,7 +144,7 @@ function (angular, $, _) { $(".panel-container").removeClass('panel-highlight'); $panelContainer.toggleClass('panel-highlight'); - dismiss(2500); + dismiss(2200); }; if ($scope.panelMeta.titlePos && $scope.panel.title) { diff --git a/src/app/features/panellinkeditor/linkSrv.js b/src/app/features/panellinkeditor/linkSrv.js index eacdf1d2c35..8d06b22b99d 100644 --- a/src/app/features/panellinkeditor/linkSrv.js +++ b/src/app/features/panellinkeditor/linkSrv.js @@ -29,7 +29,7 @@ function (angular, kbn) { info.href += '&to=' + range.to; if (link.params) { - info.href += "&" + link.params; + info.href += "&" + templateSrv.replace(link.params); } return info; diff --git a/src/app/panels/graph/graph.js b/src/app/panels/graph/graph.js index e0ddfc76402..837196660a6 100755 --- a/src/app/panels/graph/graph.js +++ b/src/app/panels/graph/graph.js @@ -72,10 +72,11 @@ function (angular, $, kbn, moment, _, GraphTooltip) { height = parseInt(height.replace('px', ''), 10); } + height -= 5; // padding height -= scope.panel.title ? 24 : 9; // subtract panel title bar if (scope.panel.legend.show && !scope.panel.legend.rightSide) { - height = height - 21; // subtract one line legend + height = height - 26; // subtract one line legend } elem.css('height', height + 'px'); @@ -114,7 +115,12 @@ function (angular, $, kbn, moment, _, GraphTooltip) { var series = data[i]; var axis = yaxis[series.yaxis - 1]; var formater = kbn.valueFormats[scope.panel.y_formats[series.yaxis - 1]]; - series.updateLegendValues(formater, axis.tickDecimals, axis.scaledDecimals + 2); + + // legend and tooltip gets one more decimal precision + // than graph legend ticks + var tickDecimals = (axis.tickDecimals || -1) + 1; + + series.updateLegendValues(formater, tickDecimals, axis.scaledDecimals + 2); if(!scope.$$phase) { scope.$digest(); } } } diff --git a/src/app/panels/graph/graph.tooltip.js b/src/app/panels/graph/graph.tooltip.js index e0fede2f62d..4c5f727f919 100644 --- a/src/app/panels/graph/graph.tooltip.js +++ b/src/app/panels/graph/graph.tooltip.js @@ -38,18 +38,26 @@ function ($) { }; this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex; + var value, i, series, hoverIndex, seriesTmp; var results = []; - var pointCount = seriesList[0].data.length; - for (i = 1; i < seriesList.length; i++) { - if (seriesList[i].data.length !== pointCount) { + var pointCount; + for (i = 0; i < seriesList.length; i++) { + seriesTmp = seriesList[i]; + if (!seriesTmp.data.length) { continue; } + + if (!pointCount) { + series = seriesTmp; + pointCount = series.data.length; + continue; + } + + if (seriesTmp.data.length !== pointCount) { results.pointCountMismatch = true; return results; } } - series = seriesList[0]; hoverIndex = this.findHoverIndexFromData(pos.x, series); var lasthoverIndex = 0; if(!scope.panel.steppedLine) { @@ -62,6 +70,7 @@ function ($) { for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; + if (!series.data.length) { continue; } if (scope.panel.stack) { if (scope.panel.tooltip.value_type === 'individual') { diff --git a/src/app/panels/graph/module.html b/src/app/panels/graph/module.html index dd9459ab6f9..65fbb00b1f7 100644 --- a/src/app/panels/graph/module.html +++ b/src/app/panels/graph/module.html @@ -4,7 +4,9 @@
- No datapoints Can be caused by timezone mismatch between browser and graphite server + + No datapoints No datapoints returned from metric query + Datapoints outside time range Can be caused by timezone mismatch between browser and graphite server
diff --git a/src/app/panels/singlestat/editor.html b/src/app/panels/singlestat/editor.html index 885b1f20077..3265b3961d7 100644 --- a/src/app/panels/singlestat/editor.html +++ b/src/app/panels/singlestat/editor.html @@ -42,12 +42,12 @@
Coloring
-
+
-
- +
+ diff --git a/src/app/panels/singlestat/module.js b/src/app/panels/singlestat/module.js index c17b5afea38..91dd019c4b7 100644 --- a/src/app/panels/singlestat/module.js +++ b/src/app/panels/singlestat/module.js @@ -125,7 +125,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) { $scope.getDecimalsForValue = function(value) { var opts = {}; - if (value === 0) { + if (value === 0 || value === 1) { return { decimals: 0, scaledDecimals: 0 }; } diff --git a/src/app/panels/singlestat/singleStatPanel.js b/src/app/panels/singlestat/singleStatPanel.js index d1131f29e94..57df1a5eaae 100644 --- a/src/app/panels/singlestat/singleStatPanel.js +++ b/src/app/panels/singlestat/singleStatPanel.js @@ -29,6 +29,7 @@ function (angular, app, _, $) { height = parseInt(height.replace('px', ''), 10); } + height -= 5; // padding height -= panel.title ? 24 : 9; // subtract panel title bar elem.css('height', height + 'px'); @@ -195,7 +196,7 @@ function (angular, app, _, $) { drilldownTooltip.text('click to go to: ' + panel.links[0].title); - drilldownTooltip.place_tt(e.clientX+20, e.clientY-15); + drilldownTooltip.place_tt(e.pageX+20, e.pageY-15); }); } }; diff --git a/src/app/services/influxdb/influxSeries.js b/src/app/services/influxdb/influxSeries.js index e9c8304d1f0..ff6906dd087 100644 --- a/src/app/services/influxdb/influxSeries.js +++ b/src/app/services/influxdb/influxSeries.js @@ -88,7 +88,7 @@ function (_) { _.each(series.points, function (point) { var data = { annotation: self.annotation, - time: point[timeCol] * 1000, + time: point[timeCol], title: point[titleCol], tags: point[tagsCol], text: point[textCol] diff --git a/src/css/less/bootswatch.dark.less b/src/css/less/bootswatch.dark.less index 0ebb73ca0ea..98b91b4b8d8 100644 --- a/src/css/less/bootswatch.dark.less +++ b/src/css/less/bootswatch.dark.less @@ -575,3 +575,12 @@ a:hover { // MEDIA QUERIES // ----------------------------------------------------- + +.caret { + color: @textColor +} + +.dropdown-submenu > a:after { + border-left-color: @textColor; +} + diff --git a/src/css/less/grafana.less b/src/css/less/grafana.less index af44071147e..333820b2374 100644 --- a/src/css/less/grafana.less +++ b/src/css/less/grafana.less @@ -111,28 +111,6 @@ font-size: 12px; } -.panel-fullscreen { - z-index: 100; - display: block; - position: fixed; - left: 0px; - right: 0px; - top: 51px; - height: 100%; - padding: 0 10px; - background: @grafanaPanelBackground; - overflow-y: scroll; - height: 100%; - - .panel-content { - padding-bottom: 130px; - } - - .dropdown-menu { - margin-bottom: 70px; - } -} - .dashboard-fullscreen { .main-view-container { overflow: hidden; @@ -462,8 +440,6 @@ select.grafana-target-segment-input { line-height: 14px; } - - .grafana-tooltip hr { padding: 2px; color: #c8c8c8; diff --git a/src/css/less/panel.less b/src/css/less/panel.less index 5c3dd93c6cf..7d00e2d9cec 100644 --- a/src/css/less/panel.less +++ b/src/css/less/panel.less @@ -10,6 +10,11 @@ background: @grafanaPanelBackground; margin: 5px; position: relative; + &:hover { + .panel-actions { + display: block; + } + } } .panel-content { @@ -27,6 +32,8 @@ font-weight: bold; position: relative; cursor: context-menu; + width: 100%; + display: block; &.has-panel-links { .panel-title-text:after { @@ -72,6 +79,32 @@ bottom: 0; } +.panel-fullscreen { + z-index: 100; + display: block; + position: fixed; + left: 0px; + right: 0px; + top: 51px; + height: 100%; + padding: 0 10px; + background: @grafanaPanelBackground; + overflow-y: scroll; + height: 100%; + + .panel-content { + padding-bottom: 130px; + } + + .dropdown-menu { + margin-bottom: 70px; + } + + .panel-menu { + top: 0px; + } +} + .panel-menu { z-index: 1000; position: absolute; @@ -124,3 +157,5 @@ border: 1px solid @grayDark; } } + + diff --git a/src/css/less/variables.dark.less b/src/css/less/variables.dark.less index 42ab0bba861..dc0e2ba4da0 100644 --- a/src/css/less/variables.dark.less +++ b/src/css/less/variables.dark.less @@ -277,7 +277,7 @@ // Tooltips and popovers // ------------------------- @tooltipColor: #fff; -@tooltipBackground: @heroUnitBackground; +@tooltipBackground: rgb(58, 57, 57); @tooltipArrowWidth: 5px; @tooltipArrowColor: @tooltipBackground; diff --git a/src/test/specs/graph-tooltip-specs.js b/src/test/specs/graph-tooltip-specs.js index 6d1b9a40a58..b7def74ac89 100644 --- a/src/test/specs/graph-tooltip-specs.js +++ b/src/test/specs/graph-tooltip-specs.js @@ -59,6 +59,34 @@ define([ }); }); + describeSharedTooltip("point count missmatch", function(ctx) { + ctx.setup(function() { + ctx.data = [ + { data: [[10, 15], [12, 20]], }, + { data: [[10, 2]] } + ]; + ctx.pos = { x: 11 }; + }); + + it('should set pointCountMismatch to true', function() { + expect(ctx.results.pointCountMismatch).to.be(true); + }); + }); + + describeSharedTooltip("one series is hidden", function(ctx) { + ctx.setup(function() { + ctx.data = [ + { data: [[10, 15], [12, 20]], }, + { data: [] } + ]; + ctx.pos = { x: 11 }; + }); + + it('should set pointCountMismatch to false', function() { + expect(ctx.results.pointCountMismatch).to.be(undefined); + }); + }); + describeSharedTooltip("steppedLine false, stack true, individual false", function(ctx) { ctx.setup(function() { ctx.data = [ diff --git a/src/test/specs/influxSeries-specs.js b/src/test/specs/influxSeries-specs.js index 591e8a9d8f4..ee632748966 100644 --- a/src/test/specs/influxSeries-specs.js +++ b/src/test/specs/influxSeries-specs.js @@ -146,7 +146,7 @@ define([ { columns: ['time', 'text', 'sequence_number', 'title', 'tags'], name: 'events1', - points: [[1402596000, 'some text', 1, 'Hello', 'B'], [1402596001, 'asd', 2, 'Hello2', 'B']] + points: [[1402596000000, 'some text', 1, 'Hello', 'B'], [1402596001000, 'asd', 2, 'Hello2', 'B']] } ], annotation: { @@ -176,7 +176,7 @@ define([ { columns: ['time', 'text', 'sequence_number'], name: 'events1', - points: [[1402596000, 'some text', 1]] + points: [[1402596000000, 'some text', 1]] } ], annotation: { query: 'select' } diff --git a/src/vendor/angular/angular-dragdrop.js b/src/vendor/angular/angular-dragdrop.js index eb982ca0c8e..40fb1c9e814 100644 --- a/src/vendor/angular/angular-dragdrop.js +++ b/src/vendor/angular/angular-dragdrop.js @@ -6,13 +6,14 @@ * To change this template use File | Settings | File Templates. */ -(function(){ +(function(angular){ function isDnDsSupported(){ - return 'draggable' in document.createElement("span"); + return 'ondrag' in document.createElement("a"); } if(!isDnDsSupported()){ + angular.module("ang-drag-drop", []); return; } @@ -22,7 +23,7 @@ if (window.jQuery && (-1 == window.jQuery.event.props.indexOf("dataTransfer"))) var currentData; -angular.module("ngDragDrop",[]) +angular.module("ang-drag-drop",[]) .directive("uiDraggable", [ '$parse', '$rootScope', @@ -71,13 +72,13 @@ angular.module("ngDragDrop",[]) if (e.dataTransfer && e.dataTransfer.dropEffect !== "none") { if (attrs.onDropSuccess) { var fn = $parse(attrs.onDropSuccess); - scope.$apply(function () { + scope.$evalAsync(function () { fn(scope, {$event: e}); }); } else { if (attrs.onDropFailure) { var fn = $parse(attrs.onDropFailure); - scope.$apply(function () { + scope.$evalAsync(function () { fn(scope, {$event: e}); }); } @@ -101,7 +102,7 @@ angular.module("ngDragDrop",[]) if (dragImage) { var dragImageFn = $parse(attrs.dragImage); - scope.$apply(function() { + scope.$evalAsync(function() { var dragImageParameters = dragImageFn(scope, {$event: e}); if (dragImageParameters) { if (angular.isString(dragImageParameters)) { @@ -116,10 +117,10 @@ angular.module("ngDragDrop",[]) }); } - e.dataTransfer.setData("Text", sendData); + e.dataTransfer.setData("dataToSend", sendData); currentData = angular.fromJson(sendData); e.dataTransfer.effectAllowed = "copyMove"; - $rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel); + $rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel, currentData.data); } else { e.preventDefault(); @@ -138,6 +139,8 @@ angular.module("ngDragDrop",[]) var dragChannel = ""; var dragEnterClass = attr.dragEnterClass || "on-drag-enter"; var dragHoverClass = attr.dragHoverClass || "on-drag-hover"; + var customDragEnterEvent = $parse(attr.onDragEnter); + var customDragLeaveEvent = $parse(attr.onDragLeave); function onDragOver(e) { if (e.preventDefault) { @@ -148,20 +151,57 @@ angular.module("ngDragDrop",[]) e.stopPropagation(); } + var fn = $parse(attr.uiOnDragOver); + scope.$evalAsync(function () { + fn(scope, {$event: e, $channel: dropChannel}); + }); + e.dataTransfer.dropEffect = e.shiftKey ? 'copy' : 'move'; return false; } function onDragLeave(e) { - dragging--; - if (dragging == 0) { - element.removeClass(dragHoverClass); - } + if (e.preventDefault) { + e.preventDefault(); + } + + if (e.stopPropagation) { + e.stopPropagation(); + } + dragging--; + + if (dragging == 0) { + scope.$evalAsync(function () { + customDragEnterEvent(scope, {$event: e}); + }); + element.removeClass(dragHoverClass); + } + + var fn = $parse(attr.uiOnDragLeave); + scope.$evalAsync(function () { + fn(scope, {$event: e, $channel: dropChannel}); + }); } function onDragEnter(e) { + if (e.preventDefault) { + e.preventDefault(); + } + + if (e.stopPropagation) { + e.stopPropagation(); + } dragging++; + + var fn = $parse(attr.uiOnDragEnter); + scope.$evalAsync(function () { + fn(scope, {$event: e, $channel: dropChannel}); + }); + $rootScope.$broadcast("ANGULAR_HOVER", dragChannel); + scope.$evalAsync(function () { + customDragLeaveEvent(scope, {$event: e}); + }); element.addClass(dragHoverClass); } @@ -173,11 +213,11 @@ angular.module("ngDragDrop",[]) e.stopPropagation(); // Necessary. Allows us to drop. } - var sendData = e.dataTransfer.getData("Text"); + var sendData = e.dataTransfer.getData("dataToSend"); sendData = angular.fromJson(sendData); var fn = $parse(attr.uiOnDrop); - scope.$apply(function () { + scope.$evalAsync(function () { fn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel}); }); element.removeClass(dragEnterClass); @@ -338,4 +378,4 @@ angular.module("ngDragDrop",[]) } ]); -}()); +}(angular));