Merge branch 'master' into valuepanel

pull/1020/merge
Torkel Ödegaard 11 years ago
commit 22297be3cf
  1. 3
      CHANGELOG.md
  2. 4
      src/app/components/require.config.js
  3. 13
      src/app/components/timeSeries.js
  4. 21
      src/app/controllers/dashboardCtrl.js
  5. 5
      src/app/controllers/influxTargetCtrl.js
  6. 15
      src/app/controllers/row.js
  7. 4
      src/app/directives/grafanaGraph.tooltip.js
  8. 2
      src/app/directives/grafanaPanel.js
  9. 11
      src/app/directives/panelMenu.js
  10. 30
      src/app/partials/dashboard.html
  11. 2
      src/app/partials/influxdb/editor.html
  12. 1
      src/app/services/all.js
  13. 15
      src/app/services/dashboard/dashboardSrv.js
  14. 6
      src/app/services/elasticsearch/es-datasource.js
  15. 5
      src/app/services/graphite/gfunc.js
  16. 9
      src/app/services/influxdb/influxdbDatasource.js
  17. 85
      src/app/services/panelMove.js
  18. 4
      src/css/less/overrides.less
  19. 14
      src/css/less/panel.less
  20. 4
      src/test/test-main.js
  21. 649
      src/vendor/angular/angular-dragdrop.js
  22. 5253
      src/vendor/jquery/jquery-ui-1.10.3.js

@ -6,6 +6,9 @@
- [Issue #877](https://github.com/grafana/grafana/issues/877). Graph: Smart auto decimal precision when using scaled unit formats
- [Issue #850](https://github.com/grafana/grafana/issues/850). Graph: Shared tooltip that shows multiple series & crosshair line, thx @toni-moreno
**Fixes**
- [Issue #925](https://github.com/grafana/grafana/issues/925). Graph: bar width calculation fix for some edge cases (bars would render on top of each other)
=======
# 1.8.1 (2014-09-30)

@ -29,7 +29,6 @@ require.config({
bootstrap: '../vendor/bootstrap/bootstrap',
jquery: '../vendor/jquery/jquery-2.1.1.min',
'jquery-ui': '../vendor/jquery/jquery-ui-1.10.3',
'extend-jquery': 'components/extend-jquery',
@ -76,7 +75,6 @@ require.config({
// simple dependency declaration
//
'jquery-ui': ['jquery'],
'jquery.flot': ['jquery'],
'jquery.flot.pie': ['jquery', 'jquery.flot'],
'jquery.flot.events': ['jquery', 'jquery.flot'],
@ -86,7 +84,7 @@ require.config({
'jquery.flot.time': ['jquery', 'jquery.flot'],
'jquery.flot.crosshair':['jquery', 'jquery.flot'],
'angular-cookies': ['angular'],
'angular-dragdrop': ['jquery','jquery-ui','angular'],
'angular-dragdrop': ['jquery', 'angular'],
'angular-loader': ['angular'],
'angular-mocks': ['angular'],
'angular-resource': ['angular'],

@ -63,8 +63,10 @@ function (_, kbn) {
this.yaxis = this.info.yaxis;
this.stats.total = 0;
this.stats.max = -212312321312;
this.stats.min = 212312321312;
this.stats.max = Number.MIN_VALUE;
this.stats.min = Number.MAX_VALUE;
this.stats.avg = null;
this.stats.current = null;
var ignoreNulls = fillStyle === 'connected';
var nullAsZero = fillStyle === 'null as zero';
@ -97,10 +99,13 @@ function (_, kbn) {
result.push([currentTime * 1000, currentValue]);
}
if (result.length >= 2) {
this.stats.timeStep = result[1][0] - result[0][0];
if (this.datapoints.length >= 2) {
this.stats.timeStep = (this.datapoints[1][1] - this.datapoints[0][1]) * 1000;
}
if (this.stats.max === Number.MIN_VALUE) { this.stats.max = null; }
if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; }
if (result.length) {
this.stats.avg = (this.stats.total / result.length);
this.stats.current = result[result.length-1][1];

@ -18,7 +18,6 @@ function (angular, $, config, _) {
templateValuesSrv,
dashboardSrv,
dashboardViewStateSrv,
panelMoveSrv,
$timeout) {
$scope.editor = { index: 0 };
@ -51,7 +50,6 @@ function (angular, $, config, _) {
// init services
timeSrv.init($scope.dashboard);
templateValuesSrv.init($scope.dashboard, $scope.dashboardViewState);
panelMoveSrv.init($scope.dashboard, $scope);
$scope.checkFeatureToggles();
dashboardKeybindings.shortcuts($scope);
@ -128,5 +126,24 @@ function (angular, $, config, _) {
return $scope.editorTabs;
};
$scope.onDrop = function(panelId, row, dropTarget) {
var info = $scope.dashboard.getPanelInfoById(panelId);
if (dropTarget) {
var dropInfo = $scope.dashboard.getPanelInfoById(dropTarget.id);
dropInfo.row.panels[dropInfo.index] = info.panel;
info.row.panels[info.index] = dropTarget;
var dragSpan = info.panel.span;
info.panel.span = dropTarget.span;
dropTarget.span = dragSpan;
}
else {
info.row.panels.splice(info.index, 1);
info.panel.span = 12 - $scope.dashboard.rowSpan(row);
row.panels.push(info.panel);
}
$rootScope.$broadcast('render');
};
});
});

@ -83,10 +83,11 @@ function (angular) {
};
$scope.listSeries = function(query, callback) {
if (!seriesList || query === '') {
if (query !== '') {
seriesList = [];
$scope.datasource.listSeries().then(function(series) {
$scope.datasource.listSeries(query).then(function(series) {
seriesList = series;
console.log(series);
callback(seriesList);
});
}

@ -145,13 +145,18 @@ function (angular, app, _) {
module.directive('panelDropZone', function() {
return function(scope, element) {
scope.$watch('dashboard.$$panelDragging', function(newVal) {
if (newVal && scope.dashboard.rowSpan(scope.row) < 10) {
scope.$on("ANGULAR_DRAG_START", function() {
var dropZoneSpan = 12 - scope.dashboard.rowSpan(scope.row);
if (dropZoneSpan > 0) {
element.find('.panel-container').css('height', scope.row.height);
element[0].style.width = ((dropZoneSpan / 1.2) * 10) + '%';
element.show();
}
else {
element.hide();
}
});
scope.$on("ANGULAR_DRAG_END", function() {
element.hide();
});
};
});

@ -120,6 +120,10 @@ function ($) {
scope.appEvent('setCrosshair', { pos: pos, scope: scope });
}
if (seriesList.length === 0) {
return;
}
if (scope.panel.tooltip.shared) {
plot.unhighlight();

@ -26,7 +26,7 @@ function (angular, $) {
'<i class="icon-spinner icon-spin icon-large"></i>' +
'</span>' +
'<div class="panel-title-container" panel-menu></div>' +
'<div class="panel-title-container drag-handle" panel-menu></div>' +
'</div>'+
'</div>';

@ -9,15 +9,7 @@ function (angular, $, _) {
angular
.module('grafana.directives')
.directive('panelMenu', function($compile) {
var linkTemplate = '<a class="panel-title">{{panel.title | interpolateTemplateVars}}</a>';
var moveAttributes = ' data-drag=true data-jqyoui-options="kbnJqUiDraggableOptions"'+
' jqyoui-draggable="{'+
'animate:false,'+
'mutate:false,'+
'index:{{$index}},'+
'onStart:\'panelMoveStart\','+
'onStop:\'panelMoveStop\''+
'}" ng-model="panel" ';
var linkTemplate = '<a class="panel-title drag-handle">{{panel.title | interpolateTemplateVars}}</a>';
function createMenuTemplate($scope) {
var template = '<div class="panel-menu small">';
@ -26,7 +18,6 @@ function (angular, $, _) {
template += '<a class="panel-menu-icon pull-left" ng-click="updateColumnSpan(-1)"><i class="icon-minus"></i></a>';
template += '<a class="panel-menu-icon pull-left" ng-click="updateColumnSpan(1)"><i class="icon-plus"></i></a>';
template += '<a class="panel-menu-icon pull-right" ng-click="remove_panel_from_row(row, panel)"><i class="icon-remove"></i></a>';
template += '<a class="panel-menu-icon pull-right" ' + moveAttributes + '><i class="icon-move"></i></a>';
template += '<div class="clearfix"></div>';
template += '</div>';

@ -77,29 +77,25 @@
<div class="panels-wrapper" ng-if="!row.collapse">
<div class="row-text pointer" ng-click="toggle_row(row)" ng-if="row.showTitle" ng-bind="row.title">
</div>
<div class="panel-menu-container" data-menu-container>
<!-- <a class="pointer"><i class="icon&#45;eye&#45;open"></i> <span>view</span></a> -->
<!-- <a class="pointer"><i class="icon&#45;cog"></i> <span>edit</span></a> -->
<!-- <a class="pointer"><i class="icon&#45;resize&#45;horizontal"></i> <span>span</span></a> -->
<!-- <a class="pointer"><i class="icon&#45;copy"></i> <span>duplicate</span></a> -->
<!-- <a class="pointer"><i class="icon&#45;share"></i> <span>share</span></a> -->
<!-- <a class="pointer"><i class="icon&#45;remove"></i> <span>remove</span></a> -->
</div>
<!-- Panels -->
<div ng-repeat="(name, panel) in row.panels"
class="panel nospace"
style="position:relative"
data-drop="true"
panel-width
ng-model="panel"
data-jqyoui-options
jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}"
ng-class="{'dragInProgress':dashboard.$$panelDragging}">
class="panel"
ui-draggable="true" drag="panel.id"
ui-on-Drop="onDrop($data, row, panel)"
drag-handle-class="drag-handle" panel-width ng-model="panel">
<grafana-panel type="panel.type" ng-cloak></grafana-panel>
</div>
<div panel-drop-zone class="panel dragInProgress" style="margin:5px;width:30%;background:rgba(100,100,100,0.50)" ng-style="{height:row.height}" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
<div panel-drop-zone class="panel panel-drop-zone"
ui-on-drop="onDrop($data, row)"
data-drop="true">
<div class="panel-container" style="background: transparent">
<div style="text-align: center">
<em>Drop here</em>
</div>
</div>
</div>
<div class="clearfix"></div>

@ -64,6 +64,8 @@
ng-model="target.series"
spellcheck='false'
bs-typeahead="listSeries"
match-all="true"
min-length="3"
placeholder="series name"
data-min-length=0 data-items=100
ng-blur="seriesBlur()">

@ -7,7 +7,6 @@ define([
'./templateValuesSrv',
'./panelSrv',
'./timer',
'./panelMove',
'./keyboardManager',
'./annotationsSrv',
'./playlistSrv',

@ -91,6 +91,21 @@ function (angular, $, kbn, _, moment) {
row.panels.push(panel);
};
p.getPanelInfoById = function(panelId) {
var result = {};
_.each(this.rows, function(row) {
_.each(row.panels, function(panel, index) {
if (panel.id === panelId) {
result.panel = panel;
result.row = row;
result.index = index;
}
});
});
return result;
};
p.duplicatePanel = function(panel, row) {
var rowIndex = _.indexOf(this.rows, row);
var newPanel = angular.copy(panel);

@ -279,11 +279,11 @@ function (angular, _, config, kbn, moment) {
return { dashboards: [], tags: [] };
}
var resultsHits = results.hits;
var resultsHits = results.hits.hits;
var displayHits = { dashboards: [], tags: results.facets.tags.terms || [] };
for (var i = 0, len = resultsHits.total; i < len; i++) {
var hit = resultsHits.hits[i];
for (var i = 0, len = resultsHits.length; i < len; i++) {
var hit = resultsHits[i];
displayHits.dashboards.push({
id: hit._id,
title: hit._source.title,

@ -155,7 +155,10 @@ function (_) {
addFuncDef({
name: 'averageSeriesWithWildcards',
category: categories.Combine,
params: [{ name: "node", type: "int" }],
params: [
{ name: "node", type: "int" },
{ name: "node", type: "int", optional: true },
],
defaultParams: [3]
});

@ -85,8 +85,13 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
});
};
InfluxDatasource.prototype.listSeries = function() {
return this._seriesQuery('list series').then(function(data) {
InfluxDatasource.prototype.listSeries = function(query) {
// wrap in regex
if (query && query.length > 0 && query[0] !== '/') {
query = '/' + query + '/';
}
return this._seriesQuery('list series ' + query).then(function(data) {
if (!data || data.length === 0) {
return [];
}

@ -1,85 +0,0 @@
define([
'angular',
'lodash'
],
function (angular, _) {
'use strict';
var module = angular.module('grafana.services');
module.service('panelMoveSrv', function($rootScope) {
function PanelMoveSrv(dashboard) {
this.dashboard = dashboard;
_.bindAll(this, 'onStart', 'onOver', 'onOut', 'onDrop', 'onStop', 'cleanup');
}
var p = PanelMoveSrv.prototype;
/* each of these can take event,ui,data parameters */
p.onStart = function() {
this.dashboard.$$panelDragging = true;
$rootScope.$apply();
};
p.onOver = function() {
$rootScope.$apply();
};
p.onOut = function() {
$rootScope.$apply();
};
/*
Use our own drop logic. the $parent.$parent this is ugly.
*/
p.onDrop = function(event,ui,data) {
var
dragRow = data.draggableScope.$parent.$parent.row.panels,
dropRow = data.droppableScope.$parent.$parent.row.panels,
dragIndex = data.dragSettings.index,
dropIndex = data.dropSettings.index;
// Remove panel from source row
dragRow.splice(dragIndex,1);
// Add to destination row
if (!_.isUndefined(dropRow)) {
dropRow.splice(dropIndex,0,data.dragItem);
}
this.dashboard.$$panelDragging = false;
// Cleanup nulls/undefined left behind
this.cleanup();
$rootScope.$apply();
$rootScope.$broadcast('render');
};
p.onStop = function() {
this.dashboard.$$panelDragging = false;
this.cleanup();
$rootScope.$apply();
};
p.cleanup = function () {
_.each(this.dashboard.rows, function(row) {
row.panels = _.without(row.panels,{});
row.panels = _.compact(row.panels);
});
};
return {
init: function(dashboard, scope) {
var panelMove = new PanelMoveSrv(dashboard);
scope.panelMoveDrop = panelMove.onDrop;
scope.panelMoveStart = panelMove.onStart;
scope.panelMoveStop = panelMove.onStop;
scope.panelMoveOver = panelMove.onOver;
scope.panelMoveOut = panelMove.onOut;
}
};
});
});

@ -231,10 +231,6 @@ form input.ng-invalid {
z-index: 9999;
}
.dragInProgress .panel-container {
border: 3px solid rgba(100,100,100,0.50);
}
.link {
color: @linkColor;
cursor: pointer;

@ -2,6 +2,7 @@
display: inline-block;
float: left;
vertical-align: top;
position: relative;
}
.panel-container {
@ -98,3 +99,16 @@
.panel-highlight {
.box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236, 0.8)");
}
.on-drag-hover {
.panel-container {
.box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236, 0.8)");
}
}
.panel-drop-zone {
display: none;
.panel-container {
border: 1px solid @grayDark;
}
}

@ -32,8 +32,6 @@ require.config({
bootstrap: '../vendor/bootstrap/bootstrap',
'bootstrap-tagsinput': '../vendor/tagsinput/bootstrap-tagsinput',
'jquery-ui': '../vendor/jquery/jquery-ui-1.10.3',
'extend-jquery': 'components/extend-jquery',
'jquery.flot': '../vendor/jquery/jquery.flot',
@ -82,7 +80,7 @@ require.config({
'angular-route': ['angular'],
'angular-cookies': ['angular'],
'angular-dragdrop': ['jquery','jquery-ui','angular'],
'angular-dragdrop': ['jquery', 'angular'],
'angular-loader': ['angular'],
'angular-mocks': ['angular'],
'angular-resource': ['angular'],

@ -1,338 +1,341 @@
/**
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
* Created with IntelliJ IDEA.
* User: Ganaraj.Pr
* Date: 11/10/13
* Time: 11:27
* To change this template use File | Settings | File Templates.
*/
/**
* Implementing Drag and Drop functionality in AngularJS is easier than ever.
* Demo: http://codef0rmer.github.com/angular-dragdrop/
*
* @version 1.0.4
*
* (c) 2013 Amit Gharat a.k.a codef0rmer <amit.2006.it@gmail.com> - amitgharat.wordpress.com
*/
(function(){
function isDnDsSupported(){
return 'draggable' in document.createElement("span");
}
if(!isDnDsSupported()){
return;
}
if (window.jQuery && (-1 == window.jQuery.event.props.indexOf("dataTransfer"))) {
window.jQuery.event.props.push("dataTransfer");
}
var currentData;
angular.module("ngDragDrop",[])
.directive("uiDraggable", [
'$parse',
'$rootScope',
'$dragImage',
function ($parse, $rootScope, $dragImage) {
return function (scope, element, attrs) {
var dragData = "",
isDragHandleUsed = false,
dragHandleClass,
draggingClass = attrs.draggingClass || "on-dragging",
dragTarget;
element.attr("draggable", false);
attrs.$observe("uiDraggable", function (newValue) {
if(newValue){
element.attr("draggable", newValue);
}
else{
element.removeAttr("draggable");
}
});
if (attrs.drag) {
scope.$watch(attrs.drag, function (newValue) {
dragData = newValue || "";
});
}
/**
* This has been modified from the default angular-draganddrop to provide the original
* model and some other stuff as the 'data' arguement to callEventCallback
*/
if (angular.isString(attrs.dragHandleClass)) {
isDragHandleUsed = true;
dragHandleClass = attrs.dragHandleClass.trim() || "drag-handle";
(function (window, angular, undefined) {
'use strict';
var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) {
this.callEventCallback = function (scope, callbackName, event, ui, data) {
if (!callbackName) {
return;
}
var args = [event, ui, data];
var match = callbackName.match(/^(.+)\((.+)\)$/);
if (match !== null) {
callbackName = match[1];
var values = eval('[' + match[0].replace(/^(.+)\(/, '').replace(/\)/, '') + ']');
args.push.apply(args, values);
}
if(scope[callbackName]) {
scope[callbackName].apply(scope, args);
}
};
this.invokeDrop = function ($draggable, $droppable, event, ui) {
var dragModel = '',
dropModel = '',
dragSettings = {},
dropSettings = {},
jqyoui_pos = null,
dragItem = {},
dropItem = {},
dragModelValue,
dropModelValue,
$droppableDraggable = null,
droppableScope = $droppable.scope(),
draggableScope = $draggable.scope(),
data = {};
dragModel = $draggable.ngattr('ng-model');
dropModel = $droppable.ngattr('ng-model');
dragModelValue = draggableScope.$eval(dragModel);
dropModelValue = droppableScope.$eval(dropModel);
$droppableDraggable = $droppable.find('[jqyoui-draggable]:last');
dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable')) || [];
dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable')) || [];
// Helps pick up the right item
dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);
jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue;
if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
dropItem = dropModelValue[dropSettings.index];
} else if (!angular.isArray(dropModelValue)) {
dropItem = dropModelValue;
} else {
dropItem = {};
}
data = {
dragModel: dragModel,
dropModel: dropModel,
dragSettings: dragSettings,
dropSettings: dropSettings,
jqyoui_pos: jqyoui_pos,
dragItem: dragItem,
dropItem: dropItem,
dragModelValue: dragModelValue,
dropModelValue: dropModelValue,
droppableScope: $droppable.scope(),
draggableScope: $draggable.scope()
};
if (dragSettings.animate === true) {
this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, function() {
$timeout(function() {
// Do not move this into move() to avoid flickering issue
$draggable.css({'position': 'relative', 'left': '', 'top': ''});
$droppableDraggable.css({'position': 'relative', 'left': '', 'top': ''});
if(dragSettings.mutate !== false) {
this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
}
element.bind("mousedown", function (e) {
dragTarget = e.target;
});
}
if(dropSettings.mutate !== false) {
this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
}
function dragendHandler(e) {
setTimeout(function() {
element.unbind('$destroy', dragendHandler);
}, 0);
var sendChannel = attrs.dragChannel || "defaultchannel";
$rootScope.$broadcast("ANGULAR_DRAG_END", sendChannel);
if (e.dataTransfer && e.dataTransfer.dropEffect !== "none") {
if (attrs.onDropSuccess) {
var fn = $parse(attrs.onDropSuccess);
scope.$apply(function () {
fn(scope, {$event: e});
});
} else {
if (attrs.onDropFailure) {
var fn = $parse(attrs.onDropFailure);
scope.$apply(function () {
fn(scope, {$event: e});
});
}
}
}
element.removeClass(draggingClass);
}
this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data);
}.bind(this));
}.bind(this));
} else {
$timeout(function() {
if(dragSettings.mutate !== false) {
this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
}
if(dropSettings.mutate !== false) {
this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
}
this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data);
}.bind(this));
}
};
this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) {
if ($fromEl.length === 0) {
if (callback) {
window.setTimeout(function() {
callback();
}, 300);
}
return false;
}
var zIndex = 9999,
fromPos = $fromEl.offset(),
wasVisible = $toEl && $toEl.is(':visible');
if (toPos === null && $toEl.length > 0) {
if ($toEl.attr('jqyoui-draggable') !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
toPos = $toEl.offset();
if (dropSettings.stack === false) {
toPos.left+= $toEl.outerWidth(true);
} else {
toPos.top+= $toEl.outerHeight(true);
}
} else {
toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'}).offset();
$toEl.css({'visibility': '','display': wasVisible ? '' : 'none'});
element.bind("dragend", dragendHandler);
element.bind("dragstart", function (e) {
var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);
if (isDragAllowed) {
var sendChannel = attrs.dragChannel || "defaultchannel";
var sendData = angular.toJson({ data: dragData, channel: sendChannel });
var dragImage = attrs.dragImage || null;
element.addClass(draggingClass);
element.bind('$destroy', dragendHandler);
if (dragImage) {
var dragImageFn = $parse(attrs.dragImage);
scope.$apply(function() {
var dragImageParameters = dragImageFn(scope, {$event: e});
if (dragImageParameters) {
if (angular.isString(dragImageParameters)) {
dragImageParameters = $dragImage.generate(dragImageParameters);
}
if (dragImageParameters.image) {
var xOffset = dragImageParameters.xOffset || 0,
yOffset = dragImageParameters.yOffset || 0;
e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
}
}
});
}
e.dataTransfer.setData("Text", sendData);
currentData = angular.fromJson(sendData);
e.dataTransfer.effectAllowed = "copyMove";
$rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel);
}
else {
e.preventDefault();
}
});
};
}
}
])
.directive("uiOnDrop", [
'$parse',
'$rootScope',
function ($parse, $rootScope) {
return function (scope, element, attr) {
var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
var dropChannel = attr.dropChannel || "defaultchannel" ;
var dragChannel = "";
var dragEnterClass = attr.dragEnterClass || "on-drag-enter";
var dragHoverClass = attr.dragHoverClass || "on-drag-hover";
function onDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
if (e.stopPropagation) {
e.stopPropagation();
}
e.dataTransfer.dropEffect = e.shiftKey ? 'copy' : 'move';
return false;
}
$fromEl.css({'position': 'absolute', 'z-index': zIndex})
.css(fromPos)
.animate(toPos, duration, function() {
if (callback) callback();
});
};
function onDragLeave(e) {
dragging--;
if (dragging == 0) {
element.removeClass(dragHoverClass);
}
}
this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
var dropModelValue = scope.$eval(dropModel);
function onDragEnter(e) {
dragging++;
$rootScope.$broadcast("ANGULAR_HOVER", dragChannel);
element.addClass(dragHoverClass);
}
scope.__dragItem = dragItem;
function onDrop(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
if (e.stopPropagation) {
e.stopPropagation(); // Necessary. Allows us to drop.
}
var sendData = e.dataTransfer.getData("Text");
sendData = angular.fromJson(sendData);
var fn = $parse(attr.uiOnDrop);
scope.$apply(function () {
fn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel});
});
element.removeClass(dragEnterClass);
dragging = 0;
}
if (angular.isArray(dropModelValue)) {
if (dropSettings && dropSettings.index >= 0) {
dropModelValue[dropSettings.index] = dragItem;
} else {
dropModelValue.push(dragItem);
}
if (dragSettings && dragSettings.placeholder === true) {
dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
}
} else {
$parse(dropModel + ' = __dragItem')(scope);
if (dragSettings && dragSettings.placeholder === true) {
dropModelValue['jqyoui_pos'] = jqyoui_pos;
}
}
};
this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
var isEmpty = angular.equals(angular.copy(dropItem), {}),
dragModelValue = scope.$eval(dragModel);
scope.__dropItem = dropItem;
if (dragSettings && dragSettings.placeholder) {
if (dragSettings.placeholder != 'keep'){
if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
dragModelValue[dragSettings.index] = dropItem;
} else {
$parse(dragModel + ' = __dropItem')(scope);
}
}
} else {
if (angular.isArray(dragModelValue)) {
if (isEmpty) {
if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) {
dragModelValue.splice(dragSettings.index, 1);
}
} else {
dragModelValue[dragSettings.index] = dropItem;
}
} else {
// Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
// P.S.: Could not figure out why it happened
$parse(dragModel + ' = __dropItem')(scope);
if (scope.$parent) {
$parse(dragModel + ' = __dropItem')(scope.$parent);
}
function isDragChannelAccepted(dragChannel, dropChannel) {
if (dropChannel === "*") {
return true;
}
var channelMatchPattern = new RegExp("(\\s|[,])+(" + dragChannel + ")(\\s|[,])+", "i");
return channelMatchPattern.test("," + dropChannel + ",");
}
function preventNativeDnD(e) {
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
e.dataTransfer.dropEffect = "none";
return false;
}
var deregisterDragStart = $rootScope.$on("ANGULAR_DRAG_START", function (event, channel) {
dragChannel = channel;
if (isDragChannelAccepted(channel, dropChannel)) {
if (attr.dropValidate) {
var validateFn = $parse(attr.dropValidate);
var valid = validateFn(scope, {$data: currentData.data, $channel: currentData.channel});
if (!valid) {
element.bind("dragover", preventNativeDnD);
element.bind("dragenter", preventNativeDnD);
element.bind("dragleave", preventNativeDnD);
element.bind("drop", preventNativeDnD);
return;
}
}
element.bind("dragover", onDragOver);
element.bind("dragenter", onDragEnter);
element.bind("dragleave", onDragLeave);
element.bind("drop", onDrop);
element.addClass(dragEnterClass);
}
else {
element.bind("dragover", preventNativeDnD);
element.bind("dragenter", preventNativeDnD);
element.bind("dragleave", preventNativeDnD);
element.bind("drop", preventNativeDnD);
}
});
var deregisterDragEnd = $rootScope.$on("ANGULAR_DRAG_END", function (e, channel) {
dragChannel = "";
if (isDragChannelAccepted(channel, dropChannel)) {
element.unbind("dragover", onDragOver);
element.unbind("dragenter", onDragEnter);
element.unbind("dragleave", onDragLeave);
element.unbind("drop", onDrop);
element.removeClass(dragHoverClass);
element.removeClass(dragEnterClass);
}
element.unbind("dragover", preventNativeDnD);
element.unbind("dragenter", preventNativeDnD);
element.unbind("dragleave", preventNativeDnD);
element.unbind("drop", preventNativeDnD);
});
var deregisterDragHover = $rootScope.$on("ANGULAR_HOVER", function (e, channel) {
if (isDragChannelAccepted(channel, dropChannel)) {
element.removeClass(dragHoverClass);
}
});
scope.$on('$destroy', function () {
deregisterDragStart();
deregisterDragEnd();
deregisterDragHover();
});
attr.$observe('dropChannel', function (value) {
if (value) {
dropChannel = value;
}
});
};
}
}
$draggable.css({'z-index': '', 'left': '', 'top': ''});
};
this.fixIndex = function(scope, settings, modelValue) {
if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) {
var dragModelValueFiltered = scope[settings.applyFilter](),
lookup = dragModelValueFiltered[settings.index],
actualIndex = undefined;
modelValue.forEach(function(item, i) {
if (angular.equals(item, lookup)) {
actualIndex = i;
}
});
return actualIndex;
}
return settings.index;
};
}]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) {
return {
require: '?jqyouiDroppable',
restrict: 'A',
link: function(scope, element, attrs) {
// grafana change, remove watcher after first evaluation
var dragSettings, zIndex, removeWatcher;
var updateDraggable = function(newValue, oldValue) {
if (newValue) {
removeWatcher();
dragSettings = scope.$eval(element.attr('jqyoui-draggable')) || [];
element
.draggable({disabled: false})
.draggable(scope.$eval(attrs.jqyouiOptions) || {})
.draggable({
start: function(event, ui) {
zIndex = angular.element(this).css('z-index');
angular.element(this).css('z-index', 99999);
jqyoui.startXY = angular.element(this).offset();
ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
},
stop: function(event, ui) {
angular.element(this).css('z-index', zIndex);
ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
},
drag: function(event, ui) {
ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
])
.constant("$dragImageConfig", {
height: 20,
width: 200,
padding: 10,
font: 'bold 11px Arial',
fontColor: '#eee8d5',
backgroundColor: '#93a1a1',
xOffset: 0,
yOffset: 0
})
.service("$dragImage", [
'$dragImageConfig',
function (defaultConfig) {
var ELLIPSIS = '…';
function fitString(canvas, text, config) {
var width = canvas.measureText(text).width;
if (width < config.width) {
return text;
}
});
} else {
element.draggable({disabled: true});
}
};
removeWatcher = scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable);
updateDraggable();
}
};
}]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) {
return {
restrict: 'A',
priority: 1,
link: function(scope, element, attrs) {
// grafana change, remove watcher after first evaluation
var removeWatcher;
var updateDroppable = function(newValue, oldValue) {
if (newValue) {
removeWatcher();
element
.droppable({disabled: false})
.droppable(scope.$eval(attrs.jqyouiOptions) || {})
.droppable({
over: function(event, ui) {
var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
},
out: function(event, ui) {
var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
},
drop: function(event, ui) {
if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
} else {
ngDragDropService.callEventCallback(scope, (scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []).onDrop, event, ui);
}
while (width + config.padding > config.width) {
text = text.substring(0, text.length - 1);
width = canvas.measureText(text + ELLIPSIS).width;
}
});
} else {
element.droppable({disabled: true});
}
};
removeWatcher = scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable);
updateDroppable();
}
};
}]);
$.fn.ngattr = function(name, value) {
var element = angular.element(this).get(0);
return element.getAttribute(name) || element.getAttribute('data-' + name);
};
})(window, window.angular);
return text + ELLIPSIS;
};
this.generate = function (text, options) {
var config = angular.extend({}, defaultConfig, options || {});
var el = document.createElement('canvas');
el.height = config.height;
el.width = config.width;
var canvas = el.getContext('2d');
canvas.fillStyle = config.backgroundColor;
canvas.fillRect(0, 0, config.width, config.height);
canvas.font = config.font;
canvas.fillStyle = config.fontColor;
var title = fitString(canvas, text, config);
canvas.fillText(title, 4, config.padding + 4);
var image = new Image();
image.src = el.toDataURL();
return {
image: image,
xOffset: config.xOffset,
yOffset: config.yOffset
};
}
}
]);
}());

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save