Refactor Flo project layout

This commit is contained in:
BoykoAlex
2016-09-08 13:52:36 -04:00
parent 017a6fdd0a
commit 60ab9846b4
19 changed files with 698 additions and 550 deletions

190
dist/spring-flo.js vendored
View File

@@ -25472,101 +25472,103 @@ define('palette-manager',['require','jquery','joint','shapes-factory','angular']
// TODO lock to grid on drag? (if grid is on)
var ConstraintElementView = joint.dia.ElementView.extend({
pointerdown: function(/*evt, x, y*/) {
// Remove the tooltip
$('.node-tooltip').remove();
// TODO move metadata to the right place (not inside attrs I think)
clickedElement = this.model;
if (clickedElement.attr('metadata')) {
$(document).on('mousemove', handleDrag);
}
},
pointermove: function(/*evt, x, y*/) {
// Nothing to prevent move within the palette canvas
},
events: {
// Tooltips on the palette elements
'mouseenter': function(evt) {
// Ignore 'mouseenter' if any other buttons are pressed
if (evt.buttons) {
return;
function getPaletteView(view) {
return view.extend({
pointerdown: function(/*evt, x, y*/) {
// Remove the tooltip
$('.node-tooltip').remove();
// TODO move metadata to the right place (not inside attrs I think)
clickedElement = this.model;
if (clickedElement.attr('metadata')) {
$(document).on('mousemove', handleDrag);
}
},
pointermove: function(/*evt, x, y*/) {
// Nothing to prevent move within the palette canvas
},
events: {
// Tooltips on the palette elements
'mouseenter': function(evt) {
// Ignore 'mouseenter' if any other buttons are pressed
if (evt.buttons) {
return;
}
var model = this.model;
var metadata = model.attr('metadata');
if (!metadata) {
return;
}
this.showTooltip(evt.pageX, evt.pageY);
},
// TODO bug here - if the call to get the info takes a while, the tooltip may appear after the pointer has left the cell
'mouseleave': function(/*evt, x,y*/) {
this.hideTooltip();
},
'mousemove': function(evt) {
this.moveTooltip(evt.pageX, evt.pageY);
}
},
showTooltip: function(x, y) {
var model = this.model;
var metadata = model.attr('metadata');
// TODO refactor to use tooltip module
var nodeTooltip = document.createElement('div');
$(nodeTooltip).addClass('node-tooltip');
$(nodeTooltip).appendTo($('body')).fadeIn('fast');
var nodeDescription = document.createElement('div');
$(nodeTooltip).addClass('tooltip-description');
$(nodeTooltip).append(nodeDescription);
metadata.get('description').then(function(description) {
$(nodeDescription).text(description ? description : model.attr('metadata/name'));
}, function() {
$(nodeDescription).text(model.attr('metadata/name'));
});
if (!metadata.metadata || !metadata.metadata['hide-tooltip-options']) {
metadata.get('properties').then(function(metaProps) {
if (metaProps) {
Object.keys(metaProps).sort().forEach(function(propertyName) {
var optionRow = document.createElement('div');
var optionName = document.createElement('span');
var optionDescription = document.createElement('span');
$(optionName).addClass('node-tooltip-option-name');
$(optionDescription).addClass('node-tooltip-option-description');
$(optionName).text(metaProps[propertyName].name);
$(optionDescription).text(metaProps[propertyName].description);
$(optionRow).append(optionName);
$(optionRow).append(optionDescription);
$(nodeTooltip).append(optionRow);
});
}
}, function(error) {
if (error) {
$log.error(error);
}
});
}
var model = this.model;
var metadata = model.attr('metadata');
if (!metadata) {
return;
}
this.showTooltip(evt.pageX, evt.pageY);
},
// TODO bug here - if the call to get the info takes a while, the tooltip may appear after the pointer has left the cell
'mouseleave': function(/*evt, x,y*/) {
this.hideTooltip();
},
'mousemove': function(evt) {
this.moveTooltip(evt.pageX, evt.pageY);
}
},
showTooltip: function(x, y) {
var model = this.model;
var metadata = model.attr('metadata');
// TODO refactor to use tooltip module
var nodeTooltip = document.createElement('div');
$(nodeTooltip).addClass('node-tooltip');
$(nodeTooltip).appendTo($('body')).fadeIn('fast');
var nodeDescription = document.createElement('div');
$(nodeTooltip).addClass('tooltip-description');
$(nodeTooltip).append(nodeDescription);
metadata.get('description').then(function(description) {
$(nodeDescription).text(description ? description : model.attr('metadata/name'));
}, function() {
$(nodeDescription).text(model.attr('metadata/name'));
});
if (!metadata.metadata || !metadata.metadata['hide-tooltip-options']) {
metadata.get('properties').then(function(metaProps) {
if (metaProps) {
Object.keys(metaProps).sort().forEach(function(propertyName) {
var optionRow = document.createElement('div');
var optionName = document.createElement('span');
var optionDescription = document.createElement('span');
$(optionName).addClass('node-tooltip-option-name');
$(optionDescription).addClass('node-tooltip-option-description');
$(optionName).text(metaProps[propertyName].name);
$(optionDescription).text(metaProps[propertyName].description);
$(optionRow).append(optionName);
$(optionRow).append(optionDescription);
$(nodeTooltip).append(optionRow);
});
}
}, function(error) {
if (error) {
$log.error(error);
}
});
}
var mousex = x + 10;
var mousey = y + 10;
$('.node-tooltip').css({ top: mousey, left: mousex });
},
hideTooltip: function() {
$('.node-tooltip').remove();
},
moveTooltip: function(x, y) {
var mousex = x + 10; // Get X coordinates
var mousey = y + 10; // Get Y coordinates
$('.node-tooltip').css({ top: mousey, left: mousex });
}
var mousex = x + 10;
var mousey = y + 10;
$('.node-tooltip').css({ top: mousey, left: mousex });
},
hideTooltip: function() {
$('.node-tooltip').remove();
},
moveTooltip: function(x, y) {
var mousex = x + 10; // Get X coordinates
var mousey = y + 10; // Get Y coordinates
$('.node-tooltip').css({ top: mousey, left: mousex });
}
});
});
}
function createPaletteGroup(title, isOpen) {
var newGroupHeader = new joint.shapes.flo.PaletteGroupHeader({attrs:{text:{text:title}}});
@@ -25808,7 +25810,7 @@ define('palette-manager',['require','jquery','joint','shapes-factory','angular']
model:paletteGraph,
height: $(domContext.parentNode).height(),
width: $(domContext.parentNode).width(),
elementView: ConstraintElementView
elementView: getPaletteView(renderService && angular.isFunction(renderService.getNodeView) ? renderService.getNodeView() : joint.dia.ElementView)
});
palette.on('cell:pointerup',

File diff suppressed because one or more lines are too long

View File

@@ -44,9 +44,9 @@ module.exports = function(grunt) {
// global config
options: {
baseUrl: './src',
name: 'flo',
name: 'app',
exclude: [ 'joint', 'angular', 'jquery', 'bootstrap', 'underscore' ],
insertRequire: [ 'flo' ],
// insertRequire: [ './app' ],
mainConfigFile: './src/main.js'
},
production: {

35
src/app.js Normal file
View File

@@ -0,0 +1,35 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
/**
* Flo Angular app module
*
* @author Alex Boyko
*/
define('flo', function(require) {
'use strict';
require('angular');
require('floDirectives');
require('floServices');
return angular.module('spring.flo', [
'flo.services',
'flo.directives'
]);
});
require(['flo']);

View File

@@ -29,9 +29,9 @@ define(function(require) {
var angular = require('angular');
var joint = require('joint');
var shapesFactory = require('shapes-factory');
var createProperties = require('properties-manager');
var createEventManager = require('event-manager');
var shapesFactory = require('common/shapes-factory');
var createProperties = require('common/properties-manager');
var createEventManager = require('common/event-manager');
return ['$q', '$scope', '$timeout', '$http', '$log', '$injector', function($q, $scope, $timeout, $http, $log, $injector) {

View File

@@ -19,60 +19,60 @@ define(function(require) {
var $ = require('jquery');
var joint = require('joint');
var shapesFactory = require('shapes-factory');
var shapesFactory = require('common/shapes-factory');
var angular = require('angular');
// TODO move this node into custom nodes and links to centralize
if (!joint.shapes.flo) {
joint.shapes.flo = {};
}
// http://stackoverflow.com/questions/23960312/can-i-add-new-attributes-in-jointjs-element
joint.shapes.flo.PaletteGroupHeader = joint.shapes.basic.Generic.extend({
// The path is the open/close arrow, defaults to vertical (open)
markup: '<g class="scalable"><rect/></g><text/><g class="rotatable"><path d="m 10 10 l 5 8.7 l 5 -8.7 z"/></g>',
defaults: joint.util.deepSupplement({
type: 'palette.groupheader',
type: 'palette.groupheader',
size:{width:170,height:30},
position:{x:0,y:0},
attrs: {
'rect': { fill: '#34302d', 'stroke-width': 1, stroke: '#6db33f', 'follow-scale':true, width:80, height:40 },
'text': {
text:'',
fill: '#eeeeee',
'ref-x': 0.5,
'ref-y': 7,
'x-alignment':'middle',
'font-size': 18/*, 'font-weight': 'bold', 'font-variant': 'small-caps', 'text-transform': 'capitalize'*/
},
'path': { fill: 'white', 'stroke-width': 2, stroke: 'white'/*,transform:'rotate(90,15,15)'*/}
},
// custom properties
isOpen:true
attrs: {
'rect': { fill: '#34302d', 'stroke-width': 1, stroke: '#6db33f', 'follow-scale':true, width:80, height:40 },
'text': {
text:'',
fill: '#eeeeee',
'ref-x': 0.5,
'ref-y': 7,
'x-alignment':'middle',
'font-size': 18/*, 'font-weight': 'bold', 'font-variant': 'small-caps', 'text-transform': 'capitalize'*/
},
'path': { fill: 'white', 'stroke-width': 2, stroke: 'white'/*,transform:'rotate(90,15,15)'*/}
},
// custom properties
isOpen:true
}, joint.shapes.basic.Generic.prototype.defaults)
});
return ['$scope', '$timeout', '$log', '$injector', function($scope, $timeout, $log, $injector) {
var domContext;
var metamodelService;
var renderService;
var paletteGraph = new joint.dia.Graph();
paletteGraph.attributes.type = joint.shapes.flo.PALETTE_TYPE;
var palette;
/**
* The names of any groups in the palette that have been deliberately closed (the arrow clicked on)
* @type {String[]}
*/
var closedGroups = [];
/**
* Model of the clicked element
* Model of the clicked element
*/
var clickedElement;
@@ -134,101 +134,103 @@ define(function(require) {
// TODO lock to grid on drag? (if grid is on)
var ConstraintElementView = joint.dia.ElementView.extend({
pointerdown: function(/*evt, x, y*/) {
// Remove the tooltip
$('.node-tooltip').remove();
// TODO move metadata to the right place (not inside attrs I think)
clickedElement = this.model;
if (clickedElement.attr('metadata')) {
$(document).on('mousemove', handleDrag);
}
},
pointermove: function(/*evt, x, y*/) {
// Nothing to prevent move within the palette canvas
},
events: {
// Tooltips on the palette elements
'mouseenter': function(evt) {
// Ignore 'mouseenter' if any other buttons are pressed
if (evt.buttons) {
return;
}
var model = this.model;
var metadata = model.attr('metadata');
if (!metadata) {
return;
}
this.showTooltip(evt.pageX, evt.pageY);
},
// TODO bug here - if the call to get the info takes a while, the tooltip may appear after the pointer has left the cell
'mouseleave': function(/*evt, x,y*/) {
this.hideTooltip();
},
'mousemove': function(evt) {
this.moveTooltip(evt.pageX, evt.pageY);
}
},
showTooltip: function(x, y) {
var model = this.model;
var metadata = model.attr('metadata');
// TODO refactor to use tooltip module
var nodeTooltip = document.createElement('div');
$(nodeTooltip).addClass('node-tooltip');
$(nodeTooltip).appendTo($('body')).fadeIn('fast');
var nodeDescription = document.createElement('div');
$(nodeTooltip).addClass('tooltip-description');
$(nodeTooltip).append(nodeDescription);
metadata.get('description').then(function(description) {
$(nodeDescription).text(description ? description : model.attr('metadata/name'));
}, function() {
$(nodeDescription).text(model.attr('metadata/name'));
});
if (!metadata.metadata || !metadata.metadata['hide-tooltip-options']) {
metadata.get('properties').then(function(metaProps) {
if (metaProps) {
Object.keys(metaProps).sort().forEach(function(propertyName) {
var optionRow = document.createElement('div');
var optionName = document.createElement('span');
var optionDescription = document.createElement('span');
$(optionName).addClass('node-tooltip-option-name');
$(optionDescription).addClass('node-tooltip-option-description');
$(optionName).text(metaProps[propertyName].name);
$(optionDescription).text(metaProps[propertyName].description);
$(optionRow).append(optionName);
$(optionRow).append(optionDescription);
$(nodeTooltip).append(optionRow);
});
}
}, function(error) {
if (error) {
$log.error(error);
}
});
}
var mousex = x + 10;
var mousey = y + 10;
$('.node-tooltip').css({ top: mousey, left: mousex });
},
hideTooltip: function() {
$('.node-tooltip').remove();
},
moveTooltip: function(x, y) {
var mousex = x + 10; // Get X coordinates
var mousey = y + 10; // Get Y coordinates
$('.node-tooltip').css({ top: mousey, left: mousex });
}
function getPaletteView(view) {
return view.extend({
pointerdown: function(/*evt, x, y*/) {
// Remove the tooltip
$('.node-tooltip').remove();
// TODO move metadata to the right place (not inside attrs I think)
clickedElement = this.model;
if (clickedElement.attr('metadata')) {
$(document).on('mousemove', handleDrag);
}
},
pointermove: function(/*evt, x, y*/) {
// Nothing to prevent move within the palette canvas
},
events: {
// Tooltips on the palette elements
'mouseenter': function(evt) {
});
// Ignore 'mouseenter' if any other buttons are pressed
if (evt.buttons) {
return;
}
var model = this.model;
var metadata = model.attr('metadata');
if (!metadata) {
return;
}
this.showTooltip(evt.pageX, evt.pageY);
},
// TODO bug here - if the call to get the info takes a while, the tooltip may appear after the pointer has left the cell
'mouseleave': function(/*evt, x,y*/) {
this.hideTooltip();
},
'mousemove': function(evt) {
this.moveTooltip(evt.pageX, evt.pageY);
}
},
showTooltip: function(x, y) {
var model = this.model;
var metadata = model.attr('metadata');
// TODO refactor to use tooltip module
var nodeTooltip = document.createElement('div');
$(nodeTooltip).addClass('node-tooltip');
$(nodeTooltip).appendTo($('body')).fadeIn('fast');
var nodeDescription = document.createElement('div');
$(nodeTooltip).addClass('tooltip-description');
$(nodeTooltip).append(nodeDescription);
metadata.get('description').then(function(description) {
$(nodeDescription).text(description ? description : model.attr('metadata/name'));
}, function() {
$(nodeDescription).text(model.attr('metadata/name'));
});
if (!metadata.metadata || !metadata.metadata['hide-tooltip-options']) {
metadata.get('properties').then(function(metaProps) {
if (metaProps) {
Object.keys(metaProps).sort().forEach(function(propertyName) {
var optionRow = document.createElement('div');
var optionName = document.createElement('span');
var optionDescription = document.createElement('span');
$(optionName).addClass('node-tooltip-option-name');
$(optionDescription).addClass('node-tooltip-option-description');
$(optionName).text(metaProps[propertyName].name);
$(optionDescription).text(metaProps[propertyName].description);
$(optionRow).append(optionName);
$(optionRow).append(optionDescription);
$(nodeTooltip).append(optionRow);
});
}
}, function(error) {
if (error) {
$log.error(error);
}
});
}
var mousex = x + 10;
var mousey = y + 10;
$('.node-tooltip').css({ top: mousey, left: mousex });
},
hideTooltip: function() {
$('.node-tooltip').remove();
},
moveTooltip: function(x, y) {
var mousex = x + 10; // Get X coordinates
var mousey = y + 10; // Get Y coordinates
$('.node-tooltip').css({ top: mousey, left: mousex });
}
});
}
function createPaletteGroup(title, isOpen) {
var newGroupHeader = new joint.shapes.flo.PaletteGroupHeader({attrs:{text:{text:title}}});
@@ -417,7 +419,7 @@ define(function(require) {
}
// TODO better name for this function as this does the animation *and* updates the palette
/*
* Modify the rotation of the arrow in the header from vertical(open) to horizontal(closed)
*/
@@ -436,31 +438,31 @@ define(function(require) {
}
};
$timeout(rotateIt);
}
}
function handleMouseUp(/*event*/) {
$(document).off('mousemove', handleDrag);
}
function dispose() {
if (metamodelService && angular.isFunction(metamodelService.unsubscribe)) {
metamodelService.unsubscribe(_metamodelListener);
}
$(document).off('mouseup', handleMouseUp);
}
function filterTextUpdated() {
metamodelService.load().then(buildPalette);
}
function init(element/*, attrs*/) {
domContext = element;
metamodelService = $injector.get($scope.metamodelServiceName);
if ($scope.renderServiceName) {
renderService = $injector.get($scope.renderServiceName);
renderService = $injector.get($scope.renderServiceName);
}
// Create the paper for the palette using the specified element view
@@ -470,19 +472,19 @@ define(function(require) {
model:paletteGraph,
height: $(domContext.parentNode).height(),
width: $(domContext.parentNode).width(),
elementView: ConstraintElementView
elementView: getPaletteView(renderService && angular.isFunction(renderService.getNodeView) ? renderService.getNodeView() : joint.dia.ElementView)
});
palette.on('cell:pointerup',
function(cellview, evt) {
$log.debug('pointerup');
if (viewBeingDragged) {
trigger('drop',{'dragged':viewBeingDragged,'evt':evt});
viewBeingDragged = null;
}
clickedElement = null;
$('#palette-floater').remove();
});
function(cellview, evt) {
$log.debug('pointerup');
if (viewBeingDragged) {
trigger('drop',{'dragged':viewBeingDragged,'evt':evt});
viewBeingDragged = null;
}
clickedElement = null;
$('#palette-floater').remove();
});
// Toggle the header open/closed on a click
palette.on('cell:pointerclick',
@@ -501,7 +503,7 @@ define(function(require) {
// TODO [palette] ensure other mouse handling events do nothing for headers
// TODO [palette] move 'metadata' field to the right place (not inside attrs I think)
});
$(document).on('mouseup', handleMouseUp);
if (metamodelService) {
@@ -518,22 +520,22 @@ define(function(require) {
if ($scope.flo) {
$scope.flo.paletteSize = $scope.flo.paletteSize || $(domContext.parentNode).width();
}
}
$scope.$on('$destroy', dispose);
$scope.init = init;
$scope.filterTextUpdated = filterTextUpdated;
if ($scope.flo) {
$scope.flo._paletteGraph = function() {
return paletteGraph;
};
$scope.flo.paletteEntryPadding = $scope.flo.paletteEntryPadding || {x:12, y:12};
}
$scope.$watch(function() {
return $scope.flo.paletteSize;
}, function(newValue, oldValue) {
@@ -541,7 +543,7 @@ define(function(require) {
metamodelService.load().then(buildPalette);
}
});
}];
});

110
src/directives.js Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
/**
* Definition of directives
*
* @author Alex Boyko
*/
define(function(require) {
'use strict';
var angular = require('angular');
return angular.module('flo.directives', [])
.directive('resizer', function($document) {
return function($scope, $element, $attrs) {
function resize(size) {
if ($attrs.resizer === 'vertical') {
// Handle vertical resizer
var x = size;
if ($attrs.resizerMax && x > $attrs.resizerMax) {
x = parseInt($attrs.resizerMax);
}
$element.css({
left: x + 'px'
});
$($attrs.resizerLeft).css({
width: x + 'px'
});
$($attrs.resizerRight).css({
left: (x + parseInt($attrs.resizerWidth)) + 'px'
});
} else {
// Handle horizontal resizer
var y = size;
$element.css({
bottom: y + 'px'
});
$($attrs.resizerTop).css({
bottom: (y + parseInt($attrs.resizerHeight)) + 'px'
});
$($attrs.resizerBottom).css({
height: y + 'px'
});
}
}
function mousemove(event) {
var size;
if ($attrs.resizer === 'vertical') {
// Handle vertical resizer. Calculate new size relative to palette container DOM node
size = event.pageX - $($attrs.resizerLeft).offset().left;
} else {
// Handle horizontal resizer Calculate new size relative to palette container DOM node
size = window.innerHeight - event.pageY - $($attrs.resizerTop).offset().top;
}
$scope.flo.paletteSize = size;
// Apply changes to the scope such that various watchers can react to palette size change
$scope.$apply();
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
}
$element.on('mousedown', function(event) {
event.preventDefault();
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
});
// Adjust DOM node sizes based on palette size model value
$scope.$watch(function() {
return $scope.flo.paletteSize;
}, function(newValue) {
resize(newValue);
});
if ($scope.flo.paletteSize) {
resize($scope.flo.paletteSize);
}
};
})
.directive('dslEditor', require('directives/synced-dsl-editor'))
.directive('codeEditor', require('directives/code-editor'))
.directive('floPalette', require('directives/palette'))
.directive('floEditor', require('directives/graph-editor'));
});

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
define(function () {
'use strict';
return [function () {
return {
restrict: 'A',
controller: require('controllers/code-editor'),
link: function(scope, element, attrs) {
scope.init(element.context, attrs);
},
scope: {
language: '=codeLanguage',
text: '=codeText',
decodeFunction: '&',
encodeFunction: '&'
}
};
}];
});

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
define(function () {
'use strict';
return [function () {
return {
restrict: 'E',
link: function(scope, element, attrs) {
if (attrs.metamodelServiceName) {
scope.metamodelServiceName = attrs.metamodelServiceName;
}
if (attrs.renderServiceName) {
scope.renderServiceName = attrs.renderServiceName;
}
if (attrs.editorServiceName) {
scope.editorServiceName = attrs.editorServiceName;
}
if (attrs.paletteSize) {
scope.flo.paletteSize = Number(attrs.paletteSize);
}
if (attrs.paperPadding) {
scope.paperPadding = Number(attrs.paperPadding);
}
scope.init(element.context, attrs);
element.find('#paper').bind('keydown', function(e) {
if (e.which === 8 || e.which === 46) {
if (!scope.flo.readOnly() /*&& scope.flo.getSelection()*/) {
scope.flo.deleteSelectedNode();
e.preventDefault();
}
}
});
},
controller: require('controllers/graph-editor'),
controllerAs: 'floModel',
transclude: true,
template:
'<ng-transclude></ng-transclude>' +
'<div id="flow-view" class="flow-view" style="position:relative;">' +
'<div id="canvas" class="canvas" style="position:relative; display: block; width: 100%; height: 100%;">' +
'<div id="palette-container" class="palette-container" ng-if="!flo.noPalette" style="overflow:hidden;">' +
'<flo-palette></flo-palette>' +
'</div>' +
'<div id="sidebar-resizer" ng-if="!flo.noPalette"' +
'resizer="vertical"' +
'resizer-width="6"' +
'resizer-left="#palette-container"' +
'resizer-right="#paper-container">' +
'</div>' +
'<div id="paper-container">' +
'<div id="paper" class="paper" tabindex="0" style="overflow: hidden; position: absolute; display: block; height:100%; width:100%; overflow:auto;"></div>' +
'<span class="canvas-controls-container" ng-if="canvasControls">' +
'<table ng-if="canvasControls.zoom" class="canvas-control zoom-canvas-control"><tbody><tr>' +
'<td><input class="zoom-canvas-input canvas-control zoom-canvas-control" type="text" data-inline="true" ng-model="flo.zoomPercent" ng-model-options="{ getterSetter: true, updateOn: \'blur change\' }" size="3"></input>' +
'<label class="canvas-control zoom-canvas-label">%</label></td>' +
'<td><input type="range" data-inline="true" ng-model="flo.zoomPercent" ng-model-options="{ getterSetter: true }" step="{{flo.getZoomStep()}}" max="{{flo.getMaxZoom()}}" min="{{flo.getMinZoom()}}" data-type="range" name="range" class="canvas-control zoom-canvas-control"></input></td>' +
'</tr></tbody></table>' +
'</span>' +
'<div id="properties" class="properties" style="position:absolute; bottom:0; width:100%; overflow:auto;"></div>' +
'</div>' +
'</div>' +
'<div id="hook"></div>' +
'<div id="dialogs"></div>' +
'</div>'
};
}];
});

42
src/directives/palette.js Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
define(function () {
'use strict';
return [function () {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
if (attrs.metamodelServiceName) {
scope.metamodelServiceName = attrs.metamodelServiceName;
}
scope.init(element.context, attrs);
},
controller: require('controllers/palette'),
controllerAs: 'floPalette',
template:
'<div id="palette-filter" class="palette-filter">' +
'<input type="text" id="palette-filter-textfield" ng-model="filterText" ng-change="filterTextUpdated()" class="palette-filter-textfield" ng-model-options="{ debounce: 500 }"/>' +
'</div>' +
'<div id="palette-paper-container" style="height:calc(100% - 46px); width:100%; overflow:auto;">' +
'<div id="palette-paper" class="palette-paper" style="overflow:hidden;"></div>' +
'</div>'
};
}];
});

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
define(function () {
'use strict';
return [function () {
return {
restrict: 'A',
scope: true,
controller: require('controllers/synced-dsl-editor'),
link: function (scope, element, attrs) {
if (attrs.contentAssistServiceName) {
scope.contentAssistServiceName = attrs.contentAssistServiceName;
}
scope.init(element.context);
}
};
}];
});

View File

@@ -1,301 +0,0 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
define(function(require) {
'use strict';
require('angular');
var app = angular.module('spring.flo', []);
app.directive('dslEditor', function() {
return {
restrict: 'A',
scope: true,
controller: require('dsl-editor'),
link: function(scope, element, attrs) {
if (attrs.contentAssistServiceName) {
scope.contentAssistServiceName = attrs.contentAssistServiceName;
}
scope.init(element.context);
}
};
});
app.directive('codeEditor', function() {
return {
restrict: 'A',
controller: require('code-editor'),
link: function(scope, element, attrs) {
scope.init(element.context, attrs);
},
scope: {
language: '=codeLanguage',
text: '=codeText',
decodeFunction: '&',
encodeFunction: '&'
}
};
});
app.directive('floPalette', function() {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
if (attrs.metamodelServiceName) {
scope.metamodelServiceName = attrs.metamodelServiceName;
}
scope.init(element.context, attrs);
},
controller: require('palette-manager'),
controllerAs: 'floPalette',
template:
'<div id="palette-filter" class="palette-filter">' +
'<input type="text" id="palette-filter-textfield" ng-model="filterText" ng-change="filterTextUpdated()" class="palette-filter-textfield" ng-model-options="{ debounce: 500 }"/>' +
'</div>' +
'<div id="palette-paper-container" style="height:calc(100% - 46px); width:100%; overflow:auto;">' +
'<div id="palette-paper" class="palette-paper" style="overflow:hidden;"></div>' +
'</div>'
};
});
// Div for resizing the palette
app.directive('resizer', function($document) {
return function($scope, $element, $attrs) {
function resize(size) {
if ($attrs.resizer === 'vertical') {
// Handle vertical resizer
var x = size;
if ($attrs.resizerMax && x > $attrs.resizerMax) {
x = parseInt($attrs.resizerMax);
}
$element.css({
left: x + 'px'
});
$($attrs.resizerLeft).css({
width: x + 'px'
});
$($attrs.resizerRight).css({
left: (x + parseInt($attrs.resizerWidth)) + 'px'
});
} else {
// Handle horizontal resizer
var y = size;
$element.css({
bottom: y + 'px'
});
$($attrs.resizerTop).css({
bottom: (y + parseInt($attrs.resizerHeight)) + 'px'
});
$($attrs.resizerBottom).css({
height: y + 'px'
});
}
}
function mousemove(event) {
var size;
if ($attrs.resizer === 'vertical') {
// Handle vertical resizer. Calculate new size relative to palette container DOM node
size = event.pageX - $($attrs.resizerLeft).offset().left;
} else {
// Handle horizontal resizer Calculate new size relative to palette container DOM node
size = window.innerHeight - event.pageY - $($attrs.resizerTop).offset().top;
}
$scope.flo.paletteSize = size;
// Apply changes to the scope such that various watchers can react to palette size change
$scope.$apply();
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
}
$element.on('mousedown', function(event) {
event.preventDefault();
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
});
// Adjust DOM node sizes based on palette size model value
$scope.$watch(function() {
return $scope.flo.paletteSize;
}, function(newValue) {
resize(newValue);
});
if ($scope.flo.paletteSize) {
resize($scope.flo.paletteSize);
}
};
});
app.directive('floEditor', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
if (attrs.metamodelServiceName) {
scope.metamodelServiceName = attrs.metamodelServiceName;
}
if (attrs.renderServiceName) {
scope.renderServiceName = attrs.renderServiceName;
}
if (attrs.editorServiceName) {
scope.editorServiceName = attrs.editorServiceName;
}
if (attrs.paletteSize) {
scope.flo.paletteSize = Number(attrs.paletteSize);
}
if (attrs.paperPadding) {
scope.paperPadding = Number(attrs.paperPadding);
}
scope.init(element.context, attrs);
element.find('#paper').bind('keydown', function(e) {
if (e.which === 8 || e.which === 46) {
if (!scope.flo.readOnly() /*&& scope.flo.getSelection()*/) {
scope.flo.deleteSelectedNode();
e.preventDefault();
}
}
});
},
controller: require('editor-manager'),
controllerAs: 'floModel',
transclude: true,
template:
'<ng-transclude></ng-transclude>' +
'<div id="flow-view" class="flow-view" style="position:relative;">' +
'<div id="canvas" class="canvas" style="position:relative; display: block; width: 100%; height: 100%;">' +
'<div id="palette-container" class="palette-container" ng-if="!flo.noPalette" style="overflow:hidden;">' +
'<flo-palette></flo-palette>' +
'</div>' +
'<div id="sidebar-resizer" ng-if="!flo.noPalette"' +
'resizer="vertical"' +
'resizer-width="6"' +
'resizer-left="#palette-container"' +
'resizer-right="#paper-container">' +
'</div>' +
'<div id="paper-container">' +
'<div id="paper" class="paper" tabindex="0" style="overflow: hidden; position: absolute; display: block; height:100%; width:100%; overflow:auto;"></div>' +
'<span class="canvas-controls-container" ng-if="canvasControls">' +
'<table ng-if="canvasControls.zoom" class="canvas-control zoom-canvas-control"><tbody><tr>' +
'<td><input class="zoom-canvas-input canvas-control zoom-canvas-control" type="text" data-inline="true" ng-model="flo.zoomPercent" ng-model-options="{ getterSetter: true, updateOn: \'blur change\' }" size="3"></input>' +
'<label class="canvas-control zoom-canvas-label">%</label></td>' +
'<td><input type="range" data-inline="true" ng-model="flo.zoomPercent" ng-model-options="{ getterSetter: true }" step="{{flo.getZoomStep()}}" max="{{flo.getMaxZoom()}}" min="{{flo.getMinZoom()}}" data-type="range" name="range" class="canvas-control zoom-canvas-control"></input></td>' +
'</tr></tbody></table>' +
'</span>' +
'<div id="properties" class="properties" style="position:absolute; bottom:0; width:100%; overflow:auto;"></div>' +
'</div>' +
'</div>' +
'<div id="hook"></div>' +
'<div id="dialogs"></div>' +
'</div>'
};
});
app.factory('MetamodelUtils', ['$q', function($q) {
return {
/**
* Return the metadata for a particular palette entry in a particular group.
* @param {String} name - name of the palette entry
* @param {string} group - group in which the palette entry should exist (e.g. sinks)
* @return {{name:string,group:string,unresolved:Boolean}}
*/
getMetadata: function(metamodel, name, group) {
if (name && group && metamodel[group][name]) {
return metamodel[group][name];
} else {
return {
name: name,
group: group,
unresolved: true,
get: function() {
var deferred = $q.defer();
deferred.resolve();
return deferred.promise;
}
};
}
},
matchGroup: function(metamodel, type, incoming, outgoing) {
incoming = typeof incoming === 'number' ? incoming : 0;
outgoing = typeof outgoing === 'number' ? outgoing : 0;
var matches = [];
var i;
if (type) {
for (i in metamodel) {
if (metamodel[i][type]) {
matches.push(metamodel[i][type]);
}
}
}
var group;
var score = Number.MIN_VALUE;
for (i = 0; i < matches.length; i++) {
var constraints = matches[i].constraints;
if (constraints) {
var failedConstraintsNumber = 0;
if (typeof constraints.maxOutgoingLinksNumber === 'number' && constraints.maxOutgoingLinksNumber < outgoing) {
failedConstraintsNumber++;
}
if (typeof constraints.minOutgoingLinksNumber === 'number' && constraints.minOutgoingLinksNumber > outgoing) {
failedConstraintsNumber++;
}
if (typeof constraints.maxIncomingLinksNumber === 'number' && constraints.maxIncomingLinksNumber < incoming) {
failedConstraintsNumber++;
}
if (typeof constraints.minIncomingLinksNumber === 'number' && constraints.minIncomingLinksNumber > incoming) {
failedConstraintsNumber++;
}
if (failedConstraintsNumber === 0) {
return matches[i].group;
} else if (failedConstraintsNumber > score) {
score = failedConstraintsNumber;
group = matches[i].group;
}
} else {
return matches[i].group;
}
}
return group;
}
};
}]);
return app;
});

View File

@@ -22,7 +22,9 @@ requirejs.config({
jquery: '../lib/jquery/jquery',
bootstrap: '../lib/bootstrap/bootstrap',
lodash: '../lib/lodash/lodash.compat',
jshint: '../lib/jshint/dist/jshint'
jshint: '../lib/jshint/dist/jshint',
floDirectives: './directives',
floServices: './services'
},
map: {
'*': {

101
src/services.js Normal file
View File

@@ -0,0 +1,101 @@
/*
* Copyright 2016 the original author or authors.
*
* 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.
*/
/**
* Services
*
* @author Alex Boyko
*/
define(['angular'], function (angular) {
'use strict';
return angular.module('flo.services', [])
.factory('MetamodelUtils', ['$q', function ($q) {
return {
/**
* Return the metadata for a particular palette entry in a particular group.
* @param {String} name - name of the palette entry
* @param {string} group - group in which the palette entry should exist (e.g. sinks)
* @return {{name:string,group:string,unresolved:Boolean}}
*/
getMetadata: function (metamodel, name, group) {
if (name && group && metamodel[group][name]) {
return metamodel[group][name];
} else {
return {
name: name,
group: group,
unresolved: true,
get: function () {
var deferred = $q.defer();
deferred.resolve();
return deferred.promise;
}
};
}
},
matchGroup: function (metamodel, type, incoming, outgoing) {
incoming = typeof incoming === 'number' ? incoming : 0;
outgoing = typeof outgoing === 'number' ? outgoing : 0;
var matches = [];
var i;
if (type) {
for (i in metamodel) {
if (metamodel[i][type]) {
matches.push(metamodel[i][type]);
}
}
}
var group;
var score = Number.MIN_VALUE;
for (i = 0; i < matches.length; i++) {
var constraints = matches[i].constraints;
if (constraints) {
var failedConstraintsNumber = 0;
if (typeof constraints.maxOutgoingLinksNumber === 'number' && constraints.maxOutgoingLinksNumber < outgoing) {
failedConstraintsNumber++;
}
if (typeof constraints.minOutgoingLinksNumber === 'number' && constraints.minOutgoingLinksNumber > outgoing) {
failedConstraintsNumber++;
}
if (typeof constraints.maxIncomingLinksNumber === 'number' && constraints.maxIncomingLinksNumber < incoming) {
failedConstraintsNumber++;
}
if (typeof constraints.minIncomingLinksNumber === 'number' && constraints.minIncomingLinksNumber > incoming) {
failedConstraintsNumber++;
}
if (failedConstraintsNumber === 0) {
return matches[i].group;
} else if (failedConstraintsNumber > score) {
score = failedConstraintsNumber;
group = matches[i].group;
}
} else {
return matches[i].group;
}
}
return group;
}
};
}]);
});