summaryrefslogtreecommitdiffstats
path: root/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget
diff options
context:
space:
mode:
Diffstat (limited to 'ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget')
-rw-r--r--ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js246
-rw-r--r--ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js164
-rw-r--r--ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js64
-rw-r--r--ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js104
4 files changed, 578 insertions, 0 deletions
diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js
new file mode 100644
index 00000000..9ac57b19
--- /dev/null
+++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+angular.module('ui.dashboard')
+ .controller('DashboardWidgetCtrl', ['$scope', '$element', '$compile', '$window', '$timeout',
+ function($scope, $element, $compile, $window, $timeout) {
+
+ $scope.status = {
+ isopen: false
+ };
+
+ // Fills "container" with compiled view
+ $scope.makeTemplateString = function() {
+
+ var widget = $scope.widget;
+
+ // First, build template string
+ var templateString = '';
+
+ if (widget.templateUrl) {
+
+ // Use ng-include for templateUrl
+ templateString = '<div ng-include="\'' + widget.templateUrl + '\'"></div>';
+
+ } else if (widget.template) {
+
+ // Direct string template
+ templateString = widget.template;
+
+ } else {
+
+ // Assume attribute directive
+ templateString = '<div ' + widget.directive;
+
+ // Check if data attribute was specified
+ if (widget.dataAttrName) {
+ widget.attrs = widget.attrs || {};
+ widget.attrs[widget.dataAttrName] = 'widgetData';
+ }
+
+ // Check for specified attributes
+ if (widget.attrs) {
+
+ // First check directive name attr
+ if (widget.attrs[widget.directive]) {
+ templateString += '="' + widget.attrs[widget.directive] + '"';
+ }
+
+ // Add attributes
+ _.each(widget.attrs, function(value, attr) {
+
+ // make sure we aren't reusing directive attr
+ if (attr !== widget.directive) {
+ templateString += ' ' + attr + '="' + value + '"';
+ }
+
+ });
+ }
+ templateString += '></div>';
+ }
+ return templateString;
+ };
+
+ $scope.grabResizer = function(e) {
+
+ var widget = $scope.widget;
+ var widgetElm = $element.find('.widget');
+
+ // ignore middle- and right-click
+ if (e.which !== 1) {
+ return;
+ }
+
+ e.stopPropagation();
+ e.originalEvent.preventDefault();
+
+ // get the starting horizontal position
+ var initX = e.clientX;
+ // console.log('initX', initX);
+
+ // Get the current width of the widget and dashboard
+ var pixelWidth = widgetElm.width();
+ var pixelHeight = widgetElm.height();
+ var widgetStyleWidth = widget.containerStyle.width;
+ var widthUnits = widget.widthUnits;
+ var unitWidth = parseFloat(widgetStyleWidth);
+
+ // create marquee element for resize action
+ var $marquee = angular.element('<div class="widget-resizer-marquee" style="height: ' + pixelHeight + 'px; width: ' + pixelWidth + 'px; z-index:'+ 200 +';"></div>');
+ widgetElm.append($marquee);
+ // create an overlaying div to block other widgets in order to stop their iframe events from being triggered
+ var $marquee2 = angular.element('<div style=" position: absolute; top: 0; left: 0; height: ' + pixelHeight + 'px; width: ' + (pixelWidth+200) + 'px; z-index:'+ 100 +';"></div>');
+ widgetElm.append($marquee2);
+
+ // determine the unit/pixel ratio
+ var transformMultiplier = unitWidth / pixelWidth;
+
+ // updates marquee with preview of new width
+ var mousemove = function(e) {
+ var curX = e.clientX;
+// console.log(curX);
+// console.log(e);
+ var pixelChange = curX - initX;
+ var newWidth = pixelWidth + pixelChange;
+ $marquee.css('width', newWidth + 'px');
+ $marquee2.css('width', (newWidth + 200) + 'px');
+
+ };
+
+ // sets new widget width on mouseup
+ var mouseup = function(e) {
+ // remove listener and marquee
+ jQuery($window).off('mousemove', mousemove);
+ $marquee.remove();
+ $marquee2.remove();
+
+ // calculate change in units
+ var curX = e.clientX;
+ var pixelChange = curX - initX;
+ var unitChange = Math.round(pixelChange * transformMultiplier * 100) / 100;
+
+ // add to initial unit width
+ var newWidth = unitWidth * 1 + unitChange;
+ widget.setWidth(newWidth, widthUnits);
+ $scope.$emit('widgetChanged', widget);
+ $scope.$apply();
+ $scope.$broadcast('widgetResized', {
+ width: newWidth
+ });
+ };
+
+// jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup);
+ jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup);
+ };
+
+ //TODO refactor
+ $scope.grabSouthResizer = function(e) {
+ var widgetElm = $element.find('.widget');
+
+ // ignore middle- and right-click
+ if (e.which !== 1) {
+ return;
+ }
+
+ e.stopPropagation();
+ e.originalEvent.preventDefault();
+
+ // get the starting horizontal position
+ var initY = e.clientY;
+ // console.log('initX', initX);
+
+ // Get the current width of the widget and dashboard
+ var pixelWidth = widgetElm.width();
+ var pixelHeight = widgetElm.height();
+
+ // create marquee element for resize action
+ var $marquee = angular.element('<div class="widget-resizer-marquee" style="height: ' + pixelHeight + 'px; width: ' + pixelWidth + 'px;"></div>');
+ widgetElm.append($marquee);
+
+ // updates marquee with preview of new height
+ var mousemove = function(e) {
+ var curY = e.clientY;
+ var pixelChange = curY - initY;
+ var newHeight = pixelHeight + pixelChange;
+ $marquee.css('height', newHeight + 'px');
+ };
+
+ // sets new widget width on mouseup
+ var mouseup = function(e) {
+ // remove listener and marquee
+ jQuery($window).off('mousemove', mousemove);
+ $marquee.remove();
+
+ // calculate height change
+ var curY = e.clientY;
+ var pixelChange = curY - initY;
+
+ //var widgetContainer = widgetElm.parent(); // widget container responsible for holding widget width and height
+ var widgetContainer = widgetElm.find('.widget-content');
+
+ var diff = pixelChange;
+ var height = parseInt(widgetContainer.css('height'), 10);
+ var newHeight = (height + diff);
+
+ //$scope.widget.style.height = newHeight + 'px';
+
+ $scope.widget.setHeight(newHeight + 'px');
+
+ $scope.$emit('widgetChanged', $scope.widget);
+ $scope.$apply(); // make AngularJS to apply style changes
+
+ $scope.$broadcast('widgetResized', {
+ height: newHeight
+ });
+ };
+
+ jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup);
+ };
+
+ // replaces widget title with input
+ $scope.editTitle = function(widget) {
+ var widgetElm = $element.find('.widget');
+ widget.editingTitle = true;
+ // HACK: get the input to focus after being displayed.
+ $timeout(function() {
+ widgetElm.find('form.widget-title input:eq(0)').focus()[0].setSelectionRange(0, 9999);
+ });
+ };
+
+ // saves whatever is in the title input as the new title
+ $scope.saveTitleEdit = function(widget) {
+ widget.editingTitle = false;
+ $scope.$emit('widgetChanged', widget);
+ };
+
+ $scope.compileTemplate = function() {
+ var container = $scope.findWidgetContainer($element);
+ var templateString = $scope.makeTemplateString();
+ var widgetElement = angular.element(templateString);
+
+ container.empty();
+ container.append(widgetElement);
+ $compile(widgetElement)($scope);
+ };
+
+ $scope.findWidgetContainer = function(element) {
+ // widget placeholder is the first (and only) child of .widget-content
+ return element.find('.widget-content');
+ };
+ }
+ ]); \ No newline at end of file
diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js
new file mode 100644
index 00000000..55604646
--- /dev/null
+++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js
@@ -0,0 +1,164 @@
+'use strict';
+
+describe('Controller: DashboardWidgetCtrl', function() {
+
+ var $scope, $element, $timeout, injections;
+
+ beforeEach(module('ui.dashboard'));
+
+ beforeEach(inject(function($rootScope, $controller){
+ $scope = $rootScope.$new();
+ $element = angular.element('<div><div class="widget"></div></div>');
+ $timeout = function timeout(fn) {
+ fn();
+ };
+ injections = {
+ $scope: $scope,
+ $element: $element,
+ $timeout: $timeout
+ };
+ spyOn(injections, '$timeout');
+ $controller('DashboardWidgetCtrl', injections);
+ }));
+
+ describe('the makeTemplateString method', function() {
+
+ it('should return a string', function() {
+ $scope.widget = {
+ templateUrl: 'some/template.html'
+ };
+ expect(typeof $scope.makeTemplateString()).toEqual('string');
+ });
+
+ it('should use ng-include if templateUrl is specified on widget, despite any other options', function() {
+ $scope.widget = {
+ templateUrl: 'some/template.html',
+ template: 'not this one',
+ directive: 'or-this',
+ attrs: {
+ something: 'awesome',
+ other: 'thing'
+ }
+ };
+ expect($scope.makeTemplateString()).toMatch(/ng-include="'some\/template\.html'"/);
+ });
+
+ it('should return widget.template if specified, regardless of presence of directive or attrs', function() {
+ $scope.widget = {
+ template: '<div class="testing"></div>',
+ directive: 'no-good'
+ };
+ expect($scope.makeTemplateString()).toEqual($scope.widget.template);
+ });
+
+ it('should use widget.directive as attribute directive', function() {
+ $scope.widget = {
+ directive: 'ng-awesome'
+ };
+ expect($scope.makeTemplateString()).toEqual('<div ng-awesome></div>');
+ });
+
+ it('should attach attributes if provided', function() {
+ $scope.widget = {
+ directive: 'ng-awesome',
+ attrs: {
+ 'ng-awesome': 'test1',
+ other: 'attr',
+ more: 'stuff'
+ }
+ };
+ expect($scope.makeTemplateString()).toEqual('<div ng-awesome="test1" other="attr" more="stuff"></div>');
+ });
+
+ it('should place widgetData into dataAttrName attribute if specified', function() {
+ $scope.widget = {
+ directive: 'ng-awesome',
+ attrs: {
+ 'ng-awesome': 'test1',
+ other: 'attr',
+ more: 'stuff'
+ },
+ dataAttrName: 'data'
+ };
+ expect($scope.makeTemplateString()).toEqual('<div ng-awesome="test1" other="attr" more="stuff" data="widgetData"></div>');
+ });
+
+ it('should add attrs to the widget object if it does not exist and dataAttrName is specified', function() {
+ $scope.widget = {
+ directive: 'ng-awesome',
+ dataAttrName: 'data'
+ };
+ expect($scope.makeTemplateString()).toEqual('<div ng-awesome data="widgetData"></div>');
+ });
+
+ });
+
+ describe('the grabResizer method', function() {
+
+ var evt, widget, WidgetModel;
+
+ beforeEach(inject(function (_WidgetModel_) {
+ WidgetModel = _WidgetModel_;
+ }));
+
+ beforeEach(function() {
+ evt = {
+ stopPropagation: jasmine.createSpy('stopPropagation'),
+ originalEvent: {
+ preventDefault: jasmine.createSpy('preventDefault')
+ },
+ clientX: 100,
+ which: 1
+ };
+ $scope.widget = widget = new WidgetModel({
+ style: {
+ width: '30%'
+ }
+ });
+ });
+
+ it('should do nothing if event.which is not 1 (left click)', function() {
+ evt.which = 2;
+ $scope.grabResizer(evt);
+ expect(evt.stopPropagation).not.toHaveBeenCalled();
+ });
+
+ it('should call stopPropagation and preventDefault', function() {
+ $scope.grabResizer(evt);
+ expect(evt.stopPropagation).toHaveBeenCalled();
+ expect(evt.originalEvent.preventDefault).toHaveBeenCalled();
+ });
+
+ it('should add a .widget-resizer-marquee element to the .widget element', function() {
+ $scope.grabResizer(evt);
+ expect($element.find('.widget-resizer-marquee').length).toBeGreaterThan(0);
+ });
+
+ });
+
+ describe('the editTitle method', function() {
+
+ it('should set editingTitle=true on the widget object', function() {
+ var widget = {};
+ $scope.editTitle(widget);
+ expect(widget.editingTitle).toEqual(true);
+ });
+
+ it('should call $timeout', function() {
+ var widget = {};
+ $scope.editTitle(widget);
+ expect(injections.$timeout).toHaveBeenCalled();
+ });
+
+ });
+
+ describe('the saveTitleEdit method', function() {
+
+ it('should set editingTitle=false', function() {
+ var widget = { editingTitle: true };
+ $scope.saveTitleEdit(widget);
+ expect(widget.editingTitle).toEqual(false);
+ });
+ });
+
+}); \ No newline at end of file
diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js
new file mode 100644
index 00000000..f5a6ebef
--- /dev/null
+++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+angular.module('ui.dashboard')
+ .directive('widget', ['$injector', function ($injector) {
+
+ return {
+
+ controller: 'DashboardWidgetCtrl',
+
+ link: function (scope) {
+
+ var widget = scope.widget;
+ var dataModelType = widget.dataModelType;
+
+ // set up data source
+ if (dataModelType) {
+ var DataModelConstructor; // data model constructor function
+
+ if (angular.isFunction(dataModelType)) {
+ DataModelConstructor = dataModelType;
+ } else if (angular.isString(dataModelType)) {
+ $injector.invoke([dataModelType, function (DataModelType) {
+ DataModelConstructor = DataModelType;
+ }]);
+ } else {
+ throw new Error('widget dataModelType should be function or string');
+ }
+
+ var ds;
+ if (widget.dataModelArgs) {
+ ds = new DataModelConstructor(widget.dataModelArgs);
+ } else {
+ ds = new DataModelConstructor();
+ }
+ widget.dataModel = ds;
+ ds.setup(widget, scope);
+ ds.init();
+ scope.$on('$destroy', _.bind(ds.destroy,ds));
+ }
+
+ // Compile the widget template, emit add event
+ scope.compileTemplate();
+ scope.$emit('widgetAdded', widget);
+
+ }
+
+ };
+ }]);
diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js
new file mode 100644
index 00000000..0997e071
--- /dev/null
+++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js
@@ -0,0 +1,104 @@
+// 'use strict';
+
+describe('Directive: widget', function () {
+
+ var element, scope, rootScope, isoScope, compile, provide;
+
+ function Type() {
+ }
+
+ Type.prototype = {
+ setup: function () {
+ },
+ init: function () {
+ },
+ destroy: function () {
+ }
+ };
+
+ beforeEach(function () {
+ spyOn(Type.prototype, 'setup');
+ spyOn(Type.prototype, 'init');
+ spyOn(Type.prototype, 'destroy');
+ // define mock objects here
+ });
+
+ // load the directive's module
+ beforeEach(module('ui.dashboard', function ($provide, $controllerProvider) {
+ provide = $provide;
+ // Inject dependencies like this:
+ $controllerProvider.register('DashboardWidgetCtrl', function ($scope) {
+
+ });
+
+ }));
+
+ beforeEach(inject(function ($compile, $rootScope) {
+ // Cache these for reuse
+ rootScope = $rootScope;
+ compile = $compile;
+
+ // Other setup, e.g. helper functions, etc.
+
+ // Set up the outer scope
+ scope = $rootScope.$new();
+ scope.widget = {
+ dataModelType: Type
+ };
+
+ compileTemplate = jasmine.createSpy('compileTemplate');
+ scope.compileTemplate = compileTemplate;
+ }));
+
+ function compileWidget() {
+ // Define and compile the element
+ element = angular.element('<div widget><div class="widget-content"></div></div>');
+ element = compile(element)(scope);
+ scope.$digest();
+ isoScope = element.isolateScope();
+ }
+
+ it('should create a new instance of dataModelType if provided in scope.widget', function () {
+ compileWidget();
+ expect(scope.widget.dataModel instanceof Type).toBe(true);
+ });
+
+ it('should call setup and init on the new dataModel', function () {
+ compileWidget();
+ expect(Type.prototype.setup).toHaveBeenCalled();
+ expect(Type.prototype.init).toHaveBeenCalled();
+ });
+
+ it('should call compile template', function () {
+ compileWidget();
+ expect(scope.compileTemplate).toHaveBeenCalled();
+ });
+
+ it('should create a new instance of dataModelType from string name', function () {
+ // register data model with $injector
+ provide.factory('StringNameDataModel', function () {
+ return Type;
+ });
+
+ scope.widget = {
+ dataModelType: 'StringNameDataModel'
+ };
+
+ compileWidget();
+
+ expect(scope.widget.dataModel instanceof Type).toBe(true);
+ expect(Type.prototype.setup).toHaveBeenCalled();
+ expect(Type.prototype.init).toHaveBeenCalled();
+ });
+
+ it('should validate data model type', function () {
+ scope.widget = {
+ dataModelType: {}
+ };
+
+ expect(function () {
+ compileWidget()
+ }).toThrowError();
+ });
+
+}); \ No newline at end of file