Commit 4201d626 authored by Colin PUY's avatar Colin PUY

add community frontend

add community frontend pom

gitignore

add community front files

update server.sh

fix e2econfig

fix preview e2e

fix e2e config res

fix editor-menu.spec.js
parent f9b08560
......@@ -13,3 +13,7 @@ npm-debug.log
backend/webapp/node/
backend/webapp/src/main/webapp/i18n/*.json
backend/webapp/src/test/resources/i18n/*.json
frontend/node/
frontend/build/
......@@ -17,6 +17,14 @@
<artifactId>ui-designer-backend-contract</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.bonitasoft.web</groupId>
<artifactId>ui-designer-frontend</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>dist</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
......@@ -262,6 +270,32 @@
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>unpack</id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.bonitasoft.web</groupId>
<artifactId>ui-designer-frontend</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>dist</classifier>
<outputDirectory>${project.build.directory}/classes/static</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
......
{
"directory": "app/bower_components"
}
\ No newline at end of file
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
[*.md]
trim_trailing_whitespace = false
[*.js]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2
[*.html]
indent_style = space
indent_size = 4
{
"node": false,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": false,
"trailing": true,
"smarttabs": true,
"globals": {
"angular": false,
"ace": false,
// Logic
"console": false,
// For IE
"ActiveXObject": false,
// Jasmine
"describe": false,
"beforeEach": false,
"module": false,
"inject": false,
"afterEach": false,
"it": false,
"expect": false,
"jasmine": false,
"spyOn": false,
"$$": false,
"$": false,
"require": false
}
}
# Front end
## Prerequisites
- Install node.js and npm
- Install gulp globally :
```shell
$ npm install -g gulp
```
- Install the local npm packages:
```shell
$ npm install
```
- Install selenium and the chrome driver
```shell
$ ./node_modules/protractor/bin/webdriver-manager update
```
## Dev
```shell
$ gulp serve
```
## Build
```shell
$ npm run build
```
or
```shell
$ gulp [default] --dist
```
> You can build without the flag *--dist*, but your application will be in debug mode cf [Running an AngularJS App in Production](https://docs.angularjs.org/guide/production)
## Start a server
```shell
$ gulp [default] serve
```
This starts a server on port 3000 which allows serving the development page (index-dev.html)
## Start built files
```shell
$ gulp [default] serve:dist
```
This will serve the production page (build/dist/index.html), where the less files are compiled to css and all the source JS files are compiled to a single
minified file.
## Execute e2e tests
```shell
$ gulp e2e
```
or
```shell
$ npm run e2e
```
This generates the CSS, JS and html files, starts a selenium server on port 12001, executes the e2e tests, then
stops the server.
You can also start the app in e2e mode without launching e2e tests (if you want to launch e2e tests from your IDE)
```shell
$ gulp serve:e2e
```
## Execute unit-tests
### Single run
```shell
$ npm test
```
or
```shell
$ gulp test
```
### Watch source files and tests
```shell
$ npm run test-watch
```
or
```shell
$ gulp test-watch
```
## Internationalization
Translatable text are extracted from sources during the build process. We use gettext technology which result in a .pot file. From that we can create a PO file for each supported language using a translation tool (e.g. Crowdin).
At runtime the backend transform those PO files into [angular-gettext](https://angular-gettext.rocketeer.be/) friendly JSON files.
<!DOCTYPE html>
<html ng-app="uidesigner" lang="en">
<head>
<meta charset="utf-8">
<title>UI designer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<!-- build:css -->
<link rel="stylesheet" href="css/main.css"/>
<!-- endbuild -->
<!-- build:vendor -->
<script src="/js/vendor.min.js"></script>
<!-- endbuild -->
<!-- build:js -->
<script src="/js/pb.module.js"></script>
<script src="/js/preview/preview.module.js"></script>
<script src="/js/home/home.module.js"></script>
<script src="/js/custom-widget/custom-widget.module.js"></script>
<script src="/js/common/repositories/repositories.module.js"></script>
<script src="/js/common/services/services.module.js"></script>
<script src="/js/routes.js"></script>
<script src="/js/app.config.js"></script>
<script src="/js/home/home.controller.js"></script>
<script src="/js/home/file-input-change.directive.js"></script>
<script src="/js/home/custom-widget-factory.service.js"></script>
<script src="/js/home/deletion-popup.controller.js"></script>
<script src="/js/preview/preview.controller.js"></script>
<script src="/js/preview/open-preview.directive.js"></script>
<script src="/js/preview/web-socket.service.js"></script>
<script src="/js/editor/editor.controller.js"></script>
<script src="/js/editor/component-utils.service.js"></script>
<script src="/js/editor/component-scope-builder.service.js"></script>
<script src="/js/editor/whiteboard.service.js"></script>
<script src="/js/editor/component-factory.service.js"></script>
<script src="/js/editor/palette.service.js"></script>
<script src="/js/editor/common-params.service.js"></script>
<script src="/js/editor/widget-factory.service.js"></script>
<script src="/js/editor/palette/palette.controller.js"></script>
<script src="/js/editor/palette/palette-widget.directive.js"></script>
<script src="/js/editor/data-panel/data.controller.js"></script>
<script src="/js/editor/data-panel/data-popup.controller.js"></script>
<script src="/js/editor/data-panel/data-type.service.js"></script>
<script src="/js/editor/data-panel/valid-json.directive.js"></script>
<script src="/js/editor/properties-panel/property-field.directive.js"></script>
<script src="/js/editor/properties-panel/property-field.controller.js"></script>
<script src="/js/editor/properties-panel/empty-typeahead.directive.js"></script>
<script src="/js/editor/workspace/component.directive.js"></script>
<script src="/js/editor/workspace/container.directive.js"></script>
<script src="/js/editor/workspace/container.controller.js"></script>
<script src="/js/editor/workspace/element-resizer.directive.js"></script>
<script src="/js/editor/workspace/form-container.directive.js"></script>
<script src="/js/editor/workspace/tabs-container.directive.js"></script>
<script src="/js/editor/workspace/tabs-container.controller.js"></script>
<script src="/js/editor/workspace/component-mover.directive.js"></script>
<script src="/js/editor/workspace/component-mover.controller.js"></script>
<script src="/js/editor/workspace/drop-zone.directive.js"></script>
<script src="/js/editor/workspace/component-highlighter.directive.js"></script>
<script src="/js/editor/workspace/data.filter.js"></script>
<script src="/js/editor/workspace/element-resizer-model.service.js"></script>
<script src="/js/custom-widget/custom-widget-editor.controller.js"></script>
<script src="/js/custom-widget/property-editor-popup.controller.js"></script>
<script src="/js/common/repositories/page-repository.service.js"></script>
<script src="/js/common/repositories/widget-repository.service.js"></script>
<script src="/js/common/services/ace-data-completer.service.js"></script>
<script src="/js/common/services/arrays.service.js"></script>
<script src="/js/common/services/alerts.service.js"></script>
<script src="/js/common/services/error-interceptor.service.js"></script>
<script src="/js/common/services/clock.service.js"></script>
<script src="/js/common/services/keymaster.service.js"></script>
<script src="/js/common/services/resolutions.service.js"></script>
<script src="/js/common/services/utils.service.js"></script>
<script src="/js/common/directives/alerts.directive.js"></script>
<script src="/js/common/directives/ace-editor.directive.js"></script>
<script src="/js/common/directives/autofocus.directive.js"></script>
<script src="/js/common/directives/splitter-vertical.directive.js"></script>
<script src="/js/common/directives/splitter-horizontal.directive.js"></script>
<script src="/js/common/directives/splitter-toggle.directive.js"></script>
<script src="/js/common/directives/tooltip-toggle.directive.js"></script>
<!-- endbuild -->
<!-- build:e2e -->
<!-- endbuild -->
</head>
<body ng-cloak>
<alerts></alerts>
<ui-view></ui-view>
</body>
</html>
(function() {
'use strict';
// configure ace base paths
ace.config.set('basePath', 'js');
ace.config.set('modePath', 'js');
ace.config.set('themePath', 'js');
ace.config.set('workerPath', 'js');
var isIE9 = window.navigator.userAgent.indexOf('MSIE 9') > -1;
angular.module('uidesigner', ['pb'])
.value('isIE9', isIE9)
.config(configureModule);
/* @ngInject */
function configureModule($compileProvider, boDraggableItemProvider, $tooltipProvider, $stateProvider, $urlRouterProvider, appStates) {
/**
* For the build, gulp replaces '%debugMode%' by false. For the dev no need to replace, it's eval to true.
* {@link https://docs.angularjs.org/guide/production}
*/
$compileProvider.debugInfoEnabled('%debugMode%');
boDraggableItemProvider.cloneOnDrop(false);
boDraggableItemProvider.activeBodyClassName(true);
/* create custom trigger for popover */
$tooltipProvider.setTriggers({
'show-tooltip': 'hide-tooltip'
});
/* set default url */
$urlRouterProvider.otherwise('/en/home');
/* create ui-router states */
Object.keys(appStates).forEach(function(stateName){
$stateProvider.state(stateName, appStates[stateName]);
});
}
})();
/**
* Wrap ace directive according to page builder needs
*/
angular.module('pb.directives').directive('aceEditor', function(aceDataCompleter) {
'use strict';
var langTools = ace.require('ace/ext/language_tools');
return {
restrict: 'E',
replace: true,
scope: {
mode: '@',
autoCompletion: '@'
},
template: '<div ui-ace="{ mode: \'{{mode}}\', showGutter: true, onLoad: loaded }"></div>',
controller: function($scope, $parse, $attrs ) {
var ctrl = this;
$scope.loaded = function(editor) {
if (!editor) {
return;
}
if ($attrs.autoCompletion) {
var dataCompleter = aceDataCompleter($scope.$eval($scope.autoCompletion));
langTools.setCompleters([dataCompleter, langTools.keyWordCompleter ]);
editor.setOptions({
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
});
}
ctrl.editor = editor;
editor.setShowPrintMargin(false);
// used by e2e tests to clear the editor
editor.commands.addCommand({
name: 'deleteAll',
bindKey: {win: 'Ctrl-Alt-Shift-D', mac: 'Ctrl-Alt-Shift-D'},
exec: function(editor) {
editor.setValue('');
},
readOnly: false
});
};
$scope.$on('$destroy', function(){
if (ctrl.editor.completer) {
ctrl.editor.completer.detach();
}
ctrl.editor.destroy();
});
}
};
});
/**
* Element directive used to display alerts of the alerts service.
* Usage:
* <alerts></alerts>
*/
angular.module('pb.directives').directive('alerts', function() {
'use strict';
return {
restrict: 'E',
controller: function($scope, alerts) {
$scope.alerts = alerts.alerts;
$scope.remove = function(index) {
alerts.remove(index);
};
},
template: '<div class="alerts-wrapper" ng-show="alerts.length > 0"><alert ng-repeat="alert in alerts" type="{{ alert.type }}" close="remove($index)" class="text-center">{{ alert.message }}</alert></div>'
};
});
angular.module('pb.directives')
.directive('autofocus', function ($timeout) {
'use strict';
// Because autofocus does not work as expected with angular :/
return {
restrict: 'A',
require: '?aceEditor',
link: function(scope, element, attr, ctrl) {
var input = element.get(0);
if(attr.autofocus) {
// If the previous state was false and new one is true focus dat input
scope.$watch(attr.autofocus, function (newVal, oldVal) {
if(!oldVal && newVal) {
// To be trigger after the watch...
$timeout(function() {
input.focus();
if (input.type === 'text' && input.value.length > 0 ) {
input.select();
}
});
}
});
}
// If you do not depend of a property to watch as the defautl autofocus works only once with angular
if(!attr.autofocus) {
$timeout(function() {
if (ctrl) {
ctrl.editor.focus();
} else {
input.focus();
if (input.type === 'text' && input.value.length > 0 ) {
input.select();
}
}
});
}
}
};
});
/**
* Horizontal splitter based on an absolute positioning
* Allow to resize, close, open two panes
*
* Panes should be passed via attributes pane-top and pane-bottom by css selectors
*/
angular.module('pb.directives')
.directive('splitterHorizontal', function($document) {
return {
scope: {
paneTop: '@',
paneBottom: '@',
paneBottomMax: '@',
paneBottomMin: '@',
closedOnInit: '@'
},
transclude: true,
template: '<div class="splitter splitter-horizontal"></div><ng-transclude></ng-transclude>',
controller: function($scope) {
this.displayed = true;
this.topElem = $($scope.paneTop);
this.bottomElem = $($scope.paneBottom);
/**
* Compute new absolute y positioning according to min and max size
* @param {number} bottom bottom bound of the bottom pane
* @param {number} pointerY current pointer y position
* @returns {number}
*/
function computeY (bottom, pointerY) {
var y = bottom - pointerY;
if (y > $scope.paneBottomMax) {
y = $scope.paneBottomMax;
}
if (y < $scope.paneBottomMin) {
y = $scope.paneBottomMin;
}
return y;
}
/**
* Resize the two panes
* @param {number} pointerY current pointer y position
*/
this.resize = function(pointerY) {
var y = computeY(this.bottomElem[0].getBoundingClientRect().bottom, pointerY);
this.topElem.css({bottom: y + 'px'});
this.bottomElem.css({height: y + 'px'});
};
/**
* Close the bottom pane
*/
this.closeBottom = function() {
this.topElem.css({bottom: 0});
this.bottomElem.addClass('splitter-pane-closed');
this.displayed = false;
};
/**
* Open the bottom pane
*/
this.openBottom = function() {
this.bottomElem.removeClass('splitter-pane-closed');
this.topElem.css({bottom: this.bottomElem[0].getBoundingClientRect().height + 'px'});
this.displayed = true;
};
this.toggleBottom = function() {
if (this.displayed) {
this.closeBottom();
} else {
this.openBottom();
}
};
},
link: function($scope, $element, $attrs, $ctrl) {
var paneTop = $($attrs.paneTop);
var paneBottom = $($attrs.paneBottom);
paneTop.addClass('splitter-pane splitter-pane-top');
paneBottom.addClass('splitter-pane splitter-pane-bottom');
if (!!$scope.$eval($attrs.closedOnInit) ) {
$ctrl.closeBottom();
}
$element.find('.splitter').on('mousedown', function(event) {
event.preventDefault();
// delegate event to document beacause when moving mouse we go out of the splitter
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
});
// unbind events
function mouseup () {
paneTop.removeClass('splitter-onmove');
paneBottom.removeClass('splitter-onmove');
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
}
function mousemove (event) {
paneTop.addClass('splitter-onmove');
paneBottom.addClass('splitter-onmove');
$ctrl.resize(event.pageY);
}
$element.on('splitter:toggle:bottom', function(event) {
event.preventDefault();
$ctrl.toggleBottom();
});
}
};
});
</