Remove usages of global angular.*

pull/6365/head
Oliver Günther 6 years ago
parent ae238df77e
commit 4c0f54443c
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 8
      app/assets/javascripts/styleguide.js
  2. 82
      app/assets/stylesheets/content/_select2.lsg
  3. 2
      docs/development/development-environment-ubuntu.md
  4. 2
      frontend/doc/RAILS.md
  5. 150
      frontend/src/app/angular-modules.ts
  6. 14
      frontend/src/app/angular4-modules.ts
  7. 6
      frontend/src/app/angular4-transition-utils.ts
  8. 2
      frontend/src/app/bootstrap.ts
  9. 129
      frontend/src/app/components/api/op-file-upload/op-file-upload.service.test.ts
  10. 8
      frontend/src/app/components/api/op-file-upload/op-file-upload.service.ts
  11. 8
      frontend/src/app/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ng2.test.ts
  12. 2
      frontend/src/app/components/filters/query-filter/query-filter.component.ts
  13. 2
      frontend/src/app/components/filters/query-filters/query-filters.component.ts
  14. 62
      frontend/src/app/components/filters/wp-filters/wp-filters.service.test.ts
  15. 49
      frontend/src/app/components/open-project.module.test.ts
  16. 32
      frontend/src/app/components/open-project.module.ts
  17. 16
      frontend/src/app/components/projects/current-project.service.test.ts
  18. 2
      frontend/src/app/components/projects/current-project.service.ts
  19. 2
      frontend/src/app/components/projects/project-menu-autocomplete/project-menu-autocomplete.component.ts
  20. 6
      frontend/src/app/components/types/form-configuration/group-edit-in-place.directive.ts
  21. 32
      frontend/src/app/components/types/form-configuration/types-form-configuration.controller.ts
  22. 2
      frontend/src/app/components/wp-activity/comment-service.ts
  23. 1
      frontend/src/app/components/wp-activity/user/user-activity.component.ts
  24. 4
      frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts
  25. 4
      frontend/src/app/components/wp-buttons/wp-zen-mode-toggle-button/wp-zen-mode-toggle-button.component.ts
  26. 2
      frontend/src/app/components/wp-edit-form/display-field-renderer.ts
  27. 1
      frontend/src/app/components/wp-edit-form/work-package-changeset.ts
  28. 2
      frontend/src/app/components/wp-edit-form/work-package-filter-values.ts
  29. 2
      frontend/src/app/components/wp-edit/wp-edit-field/wp-edit-field.component.ts
  30. 2
      frontend/src/app/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts
  31. 5
      frontend/src/app/components/wp-fast-table/state/wp-table-base.service.ts
  32. 2
      frontend/src/app/components/wp-inline-create/wp-inline-create.component.ts
  33. 4
      frontend/src/app/components/wp-query-menu/wp-query-menu.service.ts
  34. 4
      frontend/src/app/components/wp-query-select/wp-query-select-dropdown.component.ts
  35. 6
      frontend/src/app/components/wp-query/url-params-helper.test.ts
  36. 8
      frontend/src/app/components/wp-query/url-params-helper.ts
  37. 4
      frontend/src/app/components/wp-relations/wp-relations-parent/wp-relations-parent.component.ts
  38. 2
      frontend/src/app/components/wp-relations/wp-relations.component.ts
  39. 27
      frontend/src/app/components/wp-single-view-tabs/keep-tab/keep-tab.service.test.ts
  40. 2
      frontend/src/app/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts
  41. 2
      frontend/src/app/components/wp-table/configuration-modal/tabs/columns-tab.component.ts
  42. 2
      frontend/src/app/components/wp-table/configuration-modal/tabs/filters-tab.component.ts
  43. 12
      frontend/src/app/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts
  44. 5
      frontend/src/app/components/wp-table/table-pagination/wp-table-pagination.component.ng2.test.ts
  45. 227
      frontend/src/app/components/wp-table/table-pagination/wp-table-pagination.component.test.ts
  46. 80
      frontend/src/app/helpers/angular-rx-utils.ts
  47. 11
      frontend/src/app/init-globals.ts
  48. 34
      frontend/src/app/layout/controllers/index.js
  49. 86
      frontend/src/app/layout/controllers/main-menu-controller.js
  50. 29
      frontend/src/app/layout/index.js
  51. 4
      frontend/src/app/modules/common/focus/focus-helper.ts
  52. 3
      frontend/src/app/modules/common/focus/focus.directive.ts
  53. 91
      frontend/src/app/modules/common/icon/op-icon.ng2.test.ts
  54. 79
      frontend/src/app/modules/common/icon/op-icon.test.ts
  55. 37
      frontend/src/app/modules/common/latest-items/latest-items.filter.js
  56. 53
      frontend/src/app/modules/common/latest-items/latest-items.filter.test.js
  57. 60
      frontend/src/app/modules/common/lazy/lazy.service.ts
  58. 42
      frontend/src/app/modules/common/loading-indicator/loading-indicator.service.test.ts
  59. 12
      frontend/src/app/modules/common/model-auth/model-auth.service.test.ts
  60. 2
      frontend/src/app/modules/common/op-date-picker/datepicker.ts
  61. 6
      frontend/src/app/modules/common/openproject-common.module.ts
  62. 7
      frontend/src/app/modules/common/path-helper/path-helper.service.test.ts
  63. 7
      frontend/src/app/modules/fields/display/field-types/wp-display-float-field.module.ts
  64. 4
      frontend/src/app/modules/fields/display/field-types/wp-display-formattable-field.module.ts
  65. 3
      frontend/src/app/modules/fields/edit/field-types/formattable-edit-field.ts
  66. 4
      frontend/src/app/modules/hal/helpers/hal-resource-builder.ts
  67. 10
      frontend/src/app/modules/hal/helpers/lazy-accessor.test.ts
  68. 4
      frontend/src/app/modules/hal/resources/work-package-resource.ng2.test.ts
  69. 2
      frontend/src/app/modules/hal/resources/work-package-resource.ts
  70. 3
      frontend/src/app/openproject-tests.js
  71. 45
      frontend/tests/unit/mocks/projects.json
  72. 103
      frontend/tests/unit/tests/asset_functions.js
  73. 82
      frontend/tests/unit/tests/layout/controllers/main-menu-controller-test.js

@ -32,11 +32,3 @@ window.openProject = {
environment: 'dev'
};
jQuery(document).ready(function () {
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['openproject']);
});
angular.module('openproject-style-guide', ['ui.select']);

@ -1,82 +0,0 @@
# Select2
## select2 (jQuery)
```
<div style="min-height: 100px">
<select id="select2-example">
<option>one</option>
<option>two</option>
<option>three</option>
</select>
</div>
```
```
@javascript
jQuery(function($) {
$('#select2-example').select2();
});
```
## ui-select (Angular)
```
<div ng-controller="UiSelectExample" style="min-height: 100px">
<ui-select ng-model="person.selected" theme="select2" ng-disabled="disabled" style="min-width: 300px;">
<ui-select-match placeholder="Select a person in the list or search their name">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in people | filter: $select.search">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
</small>
</ui-select-choices>
</ui-select>
</div>
```
## ui-select (Angular): multiple
```
<div ng-controller="UiSelectExample" style="min-height: 100px">
<ui-select multiple ng-model="selectedPeople" theme="select2" ng-disabled="disabled" style="min-width: 300px;">
<ui-select-match placeholder="Select a person in the list or search their name">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="person in people | filter: $select.search">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
</small>
</ui-select-choices>
</ui-select>
</div>
```
```
@javascript
angular.module('openproject').controller('UiSelectExample', ['$scope', function($scope) {
$scope.disabled = undefined;
$scope.enable = function() {
$scope.disabled = false;
};
$scope.disable = function() {
$scope.disabled = true;
};
$scope.person = {};
$scope.people = [
{ name: 'Adam', email: 'adam@email.com' },
{ name: 'Amalie', email: 'amalie@email.com' },
{ name: 'Wladimir', email: 'wladimir@email.com' },
{ name: 'Samantha', email: 'samantha@email.com' },
{ name: 'Estefanía', email: 'estefanía@email.com' },
{ name: 'Natasha', email: 'natasha@email.com' },
{ name: 'Nicole', email: 'nicole@email.com' },
{ name: 'Adrian', email: 'adrian@email.com' }
];
$scope.selectedPeople = [$scope.people[5], $scope.people[4]];
}]);
```

@ -254,7 +254,7 @@ This will start the development server on port `3000` by default.
```
This will watch for any changes within the `frontend/` and compile the application javascript bundle on demand. You will need to watch this tab for the compilation output,
should you be working on the TypeScript / angular.js frontend part.
should you be working on the TypeScript / Angular frontend part.
## Start Coding

@ -27,7 +27,7 @@ An example would be the use of the `AutoCompleteHelper` (see `./frontend/app/hel
// the goal is to use the service for initializing atWho after a new piece of HTML has been inserted into the document
// the body is always the angular application on every page
var injector = angular.element('body').injector();
var injector = jQuery('body').injector();
// the injecotr can be used to apply our service in arbitrary scopes
// NOTE: textareas is a collection of angular elements

@ -1,150 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
// import * as angular from 'angular';
//
// // global
// export const opUiComponentsModule = angular.module('openproject.uiComponents',
// ['ui.select', 'ui.router', 'ui.router.upgrade', 'openproject.workPackages.services'])
// .run(['$rootScope', function ($rootScope:ng.IRootScopeService) {
// ($rootScope as any)['I18n'] = I18n;
// }]);
// export const animationsModule = angular.module('openproject.animations', [
// 'ngAnimate'
// ]);
// export const opConfigModule = angular.module('openproject.config', []);
// export const opServicesModule = angular.module('openproject.services', [
// 'openproject.uiComponents',
// 'openproject.config',
// 'openproject.helpers',
// 'openproject.workPackages.config',
// 'openproject.workPackages.helpers',
// 'openproject.api'
// ]);
// angular.module('openproject.helpers', ['openproject.services']);
//
// // work packages
// export const opWorkPackagesModule = angular.module('openproject.workPackages', [
// 'openproject.workPackages.activities',
// 'openproject.workPackages.controllers',
// 'openproject.workPackages.filters',
// 'openproject.workPackages.directives',
// 'openproject.workPackages.tabs',
// 'openproject.uiComponents',
// 'ngFileUpload'
// ]);
// export const wpServicesModule = angular.module('openproject.workPackages.services', [
// 'openproject.inplace-edit'
// ]);
// angular.module(
// 'openproject.workPackages.helpers', [
// 'openproject.helpers',
// 'openproject.workPackages.services'
// ]);
// angular.module('openproject.workPackages.filters', [
// 'openproject.workPackages.helpers'
// ]);
// angular.module('openproject.workPackages.config', []);
// export const wpControllersModule = angular.module('openproject.workPackages.controllers', [
// 'openproject.workPackages.helpers',
// 'openproject.services',
// 'openproject.workPackages.config',
// 'openproject.layout'
// ]);
// angular.module('openproject.workPackages.models', []);
// export const wpDirectivesModule = angular.module('openproject.workPackages.directives', [
// 'openproject.uiComponents',
// 'openproject.services',
// 'openproject.workPackages.services',
// 'openproject.workPackages.models'
// ]);
// export const wpTabsModule = angular.module('openproject.workPackages.tabs', []);
// angular.module('openproject.workPackages.activities', []);
//
// // messages
// angular.module('openproject.messages', [
// 'openproject.messages.controllers'
// ]);
// angular.module('openproject.messages.controllers', []);
//
// // time entries
// angular.module('openproject.timeEntries', [
// 'openproject.timeEntries.controllers'
// ]);
// angular.module('openproject.timeEntries.controllers', []);
//
// angular.module('openproject.layout', [
// 'openproject.layout.controllers',
// 'ui.router',
// 'ui.router.upgrade'
// ]);
// angular.module('openproject.layout.controllers', []);
//
// export const opApiModule = angular.module('openproject.api', [
// 'openproject.workPackages',
// 'openproject.services'
// ]);
//
// export const opTemplatesModule = angular.module('openproject.templates', []);
//
// export const opNotificationsModule = angular.module('openproject.notifications', [
// 'openproject.config'
// ]);
//
// // refactoring
// angular.module('openproject.inplace-edit', []);
// angular.module('openproject.responsive', []);
//
// export const wpButtonsModule = angular.module('openproject.wpButtons',
// ['ui.router', 'ui.router.upgrade', 'openproject.services']);
//
// // main app
// var angularDragula:any = require('angular-dragula');
//
// export const openprojectModule = angular.module('openproject', [
// 'ui.router',
// 'ui.router.upgrade',
// 'openproject.animations',
// 'openproject.config',
// 'openproject.uiComponents',
// 'openproject.workPackages',
// 'openproject.messages',
// 'openproject.timeEntries',
// 'ngAnimate',
// 'ngAria',
// angularDragula(angular),
// 'openproject.layout',
// 'openproject.api',
// 'openproject.templates',
// 'monospaced.elastic',
// 'openproject.inplace-edit',
// wpButtonsModule.name,
// 'openproject.responsive',
// ]);
//
// export default openprojectModule;

@ -198,6 +198,9 @@ import {initializeUiRouterConfiguration, OPENPROJECT_ROUTES} from "core-componen
import {WorkPackagesBaseComponent} from "core-components/routing/main/work-packages-base.component";
import {ExpressionService} from "core-app/modules/common/xss/expression.service";
import {WorkPackageService} from "core-components/work-packages/work-package.service";
import {OpenprojectPluginsModule} from "core-app/modules/plugins/openproject-plugins.module";
import {ConfirmFormSubmitController} from "core-components/modals/confirm-form-submit/confirm-form-submit.directive";
import {ProjectMenuAutocompleteComponent} from "core-components/projects/project-menu-autocomplete/project-menu-autocomplete.component";
@NgModule({
imports: [
@ -215,7 +218,9 @@ import {WorkPackageService} from "core-components/work-packages/work-package.ser
// Hal Module
OpenprojectHalModule,
// Display + Edit field functionality
OpenprojectFieldsModule
OpenprojectFieldsModule,
// Plugin hooks and modules
OpenprojectPluginsModule,
],
providers: [
{
@ -292,6 +297,7 @@ import {WorkPackageService} from "core-components/work-packages/work-package.ser
ExternalQueryConfigurationService,
],
declarations: [
ConfirmFormSubmitController,
WorkPackagesBaseComponent,
WorkPackagesListComponent,
OpContextMenuTrigger,
@ -429,6 +435,9 @@ import {WorkPackageService} from "core-components/work-packages/work-package.ser
// External query configuration
ExternalQueryConfigurationComponent,
// Project autocompleter
ProjectMenuAutocompleteComponent,
// Form configuration
OpDragScrollDirective,
@ -473,6 +482,9 @@ import {WorkPackageService} from "core-components/work-packages/work-package.ser
OPContextMenuComponent,
WorkPackageQuerySelectDropdownComponent,
// Project autocompleter
ProjectMenuAutocompleteComponent,
// Embedded table
WorkPackageEmbeddedTableComponent,

@ -30,14 +30,8 @@ import {InjectionToken} from '@angular/core';
import {StateService} from '@uirouter/core';
import {IQService, IRootScopeService, ITimeoutService} from 'angular';
export const $qToken = new InjectionToken<IQService>('$q');
export const $localeToken = new InjectionToken<any>('$locale');
export const TextileServiceToken = new InjectionToken<any>('TextileServiceToken');
export const $httpToken = new InjectionToken<any>('$http');
export const OpContextMenuLocalsToken = new InjectionToken<any>('CONTEXT_MENU_LOCALS');
export const OpModalLocalsToken = new InjectionToken<any>('OP_MODAL_LOCALS');
export const HookServiceToken = new InjectionToken<any>('HookService');
export function upgradeService(factory:(i:any) => any, token:any) {
return {

@ -16,7 +16,7 @@ jQuery(function() {
platformBrowserDynamic()
.bootstrapModule(OpenProjectModule)
.then(platformRef => {
angular.element('body').addClass('__ng2-bootstrap-has-run');
jQuery('body').addClass('__ng2-bootstrap-has-run');
});
});

@ -26,70 +26,65 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
import {OpenProjectFileUploadService, UploadFile} from './op-file-upload.service';
import IRootScopeService = angular.IRootScopeService;
describe('opFileUpload service', () => {
var $rootScope:IRootScopeService;
var opFileUpload:OpenProjectFileUploadService;
var Upload:any;
beforeEach(angular.mock.inject(function(_$rootScope_:any, _opFileUpload_:any, _Upload_:any) {
[$rootScope, opFileUpload, Upload] = _.toArray(arguments);
}));
it('should exist', () => {
expect(opFileUpload).to.exist;
});
describe('when uploading multiple files', () => {
var uploadStub:any;
var result:any;
const file:any = {
name: 'name',
description: 'description'
};
const directory:any = {type: 'directory'};
const files = [file, file, directory, directory];
const filtered = [file, file];
beforeEach(() => {
uploadStub = sinon.stub(Upload, 'upload');
result = opFileUpload.upload('somewhere', files);
});
afterEach(() => {
$rootScope.$apply();
uploadStub.restore();
});
it('should call upload once for every file, that is no directory', () => {
expect(uploadStub.callCount).to.equal(filtered.length);
});
it('should not mutate the original files array', () => {
expect(files).to.have.length(4);
});
it('should call upload with the correct parameters', () => {
expect(uploadStub.calledOn({
file,
url: 'somewhere',
fields: {
metadata: {
description: file.description,
fileName: file.name
}
}
}));
});
it('should return a result object that contains each upload in an array', () => {
expect(result.uploads).to.have.length(filtered.length);
});
it('should return a resolved promise that is the summary of the uploads', () => {
expect(result.finished).to.eventually.be.fulfilled;
});
});
});
// import {OpenProjectFileUploadService, UploadFile} from './op-file-upload.service';
//
// describe('opFileUpload service', () => {
// var opFileUpload:OpenProjectFileUploadService;
// var Upload:any;
//
//
// it('should exist', () => {
// expect(opFileUpload).to.exist;
// });
//
// describe('when uploading multiple files', () => {
// var uploadStub:any;
// var result:any;
// const file:any = {
// name: 'name',
// description: 'description'
// };
// const directory:any = {type: 'directory'};
// const files = [file, file, directory, directory];
// const filtered = [file, file];
//
// beforeEach(() => {
// uploadStub = sinon.stub(Upload, 'upload');
// result = opFileUpload.upload('somewhere', files);
// });
//
// afterEach(() => {
// $rootScope.$apply();
// uploadStub.restore();
// });
//
// it('should call upload once for every file, that is no directory', () => {
// expect(uploadStub.callCount).to.equal(filtered.length);
// });
//
// it('should not mutate the original files array', () => {
// expect(files).to.have.length(4);
// });
//
// it('should call upload with the correct parameters', () => {
// expect(uploadStub.calledOn({
// file,
// url: 'somewhere',
// fields: {
// metadata: {
// description: file.description,
// fileName: file.name
// }
// }
// }));
// });
//
// it('should return a result object that contains each upload in an array', () => {
// expect(result.uploads).to.have.length(filtered.length);
// });
//
// it('should return a resolved promise that is the summary of the uploads', () => {
// expect(result.finished).to.eventually.be.fulfilled;
// });
// });
// });

@ -26,9 +26,6 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
import IQService = angular.IQService;
import IPromise = angular.IPromise;
export interface UploadFile extends File {
description?:string;
customName?:string;
@ -40,8 +37,7 @@ export interface UploadResult {
}
export class OpenProjectFileUploadService {
constructor(protected $q:IQService,
protected Upload:any) {
constructor(protected Upload:any) {
}
/**
@ -65,7 +61,7 @@ export class OpenProjectFileUploadService {
return this.Upload.upload({data, url});
});
const finished = this.$q.all(uploads);
const finished = Promise.all(uploads);
return {uploads, finished} as any;
}
}

@ -26,11 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
import {
$httpToken,
$qToken,
} from 'core-app/angular4-transition-utils';
import {async, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {async, fakeAsync, TestBed} from '@angular/core/testing';
import {ComponentFixture} from '@angular/core/testing/src/component_fixture';
import {FilterToggledMultiselectValueComponent} from './filter-toggled-multiselect-value.component';
import {HalResource} from 'core-app/modules/hal/resources/hal-resource';
@ -84,8 +80,6 @@ describe('FilterToggledMultiselectValueComponent', () => {
{ provide: I18nService, useValue: I18nStub },
{ provide: PathHelperService, useValue: {} },
{ provide: RootDmService, useValue: {} },
{ provide: $qToken, useValue: {} },
{ provide: $httpToken, useValue: {} },
{ provide: HalResourceService, useValue: {} },
HalResourceSortingService
]

@ -74,7 +74,7 @@ export class QueryFilterComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.eeShowBanners = angular.element('body').hasClass('ee-banners-visible');
this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible');
this.availableOperators = this.filter.schema.availableOperators;
this.showValuesInput = this.filter.currentSchema!.isValueRequired();
}

@ -73,7 +73,7 @@ export class QueryFiltersComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnInit() {
this.eeShowBanners = angular.element('body').hasClass('ee-banners-visible');
this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible');
}
ngOnDestroy() {

@ -1,62 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageFiltersService} from "./wp-filters.service";
const expect = chai.expect;
describe('wpFiltersService', () => {
var wpFiltersService:WorkPackageFiltersService;
beforeEach(angular.mock.module('openproject.services'));
beforeEach(angular.mock.inject((_wpFiltersService_:any) => {
wpFiltersService = _wpFiltersService_;
}));
it('should exist', () => {
expect(wpFiltersService).to.exist;
});
it('should have set its visibility to false', () => {
expect(wpFiltersService.visible).to.be.false;
});
describe('when using toggleVisibility', () => {
beforeEach(() => {
wpFiltersService.toggleVisibility();
});
it('should turn its visibility to true', () => {
expect(wpFiltersService.visible).to.be.true;
});
it('should turn off its visibility when used again', () => {
wpFiltersService.toggleVisibility();
expect(wpFiltersService.visible).to.be.false;
});
});
});

@ -1,49 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {opDirective} from './open-project.module';
describe('OpenProject module', () => {
describe('opDirective function', () => {
it('should deeply merge the given configuration parameters', () => {
var directive:ng.IDirective = {
scope: {
someValue: '='
}
};
var config:ng.IDirective = {
scope: {
someOtherValue: '='
}
};
var merged:any = opDirective(directive, config);
expect(merged.scope['someValue']).to.eq(merged.scope['someOtherValue']);
});
});
});

@ -1,32 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
export function opDirective(directive:ng.IDirective = {}, config:ng.IDirective = {}):ng.IDirective {
// TODO: Replace '_.merge' with AngularJS v1.4 'angular.merge' method
return _.merge(directive, config);
}

@ -29,17 +29,11 @@
/*jshint expr: true*/
import {CurrentProjectService} from './current-project.service';
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service";
describe('currentProject service', function() {
var element:ng.IAugmentedJQuery;
var currentProject:CurrentProjectService;
beforeEach(angular.mock.module('openproject.templates',
'openproject.services'));
beforeEach(angular.mock.inject((_currentProject_:CurrentProjectService) => {
currentProject = _currentProject_;
}));
var currentProject:CurrentProjectService = new CurrentProjectService(new PathHelperService());
describe('with no meta present', () => {
it('returns null values', () => {
@ -57,12 +51,12 @@ describe('currentProject service', function() {
<meta name="current_project" data-project-name="Foo 1234" data-project-id="1" data-project-identifier="foobar"/>
`;
element = angular.element(html);
angular.element(document.body).append(element);
element = jQuery(html);
jQuery(document.body).append(element);
currentProject.detect();
});
afterEach(angular.mock.inject(() => {
afterEach((() => {
element.remove();
}));

@ -81,7 +81,7 @@ export class CurrentProjectService {
* Detect the current project from its meta tag.
*/
public detect() {
const element = angular.element('meta[name=current_project]');
const element = jQuery('meta[name=current_project]');
if (element.length) {
this.current = {
id: element.data('projectId'),

@ -51,7 +51,7 @@ type ProjectAutocompleteItem = IAutocompleteItem<IProjectMenuEntry>;
templateUrl: './project-menu-autocomplete.template.html',
selector: 'project-menu-autocomplete'
})
export class ProjectMenuAutocompleteController extends ILazyAutocompleterBridge<IProjectMenuEntry> implements OnInit {
export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge<IProjectMenuEntry> implements OnInit {
public text:any;
// The project dropdown menu

@ -68,7 +68,7 @@
// scope.editing = true;
// scope.nameBefore = scope.name;
// $timeout(function(){
// angular.element('input', element).trigger('focus');
// jQuery('input', element).trigger('focus');
// }, 100);
// };
//
@ -85,7 +85,7 @@
// };
//
// scope.saveEdition = function() {
// let newValue: string = angular.element("input", element[0]).first().val();
// let newValue: string = jQuery("input", element[0]).first().val();
// scope.nameOriginal = scope.name;
// scope.name = newValue.trim();
// scope.leaveEditingMode();
@ -104,7 +104,7 @@
// if ($event.keyCode == 13) {
// // ENTER
// // a blur event will trigger `saveEdition`
// angular.element('input', element[0]).blur();
// jQuery('input', element[0]).blur();
// // Do not submit the whole form:
// $event.preventDefault();
// $event.stopPropagation();

@ -62,7 +62,7 @@
//
// dragulaService.options($scope, 'groups', {
// moves: function(el:any, container:any, handle:any) {
// const editing = angular.element(el).find('.group-edit-in-place--input').length > 0;
// const editing = jQuery(el).find('.group-edit-in-place--input').length > 0;
// return !editing && handle.classList.contains('group-handle');
// }
// });
@ -82,7 +82,7 @@
// button_continue: I18n.t('js.label_reset')
// }
// }).then(() => {
// angular.element('input#type_attribute_groups').first().val(JSON.stringify([]));
// jQuery('input#type_attribute_groups').first().val(JSON.stringify([]));
//
// // Disable our form handler that updates the attribute groups
// form.off('submit.typeformupdater');
@ -95,16 +95,16 @@
// };
//
// $scope.deactivateAttribute = ($event:any) => {
// angular.element($event.target)
// jQuery($event.target)
// .parents('.type-form-conf-attribute')
// .appendTo('#type-form-conf-inactive-group .attributes');
// $scope.updateHiddenFields();
// };
//
// $scope.deleteGroup = ($event:any):void => {
// let group:JQuery = angular.element($event.target).parents('.type-form-conf-group');
// let attributes:JQuery = angular.element('.attributes', group).children();
// let inactiveAttributes:JQuery = angular.element('#type-form-conf-inactive-group .attributes');
// let group:JQuery = jQuery($event.target).parents('.type-form-conf-group');
// let attributes:JQuery = jQuery('.attributes', group).children();
// let inactiveAttributes:JQuery = jQuery('#type-form-conf-inactive-group .attributes');
//
// inactiveAttributes.prepend(attributes);
//
@ -113,8 +113,8 @@
// };
//
// $scope.addGroup = (event:any) => {
// let newGroup:JQuery = angular.element('#type-form-conf-group-template').clone();
// let draggableGroups:JQuery = angular.element('#draggable-groups');
// let newGroup:JQuery = jQuery('#type-form-conf-group-template').clone();
// let draggableGroups:JQuery = jQuery('#draggable-groups');
// let randomId:string = Math.ceil(Math.random() * 10000000).toString();
//
// // Remove the id of the template:
@ -122,16 +122,16 @@
// // Every group needs a key and an original-key:
// newGroup.attr('data-key', randomId);
// newGroup.attr('data-original-key', randomId);
// angular.element('group-edit-in-place', newGroup).attr('key', randomId);
// jQuery('group-edit-in-place', newGroup).attr('key', randomId);
//
// draggableGroups.prepend(newGroup);
// $compile(newGroup)($scope);
// };
//
// $scope.addQuery = (event:any) => {
// let newGroup:JQuery = angular.element('#type-form-conf-query-template').clone();
// let newGroup:JQuery = jQuery('#type-form-conf-query-template').clone();
//
// let draggableGroups:JQuery = angular.element('#draggable-groups');
// let draggableGroups:JQuery = jQuery('#draggable-groups');
// let randomId:string = Math.ceil(Math.random() * 10000000).toString();
//
// // Remove the id of the template:
@ -139,7 +139,7 @@
// // Every group needs a key and an original-key:
// newGroup.attr('data-key', randomId);
// newGroup.attr('data-original-key', randomId);
// angular.element('group-edit-in-place', newGroup).attr('key', randomId);
// jQuery('group-edit-in-place', newGroup).attr('key', randomId);
//
// draggableGroups.prepend(newGroup);
// $compile(newGroup)($scope);
@ -169,7 +169,7 @@
// };
//
// $scope.updateHiddenFields = ():boolean => {
// let groups:HTMLElement[] = angular.element('.type-form-conf-group').not('#type-form-conf-group-template').toArray();
// let groups:HTMLElement[] = jQuery('.type-form-conf-group').not('#type-form-conf-group-template').toArray();
// let seenGroupNames:{ [name:string]:boolean } = {};
// let newAttrGroups:Array<Array<(string | Array<string> | boolean)>> = [];
// let inputAttributeGroups:JQuery;
@ -186,7 +186,7 @@
// let keyIsSymbol:boolean = JSON.parse(group.attr('data-key-is-symbol'));
// let attrKeys:string[] = [];
//
// angular.element(group).removeClass('-error');
// jQuery(group).removeClass('-error');
// if (groupKey == null || groupKey.length === 0) {
// // Do not save groups without a name.
// return;
@ -197,7 +197,7 @@
// NotificationsService.addError(
// I18n.t('js.types.attribute_groups.error_duplicate_group_name', {group: groupKey})
// );
// angular.element(group).addClass('-error');
// jQuery(group).addClass('-error');
// hasError = true;
// return;
// }
@ -225,7 +225,7 @@
// });
//
// // Finally update hidden input fields
// inputAttributeGroups = angular.element('input#type_attribute_groups').first();
// inputAttributeGroups = jQuery('input#type_attribute_groups').first();
//
// inputAttributeGroups.val(JSON.stringify(newAttrGroups));
//

@ -38,7 +38,7 @@ import {I18nService} from "core-app/modules/common/i18n/i18n.service";
@Injectable()
export class CommentService {
// Replacement for $scope.$emit on activty-entry to mark comments to be quoted.
// Replacement for ng1 $scope.$emit on activty-entry to mark comments to be quoted.
// Should be generalized if needed for more than that.
public quoteEvents = input<string>();

@ -34,7 +34,6 @@ import {WorkPackageCommentField} from 'core-components/work-packages/work-packag
import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource';
import {WorkPackagesActivityService} from 'core-components/wp-single-view-tabs/activity-panel/wp-activity.service';
import {TextileService} from "core-app/modules/common/textile/textile-service";
import {TextileServiceToken} from "core-app/angular4-transition-utils";
import {AfterViewInit, Component, ElementRef, Inject, Input, OnInit} from "@angular/core";
import {UserCacheService} from "core-components/user/user-cache.service";
import {IEditFieldHandler} from "core-app/modules/fields/edit/editing-portal/edit-field-handler.interface";

@ -92,7 +92,7 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu
projectPath: this.projectIdentifier
};
angular.extend(params, this.$state.params);
_.extend(params, this.$state.params);
this.$state.go(this.listState, params);
}
@ -102,7 +102,7 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu
projectPath: this.projectIdentifier,
};
angular.extend(params, this.$state.params);
_.extend(params, this.$state.params);
this.$state.go(this.keepTab.currentDetailsState, params);
}
}

@ -83,7 +83,7 @@ export class WorkPackageZenModeButtonComponent extends AbstractWorkPackageButton
private deactivateZenMode():void {
WorkPackageZenModeButtonComponent.inZenMode = false;
angular.element('body').removeClass('zen-mode');
jQuery('body').removeClass('zen-mode');
this.disabled = false;
if (screenfull.enabled && screenfull.isFullscreen) {
screenfull.exit();
@ -92,7 +92,7 @@ export class WorkPackageZenModeButtonComponent extends AbstractWorkPackageButton
private activateZenMode():void {
WorkPackageZenModeButtonComponent.inZenMode = true;
angular.element('body').addClass('zen-mode');
jQuery('body').addClass('zen-mode');
if (screenfull.enabled) {
screenfull.request();
}

@ -129,7 +129,7 @@ export class DisplayFieldRenderer {
if (field.isFormattable && !field.isEmpty()) {
try {
titleContent = _.escape(angular.element(`<div>${labelContent}</div>`).text());
titleContent = _.escape(jQuery(`<div>${labelContent}</div>`).text());
} catch (e) {
console.error("Failed to parse formattable labelContent");
titleContent = "Label for " + field.displayName;

@ -140,7 +140,6 @@ export class WorkPackageChangeset {
/**
* Update the form resource from the API.
* @return {angular.IPromise<any>}
*/
public updateForm():Promise<FormResource> {
let payload = this.buildPayloadFromChanges();

@ -16,7 +16,7 @@ export class WorkPackageFilterValues {
public applyDefaultsFromFilters() {
return this.changeset.getForm().then((form) => {
const promises:Promise<any>[] = [];
angular.forEach(this.filters, filter => {
_.each(this.filters, filter => {
// Ignore any filters except =
if (filter.operator.id !== '=') {
return;

@ -84,7 +84,7 @@ export class WorkPackageEditFieldComponent implements OnInit {
}
public ngOnInit() {
this.$element = angular.element(this.elementRef.nativeElement);
this.$element = jQuery(this.elementRef.nativeElement);
this.wpEditFieldGroup.register(this);
}

@ -43,7 +43,7 @@ export class WorkPackageReplacementLabelComponent implements OnInit {
}
ngOnInit() {
this.$element = angular.element(this.elementRef.nativeElement);
this.$element = jQuery(this.elementRef.nativeElement);
}
public activate(evt:JQueryEventObject) {

@ -29,7 +29,6 @@
import {TableState} from 'core-components/wp-table/table-state/table-state';
import {InputState, State} from 'reactivestates';
import {mapTo, take, takeUntil} from 'rxjs/operators';
import {scopedObservable} from '../../../helpers/angular-rx-utils';
import {Observable} from 'rxjs';
import {QueryResource} from 'core-app/modules/hal/resources/query-resource';
import {QuerySchemaResource} from 'core-app/modules/hal/resources/query-schema-resource';
@ -69,10 +68,6 @@ export abstract class WorkPackageTableBaseService<T> {
this.state.clear(reason);
}
public observeOnScope(scope:ng.IScope) {
return scopedObservable(scope, this.state.values$());
}
public observeUntil(unsubscribe:Observable<any>) {
return this.state.values$().pipe(takeUntil(unsubscribe));
}

@ -110,7 +110,7 @@ export class WorkPackageInlineCreateComponent implements OnInit, OnChanges, OnDe
}
ngOnInit() {
this.$element = angular.element(this.elementRef.nativeElement);
this.$element = jQuery(this.elementRef.nativeElement);
}
ngOnChanges() {

@ -189,7 +189,7 @@ export class QueryMenuService {
return null;
}
let previousElement = angular.element(allItems[allItems.length - 1]);
let previousElement = jQuery(allItems[allItems.length - 1]);
let i = allItems.length - 2;
for (i; i >= 0; i--) {
@ -198,7 +198,7 @@ export class QueryMenuService {
return previousElement;
}
else {
previousElement = angular.element(allItems[i]);
previousElement = jQuery(allItems[i]);
}
}

@ -91,8 +91,8 @@ export class WorkPackageQuerySelectDropdownComponent implements OnInit {
private setupAutoCompletion(autocompleteValues:IAutocompleteItem[]) {
this.defineJQueryQueryComplete();
let input = angular.element('#query-title-filter') as IQueryAutocompleteJQuery;
let noResults = angular.element('.query-select-dropdown--no-results');
let input = jQuery('#query-title-filter') as IQueryAutocompleteJQuery;
let noResults = jQuery('.query-select-dropdown--no-results');
input.querycomplete({
delay: 0,

@ -153,7 +153,7 @@ describe('UrlParamsHelper', function() {
pageSize: 100
};
expect(angular.equals(decodedQueryParams, expected)).to.be.true;
expect(_.equals(decodedQueryParams, expected)).to.be.true;
});
});
@ -234,7 +234,7 @@ describe('UrlParamsHelper', function() {
pageSize: 100
};
expect(angular.equals(v3Params, expected)).to.be.true;
expect(_.equals(v3Params, expected)).to.be.true;
});
it('decodes string object filters', function() {
@ -286,7 +286,7 @@ describe('UrlParamsHelper', function() {
sortBy: '[]'
};
expect(angular.equals(v3Params, expected)).to.be.true;
expect(_.isEqual(v3Params, expected)).to.be.true;
});
});
});

@ -46,11 +46,11 @@ export class UrlParamsHelperService {
}
var parts:string[] = [];
angular.forEach(params, function (value, key) {
_.each(params, (value, key) => {
if (!value) return;
if (!Array.isArray(value)) value = [value];
angular.forEach(value, function (v) {
_.each(value, (v) => {
if (v !== null && typeof v === 'object') {
v = JSON.stringify(v);
}
@ -159,7 +159,7 @@ export class UrlParamsHelperService {
// the array check is only there for backwards compatibility reasons.
// Nowadays, it will always be an array;
var vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v];
angular.extend(attributes, { values: vs });
_.extend(attributes, { values: vs });
}
const filterData:any = {};
filterData[urlFilter.n] = attributes;
@ -202,7 +202,7 @@ export class UrlParamsHelperService {
// Sortation
queryData.sortBy = this.buildV3GetSortByFromQuery(query);
return angular.extend(queryData, additionalParams);
return _.extend(queryData, additionalParams);
}
public queryFilterValueToParam(value:any) {

@ -68,7 +68,7 @@ export class WpRelationParentComponent implements OnInit, OnDestroy {
this.wpRelationsHierarchyService.changeParent(this.workPackage, newParentId)
.then((updatedWp:WorkPackageResource) => {
setTimeout(() => angular.element('#hierarchy--parent').focus());
setTimeout(() => jQuery('#hierarchy--parent').focus());
})
.catch((err:any) => {
this.wpNotificationsService.handleErrorResponse(err, this.workPackage);
@ -86,7 +86,7 @@ export class WpRelationParentComponent implements OnInit, OnDestroy {
.then(() => {
this.wpNotificationsService.showSave(this.workPackage);
setTimeout(() => {
angular.element('#hierarchy--add-parent').focus();
jQuery('#hierarchy--add-parent').focus();
});
});
}

@ -111,7 +111,7 @@ export class WorkPackageRelationsComponent implements OnInit, OnDestroy {
}
protected buildRelationGroups() {
if (!angular.isDefined(this.currentRelations)) {
if (_.isNil(this.currentRelations)) {
return;
}

@ -27,26 +27,32 @@
// ++
import {KeepTabService} from './keep-tab.service';
import {StateService} from '@uirouter/core';
import {StateService, Transition} from '@uirouter/core';
var expect = chai.expect;
describe('keepTab service', () => {
var $state:StateService;
var keepTab:KeepTabService;
var callback:(transition:any) => void;
var includes:any;
var $state:any = {
current: {
name: 'whatever',
},
includes: includes
};
var $transitions:any = {
onSuccess: (criteria:any, cb:(transition:any) => void) => callback = cb
};
var keepTab:KeepTabService = new KeepTabService($state, $transitions);
var defaults = {
showTab: 'work-packages.show.activity',
detailsTab: 'work-packages.list.details.overview'
};
beforeEach(angular.mock.module('openproject.wpButtons'));
beforeEach(angular.mock.inject((_$state_:any, _keepTab_:any) => {
$state = _$state_;
keepTab = _keepTab_;
}));
describe('when initially invoked, or when an unsupported route is opened', () => {
it('should have the correct default value for the currentShowTab', () => {
expect(keepTab.currentShowState).to.eq(defaults.showTab);
@ -60,7 +66,6 @@ describe('keepTab service', () => {
});
describe('when opening a show route', () => {
var includes:any;
beforeEach(() => {
includes = sinon.stub($state, 'includes');

@ -75,7 +75,7 @@ export class WorkPackageWatchersTabComponent implements OnInit, OnDestroy {
}
public ngOnInit() {
this.$element = angular.element(this.elementRef.nativeElement);
this.$element = jQuery(this.elementRef.nativeElement);
this.workPackageId = this.$transition.params('to').workPackageId;
this.wpCacheService.loadWorkPackage(this.workPackageId)

@ -48,7 +48,7 @@ export class WpTableConfigurationColumnsTab implements TabComponent {
}
ngOnInit() {
this.eeShowBanners = angular.element('body').hasClass('ee-banners-visible');
this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible');
this.selectedColumns.forEach((column:QueryColumn) => {
this.selectedColumnMap[column.id] = true;
});

@ -29,7 +29,7 @@ export class WpTableConfigurationFiltersTab implements TabComponent {
}
ngOnInit() {
this.eeShowBanners = angular.element('body').hasClass('ee-banners-visible');
this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible');
this.wpTableFilters
.onReady()
.then(() => this.filters = this.wpTableFilters.currentState.$copy());

@ -29,8 +29,8 @@ import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-r
import {WorkPackageTableTimelineService} from "../../wp-fast-table/state/wp-table-timeline.service";
import {Inject, Injectable} from "@angular/core";
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service";
import {HookServiceToken} from "core-app/angular4-transition-utils";
import {UrlParamsHelperService} from 'core-components/wp-query/url-params-helper';
import {HookService} from "core-app/modules/plugins/hook-service";
export type WorkPackageAction = {
text:string;
@ -71,7 +71,7 @@ export class WorkPackageContextMenuHelperService {
}
];
constructor(@Inject(HookServiceToken) private HookService:any,
constructor(private HookService:HookService,
private UrlParamsHelper:UrlParamsHelperService,
private wpTableTimeline:WorkPackageTableTimelineService,
private PathHelper:PathHelperService) {
@ -82,7 +82,7 @@ export class WorkPackageContextMenuHelperService {
var allowedActions = this.getAllowedActions(workPackage, permittedActionConstants);
angular.forEach(allowedActions, function(allowedAction) {
_.each(allowedActions, (allowedAction) => {
singularPermittedActions.push({
key: allowedAction.key,
text: allowedAction.text,
@ -103,7 +103,7 @@ export class WorkPackageContextMenuHelperService {
});
});
angular.forEach(permittedActions, (permittedAction:any) => {
_.each(permittedActions, (permittedAction:any) => {
bulkPermittedActions.push({
key: permittedAction.key,
text: permittedAction.text,
@ -132,14 +132,14 @@ export class WorkPackageContextMenuHelperService {
public getAllowedActions(workPackage:WorkPackageResource, actions:WorkPackageAction[]):WorkPackageAction[] {
var allowedActions:WorkPackageAction[] = [];
angular.forEach(actions, (action) => {
_.each(actions, (action) => {
if (workPackage.hasOwnProperty(action.link)) {
action.text = action.text || I18n.t('js.button_' + action.key);
allowedActions.push(action);
}
});
angular.forEach(this.HookService.call('workPackageTableContextMenu'), (action) => {
_.each(this.HookService.call('workPackageTableContextMenu'), (action) => {
if (workPackage.hasOwnProperty(action.link)) {
var index = action.indexBy ? action.indexBy(allowedActions) : allowedActions.length;
allowedActions.splice(index, 0, action)

@ -33,7 +33,6 @@ require('core-app/angular4-test-setup');
import {TableState} from 'core-components/wp-table/table-state/table-state';
import {ConfigurationDmService} from 'core-app/modules/hal/dm-services/configuration-dm.service';
import {async, inject, TestBed} from '@angular/core/testing';
import {$httpToken, $qToken} from 'core-app/angular4-transition-utils';
import {States} from 'core-components/states.service';
import {PaginationInstance} from 'core-components/table-pagination/pagination-instance';
import {IPaginationOptions, PaginationService} from 'core-components/table-pagination/pagination-service';
@ -79,9 +78,7 @@ describe('wpTablePagination Directive', () => {
HalResourceService,
ConfigurationDmService,
TableState,
I18nService,
{provide: $qToken, useValue: {}},
{provide: $httpToken, useValue: {}},
I18nService
]
}).compileComponents();
}));

@ -1,227 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {PaginationService} from 'core-components/table-pagination/pagination-service';
import {BehaviorSubject} from 'rxjs';
describe.skip('wpTablePagination Directive', function () {
var compile:any, element:any, rootScope:any, scope:any, paginationService:PaginationService, paginationOptions:any;
let state:any;
let subject:any;
var wpTablePagination:any;
beforeEach(angular.mock.module('openproject.workPackages.directives'));
beforeEach(angular.mock.module('openproject.templates'));
beforeEach(angular.mock.module('openproject.services', function($provide:any) {
wpTablePagination = {
observeOnScope: function(scope:ng.IScope) {
return subject;
}
};
$provide.constant('wpTablePagination', wpTablePagination);
}));
beforeEach(angular.mock.inject(function (_paginationService_:PaginationService) {
paginationService = _paginationService_;
}));
let setTotalResults = (total:number) => {
let current = subject.getValue();
pushState(total, current.current.perPage, current.current.page);
};
let pushState = (total:number, perPage:number, page:number) => {
subject.next({
total: total,
current: {
perPage: perPage,
page: page
}
});
scope.$apply();
};
beforeEach(inject(function ($rootScope:any, $compile:any) {
var html;
html = `
<wp-table-pagination> </wp-table-pagination>'
`;
element = angular.element(html);
rootScope = $rootScope;
scope = $rootScope.$new();
state = {
total: 100,
current: {
perPage: 10,
page: 1
}
};
subject = new BehaviorSubject(state);
sinon.stub(paginationService, 'loadPaginationOptions');
sinon.stub(paginationService, 'getPerPageOptions', () => [10, 100, 500, 1000]);
compile = function () {
subject = new BehaviorSubject(state);
$compile(element)(scope);
scope.$apply();
};
}));
describe('page ranges and links', function () {
/*
it('should display the correct page range', function () {
var pageString = function () {
return element.find('.pagination--range').text().trim();
};
compile();
setTotalResults(0);
expect(pageString()).to.equal('');
setTotalResults(11);
expect(pageString()).to.equal('(1 - 10/11)');
setTotalResults(663);
expect(pageString()).to.equal('(1 - 10/663)');
});
*/
/*describe('"next" link', function() {
it('hidden on the last page', function() {
state = {
total: 11,
current: {
perPage: 10,
page: 2
}
};
compile();
expect(element.find('.pagination--next-link').parent().hasClass('ng-hide')).to.be.true;
});
});*/
/*
it('should display correct number of page number links', function () {
var numberOfPageNumberLinks = function () {
return element.find('a[rel="next"]').length;
};
compile();
setTotalResults(1);
expect(numberOfPageNumberLinks()).to.eq(1);
setTotalResults(11);
expect(numberOfPageNumberLinks()).to.eq(2);
setTotalResults(59);
expect(numberOfPageNumberLinks()).to.eq(6);
setTotalResults(101);
expect(numberOfPageNumberLinks()).to.eq(7);
});
*/
});
describe('updating the state', function () {
let updateFromObject:any;
beforeEach(function() {
compile();
setTotalResults(20);
updateFromObject = sinon.spy();
wpTablePagination['updateFromObject'] = updateFromObject;
});
it('when showing a different page', function() {
element.find('a[rel="next"]:first').click();
expect(updateFromObject).to.have.been.calledWith({page: 2});
});
it('when selecting a different per page option', function() {
// click on first per-page anchor (current is not an anchor)
element.find('.pagination--options a:eq(0)').click();
expect(updateFromObject).to.have.been.calledWith({page: 1, perPage: 100});
});
});
describe('perPage options', function () {
describe('with no entries', function() {
beforeEach(function() {
compile();
setTotalResults(0);
});
it('should have no perPage options', function () {
var perPageOptions = element.find('.pagination--options');
expect(perPageOptions.text()).to.not.include('Per page:');
});
});
describe('with few entries', function() {
beforeEach(function() {
compile();
setTotalResults(1);
});
it('should have no perPage options', function () {
var perPageOptions = element.find('.pagination--options');
expect(perPageOptions.text()).to.not.include('Per page:');
});
});
describe('with multiple entries', function() {
beforeEach(function() {
compile();
setTotalResults(20);
});
it('should render perPage options', function () {
var perPageOptions = element.find('.pagination--options');
expect(perPageOptions.text()).to.include('Per page:');
});
});
});
});

@ -1,80 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import IScope = angular.IScope;
import {Observable} from 'rxjs';
import {Observer} from 'rxjs';
export function runInScopeDigest(scope:IScope, fn:() => void) {
if (scope.$root.$$phase !== '$apply' && scope.$root.$$phase !== '$digest') {
scope.$apply(fn);
} else {
fn();
}
}
export function scopedObservable<T>(scope:IScope, observable:Observable<T>):Observable<T> {
return Observable.create((observer:Observer<T>) => {
var disposable = observable.subscribe(
value => {
runInScopeDigest(scope, () => observer.next(value));
},
exception => {
runInScopeDigest(scope, () => observer.error(exception));
},
() => {
runInScopeDigest(scope, () => observer.complete());
}
);
scope.$on('$destroy', () => {
return disposable.unsubscribe();
});
});
}
export function asyncTest<T>(done:(error?:any) => void, fn:(value:T) => any):(T:any) => any {
return (value:T) => {
try {
fn(value);
done();
} catch (err) {
done(err);
}
};
}
export function scopeDestroyed$(scope:IScope):Observable<IScope> {
return Observable.create((s:Observer<IScope>) => {
scope.$on('$destroy', () => {
s.next(scope);
s.complete();
});
});
}

@ -26,8 +26,8 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {getUIRouter} from '@uirouter/angular-hybrid';
import {ExpressionService} from 'core-app/modules/common/xss/expression.service';
import {whenDebugging} from 'core-app/helpers/debug_output';
import {enableReactiveStatesLogging} from "reactivestates";
// Run the browser detection
require('expose-loader?bowser!bowser');
@ -45,7 +45,7 @@ var requireGlobals = require.context('./globals/', true, /\.ts$/);
requireGlobals.keys().forEach(requireGlobals);
// load I18n, depending on the html element having a 'lang' attribute
var documentLang = (angular.element('html').attr('lang') || 'en').toLowerCase();
var documentLang = (jQuery('html').attr('lang') || 'en').toLowerCase();
try {
require('angular-i18n/angular-locale_' + documentLang + '.js');
}
@ -53,17 +53,12 @@ catch(e) {
require('angular-i18n/angular-locale_en.js');
}
import {whenDebugging} from 'core-app/helpers/debug_output';
import {enableReactiveStatesLogging} from "reactivestates";
window.appBasePath = jQuery('meta[name=app_base_path]').attr('content') || '';
const meta = jQuery('meta[name=openproject_initializer]');
I18n.locale = meta.data('defaultLocale');
I18n.locale = meta.data('locale');
require('./layout');
var requireComponent = require.context('./components/', true, /^((?!\.(test|spec)).)*\.(js|ts|html)$/);
requireComponent.keys().forEach(requireComponent);

@ -1,34 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
// angular.module('openproject.layout.controllers')
// .controller('MainMenuController', [
// '$rootScope',
// '$window',
// require('./main-menu-controller')
// ]);

@ -1,86 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
module.exports = function($rootScope, $window) {
var self = this;
this.toggleNavigation = function(event) {
event.preventDefault();
event.stopPropagation();
if (!$rootScope.showNavigation) {
// main menu shall expand.
// Set focus on first visible main menu item.
//
// This needs to be called after AngularJS has rendered the menu, which happens some when after(!) we leave this
// method here. So we need to set the focus after a timeout.
setTimeout(function() {
jQuery('#main-menu [class*="-menu-item"]:visible').first().focus();
}, 100);
// On mobile the main menu shall close whenever you click outside the menu.
if ($window.innerWidth < 680) {
self.setupAutocloseMainMenu();
}
// Regain default width: Expand to default menu width if collapsed slimmer than default width.
var savedMainMenuWidth = parseInt(window.OpenProject.guardedLocalStorage("openProject-mainMenuWidth"));
if (savedMainMenuWidth < 230) {
document.documentElement.style.setProperty("--main-menu-width", '230px');
window.OpenProject.guardedLocalStorage("openProject-mainMenuWidth", '230');
}
}
$rootScope.showNavigation = !$rootScope.showNavigation;
$rootScope.$broadcast('openproject.layout.navigationToggled', $rootScope.showNavigation);
$window.sessionStorage.setItem('openproject:navigation-toggle',
!$rootScope.showNavigation ? 'collapsed' : 'expanded');
};
this.setupAutocloseMainMenu = function() {
jQuery('#main-menu').on('focusout.main_menu', function (event) {
jQuery('#main-menu').off('focusout.main_menu');
// Check that main menu is not closed and that the `focusout` event is not a click on an element that tries to close
// the menu anyways.
if (!$rootScope.showNavigation ||
jQuery.contains(document.getElementById('main-menu-toggle'), event.relatedTarget)) {
return;
}
// There might be a time gap between `focusout` and the focussing of the activeElement, thus we need a timeout.
setTimeout(function() {
if (!jQuery.contains(document.getElementById('main-menu'), document.activeElement)) {
// activeElement is outside of main menu.
self.toggleNavigation(new Event('onclick'));
}
}, 0);
});
}
};

@ -1,29 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
require('./controllers');

@ -79,8 +79,8 @@ export class FocusHelperService {
}
public focus(element:JQuery) {
var focusable = angular.element(this.getFocusableElement(element)),
$focusable = angular.element(focusable),
var focusable = jQuery(this.getFocusableElement(element)),
$focusable = jQuery(focusable),
isDisabled = $focusable.is('[disabled]');
if (isDisabled && !$focusable.attr('ng-disabled')) {

@ -46,7 +46,8 @@ export class FocusDirective implements AfterViewInit {
private updateFocus() {
if (this.condition) {
this.FocusHelper.focusElement(this.elementRef.nativeElement, this.priority);
const element = jQuery(this.elementRef.nativeElement);
this.FocusHelper.focusElement(element, this.priority);
}
}
}

@ -0,0 +1,91 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
import {AccessibleByKeyboardComponent} from "core-app/modules/a11y/accessible-by-keyboard.component";
import {async, TestBed} from "@angular/core/testing";
import {OpIcon} from "core-app/modules/common/icon/op-icon";
import {ComponentFixture} from "@angular/core/testing/src/component_fixture";
import {By} from "@angular/platform-browser";
import {DebugElement} from "@angular/core";
require('core-app/angular4-test-setup');
describe('opIcon Directive', function() {
let app:OpIcon;
let fixture:ComponentFixture<OpIcon>;
let element:DebugElement;
beforeEach(async(() => {
// noinspection JSIgnoredPromiseFromCall
TestBed.configureTestingModule({
declarations: [
OpIcon
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OpIcon);
app = fixture.debugElement.componentInstance;
element = fixture.debugElement;
app.iconClasses = 'icon-foobar icon-context';
fixture.detectChanges();
});
describe('without a title', function() {
it('should render an icon', function () {
const i = element.query(By.css('i'));
expect(i.nativeElement.tagName.toLowerCase()).to.equal('i');
expect(i.classes['icon-foobar']).to.be.true;
expect(i.classes['icon-context']).to.be.true;
expect(element.query(By.css('span'))).to.not.be.ok;
});
});
describe('with a title', function() {
beforeEach(function() {
app.iconTitle = 'blabla';
fixture.detectChanges();
});
it('should render icon and title', function() {
const i = element.query(By.css('i'));
const span = element.query(By.css('span'));
expect(i.nativeElement.tagName.toLowerCase()).to.equal('i');
expect(i.classes['icon-foobar']).to.be.true;
expect(i.classes['icon-context']).to.be.true;
expect(span.nativeElement.tagName.toLowerCase()).to.equal('span');
expect(span.nativeElement.textContent).to.equal('blabla');
});
});
});

@ -1,79 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
describe('opIcon Directive', function() {
let compile:Function;
let element:ng.IAugmentedJQuery;
let html:string;
beforeEach(angular.mock.module('openproject.uiComponents'));
beforeEach(inject(function($rootScope:ng.IRootScopeService, $compile:ng.ICompileService) {
html = `<op-icon icon-classes="icon-foobar icon-context"></op-icon>`;
compile = function() {
element = angular.element(html);
$compile(element)($rootScope);
$rootScope.$digest();
};
}));
describe('without a title', function() {
beforeEach(function () {
compile();
});
it('should render an icon', function () {
const i = element.find('i');
expect(i[0].tagName.toLowerCase()).to.equal('i');
expect(i.hasClass('icon-foobar')).to.be.true;
expect(i.hasClass('icon-context')).to.be.true;
expect(element.find('span').length).to.equal(0);
});
});
describe('with a title', function() {
beforeEach(function() {
html = `<op-icon icon-title="blabla" icon-classes="icon-foobar icon-context"></op-icon>`;
compile();
});
it('should render icon and title', function() {
const i = element.find('i');
const span = element.find('span');
expect(i[0].tagName.toLowerCase()).to.equal('i');
expect(i.hasClass('icon-foobar')).to.be.true;
expect(i.hasClass('icon-context')).to.be.true;
expect(span[0].tagName.toLowerCase()).to.equal('span');
expect(span.text()).to.equal('blabla');
});
});
});

@ -1,37 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular
.module('openproject.workPackages.filters')
.filter('latestItems', latestItems);
function latestItems() {
return function(items, visible, reverse) {
return reverse ? items.slice(-visible).reverse() : items.slice(0,visible);
};
}

@ -1,53 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
describe('Latest items filter', function() {
beforeEach(angular.mock.module('openproject.workPackages.filters'));
describe('latestItems', function() {
var items;
beforeEach(function(){
items = [1,2,3,4,5,6,7,8,9];
});
it('should be defined', inject(function($filter) {
expect($filter('latestItems')).not.to.equal(null);
}));
it('should return the first 3 items', inject(function($filter) {
expect($filter('latestItems')(items, 3, true)).to.eql([9,8,7]);
}));
it('should return the last 3 items reversed', inject(function($filter) {
expect($filter('latestItems')(items, 3)).to.eql([1,2,3]);
}));
});
});

@ -1,60 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
function lazy (obj:any, property:string, getter:{():any}, setter?:{(value:any):void}):void {
if (angular.isObject(obj)) {
let done = false;
let value:any;
let config:any = {
get() {
if (!done) {
value = getter();
done = true;
}
return value;
},
set: ():void => undefined,
configurable: true,
enumerable: true
};
if (setter) {
config.set = (val:any) => {
value = setter(val);
done = true;
};
}
Object.defineProperty(obj, property, config);
}
}
angular
.module('openproject.services')
.factory('lazy', () => lazy);

@ -1,42 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
describe('loadingIndicator service', () => {
var loadingIndicator:any;
beforeEach(angular.mock.module('openproject.services'));
beforeEach(angular.mock.inject((_loadingIndicator_:any) => {
loadingIndicator = _loadingIndicator_;
}));
it('should exist', () => {
expect(loadingIndicator).to.exist;
});
});

@ -31,18 +31,10 @@
import {AuthorisationService} from './model-auth.service';
describe('authorisationService', function() {
var authorisationService:AuthorisationService, $rootScope:ng.IRootScopeService, query:any;
beforeEach(angular.mock.module('openproject.services'));
beforeEach(inject(function(_authorisationService_:AuthorisationService, _$rootScope_:ng.IRootScopeService){
authorisationService = _authorisationService_;
$rootScope = _$rootScope_;
}));
let authorisationService:AuthorisationService = new AuthorisationService();
describe('model action authorisation', function () {
beforeEach(function(){
beforeEach(function() {
authorisationService.initModelAuth('query', {
create: '/queries'
});

@ -47,7 +47,7 @@ export class DatePicker {
const firstDayOfWeek = this.ConfigurationService.startOfWeekPresent() ?
this.ConfigurationService.startOfWeek() : (jQuery.datepicker as any)._defaults.firstDay;
var mergedOptions = angular.extend({}, options, {
var mergedOptions = _.extend({}, options, {
firstDay: firstDayOfWeek,
showWeeks: true,
changeMonth: true,

@ -66,6 +66,8 @@ import {PersistentToggleDirective} from "core-app/modules/common/persistent-togg
import {HighlightColDirective} from "core-app/modules/common/highlight-col/highlight-col.directive";
import {CopyToClipboardDirective} from "core-app/modules/common/copy-to-clipboard/copy-to-clipboard.directive";
import {WikiToolbarDirective} from "core-app/modules/common/wiki-toolbar/wiki-toolbar.directive";
import {RefreshOnFormChangesDirective} from "core-app/modules/common/remote/refresh-on-form-changes.directive";
import {RemoteFieldUpdaterDirective} from "core-app/modules/common/remote/remote-field-updater.directive";
@NgModule({
imports: [
@ -107,6 +109,8 @@ import {WikiToolbarDirective} from "core-app/modules/common/wiki-toolbar/wiki-to
ShowSectionDropdownComponent,
AutocompleteSelectDecorationComponent,
CollapsibleSectionComponent,
RefreshOnFormChangesDirective,
RemoteFieldUpdaterDirective,
],
declarations: [
OpDatePickerComponent,
@ -147,6 +151,8 @@ import {WikiToolbarDirective} from "core-app/modules/common/wiki-toolbar/wiki-to
PersistentToggleDirective,
CopyToClipboardDirective,
WikiToolbarDirective,
RefreshOnFormChangesDirective,
RemoteFieldUpdaterDirective,
],
entryComponents: [
OpDateTimeComponent,

@ -29,12 +29,7 @@
import {PathHelperService} from './path-helper.service';
describe('PathHelper', function() {
var PathHelper:PathHelperService;
beforeEach(angular.mock.module('openproject.helpers'));
beforeEach(inject(function(_PathHelper_:PathHelperService) {
PathHelper = _PathHelper_;
}));
var PathHelper:PathHelperService = new PathHelperService();
context('apiV3', function() {
var projectIdentifier = 'majora';

@ -28,20 +28,15 @@
import {DisplayField} from "core-app/modules/fields/display/display-field.module";
import * as angular from 'angular';
import {$localeToken} from 'core-app/angular4-transition-utils';
export class FloatDisplayField extends DisplayField {
private $locale:angular.ILocaleService = this.$injector.get($localeToken);
public get valueString():string {
if (this.value == null) {
return '';
}
return this.value.toLocaleString(
this.$locale.id,
this.I18n.locale,
{ useGrouping: true, maximumFractionDigits: 20 }
);
}

@ -33,8 +33,8 @@ export class FormattableDisplayField extends DisplayField {
protected ExpressionService:ExpressionService = this.$injector.get(ExpressionService);
public render(element:HTMLElement, displayText:string):void {
angular.element(element).addClass('-multiline');
angular.element(element).addClass('read-value--html');
jQuery(element).addClass('-multiline');
jQuery(element).addClass('read-value--html');
let span = document.createElement('span');
span.innerHTML = displayText;

@ -27,7 +27,6 @@
// ++
import {ConfigurationService} from 'core-app/modules/common/config/configuration.service';
import {TextileServiceToken} from 'core-app/angular4-transition-utils';
import {TextileService} from "core-app/modules/common/textile/textile-service";
import {ICkeditorStatic} from "core-components/ckeditor/op-ckeditor-form.component";
import {EditField} from "core-app/modules/fields/edit/edit.field.module";
@ -44,7 +43,7 @@ declare global {
export class FormattableEditField extends EditField {
// Dependencies
readonly textileService:TextileService = this.$injector.get(TextileServiceToken);
readonly textileService:TextileService = this.$injector.get(TextileService);
readonly AutoCompleteHelper:AutoCompleteHelperService = this.$injector.get(AutoCompleteHelperService);
readonly ConfigurationService:ConfigurationService = this.$injector.get(ConfigurationService);

@ -132,7 +132,7 @@ export function initializeHalProperties<T extends HalResource>(halResourceServic
const sourceName = '_' + name;
const sourceObj = halResource.$source[sourceName];
if (angular.isObject(sourceObj)) {
if (_.isObject(sourceObj)) {
Object.keys(sourceObj).forEach(propName => {
OpenprojectHalModuleHelpers.lazy((halResource)[instanceName],
propName,
@ -160,7 +160,7 @@ export function initializeHalProperties<T extends HalResource>(halResourceServic
}
if (_.isObject(element)) {
angular.forEach(element, (child:any, name:string) => {
_.each(element, (child:any, name:string) => {
if (child && (child._embedded || child._links)) {
OpenprojectHalModuleHelpers.lazy(element,
name,

@ -26,13 +26,11 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {OpenprojectHalModuleHelpers} from "core-app/modules/hal/helpers/lazy-accessor";
describe('lazy service', () => {
var lazy:any;
var lazy = OpenprojectHalModuleHelpers.lazy;
beforeEach(angular.mock.module('openproject.services'));
beforeEach(angular.mock.inject((_lazy_:any) => {
lazy = _lazy_;
}));
it('should exist', () => {
expect(lazy).to.exist;
@ -84,7 +82,7 @@ describe('lazy service', () => {
});
it('should do nothing if the target is not an object', () => {
let obj = null;
let obj:any = null;
lazy(obj, 'prop', () => '');
expect(obj).to.not.be.ok;
});

@ -112,7 +112,7 @@ describe('WorkPackage', () => {
describe('when retrieving `canAddAttachment`', () => {
beforeEach(createWorkPackage);
const expectValue = (value:any, prepare = () => angular.noop()) => {
const expectValue = (value:any, prepare = () => _.noop) => {
value = value.toString();
beforeEach(prepare);
@ -140,7 +140,7 @@ describe('WorkPackage', () => {
describe('when the work work package has an `addAttachment` link', () => {
expectValue(true, () => {
workPackage.$links.addAttachment = <any> angular.noop;
workPackage.$links.addAttachment = <any> _.noop;
});
});
});

@ -248,7 +248,7 @@ export class WorkPackageResource extends HalResource {
private performUpload(files:UploadFile[]):UploadResult {
const href = this.attachments.$href!;
// TODO upgrade
const opFileUpload:OpenProjectFileUploadService = angular.element('body').injector().get('opFileUpload');
const opFileUpload:OpenProjectFileUploadService = jQuery('body').injector().get('opFileUpload');
return opFileUpload.upload(href, files);
}

@ -27,13 +27,10 @@
//++
require('./init-angularjs');
require('angular-mocks/ngMock');
require('jquery-mockjax')(jQuery, window);
var requireComponent;
window.$injector = angular.injector(['ng', 'ngMock', 'openproject.uiComponents', 'openproject.services']);
requireComponent = require.context('../tests/unit/tests/', true, /test\.(js|ts)$/);
requireComponent.keys().forEach(requireComponent);

@ -1,45 +0,0 @@
{
"projects": [
{
"identifier": "bums",
"created_on": "2012-12-18T07:00:17Z",
"level": 0,
"updated_on": "2012-12-18T09:09:10Z",
"name": "Bums zzz",
"id": 3
},
{
"identifier": "things",
"created_on": "2012-12-14T14:01:27Z",
"level": 0,
"updated_on": "2012-12-14T14:01:27Z",
"name": "Things",
"id": 1
},
{
"identifier": "things-bums",
"created_on": "2012-12-18T06:59:50Z",
"level": 1,
"updated_on": "2012-12-18T14:26:05Z",
"name": "Thingsb-Bums",
"id": 2
},
{
"identifier": "bums-bums",
"created_on": "2012-12-18T08:57:46Z",
"level": 2,
"updated_on": "2012-12-18T08:57:46Z",
"name": "Bums Bums",
"id": 5
},
{
"identifier": "zzz",
"created_on": "2012-12-18T08:57:14Z",
"level": 0,
"updated_on": "2012-12-18T08:57:14Z",
"name": "ZZZ",
"id": 4
}
],
"size": 5
}

@ -1,103 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
function objectSortationEqual(array1, array2) {
if (array1.length !== array2.length) {
return false;
}
var i;
for (i = 0; i < array2.length; i += 1) {
if (array1[i].id !== array2[i].id || array1[i].name !== array2[i].name) {
return false;
}
}
return true;
}
function objectsortation() {
var givenSortation = arguments;
return function (arr) {
return objectSortationEqual(arr, givenSortation);
};
}
function sortById(a, b) {
return a.id > b.id;
}
function objectContainsAll(givenArray) {
var givenObjects;
if (arguments.length === 1 && givenArray instanceof Array) {
givenObjects = givenArray;
} else {
givenObjects = Array.prototype.slice.call(arguments);
}
givenObjects.sort(sortById);
return function (arr) {
arr.sort(sortById);
return objectSortationEqual(arr, givenObjects);
};
}
var a = function () {
return new attributeBuilder();
};
var attributeBuilder = function () {};
var w = this;
function addProperty(obj, attr) {
Object.defineProperty(obj, "s" + attr,
{
get: function () {
return function (val) {
this[attr] = val;
return this;
};
}, configurable: true
}
);
}
var properties = ["id", "name", "identifier"];
var i;
for (i = 0; i < properties.length; i += 1) {
addProperty(attributeBuilder.prototype, properties[i]);
}
attributeBuilder.prototype.b = function () {
return this._result;
};

@ -1,82 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
/*jshint expr: true*/
describe('MainMenuController', function() {
var rootScope, sessionStorage, ctrl;
beforeEach(angular.mock.module('openproject.layout.controllers'));
beforeEach(inject(function($rootScope, $controller) {
rootScope = $rootScope.$new();
event = new Event('conclick');
var fakeSession = {};
sessionStorage = {
setItem: function(k, v) { fakeSession[k] = v; },
getItem: function(k) { return fakeSession[k]; }
};
ctrl = $controller("MainMenuController", {
$rootScope: rootScope,
$window: { sessionStorage: sessionStorage }
});
}));
describe('toggleNavigation', function() {
it('should toggle navigation off', function() {
rootScope.showNavigation = true;
ctrl.toggleNavigation(event);
expect(rootScope.showNavigation).to.be.false;
});
it('should toggle navigation on', function() {
rootScope.showNavigation = false;
ctrl.toggleNavigation(event);
expect(rootScope.showNavigation).to.be.true;
});
it('should fire an event when toggled', function() {
var callback = sinon.spy();
rootScope.$on('openproject.layout.navigationToggled', callback);
ctrl.toggleNavigation(event);
expect(callback).to.have.been.calledWithMatch(sinon.match.any, sinon.match.truthy);
});
it('should persist choice to sessionStorage', function() {
expect(sessionStorage.getItem('openproject:navigation-toggle')).to.be.undefined;
ctrl.toggleNavigation(event);
expect(sessionStorage.getItem('openproject:navigation-toggle')).to.equal('expanded');
ctrl.toggleNavigation(event);
expect(sessionStorage.getItem('openproject:navigation-toggle')).to.equal('collapsed');
});
});
});
Loading…
Cancel
Save