Merge branch 'development'

* 0.4.0
  ...
This commit is contained in:
Mario Romano 2016-11-12 00:24:10 +00:00
commit f21ca970a1
923 changed files with 47977 additions and 10303 deletions

View File

@ -1,10 +1,19 @@
# http://editorconfig.org
root = true
[{src,scripts}/**.{ts,json,js}]
end_of_line = crlf
[*]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[package.json]
indent_style = space
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

32
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,32 @@
<!--
PLEASE FILL OUT THE FOLLOWING INFORMATION, THIS WILL HELP US TO RESOLVE YOUR PROBLEM FASTER.
REMEMBER FOR SUPPORT REQUESTS YOU CAN ALSO ASK ON OUR GITTER CHAT:
Please ask before on our gitter channel https://gitter.im/Alfresco/alfresco-ng2-components
-->
**Type of issue:** (check with "[x]")
- [ ] New feature request
- [ ] Bug
- [ ] Support request
**Current behavior:**
<!-- Describe the current behavior. -->
**Expected behavior:**
<!-- Describe the expected behavior. -->
**Steps to reproduce the issue:**
<!-- Describe the steps to reproduce the issue. -->
**Component name and version:**
<!-- Example: ng2-alfresco-login. Check before if this issue is still present in the most recent version -->
**Browser and version:**
<!-- [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] -->
**Node version (for build issues):**
<!-- To check the version: node --version -->
**New feature request:**
<!-- Describe the feature, motivation and the concrete use case (only in case of new feature request) -->

37
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,37 @@
<!--
Before submitting your PR, please check that your code follows our contribution guidelines:
https://github.com/Alfresco/alfresco-ng2-components/wiki/Code-contribution-acceptance-criteria
Check also that your commit messages follow our commit message format guidelines:
https://github.com/Alfresco/alfresco-ng2-components/wiki/Commit-format
-->
**Type of contribution:** (check one with "x")
```
[ ] Bugfix
[ ] Feature
[ ] Code formatting
[ ] Code Refactoring
[ ] Build related changes
[ ] Documentation
[ ] Other... Please describe:
```
**This PR adds the following feature:**
<!-- you can ignore this line in the case of a bugfix -->
**Current behavior:**
**New behavior:**
**This PR fixes the following issue:**
<!-- link to the open issue, ignore this if there are no issues open -->
**This PR introduces a breaking change:** (check one with "x")
- [ ] Yes
- [ ] No
<!-- Please describe the reason to introduce a breaking change, and what exactly breaks -->
**More information:**

4
.gitignore vendored
View File

@ -4,3 +4,7 @@ workspace.xml
.idea/
dist/
!systemjs.config.js
demo-shell-ng2/app/components/router/
ng2-components/ng2-alfresco-userinfo-old/demo/src/app/
ng2-components/ng2-alfresco-userinfo-old/src/services/bpm-user.service.spec.ts
ng2-components/ng2-alfresco-userinfo-old/src/services/ecm-user.service.spec.ts

View File

@ -27,9 +27,13 @@ env:
- MODULE=ng2-alfresco-upload
- MODULE=ng2-alfresco-viewer
- MODULE=ng2-alfresco-webscript
- MODULE=ng2-alfresco-tag
- MODULE=ng2-activiti-form
- MODULE=ng2-activiti-tasklist
- MODULE=ng2-activiti-processlist
- MODULE=ng2-activiti-diagrams
- MODULE=ng2-activiti-analytics
- MODULE=ng2-alfresco-userinfo
before_script:
- if ([ "$MODULE" != "ng2-alfresco-core" ]); then
@ -44,21 +48,12 @@ before_script:
- if ([ "$MODULE" == "ng2-activiti-processlist" ]); then
(cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; npm install; npm link);
fi
- if ([ "$MODULE" == "ng2-activiti-analytics" ]); then
(cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; npm install; npm link);
fi
- cd ng2-components/$MODULE;
- if ([ "$MODULE" != "ng2-alfresco-core" ]); then
npm link ng2-alfresco-core;
fi
- if ([ "$MODULE" == "ng2-alfresco-documentlist" ] || [ "$MODULE" == "ng2-alfresco-webscript" ] || [ "$MODULE" == "ng2-activiti-processlist" ] || [ "$MODULE" == "ng2-activiti-tasklist" ]); then
npm link ng2-alfresco-datatable;
fi
- if ([ "$MODULE" == "ng2-activiti-tasklist" ]); then
npm link ng2-activiti-form;
fi
- if ([ "$MODULE" == "ng2-activiti-processlist" ]); then
npm link ng2-activiti-tasklist;
fi
- npm install;
- npm run travis
- npm install;
- ls -ltrh ./node_modules/
script: npm run test
# Send coverage data to Coveralls
@ -79,3 +74,7 @@ cache:
- ng2-components/ng2-alfresco-upload/node_modules
- ng2-components/ng2-alfresco-viewer/node_modules
- ng2-components/ng2-alfresco-webscript/node_modules
- ng2-components/ng2-alfresco-tag/node_modules
- ng2-components/ng2-activiti-analytics/node_modules
- ng2-components/ng2-alfresco-userinfo/node_modules
- ng2-components/ng2-activiti-diagrams/node_modules

11
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/.DS_Store": true,
"**/*.js": { "when": "$(basename).ts"},
"**/*.js.map": { "when": "$(basename)"}
}
}

55
BROWSER-SUPPORT.md Normal file
View File

@ -0,0 +1,55 @@
# Browser Support
Browser support and polyfills guide.
Browser compatibility and support depends on targeted browsers and 3rd party libraries. ADF framework is based on the following libraries and components:
- Angular 2 (all ADF components)
- Material Design Lite (all ADF components)
- Moment.js (many ADF components)
- PDF.js (`ng2-alfresco-viewer` component)
- Raphael.js (`ng2-alfresco-diagrams`, `ng2-alfresco-analytics`)
- Chart.js (`ng2-alfresco-analytics`)
- Material Design - Date and Time Picker (`ng2-activiti-form`, `ng2-alfresco-analytics`)
## Browser polyfills
### Angular 2
Please refer to the [official guide](https://angular.io/docs/ts/latest/guide/browser-support.html) for Angular 2 browser support.
ADF (demo shell) imports by default the following set of recommended polyfills:
- [core-js](https://www.npmjs.com/package/core-js) (ES6 standard support)
### 3rd party libraries
Please refer to the following list of [popular polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills) for HTML5 cross-browser compatibility.
ADF (demo shell) imports by default the following set of recommended polyfills:
- [intl](https://www.npmjs.com/package/intl), Polyfill the ECMA-402 Intl API (except collation), **IE**/**Safari**
- [dom4](https://github.com/WebReflection/dom4), A fully tested and covered polyfill for new DOM Level 4 entries, **IE**
- [element.scrollintoviewifneeded-polyfill](https://www.npmjs.com/package/element.scrollintoviewifneeded-polyfill), simple JavaScript implementation of the non-standard WebKit method scrollIntoViewIfNeeded that can be called on DOM elements, **IE**/**Firefox**/**Safari**, required only to support **Material Design - Date and Time Picker** component
- [pdfjs compatibility](https://www.npmjs.com/package/pdfjs-dist), Portable Document Format (PDF) library that is built with HTML5, **IE**
- (dialog-polyfill)[https://www.npmjs.com/package/dialog-polyfill], Polyfill for the dialog element, **IE**/**Safari**/**Firefox**
## Example
```html
<!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
<!-- Polyfill(s) for pdf support -->
<script src="node_modules/pdfjs-dist/web/compatibility.js"></script>
<!-- Polyfill(s) for dialogs -->
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
```
For a complete code please refer to [demo shell](demo-shell-ng2/index.html) example.

View File

@ -2,94 +2,18 @@
The [Angular 2](https://angular.io/) based application development framework requires the following:
- An Alfresco Platform Repository (version [5.2.a-EA](https://wiki.alfresco.com/wiki/Community_file_list_201606-EA) or newer) to talk to, which has [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) enabled.
- An Alfresco Platform Repository (version [201609 Early Access](https://community.alfresco.com/docs/DOC-6372-alfresco-community-edition-file-list-201609-ea) or newer) to talk to, which has [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) enabled.
- [Download and install Activiti](https://www.alfresco.com/products/bpm/alfresco-activiti/trial)
- [Node.js](https://nodejs.org/en/) JavaScript runtime.
- [npm](https://www.npmjs.com/) package manager for JavaScript.
- (If you use ECM and BPM together) Make sure your user has the same username and password in both system
*Note: Default username for activiti is "admin@app.activiti.com" and "admin" for Alfresco, and also the default password are different. Change them to be equal.*
**Verify that you are running at least node `v5.x.x` and npm `3.x.x`**
by running `node -v` and `npm -v` in a terminal/console window.
Older versions produce errors.
## Installing Alfresco
Alfresco comes with installers that will install all the servers, webapps, and tools needed to run Alfresco.
- Download Alfresco Community from this [page](https://www.alfresco.com/products/community/download).
- Install Alfresco following these [instructions](http://docs.alfresco.com/5.1/concepts/installs-eval-intro.html).
This will install the following Alfresco web applications:
- Alfresco Platform with the Content Repository, which we need so we can access content from our custom web client
- Alfresco Solr, which we need so we can search for content from our custom web client
- Alfresco Share, not technically needed, but can be useful for creating users and groups, and to access and upload content to the repository while we are developing the custom web client
### Enable CORS in Alfresco
The web client that we are building with the application development framework will be loaded from a different web server than the Alfresco Platform is running on.
So we need to tell the Alfresco server that any request that comes in from this custom web client should be allowed access
to the Content Repository. This is done by enabling CORS.
To enable CORS in the Alfresco Platform do one of the following:
**Download and install the enable CORS module**
This is the easiest way, add the [enablecors](https://artifacts.alfresco.com/nexus/service/local/repositories/releases/content/org/alfresco/enablecors/1.0/enablecors-1.0.jar)
platform module JAR to the *$ALF_INSTALL_DIR/modules/platform* directory and restart the server.
Note. by default the CORS filter that is enabled will allow any orgin.
**Manually update the web.xml file**
Modify *$ALF_INSTALL_DIR/tomcat/webapps/alfresco/WEB-INF/web.xml* and uncomment the following section and update
`cors.allowOrigin` to `http://localhost:3000`:
```
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>http://localhost:3000</param-value>
</init-param>
<init-param>
<param-name>cors.allowSubdomains</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, PUT, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>origin, authorization, x-file-size, x-file-name, content-type, accept, x-file-type</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.maxAge</param-name>
<param-value>3600</param-value>
</init-param>
</filter>
```
When specifying the `cors.allowOrigin` URL make sure to use the URL that will be used by the web client.
Then uncomment filter mappings:
```
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/api/*</url-pattern>
<url-pattern>/service/*</url-pattern>
<url-pattern>/s/*</url-pattern>
<url-pattern>/cmisbrowser/*</url-pattern>
</filter-mapping>
```
## Installing Node.js
If you don't have Node.js installed then access this [page](https://nodejs.org/en/download/) and use the appropriate installer for your OS.
@ -101,4 +25,26 @@ $ node -v
v5.12.0
```
## Configure Nginx
To correctly configure Nginx use the following file [nginx.conf](/nginx.conf).
This will put Activiti, Alfresco and the app dev framework under the same domain.
* ECM : http://localhost:8888/alfresco/
* BPM : http://localhost:8888/activiti/
To make everything work, you have to change the address of the ECM and BPM. In the demo app you can do that clicking on the top left menu and changing the bottom left options: ECM host and BPM host.
This configuration assumes few things:
* Port mapping:
* Nginx entry point: 8888
* Demo Shell: 3000
* Alfresco: 8080
* Activiti: 9999
All those values can be modified at their respective `location` directive on the [nginx.conf](/nginx.conf) file.
It also need to be compiled with the [Headers More](https://www.nginx.com/resources/wiki/modules/headers_more/) module , which add more control over sending headers to the backend.
If you want to know more on how to install and configure Nginx to work with the Application Development Framework can be found [here](https://community.alfresco.com/community/application-development-framework/blog/2016/09/28/adf-development-set-up-with-nginx-proxy)

View File

@ -52,9 +52,9 @@ necessary configuration, see this [page](PREREQUISITES.md).
## Components
To view the complete list of all the components that you can use to build your custom Alfresco (ECM,BPM) client follow this link:
[Components](https://github.com/Alfresco/alfresco-ng2-components/tree/master/ng2-components).
[Components](/ng2-components).
You can browse all the components also at the following [page](http://devproducts.alfresco.com/).
You can browse all the components at the following [page](http://devproducts.alfresco.com/).
## Yeoman generators
@ -89,3 +89,17 @@ To deploy directly on your AWS instance our demo shell click the button below:
<a title="Deploy to AWS" href="https://console.aws.amazon.com/elasticbeanstalk/home?region=us-west-2#/newApplication?applicationName=Alfresco&solutionStackName=Node.js&tierName=WebServer&sourceBundleUrl=https://s3-us-west-2.amazonaws.com/elasticbeanstalk-us-west-2-677901592050/2016210o6l-Archive-v7.zip" target="_blank"><img src="http://d0.awsstatic.com/product-marketing/Elastic%20Beanstalk/deploy-to-aws.png" height="40"></a>
## Browser Support
All components are supported in the below browsers:
|**Browser** |**Version** |
|--- |--- |
|Chrome |Latest |
|Safari (OS X) |9.x |
|Firefox* |Latest |
|Edge |13, 14 |
|Internet Explorer |11 |
*Concerning Alfresco Upload Component, folder upload currently not supported [firefox known issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1188880 )
Please refer to [Browser Support](BROWSER-SUPPORT.md) article for more details.

View File

@ -14,10 +14,14 @@ environment:
- COMPONENT_NAME: ng2-alfresco-search
- COMPONENT_NAME: ng2-alfresco-upload
- COMPONENT_NAME: ng2-alfresco-viewer
- COMPONENT_NAME: ng2-alfresco-tag
- COMPONENT_NAME: ng2-alfresco-webscript
- COMPONENT_NAME: ng2-activiti-form
- COMPONENT_NAME: ng2-activiti-tasklist
- COMPONENT_NAME: ng2-activiti-processlist
- COMPONENT_NAME: ng2-activiti-diagrams
- COMPONENT_NAME: ng2-activiti-analytics
- COMPONENT_NAME: ng2-alfresco-userinfo
# Install scripts. (runs after repo cloning)
install:
@ -35,10 +39,10 @@ install:
- cd ng2-components/%COMPONENT_NAME%
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (npm link ng2-alfresco-core)
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (npm link ng2-activiti-tasklist)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm link ng2-activiti-tasklist)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (npm link ng2-alfresco-diagrams)
- npm install
# Post-install test scripts.

View File

@ -3,6 +3,7 @@ node_modules/
bower_components/
app/**/*.js
app/**/*.js.map
!app/js/Polyline.js
.idea
dist/

View File

@ -3,3 +3,15 @@
display: none;
}
}
.mdl-layout-title {
font-size: 17px;
}
.user-profile {
margin-right: 50px;
}
.mdl-button-padding {
padding-left: 20px;
}

View File

@ -1,7 +1,15 @@
<!-- Always shows a header, even in smaller screens. -->
<div mdl class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<div *ngIf="!isLoginPage()">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<div class="mdl-button-padding mdl-layout__header-row">
<div *ngIf="isLoggedIn()">
<ng2-alfresco-userinfo class="user-profile" [menuOpenType]="left">
</ng2-alfresco-userinfo>
</div>
<!-- Title -->
<span class="mdl-layout-title">Demo Application</span>
<!-- Add spacer, to align navigation to the right -->
@ -17,8 +25,9 @@
<a class="mdl-navigation__link" data-automation-id="uploader" href="" routerLink="/uploader">Uploader</a>
<a class="mdl-navigation__link" data-automation-id="activiti" href="" routerLink="/activiti">Activiti</a>
<a class="mdl-navigation__link" data-automation-id="webscript" href="" routerLink="/webscript">Webscript</a>
<a class="mdl-navigation__link" data-automation-id="tag" href="" routerLink="/tag">Tag</a>
<a class="mdl-navigation__link" data-automation-id="login" href="" routerLink="/login">Login</a>
<a class="mdl-navigation__link" data-automation-id="about" href="" routerLink="/about">About</a>
<a class="mdl-navigation__link" data-automation-id="settings" href="" routerLink="/settings">Settings</a>
</nav>
<!-- Right aligned menu below button -->
@ -29,12 +38,27 @@
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect"
for="demo-menu-lower-right">
<li class="mdl-menu__item " (click)="changeLanguage('en')"><span class="flag-icon flag-icon-gb"></span> English</li>
<li class="mdl-menu__item" (click)="changeLanguage('gr')"><span class="flag-icon flag-icon-gr"></span> Greek</li>
<li class="mdl-menu__item" (click)="changeLanguage('it')"><span class="flag-icon flag-icon-it"></span> Italian</li>
<li *ngIf="!isLoggedIn()" class="mdl-menu__item">More</li>
<a *ngIf="!isLoggedIn()" class="mdl-menu__item" routerLink="/login">Login</a>
<li *ngIf="isLoggedIn()" class="mdl-menu__item" (click)="onLogout($event)">Logout</li>
<li class="mdl-menu__item" (click)="changeLanguage('en')">
<span class="flag-icon flag-icon-gb"></span>
<label tabindex="0"> English</label>
</li>
<li class="mdl-menu__item" (click)="changeLanguage('gr')">
<span class="flag-icon flag-icon-gr"></span>
<label tabindex="0"> Greek</label>
</li>
<li class="mdl-menu__item" (click)="changeLanguage('it')">
<span class="flag-icon flag-icon-it"></span>
<label tabindex="0"> Italian</label>
</li>
<a class="mdl-menu__item" routerLink="/about">
<label tabindex="0">About</label>
</a>
<a *ngIf="!isLoggedIn()" class="mdl-menu__item" routerLink="/login">
<label tabindex="0">Login</label>
</a>
<li *ngIf="isLoggedIn()" class="mdl-menu__item" (click)="onLogout($event)">
<label tabindex="0">Logout</label>
</li>
</ul>
</div>
</header>
@ -47,22 +71,13 @@
<a class="mdl-navigation__link" href="" routerLink="/login" (click)="hideDrawer()">Login Demo</a>
<a class="mdl-navigation__link" href="" routerLink="/activiti" (click)="hideDrawer()">Activiti Components Demo</a>
<a class="mdl-navigation__link" href="" routerLink="/webscript" (click)="hideDrawer()">Webscript</a>
<a class="mdl-navigation__link" href="" routerLink="/about" (click)="hideDrawer()">About</a>
</nav>
<span class="mdl-layout-title">ECM host</span>
<nav class="mdl-navigation">
<input type="text" class="mdl-textfield__input" id="ecmHost" data-automation-id="ecmHost"
tabindex="1" (change)="onChangeECMHost($event)" value="{{ecmHost}}"/>
</nav>
<span class="mdl-layout-title">BPM host</span>
<nav class="mdl-navigation">
<input type="text" class="mdl-textfield__input" id="bpmHost" data-automation-id="bpmHost"
tabindex="1" (change)="onChangeBPMHost($event)" value="{{bpmHost}}"/>
<a class="mdl-navigation__link" href="" routerLink="/tag" (click)="hideDrawer()">Tag</a>
<a class="mdl-navigation__link" href="" routerLink="/settings" (click)="hideDrawer()">Settings</a>
</nav>
</div>
</div>
<main class="mdl-layout__content">
<div class="page-content">
<alfresco-login method="{{methodName}}"></alfresco-login>
<router-outlet></router-outlet>
</div>
</main>

View File

@ -16,29 +16,22 @@
*/
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES, Router } from '@angular/router';
import { Router } from '@angular/router';
import {
MDL,
AlfrescoSettingsService,
AlfrescoTranslationService,
AlfrescoPipeTranslate,
AlfrescoAuthenticationService
AlfrescoAuthenticationService,
AlfrescoSettingsService
} from 'ng2-alfresco-core';
import { SearchBarComponent } from './components/index';
declare var document: any;
@Component({
selector: 'alfresco-app',
templateUrl: 'app/app.component.html',
styleUrls: ['app/app.component.css'],
directives: [SearchBarComponent, ROUTER_DIRECTIVES, MDL],
pipes: [AlfrescoPipeTranslate]
styleUrls: ['app/app.component.css']
})
export class AppComponent {
translate: AlfrescoTranslationService;
searchTerm: string = '';
ecmHost: string = 'http://' + window.location.hostname + ':8080';
@ -46,38 +39,46 @@ export class AppComponent {
constructor(public auth: AlfrescoAuthenticationService,
public router: Router,
translate: AlfrescoTranslationService,
public alfrescoSettingsService: AlfrescoSettingsService) {
public alfrescoSettingsService: AlfrescoSettingsService,
private translate: AlfrescoTranslationService) {
this.setEcmHost();
this.setBpmHost();
this.setProvider();
this.translate = translate;
this.translate.addTranslationFolder();
if (translate) {
translate.addTranslationFolder();
}
public onChangeECMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value);
this.ecmHost = (<HTMLInputElement>event.target).value;
this.alfrescoSettingsService.ecmHost = this.ecmHost;
localStorage.setItem(`ecmHost`, this.ecmHost);
}
public onChangeBPMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value);
this.bpmHost = (<HTMLInputElement>event.target).value;
this.alfrescoSettingsService.bpmHost = this.bpmHost;
localStorage.setItem(`bpmHost`, this.bpmHost);
}
isLoggedIn(): boolean {
this.redirectToLoginPageIfNotLoggedIn();
return this.auth.isLoggedIn();
}
redirectToLoginPageIfNotLoggedIn(): void {
if (!this.isLoginPage() && !this.auth.isLoggedIn()) {
this.router.navigate(['/login']);
}
}
isLoginPage(): boolean {
return location.pathname === '/login' || location.pathname === '/' || location.pathname === '/settings';
}
onLogout(event) {
event.preventDefault();
this.auth.logout()
.subscribe(
() => this.router.navigate(['/login'])
() => {
this.router.navigate(['/login']);
},
($event: any) => {
if ($event && $event.response && $event.response.status === 401) {
this.router.navigate(['/login']);
} else {
console.error('An unknown error occurred while logging out', $event);
}
}
);
}
@ -117,4 +118,10 @@ export class AppComponent {
this.alfrescoSettingsService.bpmHost = this.bpmHost;
}
}
private setProvider() {
if (localStorage.getItem(`providers`)) {
this.alfrescoSettingsService.setProviders(localStorage.getItem(`providers`));
}
}
}

View File

@ -0,0 +1,93 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CoreModule } from 'ng2-alfresco-core';
import { SearchModule } from 'ng2-alfresco-search';
import { LoginModule } from 'ng2-alfresco-login';
import { DataTableModule } from 'ng2-alfresco-datatable';
import { DocumentListModule } from 'ng2-alfresco-documentlist';
import { UploadModule } from 'ng2-alfresco-upload';
import { TagModule } from 'ng2-alfresco-tag';
import { WebScriptModule } from 'ng2-alfresco-webscript';
import { ViewerModule } from 'ng2-alfresco-viewer';
import { ActivitiFormModule } from 'ng2-activiti-form';
import { ActivitiTaskListModule } from 'ng2-activiti-tasklist';
import { ActivitiProcessListModule } from 'ng2-activiti-processlist';
import { UserInfoComponentModule } from 'ng2-alfresco-userinfo';
import { AnalyticsModule } from 'ng2-activiti-analytics';
import { AppComponent } from './app.component';
import { routing } from './app.routes';
import {
DataTableDemoComponent,
SearchComponent,
SearchBarComponent,
LoginDemoComponent,
ActivitiDemoComponent,
FormViewer,
WebscriptComponent,
TagComponent,
AboutComponent,
FilesComponent,
FormNodeViewer,
SettingComponent
} from './components/index';
@NgModule({
imports: [
BrowserModule,
routing,
CoreModule.forRoot(),
LoginModule,
SearchModule.forRoot(),
DataTableModule,
DocumentListModule.forRoot(),
UploadModule.forRoot(),
TagModule.forRoot(),
WebScriptModule,
ViewerModule.forRoot(),
ActivitiFormModule.forRoot(),
ActivitiTaskListModule.forRoot(),
ActivitiProcessListModule.forRoot(),
UserInfoComponentModule.forRoot(),
AnalyticsModule.forRoot()
],
declarations: [
AppComponent,
SearchBarComponent,
DataTableDemoComponent,
SearchComponent,
SearchBarComponent,
LoginDemoComponent,
ActivitiDemoComponent,
FormViewer,
WebscriptComponent,
TagComponent,
AboutComponent,
FilesComponent,
FormNodeViewer,
SettingComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -15,22 +15,26 @@
* limitations under the License.
*/
import { provideRouter, RouterConfig } from '@angular/router';
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {
FilesComponent,
UploadButtonComponent,
DataTableDemoComponent,
SearchComponent,
LoginDemoComponent,
ActivitiDemoComponent,
WebscriptComponent,
TagComponent,
AboutComponent,
FormViewer
FormViewer,
FormNodeViewer,
SettingComponent
} from './components/index';
import { FormNodeViewer } from './components/activiti/form-node-viewer.component';
export const routes: RouterConfig = [
import { UploadButtonComponent } from 'ng2-alfresco-upload';
export const appRoutes: Routes = [
{ path: 'home', component: FilesComponent },
{ path: 'files', component: FilesComponent },
{ path: 'datatable', component: DataTableDemoComponent },
@ -39,12 +43,13 @@ export const routes: RouterConfig = [
{ path: 'login', component: LoginDemoComponent },
{ path: 'search', component: SearchComponent },
{ path: 'activiti', component: ActivitiDemoComponent },
{ path: 'activiti/appId/:appId', component: ActivitiDemoComponent },
{ path: 'activiti/tasks/:id', component: FormViewer },
{ path: 'activiti/tasksnode/:id', component: FormNodeViewer },
{ path: 'webscript', component: WebscriptComponent },
{ path: 'about', component: AboutComponent }
{ path: 'tag', component: TagComponent },
{ path: 'about', component: AboutComponent },
{ path: 'settings', component: SettingComponent }
];
export const appRouterProviders = [
provideRouter(routes)
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

View File

@ -17,21 +17,14 @@
import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import {
ALFRESCO_DATATABLE_DIRECTIVES,
ObjectDataTableAdapter /*,
DataSorting,
ObjectDataRow,
ObjectDataColumn*/
} from 'ng2-alfresco-datatable';
import { ObjectDataTableAdapter } from 'ng2-alfresco-datatable';
declare let __moduleName: string;
@Component({
moduleId: __moduleName,
selector: 'about-page',
templateUrl: './about.component.html',
directives: [ALFRESCO_DATATABLE_DIRECTIVES]
templateUrl: './about.component.html'
})
export class AboutComponent implements OnInit {

View File

@ -1,65 +1,98 @@
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<!-- Tabs -->
<div class="mdl-layout__tab-bar mdl-js-ripple-effect">
<a href="#scroll-tab-1" class="mdl-layout__tab is-active">TASK LIST</a>
<a href="#scroll-tab-2" class="mdl-layout__tab">PROCESS LIST</a>
<a href="#scroll-tab-3" class="mdl-layout__tab">REPORT</a>
<!-- TABS -->
<div class="mdl-layout__tab-bar mdl-js-ripple-effect" #tabheader>
<a id="apps-header" href="#apps" class="mdl-layout__tab is-active">APPS</a>
<a id="tasks-header" href="#tasks" class="mdl-layout__tab">TASK LIST</a>
<a id="processes-header" href="#processes" class="mdl-layout__tab">PROCESS LIST</a>
<a id="report-header" href="#report" class="mdl-layout__tab">ANALYTICS</a>
</div>
</header>
<main class="mdl-layout__content activiti">
<section class="mdl-layout__tab-panel is-active" id="scroll-tab-1">
<main class="mdl-layout__content activiti" #tabmain>
<!-- APPPS COMPONENT -->
<section class="mdl-layout__tab-panel is-active" id="apps">
<div class="page-content">
<activiti-apps [layoutType]="layoutType" (appClick)="onAppClick($event)" #activitiapps></activiti-apps>
</div>
</section>
<!-- TASKS COMPONENT -->
<section class="mdl-layout__tab-panel" id="tasks">
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp">
<span>Task Filters</span>
<activiti-filters (filterClick)="onTaskFilterClick($event)"></activiti-filters>
<activiti-start-task [appId]="appId" (onSuccess)="onStartTaskSuccess($event)"></activiti-start-task>
<activiti-filters [appId]="appId" (filterClick)="onTaskFilterClick($event)" (onSuccess)="onSuccessTaskFilterList($event)"
#activitifilter></activiti-filters>
</div>
<div class="mdl-cell mdl-cell--3-col task-column mdl-shadow--2dp">
<span>Task List</span>
<activiti-tasklist *ngIf="isTaskListSelected()" [taskFilter]="taskFilter" [schemaColumn]="taskSchemaColumns"
(rowClick)="onTaskRowClick($event)" #activititasklist></activiti-tasklist>
<activiti-tasklist [taskFilter]="taskFilter" [data]="dataTasks"
(rowClick)="onTaskRowClick($event)" (onSuccess)="onSuccessTaskList($event)"
#activititasklist></activiti-tasklist>
</div>
<div class="mdl-cell mdl-cell--7-col task-column mdl-shadow--2dp">
<span>Task Details</span>
<activiti-task-details [taskId]="currentTaskId" #activitidetails></activiti-task-details>
<activiti-task-details [taskId]="currentTaskId" (formCompleted)="onFormCompleted($event)"
#activitidetails></activiti-task-details>
</div>
</div>
</div>
</section>
<section class="mdl-layout__tab-panel" id="scroll-tab-2">
<!-- PROCESS COMPONENT -->
<section class="mdl-layout__tab-panel" id="processes">
<div class="page-content">
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column">
<span>Process Filters</span>
<activiti-start-process [appId]="appId"></activiti-start-process>
<activiti-process-filters (filterClick)="onProcessFilterClick($event)"></activiti-process-filters>
<activiti-start-process-instance [appId]="appId" (start)="onStartProcessInstance($event)"></activiti-start-process-instance>
<activiti-process-instance-filters [appId]="appId"
(filterClick)="onProcessFilterClick($event)" (onSuccess)="onSuccessProcessFilterList($event)"
#activitiprocessfilter></activiti-process-instance-filters>
</div>
<div class="mdl-cell mdl-cell--3-col task-column">
<span>Process List</span>
<activiti-process-instance-list [filter]="processFilter" [schemaColumn]="processSchemaColumns"
(rowClick)="onProcessRowClick($event)" #activitiprocesslist></activiti-process-instance-list>
<activiti-process-instance-list [filter]="processFilter" [data]="dataProcesses"
(rowClick)="onProcessRowClick($event)" (onSuccess)="onSuccessProcessList($event)"
#activitiprocesslist></activiti-process-instance-list>
</div>
<div class="mdl-cell mdl-cell--7-col task-column">
<span>Process Details</span>
<activiti-process-instance-details [processInstanceId]="currentProcessInstanceId" (taskFormCompleted)="taskFormCompleted()" (processCancelled)="processCancelled()" #activitiprocessdetails></activiti-process-instance-details>
<activiti-process-instance-details [processInstanceId]="currentProcessInstanceId" (activitiprocesslist)="taskFormCompleted()" (processCancelled)="processCancelled()" #activitiprocessdetails></activiti-process-instance-details>
</div>
</div>
</div>
</div>
</section>
<section class="mdl-layout__tab-panel" id="scroll-tab-3">
<div class="page-content"><!-- Your content goes here --></div>
</section>
<section class="mdl-layout__tab-panel" id="scroll-tab-4">
<div class="page-content"><!-- Your content goes here --></div>
</section>
<section class="mdl-layout__tab-panel" id="scroll-tab-5">
<div class="page-content"><!-- Your content goes here --></div>
</section>
<section class="mdl-layout__tab-panel" id="scroll-tab-6">
<div class="page-content"><!-- Your content goes here --></div>
<!-- ANALYTICS COMPONENT -->
<section class="mdl-layout__tab-panel" id="report">
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp">
<analytics-report-list (reportClick)="onReportClick($event)"></analytics-report-list>
</div>
<div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp">
<activiti-analytics [appId]="appId" *ngIf="report" [reportId]="report.id"></activiti-analytics>
</div>
</div>
</div>
</section>
</main>
</div>

View File

@ -16,9 +16,19 @@
*/
import { Component, AfterViewChecked, ViewChild, Input } from '@angular/core';
import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist';
import { ACTIVITI_PROCESSLIST_DIRECTIVES } from 'ng2-activiti-processlist';
import { ActivitiForm } from 'ng2-activiti-form';
import {
AppDefinitionRepresentationModel,
FilterRepresentationModel,
ActivitiApps,
ActivitiTaskList
} from 'ng2-activiti-tasklist';
import { ActivitiProcessInstanceListComponent } from 'ng2-activiti-processlist';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
import {
ObjectDataTableAdapter,
DataSorting
} from 'ng2-alfresco-datatable';
declare let __moduleName: string;
declare var componentHandler;
@ -27,25 +37,41 @@ declare var componentHandler;
moduleId: __moduleName,
selector: 'activiti-demo',
templateUrl: './activiti-demo.component.html',
styleUrls: ['./activiti-demo.component.css'],
directives: [ALFRESCO_TASKLIST_DIRECTIVES, ACTIVITI_PROCESSLIST_DIRECTIVES, ActivitiForm]
styleUrls: ['./activiti-demo.component.css']
})
export class ActivitiDemoComponent implements AfterViewChecked {
currentChoice: string = 'task-list';
@ViewChild('activitiapps')
activitiapps: ActivitiApps;
@ViewChild('activitifilter')
activitifilter: any;
@ViewChild('activitidetails')
activitidetails: any;
@ViewChild('activititasklist')
activititasklist: any;
@ViewChild(ActivitiTaskList)
activititasklist: ActivitiTaskList;
@ViewChild('activitiprocesslist')
activitiprocesslist: any;
@ViewChild('activitiprocessfilter')
activitiprocessfilter: any;
@ViewChild(ActivitiProcessInstanceListComponent)
activitiprocesslist: ActivitiProcessInstanceListComponent;
@ViewChild('activitiprocessdetails')
activitiprocessdetails: any;
@ViewChild('tabmain')
tabMain: any;
@ViewChild('tabheader')
tabHeader: any;
@Input()
appId: number;
layoutType: string;
currentTaskId: string;
currentProcessInstanceId: string;
@ -53,51 +79,106 @@ export class ActivitiDemoComponent implements AfterViewChecked {
processSchemaColumns: any [] = [];
taskFilter: any;
report: any;
processFilter: any;
@Input()
appId: string;
sub: Subscription;
setChoice($event) {
this.currentChoice = $event.target.value;
dataTasks: ObjectDataTableAdapter;
dataProcesses: ObjectDataTableAdapter;
constructor(private route: ActivatedRoute) {
this.dataTasks = new ObjectDataTableAdapter(
[],
[
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true},
{type: 'text', key: 'created', title: 'Created', cssClass: 'hidden', sortable: true}
]
);
this.dataTasks.setSorting(new DataSorting('created', 'desc'));
this.dataProcesses = new ObjectDataTableAdapter(
[],
[
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true},
{type: 'text', key: 'started', title: 'Started', cssClass: 'hidden', sortable: true}
]
);
this.dataProcesses.setSorting(new DataSorting('started', 'desc'));
}
isProcessListSelected() {
return this.currentChoice === 'process-list';
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
this.appId = params['appId'];
});
this.layoutType = ActivitiApps.LAYOUT_GRID;
}
isTaskListSelected() {
return this.currentChoice === 'task-list';
ngOnDestroy() {
this.sub.unsubscribe();
}
constructor() {
this.taskSchemaColumns = [
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true}
// {type: 'text', key: 'created', title: 'Created', sortable: true}
];
this.processSchemaColumns = [
{type: 'text', key: 'name', title: 'Name', cssClass: 'full-width name-column', sortable: true}
];
onAppClick(app: AppDefinitionRepresentationModel) {
this.appId = app.id;
this.taskFilter = null;
this.currentTaskId = null;
this.processFilter = null;
this.currentProcessInstanceId = null;
this.changeTab('apps', 'tasks');
}
onTaskFilterClick(event: any) {
changeTab(origin: string, destination: string) {
this.tabMain.nativeElement.children[origin].classList.remove('is-active');
this.tabMain.nativeElement.children[destination].classList.add('is-active');
this.tabHeader.nativeElement.children[`${origin}-header`].classList.remove('is-active');
this.tabHeader.nativeElement.children[`${destination}-header`].classList.add('is-active');
}
onTaskFilterClick(event: FilterRepresentationModel) {
this.taskFilter = event;
this.activititasklist.load(this.taskFilter);
}
onReportClick(event: any) {
this.report = event;
}
onSuccessTaskFilterList(event: any) {
this.taskFilter = this.activitifilter.getCurrentFilter();
}
onStartTaskSuccess(event: any) {
this.activititasklist.reload();
}
onSuccessTaskList(event: FilterRepresentationModel) {
this.currentTaskId = this.activititasklist.getCurrentTaskId();
}
onProcessFilterClick(event: any) {
this.processFilter = event.filter;
this.activitiprocesslist.load(this.processFilter);
this.processFilter = event;
}
onSuccessProcessFilterList(event: any) {
this.processFilter = this.activitiprocessfilter.getCurrentFilter();
}
onSuccessProcessList(event: any) {
this.currentProcessInstanceId = this.activitiprocesslist.getCurrentProcessId();
}
onTaskRowClick(taskId) {
this.currentTaskId = taskId;
this.activitidetails.loadDetails(this.currentTaskId);
}
onProcessRowClick(processInstanceId) {
this.currentProcessInstanceId = processInstanceId;
this.activitiprocessdetails.load(this.currentProcessInstanceId);
}
onStartProcessInstance() {
this.activitiprocesslist.reload();
}
processCancelled(data: any) {
@ -105,10 +186,19 @@ export class ActivitiDemoComponent implements AfterViewChecked {
this.activitiprocesslist.reload();
}
onSuccessNewProcess(data: any) {
this.activitiprocesslist.reload();
}
taskFormCompleted(data: any) {
this.activitiprocesslist.reload();
}
onFormCompleted(form) {
this.activititasklist.load(this.taskFilter);
this.currentTaskId = null;
}
ngAfterViewChecked() {
// workaround for MDL issues with dynamic components
if (componentHandler) {

View File

@ -16,8 +16,7 @@
*/
import { Component, OnInit, OnDestroy, AfterViewChecked } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivitiForm, FormService, EcmModelService, NodeService } from 'ng2-activiti-form';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
declare let __moduleName: string;
@ -27,9 +26,7 @@ declare var componentHandler;
moduleId: __moduleName,
selector: 'form-node-viewer',
templateUrl: './form-node-viewer.component.html',
styleUrls: ['./form-node-viewer.component.css'],
directives: [ActivitiForm],
providers: [FormService, EcmModelService, NodeService]
styleUrls: ['./form-node-viewer.component.css']
})
export class FormNodeViewer implements OnInit, OnDestroy, AfterViewChecked {
@ -37,9 +34,7 @@ export class FormNodeViewer implements OnInit, OnDestroy, AfterViewChecked {
private sub: Subscription;
constructor(private formService: FormService,
private route: ActivatedRoute,
private router: Router) {
constructor(private route: ActivatedRoute) {
}
ngOnInit() {

View File

@ -16,8 +16,7 @@
*/
import { Component, OnInit, OnDestroy, AfterViewChecked } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivitiForm, FormService, EcmModelService, NodeService } from 'ng2-activiti-form';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
declare let __moduleName: string;
@ -27,9 +26,7 @@ declare var componentHandler;
moduleId: __moduleName,
selector: 'form-viewer',
templateUrl: './form-viewer.component.html',
styleUrls: ['./form-viewer.component.css'],
directives: [ActivitiForm],
providers: [FormService, EcmModelService, NodeService]
styleUrls: ['./form-viewer.component.css']
})
export class FormViewer implements OnInit, OnDestroy, AfterViewChecked {
@ -37,9 +34,7 @@ export class FormViewer implements OnInit, OnDestroy, AfterViewChecked {
private sub: Subscription;
constructor(private formService: FormService,
private route: ActivatedRoute,
private router: Router) {
constructor(private route: ActivatedRoute) {
}
ngOnInit() {

View File

@ -9,22 +9,22 @@
</div>
<div class="p-10">
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored"
(click)="reset()">
Reset to default
</button>
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored"
(click)="addRow()">
Add row
</button>
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored"
(click)="replaceRows()">
Replace rows
</button>
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored"
(click)="replaceColumns()">
Replace columns
</button>

View File

@ -17,7 +17,6 @@
import { Component } from '@angular/core';
import {
ALFRESCO_DATATABLE_DIRECTIVES,
ObjectDataTableAdapter,
DataSorting,
ObjectDataRow,
@ -29,8 +28,7 @@ declare let __moduleName: string;
@Component({
moduleId: __moduleName,
selector: 'datatable-demo',
templateUrl: './datatable-demo.component.html',
directives: [ALFRESCO_DATATABLE_DIRECTIVES]
templateUrl: './datatable-demo.component.html'
})
export class DataTableDemoComponent {

View File

@ -1,8 +1,7 @@
<div *ngIf="!fileShowed">
<div class="container">
<alfresco-upload-drag-area
[showUploadDialog]="true"
[currentFolderPath]="currentPath"
[uploaddirectory]=""
[versioning] = "versioning"
(onSuccess)="documentList.reload()">
<alfresco-document-list-breadcrumb
@ -158,13 +157,13 @@
<h5>Upload</h5>
<br>
<div *ngIf="acceptedFilesTypeShow">
<span class="mdl-input__label">Extension accepted</span>
<label class="mdl-input__label">Extension accepted
<input type="text" data-automation-id="accepted-files-type" [(ngModel)]="acceptedFilesType">
</label>
<br/>
</div>
<div *ngIf="!acceptedFilesTypeShow">
<alfresco-upload-button data-automation-id="multiple-file-upload"
[uploaddirectory]="currentPath"
[currentFolderPath]="currentPath"
[multipleFiles]="multipleFileUpload"
[uploadFolders]="folderUpload"
@ -175,7 +174,6 @@
</div>
<div *ngIf="acceptedFilesTypeShow">
<alfresco-upload-button data-automation-id="multiple-file-upload"
[uploaddirectory]="currentPath"
[currentFolderPath]="currentPath"
acceptedFilesType="{{acceptedFilesType}}"
[multipleFiles]="multipleFileUpload"
@ -185,11 +183,14 @@
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-upload-button>
</div>
<file-uploading-dialog #fileDialog></file-uploading-dialog>
</div>
<div *ngIf="fileShowed">
<alfresco-viewer [(showViewer)]="fileShowed"
[fileNodeId]="fileNodeId"
[overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>
</div>
<file-uploading-dialog #fileDialog></file-uploading-dialog>

View File

@ -17,24 +17,14 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import {
DOCUMENT_LIST_DIRECTIVES,
DOCUMENT_LIST_PROVIDERS,
DocumentActionsService,
DocumentList,
ContentActionHandler,
DocumentActionModel,
FolderActionModel
} from 'ng2-alfresco-documentlist';
import {
MDL,
AlfrescoContentService,
CONTEXT_MENU_DIRECTIVES,
AlfrescoPipeTranslate
} from 'ng2-alfresco-core';
import { PaginationComponent } from 'ng2-alfresco-datatable';
import { ALFRESCO_ULPOAD_COMPONENTS } from 'ng2-alfresco-upload';
import { VIEWERCOMPONENT } from 'ng2-alfresco-viewer';
import { FormService } from 'ng2-activiti-form';
declare let __moduleName: string;
@ -43,17 +33,7 @@ declare let __moduleName: string;
moduleId: __moduleName,
selector: 'files-component',
templateUrl: './files.component.html',
styleUrls: ['./files.component.css'],
directives: [
DOCUMENT_LIST_DIRECTIVES,
MDL,
ALFRESCO_ULPOAD_COMPONENTS,
VIEWERCOMPONENT,
CONTEXT_MENU_DIRECTIVES,
PaginationComponent
],
providers: [DOCUMENT_LIST_PROVIDERS, FormService],
pipes: [AlfrescoPipeTranslate]
styleUrls: ['./files.component.css']
})
export class FilesComponent implements OnInit {
currentPath: string = '/Sites/swsdp/documentLibrary';
@ -70,8 +50,8 @@ export class FilesComponent implements OnInit {
@ViewChild(DocumentList)
documentList: DocumentList;
constructor(private contentService: AlfrescoContentService,
private documentActions: DocumentActionsService,
constructor(private documentActions: DocumentActionsService,
public auth: AlfrescoAuthenticationService,
private formService: FormService,
private router: Router) {
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
@ -126,10 +106,14 @@ export class FilesComponent implements OnInit {
}
ngOnInit() {
if ( this.auth.isBpmLoggedIn() ) {
this.formService.getProcessDefinitions().subscribe(
defs => this.setupBpmActions(defs || []),
err => console.log(err)
);
} else {
console.log('You are not logged in');
}
}
viewActivitiForm(event?: any) {

View File

@ -15,7 +15,6 @@
* limitations under the License.
*/
export { UploadButtonComponent } from 'ng2-alfresco-upload';
export { DataTableDemoComponent } from './datatable/datatable-demo.component';
export { SearchComponent } from './search/search.component';
export { SearchBarComponent } from './search/search-bar.component';
@ -23,5 +22,8 @@ export { LoginDemoComponent } from './login/login-demo.component';
export { ActivitiDemoComponent } from './activiti/activiti-demo.component';
export { FormViewer } from './activiti/form-viewer.component';
export { WebscriptComponent } from './webscript/webscript.component';
export { TagComponent } from './tag/tag.component';
export { AboutComponent } from './about/about.component';
export { FilesComponent } from './files/files.component';
export { FormNodeViewer } from './activiti/form-node-viewer.component';
export { SettingComponent } from './setting/setting.component';

View File

@ -0,0 +1,14 @@
.setting {
border-radius: 8px; position: absolute; background-color: papayawhip; color: cadetblue; left: 10px; top: 10px; z-index: 1;
}
.banned{
width:130px;margin: 10px;
}
.toggle {
width:120px;margin: 20px;
}
.setting-button {
position: absolute; right: 10px; top: 10px; z-index: 1;
}

View File

@ -1,15 +1,36 @@
<div style="border-radius: 8px; position: absolute; background-color: papayawhip; color: cadetblue; left: 10px; top: 10px; z-index: 1;">
<p style="width:120px;margin: 20px;">
<div class="setting">
<p class="toggle">
<label for="switch1" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" id="switch1" class="mdl-switch__input" checked (click)="toggleECM(ecm.checked)" #ecm>
<input type="checkbox" id="switch1" [checked]="isECM" class="mdl-switch__input"
(click)="toggleECM(ecm.checked)" #ecm>
<span class="mdl-switch__label">ECM</span>
</label>
</p>
<p style="width:120px;margin: 20px;">
<p class="toggle">
<label for="switch2" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" id="switch2" class="mdl-switch__input" (click)="toggleBPM(bpm.checked)" #bpm>
<input type="checkbox" id="switch2" [checked]="isBPM" class="mdl-switch__input"
(click)="toggleBPM(bpm.checked)" #bpm>
<span class="mdl-switch__label">BPM</span>
</label>
</p>
<p class="toggle">
<label for="switch3" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" id="switch3" class="mdl-switch__input" checked (click)="toggleCSRF()" #csrf>
<span class="mdl-switch__label">CSRF</span>
</label>
</p>
<p class="banned">
<label for="blacklistusername">Banned username</label><br>
<input id="blacklistusername" type="text" placeholder="banned username" [(ngModel)]="blackListUsername"/>
</p>
</div>
<alfresco-login [providers]="providers" (onSuccess)="onLogin($event)" (onError)="onError($event)"></alfresco-login>
<a class="mdl-navigation__link setting-button" data-automation-id="settings" href="" routerLink="/settings">
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">settings</i>
</button>
</a>
<alfresco-login [providers]="providers" [fieldsValidation]="customValidation" [disableCsrf]="disableCsrf"
(executeSubmit)="validateForm($event)"
(onSuccess)="onLogin($event)"
(onError)="onError($event)" #alfrescologin></alfresco-login>

View File

@ -15,9 +15,9 @@
* limitations under the License.
*/
import {Component} from '@angular/core';
import {AlfrescoLoginComponent} from 'ng2-alfresco-login';
import {ROUTER_DIRECTIVES, Router} from '@angular/router';
import { Component, ViewChild, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Validators } from '@angular/forms';
declare let __moduleName: string;
@ -25,14 +25,51 @@ declare let __moduleName: string;
moduleId: __moduleName,
selector: 'login-demo',
templateUrl: './login-demo.component.html',
directives: [ROUTER_DIRECTIVES, AlfrescoLoginComponent],
pipes: []
styleUrls: ['./login-demo.component.css']
})
export class LoginDemoComponent {
export class LoginDemoComponent implements OnInit {
@ViewChild('alfrescologin')
alfrescologin: any;
providers: string = 'ECM';
disableCsrf: boolean = false;
blackListUsername: string;
customValidation: any;
isECM: boolean = true;
isBPM: boolean = false;
constructor(public router: Router) {
this.customValidation = {
username: ['', Validators.compose([Validators.required, Validators.minLength(4)])],
password: ['', Validators.required]
};
}
ngOnInit() {
this.alfrescologin.addCustomValidationError('username', 'required', 'LOGIN.MESSAGES.USERNAME-REQUIRED');
this.alfrescologin.addCustomValidationError('username', 'minlength', 'LOGIN.MESSAGES.USERNAME-MIN');
this.alfrescologin.addCustomValidationError('password', 'required', 'LOGIN.MESSAGES.PASSWORD-REQUIRED');
if (localStorage.getItem('providers')) {
this.providers = localStorage.getItem('providers');
}
this.setProviders();
}
setProviders() {
if (this.providers === 'BPM') {
this.isECM = false;
this.isBPM = true;
} else if (this.providers === 'ECM') {
this.isECM = true;
this.isBPM = false;
} else if (this.providers === 'ALL') {
this.isECM = true;
this.isBPM = true;
}
}
onLogin($event) {
@ -49,9 +86,13 @@ export class LoginDemoComponent {
this.providers = 'ALL';
} else if (checked) {
this.providers = 'ECM';
} else {
this.providers = undefined;
} else if (!checked && this.providers === 'ALL') {
this.providers = 'BPM';
} else if (!checked && this.providers === 'ECM') {
this.providers = '';
}
localStorage.setItem('providers', this.providers);
}
toggleBPM(checked) {
@ -59,8 +100,25 @@ export class LoginDemoComponent {
this.providers = 'ALL';
} else if (checked) {
this.providers = 'BPM';
} else {
this.providers = undefined;
} else if (!checked && this.providers === 'ALL') {
this.providers = 'ECM';
} else if (!checked && this.providers === 'BPM') {
this.providers = '';
}
localStorage.setItem('providers', this.providers);
}
toggleCSRF() {
this.disableCsrf = !this.disableCsrf;
}
validateForm(event: any) {
let values = event.values;
if (values.controls['username'].value === this.blackListUsername) {
this.alfrescologin.addCustomFormError('username', 'This particular username has been blocked');
event.preventDefault();
}
}
}

View File

@ -1,53 +0,0 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
/*
import { ElementRef, DynamicComponentLoader, Directive, Attribute } from '@angular/core';
import { Router, RouterOutlet, ComponentInstruction } from '@angular/router-deprecated';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core/dist/ng2-alfresco-core';
@Directive({selector: 'auth-router-outlet'})
export class AuthRouterOutlet extends RouterOutlet {
publicRoutes: Array<string>;
private router: Router;
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
private authentication: AlfrescoAuthenticationService) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.router = _parentRouter;
this.publicRoutes = [
'', 'login', 'signup'
];
}
activate(instruction: ComponentInstruction) {
if (this._canActivate(instruction.urlPath)) {
return super.activate(instruction);
}
this.router.navigate(['Login']);
}
_canActivate(url) {
return this.publicRoutes.indexOf(url) !== -1
|| this.authentication.isLoggedIn();
}
}
*/

View File

@ -1,5 +1,12 @@
<alfresco-search-control *ngIf="isLoggedIn()" [searchTerm]="searchTerm" [autocomplete]="false"
(searchChange)="searchTermChange($event);" (expand)="onExpandToggle($event);" (preview)="onFileClicked($event)"></alfresco-search-control>
<alfresco-search-control *ngIf="isLoggedIn()"
[searchTerm]="searchTerm"
[autocomplete]="false"
[liveSearchResultType]="'cm:content'"
(searchSubmit)="onSearchSubmit($event);"
(searchChange)="onSearchTermChange($event);"
(expand)="onExpandToggle($event);"
(fileSelect)="onFileClicked($event)">
</alfresco-search-control>
<alfresco-viewer [(showViewer)]="fileShowed"
[fileNodeId]="fileNodeId"

View File

@ -17,39 +17,26 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { Router } from '@angular/router';
import { ALFRESCO_SEARCH_DIRECTIVES } from 'ng2-alfresco-search';
import { VIEWERCOMPONENT } from 'ng2-alfresco-viewer';
import {
AlfrescoAuthenticationService,
AlfrescoContentService
} from 'ng2-alfresco-core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
declare let __moduleName: string;
@Component({
moduleId: __moduleName,
selector: 'search-bar',
templateUrl: './search-bar.component.html',
styles: [`
`],
directives: [ ALFRESCO_SEARCH_DIRECTIVES, VIEWERCOMPONENT ]
templateUrl: './search-bar.component.html'
})
export class SearchBarComponent {
urlFile: string;
fileName: string;
mimeType: string;
fileNodeId: string;
fileShowed: boolean = false;
searchTerm: string = '';
@Output()
expand = new EventEmitter();
constructor(
public router: Router,
public auth: AlfrescoAuthenticationService,
public contentService: AlfrescoContentService
) {
constructor(public router: Router,
public auth: AlfrescoAuthenticationService) {
}
isLoggedIn(): boolean {
@ -57,25 +44,27 @@ export class SearchBarComponent {
}
/**
* Called when a new search term is submitted
* Called when the user submits the search, e.g. hits enter or clicks submit
*
* @param params Parameters relating to the search
* @param event Parameters relating to the search
*/
searchTermChange(params) {
this.router.navigate(['Search', {
q: params.value
onSearchSubmit(event) {
this.router.navigate(['/search', {
q: event.value
}]);
}
onFileClicked(event) {
if (event.value.entry.isFile) {
this.fileName = event.value.entry.name;
this.mimeType = event.value.entry.content.mimeType;
this.urlFile = this.contentService.getContentUrl(event.value);
this.fileNodeId = event.value.entry.id;
this.fileShowed = true;
}
}
onSearchTermChange(event) {
this.searchTerm = event.value;
}
onExpandToggle(event) {
this.expand.emit(event);
}

View File

@ -1,8 +1,8 @@
<div class="search-results-container">
<h1>Search results</h1>
<alfresco-search (preview)="onFileClicked($event)"></alfresco-search>
<alfresco-search [resultType]="'cm:content'" (preview)="onFileClicked($event)"></alfresco-search>
</div>
<alfresco-viewer [(showViewer)]="previewActive" [urlFile]="previewContentUrl" [fileName]="previewName" [mimeType]="previewMimeType" [overlayMode]="true">
<alfresco-viewer [(showViewer)]="fileShowed" [fileNodeId]="fileNodeId" [overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>

View File

@ -16,9 +16,6 @@
*/
import { Component } from '@angular/core';
import { AlfrescoContentService } from 'ng2-alfresco-core';
import { ALFRESCO_SEARCH_DIRECTIVES } from 'ng2-alfresco-search';
import { VIEWERCOMPONENT } from 'ng2-alfresco-viewer';
declare let __moduleName: string;
@ -47,24 +44,17 @@ declare let __moduleName: string;
width: 100%;
}
}
`],
directives: [ ALFRESCO_SEARCH_DIRECTIVES, VIEWERCOMPONENT ]
`]
})
export class SearchComponent {
previewContentUrl: string;
previewName: string;
previewMimeType: string;
previewActive: boolean = false;
fileShowed: boolean = false;
fileNodeId: string;
constructor(public contentService: AlfrescoContentService) {
}
onFileClicked(event) {
if (event.value.entry.isFile) {
this.fileNodeId = event.value.entry.id;
this.previewActive = true;
this.fileShowed = true;
}
}
}

View File

@ -0,0 +1,26 @@
.setting-card.mdl-card {
width: 320px;
height: 320px;
}
.setting-card > .mdl-card__title {
color: #fff;
background: bottom right 15% no-repeat #1fbcd2;
}
.setting-card-padding {
width: 50%;
display: table-cell;
vertical-align: middle;
margin: 0;
}
.setting-container {
display: table;
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}
.icon-margin {
margin-right: 9px;
}

View File

@ -0,0 +1,30 @@
<div class="setting-container">
<div style="display: table-row">
<div class="setting-card-padding"></div>
<div class="setting-card mdl-card mdl-shadow--2dp">
<div class="mdl-card__title mdl-card--expand">
<h2 class="mdl-card__title-text">SETTINGS</h2>
</div>
<div class="mdl-card__actions mdl-card--border">
<div class="mdl-card__supporting-text">
ECM host URL configuration
</div>
<nav class="mdl-navigation">
<div class="icon material-icons icon-margin">link</div>
<input type="text" class="mdl-textfield__input" id="ecmHost" data-automation-id="ecmHost"
tabindex="1" (change)="onChangeECMHost($event)" value="{{ecmHost}}"/>
</nav>
<div class="mdl-card__supporting-text">
BPM host URL configuration
</div>
<nav class="mdl-navigation">
<div class="icon material-icons icon-margin">link</div>
<input type="text" class="mdl-textfield__input" id="bpmHost" data-automation-id="bpmHost"
tabindex="1" (change)="onChangeBPMHost($event)" value="{{bpmHost}}"/>
</nav>
</div>
</div>
<div class="setting-card-padding"></div>
</div>
</div>

View File

@ -0,0 +1,55 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component } from '@angular/core';
import {
AlfrescoSettingsService
} from 'ng2-alfresco-core';
declare let __moduleName: string;
@Component({
moduleId: __moduleName,
selector: 'alfresco-setting-demo',
templateUrl: './setting.component.html',
styleUrls: ['./setting.component.css']
})
export class SettingComponent {
ecmHost: string;
bpmHost: string;
constructor(public alfrescoSettingsService: AlfrescoSettingsService) {
this.ecmHost = this.alfrescoSettingsService.ecmHost;
this.bpmHost = this.alfrescoSettingsService.bpmHost;
}
public onChangeECMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value);
this.ecmHost = (<HTMLInputElement>event.target).value;
this.alfrescoSettingsService.ecmHost = this.ecmHost;
localStorage.setItem(`ecmHost`, this.ecmHost);
}
public onChangeBPMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value);
this.bpmHost = (<HTMLInputElement>event.target).value;
this.alfrescoSettingsService.bpmHost = this.bpmHost;
localStorage.setItem(`bpmHost`, this.bpmHost);
}
}

View File

@ -0,0 +1,38 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component } from '@angular/core';
@Component({
selector: 'alfresco-tag-demo',
template: `
<label for="nodeId"><b>Insert Node Id</b></label><br>
<input id="nodeId" type="text" size="48" [(ngModel)]="nodeId"><br>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col"><alfresco-tag-node-actions-list [nodeId]="nodeId"></alfresco-tag-node-actions-list></div>
<div class="mdl-cell mdl-cell--4-col">List Tags ECM <alfresco-tag-list></alfresco-tag-list></div>
<div class="mdl-cell mdl-cell--4-col">
Tag list By Node ID
<alfresco-tag-node-list [nodeId]="nodeId"></alfresco-tag-node-list>
</div>
</div>
`
})
export class TagComponent {
nodeId: string = '74cd8a96-8a21-47e5-9b3b-a1b3e296787d';
}

View File

@ -16,29 +16,24 @@
*/
import { Component } from '@angular/core';
import {
CONTEXT_MENU_DIRECTIVES
} from 'ng2-alfresco-core';
import { WEBSCRIPTCOMPONENT } from 'ng2-alfresco-webscript';
@Component({
selector: 'alfresco-webscript-demo',
template: `
<label for="token"><b>Insert a scriptPath</b></label><br>
<input id="token" type="text" size="48" [(ngModel)]="scriptPath"><br>
<label for="token"><b>Insert a contextRoot</b></label><br>
<input id="token" type="text" size="48" [(ngModel)]="contextRoot"><br>
<label for="token"><b>Insert a servicePath</b></label><br>
<input id="token" type="text" size="48" [(ngModel)]="servicePath"><br>
<label for="script-path"><b>Insert a scriptPath</b></label><br>
<input id="script-path" type="text" size="48" [(ngModel)]="scriptPath"><br>
<label for="context-root"><b>Insert a contextRoot</b></label><br>
<input id="context-root" type="text" size="48" [(ngModel)]="contextRoot"><br>
<label for="service-path"><b>Insert a servicePath</b></label><br>
<input id="service-path" type="text" size="48" [(ngModel)]="servicePath"><br>
<alfresco-webscript-get [scriptPath]="scriptPath"
[scriptArgs]="scriptArgs"
[contextRoot]="contextRoot"
[servicePath]="servicePath"
[contentType]="'HTML'"
(onSuccess)= "logData($event)"></alfresco-webscript-get>
`,
directives: [WEBSCRIPTCOMPONENT, CONTEXT_MENU_DIRECTIVES]
`
})
export class WebscriptComponent {

View File

@ -35,3 +35,7 @@ body, html {
text-align: center;
position: relative;
}
._dialog_overlay {
position: static !important;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 B

View File

@ -1,782 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="110px"
height="110px" viewBox="0 0 110 110" style="enable-background:new 0 0 110 110;" xml:space="preserve">
<g id="Artboard" style="display:none;">
<rect x="-443" y="-79" style="display:inline;fill:#8C8C8C;" width="1412" height="743"/>
</g>
<g id="R-Multicolor" style="display:none;">
<circle style="display:inline;fill:#E04F5F;" cx="55" cy="55" r="55"/>
<g style="display:inline;">
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
<path style="fill:#F0F1F1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
<polygon style="fill:#F4D0A1;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
<polygon style="fill:#EDBC7C;" points="87,47 87,41.5 82,31 82,47 "/>
<path style="fill:#F79392;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
<path style="fill:#E2E4E5;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,44,62,44.45,62,45
L62,45z"/>
<path style="fill:#E2E4E5;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
<path style="fill:#E2E4E5;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,57,62,57.45,62,58
L62,58z"/>
<path style="fill:#E2E4E5;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
<path style="fill:#E2E4E5;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,70,62,70.45,62,71
L62,71z"/>
<path style="fill:#E2E4E5;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
<g>
<path style="fill:#17B198;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
<rect x="77" y="79" style="fill:#FACB1B;" width="10" height="3"/>
<rect x="77" y="80" style="fill:#FBE158;" width="5" height="2"/>
<path style="fill:#3E3E3F;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.422,1.857-1.101
L82.013,30.984z"/>
<path style="fill:#0484AB;" d="M84.5,43c-1.154,0-2.126-0.782-2.413-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.571C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.131-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
<path style="fill:#21B2D1;" d="M77,80V41.493c0-0.111,0.147-0.139,0.19-0.036C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.859C81.931,41.089,82,41.096,82,41.15V80H77z"/>
<path style="fill:#F7B4B4;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
<g id="Multicolor">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
<path style="fill:#F0F1F1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
<polygon style="fill:#F4D0A1;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
<polygon style="fill:#EDBC7C;" points="87,47 87,41.5 82,31 82,47 "/>
<path style="fill:#F79392;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
<path style="fill:#E2E4E5;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,44,62,44.45,62,45
L62,45z"/>
<path style="fill:#E2E4E5;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
<path style="fill:#E2E4E5;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,57,62,57.45,62,58
L62,58z"/>
<path style="fill:#E2E4E5;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
<path style="fill:#E2E4E5;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,70,62,70.45,62,71
L62,71z"/>
<path style="fill:#E2E4E5;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
<g>
<path style="fill:#17B198;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
<rect x="77" y="79" style="fill:#FACB1B;" width="10" height="3"/>
<rect x="77" y="80" style="fill:#FBE158;" width="5" height="2"/>
<path style="fill:#3E3E3F;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.422,1.857-1.101
L82.013,30.984z"/>
<path style="fill:#0484AB;" d="M84.5,43c-1.154,0-2.126-0.782-2.413-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.571C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.131-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
<path style="fill:#21B2D1;" d="M77,80V41.493c0-0.111,0.147-0.139,0.19-0.036C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.859C81.931,41.089,82,41.096,82,41.15V80H77z"/>
<path style="fill:#F7B4B4;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
<g id="Blue" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#E1F5FA;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#E1F5FA;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#0F6175;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#33899E;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0C333D;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#0081A1;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#009FC7;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="R-Blue" style="display:none;">
<g style="display:inline;">
<circle style="fill:#81D2EB;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#E1F5FA;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#E1F5FA;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#0F6175;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#33899E;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0C333D;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#0081A1;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#009FC7;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Green" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#D7FCF9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#D7FCF9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#14635B;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#3E948B;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0A3631;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#009687;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.625,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#00B8A5;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="R-Green" style="display:none;">
<g style="display:inline;">
<circle style="fill:#87E0C8;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72C71,85.281,69.28,87,67.16,87H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#D7FCF9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#D7FCF9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#14635B;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#3E948B;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0A3631;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101
c0.705,0,1.345-0.423,1.857-1.101L82.013,30.984z"/>
</g>
<g>
<path style="fill:#009687;" d="M84.5,43c-1.154,0-2.125-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.625,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.452,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#00B8A5;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.858C81.93,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Red" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M22.896,26.84c0-2.121,1.719-3.84,3.84-3.84h28.16l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84
h-40.32c-2.121,0-3.84-1.719-3.84-3.84V26.84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M54.896,23v9.6c0,2.121,1.719,3.84,3.84,3.84h12.16L54.896,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="76.896,41.5 81.896,31 86.896,41.5 86.896,80 76.896,80 "/>
</g>
<g>
<polygon style="fill:#FFEFED;" points="86.896,47 86.896,41.5 81.896,31 81.896,47 "/>
</g>
<g>
<path style="fill:#FFEFED;" d="M86.896,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,45c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,44,61.896,44.45,61.896,45L61.896,45z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,49,55.896,49.45,55.896,50L55.896,50z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,58c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,57,61.896,57.45,61.896,58L61.896,58z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,62,55.896,62.45,55.896,63L55.896,63z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,71c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,70,61.896,70.45,61.896,71L61.896,71z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,75,55.896,75.45,55.896,76L55.896,76z"/>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,78z"/>
</g>
</g>
<g>
<rect x="76.896" y="79" style="fill:#7D261E;" width="10" height="3"/>
</g>
<g>
<rect x="76.896" y="80" style="fill:#B84E44;" width="5" height="2"/>
</g>
<g>
<path style="fill:#4A1410;" d="M81.909,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L81.909,30.984z"/>
</g>
<g>
<path style="fill:#C23023;" d="M84.396,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.521,42.218,80.55,43,79.396,43c-1.052,0-1.953-0.65-2.322-1.57c-0.039-0.098-0.178-0.071-0.178,0.034V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.349,42.35,85.448,43,84.396,43z"/>
</g>
<g>
<path style="fill:#E54B44;" d="M76.896,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.461,42.363,78.354,43,79.396,43
c1.159,0,2.134-0.789,2.416-1.858c0.014-0.052,0.084-0.046,0.084,0.008V80H76.896z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M76.896,82v2c0,1.657,1.343,3,3,3h2v-5H76.896z"/>
</g>
</g>
</g>
<g id="R-Red" style="display:none;">
<g style="display:inline;">
<circle style="fill:#FABBAF;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#FFEFED;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#FFEFED;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#7D261E;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#B84E44;" width="5" height="2"/>
</g>
<g>
<path style="fill:#4A1410;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#C23023;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#E54B44;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Yellow" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M22.896,26.84c0-2.121,1.719-3.84,3.84-3.84h28.16l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84
h-40.32c-2.121,0-3.84-1.719-3.84-3.84V26.84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M54.896,23v9.6c0,2.121,1.719,3.84,3.84,3.84h12.16L54.896,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="76.896,41.5 81.896,31 86.896,41.5 86.896,80 76.896,80 "/>
</g>
<g>
<polygon style="fill:#FFF5D9;" points="86.896,47 86.896,41.5 81.896,31 81.896,47 "/>
</g>
<g>
<path style="fill:#FFF5D9;" d="M86.896,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,45c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,44,61.896,44.45,61.896,45L61.896,45z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,49,55.896,49.45,55.896,50L55.896,50z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,58c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,57,61.896,57.45,61.896,58L61.896,58z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,62,55.896,62.45,55.896,63L55.896,63z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,71c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,70,61.896,70.45,61.896,71L61.896,71z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,75,55.896,75.45,55.896,76L55.896,76z"/>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,78z"/>
</g>
</g>
<g>
<rect x="76.896" y="79" style="fill:#66510D;" width="10" height="3"/>
</g>
<g>
<rect x="76.896" y="80" style="fill:#947615;" width="5" height="2"/>
</g>
<g>
<path style="fill:#3D320C;" d="M81.909,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L81.909,30.984z"/>
</g>
<g>
<path style="fill:#E07000;" d="M84.396,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.521,42.218,80.55,43,79.396,43c-1.052,0-1.953-0.65-2.322-1.57c-0.039-0.098-0.178-0.071-0.178,0.034V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.349,42.35,85.448,43,84.396,43z"/>
</g>
<g>
<path style="fill:#FA9200;" d="M76.896,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.461,42.363,78.354,43,79.396,43
c1.159,0,2.134-0.789,2.416-1.858c0.014-0.052,0.084-0.046,0.084,0.008V80H76.896z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M76.896,82v2c0,1.657,1.343,3,3,3h2v-5H76.896z"/>
</g>
</g>
</g>
<g id="R-Yellow" style="display:none;">
<g style="display:inline;">
<circle style="fill:#FFD75E;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#FFF5D9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#FFF5D9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#66510D;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#947615;" width="5" height="2"/>
</g>
<g>
<path style="fill:#3D320C;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#E07000;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#FA9200;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 80 80" style="enable-background:new 0 0 80 80;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4B4B4B;}
</style>
<g id="XMLID_52_">
<path id="XMLID_1171_" class="st0" d="M46.5,78.9c-7.7,0-15.5,0-23.2,0c-5.5,0-9.2-3.7-9.2-9.2c0-15.5,0-31,0-46.5
c0-5.5,3.7-9.2,9.1-9.2c15.6,0,31.2,0,46.7,0c5.2,0,9,3.8,9,9c0,15.5,0,31.1,0,46.6c0,5.5-3.7,9.2-9.2,9.2
C62,78.9,54.2,78.9,46.5,78.9z M20.5,46.5c0,7.4,0,14.9,0,22.3c0,2.4,1.3,3.7,3.7,3.7c14.8,0,29.7,0,44.5,0c2.5,0,3.7-1.3,3.7-3.7
c0-14.8,0-29.7,0-44.5c0-2.5-1.3-3.7-3.8-3.7c-14.8,0-29.7,0-44.5,0c-2.4,0-3.7,1.3-3.7,3.7C20.5,31.6,20.5,39.1,20.5,46.5z"/>
<path id="XMLID_1170_" class="st0" d="M10.8,65.9c-5.6,0.4-9.7-3.5-9.7-8.9c0-15.6,0-31.3,0-46.9c0-5.2,3.8-8.9,9-8.9
c15.7,0,31.4,0,47,0c5.3,0,9.2,4.2,8.8,9.7c-0.4,0-0.8,0-1.2,0c-15.1,0-30.3,0-45.4,0c-5.1,0-8.5,3.5-8.5,8.5c0,15.1,0,30.2,0,45.3
C10.8,65,10.8,65.4,10.8,65.9z"/>
<path id="XMLID_899_" class="st0" d="M37.9,50.5c4.7-4.7,9.3-9.3,13.8-13.9c1.8-1.8,3.5-3.5,5.3-5.3c2.1-2.1,4.8-2,6.9,0.1
c2.3,2.3,2.5,5,0.4,7.1c-7.7,7.7-15.3,15.3-23,23c-2.1,2.1-4.8,2.1-6.8,0c-2.2-2.2-4.5-4.5-6.7-6.8c-1.9-2-1.9-4.6,0-6.6
c1.4-1.5,2.9-2.5,5-1.9c0.8,0.2,1.6,0.7,2.2,1.3C36.1,48.5,36.9,49.5,37.9,50.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,372 @@
/**
* Class to generate polyline
*
* @author Dmitry Farafonov
*/
var ANCHOR_TYPE= {
main: "main",
middle: "middle",
first: "first",
last: "last"
};
function Anchor(uuid, type, x, y) {
this.uuid = uuid;
this.x = x;
this.y = y;
this.type = (type == ANCHOR_TYPE.middle) ? ANCHOR_TYPE.middle : ANCHOR_TYPE.main;
};
Anchor.prototype = {
uuid: null,
x: 0,
y: 0,
type: ANCHOR_TYPE.main,
isFirst: false,
isLast: false,
ndex: 0,
typeIndex: 0
};
function Polyline(uuid, points, strokeWidth, paper) {
/* Array on coordinates:
* points: [{x: 410, y: 110}, 1
* {x: 570, y: 110}, 1 2
* {x: 620, y: 240}, 2 3
* {x: 750, y: 270}, 3 4
* {x: 650, y: 370}]; 4
*/
this.points = points;
/*
* path for graph
* [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
*/
this.path = [];
this.anchors = [];
if (strokeWidth) this.strokeWidth = strokeWidth;
this.paper = paper;
this.closePath = false;
this.init();
};
Polyline.prototype = {
id: null,
points: [],
path: [],
anchors: [],
strokeWidth: 1,
radius: 1,
showDetails: false,
paper: null,
element: null,
isDefaultConditionAvailable: false,
closePath: false,
init: function(points){
var linesCount = this.getLinesCount();
if (linesCount < 1)
return;
this.normalizeCoordinates();
// create anchors
this.pushAnchor(ANCHOR_TYPE.first, this.getLine(0).x1, this.getLine(0).y1);
for (var i = 1; i < linesCount; i++)
{
var line1 = this.getLine(i-1);
this.pushAnchor(ANCHOR_TYPE.main, line1.x2, line1.y2);
}
this.pushAnchor(ANCHOR_TYPE.last, this.getLine(linesCount-1).x2, this.getLine(linesCount-1).y2);
this.rebuildPath();
},
normalizeCoordinates: function(){
for(var i=0; i < this.points.length; i++){
this.points[i].x = parseFloat(this.points[i].x);
this.points[i].y = parseFloat(this.points[i].y);
}
},
getLinesCount: function(){
return this.points.length-1;
},
_getLine: function(i){
if (this.points.length > i && this.points[i]) {
return {x1: this.points[i].x, y1: this.points[i].y, x2: this.points[i+1].x, y2: this.points[i+1].y};
} else {
return undefined;
}
},
getLine: function(i){
var line = this._getLine(i);
if (line != undefined) {
line.angle = this.getLineAngle(i);
}
return line;
},
getLineAngle: function(i){
var line = this._getLine(i);
return Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
},
getLineLengthX: function(i){
var line = this.getLine(i);
return (line.x2 - line.x1);
},
getLineLengthY: function(i){
var line = this.getLine(i);
return (line.y2 - line.y1);
},
getLineLength: function(i){
return Math.sqrt(Math.pow(this.getLineLengthX(i), 2) + Math.pow(this.getLineLengthY(i), 2));
},
getAnchors: function(){
return this.anchors;
},
getAnchorsCount: function(type){
if (!type)
return this.anchors.length;
else {
var count = 0;
for(var i=0; i < this.getAnchorsCount(); i++){
var anchor = this.anchors[i];
if (anchor.getType() == type) {
count++;
}
}
return count;
}
},
pushAnchor: function(type, x, y, index){
if (type == ANCHOR_TYPE.first) {
index = 0;
typeIndex = 0;
} else if (type == ANCHOR_TYPE.last) {
index = this.getAnchorsCount();
typeIndex = 0;
} else if (!index) {
index = this.anchors.length;
} else {
for(var i=0; i < this.getAnchorsCount(); i++){
var anchor = this.anchors[i];
if (anchor.index > index) {
anchor.index++;
anchor.typeIndex++;
}
}
}
var anchor = new Anchor(this.id, ANCHOR_TYPE.main, x, y, index, typeIndex);
this.anchors.push(anchor);
},
getAnchor: function(position){
return this.anchors[position];
},
getAnchorByType: function(type, position){
if (type == ANCHOR_TYPE.first)
return this.anchors[0];
if (type == ANCHOR_TYPE.last)
return this.anchors[this.getAnchorsCount()-1];
for(var i=0; i < this.getAnchorsCount(); i++){
var anchor = this.anchors[i];
if (anchor.type == type) {
if( position == anchor.position)
return anchor;
}
}
return null;
},
addNewPoint: function(position, x, y){
//
for(var i = 0; i < this.getLinesCount(); i++){
var line = this.getLine(i);
if (x > line.x1 && x < line.x2 && y > line.y1 && y < line.y2) {
this.points.splice(i+1,0,{x: x, y: y});
break;
}
}
this.rebuildPath();
},
rebuildPath: function(){
var path = [];
for(var i = 0; i < this.getAnchorsCount(); i++){
var anchor = this.getAnchor(i);
var pathType = "";
if (i == 0)
pathType = "M";
else
pathType = "L";
// TODO: save previous points and calculate new path just if points are updated, and then save currents values as previous
var targetX = anchor.x, targetY = anchor.y;
if (i>0 && i < this.getAnchorsCount()-1) {
// get new x,y
var cx = anchor.x, cy = anchor.y;
// pivot point of prev line
var AO = this.getLineLength(i-1);
if (AO < this.radius) {
AO = this.radius;
}
this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
var ED = this.getLineLengthY(i-1) * this.radius / AO;
var OD = this.getLineLengthX(i-1) * this.radius / AO;
targetX = anchor.x - OD;
targetY = anchor.y - ED;
if (AO < 2*this.radius && i>1) {
targetX = anchor.x - this.getLineLengthX(i-1)/2;
targetY = anchor.y - this.getLineLengthY(i-1)/2;;
}
// pivot point of next line
var AO = this.getLineLength(i);
if (AO < this.radius) {
AO = this.radius;
}
var ED = this.getLineLengthY(i) * this.radius / AO;
var OD = this.getLineLengthX(i) * this.radius / AO;
var nextSrcX = anchor.x + OD;
var nextSrcY = anchor.y + ED;
if (AO < 2*this.radius && i<this.getAnchorsCount()-2) {
nextSrcX = anchor.x + this.getLineLengthX(i)/2;
nextSrcY = anchor.y + this.getLineLengthY(i)/2;;
}
var dx0 = (cx - targetX) / 3,
dy0 = (cy - targetY) / 3,
ax = cx - dx0,
ay = cy - dy0,
dx1 = (cx - nextSrcX) / 3,
dy1 = (cy - nextSrcY) / 3,
bx = cx - dx1,
by = cy - dy1,
zx=nextSrcX, zy=nextSrcY;
} else if (i==1 && this.getAnchorsCount() == 2){
var AO = this.getLineLength(i-1);
if (AO < this.radius) {
AO = this.radius;
}
this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
}
// anti smoothing
if (this.strokeWidth%2 == 1) {
targetX += 0.5;
targetY += 0.5;
}
path.push([pathType, targetX, targetY]);
if (i>0 && i < this.getAnchorsCount()-1) {
path.push(["C", ax, ay, bx, by, zx, zy]);
}
}
if (this.closePath)
{
path.push(["Z"]);
}
this.path = path;
},
transform: function(transformation)
{
this.element.transform(transformation);
},
attr: function(attrs)
{
// TODO: foreach and set each
this.element.attr(attrs);
}
};
function Polygone(points, strokeWidth) {
/* Array on coordinates:
* points: [{x: 410, y: 110}, 1
* {x: 570, y: 110}, 1 2
* {x: 620, y: 240}, 2 3
* {x: 750, y: 270}, 3 4
* {x: 650, y: 370}]; 4
*/
this.points = points;
/*
* path for graph
* [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
*/
this.path = [];
this.anchors = [];
if (strokeWidth) this.strokeWidth = strokeWidth;
this.closePath = true;
this.init();
};
/*
* Poligone is inherited from Poliline: draws closedPath of polyline
*/
var Foo = function () { };
Foo.prototype = Polyline.prototype;
Polygone.prototype = new Foo();
Polygone.prototype.rebuildPath = function(){
var path = [];
for(var i = 0; i < this.getAnchorsCount(); i++){
var anchor = this.getAnchor(i);
var pathType = "";
if (i == 0)
pathType = "M";
else
pathType = "L";
var targetX = anchor.x, targetY = anchor.y;
// anti smoothing
if (this.strokeWidth%2 == 1) {
targetX += 0.5;
targetY += 0.5;
}
path.push([pathType, targetX, targetY]);
}
if (this.closePath)
path.push(["Z"]);
this.path = path;
};

View File

@ -1,178 +0,0 @@
/* This work is licensed under Creative Commons GNU LGPL License.
License: http://creativecommons.org/licenses/LGPL/2.1/
Version: 0.9
Author: Stefan Goessner/2006
Web: http://goessner.net/
*/
var parseXml;
if (typeof window.DOMParser != "undefined") {
parseXml = function(xmlStr) {
return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml");
};
} else if (typeof window.ActiveXObject != "undefined" &&
new window.ActiveXObject("Microsoft.XMLDOM")) {
parseXml = function(xmlStr) {
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
} else {
throw new Error("No XML parser found");
}
function xml2json(xml, tab) {
var X = {
toObj: function(xml) {
var o = {};
if (xml.nodeType==1) { // element node ..
if (xml.attributes.length) // element with attributes ..
for (var i=0; i<xml.attributes.length; i++)
o["@"+xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue||"").toString();
if (xml.firstChild) { // element has child nodes ..
var textChild=0, cdataChild=0, hasElementChild=false;
for (var n=xml.firstChild; n; n=n.nextSibling) {
if (n.nodeType==1) hasElementChild = true;
else if (n.nodeType==3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
else if (n.nodeType==4) cdataChild++; // cdata section node
}
if (hasElementChild) {
if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
X.removeWhite(xml);
for (var n=xml.firstChild; n; n=n.nextSibling) {
if (n.nodeType == 3) // text node
o["#text"] = X.escape(n.nodeValue);
else if (n.nodeType == 4) // cdata node
o["#cdata"] = X.escape(n.nodeValue);
else if (o[n.nodeName]) { // multiple occurence of element ..
if (o[n.nodeName] instanceof Array)
o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
else
o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
}
else // first occurence of element..
o[n.nodeName] = X.toObj(n);
}
}
else { // mixed content
if (!xml.attributes.length)
o = X.escape(X.innerXml(xml));
else
o["#text"] = X.escape(X.innerXml(xml));
}
}
else if (textChild) { // pure text
if (!xml.attributes.length)
o = X.escape(X.innerXml(xml));
else
o["#text"] = X.escape(X.innerXml(xml));
}
else if (cdataChild) { // cdata
if (cdataChild > 1)
o = X.escape(X.innerXml(xml));
else
for (var n=xml.firstChild; n; n=n.nextSibling)
o["#cdata"] = X.escape(n.nodeValue);
}
}
if (!xml.attributes.length && !xml.firstChild) o = null;
}
else if (xml.nodeType==9) { // document.node
o = X.toObj(xml.documentElement);
}
else
alert("unhandled node type: " + xml.nodeType);
return o;
},
toJson: function(o, name, ind) {
var json = name ? ("\""+name+"\"") : "";
if (o instanceof Array) {
for (var i=0,n=o.length; i<n; i++)
o[i] = X.toJson(o[i], "", ind+"\t");
json += (name?":[":"[") + (o.length > 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : o.join("")) + "]";
}
else if (o == null)
json += (name&&":") + "null";
else if (typeof(o) == "object") {
var arr = [];
for (var m in o)
arr[arr.length] = X.toJson(o[m], m, ind+"\t");
json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : arr.join("")) + "}";
}
else if (typeof(o) == "string")
json += (name&&":") + "\"" + o.toString() + "\"";
else
json += (name&&":") + o.toString();
return json;
},
innerXml: function(node) {
var s = ""
if ("innerHTML" in node)
s = node.innerHTML;
else {
var asXml = function(n) {
var s = "";
if (n.nodeType == 1) {
s += "<" + n.nodeName;
for (var i=0; i<n.attributes.length;i++)
s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue||"").toString() + "\"";
if (n.firstChild) {
s += ">";
for (var c=n.firstChild; c; c=c.nextSibling)
s += asXml(c);
s += "</"+n.nodeName+">";
}
else
s += "/>";
}
else if (n.nodeType == 3)
s += n.nodeValue;
else if (n.nodeType == 4)
s += "<![CDATA[" + n.nodeValue + "]]>";
return s;
};
for (var c=node.firstChild; c; c=c.nextSibling)
s += asXml(c);
}
return s;
},
escape: function(txt) {
return txt.replace(/[\\]/g, "\\\\")
.replace(/[\"]/g, '\\"')
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r');
},
removeWhite: function(e) {
e.normalize();
for (var n = e.firstChild; n; ) {
if (n.nodeType == 3) { // text node
if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
var nxt = n.nextSibling;
e.removeChild(n);
n = nxt;
}
else
n = n.nextSibling;
}
else if (n.nodeType == 1) { // element node
X.removeWhite(n);
n = n.nextSibling;
}
else // any other node
n = n.nextSibling;
}
return e;
}
};
xml = parseXml(xml);
if (xml.nodeType == 9) // document node
xml = xml.documentElement;
var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
}

View File

@ -15,21 +15,8 @@
* limitations under the License.
*/
import { bootstrap } from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';
import { ALFRESCO_SEARCH_PROVIDERS } from 'ng2-alfresco-search';
import { ALFRESCO_CORE_PROVIDERS } from 'ng2-alfresco-core';
import { ATIVITI_FORM_PROVIDERS } from 'ng2-activiti-form';
import { UploadService } from 'ng2-alfresco-upload';
import { AppComponent } from './app.component';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { appRouterProviders } from './app.routes';
bootstrap(AppComponent, [
appRouterProviders,
HTTP_PROVIDERS,
ALFRESCO_CORE_PROVIDERS,
ALFRESCO_SEARCH_PROVIDERS,
UploadService,
ATIVITI_FORM_PROVIDERS
]).catch(err => console.error(err));
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

View File

@ -4,7 +4,7 @@
"DOCUMENT_LIST": {
"COLUMNS": {
"DISPLAY_NAME": "Display name",
"CREATED_BY": "mario",
"CREATED_BY": "Created by",
"CREATED_ON": "Created on"
},
"ACTIONS": {
@ -57,7 +57,7 @@
"LOGIN-SUCCESS": "Login successful"
},
"BUTTON": {
"LOGIN": "MARIO"
"LOGIN": "Login"
},
"ACTION": {
"HELP": "NEED HELP?",

View File

@ -9,26 +9,44 @@
<link href="app/css/muli-font.css" rel="stylesheet">
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96">
<!-- Google Material Design Lite -->
<link rel="stylesheet" href="./assets/material.orange-blue.min.css">
<link href="./assets/material.orange-blue.min.css" rel="stylesheet">
<script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<link href="node_modules/material-design-icons/iconfont/material-icons.css" rel="stylesheet">
<link href="node_modules/flag-icon-css/css/flag-icon.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en" rel="stylesheet" type="text/css">
<link href="node_modules/md-date-time-picker/dist/css/mdDateTimePicker.css" media="all" rel="stylesheet">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
<!-- Polyfill(s) for dialogs -->
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
<link href="node_modules/dialog-polyfill/dialog-polyfill.css" rel="stylesheet" type="text/css" />
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="/app/js/xml2json.js"></script>
<script src="node_modules/alfresco-js-api/dist/alfresco-js-api.js"></script>
<script src="node_modules/moment/min/moment.min.js"></script>
<script src="/app/js/Polyline.js"></script>
<script src="node_modules/pdfjs-dist/web/compatibility.js"></script>
<script src="node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="node_modules/pdfjs-dist/build/pdf.worker.js"></script>
<script src="node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
<script src="node_modules/chart.js/dist/Chart.bundle.min.js"></script>
<script src="node_modules/raphael/raphael.min.js"></script>
<script src="node_modules/md-date-time-picker/dist/js/mdDateTimePicker.min.js"></script>
<script src="node_modules/md-date-time-picker/dist/js/draggabilly.pkgd.min.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>

View File

@ -1,18 +1,18 @@
{
"name": "Alfresco-Angular2-Demo",
"description": "Demo shell for Alfresco Angular2 components",
"version": "0.3.0",
"version": "0.4.0",
"author": "Alfresco Software, Ltd.",
"scripts": {
"postinstall": "typings install",
"clean": "rimraf node_modules typings",
"clean": "npm install rimraf && rimraf dist node_modules typings",
"build": "npm run tslint && npm run tsc && npm run licensecheck",
"start": "npm run build && concurrently \"npm run tsc:w\" \"npm run serve\" ",
"start": "npm run build && npm run serve",
"start:dev": "npm run build && concurrently \"npm run tsc:w\" \"npm run serve:dev\" ",
"aws": "node app.js",
"tsc": "tsc",
"tsc:w": "tsc -w",
"serve": "wsrv -o -s -l -p 3000 -a 0.0.0.0 -x ./server/versions.js",
"typings": "typings install",
"serve": "wsrv -O http://localhost:3000 -s -p 3000 -a 0.0.0.0 -x ./server/versions.js",
"serve:dev": "wsrv -O http://localhost:3000 -s -l -p 3000 -a 0.0.0.0 -x ./server/versions.js",
"tslint": "tslint -c tslint.json *.ts && tslint -c tslint.json 'app/{,**/}**.ts'",
"licensecheck": "license-check"
},
@ -53,53 +53,66 @@
"alfresco"
],
"dependencies": {
"@angular/common": "2.0.0-rc.3",
"@angular/compiler": "2.0.0-rc.3",
"@angular/core": "2.0.0-rc.3",
"@angular/forms": "0.1.1",
"@angular/http": "2.0.0-rc.3",
"@angular/platform-browser": "2.0.0-rc.3",
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
"@angular/router": "3.0.0-alpha.7",
"@angular/router-deprecated": "2.0.0-rc.2",
"@angular/upgrade": "2.0.0-rc.3",
"alfresco-js-api": "^0.3.0",
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"@angular/upgrade": "2.0.0",
"@types/node": "^6.0.42",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"core-js": "2.4.0",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "0.6.12",
"zone.js": "^0.6.23",
"rimraf": "2.5.2",
"material-design-icons": "2.2.3",
"material-design-lite": "1.1.3",
"ng2-translate": "2.2.0",
"pdfjs-dist": "1.5.258",
"material-design-lite": "1.2.1",
"ng2-translate": "2.5.0",
"pdfjs-dist": "1.5.404",
"flag-icon-css": "2.3.0",
"ng2-alfresco-core": "0.3.0",
"ng2-alfresco-datatable": "0.3.0",
"ng2-alfresco-documentlist": "0.3.0",
"ng2-alfresco-login": "0.3.0",
"ng2-alfresco-search": "0.3.0",
"ng2-alfresco-upload": "0.3.0",
"ng2-alfresco-viewer": "0.3.0",
"ng2-activiti-form": "0.3.0",
"ng2-activiti-tasklist": "0.3.0",
"ng2-activiti-processlist": "0.3.0",
"ng2-alfresco-webscript": "0.3.0"
"intl": "1.2.4",
"moment": "2.15.1",
"chart.js": "^2.1.4",
"ng2-charts": "1.1.0",
"raphael": "^2.2.6",
"md-date-time-picker": "^2.2.0",
"alfresco-js-api": "^0.4.0",
"ng2-activiti-analytics": "0.4.0",
"ng2-alfresco-core": "0.4.0",
"ng2-alfresco-datatable": "0.4.0",
"ng2-alfresco-documentlist": "0.4.0",
"ng2-alfresco-login": "0.4.0",
"ng2-alfresco-search": "0.4.0",
"ng2-alfresco-upload": "0.4.0",
"ng2-alfresco-viewer": "0.4.0",
"ng2-activiti-form": "0.4.0",
"ng2-activiti-tasklist": "0.4.0",
"ng2-alfresco-userinfo": "0.4.0",
"ng2-activiti-processlist": "0.4.0",
"ng2-alfresco-webscript": "0.4.0",
"ng2-alfresco-tag": "0.4.0",
"dialog-polyfill": "^0.4.3",
"element.scrollintoviewifneeded-polyfill": "^1.0.1"
},
"devDependencies": {
"concurrently": "2.0.0",
"@types/core-js": "^0.9.32",
"@types/jasmine": "^2.2.33",
"concurrently": "^2.2.0",
"license-check": "1.1.5",
"mime": "^1.3.4",
"tslint": "3.8.1",
"typescript": "1.8.10",
"typings": "1.0.4",
"wsrv": "0.1.4"
"tslint": "3.15.1",
"typescript": "^2.0.3",
"wsrv": "^0.1.5"
},
"license-check-config": {
"src": [
"./app/**/*.js",
"!./app/js/xml2json.js"
"!./app/js/*.js"
],
"path": "assets/license_header.txt",
"blocking": true,

View File

@ -3,77 +3,74 @@
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
var map = {
'app': 'app', // 'dist',
'@angular': 'node_modules/@angular',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'rxjs': 'node_modules/rxjs',
'ng2-translate': 'node_modules/ng2-translate',
'ng2-alfresco-core': 'node_modules/ng2-alfresco-core/dist',
'ng2-alfresco-datatable': 'node_modules/ng2-alfresco-datatable/dist',
'ng2-alfresco-documentlist': 'node_modules/ng2-alfresco-documentlist/dist',
'ng2-alfresco-login': 'node_modules/ng2-alfresco-login/dist',
'ng2-alfresco-search': 'node_modules/ng2-alfresco-search/dist',
'ng2-alfresco-upload': 'node_modules/ng2-alfresco-upload/dist',
'ng2-activiti-form': 'node_modules/ng2-activiti-form/dist',
'ng2-alfresco-viewer': 'node_modules/ng2-alfresco-viewer/dist',
'ng2-alfresco-webscript': 'node_modules/ng2-alfresco-webscript/dist',
'ng2-activiti-processlist': 'node_modules/ng2-activiti-processlist/dist',
'ng2-activiti-tasklist': 'node_modules/ng2-activiti-tasklist/dist'
};
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'moment': 'npm:moment/min/moment.min.js',
'ng2-charts' : 'npm:ng2-charts',
'ng2-translate': 'npm:ng2-translate',
'ng2-alfresco-core': 'npm:ng2-alfresco-core/dist',
'ng2-alfresco-datatable': 'npm:ng2-alfresco-datatable/dist',
'ng2-alfresco-documentlist': 'npm:ng2-alfresco-documentlist/dist',
'ng2-alfresco-login': 'npm:ng2-alfresco-login/dist',
'ng2-alfresco-search': 'npm:ng2-alfresco-search/dist',
'ng2-alfresco-upload': 'npm:ng2-alfresco-upload/dist',
'ng2-activiti-form': 'npm:ng2-activiti-form/dist',
'ng2-alfresco-viewer': 'npm:ng2-alfresco-viewer/dist',
'ng2-alfresco-webscript': 'npm:ng2-alfresco-webscript/dist',
'ng2-alfresco-tag': 'npm:ng2-alfresco-tag/dist',
'ng2-activiti-tasklist': 'npm:ng2-activiti-tasklist/dist',
'alfresco-js-api': 'npm:alfresco-js-api/dist',
'ng2-activiti-processlist': 'npm:ng2-activiti-processlist/dist',
'ng2-alfresco-userinfo': 'npm:ng2-alfresco-userinfo/dist',
'ng2-activiti-analytics': 'npm:ng2-activiti-analytics/dist',
'ng2-activiti-diagrams': 'npm:ng2-activiti-diagrams/dist'
},
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
'ng2-translate': { defaultExtension: 'js' },
'ng2-charts': { defaultExtension: 'js' },
'ng2-alfresco-core': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-datatable': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-documentlist': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-login': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-search': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-upload': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-viewer': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-form': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-processlist': { main: 'index.js', defaultExtension: 'js'},
'ng2-activiti-tasklist': { main: 'index.js', defaultExtension: 'js'},
'ng2-alfresco-webscript': { main: 'index.js', defaultExtension: 'js'}
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'upgrade'
];
// Individual files (~300 requests):
function packIndex(pkgName) {
packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
'ng2-alfresco-core': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-datatable': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-documentlist': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-login': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-search': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-upload': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-viewer': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-form': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-processlist': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-tasklist': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-webscript': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-tag': { main: './index.js', defaultExtension: 'js'},
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
'ng2-alfresco-userinfo': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-analytics': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-diagrams': { main: './index.js', defaultExtension: 'js'}
}
// Bundled (~40 requests):
function packUmd(pkgName) {
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
}
// Most environments should use UMD; some (Karma) need the individual index files
var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
// Add package entries for angular packages
ngPackageNames.forEach(setPackageConfig);
// No umd for router yet
packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
var config = {
map: map,
packages: packages
};
System.config(config);
});
})(this);

View File

@ -7,7 +7,8 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
"noImplicitAny": false,
"types": ["core-js", "jasmine"]
},
"exclude": [
"dist",

View File

@ -10,8 +10,7 @@
"class-name": true,
"comment-format": [
true,
"check-space",
"check-lowercase"
"check-space"
],
"curly": true,
"eofline": true,

View File

@ -1,7 +0,0 @@
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160317120654",
"jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
"node": "registry:dt/node#4.0.0+20160509154515"
}
}

View File

@ -8,6 +8,7 @@
"node_modules/ng2-alfresco-upload/dist/**/*.{html,css,js}",
"node_modules/ng2-alfresco-viewer/dist/**/*.{html,css,js}",
"node_modules/ng2-alfresco-webscript/dist/**/*.{html,css,js}",
"node_modules/ng2-activiti-form/dist/**/*.{html,css,js}"
"node_modules/ng2-activiti-form/dist/**/*.{html,css,js}",
"node_modules/ng2-activiti-tasklist/dist/**/*.{html,css,js}"
]
}

View File

@ -43,13 +43,49 @@
- [Login](ng2-alfresco-login/README.md)
- [Upload](ng2-alfresco-upload/README.md)
- [Webscript Viewer](ng2-alfresco-webscript/README.md)
- [Tag list and controls](ng2-alfresco-tag/README.md)
## BPM components
- [Activiti Forms](ng2-activiti-form/README.md)
- [Activiti Process](ng2-activiti-processlist/README.md)
- [Activiti Tasks](ng2-activiti-tasklist/README.md)
- [TaskList](ng2-activiti-tasklist/README.md)
- [ProcessList](ng2-activiti-processlist/README.md)
- [Form](ng2-activiti-form/README.md)
You can browse all the components at the following address:
http://devproducts.alfresco.com/
## How to test a change to a generic component in its own demo
Let's suppose that for some reason you have changed a component and you want to test this changes.
The example is based on the ng2-alfresco-login component, but you can use the same steps for any component.
1. Move inside the component folder and link it.
```sh
cd ng2-alfresco-login
npm link
```
2. Build the component with the watcher enabled.
```sh
npm run build:w
```
3. From another terminal move inside the demo sub folder and link the component to the local node_modules folder.
```sh
cd demo
npm link ng2-alfresco-login
```
4. Start the demo project.
```sh
npm run start
```

View File

@ -0,0 +1,23 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[package.json]
indent_style = space
indent_size = 2
[karma.conf.js]
indent_style = space
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

View File

@ -0,0 +1,15 @@
npm-debug.log
node_modules
.idea
typings
coverage
dist
src/**/*.js
src/**/*.js.map
demo/**/*.js
demo/**/*.js.map
demo/**/*.d.ts
index.js
index.js.map
!systemjs.config.js

View File

@ -0,0 +1,15 @@
npm-debug.log
.idea
coverage/
node_modules
typings/
fonts/
/.editorconfig
/.travis.yml
/*.js
/*.json
/*.ts
/*.js.map
/.npmignore

View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,317 @@
# Activiti Analytics Component for Angular 2
<p>
<a title='Build Status Travis' href="https://travis-ci.org/Alfresco/alfresco-ng2-components">
<img src='https://travis-ci.org/Alfresco/alfresco-ng2-components.svg?branch=master' alt='travis
Status' />
</a>
<a title='Build Status AppVeyor' href="https://ci.appveyor.com/project/alfresco/alfresco-ng2-components">
<img src='https://ci.appveyor.com/api/projects/status/github/Alfresco/alfresco-ng2-components' alt='travis
Status' />
</a>
<a href='https://codecov.io/gh/Alfresco/alfresco-ng2-components'>
<img src='https://img.shields.io/codecov/c/github/Alfresco/alfresco-ng2-components/master.svg?maxAge=2592000' alt='Coverage Status' />
</a>
<a href='https://www.npmjs.com/package/ng2-activiti-analytics'>
<img src='https://img.shields.io/npm/dt/ng2-activiti-analytics.svg' alt='npm downloads' />
</a>
<a href='https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE'>
<img src='https://img.shields.io/hexpm/l/plug.svg' alt='license' />
</a>
<a href='https://www.alfresco.com/'>
<img src='https://img.shields.io/badge/style-component-green.svg?label=alfresco' alt='alfresco component' />
</a>
<a href='https://angular.io/'>
<img src='https://img.shields.io/badge/style-2-red.svg?label=angular' alt='angular 2' />
</a>
<a href='https://www.typescriptlang.org/docs/tutorial.html'>
<img src='https://img.shields.io/badge/style-lang-blue.svg?label=typescript' alt='typescript' />
</a>
<a href='https://www.alfresco.com/'>
<img src='https://img.shields.io/badge/style-%3E5.0.0-blue.svg?label=node%20version' alt='node version' />
</a>
</p>
## Prerequisites
Before you start using this development framework, make sure you have installed all required software and done all the
necessary configuration, see this [page](https://github.com/Alfresco/alfresco-ng2-components/blob/master/PREREQUISITES.md).
## Install
Follow the 3 steps below:
1. Npm
```sh
npm install ng2-activiti-analytics --save
```
2. Html
Include these dependencies in your index.html page:
```html
<!-- Charts -->
<script src="node_modules/chart.js/dist/Chart.bundle.min.js"></script>
<script src="node_modules/raphael/raphael.min.js"></script>
<!-- Moment js -->
<script src="node_modules/moment/min/moment.min.js"></script>
<!-- Date picker -->
<script src="node_modules/md-date-time-picker/dist/js/mdDateTimePicker.min.js"></script>
<script src="node_modules/md-date-time-picker/dist/js/draggabilly.pkgd.min.js"></script>
<link rel="stylesheet" href="node_modules/md-date-time-picker/dist/css/mdDateTimePicker.css" media="all">
<!-- Google Material Design Lite -->
<link rel="stylesheet" href="node_modules/material-design-lite/material.min.css">
<script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
<!-- Polyfill(s) for dialogs -->
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
<style>._dialog_overlay { position: static !important; } </style>
<!-- Modules -->
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
```
3. SystemJs
Add the following components to your systemjs.config.js file:
- moment
- ng2-charts
- ng2-translate
- alfresco-js-api
- ng2-alfresco-core
- ng2-activiti-diagrams
- ng2-activiti-analytics
Please refer to the following example file: [systemjs.config.js](demo/systemjs
.config.js) .
## Basic usage example Activiti Analytics List
The component shows the list of all the available reports
```html
<analytics-report-list></analytics-report-list>
```
Usage example of this component :
**main.ts**
```ts
import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AnalyticsModule } from 'ng2-activiti-analytics';
@Component({
selector: 'activiti-analytics-demo',
template: `
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp">
<analytics-report-list></analytics-report-list>
</div>
</div>
</div>`
})
export class AnalyticsDemoComponent {
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
settingsService.bpmHost = 'http://localhost:9999';
this.authService.login('admin', 'admin').subscribe(
ticket => {
console.log(ticket);
},
error => {
console.log(error);
});
}
}
@NgModule({
imports: [
BrowserModule,
CoreModule.forRoot(),
AnalyticsModule
],
declarations: [ AnalyticsDemoComponent ],
bootstrap: [ AnalyticsDemoComponent ]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
```
#### Events
| Name | Description |
| --- | --- |
|`onSuccess`| The event is emitted when the report list are loaded |
|`onError`| The event is emitted when an error occur during the loading |
|`reportClick`| The event is emitted when the report in the list is selected |
#### Options
No options.
## Basic usage example Activiti Analytics
The component shows the charts related to the reportId passed as input
```html
<activiti-analytics [appId]="appId" [reportId]="reportId"></activiti-analytics>
```
Example of an App that use Activiti Analytics component :
**main.ts**
```ts
import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AnalyticsModule } from 'ng2-activiti-analytics';
@Component({
selector: 'activiti-analytics-demo',
template: `
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp">
<activiti-analytics [appId]="1001" [reportId]="2006"></activiti-analytics>
</div>
</div>
</div>`
})
export class AnalyticsDemoComponent {
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
settingsService.bpmHost = 'http://localhost:9999';
this.authService.login('admin', 'admin').subscribe(
ticket => {
console.log(ticket);
},
error => {
console.log(error);
});
}
}
@NgModule({
imports: [
BrowserModule,
CoreModule.forRoot(),
AnalyticsModule
],
declarations: [ AnalyticsDemoComponent ],
bootstrap: [ AnalyticsDemoComponent ]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
```
#### Events
| Name | Description |
| --- | --- |
|`onSuccess` | The event is emitted when the report parameters are loaded |
|`onError` | The event is emitted when an error occur during the loading |
#### Options
| Name | Description |
| --- | --- |
|`appId` | The application id |
|`reportId` | The report id |
|`debug` | Flag to enable or disable the Form values in the console log |
## Build from sources
Alternatively you can build component from sources with the following commands:
```sh
npm install
npm run build
```
### Build the files and keep watching for changes
```sh
$ npm run build:w
```
## Running unit tests
```sh
npm test
```
### Running unit tests in browser
```sh
npm test-browser
```
This task rebuilds all the code, runs tslint, license checks and other quality check tools
before performing unit testing.
### Code coverage
```sh
npm run coverage
```
## Demo
If you want have a demo of how the component works, please check the demo folder :
```sh
cd demo
npm install
npm start
```
## NPM scripts
| Command | Description |
| --- | --- |
| npm run build | Build component |
| npm run build:w | Build component and keep watching the changes |
| npm run test | Run unit tests in the console |
| npm run test-browser | Run unit tests in the browser
| npm run coverage | Run unit tests and display code coverage report |
## License
[Apache Version 2.0](https://github.com/Alfresco/alfresco-ng2-components/blob/master/LICENSE)

View File

@ -0,0 +1,16 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/

View File

@ -0,0 +1,6 @@
node_modules
.idea
coverage
dist
typings
!systemjs.config.js

View File

@ -0,0 +1,3 @@
node_modules
dist
typings

View File

@ -0,0 +1,13 @@
# Activiti Analytics demo
Install:
```
npm install
```
Run the project:
```
npm start
```

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alfresco Angular 2 Activiti Analytics - Demo</title>
<base href="./">
<!-- Charts -->
<script src="node_modules/chart.js/dist/Chart.bundle.min.js"></script>
<script src="node_modules/raphael/raphael.min.js"></script>
<!-- Moment js -->
<script src="node_modules/moment/min/moment.min.js"></script>
<!-- Date picker -->
<script src="node_modules/md-date-time-picker/dist/js/mdDateTimePicker.min.js"></script>
<script src="node_modules/md-date-time-picker/dist/js/draggabilly.pkgd.min.js"></script>
<link rel="stylesheet" href="node_modules/md-date-time-picker/dist/css/mdDateTimePicker.css" media="all">
<!-- Google Material Design Lite -->
<link rel="stylesheet" href="node_modules/material-design-lite/material.min.css">
<script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"></script>
<script src="node_modules/element.scrollintoviewifneeded-polyfill/index.js"></script>
<!-- Polyfill(s) for dialogs -->
<script src="node_modules/dialog-polyfill/dialog-polyfill.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/dialog-polyfill/dialog-polyfill.css" />
<style>._dialog_overlay { position: static !important; } </style>
<!-- Modules -->
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<style> .chart {display: block; width: 100%;}</style>
<body>
<alfresco-app-demo></alfresco-app-demo>
</body>
</html>

View File

@ -0,0 +1,74 @@
{
"name": "ng2-activiti-diagrams-demo",
"description": "Alfresco Angular2 Diagrams Component - Demo",
"version": "0.1.0",
"author": "Alfresco Software, Ltd.",
"main": "index.js",
"scripts": {
"clean": "npm install rimraf && rimraf dist node_modules typings dist",
"postinstall": "npm run build",
"start": "npm run build && concurrently \"npm run tsc:w\" \"npm run server\" ",
"server": "wsrv -o -s -l",
"build": "npm run tslint && rimraf dist && tsc",
"build:w": "npm run tslint && rimraf dist && tsc -w",
"tsc": "tsc",
"tsc:w": "tsc -w",
"tslint": "tslint -c tslint.json *.ts && tslint -c tslint.json src/{,**/}**.ts"
},
"license": "Apache-2.0",
"contributors": [
{
"name": "Maurizio Vitale",
"email": "maurizio.vitale@alfresco.com"
}
],
"keywords": [
"ng2",
"angular",
"angular2",
"activiti",
"activiti-diagrams"
],
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"zone.js": "^0.6.23",
"intl": "1.2.4",
"dialog-polyfill": "^0.4.3",
"element.scrollintoviewifneeded-polyfill": "^1.0.1",
"material-design-icons": "2.2.3",
"material-design-lite": "1.2.1",
"chart.js": "^2.1.4",
"md-date-time-picker": "^2.2.0",
"ng2-charts": "1.1.0",
"moment": "2.15.1",
"raphael": "^2.2.6",
"ng2-translate": "2.5.0",
"alfresco-js-api": "^0.4.0",
"ng2-alfresco-core": "0.4.0",
"ng2-activiti-diagrams": "0.4.0",
"ng2-activiti-analytics": "^0.4.0"
},
"devDependencies": {
"@types/node": "^6.0.42",
"@types/core-js": "^0.9.32",
"@types/jasmine": "^2.2.33",
"concurrently": "^2.2.0",
"rimraf": "2.5.2",
"tslint": "^3.8.1",
"typescript": "^2.0.3",
"wsrv": "^0.1.5"
}
}

View File

@ -0,0 +1,115 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { NgModule, Component, OnInit } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AnalyticsModule } from 'ng2-activiti-analytics';
@Component({
selector: 'alfresco-app-demo',
template: `
<label for="ticket"><b>Insert a valid ticket:</b></label><br>
<input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br>
<label for="host"><b>Insert the ip of your Activiti instance:</b></label><br>
<input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br>
<div *ngIf="!authenticated" style="color:#FF2323">
Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform
operations.
</div>
<hr>
<div class="page-content">
<label for="appId"><b>Insert the appId:</b></label><br>
<input id="appId" size="10" type="text" [(ngModel)]="appId">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp">
<analytics-report-list (reportClick)="onReportClick($event)"></analytics-report-list>
</div>
<div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp">
<activiti-analytics [appId]="appId" *ngIf="report" [reportId]="report.id"></activiti-analytics>
</div>
</div>
</div>`
})
export class AnalyticsDemoComponent implements OnInit {
appId: number;
report: any;
authenticated: boolean;
host: string = 'http://localhost:9999';
ticket: string;
constructor(private authService: AlfrescoAuthenticationService, private settingsService: AlfrescoSettingsService) {
settingsService.bpmHost = this.host;
settingsService.setProviders('BPM');
if (this.authService.getTicketBpm()) {
this.ticket = this.authService.getTicketBpm();
}
}
onReportClick(event: any) {
this.report = event;
}
public updateTicket(): void {
localStorage.setItem('ticket-BPM', this.ticket);
}
public updateHost(): void {
this.settingsService.bpmHost = this.host;
this.login();
}
public ngOnInit(): void {
this.login();
}
login() {
this.authService.login('admin', 'admin').subscribe(
ticket => {
console.log(ticket);
this.ticket = this.authService.getTicketBpm();
this.authenticated = true;
},
error => {
console.log(error);
this.authenticated = false;
});
}
}
@NgModule({
imports: [
BrowserModule,
CoreModule.forRoot(),
AnalyticsModule
],
declarations: [AnalyticsDemoComponent],
bootstrap: [AnalyticsDemoComponent]
})
export class AppModule {
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -0,0 +1,51 @@
/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'dist',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'moment': 'npm:moment/min/moment.min.js',
'ng2-charts': 'npm:ng2-charts',
'ng2-translate': 'npm:ng2-translate',
'alfresco-js-api': 'npm:alfresco-js-api/dist',
'ng2-alfresco-core': 'npm:ng2-alfresco-core/dist',
'ng2-activiti-diagrams': 'npm:ng2-activiti-diagrams/dist',
'ng2-activiti-analytics': 'npm:ng2-activiti-analytics/dist'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
'ng2-translate': { defaultExtension: 'js' },
'ng2-charts': { main: 'ng2-charts.js', defaultExtension: 'js'},
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
'ng2-alfresco-core': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-diagrams': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-analytics': { main: './index.js', defaultExtension: 'js'}
}
});
})(this);

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"removeComments": true,
"declaration": true,
"noLib": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"noImplicitAny": false,
"noImplicitReturns": false,
"noImplicitUseStrict": false,
"noFallthroughCasesInSwitch": true,
"outDir": "dist",
"types": ["core-js", "jasmine", "node"]
},
"exclude": [
"demo",
"node_modules",
"dist"
]
}

View File

@ -0,0 +1,124 @@
{
"rules": {
"align": [
true,
"parameters",
"arguments",
"statements"
],
"ban": false,
"class-name": true,
"comment-format": [
true,
"check-space",
"check-lowercase"
],
"curly": true,
"eofline": true,
"forin": true,
"indent": [
true,
"spaces"
],
"interface-name": false,
"jsdoc-format": true,
"label-position": true,
"label-undefined": true,
"max-line-length": [
true,
180
],
"member-ordering": [
true,
"public-before-private",
"static-before-instance",
"variables-before-functions"
],
"no-any": false,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-consecutive-blank-lines": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-constructor-vars": false,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": true,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-require-imports": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unreachable": true,
"no-unused-expression": true,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": [
true,
"single",
"avoid-escape"
],
"radix": true,
"semicolon": true,
"switch-default": true,
"trailing-comma": [
true,
{
"multiline": "never",
"singleline": "never"
}
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef": false,
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"use-strict": false,
"variable-name": [
true,
"check-format",
"allow-leading-underscore",
"ban-keywords"
],
"whitespace": [
true,
"check-branch",
"check-operator",
"check-separator",
"check-type",
"check-module",
"check-decl"
]
}
}

View File

@ -0,0 +1,74 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CoreModule } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportListComponent } from './src/components/analytics-report-list.component';
import { AnalyticsReportParametersComponent } from './src/components/analytics-report-parameters.component';
import { AnalyticsComponent } from './src/components/analytics.component';
import { AnalyticsReportHeatMapComponent } from './src/components/analytics-report-heat-map.component';
import { AnalyticsService } from './src/services/analytics.service';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { WIDGET_DIRECTIVES } from './src/components/widgets/index';
export * from './src/components/analytics.component';
export * from './src/components/analytics-report-list.component';
export * from './src/components/analytics-report-parameters.component';
export * from './src/services/analytics.service';
export * from './src/components/widgets/index';
export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent,
AnalyticsReportListComponent,
AnalyticsReportParametersComponent,
AnalyticsReportHeatMapComponent,
WIDGET_DIRECTIVES
];
export const ANALYTICS_PROVIDERS: any[] = [
AnalyticsService
];
@NgModule({
imports: [
CoreModule,
DiagramsModule
],
declarations: [
...ANALYTICS_DIRECTIVES,
...CHART_DIRECTIVES
],
providers: [
...ANALYTICS_PROVIDERS
],
exports: [
...ANALYTICS_DIRECTIVES
]
})
export class AnalyticsModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: AnalyticsModule,
providers: [
...ANALYTICS_PROVIDERS
]
};
}
}

View File

@ -0,0 +1,114 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
__karma__.loaded = function() {};
var builtPath = '/base/dist/';
function isJsFile(path) {
return path.slice(-3) == '.js';
}
function isSpecFile(path) {
return /\.spec\.(.*\.)?js$/.test(path);
}
function isBuiltFile(path) {
return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath);
}
var allSpecFiles = Object.keys(window.__karma__.files)
.filter(isSpecFile)
.filter(isBuiltFile);
var paths = {
// paths serve as alias
'npm:': 'base/node_modules/'
};
var map = {
'app': 'base/dist',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// testing
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
'@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
'@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
'@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'ng2-translate': 'npm:ng2-translate',
'ng2-charts' : 'npm:ng2-charts',
'md-date-time-picker' : 'npm:md-date-time-picker',
'moment' : 'npm:moment/min/moment.min.js',
'alfresco-js-api': 'npm:alfresco-js-api/dist',
'ng2-activiti-analytics': 'npm:ng2-activiti-analytics/dist',
'ng2-activiti-diagrams': 'npm:ng2-activiti-diagrams/dist',
'ng2-alfresco-core': 'npm:ng2-alfresco-core/dist'
};
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'ng2-translate': { defaultExtension: 'js' },
'ng2-charts': { defaultExtension: 'js' },
'md-date-time-picker': { defaultExtension: 'js' },
'moment': { defaultExtension: 'js' },
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
'ng2-activiti-analytics': { main: './index.js', defaultExtension: 'js'},
'ng2-activiti-diagrams': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-core': { main: './index.js', defaultExtension: 'js'}
};
var config = {
paths: paths,
map: map,
packages: packages
};
System.config(config);
System.import('@angular/core/testing')
.then(initTestBed)
.then(initTesting);
function initTestBed(){
return Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing')
])
.then(function (providers) {
var coreTesting = providers[0];
var browserTesting = providers[1];
coreTesting.TestBed.initTestEnvironment(
browserTesting.BrowserDynamicTestingModule,
browserTesting.platformBrowserDynamicTesting());
})
}
// Import all spec files and start karma
function initTesting () {
return Promise.all(
allSpecFiles.map(function (moduleName) {
return System.import(moduleName);
})
)
.then(__karma__.start, __karma__.error);
}

View File

@ -0,0 +1,126 @@
'use strict';
module.exports = function (config) {
var configuration = {
basePath: '.',
frameworks: ['jasmine-ajax', 'jasmine'],
files: [
// System.js for module loading
'node_modules/systemjs/dist/system.src.js',
// Polyfills
'node_modules/core-js/client/shim.js',
'node_modules/reflect-metadata/Reflect.js',
// zone.js
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js',
'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// RxJs
{pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
{pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},
// Paths loaded via module imports:
// Angular itself
{pattern: 'node_modules/@angular/**/*.js', included: false, watched: false},
{pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false},
'node_modules/alfresco-js-api/dist/alfresco-js-api.js',
'node_modules/moment/min/moment.min.js',
'node_modules/md-date-time-picker/dist/js/mdDateTimePicker.js',
'node_modules/chart.js/dist/Chart.bundle.min.js',
{pattern: 'node_modules/ng2-translate/**/*.js', included: false, watched: false},
'karma-test-shim.js',
// paths loaded via module imports
{pattern: 'dist/**/*.js', included: false, watched: true},
{pattern: 'dist/**/*.html', included: true, served: true, watched: true},
{pattern: 'dist/**/*.css', included: true, served: true, watched: true},
// ng2-components
{ pattern: 'node_modules/ng2-alfresco-core/dist/**/*.*', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-activiti-diagrams/dist/**/*.*', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-charts/**/*.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/md-date-time-picker/**/*.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/moment/**/*.js', included: false, served: true, watched: false },
// paths to support debugging with source maps in dev tools
{pattern: 'src/**/*.ts', included: false, watched: false},
{pattern: 'dist/**/*.js.map', included: false, watched: false}
],
exclude: [
'node_modules/**/*spec.js'
],
// proxied base paths
proxies: {
// required for component assets fetched by Angular's compiler
'/src/': '/base/src/'
},
port: 9876,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
colors: true,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers: {
Chrome_travis_ci: {
base: 'Chrome',
flags: ['--no-sandbox']
}
},
// Karma plugins loaded
plugins: [
'karma-jasmine',
'karma-coverage',
'karma-jasmine-ajax',
'karma-chrome-launcher',
'karma-mocha-reporter',
'karma-jasmine-html-reporter'
],
// Coverage reporter generates the coverage
reporters: ['mocha', 'coverage', 'kjhtml'],
// Source files that you wanna generate coverage for.
// Do not include tests or libraries (these files will be instrumented by Istanbul)
preprocessors: {
'dist/**/!(*spec|index|*mock|*model).js': 'coverage'
},
coverageReporter: {
includeAllSources: true,
dir: 'coverage/',
subdir: 'report',
reporters: [
{type: 'text'},
{type: 'json', file: 'coverage-final.json'},
{type: 'html'},
{type: 'lcov'}
]
}
};
if (process.env.TRAVIS) {
configuration.browsers = ['Chrome_travis_ci'];
}
config.set(configuration)
};

View File

@ -0,0 +1,110 @@
{
"name": "ng2-activiti-analytics",
"description": "Activiti Angular2 Analytics Component",
"version": "0.4.0",
"author": "Alfresco Software, Ltd.",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
"clean": "npm install rimraf && rimraf dist node_modules typings",
"build": "npm run tslint && rimraf dist && tsc && npm run copy-dist && license-check",
"build:w": "npm run tslint && rimraf dist && npm run watch-task",
"watch-task": "concurrently \"npm run tsc:w\" \"npm run copy-dist:w\" \"license-check\"",
"tslint": "tslint -c tslint.json *.ts && tslint -c tslint.json 'src/{,**/}**.ts'",
"copy-dist": "cpx \"./src/**/*.{html,css,json,png,jpg,gif,svg}\" ./dist/src",
"copy-dist:w": "cpx \"./src/**/*.{html,css,json,png,jpg,gif,svg}\" ./dist/src -w",
"tsc": "tsc",
"tsc:w": "tsc -w",
"pretest": "npm run build",
"test": "karma start karma.conf.js --reporters mocha,coverage --single-run",
"test-browser": "concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"",
"posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish": "npm run build",
"travis": "npm link ng2-alfresco-core ng2-activiti-diagrams"
},
"contributors": [
{
"name": "Mario Romano",
"email": "mario.romnao@alfresco.com"
},
{
"name": "Maurizio Vitale",
"email": "maurizio.vitale84@gmail.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components.git"
},
"bugs": {
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
},
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"zone.js": "^0.6.23",
"intl": "1.2.4",
"dialog-polyfill": "^0.4.3",
"element.scrollintoviewifneeded-polyfill": "^1.0.1",
"material-design-icons": "2.2.3",
"material-design-lite": "1.2.1",
"chart.js": "^2.1.4",
"md-date-time-picker": "^2.2.0",
"ng2-charts": "1.1.0",
"moment": "2.15.1",
"raphael": "^2.2.6",
"alfresco-js-api": "^0.4.0",
"ng2-translate": "2.5.0",
"ng2-alfresco-core": "0.4.0",
"ng2-activiti-diagrams": "0.4.0"
},
"devDependencies": {
"@types/node": "^6.0.42",
"@types/core-js": "^0.9.32",
"@types/jasmine": "^2.2.33",
"concurrently": "^2.2.0",
"cpx": "1.3.1",
"jasmine-core": "2.4.1",
"karma": "0.13.22",
"karma-chrome-launcher": "1.0.1",
"karma-coverage": "1.0.0",
"karma-jasmine": "1.0.2",
"karma-jasmine-ajax": "^0.1.13",
"karma-mocha-reporter": "2.0.3",
"karma-jasmine-html-reporter": "0.2.0",
"license-check": "1.1.5",
"remap-istanbul": "0.6.3",
"rimraf": "2.5.2",
"traceur": "0.0.91",
"tslint": "3.15.1",
"typescript": "^2.0.3",
"wsrv": "^0.1.5"
},
"keywords": [
"tag",
"alfresco-component"
],
"license-check-config": {
"src": [
"./dist/**/*.js"
],
"path": "assets/license_header.txt",
"blocking": true,
"logInfo": false,
"logError": true
},
"license": "Apache-2.0"
}

View File

@ -0,0 +1,113 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
export var chartProcessDefOverview = {
'elements': [{
'id': 'id1585876275153',
'type': 'table',
'rows': [
['__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS', '9'],
['__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-INSTANCES', '41'],
['__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES', '3'],
['__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES', '38']
]
}, {
'id': 'id1585876413072',
'type': 'pieChart',
'title': 'Total process instances overview',
'titleKey': 'REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.PROC-INST-CHART-TITLE',
'values': [{
'key': 'Second Process',
'y': 4,
'keyAndValue': ['Second Process', '4']
}, {
'key': 'Simple process',
'y': 30,
'keyAndValue': ['Simple process', '30']
}, {
'key': 'Third Process',
'y': 7,
'keyAndValue': ['Third Process', '7']
}]
}, {
'id': 'id1585877659181',
'type': 'table',
'title': 'Process definition details',
'titleKey': 'REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.DETAIL-TABLE',
'columnNames': ['Process definition', 'Total', 'Active', 'Completed'],
'columnNameKeys': ['REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.DETAIL-TABLE-PROCESS',
'REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.DETAIL-TABLE-TOTAL',
'REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.DETAIL-TABLE-ACTIVE',
'REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.DETAIL-TABLE-COMPLETED'],
'columnsCentered': [false, false, false, false],
'rows': [
['Second Process', '4', '0', '4'],
['Simple process', '30', '3', '27'],
['Third Process', '7', '0', '7']
]
}]
};
export var chartTaskOverview = {
'elements': [{
'id': 'id792351752194',
'type': 'barChart',
'title': 'title',
'titleKey': 'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.TASK-HISTOGRAM-TITLE',
'values': [{
'key': 'series1',
'values': [['2016-09-30T00:00:00.000+0000', 3], ['2016-10-04T00:00:00.000+0000', 1]]
}],
'xAxisType': 'date_month',
'yAxisType': 'count'
}, {
'id': 'id792349721129',
'type': 'masterDetailTable',
'title': 'Detailed task statistics',
'titleKey': 'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.DETAILED-TASK-STATS-TITLE',
'columnNames': ['Task', 'Count', 'Sum', 'Min duration', 'Max duration', 'Average duration', 'Stddev duration'],
'columnNameKeys': [
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.DETAILED-TASK-STATS-TASK',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.COUNT',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.SUM',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.MIN-DURATION',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.MAX-DURATION',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.AVERAGE',
'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.STDDE'],
'columnsCentered': [false, false, false, false],
'rows': [
['fake 1 user task', '1', '2.0', '3.0', '4.0', '5.0', '6.0'],
['fake 2 user task', '1', '2.0', '3.0', '4.0', '5.0', '6.0']
]
}, {
'id': 'id10931125229538',
'type': 'multiBarChart',
'title': 'Task duration',
'titleKey': 'REPORTING.DEFAULT-REPORTS.TASK-OVERVIEW.TASK-DURATIONS-TITLE',
'values': [{
'key': 'averages',
'values': [[1, 0], [2, 5], [3, 2]]
}, {
'key': 'minima',
'values': [[1, 0], [2, 0], [3, 0]]
}, {
'key': 'maxima',
'values': [[1, 0], [2, 29], [3, 29]]
}],
'yAxisType': 'count'
}]
};

View File

@ -0,0 +1,150 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { ReportParameterDetailsModel } from '../models/report.model';
export var reportDefParamStatus = {
'id': 2005,
'name': 'Fake Task overview status',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"status","name":null,"nameKey":null,"type":"status","value":null,"dependsOn":null}]}'
};
export var reportDefParamNumber = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"slowProcessInstanceInteger","name":null,"nameKey":null,"type":"integer","value":10,"dependsOn":null}]}'
};
export var reportDefParamDuration = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"duration","name":null,"nameKey":null,"type":"duration","value":null,"dependsOn":null}]}'
};
export var reportDefParamCheck = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"typeFiltering","name":null,"nameKey":null,"type":"boolean","value":true,"dependsOn":null}]}'
};
export var reportDefParamDateRange = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRange","name":null,"nameKey":null,"type":"dateRange","value":null,"dependsOn":null}]}'
};
export var reportDefParamRangeInterval = {
'id': 2006,
'name': 'Fake Task overview RangeInterval',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRangeInterval","name":null,"nameKey":null,"type":"dateInterval","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDef = {
'id': 2006,
'name': 'Fake Task overview ProcessDefinition',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"processDefinitionId","name":null,"nameKey":null,"type":"processDefinition","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDefOptionsNoApp = [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
},
{
'id': 'FakeProcessTest 2:1:1',
'name': 'Fake Process Test 2 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 3:1:1',
'name': 'Fake Process Test 3 Name ',
'version': 1
}
];
export var reportDefParamProcessDefOptions = {
'size': 4, 'total': 4, 'start': 0, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
},
{
'id': 'FakeProcessTest 2:1:1',
'name': 'Fake Process Test 2 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 3:1:1',
'name': 'Fake Process Test 3 Name ',
'version': 1
}
]
};
export var reportDefParamProcessDefOptionsApp = {
'size': 2, 'total': 2, 'start': 2, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
}
]
};
export var reportDefParamTask = {
'id': 2006,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"taskName","name":null,"nameKey":null,"type":"task","value":null,"dependsOn":"processDefinitionId"}]}'
};
export var reportDefParamTaskOptions = ['Fake task name 1', 'Fake task name 2'];
export var fieldProcessDef = new ReportParameterDetailsModel(
{
id: 'processDefinitionId',
type: 'processDefinition',
value: 'fake-process-name:1:15027'
}
);

View File

@ -0,0 +1,9 @@
<h2>Process Heat map</h2>
<div *ngIf="hasMetric()">
<form [formGroup]="metricForm" novalidate>
<dropdown-widget [field]="field" [group]="metricForm.controls.metricGroup" [controllerName]="'metric'"
(fieldChanged)="onMetricChanges(field)" [showDefaultOption]="false"></dropdown-widget>
</form>
<activiti-diagram *ngIf="currentMetric" [processDefinitionId]="report.processDefinitionId" [metricPercentages]="currentMetric"></activiti-diagram>
</div>
<div *ngIf="!hasMetric()">No metric found</div>

View File

@ -0,0 +1,128 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { AnalyticsService } from '../services/analytics.service';
import { DebugElement } from '@angular/core';
declare let jasmine: any;
describe('Test ng2-activiti-analytics-report-heat-map', () => {
let componentHandler: any;
let component: any;
let fixture: ComponentFixture<AnalyticsReportHeatMapComponent>;
let debug: DebugElement;
let element: HTMLElement;
let totalCountPerc = {'sid-fake-id': 0, 'fake-start-event': 100};
let totalTimePerc = {'sid-fake-id': 10, 'fake-start-event': 30};
let avgTimePercentages = {'sid-fake-id': 5, 'fake-start-event': 50};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule,
DiagramsModule
],
declarations: [
AnalyticsReportHeatMapComponent,
...WIDGET_DIRECTIVES
],
providers: [
AnalyticsService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsReportHeatMapComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
component.report = {
totalCountsPercentages: totalCountPerc,
totalTimePercentages: totalTimePerc,
avgTimePercentages: avgTimePercentages
};
});
describe('Rendering tests: Heat Map', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('should render the dropdown with the metric options', async(() => {
component.report = {totalCountsPercentages: {'sid-fake-id': 10, 'fake-start-event': 30}};
component.onSuccess.subscribe(() => {
fixture.whenStable().then(() => {
let dropDown: any = element.querySelector('#select-metrics');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(3);
expect(dropDown[0].innerHTML).toEqual('Number of times a step is executed');
expect(dropDown[1].innerHTML).toEqual('Total time spent in a process step');
expect(dropDown[2].innerHTML).toEqual('Average time spent in a process step');
});
});
fixture.detectChanges();
}));
it('should return false when no metrics are defined in the report', async(() => {
component.report = {};
expect(component.hasMetric()).toBeFalsy();
}));
it('should return true when the metrics are defined in the report', async(() => {
expect(component.hasMetric()).toBeTruthy();
}));
it('should change the currentmetric width totalCount', async(() => {
let field = {value: 'totalCount'};
component.onMetricChanges(field);
expect(component.currentMetric).toEqual(totalCountPerc);
}));
it('should change the currentmetric width totalTime', async(() => {
let field = {value: 'totalTime'};
component.onMetricChanges(field);
expect(component.currentMetric).toEqual(totalTimePerc);
}));
it('should change the currentmetric width avgTime', async(() => {
let field = {value: 'avgTime'};
component.onMetricChanges(field);
expect(component.currentMetric).toEqual(avgTimePercentages);
}));
});
});

View File

@ -0,0 +1,89 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
@Component({
moduleId: module.id,
selector: 'analytics-report-heat-map',
templateUrl: './analytics-report-heat-map.component.html'
})
export class AnalyticsReportHeatMapComponent implements OnInit {
@Input()
report: any;
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
field: any = {};
metricForm: FormGroup;
currentMetric: string;
constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService,
private formBuilder: FormBuilder) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
}
}
ngOnInit() {
this.initForm();
this.field.id = 'metrics';
this.field.value = 'totalCount';
this.analyticsService.getMetricValues().subscribe(
(opts: any[]) => {
this.field.options = opts;
this.onSuccess.emit(opts);
}
);
}
onMetricChanges(field: any) {
if (field.value === 'totalCount') {
this.currentMetric = this.report.totalCountsPercentages;
} else if (field.value === 'totalTime') {
this.currentMetric = this.report.totalTimePercentages;
} else if (field.value === 'avgTime') {
this.currentMetric = this.report.avgTimePercentages;
}
}
initForm() {
this.metricForm = this.formBuilder.group({
metricGroup: new FormGroup({
metric: new FormControl()
})
});
}
hasMetric() {
return (this.report.totalCountsPercentages ||
this.report.totalTimePercentages ||
this.report.avgTimePercentages) ? true : false;
}
}

View File

@ -0,0 +1,19 @@
.mdl-list__item {
cursor: pointer;
}
.activiti-filters__entry {
cursor: pointer;
}
.activiti-filters__entry-icon {
margin-right: 12px !important;
}
.activiti-filters__entry.active {
color: rgb(68,138,255);
}
.activiti-filters__entry.active .activiti-filters__entry-icon {
color: rgb(68,138,255);
}

View File

@ -0,0 +1,11 @@
<div class="menu-container">
<ul class='mdl-list'>
<li class="mdl-list__item activiti-filters__entry" (click)="selectReport(report)" *ngFor="let report of reports; let idx = index"
[class.active]="currentReport === report">
<span [attr.id]="'report-list-' + idx" class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon activiti-filters__entry-icon" [attr.data-automation-id]="report.name + '_filter'">assignment</i>
<span class="text">{{report.name}}</span>
</span>
</li>
</ul>
</div>

View File

@ -0,0 +1,165 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsService } from '../services/analytics.service';
import { DebugElement } from '@angular/core';
declare let jasmine: any;
describe('Test ng2-activiti-analytics Report list', () => {
let reportList = [
{'id': 2002, 'name': 'Fake Test Process definition heat map'},
{'id': 2003, 'name': 'Fake Test Process definition overview'},
{'id': 2004, 'name': 'Fake Test Process instances overview'},
{'id': 2005, 'name': 'Fake Test Task overview'},
{'id': 2006, 'name': 'Fake Test Task service level agreement'}
];
let reportSelected = {'id': 2003, 'name': 'Fake Test Process definition overview'};
let component: any;
let fixture: ComponentFixture<AnalyticsReportListComponent>;
let debug: DebugElement;
let element: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule
],
declarations: [
AnalyticsReportListComponent
],
providers: [
AnalyticsService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsReportListComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
});
describe('Rendering tests', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Report return true with undefined reports', () => {
expect(component.isReportsEmpty()).toBeTruthy();
});
it('Report return true with an empty reports', () => {
component.reports = [];
expect(component.isReportsEmpty()).toBeTruthy();
});
it('should return the default reports when the report list is empty', (done) => {
fixture.detectChanges();
component.onSuccess.subscribe(() => {
fixture.detectChanges();
expect(element.querySelector('#report-list-0 > i').innerHTML).toBe('assignment');
expect(element.querySelector('#report-list-0 > span').innerHTML).toBe('Fake Test Process definition heat map');
expect(element.querySelector('#report-list-1 > span').innerHTML).toBe('Fake Test Process definition overview');
expect(element.querySelector('#report-list-2 > span').innerHTML).toBe('Fake Test Process instances overview');
expect(element.querySelector('#report-list-3 > span').innerHTML).toBe('Fake Test Task overview');
expect(element.querySelector('#report-list-4 > span').innerHTML).toBe('Fake Test Task service level agreement');
expect(component.isReportsEmpty()).toBeFalsy();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: []
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: []
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: reportList
});
});
it('Report render the report list relative to a single app', (done) => {
fixture.detectChanges();
component.onSuccess.subscribe(() => {
fixture.detectChanges();
expect(element.querySelector('#report-list-0 > i').innerHTML).toBe('assignment');
expect(element.querySelector('#report-list-0 > span').innerHTML).toBe('Fake Test Process definition heat map');
expect(element.querySelector('#report-list-1 > span').innerHTML).toBe('Fake Test Process definition overview');
expect(element.querySelector('#report-list-2 > span').innerHTML).toBe('Fake Test Process instances overview');
expect(element.querySelector('#report-list-3 > span').innerHTML).toBe('Fake Test Task overview');
expect(element.querySelector('#report-list-4 > span').innerHTML).toBe('Fake Test Task service level agreement');
expect(component.isReportsEmpty()).toBeFalsy();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: reportList
});
});
it('Report emit an error with a empty response', (done) => {
fixture.detectChanges();
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
it('Should return the current report when one report is selected', () => {
component.reportClick.subscribe(() => {
expect(component.currentReport).toEqual(reportSelected);
});
component.selectReport(reportSelected);
});
});
});

View File

@ -0,0 +1,119 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel } from '../models/report.model';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
@Component({
moduleId: module.id,
selector: 'analytics-report-list',
templateUrl: './analytics-report-list.component.html',
styleUrls: ['./analytics-report-list.component.css']
})
export class AnalyticsReportListComponent implements OnInit {
@Output()
reportClick: EventEmitter<ReportParametersModel> = new EventEmitter<ReportParametersModel>();
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
private reportObserver: Observer<any>;
report$: Observable<any>;
currentReport: any;
reports: ReportParametersModel[] = [];
constructor(private auth: AlfrescoAuthenticationService,
private analyticsService: AnalyticsService) {
this.report$ = new Observable<ReportParametersModel>(observer => this.reportObserver = observer).share();
}
ngOnInit() {
this.report$.subscribe((report: ReportParametersModel) => {
this.reports.push(report);
});
this.getReportListByAppId();
}
/**
* Get the report list by app id
*/
getReportListByAppId() {
this.analyticsService.getReportList().subscribe(
(res: ReportParametersModel[]) => {
if (res && res.length === 0) {
this.createDefaultReports();
} else {
res.forEach((report) => {
this.reportObserver.next(report);
});
this.onSuccess.emit(res);
}
},
(err: any) => {
this.onError.emit(err);
console.log(err);
}
);
}
/**
* Create the default reports and return the report list
*/
createDefaultReports() {
this.analyticsService.createDefaultReports().subscribe(
() => {
this.analyticsService.getReportList().subscribe(
(response: ReportParametersModel[]) => {
response.forEach((report) => {
this.reportObserver.next(report);
});
this.onSuccess.emit(response);
}
);
}
);
}
/**
* Check if the report list is empty
* @returns {boolean|ReportParametersModel[]}
*/
isReportsEmpty(): boolean {
return this.reports === undefined || (this.reports && this.reports.length === 0);
}
/**
* Select the current report
* @param report
*/
public selectReport(report: any) {
this.currentReport = report;
this.reportClick.emit(report);
}
}

View File

@ -0,0 +1,23 @@
.dropdown-widget {
width: 100%;
}
.dropdown-widget__select {
width: 100%;
}
.dropdown-widget__invalid .dropdown-widget__select {
border-color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label {
color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label:after {
background-color: #d50000;
}
.dropdown-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}

View File

@ -0,0 +1,58 @@
<div class="col-md-6">
<div *ngIf="reportParameters">
<form [formGroup]="reportForm" novalidate>
<h1>{{reportParameters.name}}</h1>
<div *ngFor="let field of reportParameters.definition.parameters">
<div [ngSwitch]="field.type">
<div *ngSwitchCase="'integer'">
<br>
<number-widget [field]="field" [group]="reportForm.controls.processInstanceGroup" [controllerName]="'slowProcessInstanceInteger'"
[required]="true"></number-widget>
</div>
<div *ngSwitchCase="'duration'">
<br>
<duration-widget [field]="field" [group]="reportForm.controls.durationGroup"
[controllerName]="'duration'"></duration-widget>
</div>
<div *ngSwitchCase="'boolean'">
<br>
<checkbox-widget [field]="field" [group]="reportForm.controls.typeFilteringGroup"
[controllerName]="'typeFiltering'"></checkbox-widget>
</div>
<div *ngSwitchCase="'status'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.statusGroup" [controllerName]="'status'"
[required]="true"></dropdown-widget>
</div>
<div *ngSwitchCase="'processDefinition'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.processDefGroup" [controllerName]="'processDefinitionId'"
[required]="true" (fieldChanged)="onProcessDefinitionChanges(field)"></dropdown-widget>
</div>
<div *ngSwitchCase="'task'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.taskGroup" [controllerName]="'taskName'"
[required]="true"></dropdown-widget>
</div>
<div *ngSwitchCase="'dateRange'">
<br>
<date-range-widget [field]="field" [group]="reportForm.controls.dateRange"></date-range-widget>
</div>
<div *ngSwitchCase="'dateInterval'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.dateIntervalGroup" [controllerName]="'dateRangeInterval'"
[required]="true" [showDefaultOption]="false"></dropdown-widget>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{field.type}}</span>
</div>
</div>
</div>
<div *ngIf="debug">
<p>ReportForm valid : {{ reportForm.valid }}</p>
<p>ReportForm status : {{ reportForm.errors | json }}</p>
<p>ReportForm FormGroup valid : {{reportForm && reportForm.controls.dateRange.valid | json }}</p>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,397 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import {
CoreModule
} from 'ng2-alfresco-core';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel } from '../models/report.model';
import * as moment from 'moment';
import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticParamsMock from '../assets/analyticsParamsReportComponent.mock';
declare let jasmine: any;
declare let mdDateTimePicker: any;
describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
let component: any;
let fixture: ComponentFixture<AnalyticsReportParametersComponent>;
let debug: DebugElement;
let element: HTMLElement;
let componentHandler: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule
],
declarations: [
AnalyticsReportParametersComponent,
...WIDGET_DIRECTIVES
],
providers: [
AnalyticsService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsReportParametersComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
fixture.detectChanges();
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
});
describe('Rendering tests', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Should initialize the Report form with a Form Group ', () => {
expect(component.reportForm.get('dateRange')).toBeDefined();
expect(component.reportForm.get('dateRange').get('startDate')).toBeDefined();
expect(component.reportForm.get('dateRange').get('endDate')).toBeDefined();
});
it('Should render a dropdown with all the status when the definition parameter type is \'status\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-status');
expect(element.querySelector('h1').innerHTML).toEqual('Fake Task overview status');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('All');
expect(dropDown[2].innerHTML).toEqual('Active');
expect(dropDown[3].innerHTML).toEqual('Complete');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamStatus
});
});
it('Should render a number with the default value when the definition parameter type is \'integer\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#slowProcessInstanceInteger');
expect(numberElement.value).toEqual('10');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamNumber
});
});
it('Should render a duration component when the definition parameter type is \'duration\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#duration');
expect(numberElement.value).toEqual('0');
let dropDown: any = element.querySelector('#select-duration');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Seconds');
expect(dropDown[1].innerHTML).toEqual('Minutes');
expect(dropDown[2].innerHTML).toEqual('Hours');
expect(dropDown[3].innerHTML).toEqual('Days');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamDuration
});
});
it('Should save an Params object when the submit is performed', () => {
component.onSuccess.subscribe((res) => {
expect(res.dateRange.startDate).toEqual('2016-09-01T00:00:00.000Z');
expect(res.dateRange.endDate).toEqual('2016-10-05T00:00:00.000Z');
expect(res.status).toEqual('All');
expect(res.processDefinitionId).toEqual('FakeProcess:1:22');
expect(res.taskName).toEqual('FakeTaskName');
expect(res.duration).toEqual(22);
expect(res.dateRangeInterval).toEqual(120);
expect(res.slowProcessInstanceInteger).toEqual(2);
expect(res.typeFiltering).toEqual(true);
});
let values: any = {
dateRange: {
startDate: '2016-09-01', endDate: '2016-10-05'
},
statusGroup: {
status: 'All'
},
processDefGroup: {
processDefinitionId: 'FakeProcess:1:22'
},
taskGroup: {
taskName: 'FakeTaskName'
},
durationGroup: {
duration: 22
},
dateIntervalGroup: {
dateRangeInterval: 120
},
processInstanceGroup: {
slowProcessInstanceInteger: 2
},
typeFilteringGroup: {
typeFiltering: true
}
};
component.submit(values);
});
it('Should render a checkbox with the value true when the definition parameter type is \'boolean\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let checkElement: any = element.querySelector('#typeFiltering');
expect(checkElement.checked).toBeTruthy();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamCheck
});
});
it('Should render a date range components when the definition parameter type is \'dateRange\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let today = moment().format('YYYY-MM-DD');
const startDate: any = element.querySelector('#startDateInput');
const endDate: any = element.querySelector('#endDateInput');
expect(startDate.value).toEqual(today);
expect(endDate.value).toEqual(today);
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamDateRange
});
});
it('Should render a dropdown with all the RangeInterval when the definition parameter type is \'dateRangeInterval\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-dateRangeInterval');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('By hour');
expect(dropDown[1].innerHTML).toEqual('By day');
expect(dropDown[2].innerHTML).toEqual('By week');
expect(dropDown[3].innerHTML).toEqual('By month');
expect(dropDown[4].innerHTML).toEqual('By year');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamRangeInterval
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' reportId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
expect(dropDown[3].innerHTML).toEqual('Fake Process Test 2 Name (v 1) ');
expect(dropDown[4].innerHTML).toEqual('Fake Process Test 3 Name (v 1) ');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDefOptionsNoApp
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' appId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(3);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
done();
});
let appId = 1;
component.appId = appId;
let change = new SimpleChange(null, appId);
component.ngOnChanges({ 'appId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDefOptionsApp
});
});
it('Should load the task list when a process definition is selected', () => {
component.onSuccessReportParams.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res[0].id).toEqual('Fake task name 1');
expect(res[0].name).toEqual('Fake task name 1');
expect(res[1].id).toEqual('Fake task name 2');
expect(res[1].name).toEqual('Fake task name 2');
});
component.reportId = 100;
component.reportParameters = new ReportParametersModel(analyticParamsMock.reportDefParamTask);
component.onProcessDefinitionChanges(analyticParamsMock.fieldProcessDef);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamTaskOptions
});
});
it('Should emit an error with a 404 response when the options response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
it('Should emit an error with a 404 response when the report parameters response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
});
it('Should convert a string in number', () => {
let numberConvert = component.convertNumber('2');
expect(numberConvert).toEqual(2);
});
});

View File

@ -0,0 +1,213 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel, ReportQuery, ParameterValueModel, ReportParameterDetailsModel } from '../models/report.model';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
import * as moment from 'moment';
@Component({
moduleId: module.id,
selector: 'analytics-report-parameters',
templateUrl: './analytics-report-parameters.component.html',
styleUrls: ['./analytics-report-parameters.component.css']
})
export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD';
@Input()
appId: string;
@Input()
reportId: string;
@Input()
debug: boolean = false;
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
@Output()
onFormValueChanged = new EventEmitter();
onDropdownChanged = new EventEmitter();
onSuccessReportParams = new EventEmitter();
onSuccessParamOpt = new EventEmitter();
reportParameters: ReportParametersModel;
reportForm: FormGroup;
private dropDownSub;
private reportParamsSub;
private paramOpts;
constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService,
private formBuilder: FormBuilder ) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
}
}
ngOnInit() {
this.initForm();
this.dropDownSub = this.onDropdownChanged.subscribe((field) => {
let paramDependOn: ReportParameterDetailsModel = this.reportParameters.definition.parameters.find(p => p.dependsOn === field.id);
if (paramDependOn) {
this.retrieveParameterOptions(this.reportParameters.definition.parameters, this.appId, this.reportId, field.value);
}
});
this.paramOpts = this.onSuccessReportParams.subscribe((report: ReportParametersModel) => {
if (report.hasParameters()) {
this.retrieveParameterOptions(report.definition.parameters, this.appId);
}
});
this.reportForm.valueChanges.subscribe(data => this.onValueChanged(data));
}
ngOnChanges(changes: SimpleChanges) {
let reportId = changes['reportId'];
if (reportId && reportId.currentValue) {
this.getReportParams(reportId.currentValue);
}
let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) {
this.getReportParams(this.reportId);
}
}
initForm() {
this.reportForm = this.formBuilder.group({
dateRange: new FormGroup({}),
statusGroup: new FormGroup({
status: new FormControl()
}),
processInstanceGroup: new FormGroup({
slowProcessInstanceInteger: new FormControl()
}),
taskGroup: new FormGroup({
taskName: new FormControl()
}),
typeFilteringGroup: new FormGroup({
typeFiltering: new FormControl()
}),
dateIntervalGroup: new FormGroup({
dateRangeInterval: new FormControl()
}),
durationGroup: new FormGroup({
duration: new FormControl()
}),
processDefGroup: new FormGroup({
processDefinitionId: new FormControl()
})
});
}
public getReportParams(reportId: string) {
this.reportParamsSub = this.analyticsService.getReportParams(reportId).subscribe(
(res: ReportParametersModel) => {
this.reportParameters = res;
if (this.reportParameters.hasParameters()) {
this.onSuccessReportParams.emit(res);
} else {
this.onSuccess.emit();
}
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
}
private retrieveParameterOptions(parameters: ReportParameterDetailsModel[], appId: string, reportId?: string, processDefinitionId?: string) {
parameters.forEach((param) => {
this.analyticsService.getParamValuesByType(param.type, appId, reportId, processDefinitionId).subscribe(
(opts: ParameterValueModel[]) => {
param.options = opts;
this.onSuccessParamOpt.emit(opts);
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
});
}
onProcessDefinitionChanges(field: any) {
if (field.value) {
this.onDropdownChanged.emit(field);
}
}
public submit(values: any) {
let reportParamQuery = this.convertFormValuesToReportParamQuery(values);
this.onSuccess.emit(reportParamQuery);
}
onValueChanged(values: any) {
this.onFormValueChanged.emit(values);
if (this.reportForm && this.reportForm.valid) {
this.submit(values);
}
}
public convertMomentDate(date: string) {
return moment(date, AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI, true)
.format(AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z';
}
public convertNumber(value: string): number {
return value != null ? parseInt(value, 10) : 0;
}
convertFormValuesToReportParamQuery(values: any): ReportQuery {
let reportParamQuery: ReportQuery = new ReportQuery();
reportParamQuery.dateRange.startDate = this.convertMomentDate(values.dateRange.startDate);
reportParamQuery.dateRange.endDate = this.convertMomentDate(values.dateRange.endDate);
reportParamQuery.status = values.statusGroup.status;
reportParamQuery.processDefinitionId = values.processDefGroup.processDefinitionId;
reportParamQuery.taskName = values.taskGroup.taskName;
reportParamQuery.duration = values.durationGroup.duration;
reportParamQuery.dateRangeInterval = values.dateIntervalGroup.dateRangeInterval;
reportParamQuery.slowProcessInstanceInteger = this.convertNumber(values.processInstanceGroup.slowProcessInstanceInteger);
reportParamQuery.typeFiltering = values.typeFilteringGroup.typeFiltering;
return reportParamQuery;
}
ngOnDestroy() {
this.dropDownSub.unsubscribe();
this.paramOpts.unsubscribe();
if (this.reportParamsSub) {
this.reportParamsSub.unsubscribe();
}
}
}

View File

@ -0,0 +1,25 @@
.chart {display: block; width: 100%;}
.dropdown-widget {
width: 100%;
}
.dropdown-widget__select {
width: 100%;
}
.dropdown-widget__invalid .dropdown-widget__select {
border-color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label {
color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label:after {
background-color: #d50000;
}
.dropdown-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}

View File

@ -0,0 +1,84 @@
<div class="col-md-6">
<analytics-report-parameters [appId]="appId" [reportId]="reportId"
(onFormValueChanged)="reset()" (onSuccess)="showReport($event)"></analytics-report-parameters>
<div *ngIf="reports">
<div *ngFor="let report of reports">
<h2>{{report.title}}</h2>
<div [ngSwitch]="report.type">
<div *ngSwitchCase="'pie'">
<div class="col-md-6">
<div *ngIf="!report.hasData()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasData()" class="chart"
[data]="report.data"
[labels]="report.labels"
[chartType]="report.type"></base-chart>
</div>
</div>
<div *ngSwitchCase="'table'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'masterDetailTable'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-master-detail-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'bar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasDatasets()" class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="report.type"></base-chart>
</div>
</div>
<div *ngSwitchCase="'multiBar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div *ngIf="report.hasDatasets()">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="'stacked-id'">
<input type="checkbox" [attr.id]="'stacked-id'" class="mdl-checkbox__input"
[checked]="report.options.scales.xAxes[0].stacked"
[(ngModel)]="report.options.scales.xAxes[0].stacked"
(change)="refresh(report)">
<span class="mdl-checkbox__label">Stacked</span>
</label>
<base-chart class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="'bar'"></base-chart>
</div>
</div>
</div>
<div *ngSwitchCase="'HeatMap'">
<analytics-report-heat-map [report]="report"></analytics-report-heat-map>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{report.type}}</span>
</div>
</div>
</div>
</div>
<br><br><br>
<div *ngIf="!reports">{{'ANALYTICS.MESSAGES.FILL-PARAMETER' | translate}}</div>
</div>

View File

@ -0,0 +1,226 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import {
CoreModule
} from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsComponent } from '../components/analytics.component';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { Chart } from '../models/chart.model';
import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticMock from '../assets/analyticsComponent.mock';
export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent,
AnalyticsReportParametersComponent,
AnalyticsReportListComponent,
AnalyticsReportHeatMapComponent,
WIDGET_DIRECTIVES
];
export const ANALYTICS_PROVIDERS: any[] = [
AnalyticsService
];
declare let jasmine: any;
declare let mdDateTimePicker: any;
describe('Test ng2-activiti-analytics Report ', () => {
let component: any;
let fixture: ComponentFixture<AnalyticsComponent>;
let debug: DebugElement;
let element: HTMLElement;
let componentHandler: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule,
DiagramsModule
],
declarations: [
...ANALYTICS_DIRECTIVES,
...CHART_DIRECTIVES
],
providers: [
...ANALYTICS_PROVIDERS
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
fixture.detectChanges();
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
});
describe('Rendering tests', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Should render the Process definition overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('table');
expect(res[0].datasets).toBeDefined();
expect(res[0].datasets.length).toEqual(4);
expect(res[0].datasets[0][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS');
expect(res[0].datasets[0][1]).toEqual('9');
expect(res[0].datasets[1][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-INSTANCES');
expect(res[0].datasets[1][1]).toEqual('41');
expect(res[0].datasets[2][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES');
expect(res[0].datasets[2][1]).toEqual('3');
expect(res[0].datasets[3][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES');
expect(res[0].datasets[3][1]).toEqual('38');
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('pie');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('table');
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.appId = 1;
component.reportId = 1001;
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartProcessDefOverview
});
});
it('Should render the Task overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('bar');
expect(res[0].labels).toBeDefined();
expect(res[0].labels.length).toEqual(2);
expect(res[0].labels[0]).toEqual('2016-09-30T00:00:00.000+0000');
expect(res[0].labels[1]).toEqual('2016-10-04T00:00:00.000+0000');
expect(res[0].datasets[0].label).toEqual('series1');
expect(res[0].datasets[0].data[0]).toEqual(3);
expect(res[0].datasets[0].data[1]).toEqual(1);
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('table');
expect(res[1].datasets).toBeDefined();
expect(res[1].datasets.length).toEqual(2);
expect(res[1].datasets[0][0]).toEqual('fake 1 user task');
expect(res[1].datasets[0][1]).toEqual('1');
expect(res[1].datasets[0][2]).toEqual('2.0');
expect(res[1].datasets[0][3]).toEqual('3.0');
expect(res[1].datasets[0][4]).toEqual('4.0');
expect(res[1].datasets[0][5]).toEqual('5.0');
expect(res[1].datasets[0][6]).toEqual('6.0');
expect(res[1].datasets[1][0]).toEqual('fake 2 user task');
expect(res[1].datasets[1][1]).toEqual('1');
expect(res[1].datasets[1][2]).toEqual('2.0');
expect(res[1].datasets[1][3]).toEqual('3.0');
expect(res[1].datasets[1][4]).toEqual('4.0');
expect(res[1].datasets[1][5]).toEqual('5.0');
expect(res[1].datasets[1][6]).toEqual('6.0');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('multiBar');
expect(res[2].labels).toBeDefined();
expect(res[2].labels.length).toEqual(3);
expect(res[2].labels[0]).toEqual(1);
expect(res[2].labels[1]).toEqual(2);
expect(res[2].labels[2]).toEqual(3);
expect(res[2].datasets[0].label).toEqual('averages');
expect(res[2].datasets[0].data[0]).toEqual(0);
expect(res[2].datasets[0].data[1]).toEqual(5);
expect(res[2].datasets[0].data[2]).toEqual(2);
expect(res[2].datasets[1].label).toEqual('minima');
expect(res[2].datasets[1].data[0]).toEqual(0);
expect(res[2].datasets[1].data[1]).toEqual(0);
expect(res[2].datasets[1].data[2]).toEqual(0);
expect(res[2].datasets[2].label).toEqual('maxima');
expect(res[2].datasets[2].data[0]).toEqual(0);
expect(res[2].datasets[2].data[1]).toEqual(29);
expect(res[2].datasets[2].data[2]).toEqual(29);
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartTaskOverview
});
});
it('Should reset the reports when the onChanged is call', () => {
let reportId = 1;
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
expect(component.reports).toBeUndefined();
});
it('Should emit onError event with a 404 response ', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
});
});

View File

@ -0,0 +1,110 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component, EventEmitter, OnChanges, Input, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import { Chart } from '../models/chart.model';
@Component({
moduleId: module.id,
selector: 'activiti-analytics',
templateUrl: './analytics.component.html',
styleUrls: ['./analytics.component.css']
})
export class AnalyticsComponent implements OnChanges {
@Input()
appId: string;
@Input()
reportId: number;
@Input()
debug: boolean = false;
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
reportParamQuery = new ReportQuery();
reports: any[];
public barChartOptions: any = {
responsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 1
}
}],
xAxes: [{
ticks: {
},
stacked: true
}]
}
};
constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService) {
console.log('AnalyticsComponent');
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
}
}
ngOnChanges(changes: SimpleChanges) {
this.reset();
}
public showReport($event) {
this.reportParamQuery = $event;
this.analyticsService.getReportsByParams(this.reportId, this.reportParamQuery).subscribe(
(res: Chart[]) => {
this.reports = res;
this.onSuccess.emit(res);
},
(err: any) => {
this.onError.emit(err);
console.log(err);
}
);
}
public reset() {
if (this.reports) {
this.reports = undefined;
}
}
public refresh(report): void {
/**
* (My guess), for Angular to recognize the change in the dataset
* it has to change the dataset variable directly,
* so one way around it, is to clone the data, change it and then
* assign it;
*/
let clone = JSON.parse(JSON.stringify(report));
report.datasets = clone.datasets;
}
}

View File

@ -0,0 +1,9 @@
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="field.id" [formGroup]="formGroup">
<input formControlName="{{controllerName}}" type="checkbox"
[attr.id]="field.id"
class="mdl-checkbox__input"
[checked]="field.value"
[(ngModel)]="field.value"
(ngModelChange)="changeValue(field)">
<span class="mdl-checkbox__label">{{field.nameKey | translate}}</span>
</label>

View File

@ -0,0 +1,41 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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.
*/
import { Component, ElementRef, Input } from '@angular/core';
import { WidgetComponent } from './../widget.component';
import { FormGroup } from '@angular/forms';
@Component({
moduleId: module.id,
selector: 'checkbox-widget',
templateUrl: './checkbox.widget.html'
})
export class CheckboxWidget extends WidgetComponent {
@Input()
field: any;
@Input('group')
public formGroup: FormGroup;
@Input('controllerName')
public controllerName: string;
constructor(public elementRef: ElementRef) {
super();
}
}

View File

@ -0,0 +1,3 @@
.date-picker-mdl {
margin-left: 20px;
}

Some files were not shown because too many files have changed in this diff Show More