diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 309640f2de..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,62 +0,0 @@ -version: 2 - -jobs: - test: - working_directory: ~/alfresco-content-app - docker: - - image: circleci/node:8-browsers - steps: - - checkout - - restore_cache: - key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }} - - run: npm install - - save_cache: - key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }} - paths: - - "node_modules" - - run: xvfb-run -a npm run test:ci - lint: - working_directory: ~/alfresco-content-app - docker: - - image: circleci/node:8-browsers - steps: - - checkout - - restore_cache: - key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }} - - run: npm install - - run: npm run lint - spellcheck: - working_directory: ~/alfresco-content-app - docker: - - image: circleci/node:8-browsers - steps: - - checkout - - restore_cache: - key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }} - - run: npm install - - run: npm run spellcheck - build: - working_directory: ~/alfresco-content-app - docker: - - image: circleci/node:8-browsers - steps: - - checkout - - restore_cache: - key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }} - - run: npm install - - run: npm run build - -workflows: - version: 2 - build_and_test: - jobs: - - test - - lint: - requires: - - test - - spellcheck: - requires: - - test - - build: - requires: - - test diff --git a/.gitignore b/.gitignore index e2df279f2a..a5dfadcc8d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ /dist /tmp /out-tsc -/src/versions.json # dependencies /node_modules diff --git a/.travis.yml b/.travis.yml index 5ee60be5dd..a9230e1b16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,6 @@ services: - docker addons: chrome: stable - artifacts: - paths: - - e2e-output - target_paths: - - /${TRAVIS_BUILD_NUMBER} language: node_js node_js: - '8' @@ -34,7 +29,7 @@ jobs: - bash <(curl -s https://codecov.io/bash) -X gcov - stage: e2e name: 'Nginx' - script: npm run build && npm run e2e:docker + script: npm run build.e2e && npm run e2e:docker - stage: e2e name: 'Tomcat' script: npm run build.tomcat.e2e && npm run docker.tomcat.e2e diff --git a/.vscode/settings.json b/.vscode/settings.json index b4f4f4d5c0..5e62875a92 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,9 +4,6 @@ "typescript.preferences.quoteStyle": "single", "typescript.preferences.importModuleSpecifier": "relative", "editor.formatOnSave": true, - "[html]": { - "editor.formatOnSave": false - }, "[json]": { "editor.formatOnSave": false } diff --git a/README.md b/README.md index 7e4317babb..25fbabd77c 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,24 @@

Alfresco - make business flow

-# Example Content Application +# Alfresco Content Application Testing Supported By
BrowserStack ## Introduction -The Alfresco Content Application is an example application built using +The Alfresco Content Application a file management application built using [Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components and was generated with [Angular CLI](https://github.com/angular/angular-cli). -### Who is this example application for +### Who is this application for -This project demonstrates how to construct an application for Alfresco Content Services using the Alfresco ADF and it represents a meaningful composition of ADF components that provide end users with a simple easy to use interface for working with files in the content repository. +The Content Application is a streamlined experience for end users on top of Alfresco Content Services, focused on file management within the content repository. It provides developers with an easily extendable environment for lightening fast custom application development by providing safe ways to inject custom controls, viewer components, pages and plug-ins without upgrade concerns. ### Where to get help There are a number of resources available to help get you started with the Content App and the ADF: * [Content App Documentation](https://alfresco.github.io/alfresco-content-app/) * [Alfresco ADF Documentation](https://alfresco.github.io/adf-component-catalog/) * [Alfresco Community](https://community.alfresco.com/) -* [ADF Gitter Channel](https://gitter.im/Alfresco/alfresco-ng2-components) To get help on Angular CLI use ng help or read the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). @@ -28,42 +27,49 @@ Isuses can be raised in GitHub or in the Alfresco JIRA project. Please include a clear description, steps to reproduce and screenshots where appropriate.All issues will be reviewed; bugs will be categorized if reproducible and enhancement/feature suggestions will be considered against existing priorities if the use case serves a general-purpose need. #### Features added in the latest release -* Application Extensibility - Phase 2 - * Document list presets - * File viewer actions - * Create menu button - * Application header - * Metadata card configuration - * see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details. -* Sharing Files - * Set expiry date for shared links - * Right click action to access shared link settings - * Automatic disable of sharing based on respository configuration +* Library Management + * For end users: Join and favorite libraries +* New search input to find Libraries, files and/or folders +* Updated action toolbar, right click context menu and refresh of some icons Please refer to the [release notes](https://github.com/Alfresco/alfresco-content-app/releases) for details of all changes. -#### High level features planned for H2 2018 (July - December) -* Library Management - * For end users: Find, join and favorite libraries +#### High level feature themes planned for 2019 +* Collaboration & File Management + * Edit offline, and edit online with Alfresco Office Services (AOS) + * Folder rule creation + * File/Folder linking via secondary association +* File Library Management * For managers: Create libraries, manage users and requests to join - +* Single Sign On + * Support for Alfresco Identity Service +* Enhanced UI and user experiance + * Search result facet improvements + * Search query input assistance + * Metadata information drawer enhancements + ### Want to help? Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing](https://github.com/Alfresco/alfresco-content-app/blob/master/CONTRIBUTING.md) and then check out one of our issues in the [Jira](https://issues.alfresco.com/jira/projects/ACA) or [GitHub](https://github.com/Alfresco/alfresco-content-app/issues) ## Available Features | Feature | Description | |------------------|----------------------------------------------------------------| -| Document List | Folder/File browsing of Personal Files, and File Libraries | +| My Files | Folder/File browsing of Personal Files.| +| File Libraries | Create, find, join and browse the file libraries of sites created in the repository.| | Shared Files | Lists all files that have shared. | -| Recent Files | List files created and/or modified by the logged users within the last 30 days| +| Recent Files | List files created and/or modified by the logged users within the last 30 days.| | Favorites | Lists all favorited files for the user. | | Trash | Lists all deleted items stored in the trash can, users can restore or permanently remove. Admin user will see items deleted by all users.| | Upload | Files and folders can be uploaded through the New button or by dragging and dropping into the browser.| | Search | Quick search with live results, and full faceted search results page.| -| Actions | A number of actions can be performed on files and/or folders, either individually or multiples at a time| -| Viewer | Viewing files in natively in the browser, unsupported formats are transformed by the repository | +| Actions | A number of actions can be performed on files and/or folders, either individually or multiples at a time.| +| Viewer | Viewing files in natively in the browser, unsupported formats are transformed by the repository. | | Metadata | The information drawer can be configured in the app.config.json to display metadata information, by default file the Properties Aspect is shown and images will also include EXIF information.| +| File Sharing | Share files, with time expiry if required, externally with uniquely generated URLs.| | Versioning | The version manager provides access and management of previous file versions, and the ability to upload new versions.| +| Permissions | Granular user permission management of the folders and files throughout the repository.| +| Extensibility | The application provides safe extension points allowing full customiation; see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details. | + ## Further help diff --git a/angular.json b/angular.json index bde639d7d6..2961f4555b 100644 --- a/angular.json +++ b/angular.json @@ -25,7 +25,6 @@ "src/assets", "src/favicon-96x96.png", "src/app.config.json", - "src/versions.json", { "glob": "**/*", "input": "node_modules/@alfresco/adf-core/prebuilt-themes", @@ -85,6 +84,23 @@ "with": "src/environments/environment.prod.ts" } ] + }, + "e2e": { + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.e2e.ts" + } + ] } } }, @@ -99,6 +115,9 @@ "configurations": { "production": { "browserTarget": "app:build:production" + }, + "e2e": { + "browserTarget": "app:build:e2e" } } }, @@ -135,7 +154,6 @@ "src/assets", "src/favicon-96x96.png", "src/app.config.json", - "src/versions.json", { "glob": "**/*", "input": "node_modules/@alfresco/adf-core/prebuilt-themes", @@ -167,7 +185,8 @@ "src/tsconfig.spec.json" ], "exclude": [ - "**/node_modules/**" + "**/node_modules/**", + "package.json" ] } } @@ -197,7 +216,8 @@ "e2e/tsconfig.e2e.json" ], "exclude": [ - "**/node_modules/**" + "**/node_modules/**", + "package.json" ] } } diff --git a/build-tomcat-e2e.sh b/build-tomcat-e2e.sh index 849e7a4640..10ac86f0f0 100755 --- a/build-tomcat-e2e.sh +++ b/build-tomcat-e2e.sh @@ -1,4 +1,4 @@ -npm run build -- --base-href ./ +npm run build.e2e -- --base-href ./ node -e " const fs = require('fs'); diff --git a/cspell.json b/cspell.json index c0c95e14fe..b63c3393aa 100644 --- a/cspell.json +++ b/cspell.json @@ -6,6 +6,7 @@ "sharedlinks", "Redistributable", "fullscreen", + "LGPL", "ngrx", "ngstack", @@ -34,6 +35,7 @@ "nginx", "docx", "SOLR", + "simpletask", "unshare", "qshare", @@ -54,7 +56,8 @@ "keycodes", "denysvuika", "submenu", - "submenus" + "submenus", + "simpletask" ], "dictionaries": ["html", "en-gb", "en_US"] } diff --git a/docker-compose/nginx.conf b/docker-compose/nginx.conf index 1abbbf0b9e..816f208655 100644 --- a/docker-compose/nginx.conf +++ b/docker-compose/nginx.conf @@ -5,6 +5,8 @@ server { proxy_pass_request_headers on; proxy_pass_header Set-Cookie; + access_log off; + location / { proxy_pass http://content-app; diff --git a/docs/README.md b/docs/README.md index bcc2ee089c..1dc9b6cea6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,416 +1,19 @@ -# Alfresco Example Content Application +# Alfresco Content Application -## Introduction +The Alfresco Content Application a file management application built using +[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components and was generated with [Angular CLI](https://github.com/angular/angular-cli). -The Alfresco Content Application is an example application built using -[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components. +## Documentation -### Who is this example application for? +The documentation is divided into the following sections: -This example application demonstrates to Angular software engineers -how to construct a content application using the Alfresco ADF. - -This example application represents a meaningful composition of ADF components that provide end users -with a simple and easy to use interface for working with files stored in the Alfresco Content Services repository. - -### Prerequisites - -This application was created based on the latest releases from Alfresco: - -- [Alfresco ADF (2.6.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started) -- [Alfresco Content Services (6.0.0)](https://www.alfresco.com/platform/content-services-ecm) - or [Alfresco Community Edition (6.0 - General Release: 201806)](https://www.alfresco.com/products/community/download) - -

-You also need node.js (LTS) installed to build it locally from source code. -

- -**Note:** The latest version of the Alfresco Content platform is required due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments. - -## Features - -The concept of this example is a simple user interface which makes accessing files in the Alfresco Content Services repository easy. - -Often Content Management systems provide more capabilities out of the box than most users need; -providing too many capabilities to these users prevents them from working efficiently, -so they may end up using unsanctioned file management solutions which presents a proliferation of content storage -and collaboration solutions as well as compliance issues for organizations. - -This application demonstrates how the complexity of Content Management can be simplified -using the Alfresco Application Development Framework to easily and quickly create custom solutions for specific user cases. - -### User Interface - layout - -There are three main areas of the application controlled by the [Layout component](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/layout): - -1. [Application Header](#header) -2. [Side Navigation](#side-navigation) -3. [Document List](#document-list-layout) - -![Features](images/features-01.png) - -### Header - -The application [header](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/header) has three main elements. - -1. [Logo and Color](#logo-and-color) -2. [Search](#search) -3. [Current User](#current-user) - -![Header](images/header.png) - -#### Logo and Color - -Logo & app primary color - logo and color are configurable by updating the -[app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) file in the root folder of the project. -Please refer to the [Application Configuration](/getting-started/configuration) documentation for more information on how to change the logo and color. - -#### Search - -The application [Search](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search) - -uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search). -The app provides a 'live' search feature, where users can open files and folders directly from the Search API results. - -![Search Input](images/search.png) - -If you type `Enter` in the text input area, you are going to see [Search Results](#search-results) page -with advanced filtering and faceted search. - -#### Current User - -[Current User](https://github.com/Alfresco/alfresco-content-app/tree/development/src/app/components/current-user) - -displays the user's name, and a menu where users can logout. -Optionally through updating the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) -a language switching menu can be displayed. - -![Current User](images/current-user.png) - -### Side Navigation - -The application [side navigation](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/sidenav) has two features: -a button menu and navigation links. - -![Side Navigation](images/side-nav.png) - -#### New button - -The New button displays a menu which provides three actions: - -- Create a new folder - provides a dialog which allows the creation of a new folder, the folder name is mandatory and the description is optional. -- Upload a file - invokes the operating system file browser and allows a user to select file(s) to upload into their current location in the content repository. -- Upload a folder - invokes the operating system folder browser and allows a user to select a folder to upload to their current location in the content repository. - -When an upload starts the [upload component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/upload) -is displayed which shows the user the progress of the uploads they have started. -The upload dialog persists on the screen and can be minimized; users are able to continue using the application whilst uploads are in progress -and uploads can be canceled which will stop uploads in progress or permanently delete already completed uploads. - -![Uploader](images/uploader.png) - -#### Navigation - -The navigation links are configurable via the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json). -Default configuration creates two sections. -See [Navigation](/getting-started/navigation) for more information about configuring the side navigation. - -### Document List Layout - -The main area of the application is composed of several individual ADF components: - -1. [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html) -2. [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html) -3. [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html) -4. [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html) - -![](images/doclist.png) - -The application has seven different Document List views which share commonalities between each view and subtle differences depending on the content being loaded which are explained below. - -#### Personal Files - -Personal Files retrieves all content from the logged in user's home area (`/User Homes//`) in the repository; -if the user is ‘admin’ who does not have a home folder then the repository root folder is shown. - -Personal Files is the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component, -using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes). - -#### File Libraries - -File Libraries retrieves all the sites that the user is a member of including what type of site it is: public, moderated or private. -File Libraries is the [Libraries](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/libraries) component, -using the [Sites API](https://api-explorer.alfresco.com/api-explorer/#/sites). - -When a user opens one of their sites then the content for the site's document library is shown. -To display the files and folders from a site (`/Sites//Document Library/`) the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component, -using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes) is used. - -#### Shared Files - -The Shared Files view aggregates all files that have been shared using the QuickShare feature in the content repository. -The [Shared Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/shared-files) component uses the [shared-links API](https://api-explorer.alfresco.com/api-explorer/#/shared-links) -and includes extra columns to display where the file is -[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) -in the content repository and who created the shared link. - -#### Recent Files - -The Recent Files view shows all the files that have been created or modified within the last 30 days by the current user. -The [Recent Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/recent-files) -component uses the Search API to query SOLR for changes made by the user and includes an extra column to display where the file is -[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) -in the content repository. - -#### Favorites - -The Favorites view shows all files and folders from the content repository that have been marked as a favorite by the current user. -The [Favorites](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/favorites) component uses the -[favorites](https://api-explorer.alfresco.com/api-explorer/#/favorites) API to retrieve all the favorite nodes for the user -and includes an extra column to display where the file is -[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) -in the content repository. - -#### Trash - -The Trash view shows all the items that a user has deleted, admin will see items deleted by all users. -The actions available in this view are Restore and Permanently Delete. -The [Trashcan](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/trashcan) component uses the -[trashcan](https://api-explorer.alfresco.com/api-explorer/#/trashcan) API to retrieve the deleted items -and perform the actions requested by the user and includes an extra column to display where the item was -[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) -in the content repository before it was deleted. - -#### Search Results - -The Search Results view shows the found items for a search query. It has a custom layout template and users can easily browse the results and perform actions on items. -For more information on the [SearchComponent](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search), please also check this [Search Results](#search-results1) section. - -#### Actions and the Actions Toolbar - -All the views incorporate the [toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html) -component from the Alfresco Application Development Framework. - -Actions are displayed in the toolbar when item(s) are selected, or a right click is performed; apart from the Trash view they all display the following actions when the current user has the necessary permissions, -actions are automatically hidden when the user does not have permission. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionFileFolder
View - Opens the selected file using the Preview component, - where the file cannot be displayed natively in a browser a PDF rendition is obtained from the repository. - Not applicable
DownloadDownloads single files to the user's computer, when multiple files are selected they are compressed into a ZIP and then downloaded.Folders are automatically compressed into a ZIP and then downloaded to the user's computer.
EditNot applicableThe folder name and description can be edited in a dialog.
Favorite - Toggle the favorite mark on or off for files and folders, when multiple items are selected - and one or more are not favorites then the mark will be toggled on. -
Copy - Files and folders can be copied to another location in the content repository using the - content-node-selector component; - once the copy action has completed the user is notified and can undo the action (which permanently deletes the created copies). -
Move - Files and folders can be moved to another location in the content repository using the - content-node-selector component; - once the move action has completed the user is notified and can undo the action (which moves the items back to the original location). -
Share - Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application. Share links can automatically expire based on a date, the minimum expiry date is controlled by the Content Services repository, which is 1 day from the date of creation. - - Not applicable. -
Delete - Files and folders can be deleted from their location in the content repository; - once the delete action has completed the user is notified and can undo the action (which restores the items from the trash). -
Manage Versions - Versions of files can be viewed, uploaded, restored, downloaded and deleted by using the version manager dialog; - once each action has completed the list of versions is updated according to the change. - Not applicable
Permissions - Permissions on a file can be adjusted as required in a number of ways; disable inheritance from the parent folder, change a user or groups role and grant users/groups access. - Not available
- -Besides the actions available in the toolbar users can -* single click on a file to view it, and a folder to open it -* single click an item to select it -* double click on a file to view it, and a folder to open it - -### File Viewer - -The File Viewer has been created using the [ViewerComponent](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html) from the ADF. The Viewer has four main areas: - -![File Viewer](images/File-Viewer.png) - -1. [Header & Toolbar](#header-and-toolbar) -2. [Content](#content) -3. [Thumbnails side pane](#thumbnails-side-pane) -4. [Viewer Controls](#viewer-controls) - -#### Header and Toolbar - -The Header & Toolbar section of the viewer contains a number of features that relate to the file currently being displayed: - -- Close 'X' will return the user to the folder that contains the file. -- The name and file type icon is shown in the middle. -- Next and previous buttons will be displayed either side of the file name so that users can navigate to other files in the folder without navigating away from the viewer. -- Finally, on the right hand side an actions toolbar provides users with the ability to download, favorite, move, copy, delete, manage versions and view info panel. - -#### Content - -The File Viewer consists of four separate views that handle displaying the content based on four types of content, covering various [file/mime](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html#supported-file-formats) types: - -- Document View: PDF files are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved. -- Image View: JPEG, PNG, GIF, BMP and SVG images are natively displayed in the application File Viewer. -- Media View: MP4, MP3, WAV, OGG and WEBM files are played natively application File Viewer. The File Viewer will download, by default, 50MB of the content at a time to ensure a smooth playback of the content. -- Text View: TXT, XML, JS, HTML, JSON and TS files are natively displayed as text in the application File Viewer. - -#### Thumbnails side pane - -The Document View includes a thumbnails pane which can be activated by a button in the Viewer Actions toolbar. Clicking on a thumbnail will take a user directly to the selected page and as users scroll through a document the current page is highlighted in the pane. - -#### Viewer Controls - -At the bottom of the content the Viewer Controls allow users to interact with the content in various ways; the actions available are dependant on the type of content being displayed. - -- Document View: - - Activate/Deactivate thumbnails pane - - Previous/Next page - - Jump to page number - - Zoom in/out - - Fit to page - - Print -- Image View: - - Zoom in/out - - Rotate left/right (does not alter content in the repository) - - Reset image - - Print -- Media View: - - Play/pause - - Timeline position - - Toggle audio - - Audio volume - - Full screen - -### Info Drawer - -The Info Drawer displays node information in the right sidebar panel. It is created by using the [InfoDrawerComponent](https://alfresco.github.io/adf-component-catalog/components/InfoDrawerComponent.html). This info is available for both folder and file nodes. - -Currently, there are 2 tabs available: Properties and Versions. - -#### Properties tab - -The Properties tab displays the node's metadata info by using the [ContentMetadataCardComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataCardComponent.html). - -![](images/content-metadata.png) - -For more information, please check also the [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html). - -#### Comments tab - -The Comments tab displays all comments made on the selected node in the respoistory by using the [CommentsComponent](https://alfresco.github.io/adf-component-catalog/components/CommentsComponent.html). Users can post new comments that will be displayed immediately. - -### Version Manager - -The versions of a file can be viewed & managed by using the [VersionManagerComponent](https://alfresco.github.io/adf-component-catalog/components/VersionManagerComponent.html). - -There are 2 ways users can access the Version Manager: - -1) From the 'Manage Versions' option of the 'More actions' menu (check [Actions and the Actions Toolbar](#actions-and-the-actions-toolbar)): - -![Version Manager Menu](images/version-manager-action.png) -![Version Manager Dialog](images/version-manager-dialog.png) - -2) From the [Info Drawer](#info-drawer) (the Details right panel): - -![Version Manager Inline](images/version-manager-tab.png) - -#### Upload new version - -A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mime type as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments. - -Please also check the [UploadVersionButtonComponent](https://alfresco.github.io/adf-component-catalog/components/UploadVersionButtonComponent.html). - -#### Actions Menu - -Each item in the version list has a couple of actions available: Restore, Download and Delete. These are displayed if user has permission to do that specific action. The 'Download' and 'Delete' can be also disabled from the app.config. - -In the app.config.json file, these are the current settings for the ACA version manager: - -```json -{ - "adf-version-manager": { - "allowComments": true, - "allowDownload": true - } -} -``` - -Set the allowComments to false if the version comments should not be displayed on the version list. - -Clicking to delete a version of a file triggers a confirmation dialog. Please see the [ConfirmDialogComponent](https://alfresco.github.io/adf-component-catalog/components/ConfirmDialogComponent.html) for more info. - -### Search Results - -Once you type the text in the Search Input component and press `Enter` you are going to see the Search Results page - -![Search Results](images/aca-search-results.png) - -This page consists of the following ADF components: - -- [Search Filter](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-filter.component.md) -- [Search Chip List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-chip-list.component.md) -- [Search Sorting Picker](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-sorting-picker.component.md) -- [Document List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/document-list.component.md) with custom layout template -- [Info Drawer](#info-drawer) with Metadata and [Version Management](#version-manager) -- [Toolbar with basic actions](#actions-and-the-actions-toolbar) like `Preview`, `Download`, `Favorite`, `Copy`, etc. - -And also the Info Drawer, Toolbar and Node Selector dialogs for copy and move operations. +- [Application features](/features/): Details of the user interface and app usage. +- [Getting started](/getting-started/): Configuration of the development environment and the app. +- [Extending](/extending/): How to extend the features of the app with your own code. +- [Tutorials](/tutorials/): Exploration of development techniques in depth. +- [Help](/help): Details of further help and support available. ## How to contribute diff --git a/docs/extending.md b/docs/extending.md deleted file mode 100644 index a1698b65ef..0000000000 --- a/docs/extending.md +++ /dev/null @@ -1,2086 +0,0 @@ ---- -title: Extending ---- - - - -

- Work is still in progress, the documentation and examples may change. -

- -# Extending - -Application extensibility is performed via the root `/src/assets/app.extensions.json`, -and any number of external plugins that are references of the main entry point. - -The application also comes with the `/src/assets/plugins/` folder -already preconfigured to store external files. - -You can create plugins that change, toggle, or extend the following areas: - -- Navigation sidebar links and groups -- Context Menu -- Sidebar (aka Info Drawer) -- Toolbar entries - - buttons - - menu buttons - - separators -- Viewer actions - - "Open With" entries - - toolbar entries - - buttons - - "More actions" buttons -- Content metadata presets (used on `Properties` tab) - -Extensions can also: - -- Overwrite or disable extension points of the main application or other plugins -- Change rules, actions or any visual element -- Register new application routes based on empty pages or layouts -- Register new rule evaluators, components, guards - -## Format - -The format is represented by a JSON file with the structure similar to the following: - -```json -{ - "$name": "app", - "$version": "1.0.0", - - "routes": [], - "actions": [], - "rules": [], - "features": {} -} -``` - -### Schema - -You can find the JSON schema at the project root folder: [extension.schema.json](https://github.com/Alfresco/alfresco-content-app/blob/master/extension.schema.json). - -

-The Schema allows you to validate extension files, provides code completion and documentation hints. -

- -```json -{ - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0" -} -``` - -### Multiple files - -You can have multiple extension files distributed separately. -All additional files are linked via the `$references` property. -The order of declaration defines the order of loading. - -```json -{ - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0", - "$references": ["plugin1.json", "plugin2.json"] -} -``` - -

-All extension files are merged together at runtime. -This allows plugins to overwrite the code from the main application or to alter other plugins. -

- -### Startup behavior - -First, the root `app.extensions.json` is loaded by means of the special `Loader` service. -The file can contain all the necessary declarations for an application to function. Extra plugin files are fully optional. - -Next, the `Loader` traverses the `$references` metadata and loads additional files if provided. -For the sake of speed the files are loaded in parallel, however once everything is loaded, they are applied in the order of declaration. - -After all the external files are fetched, the `Loader` sorts them, removes the metadata properties and stacks the resulting JSON objects on top of each other. - -

-Any top-level property name that starts with the `$` symbol is considered metadata and does not participate in the merge process. -That allows a plugin to carry extra information for maintenance and visualisation purposes, for example: `$name`, `$version`, `$description`, `$license`, etc. -

- -#### Merging properties - -There are no limits in the JSON structure and level of nesting. -All objects are merged into a single set based on property keys and object IDs (for arrays). - -Before: Plugin 1 - -```json -{ - "$name": "plugin1", - "plugin1.key": "value", - "plugin1.text": "string" -} -``` - -Before: Plugin 2 - -```json -{ - "$name": "plugin2", - "plugin2.key": "value", - "plugin1.text": "custom string" -} -``` - -Final result: - -```json -{ - "plugin1.key": "value", - "plugin1.text": "custom string", - "plugin2.key": "value" -} -``` - -Note that as a result we have two unique properties `plugin1.key` and `plugin2.key`, -and also a `plugin1.text` that was first defined in the `Plugin 1`, but then overwritten by the `Plugin 2`. - -

-JSON merging is a very powerful concept as it gives you the ability to alter any base application settings, -or toggle features in other plugins without rebuilding the application or corresponding plugin libraries. -

- -#### Merging objects - -The complex objects are merged by properties. This process is recursive and has no limits for nesting levels. - -Before: Plugin 1 - -```json -{ - "$name": "plugin1", - "features": { - "title": "some title", - "page1": { - "title": "page 1" - } - } -} -``` - -Before: Plugin 2 - -```json -{ - "$name": "plugin2", - "features": { - "page1": { - "title": "custom title" - }, - "page2": { - "title": "page 2" - } - } -} -``` - -Final result: - -```json -{ - "features": { - "title": "some title", - "page1": { - "title": "custom title" - }, - "page2": { - "title": "page 2" - } - } -} -``` - -You can see the unique properties get merged together in a single object. -However the last non-unique property overwrites the previous value. - -Using the current design it is not possible to delete any application property from the plugin. -The loader engine only supports overwriting values. Many components however support the `disabled` property you can change using an external definition: - -Before: Plugin 1 - -```json -{ - "$name": "plugin1", - "feature1": { - "disabled": false, - "text": "some-feature", - "icon": "some-icon" - } -} -``` - -Before: Plugin 2 - -```json -{ - "$name": "plugin2", - "feature1": { - "disabled": true - } -} -``` - -Final result: - -```json -{ - "feature1": { - "disabled": true, - "text": "some-feature", - "icon": "some-icon" - } -} -``` - -You can find more details in the [Disabling Content](#disabling-content) section - -#### Merging arrays - -The extension `Loader` provides a special support for merging Arrays. -By default, two collections will be merged into a single array unless objects have `id` properties. - -

-If array contains two objects with the same `id` property, the objects will be merged rather than appended. -

- -Before: Plugin 1 - -```json -{ - "$name": "plugin1", - "features": [ - { "text": "common 1" }, - { - "id": "page1", - "text": "page 1" - } - ] -} -``` - -Before: Plugin 2 - -```json -{ - "$name": "plugin2", - "features": [ - { "text": "common 2" }, - { - "id": "page1", - "text": "custom page" - } - ] -} -``` - -Final result: - -```json -{ - "features": [ - { "text": "common 1" }, - { "text": "common 2" }, - { - "id": "page1", - "text": "custom page" - } - ] -} -``` - -Note that objects with the same `page1` identifiers were merged while other unique entries were appended to the resulting array. - -### Disabling content - -Most of the schema elements can be switched off by using the `disabled` property: - -```json -{ - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0", - - "features": { - "create": [ - { - "id": "app.create.folder", - "disabled": true, - "order": 100, - "icon": "create_new_folder", - "title": "Create Folder" - } - ] - } -} -``` - -This feature becomes handy when you want to disable existing functionality from within the external plugin. - -In the example below, the plugin called `plugin1` replaces standard `app.create.folder` menu -exposed by the application with a custom one coming with the plugin. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "create": [ - { - "id": "app.create.folder", - "disabled": true, - ... - }, - { - "id": "plugin1.create.folder", - "title": "Create Folder", - ... - } - ] - } -} -``` - -## Routes - -To create a new route, populate the `routes` section with the corresponding entries. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "routes": [ - { - "id": "plugin1.routes.bin", - "path": "ext/bin", - "layout": "app.layout.main", - "component": "your.component.id" - } - ] -} -``` - -### Route properties - -| Name | Description | -| ------------- | -------------------------------------------------------------------------------------- | -| **id** | Unique identifier. | -| **path** | Runtime path of the route. | -| **component** | The main [component](#components) to use for the route. | -| layout | The layout [component](#components) to use for the route. | -| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. | -| data | Custom property bag to carry with the route. | - -Use the `app.layout.main` value for the `layout` property to get the default application layout, -with header, navigation sidebar and main content area. -You can register any component to back the `app.layout.main` value. - -

-By default, the `app.layout.main` is used if you do not specify any custom values. -Use `blank` if you want your route component take the whole page. -

- -You can define the full route schema like in the next example: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "routes": [ - { - "id": "plugin1.routes.bin", - "path": "ext/bin", - "component": "your.component.id", - "layout": "app.layout.main", - "auth": ["app.auth"], - "data": { - "title": "Custom Trashcan" - } - } - ] -} -``` - -

-All application routes require at least one authentication guard. -Defaults to the `['app.auth']` value. -

- -### Authentication Guards - -Below is the list of the authentication guards main application registers on startup. - -| Key | Type | Description | -| -------- | ------------ | ------------------------------------------------------------------------- | -| app.auth | AuthGuardEcm | ADF guard, validates ACS authentication and redirects to Login if needed. | - -You can refer those guards from within your custom extensions, -or [register](#registration) your custom implementations. - -## Components - -You can register any Angular component to participate in extensibility. - -The components are used to create custom: - -- routes and pages -- toolbar buttons -- menu items - -| Key | Type | Description | -| ---------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------- | -| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. | -| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. | -| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. | - -

-See [Registration](#registration) section for more details -on how to register your own entries to be re-used at runtime. -

- -Note that custom extensions can also replace any existing component at runtime by a known identifier, -besides registering a new one. - -## Actions - -| Name | Description | -| --------- | ------------------------------------------------------------------------------ | -| **id** | Unique identifier. | -| **type** | Action type, see [Application Actions](#application-actions) for more details. | -| _payload_ | Action payload, a string containing value or expression. | - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "actions": [ - { - "id": "plugin1.actions.settings", - "type": "NAVIGATE_URL", - "payload": "/settings" - }, - { - "id": "plugin1.actions.info", - "type": "SNACKBAR_INFO", - "payload": "I'm a nice little popup raised by extension." - }, - { - "id": "plugin1.actions.node-name", - "type": "SNACKBAR_INFO", - "payload": "$('Action for ' + context.selection.first.entry.name)" - } - ] -} -``` - -### Value expressions - -You can use light-weight expression syntax to provide custom parameters for the action payloads. - -```text -$() -``` - -Expressions are valid JavaScript blocks that evaluate to values. - -Examples: - -```text -$('hello world') // 'hello world' -$('hello' + ', ' + 'world') // 'hello, world' -$(1 + 1) // 2 -$([1, 2, 1 + 2]) // [1, 2, 3] -``` - -## Application Actions - -Application is using NgRx (Reactive libraries for Angular, inspired by Redux). -To get more information on NxRx please refer to the following resources: - -- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) - -Most of the application features are already exposed in the form of NgRx Actions and corresponding Effects. -You can invoke any action via a single `Store` dispatcher, similar to the following: - -```typescript -export class MyComponent { - constructor(private store: Store) {} - - onClick() { - this.store.dispatch(new SearchByTermAction('*')); - } -} -``` - -The code above demonstrates a simple 'click' handler that invokes `Search by Term` feature -and automatically redirects user to the **Search Results** page. - -Another example demonstrates viewing a node from a custom application service API: - -```typescript -export class MyService { - constructor(private store: Store) {} - - viewFile(node: MinimalNodeEntity) { - this.store.dispatch(new ViewFileAction(node)); - } -} -``` - -### Using with Extensions - -You can invoke every application action from the extensions, i.e. buttons, menus, etc. - -

-Many of the actions take currently selected nodes if no payload provided. -That simplifies declaring and invoking actions from the extension files. -

- -In the example below, we create a new entry to the "NEW" menu dropdown -and provide a new `Create Folder (plugin1)` command that invokes the `CREATE_FOLDER` application action. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "create": [ - { - "id": "plugin1.create.folder", - "type": "default", - "icon": "create_new_folder", - "title": "Create Folder (plugin1)", - "actions": { - "click": "CREATE_FOLDER" - } - } - ] - } -} -``` - -The `CREATE_FOLDER` action will trigger corresponding NgRx Effects to show the dialog -and perform document list reload if needed. - -Below is the list of public actions types you can use in the plugin definitions as a reference to the action: - -| Name | Payload | Description | -| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------- | -| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | -| SET_CURRENT_URL | string | Notify components about current browser URL. | -| SET_USER_PROFILE | Person | Assign current user profile. | -| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | -| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | -| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | -| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | -| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | -| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | -| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | -| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | -| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | -| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | -| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | -| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | -| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | -| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | -| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | -| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | -| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | -| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | -| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | -| NAVIGATE_URL | string | Navigate to a given route URL within the application. | -| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters) | -| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | -| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | -| NAVIGATE_LIBRARY | string | Navigate to library | -| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | -| SNACKBAR_INFO | string | Show information snackbar with the message provided. | -| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | -| SNACKBAR_ERROR | string | Show error snackbar with the message provided. | -| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | -| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | -| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | -| PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). | -| FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. | -| LOGOUT | n/a | Log out and redirect to Login screen | - -## Rules - -Rules allow evaluating conditions for extension components. -For example, you can disable or hide elements based on certain rules. - -Every rule is backed by a condition evaluator. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "rules": [ - { - "id": "app.trashcan", - "type": "app.navigation.isTrashcan" - } - ] -} -``` - -Rules can accept other rules as parameters: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "rules": [ - { - "id": "app.toolbar.favorite.canAdd", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.canAddFavorite" }, - { "type": "rule", "value": "app.navigation.isNotRecentFiles" }, - { "type": "rule", "value": "app.navigation.isNotSharedFiles" }, - { "type": "rule", "value": "app.navigation.isNotSearchResults" } - ] - } - ] -} -``` - -

-You can also negate any rule by utilizing a `!` prefix: -`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`. -

- -It is also possible to use inline references to registered evaluators without declaring rules, -in case you do not need providing extra parameters, or chaining multiple rules together. - -### Core Evaluators - -You can create new rules by chaining other rules and evaluators. - -| Key | Description | -| ---------- | ----------------------------------------------------------------------------- | -| core.every | Evaluates to `true` if all chained rules evaluate to `true`. | -| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. | -| core.not | Evaluates to `true` if all chained rules evaluate to `false`. | - -Below is an example of the composite rule definition that combines the following conditions: - -- user has selected a single file -- user is not using the **Trashcan** page - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "rules": [ - { - "id": "app.toolbar.canViewFile", - "type": "core.every", - "parameters": [ - { - "type": "rule", - "value": "app.selection.file" - }, - { - "type": "rule", - "value": "core.not", - "parameters": [ - { - "type": "rule", - "value": "app.navigation.isTrashcan" - } - ] - } - ] - } - ] -} -``` - -You can now declare a toolbar button action that is based on the rule above. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "toolbar": [ - { - "id": "app.toolbar.preview", - "type": "button", - "title": "View File", - "icon": "open_in_browser", - "actions": { - "click": "VIEW_FILE" - }, - "rules": { - "visible": "app.toolbar.canViewFile" - } - } - ] - } -} -``` - -The button will be visible only when the linked rule evaluates to `true`. - -### Application Evaluators - -| Key | Description | -| ------------------------------- | ------------------------------------------------------------ | -| app.selection.canDelete | User has permission to delete selected node(s). | -| app.selection.canDownload | User can download selected node(s). | -| app.selection.notEmpty | At least one node is selected. | -| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. | -| app.selection.canAddFavorite | User can add selected node(s) to favorites. | -| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. | -| app.selection.first.canUpdate | User has permission to update selected node(s). | -| app.selection.file | A single File node is selected. | -| app.selection.file.canShare | User is able to share the selected file. | -| app.selection.library | A single Library node is selected. | -| app.selection.folder | A single Folder node is selected. | -| app.selection.folder.canUpdate | User has permissions to update the selected folder. | - -### Navigation Evaluators - -The application exposes a set of navigation-related evaluators to help developers restrict or enable certain actions based on the route or page displayed. - -The negated evaluators are provided just to simplify development, and to avoid having complex rule trees just to negate the rules, -for example mixing `core.every` and `core.not`. - -

-You can also negate any rule by utilizing a `!` prefix: -`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`. -

- -| Key | Description | -| --------------------------------- | ------------------------------------------------------- | -| app.navigation.folder.canCreate | User can create content in the currently opened folder. | -| app.navigation.folder.canUpload | User can upload content to the currently opened folder. | -| app.navigation.isTrashcan | User is using the **Trashcan** page. | -| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. | -| app.navigation.isLibraries | User is using the **Libraries** page. | -| app.navigation.isNotLibraries | Current page is not **Libraries**. | -| app.navigation.isSharedFiles | User is using the **Shared Files** page. | -| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. | -| app.navigation.isFavorites | User is using the **Favorites** page. | -| app.navigation.isNotFavorites | Current page is not **Favorites** | -| app.navigation.isRecentFiles | User is using the **Recent Files** page. | -| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. | -| app.navigation.isSearchResults | User is using the **Search Results** page. | -| app.navigation.isNotSearchResults | Current page is not the **Search Results**. | - -

-See [Registration](#registration) section for more details -on how to register your own entries to be re-used at runtime. -

- -#### Example - -The rule in the example below evaluates to `true` if all the conditions are met: - -- user has selected node(s) -- user is not using the **Trashcan** page -- user is not using the **Libraries** page - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "rules": [ - { - "id": "app.toolbar.canCopyNode", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.notEmpty" }, - { "type": "rule", "value": "app.navigation.isNotTrashcan" }, - { "type": "rule", "value": "app.navigation.isNotLibraries" } - ] - } - ] -} -``` - -## Application Features - -This section contains application-specific features that may vary depending on the final implementation. - -The ACA supports the following set of extension points: - -- Create menu -- Navigation Bar -- Toolbar -- Context Menu -- Viewer -- Sidebar (aka Info Drawer) -- Content metadata presets (for `Properties` tab) - -All the customizations are stored in the `features` section of the configuration file: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "create": [], - "navbar": [], - "toolbar": [], - "contextMenu": [], - "viewer": { - "toolbarActions:": [], - "toolbarMoreMenu:": [], - "openWith": [], - "content": [] - }, - "sidebar": [], - "content-metadata-presets": [] - } -} -``` - -Other applications or external plugins can utilise different subsets of the configuration above. -Also, extra entries can be added to the configuration schema. - -### Content Actions - -Most of the UI elements that operate with content, like toolbar buttons or menus, -are based on `ContentActionRef` interface implementation: - -```ts -interface ContentActionRef { - id: string; - type: ContentActionType; - - title?: string; - description?: string; - order?: number; - icon?: string; - disabled?: boolean; - children?: Array; - component?: string; - actions?: { - click?: string; - [key: string]: string; - }; - rules?: { - enabled?: string; - visible?: string; - [key: string]: string; - }; -} -``` - -You can define content actions in the `app.extensions.json` file using the structure above. - -### Create Menu - -Provides extension endpoint for the "NEW" menu options. - -You can populate the menu with an extra entries like in the example below: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "create": [ - { - "id": "plugin1.create.folder", - "icon": "create_new_folder", - "title": "Create Folder (plugin1)", - "actions": { - "click": "CREATE_FOLDER" - }, - "rules": { - "enabled": "app.navigation.folder.canCreate" - } - }, - { - "id": "plugin1.create.uploadFile", - "icon": "file_upload", - "title": "Upload Files (plugin1)", - "actions": { - "click": "UPLOAD_FILES" - }, - "rules": { - "enabled": "app.navigation.folder.canUpload" - } - } - ] - } -} -``` - -Please refer to the [Content Actions](#content-actions) section for more details on supported properties. - -

-It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customize. -

- -### Navigation Bar - -The Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`). - -```ts -export interface NavBarGroupRef { - id: string; - items: Array; - - order?: number; - disabled?: boolean; -} - -export interface NavBarLinkRef { - id: string; - icon: string; - title: string; - route: string; - - url?: string; // evaluated at runtime based on route ref - description?: string; - order?: number; - disabled?: boolean; -} -``` - -Your extensions can perform the following actions at runtime: - -- Register new groups with links -- Insert new links into existing groups -- Update properties of the existing links -- Disable existing links or entire groups - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "navbar": [ - { - "id": "app.navbar.primary", - "items": [ - { - "id": "app.navbar.personalFiles", - "icon": "folder", - "title": "Personal Files", - "route": "personal-files" - }, - { - "id": "app.navbar.libraries", - "icon": "group_work", - "title": "Libraries", - "route": "libraries" - } - ] - }, - { - "id": "app.navbar.secondary", - "items": [ - { - "id": "app.navbar.shared", - "icon": "people", - "title": "Shared", - "route": "shared" - } - ] - } - ] - } -} -``` - -### Sidebar (Info Drawer) - -You can provide the following customizations for the Sidebar (aka Info Drawer) component: - -- Add extra tabs with custom components -- Disable tabs from the main application or extensions -- Replace content or properties of existing tabs - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "sidebar": [ - { - "id": "app.sidebar.properties", - "order": 100, - "title": "Properties", - "component": "app.components.tabs.metadata" - }, - { - "id": "app.sidebar.comments", - "order": 200, - "title": "Comments", - "component": "app.components.tabs.comments" - } - ] - } -} -``` - -The example above renders two tabs: - -- `Properties` tab that references the `app.components.tabs.metadata` component -- `Comments` tab that references the `app.components.tabs.comments` component - -All corresponding components must be registered for runtime use. - -

-See the [Registration](#registration) section for more details -on how to register your own entries to be re-used at runtime. -

- -#### Tab properties - -| Name | Description | -| ------------- | ----------------------------------------------------------- | -| **id** | Unique identifier. | -| **component** | The main [component](#components) to use for the route. | -| **title** | Tab title or resource key. | -| icon | Tab icon | -| disabled | Toggles disabled state. Can be assigned from other plugins. | -| order | The order of the element. | - -#### Tab components - -Every component you assign for the tab content receives the following additional properties at runtime: - -| Name | Type | Description | -| ---- | ---------------------- | --------------------------- | -| node | MinimalNodeEntryEntity | Node entry to be displayed. | - -### Toolbar - -The toolbar extension point is represented by an array of Content Action references. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "toolbar": [ - { - "id": "app.toolbar.preview", - "title": "View", - "icon": "open_in_browser", - "actions": { - "click": "VIEW_FILE" - }, - "rules": { - "visible": "app.toolbar.canViewFile" - } - }, - { - "id": "app.toolbar.download", - "title": "Download", - "icon": "get_app", - "actions": { - "click": "DOWNLOAD_NODES" - }, - "rules": { - "visible": "app.toolbar.canDownload" - } - } - ] - } -} -``` - -The content actions are applied to the toolbars for the following Views: - -- Personal Files -- Libraries -- Shared -- Recent Files -- Favorites -- Trash -- Search Results - -### Context Menu - -Context Menu extensibility is similar to the one of the Toolbar. -You may want to define a list of content actions backed by Rules and wired with Application Actions. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "contextMenu": [ - { - "id": "app.context.menu.download", - "order": 100, - "title": "Download", - "icon": "get_app", - "actions": { - "click": "DOWNLOAD_NODES" - }, - "rules": { - "visible": "app.toolbar.canDownload" - } - } - ] - } -} -``` - -Note, you can re-use any rules and evaluators that are available. - -In the example above, the context menu action `Download` utilizes the `app.toolbar.canDownload` rule, -declared in the `rules` section: - -```json -{ - "rules": [ - { - "id": "app.toolbar.canDownload", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.canDownload" }, - { "type": "rule", "value": "app.navigation.isNotTrashcan" } - ] - } - ] -} -``` - -### Viewer - -Viewer component in ACA supports the following extension points: - -- Content Viewers -- Toolbar actions -- `More` toolbar actions -- `Open With` actions - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "viewer": { - "content": [], - "toolbarActions:": [], - "toolbarMoreMenu:": [], - "openWith": [] - } - } -} -``` - -#### Content View - -You can provide custom components that render a particular type of the content based on extensions. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "viewer": { - "content": [ - { - "id": "app.viewer.pdf", - "fileExtension": "pdf", - "component": "app.components.tabs.metadata" - }, - { - "id": "app.viewer.docx", - "fileExtension": "docx", - "component": "app.components.tabs.comments" - } - ] - } - } -} -``` - -In the example above we replace `PDF` view with the `metadata` tab -and `DOCX` view with the `comments` tab. - -Every custom component receives the following properties at runtime: - -| Name | Type | Description | -| --------- | ---------------------- | --------------------------- | -| node | MinimalNodeEntryEntity | Node entry to be displayed. | -| url | string | File content URL. | -| extension | string | File name extension. | - -#### Toolbar actions - -The default toolbar actions from the ACA viewer can be customized through extensions to be replaced, modified or disabled. -New viewer toolbar actions can also be added from the extensions config: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "viewer": { - "toolbarActions": [ - { - "id": "app.viewer.versions", - "order": 500, - "title": "APP.ACTIONS.VERSIONS", - "icon": "history", - "actions": { - "click": "MANAGE_VERSIONS" - }, - "rules": { - "visible": "app.toolbar.versions" - } - } - ], - "toolbarMoreMenu": [...] - } - } -} -``` - -The ADF Viewer component allows you to provide custom entries for the `More` menu button on the toolbar. -The ACA provides an extension point for this menu that you can utilize to populate custom menu items: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "viewer": { - "toolbarActions": [...], - "toolbarMoreMenu": [ - { - "id": "app.viewer.share", - "order": 300, - "title": "Share", - "icon": "share", - "actions": { - "click": "SHARE_NODE" - }, - "rules": { - "visible": "app.selection.file.canShare" - } - } - ] - } - } -} -``` - -#### Open With actions - -You can provide a list of `Open With` actions to render with every instance of the Viewer. - -In the following example, we create a simple `Snackbar` action reference, -and invoke it from the custom `Open With` menu entry called `Snackbar`. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "actions": [ - { - "id": "plugin1.actions.info", - "type": "SNACKBAR_INFO", - "payload": "I'm a nice little popup raised by extension." - } - ], - - "features": { - "viewer": { - "openWith": [ - { - "id": "plugin1.viewer.openWith.action1", - "type": "button", - "icon": "build", - "title": "Snackbar", - "actions": { - "click": "plugin1.actions.info" - } - } - ] - } - } -} -``` - -As with other content actions, custom plugins can disable, update or extend `Open With` actions. - -### Content metadata presets - -The content metadata presets are needed by the [Content Metadata Component](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html#readme) to render the properties of metadata aspects for a given node. -The different aspects and their properties are configured in the `app.config.json` file, but they can also be set on runtime through extension files. - -Configuring these presets from `app.extensions.json` will overwrite the default application setting. -Settings them from custom plugins allows user to disable, update or extend these presets. -Check out more info about merging extensions [here](#merging-properties). - -The `content-metadata-presets` elements can be switched off by setting the `disabled` property. -This can be applied also for nested items, allowing disabling down to aspect level. - -

-In order to modify or disable existing entries, you need to know the id of the target element, along with its parents ids. -

- -Your extensions can perform the following actions at runtime: -* Add new presets items. -* Add new items to existing presets at any level. -* Disable specific items down to the aspect level. -* Modify any existing item based on id. - -Regarding properties, you can either: - * Add new properties to existing aspect, or - * Redefine the properties of an aspect. - -Review this code snippet to see how you can overwrite the properties for `exif:exif` aspect from an external plugin: -```json - { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "features": { - "content-metadata-presets": [ - { - "id": "app.content.metadata.custom", - "custom": [ - { - "id": "app.content.metadata.customGroup", - "items": [ - { - "id": "app.content.metadata.exifAspect", - "disabled": true - }, - { - "id": "app.content.metadata.exifAspect2", - "aspect": "exif:exif", - "properties": [ - "exif:orientation", - "exif:manufacturer", - "exif:model", - "exif:software" - ] - } - ] - } - ] - } - ] - } - } -``` -This external plugin disables the initial `exif:exif` aspect already defined in the `app.extensions.json` and defines other properties for the `exif:exif` aspect. -Here is the initial setting from `app.extension.json`: -```json -... - "content-metadata-presets": [ - { - "id": "app.content.metadata.custom", - "custom": [ - { - "id": "app.content.metadata.customGroup", - "title": "APP.CONTENT_METADATA.EXIF_GROUP_TITLE", - "items": [ - { - "id": "app.content.metadata.exifAspect", - "aspect": "exif:exif", - "properties": [ - "exif:pixelXDimension", - "exif:pixelYDimension", - "exif:dateTimeOriginal", - "exif:exposureTime", - "exif:fNumber", - "exif:flash", - "exif:focalLength", - "exif:isoSpeedRatings", - "exif:orientation", - "exif:manufacturer", - "exif:model", - "exif:software" - ] - } - ] - } - ] - } - ] -... - -``` -

-In order to allow the content-metadata presets to be extended, the settings from `app.config.json` must be copied to the `app.extensions.json` file and its ids must be added to all the items. -Having ids allows external plugins to extend the current setting. -

- -## Registration - -You can use `ExtensionService` to register custom components, authentication guards, -rule evaluators, etc. - -It is recommended to register custom content from within the module constructor. -In that case all plugins will be available right after the main application component is ready. - -Update the main application module `app.module.ts`, or create your own module, -and use the following snippet to register custom content: - -```typescript -import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions'; - -@NgModule({ - imports: [ ExtensionsModule.forChild() ] - declarations: [ MyComponent1, MyLayout ], - entryComponents: [ MyComponent1, MyLayout ] -}) -export class MyExtensionModule { - - constructor(extensions: ExtensionService) { - extensions.setComponents({ - 'plugin1.components.my': MyComponent1, - 'plugin1.layouts.my': MyLayout - }); - - extensions.setAuthGuards({ - 'plugin.auth': MyAuthGuard - }); - - extensions.setEvaluators({ - 'plugin1.rules.custom1': MyCustom1Evaluator, - 'plugin1.rules.custom2': MyCustom2Evaluator - }); - } - -} -``` - -Use `ExtensionsModule.forChild()` when importing into the child modules, -and `ExtensionsModule.forRoot()` for the main application module. - -

-According to Angular rules, all components that are created dynamically at runtime -need to be registered within the `entryComponents` section of the NgModule. -

- -The Registration API is not limited to the custom content only. -You can replace any existing entries by replacing the values from your module. - -## Creating custom evaluator - -Rule evaluators are plain JavaScript (or TypeScript) functions that take `RuleContext` references and an optional list of `RuleParameter` instances. - -Application provides a special [RuleEvaluator](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L30) type alias for evaluator functions: - -```typescript -export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean; -``` - -Create a function that is going to check if a user has selected one or multiple nodes. - -```typescript -export function hasSelection( - context: RuleContext, - ...args: RuleParameter[] -): boolean { - return !context.selection.isEmpty; -} -``` - -The `context` is a reference to a special instance of the [RuleContext](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L32) type, -that provides each evaluator access to runtime entities. - -```typescript -export interface RuleContext { - selection: SelectionState; - navigation: NavigationState; - permissions: NodePermissions; - - getEvaluator(key: string): RuleEvaluator; -} -``` - -The `SelectionState` interface exposes information about the global selection state: - -```typescript -export interface SelectionState { - count: number; - nodes: MinimalNodeEntity[]; - libraries: SiteEntry[]; - isEmpty: boolean; - first?: MinimalNodeEntity; - last?: MinimalNodeEntity; - folder?: MinimalNodeEntity; - file?: MinimalNodeEntity; - library?: SiteEntry; -} -``` - -Next, register the function you have created earlier with the `ExtensionService` and give it a unique identifier: - -```typescript -extensions.setEvaluators({ - 'plugin1.rules.hasSelection': hasSelection -}); -``` - -Now, the `plugin1.rules.hasSelection` evaluator can be used as an inline rule reference, -or part of the composite rule like `core.every`. - -

-See the [Registration](#registration) section for more details -on how to register your own entries to be re-used at runtime. -

- -## Tutorials - -### Custom route with parameters - -In this tutorial, we are going to implement the following features: - -- Update the **Trashcan** component to receive and log route parameters. -- Create a new route that points to the **Trashcan** component and uses the main layout. -- Create an action reference that allows redirecting to the new route. -- Create a button in the **New** menu that invokes an action. - -Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body: - -```typescript -import { ActivatedRoute, Params } from '@angular/router'; - -@Component({...}) -export class TrashcanComponent { - - constructor( - // ... - private route: ActivatedRoute - ) {} - - ngOnInit() { - // ... - - this.route.params.subscribe(({ nodeId }: Params) => { - console.log('node: ', nodeId); - }); - } - -} -``` - -The code above logs the current route parameters to the browser console -and is proof the integration works as expected. - -Next, add a new route definition as in the example below: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "routes": [ - { - "id": "custom.routes.trashcan", - "path": "ext/trashcan/:nodeId", - "component": "your.component.id", - "layout": "app.layout.main", - "auth": ["app.auth"] - } - ] -} -``` - -The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter. - -Also, we are going to use the default application layout (`app.layout.main`) -and authentication guards (`app.auth`). - -Next, create an action reference for the `NAVIGATE_ROUTE` application action -and pass route parameters: `/ext/trashcan` for the path, and `10` for the `nodeId` value. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "routes": [...], - - "actions": [ - { - "id": "custom.actions.trashcan", - "type": "NAVIGATE_ROUTE", - "payload": "$(['/ext/trashcan', '10'])" - } - ] -} -``` - -Finally, declare a new menu item for the `NEW` button and use the `custom.actions.trashcan` action created above. - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - - "routes": [...], - "actions": [...], - - "features": { - "create": [ - { - "id": "custom.create.trashcan", - "type": "default", - "icon": "build", - "title": "Custom trashcan route", - "actions": { - "click": "custom.actions.trashcan" - } - } - ] - } -} -``` - -Now, if you run the application, you should see a new menu item called "Custom Trashcan Route" in the "NEW" dropdown. -Upon clicking this item you should navigate to the `/ext/trashcan/10` route containing a **Trashcan** component. - -Check the browser console output and ensure you have the following output: - -```text -node: 10 -``` - -You have successfully created a new menu button that invokes your custom action -and redirects you to the extra application route. - -### Dialog actions - -In this tutorial, we are going to create an action that invokes a custom material dialog. - -

-Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview) -

- -#### Create a dialog - -```sh -ng g component dialogs/my-extension-dialog --module=app -``` - -According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module. - -Update the `src/app/app.module.ts` file according to the example below: - -```ts -@NgModule({ - imports: [...], - declarations: [ - ..., - MyExtensionDialogComponent - ], - entryComponents: [ - ..., - MyExtensionDialogComponent - ] -}) -``` - -Update `my-extension-dialog.component.ts`: - -```ts -import { Component } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; - -@Component({ - selector: 'aca-my-extension-dialog', - templateUrl: './my-extension-dialog.component.html', - styleUrls: ['./my-extension-dialog.component.scss'] -}) -export class MyExtensionDialogComponent { - constructor(public dialogRef: MatDialogRef) {} -} -``` - -Update `my-extension-dialog.component.html`: - -```html -

Delete all

-Are you sure? - - - - - -``` - -#### Create an action - -Append the following code to the `src/app/store/actions/app.actions.ts`: - -```ts -export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG'; - -export class ShowMydDialogAction implements Action { - readonly type = SHOW_MY_DIALOG; -} -``` - -See also: - -- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) - -#### Create an effect - -Update `src/app/store/effects/app.effects.ts`: - -```ts -import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions'; - -@Injectable() -export class AppEffects { - constructor(...) {} - - @Effect({ dispatch: false }) - showMyDialog$ = this.actions$.pipe( - ofType(SHOW_MY_DIALOG), - map(() => {}) - ); - - // ... -} -``` - -See also: - -- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) - -Update to raise a dialog - -```ts -import { MatDialog } from '@angular/material'; -import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component'; - -@Injectable() -export class AppEffects { - constructor( - ..., - private dialog: MatDialog - ) {} - - @Effect({ dispatch: false }) - showMyDialog$ = this.actions$.pipe( - ofType(SHOW_MY_DIALOG), - map(() => { - this.dialog.open(MyExtensionDialogComponent) - }) - ); - - ... - -} -``` - -#### Register a toolbar action - -Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section: - -```json -{ - ..., - - "features": { - "toolbar": [ - { - "id": "my.custom.toolbar.button", - "order": 10, - "title": "Custom action", - "icon": "extension", - "actions": { - "click": "SHOW_MY_DIALOG" - } - } - ] - } -} -``` - -Now, once you run the application, you should see an extra button that invokes your dialog on every click. - -## Redistributable libraries - -Extension libraries are based on the standard Angular libraries and definition files in the JSON format. - -

-Please read more details in the following article: [Library support in Angular CLI 6](https://github.com/angular/angular-cli/wiki/stories-create-library#library-support-in-angular-cli-6) -

- -See also - -- The Angular Library Series - Creating a Library with the Angular CLI - - Part 1: https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5 - - Part 2: https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121 - -### Creating extension library - -First, generate a new project within the workspace: - -```sh -ng generate library my-extension -``` - -You will get a new project in the `projects/my-extensions` folder. -By default, the project contains at least the following content: - -- Example component `my-extension.component.ts` -- Example service `my-extension.service.ts` -- Angular Module example `my-extension.module.ts` - -Next, build the project with the following command: - -```sh -ng build my-extension -``` - -Angular CLI automatically configures Typescript path mappings for the project, so that you do not need any additional steps to link the library. - -#### Register dynamic components - -Update `my-extension.module.ts` and put all the content you plan to use at runtime dynamically to the `entryComponents` section of the module. - -```typescript -@NgModule({ - imports: [], - declarations: [MyExtensionComponent], - exports: [MyExtensionComponent], - entryComponents: [MyExtensionComponent] -}) -export class MyExtensionModule {} -``` - -Now we need to register `MyExtensionComponent` as an extension component. -Update the code as in the next example: - -```typescript -import { ExtensionService } from '@alfresco/adf-extensions'; - -@NgModule({...}) -export class MyExtensionModule { - constructor(extensions: ExtensionService) { - extensions.setComponents({ - 'my-extension.main.component': MyExtensionComponent, - }); - } -} -``` - -Now you can use the `my-extension.main.component` identifier in the JSON definitions -if you want to reference the `MyExtensionComponent`. - -#### Plugin definition file - -Create a new `assets/my-extension.json` file in the library project root folder with the following content: - -```json -{ - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - "$description": "demo plugin", - - "routes": [ - { - "id": "my.extension.route", - "path": "ext/my/route", - "component": "my-extension.main.component" - } - ], - - "features": { - "navbar": [ - { - "id": "my.extension.nav", - "items": [ - { - "id": "my.extension.main", - "icon": "extension", - "title": "My Extension", - "route": "my.extension.route" - } - ] - } - ] - } -} -``` - -Update the root `package.json` file and append the following entry to the `scripts` section: - -```json -{ - "scripts": { - ..., - - "build:my-extension": - "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst" - } -} -``` - -You can now use that script to build the library and copy assets to the output folder. - -

-It is good practice to provide installation instructions for your library in the `README.md` file. -Be sure to mention that developers should have a build rule to copy your plugin definition file to the `assets/plugins` folder of the main application. -

- -### Publishing library to NPM - -Before you publish you should always rebuild the library: - -```sh -npm run build:my-extension -``` - -Go to the output folder and run the publish command. - -```sh -cd dist/my-extension -npm publish -``` - -Note, you are required to have a valid [NPM](https://www.npmjs.com/) account. - -

-See more details in the [Publishing your library](https://github.com/angular/angular-cli/wiki/stories-create-library#publishing-your-library) article. -

- -### Consuming extension library - -Assuming you have published your extension library to NPM, you can install it using the standard command: - -```sh -npm install my-extension -``` - -This installs the library and all its dependencies. - -

-You do not need to install the library in the original workspace as the application is already configured to use the local version from the `dist` folder. -

- -#### Copy assets - -Edit the `angular.json` configuration file and add the following rule if you develop and test extension libraries in the same workspace. - -```json -{ - "glob": "**/*.json", - "input": "dist/my-extension/assets", - "output": "/assets/plugins" -} -``` - -Use the following rule if you are installing an extension from NPM: - -```json -{ - "glob": "**/*.json", - "input": "node_modules/my-extension/assets", - "output": "/assets/plugins" -} -``` - -#### Register module - -In the main application, edit the `src/app/extensions.module.ts` file and append the module declaration as in the next example: - -```typescript -... -import { MyExtensionModule } from 'my-extension'; - -@NgModule({ - ... - imports: [ - ..., - MyExtensionModule - ] -}) -export class AppExtensionsModule {} -``` - -#### Register plugin - -Finally, update the `assets/app.extensions.json` file and add a reference to the new plugin: - -```json -{ - "$references": [ - ..., - "my-extension.json" - ] -} -``` - -### Testing library - -Run the application and ensure you have an extra navigation sidebar entry: - -```sh -npm start -``` - -Click the `My Extension` link and in the main content area you will see the extension component coming from your library. - -

-Depending on the application setup, you may need enabling external plugins via the `Settings` dialog available for `admin` users (clicking the application profile button). -

diff --git a/docs/extending/README.md b/docs/extending/README.md new file mode 100644 index 0000000000..5ed182648e --- /dev/null +++ b/docs/extending/README.md @@ -0,0 +1,21 @@ +--- +--- + +# Extending + +Learn how to extend the features of the Alfresco Content Application. + +## Contents + +- [Extensibility features](/extending/extensibility-features) +- [Extension format](/extending/extension-format) +- [Routes](/extending/routes) +- [Components](/extending/components) +- [Actions](/extending/actions) +- [Application actions](/extending/application-actions) +- [Rules](/extending/rules) +- [Application features](/extending/application-features) +- [Registration](/extending/registration) +- [Creating custom evaluators](/extending/creating-custom-evaluators) +- [Tutorials](/extending/tutorials) +- [Redistributable libraries](/extending/redistributable-libraries) diff --git a/docs/extending/actions.md b/docs/extending/actions.md new file mode 100644 index 0000000000..4519889bdb --- /dev/null +++ b/docs/extending/actions.md @@ -0,0 +1,55 @@ +--- +--- + +# Actions + +| Name | Description | +| -- | -- | +| **id** | Unique identifier. | +| **type** | Action type, see [Application Actions](/extending/application-actions) for more details. | +| **payload** | Action payload, a string containing value or expression. | + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "actions": [ + { + "id": "plugin1.actions.settings", + "type": "NAVIGATE_URL", + "payload": "/settings" + }, + { + "id": "plugin1.actions.info", + "type": "SNACKBAR_INFO", + "payload": "I'm a nice little popup raised by extension." + }, + { + "id": "plugin1.actions.node-name", + "type": "SNACKBAR_INFO", + "payload": "$('Action for ' + context.selection.first.entry.name)" + } + ] +} +``` + +## Value expressions + +You can use light-weight expression syntax to provide custom parameters for the action payloads. + +```text +$() +``` + +Expressions are valid JavaScript blocks that evaluate to values. + +Examples: + +```text +$('hello world') // 'hello world' +$('hello' + ', ' + 'world') // 'hello, world' +$(1 + 1) // 2 +$([1, 2, 1 + 2]) // [1, 2, 3] +``` diff --git a/docs/extending/application-actions.md b/docs/extending/application-actions.md new file mode 100644 index 0000000000..3210da661c --- /dev/null +++ b/docs/extending/application-actions.md @@ -0,0 +1,116 @@ +--- +--- + +# Application Actions + +Application is using NgRx (Reactive libraries for Angular, inspired by Redux). +To get more information on NxRx please refer to the following resources: + +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) + +Most of the application features are already exposed in the form of NgRx Actions and corresponding Effects. +You can invoke any action via a single `Store` dispatcher, similar to the following: + +```ts +export class MyComponent { + constructor(private store: Store) {} + + onClick() { + this.store.dispatch(new SearchByTermAction('*')); + } +} +``` + +The code above demonstrates a simple 'click' handler that invokes `Search by Term` feature +and automatically redirects user to the **Search Results** page. + +Another example demonstrates viewing a node from a custom application service API: + +```ts +export class MyService { + constructor(private store: Store) {} + + viewFile(node: MinimalNodeEntity) { + this.store.dispatch(new ViewFileAction(node)); + } +} +``` + +## Using with Extensions + +You can invoke every application action from the extensions, i.e. buttons, menus, etc. + +

+Many of the actions take currently selected nodes if no payload provided. +That simplifies declaring and invoking actions from the extension files. +

+ +In the example below, we create a new entry to the "NEW" menu dropdown +and provide a new `Create Folder (plugin1)` command that invokes the `CREATE_FOLDER` application action. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "create": [ + { + "id": "plugin1.create.folder", + "type": "default", + "icon": "create_new_folder", + "title": "Create Folder (plugin1)", + "actions": { + "click": "CREATE_FOLDER" + } + } + ] + } +} +``` + +The `CREATE_FOLDER` action will trigger corresponding NgRx Effects to show the dialog +and perform document list reload if needed. + +Below is the list of public actions types you can use in the plugin definitions as a reference to the action: + +| Name | Payload | Description | +| -- | -- | -- | +| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | +| SET_CURRENT_URL | string | Notify components about current browser URL. | +| SET_USER_PROFILE | Person | Assign current user profile. | +| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | +| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | +| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | +| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | +| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | +| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | +| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | +| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | +| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | +| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | +| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | +| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | +| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | +| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | +| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | +| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | +| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | +| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | +| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | +| NAVIGATE_URL | string | Navigate to a given route URL within the application. | +| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters). | +| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | +| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | +| NAVIGATE_LIBRARY | string | Navigate to library. | +| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | +| SNACKBAR_INFO | string | Show information snackbar with the message provided. | +| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | +| SNACKBAR_ERROR | string | Show error snackbar with the message provided. | +| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | +| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | +| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | +| PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). | +| FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. | +| LOGOUT | n/a | Log out and redirect to Login screen. | diff --git a/docs/extending/application-features.md b/docs/extending/application-features.md new file mode 100644 index 0000000000..cecaac05f1 --- /dev/null +++ b/docs/extending/application-features.md @@ -0,0 +1,637 @@ +--- +--- + +# Application Features + +This section contains application-specific features that may vary depending on the final implementation. + +The ACA supports the following set of extension points: + +- Create menu +- Navigation Bar +- Toolbar +- Context Menu +- Viewer +- Sidebar (aka Info Drawer) +- Content metadata presets (for `Properties` tab) + +All the customizations are stored in the `features` section of the configuration file: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "create": [], + "navbar": [], + "toolbar": [], + "contextMenu": [], + "viewer": { + "toolbarActions:": [], + "toolbarMoreMenu:": [], + "openWith": [], + "content": [] + }, + "sidebar": [], + "content-metadata-presets": [] + } +} +``` + +Other applications or external plugins can utilise different subsets of the configuration above. +Also, extra entries can be added to the configuration schema. + +## Content Actions + +Most of the UI elements that operate with content, like toolbar buttons or menus, +are based on `ContentActionRef` interface implementation: + +```ts +interface ContentActionRef { + id: string; + type: ContentActionType; + + title?: string; + description?: string; + order?: number; + icon?: string; + disabled?: boolean; + children?: Array; + component?: string; + actions?: { + click?: string; + [key: string]: string; + }; + rules?: { + enabled?: string; + visible?: string; + [key: string]: string; + }; +} +``` + +You can define content actions in the `app.extensions.json` file using the structure above. + +## Create Menu + +Provides extension endpoint for the "NEW" menu options. + +You can populate the menu with an extra entries like in the example below: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "create": [ + { + "id": "plugin1.create.folder", + "icon": "create_new_folder", + "title": "Create Folder (plugin1)", + "actions": { + "click": "CREATE_FOLDER" + }, + "rules": { + "enabled": "app.navigation.folder.canCreate" + } + }, + { + "id": "plugin1.create.uploadFile", + "icon": "file_upload", + "title": "Upload Files (plugin1)", + "actions": { + "click": "UPLOAD_FILES" + }, + "rules": { + "enabled": "app.navigation.folder.canUpload" + } + } + ] + } +} +``` + +Please refer to the [Content Actions](/extending/application-features#content-actions) section for more details on supported properties. + +

+It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customize. +

+ +## Navigation Bar + +The Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`). + +```ts +export interface NavBarGroupRef { + id: string; + items: Array; + + order?: number; + disabled?: boolean; +} + +export interface NavBarLinkRef { + id: string; + icon: string; + title: string; + route: string; + + url?: string; // evaluated at runtime based on route ref + description?: string; + order?: number; + disabled?: boolean; +} +``` + +Your extensions can perform the following actions at runtime: + +- Register new groups with links +- Insert new links into existing groups +- Update properties of the existing links +- Disable existing links or entire groups + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "navbar": [ + { + "id": "app.navbar.primary", + "items": [ + { + "id": "app.navbar.personalFiles", + "icon": "folder", + "title": "Personal Files", + "route": "personal-files" + }, + { + "id": "app.navbar.libraries", + "icon": "group_work", + "title": "Libraries", + "route": "libraries" + } + ] + }, + { + "id": "app.navbar.secondary", + "items": [ + { + "id": "app.navbar.shared", + "icon": "people", + "title": "Shared", + "route": "shared" + } + ] + } + ] + } +} +``` + +## Sidebar (Info Drawer) + +You can provide the following customizations for the Sidebar (aka Info Drawer) component: + +- Add extra tabs with custom components +- Disable tabs from the main application or extensions +- Replace content or properties of existing tabs + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "sidebar": [ + { + "id": "app.sidebar.properties", + "order": 100, + "title": "Properties", + "component": "app.components.tabs.metadata" + }, + { + "id": "app.sidebar.comments", + "order": 200, + "title": "Comments", + "component": "app.components.tabs.comments" + } + ] + } +} +``` + +The example above renders two tabs: + +- `Properties` tab that references the `app.components.tabs.metadata` component +- `Comments` tab that references the `app.components.tabs.comments` component + +All corresponding components must be registered for runtime use. + +

+See the [Registration](/extending/registration) section for more details +on how to register your own entries to be re-used at runtime. +

+ +### Tab properties + +| Name | Description | +| ------------- | ----------------------------------------------------------- | +| **id** | Unique identifier. | +| **component** | The main [component](/extending/components) to use for the route. | +| **title** | Tab title or resource key. | +| icon | Tab icon | +| disabled | Toggles disabled state. Can be assigned from other plugins. | +| order | The order of the element. | + +### Tab components + +Every component you assign for the tab content receives the following additional properties at runtime: + +| Name | Type | Description | +| ---- | ---------------------- | --------------------------- | +| node | MinimalNodeEntryEntity | Node entry to be displayed. | + +## Toolbar + +The toolbar extension point is represented by an array of Content Action references. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "toolbar": [ + { + "id": "app.toolbar.preview", + "title": "View", + "icon": "open_in_browser", + "actions": { + "click": "VIEW_FILE" + }, + "rules": { + "visible": "app.toolbar.canViewFile" + } + }, + { + "id": "app.toolbar.download", + "title": "Download", + "icon": "get_app", + "actions": { + "click": "DOWNLOAD_NODES" + }, + "rules": { + "visible": "app.toolbar.canDownload" + } + } + ] + } +} +``` + +The content actions are applied to the toolbars for the following Views: + +- Personal Files +- Libraries +- Shared +- Recent Files +- Favorites +- Trash +- Search Results +- Libraries Search Results + +## Context Menu + +Context Menu extensibility is similar to the one of the Toolbar. +You may want to define a list of content actions backed by Rules and wired with Application Actions. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "contextMenu": [ + { + "id": "app.context.menu.download", + "order": 100, + "title": "Download", + "icon": "get_app", + "actions": { + "click": "DOWNLOAD_NODES" + }, + "rules": { + "visible": "app.toolbar.canDownload" + } + } + ] + } +} +``` + +Note, you can re-use any rules and evaluators that are available. + +In the example above, the context menu action `Download` utilizes the `app.toolbar.canDownload` rule, +declared in the `rules` section: + +```json +{ + "rules": [ + { + "id": "app.toolbar.canDownload", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.canDownload" }, + { "type": "rule", "value": "app.navigation.isNotTrashcan" } + ] + } + ] +} +``` + +## Viewer + +Viewer component in ACA supports the following extension points: + +- Content Viewers +- Toolbar actions +- `More` toolbar actions +- `Open With` actions + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "viewer": { + "content": [], + "toolbarActions:": [], + "toolbarMoreMenu:": [], + "openWith": [] + } + } +} +``` + +### Content View + +You can provide custom components that render a particular type of the content based on extensions. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "viewer": { + "content": [ + { + "id": "app.viewer.pdf", + "fileExtension": "pdf", + "component": "app.components.tabs.metadata" + }, + { + "id": "app.viewer.docx", + "fileExtension": "docx", + "component": "app.components.tabs.comments" + } + ] + } + } +} +``` + +In the example above we replace `PDF` view with the `metadata` tab +and `DOCX` view with the `comments` tab. + +Every custom component receives the following properties at runtime: + +| Name | Type | Description | +| --------- | ---------------------- | --------------------------- | +| node | MinimalNodeEntryEntity | Node entry to be displayed. | +| url | string | File content URL. | +| extension | string | File name extension. | + +### Toolbar actions + +The default toolbar actions from the ACA viewer can be customized through extensions to be replaced, modified or disabled. +New viewer toolbar actions can also be added from the extensions config: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "viewer": { + "toolbarActions": [ + { + "id": "app.viewer.versions", + "order": 500, + "title": "APP.ACTIONS.VERSIONS", + "icon": "history", + "actions": { + "click": "MANAGE_VERSIONS" + }, + "rules": { + "visible": "app.toolbar.versions" + } + } + ], + "toolbarMoreMenu": [...] + } + } +} +``` + +The ADF Viewer component allows you to provide custom entries for the `More` menu button on the toolbar. +The ACA provides an extension point for this menu that you can utilize to populate custom menu items: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "viewer": { + "toolbarActions": [...], + "toolbarMoreMenu": [ + { + "id": "app.viewer.share", + "order": 300, + "title": "Share", + "icon": "share", + "actions": { + "click": "SHARE_NODE" + }, + "rules": { + "visible": "app.selection.file.canShare" + } + } + ] + } + } +} +``` + +### Open With actions + +You can provide a list of `Open With` actions to render with every instance of the Viewer. + +In the following example, we create a simple `Snackbar` action reference, +and invoke it from the custom `Open With` menu entry called `Snackbar`. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "actions": [ + { + "id": "plugin1.actions.info", + "type": "SNACKBAR_INFO", + "payload": "I'm a nice little popup raised by extension." + } + ], + + "features": { + "viewer": { + "openWith": [ + { + "id": "plugin1.viewer.openWith.action1", + "type": "button", + "icon": "build", + "title": "Snackbar", + "actions": { + "click": "plugin1.actions.info" + } + } + ] + } + } +} +``` + +As with other content actions, custom plugins can disable, update or extend `Open With` actions. + +## Content metadata presets + +The content metadata presets are needed by the [Content Metadata Component](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html#readme) to render the properties of metadata aspects for a given node. +The different aspects and their properties are configured in the `app.config.json` file, but they can also be set on runtime through extension files. + +Configuring these presets from `app.extensions.json` will overwrite the default application setting. +Settings them from custom plugins allows user to disable, update or extend these presets. +Check out more info about merging extensions [here](/extending/extension-format#merging-properties). + +The `content-metadata-presets` elements can be switched off by setting the `disabled` property. +This can be applied also for nested items, allowing disabling down to aspect level. + +

+In order to modify or disable existing entries, you need to know the id of the target element, along with its parents ids. +

+ +Your extensions can perform the following actions at runtime: +* Add new presets items. +* Add new items to existing presets at any level. +* Disable specific items down to the aspect level. +* Modify any existing item based on id. + +Regarding properties, you can either: + * Add new properties to existing aspect, or + * Redefine the properties of an aspect. + +Review this code snippet to see how you can overwrite the properties for `exif:exif` aspect from an external plugin: +```json + { + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "content-metadata-presets": [ + { + "id": "app.content.metadata.custom", + "custom": [ + { + "id": "app.content.metadata.customGroup", + "items": [ + { + "id": "app.content.metadata.exifAspect", + "disabled": true + }, + { + "id": "app.content.metadata.exifAspect2", + "aspect": "exif:exif", + "properties": [ + "exif:orientation", + "exif:manufacturer", + "exif:model", + "exif:software" + ] + } + ] + } + ] + } + ] + } + } +``` +This external plugin disables the initial `exif:exif` aspect already defined in the `app.extensions.json` and defines other properties for the `exif:exif` aspect. +Here is the initial setting from `app.extension.json`: +```json +... + "content-metadata-presets": [ + { + "id": "app.content.metadata.custom", + "custom": [ + { + "id": "app.content.metadata.customGroup", + "title": "APP.CONTENT_METADATA.EXIF_GROUP_TITLE", + "items": [ + { + "id": "app.content.metadata.exifAspect", + "aspect": "exif:exif", + "properties": [ + "exif:pixelXDimension", + "exif:pixelYDimension", + "exif:dateTimeOriginal", + "exif:exposureTime", + "exif:fNumber", + "exif:flash", + "exif:focalLength", + "exif:isoSpeedRatings", + "exif:orientation", + "exif:manufacturer", + "exif:model", + "exif:software" + ] + } + ] + } + ] + } + ] +... + +``` +

+In order to allow the content-metadata presets to be extended, the settings from `app.config.json` must be copied to the `app.extensions.json` file and its ids must be added to all the items. +Having ids allows external plugins to extend the current setting. +

diff --git a/docs/extending/components.md b/docs/extending/components.md new file mode 100644 index 0000000000..e978a69d9c --- /dev/null +++ b/docs/extending/components.md @@ -0,0 +1,26 @@ +--- +--- + +# Components + +You can register any Angular component to participate in extensibility. + +The components are used to create custom: + +- routes and pages +- toolbar buttons +- menu items + +| Key | Type | Description | +| --------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- | +| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. | +| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. | +| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. | +| app.toolbar.toggleFavoriteLibrary | ToggleFavoriteLibraryComponent | The toolbar button component that toggles Favorite library state for the selection. | +| app.toolbar.toggleJoinLibrary | ToggleJoinLibraryComponent | The toolbar button component that toggles Join/Cancel Join request for the selected library. | + +See [Registration](/extending/registration) section for more details +on how to register your own entries to be re-used at runtime. + +Note that custom extensions can also replace any existing component at runtime by a known identifier, +besides registering a new one. diff --git a/docs/extending/creating-custom-evaluators.md b/docs/extending/creating-custom-evaluators.md new file mode 100644 index 0000000000..957dc54d47 --- /dev/null +++ b/docs/extending/creating-custom-evaluators.md @@ -0,0 +1,68 @@ +--- +--- + +# Creating custom evaluators + +Rule evaluators are plain JavaScript (or TypeScript) functions that take `RuleContext` references and an optional list of `RuleParameter` instances. + +Application provides a special [RuleEvaluator](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L30) type alias for evaluator functions: + +```ts +export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean; +``` + +Create a function that is going to check if a user has selected one or multiple nodes. + +```ts +export function hasSelection( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + return !context.selection.isEmpty; +} +``` + +The `context` is a reference to a special instance of the [RuleContext](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L32) type, +that provides each evaluator access to runtime entities. + +```ts +export interface RuleContext { + selection: SelectionState; + navigation: NavigationState; + permissions: NodePermissions; + + getEvaluator(key: string): RuleEvaluator; +} +``` + +The `SelectionState` interface exposes information about the global selection state: + +```ts +export interface SelectionState { + count: number; + nodes: MinimalNodeEntity[]; + libraries: SiteEntry[]; + isEmpty: boolean; + first?: MinimalNodeEntity; + last?: MinimalNodeEntity; + folder?: MinimalNodeEntity; + file?: MinimalNodeEntity; + library?: SiteEntry; +} +``` + +Next, register the function you have created earlier with the `ExtensionService` and give it a unique identifier: + +```ts +extensions.setEvaluators({ + 'plugin1.rules.hasSelection': hasSelection +}); +``` + +Now, the `plugin1.rules.hasSelection` evaluator can be used as an inline rule reference, +or part of the composite rule like `core.every`. + +

+See the [Registration](/extending/registration) section for more details +on how to register your own entries to be re-used at runtime. +

diff --git a/docs/extending/extensibility-features.md b/docs/extending/extensibility-features.md new file mode 100644 index 0000000000..00c323ca94 --- /dev/null +++ b/docs/extending/extensibility-features.md @@ -0,0 +1,34 @@ +--- +--- + +# Extensibility features + +Application extensibility is performed via the root `/src/assets/app.extensions.json`, +and any number of external plugins that are references of the main entry point. + +The application also comes with the `/src/assets/plugins/` folder +already preconfigured to store external files. + +You can create plugins that change, toggle, or extend the following areas: + +- Navigation sidebar links and groups +- Context Menu +- Sidebar (aka Info Drawer) +- Toolbar entries + - buttons + - menu buttons + - separators +- Viewer actions + - "Open With" entries + - toolbar entries + - buttons + - "More actions" buttons +- Content metadata presets (used on `Properties` tab) +- Custom icons + +Extensions can also: + +- Overwrite or disable extension points of the main application or other plugins +- Change rules, actions or any visual element +- Register new application routes based on empty pages or layouts +- Register new rule evaluators, components, guards diff --git a/docs/extending/extension-format.md b/docs/extending/extension-format.md new file mode 100644 index 0000000000..ca78663654 --- /dev/null +++ b/docs/extending/extension-format.md @@ -0,0 +1,319 @@ +--- +--- + +# Extension format + +The format is represented by a JSON file with the structure similar to the following: + +```json +{ + "$id": "unique.id", + "$name": "extension.name", + "$version": "1.0.0", + "$vendor": "author.name", + "$license": "license", + "$runtime": "1.5.0", + "$description": "some description", + + "routes": [], + "actions": [], + "rules": [], + "features": {} +} +``` + +## Schema + +You can find the JSON schema at the project root folder: [extension.schema.json](https://github.com/Alfresco/alfresco-content-app/blob/master/extension.schema.json). + +

+The Schema allows you to validate extension files, provides code completion and documentation hints. +

+ +```json +{ + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0" +} +``` + +## Multiple files + +You can have multiple extension files distributed separately. +All additional files are linked via the `$references` property. +The order of declaration defines the order of loading. + +```json +{ + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0", + "$references": ["plugin1.json", "plugin2.json"] +} +``` + +

+All extension files are merged together at runtime. +This allows plugins to overwrite the code from the main application or to alter other plugins. +

+ +## Startup behavior + +First, the root `app.extensions.json` is loaded by means of the special `Loader` service. +The file can contain all the necessary declarations for an application to function. Extra plugin files are fully optional. + +Next, the `Loader` traverses the `$references` metadata and loads additional files if provided. +For the sake of speed the files are loaded in parallel, however once everything is loaded, they are applied in the order of declaration. + +After all the external files are fetched, the `Loader` sorts them, removes the metadata properties and stacks the resulting JSON objects on top of each other. + +

+Any top-level property name that starts with the `$` symbol is considered metadata and does not participate in the merge process. +That allows a plugin to carry extra information for maintenance and visualisation purposes, for example: `$name`, `$version`, `$description`, `$license`, etc. +

+ +### Merging properties + +There are no limits in the JSON structure and level of nesting. +All objects are merged into a single set based on property keys and object IDs (for arrays). + +Before: Plugin 1 + +```json +{ + "$name": "plugin1", + "plugin1.key": "value", + "plugin1.text": "string" +} +``` + +Before: Plugin 2 + +```json +{ + "$name": "plugin2", + "plugin2.key": "value", + "plugin1.text": "custom string" +} +``` + +Final result: + +```json +{ + "plugin1.key": "value", + "plugin1.text": "custom string", + "plugin2.key": "value" +} +``` + +Note that as a result we have two unique properties `plugin1.key` and `plugin2.key`, +and also a `plugin1.text` that was first defined in the `Plugin 1`, but then overwritten by the `Plugin 2`. + +

+JSON merging is a very powerful concept as it gives you the ability to alter any base application settings, +or toggle features in other plugins without rebuilding the application or corresponding plugin libraries. +

+ +### Merging objects + +The complex objects are merged by properties. This process is recursive and has no limits for nesting levels. + +Before: Plugin 1 + +```json +{ + "$name": "plugin1", + "features": { + "title": "some title", + "page1": { + "title": "page 1" + } + } +} +``` + +Before: Plugin 2 + +```json +{ + "$name": "plugin2", + "features": { + "page1": { + "title": "custom title" + }, + "page2": { + "title": "page 2" + } + } +} +``` + +Final result: + +```json +{ + "features": { + "title": "some title", + "page1": { + "title": "custom title" + }, + "page2": { + "title": "page 2" + } + } +} +``` + +You can see the unique properties get merged together in a single object. +However the last non-unique property overwrites the previous value. + +Using the current design it is not possible to delete any application property from the plugin. +The loader engine only supports overwriting values. Many components however support the `disabled` property you can change using an external definition: + +Before: Plugin 1 + +```json +{ + "$name": "plugin1", + "feature1": { + "disabled": false, + "text": "some-feature", + "icon": "some-icon" + } +} +``` + +Before: Plugin 2 + +```json +{ + "$name": "plugin2", + "feature1": { + "disabled": true + } +} +``` + +Final result: + +```json +{ + "feature1": { + "disabled": true, + "text": "some-feature", + "icon": "some-icon" + } +} +``` + +You can find more details in the [Disabling Content](/extending/extension-format#disabling-content) section + +### Merging arrays + +The extension `Loader` provides a special support for merging Arrays. +By default, two collections will be merged into a single array unless objects have `id` properties. + +

+If array contains two objects with the same `id` property, the objects will be merged rather than appended. +

+ +Before: Plugin 1 + +```json +{ + "$name": "plugin1", + "features": [ + { "text": "common 1" }, + { + "id": "page1", + "text": "page 1" + } + ] +} +``` + +Before: Plugin 2 + +```json +{ + "$name": "plugin2", + "features": [ + { "text": "common 2" }, + { + "id": "page1", + "text": "custom page" + } + ] +} +``` + +Final result: + +```json +{ + "features": [ + { "text": "common 1" }, + { "text": "common 2" }, + { + "id": "page1", + "text": "custom page" + } + ] +} +``` + +Note that objects with the same `page1` identifiers were merged while other unique entries were appended to the resulting array. + +## Disabling content + +Most of the schema elements can be switched off by using the `disabled` property: + +```json +{ + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0", + + "features": { + "create": [ + { + "id": "app.create.folder", + "disabled": true, + "order": 100, + "icon": "create_new_folder", + "title": "Create Folder" + } + ] + } +} +``` + +This feature becomes handy when you want to disable existing functionality from within the external plugin. + +In the example below, the plugin called `plugin1` replaces standard `app.create.folder` menu +exposed by the application with a custom one coming with the plugin. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "create": [ + { + "id": "app.create.folder", + "disabled": true, + ... + }, + { + "id": "plugin1.create.folder", + "title": "Create Folder", + ... + } + ] + } +} +``` diff --git a/docs/extending/icons.md b/docs/extending/icons.md new file mode 100644 index 0000000000..9903edb710 --- /dev/null +++ b/docs/extending/icons.md @@ -0,0 +1,42 @@ +# Custom Icons + +You can register and use custom `.svg` icons with toolbars, context menus, etc. + +The icons are declared in the `features.icons` section, for example: + +```json +{ + "features": { + "icons": [ + { + "id": "adf:join_library", + "value": "./assets/images/join-library.svg" + }, + { + "id": "adf:move_file", + "value": "./assets/images/adf-move-file-24px.svg" + } + ] + } +} +``` + +The `id` value must conform to the format `[namespace]:[name]`, +similar to that of the [Material Icon](https://material.angular.io/components/icon/api) component. +The icon file path should be relative to the deployed application root (or `index.html` file); + +After that, you can use the icon id with other elements, for example: + +```json +{ + "id": "app.toolbar.move", + "order": 500, + "title": "APP.ACTIONS.MOVE", + "icon": "adf:move_file", + "actions": { + "click": "MOVE_NODES" + } +} +``` + +It is also possible to override the icon value or disable the entry from within external extensions. diff --git a/docs/extending/redistributable-libraries.md b/docs/extending/redistributable-libraries.md new file mode 100644 index 0000000000..af848c211d --- /dev/null +++ b/docs/extending/redistributable-libraries.md @@ -0,0 +1,227 @@ +--- +--- + +# Redistributable libraries + +Extension libraries are based on the standard Angular libraries and definition files in the JSON format. + +Please read more details in the following article: [Library support in Angular CLI 6](https://github.com/angular/angular-cli/wiki/stories-create-library#library-support-in-angular-cli-6) + +See also + +- The Angular Library Series - Creating a Library with the Angular CLI + - Part 1: https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5 + - Part 2: https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121 + +## Creating extension library + +First, generate a new project within the workspace: + +```sh +ng generate library my-extension +``` + +You will get a new project in the `projects/my-extensions` folder. +By default, the project contains at least the following content: + +- Example component `my-extension.component.ts` +- Example service `my-extension.service.ts` +- Angular Module example `my-extension.module.ts` + +Next, build the project with the following command: + +```sh +ng build my-extension +``` + +Angular CLI automatically configures Typescript path mappings for the project, so that you do not need any additional steps to link the library. + +### Register dynamic components + +Update `my-extension.module.ts` and put all the content you plan to use at runtime dynamically to the `entryComponents` section of the module. + +```typescript +@NgModule({ + imports: [], + declarations: [MyExtensionComponent], + exports: [MyExtensionComponent], + entryComponents: [MyExtensionComponent] +}) +export class MyExtensionModule {} +``` + +Now we need to register `MyExtensionComponent` as an extension component. +Update the code as in the next example: + +```typescript +import { ExtensionService } from '@alfresco/adf-extensions'; + +@NgModule({...}) +export class MyExtensionModule { + constructor(extensions: ExtensionService) { + extensions.setComponents({ + 'my-extension.main.component': MyExtensionComponent, + }); + } +} +``` + +Now you can use the `my-extension.main.component` identifier in the JSON definitions +if you want to reference the `MyExtensionComponent`. + +### Plugin definition file + +Create a new `assets/my-extension.json` file in the library project root folder with the following content: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + "$description": "demo plugin", + + "routes": [ + { + "id": "my.extension.route", + "path": "ext/my/route", + "component": "my-extension.main.component" + } + ], + + "features": { + "navbar": [ + { + "id": "my.extension.nav", + "items": [ + { + "id": "my.extension.main", + "icon": "extension", + "title": "My Extension", + "route": "my.extension.route" + } + ] + } + ] + } +} +``` + +Update the root `package.json` file and append the following entry to the `scripts` section: + +```json +{ + "scripts": { + ..., + + "build:my-extension": + "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst" + } +} +``` + +You can now use that script to build the library and copy assets to the output folder. + +

+It is good practice to provide installation instructions for your library in the `README.md` file. +Be sure to mention that developers should have a build rule to copy your plugin definition file to the `assets/plugins` folder of the main application. +

+ +## Publishing library to NPM + +Before you publish you should always rebuild the library: + +```sh +npm run build:my-extension +``` + +Go to the output folder and run the publish command. + +```sh +cd dist/my-extension +npm publish +``` + +Note, you are required to have a valid [NPM](https://www.npmjs.com/) account. + +See more details in the [Publishing your library](https://github.com/angular/angular-cli/wiki/stories-create-library#publishing-your-library) article. + +## Consuming extension library + +Assuming you have published your extension library to NPM, you can install it using the standard command: + +```sh +npm install my-extension +``` + +This installs the library and all its dependencies. + +

+You do not need to install the library in the original workspace as the application is already configured to use the local version from the `dist` folder. +

+ +### Copy assets + +Edit the `angular.json` configuration file and add the following rule if you develop and test extension libraries in the same workspace. + +```json +{ + "glob": "**/*.json", + "input": "dist/my-extension/assets", + "output": "/assets/plugins" +} +``` + +Use the following rule if you are installing an extension from NPM: + +```json +{ + "glob": "**/*.json", + "input": "node_modules/my-extension/assets", + "output": "/assets/plugins" +} +``` + +### Register module + +In the main application, edit the `src/app/extensions.module.ts` file and append the module declaration as in the next example: + +```typescript +... +import { MyExtensionModule } from 'my-extension'; + +@NgModule({ + ... + imports: [ + ..., + MyExtensionModule + ] +}) +export class AppExtensionsModule {} +``` + +### Register plugin + +Finally, update the `assets/app.extensions.json` file and add a reference to the new plugin: + +```json +{ + "$references": [ + ..., + "my-extension.json" + ] +} +``` + +## Testing library + +Run the application and ensure you have an extra navigation sidebar entry: + +```sh +npm start +``` + +Click the `My Extension` link and in the main content area you will see the extension component coming from your library. + +

+Depending on the application setup, you may need enabling external plugins via the `Settings` dialog available for `admin` users (clicking the application profile button). +

diff --git a/docs/extending/registration.md b/docs/extending/registration.md new file mode 100644 index 0000000000..285ffa419b --- /dev/null +++ b/docs/extending/registration.md @@ -0,0 +1,53 @@ +--- +--- + +# Registration + +You can use `ExtensionService` to register custom components, authentication guards, +rule evaluators, etc. + +It is recommended to register custom content from within the module constructor. +In that case all plugins will be available right after the main application component is ready. + +Update the main application module `app.module.ts`, or create your own module, +and use the following snippet to register custom content: + +```ts +import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions'; + +@NgModule({ + imports: [ ExtensionsModule.forChild() ] + declarations: [ MyComponent1, MyLayout ], + entryComponents: [ MyComponent1, MyLayout ] +}) +export class MyExtensionModule { + + constructor(extensions: ExtensionService) { + extensions.setComponents({ + 'plugin1.components.my': MyComponent1, + 'plugin1.layouts.my': MyLayout + }); + + extensions.setAuthGuards({ + 'plugin.auth': MyAuthGuard + }); + + extensions.setEvaluators({ + 'plugin1.rules.custom1': MyCustom1Evaluator, + 'plugin1.rules.custom2': MyCustom2Evaluator + }); + } + +} +``` + +Use `ExtensionsModule.forChild()` when importing into the child modules, +and `ExtensionsModule.forRoot()` for the main application module. + +

+According to Angular rules, all components that are created dynamically at runtime +need to be registered within the `entryComponents` section of the NgModule. +

+ +The Registration API is not limited to the custom content only. +You can replace any existing entries by replacing the values from your module. diff --git a/docs/extending/routes.md b/docs/extending/routes.md new file mode 100644 index 0000000000..d72068ef43 --- /dev/null +++ b/docs/extending/routes.md @@ -0,0 +1,80 @@ +--- +--- + +# Routes + +To create a new route, populate the `routes` section with the corresponding entries. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [ + { + "id": "plugin1.routes.bin", + "path": "ext/bin", + "layout": "app.layout.main", + "component": "your.component.id" + } + ] +} +``` + +## Route properties + +| Name | Description | +| ------------- | -------------------------------------------------------------------------------------- | +| **id** | Unique identifier. | +| **path** | Runtime path of the route. | +| **component** | The main [component](/extending/components) to use for the route. | +| layout | The layout [component](/extending/components) to use for the route. | +| auth | List of [authentication guards](/extending/routes#authentication-guards). Defaults to `[ "app.auth" ]`. | +| data | Custom property bag to carry with the route. | + +Use the `app.layout.main` value for the `layout` property to get the default application layout, +with header, navigation sidebar and main content area. +You can register any component to back the `app.layout.main` value. + +

+By default, the `app.layout.main` is used if you do not specify any custom values. +Use `blank` if you want your route component take the whole page. +

+ +You can define the full route schema like in the next example: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [ + { + "id": "plugin1.routes.bin", + "path": "ext/bin", + "component": "your.component.id", + "layout": "app.layout.main", + "auth": ["app.auth"], + "data": { + "title": "Custom Trashcan" + } + } + ] +} +``` + +All application routes require at least one authentication guard. +Defaults to the `['app.auth']` value. + +## Authentication Guards + +Below is the list of the authentication guards main application registers on startup. + +| Key | Type | Description | +| -------- | ------------ | ------------------------------------------------------------------------- | +| app.auth | AuthGuardEcm | ADF guard, validates ACS authentication and redirects to Login if needed. | + +You can refer those guards from within your custom extensions, +or [register](/extending/registration) your custom implementations. diff --git a/docs/extending/rules.md b/docs/extending/rules.md new file mode 100644 index 0000000000..fef4e91eba --- /dev/null +++ b/docs/extending/rules.md @@ -0,0 +1,215 @@ +--- +--- + +# Rules + +Rules allow evaluating conditions for extension components. +For example, you can disable or hide elements based on certain rules. + +Every rule is backed by a condition evaluator. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "rules": [ + { + "id": "app.trashcan", + "type": "app.navigation.isTrashcan" + } + ] +} +``` + +Rules can accept other rules as parameters: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "rules": [ + { + "id": "app.toolbar.favorite.canAdd", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.canAddFavorite" }, + { "type": "rule", "value": "app.navigation.isNotRecentFiles" }, + { "type": "rule", "value": "app.navigation.isNotSharedFiles" }, + { "type": "rule", "value": "app.navigation.isNotSearchResults" } + ] + } + ] +} +``` + +

+You can also negate any rule by utilizing a `!` prefix: +`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`. +

+ +It is also possible to use inline references to registered evaluators without declaring rules, +in case you do not need providing extra parameters, or chaining multiple rules together. + +## Core Evaluators + +You can create new rules by chaining other rules and evaluators. + +| Key | Description | +| ---------- | ----------------------------------------------------------------------------- | +| core.every | Evaluates to `true` if all chained rules evaluate to `true`. | +| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. | +| core.not | Evaluates to `true` if all chained rules evaluate to `false`. | + +Below is an example of the composite rule definition that combines the following conditions: + +- user has selected a single file +- user is not using the **Trashcan** page + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "rules": [ + { + "id": "app.toolbar.canViewFile", + "type": "core.every", + "parameters": [ + { + "type": "rule", + "value": "app.selection.file" + }, + { + "type": "rule", + "value": "core.not", + "parameters": [ + { + "type": "rule", + "value": "app.navigation.isTrashcan" + } + ] + } + ] + } + ] +} +``` + +You can now declare a toolbar button action that is based on the rule above. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "features": { + "toolbar": [ + { + "id": "app.toolbar.preview", + "type": "button", + "title": "View File", + "icon": "open_in_browser", + "actions": { + "click": "VIEW_FILE" + }, + "rules": { + "visible": "app.toolbar.canViewFile" + } + } + ] + } +} +``` + +The button will be visible only when the linked rule evaluates to `true`. + +## Application Evaluators + +| Key | Description | +| ------------------------------- | ------------------------------------------------------------ | +| app.selection.canDelete | User has permission to delete selected node(s). | +| app.selection.canDownload | User can download selected node(s). | +| app.selection.notEmpty | At least one node is selected. | +| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. | +| app.selection.canAddFavorite | User can add selected node(s) to favorites. | +| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. | +| app.selection.first.canUpdate | User has permission to update selected node(s). | +| app.selection.file | A single File node is selected. | +| app.selection.file.canShare | User is able to share the selected file. | +| app.selection.file.isShared | A shared node is selected | +| app.selection.file.isLocked | File is locked for editing | +| app.selection.library | A single Library node is selected. | +| app.selection.isPrivateLibrary | A private Library node is selected. | +| app.selection.hasLibraryRole | The selected Library node has a role property. | +| app.selection.hasNoLibraryRole | The selected Library node has no role property. | +| app.selection.folder | A single Folder node is selected. | +| app.selection.folder.canUpdate | User has permissions to update the selected folder. | +| repository.isQuickShareEnabled | Whether the quick share repository option is enabled or not. | + +## Navigation Evaluators + +The application exposes a set of navigation-related evaluators to help developers restrict or enable certain actions based on the route or page displayed. + +The negated evaluators are provided just to simplify development, and to avoid having complex rule trees just to negate the rules, +for example mixing `core.every` and `core.not`. + +

+You can also negate any rule by utilizing a `!` prefix: +`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`. +

+ +| Key | Description | +| --------------------------------- | ------------------------------------------------------- | +| app.navigation.folder.canCreate | User can create content in the currently opened folder. | +| app.navigation.folder.canUpload | User can upload content to the currently opened folder. | +| app.navigation.isTrashcan | User is using the **Trashcan** page. | +| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. | +| app.navigation.isLibraries | User is using a **Libraries** page. | +| app.navigation.isNotLibraries | Current page is not a **Libraries** page. | +| app.navigation.isSharedFiles | User is using the **Shared Files** page. | +| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. | +| app.navigation.isFavorites | User is using the **Favorites** page. | +| app.navigation.isNotFavorites | Current page is not **Favorites** | +| app.navigation.isRecentFiles | User is using the **Recent Files** page. | +| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. | +| app.navigation.isSearchResults | User is using the **Search Results** page. | +| app.navigation.isNotSearchResults | Current page is not the **Search Results**. | + +

+See [Registration](/extending/registration) section for more details +on how to register your own entries to be re-used at runtime. +

+ +### Example + +The rule in the example below evaluates to `true` if all the conditions are met: + +- user has selected node(s) +- user is not using the **Trashcan** page +- user is not using a **Libraries** page (**My Libraries**, **Favorite Libraries** or **Libraries Search Results** pages) + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "rules": [ + { + "id": "app.toolbar.canCopyNode", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.notEmpty" }, + { "type": "rule", "value": "app.navigation.isNotTrashcan" }, + { "type": "rule", "value": "app.navigation.isNotLibraries" } + ] + } + ] +} +``` diff --git a/docs/extending/tutorials.md b/docs/extending/tutorials.md new file mode 100644 index 0000000000..7f1d7be4e3 --- /dev/null +++ b/docs/extending/tutorials.md @@ -0,0 +1,276 @@ +--- +--- + +# Tutorials + +## Custom route with parameters + +In this tutorial, we are going to implement the following features: + +- Update the **Trashcan** component to receive and log route parameters. +- Create a new route that points to the **Trashcan** component and uses the main layout. +- Create an action reference that allows redirecting to the new route. +- Create a button in the **New** menu that invokes an action. + +Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body: + +```typescript +import { ActivatedRoute, Params } from '@angular/router'; + +@Component({...}) +export class TrashcanComponent { + + constructor( + // ... + private route: ActivatedRoute + ) {} + + ngOnInit() { + // ... + + this.route.params.subscribe(({ nodeId }: Params) => { + console.log('node: ', nodeId); + }); + } + +} +``` + +The code above logs the current route parameters to the browser console +and is proof the integration works as expected. + +Next, add a new route definition as in the example below: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [ + { + "id": "custom.routes.trashcan", + "path": "ext/trashcan/:nodeId", + "component": "your.component.id", + "layout": "app.layout.main", + "auth": ["app.auth"] + } + ] +} +``` + +The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter. + +Also, we are going to use the default application layout (`app.layout.main`) +and authentication guards (`app.auth`). + +Next, create an action reference for the `NAVIGATE_ROUTE` application action +and pass route parameters: `/ext/trashcan` for the path, and `10` for the `nodeId` value. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [...], + + "actions": [ + { + "id": "custom.actions.trashcan", + "type": "NAVIGATE_ROUTE", + "payload": "$(['/ext/trashcan', '10'])" + } + ] +} +``` + +Finally, declare a new menu item for the `NEW` button and use the `custom.actions.trashcan` action created above. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [...], + "actions": [...], + + "features": { + "create": [ + { + "id": "custom.create.trashcan", + "type": "default", + "icon": "build", + "title": "Custom trashcan route", + "actions": { + "click": "custom.actions.trashcan" + } + } + ] + } +} +``` + +Now, if you run the application, you should see a new menu item called "Custom Trashcan Route" in the "NEW" dropdown. +Upon clicking this item you should navigate to the `/ext/trashcan/10` route containing a **Trashcan** component. + +Check the browser console output and ensure you have the following output: + +```text +node: 10 +``` + +You have successfully created a new menu button that invokes your custom action +and redirects you to the extra application route. + +## Dialog actions + +In this tutorial, we are going to create an action that invokes a custom material dialog. + +Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview) + +### Create a dialog + +```sh +ng g component dialogs/my-extension-dialog --module=app +``` + +According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module. + +Update the `src/app/app.module.ts` file according to the example below: + +```ts +@NgModule({ + imports: [...], + declarations: [ + ..., + MyExtensionDialogComponent + ], + entryComponents: [ + ..., + MyExtensionDialogComponent + ] +}) +``` + +Update `my-extension-dialog.component.ts`: + +```ts +import { Component } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; + +@Component({ + selector: 'aca-my-extension-dialog', + templateUrl: './my-extension-dialog.component.html', + styleUrls: ['./my-extension-dialog.component.scss'] +}) +export class MyExtensionDialogComponent { + constructor(public dialogRef: MatDialogRef) {} +} +``` + +Update `my-extension-dialog.component.html`: + +```html +

Delete all

+Are you sure? + + + + + +``` + +### Create an action + +Append the following code to the `src/app/store/actions/app.actions.ts`: + +```ts +export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG'; + +export class ShowMydDialogAction implements Action { + readonly type = SHOW_MY_DIALOG; +} +``` + +See also: + +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) + +### Create an effect + +Update `src/app/store/effects/app.effects.ts`: + +```ts +import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions'; + +@Injectable() +export class AppEffects { + constructor(...) {} + + @Effect({ dispatch: false }) + showMyDialog$ = this.actions$.pipe( + ofType(SHOW_MY_DIALOG), + map(() => {}) + ); + + // ... +} +``` + +See also: + +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) + +Update to raise a dialog + +```ts +import { MatDialog } from '@angular/material'; +import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component'; + +@Injectable() +export class AppEffects { + constructor( + ..., + private dialog: MatDialog + ) {} + + @Effect({ dispatch: false }) + showMyDialog$ = this.actions$.pipe( + ofType(SHOW_MY_DIALOG), + map(() => { + this.dialog.open(MyExtensionDialogComponent) + }) + ); + + ... + +} +``` + +### Register a toolbar action + +Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section: + +```json +{ + ..., + + "features": { + "toolbar": [ + { + "id": "my.custom.toolbar.button", + "order": 10, + "title": "Custom action", + "icon": "extension", + "actions": { + "click": "SHOW_MY_DIALOG" + } + } + ] + } +} +``` + +Now, once you run the application, you should see an extra button that invokes your dialog on every click. diff --git a/docs/features/README.md b/docs/features/README.md new file mode 100644 index 0000000000..1f4a0bfc51 --- /dev/null +++ b/docs/features/README.md @@ -0,0 +1,24 @@ +--- +--- + +# Application features + +The concept of this application is a simple user interface which makes accessing files in the Alfresco Content Services repository easy. + +Often Content Management systems provide more capabilities out of the box than most users need; +providing too many capabilities to these users prevents them from working efficiently, +so they may end up using unsanctioned file management solutions which presents a proliferation of content storage +and collaboration solutions as well as compliance issues for organizations. + +This application simplifies the complexity of Content Management and provides comprehensive extensibility features for developers, using the Alfresco Application Development Framework, to easily and quickly create custom solutions for specific user cases. + +## Contents + +- [User interface layout](/features/user-interface-layout) +- [Header](/features/header) +- [Side navigation](/features/side-navigation) +- [Document List Layout](/features/document-list-layout) +- [File Viewer](/features/file-viewer) +- [Info Drawer](/features/info-drawer) +- [Version Manager](/features/version-manager) +- [Search results](/features/search-results) diff --git a/docs/features/document-list-layout.md b/docs/features/document-list-layout.md new file mode 100644 index 0000000000..1d1726cdb7 --- /dev/null +++ b/docs/features/document-list-layout.md @@ -0,0 +1,170 @@ +--- +--- + +# Document List Layout + +The main area of the application is composed of several individual ADF components: + +1. [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html) +2. [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html) +3. [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html) +4. [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html) + +![](../images/doclist.png) + +The application has seven different Document List views which share commonalities between each view and subtle differences depending on the content being loaded which are explained below. + +## Personal Files + +Personal Files retrieves all content from the logged in user's home area (`/User Homes//`) in the repository; +if the user is ‘admin’ who does not have a home folder then the repository root folder is shown. + +Personal Files is the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component, +using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes). + +## File Libraries + +File Libraries retrieves all the sites that the user is a member of including what type of site it is: public, moderated or private. +File Libraries is the [Libraries](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/libraries) component, +using the [Sites API](https://api-explorer.alfresco.com/api-explorer/#/sites). + +When a user opens one of their sites then the content for the site's document library is shown. +To display the files and folders from a site (`/Sites//Document Library/`) the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component, +using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes) is used. + +## Shared Files + +The Shared Files view aggregates all files that have been shared using the QuickShare feature in the content repository. +The [Shared Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/shared-files) component uses the [shared-links API](https://api-explorer.alfresco.com/api-explorer/#/shared-links) +and includes extra columns to display where the file is +[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) +in the content repository and who created the shared link. + +## Recent Files + +The Recent Files view shows all the files that have been created or modified within the last 30 days by the current user. +The [Recent Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/recent-files) +component uses the Search API to query SOLR for changes made by the user and includes an extra column to display where the file is +[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) +in the content repository. + +## Favorites + +The Favorites view shows all files and folders from the content repository that have been marked as a favorite by the current user. +The [Favorites](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/favorites) component uses the +[favorites](https://api-explorer.alfresco.com/api-explorer/#/favorites) API to retrieve all the favorite nodes for the user +and includes an extra column to display where the file is +[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) +in the content repository. + +## Trash + +The Trash view shows all the items that a user has deleted, admin will see items deleted by all users. +The actions available in this view are Restore and Permanently Delete. +The [Trashcan](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/trashcan) component uses the +[trashcan](https://api-explorer.alfresco.com/api-explorer/#/trashcan) API to retrieve the deleted items +and perform the actions requested by the user and includes an extra column to display where the item was +[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link) +in the content repository before it was deleted. + +## Search Results + +The Search Results view shows the found items for a search query. It has a custom layout template and users can easily browse the results and perform actions on items. +For more information on the [SearchComponent](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search), please also check this [Search Results](/features/search-results) section. + +Another custom template layout shows the results when searching for libaries so that users can find, join and favorite libraries that they aren't already members of. + +## Actions and the Actions Toolbar + +All the views incorporate the [toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html) +component from the Alfresco Application Development Framework. + +Actions are displayed in the toolbar when item(s) are selected, or a right click is performed; apart from the Trash view they all display the following actions when the current user has the necessary permissions, +actions are automatically hidden when the user does not have permission. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ActionFileFolder
View + Opens the selected file using the Preview component, + where the file cannot be displayed natively in a browser a PDF rendition is obtained from the repository. + Not applicable
DownloadDownloads single files to the user's computer, when multiple files are selected they are compressed into a ZIP and then downloaded.Folders are automatically compressed into a ZIP and then downloaded to the user's computer.
EditNot applicableThe folder name and description can be edited in a dialog.
Favorite + Toggle the favorite mark on or off for files and folders, when multiple items are selected + and one or more are not favorites then the mark will be toggled on. +
Copy + Files and folders can be copied to another location in the content repository using the + content-node-selector component; + once the copy action has completed the user is notified and can undo the action (which permanently deletes the created copies). +
Move + Files and folders can be moved to another location in the content repository using the + content-node-selector component; + once the move action has completed the user is notified and can undo the action (which moves the items back to the original location). +
Share + Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application. Share links can automatically expire based on a date, the minimum expiry date is controlled by the Content Services repository, which is 1 day from the date of creation. + + Not applicable. +
Delete + Files and folders can be deleted from their location in the content repository; + once the delete action has completed the user is notified and can undo the action (which restores the items from the trash). +
Manage Versions + Versions of files can be viewed, uploaded, restored, downloaded and deleted by using the version manager dialog; + once each action has completed the list of versions is updated according to the change. + Not applicable
Permissions + Permissions on a file can be adjusted as required in a number of ways; disable inheritance from the parent folder, change a user or groups role and grant users/groups access. + Not available
+ +Besides the actions available in the toolbar users can +* single click on a file to view it, and a folder to open it +* single click an item to select it +* double click on a file to view it, and a folder to open it diff --git a/docs/features/file-viewer.md b/docs/features/file-viewer.md new file mode 100644 index 0000000000..a0dea420c5 --- /dev/null +++ b/docs/features/file-viewer.md @@ -0,0 +1,58 @@ +--- +--- + +# File Viewer + +The File Viewer has been created using the [ViewerComponent](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html) from the ADF. The Viewer has four main areas: + +![File Viewer](../images/File-Viewer.png) + +1. [Header & Toolbar](#header-and-toolbar) +2. [Content](#content) +3. [Thumbnails side pane](#thumbnails-side-pane) +4. [Viewer Controls](#viewer-controls) + +## Header and Toolbar + +The Header & Toolbar section of the viewer contains a number of features that relate to the file currently being displayed: + +- Close 'X' will return the user to the folder that contains the file. +- The name and file type icon is shown in the middle. +- Next and previous buttons will be displayed either side of the file name so that users can navigate to other files in the folder without navigating away from the viewer. +- Finally, on the right hand side an actions toolbar provides users with the ability to download, favorite, move, copy, delete, manage versions and view info panel. + +## Content + +The File Viewer consists of four separate views that handle displaying the content based on four types of content, covering various [file/mime](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html#supported-file-formats) types: + +- Document View: PDF files are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved. +- Image View: JPEG, PNG, GIF, BMP and SVG images are natively displayed in the application File Viewer. +- Media View: MP4, MP3, WAV, OGG and WEBM files are played natively application File Viewer. The File Viewer will download, by default, 50MB of the content at a time to ensure a smooth playback of the content. +- Text View: TXT, XML, JS, HTML, JSON and TS files are natively displayed as text in the application File Viewer. + +## Thumbnails side pane + +The Document View includes a thumbnails pane which can be activated by a button in the Viewer Actions toolbar. Clicking on a thumbnail will take a user directly to the selected page and as users scroll through a document the current page is highlighted in the pane. + +## Viewer Controls + +At the bottom of the content the Viewer Controls allow users to interact with the content in various ways; the actions available are dependant on the type of content being displayed. + +- Document View: + - Activate/Deactivate thumbnails pane + - Previous/Next page + - Jump to page number + - Zoom in/out + - Fit to page + - Print +- Image View: + - Zoom in/out + - Rotate left/right (does not alter content in the repository) + - Reset image + - Print +- Media View: + - Play/pause + - Timeline position + - Toggle audio + - Audio volume + - Full screen diff --git a/docs/features/header.md b/docs/features/header.md new file mode 100644 index 0000000000..8f2953b05a --- /dev/null +++ b/docs/features/header.md @@ -0,0 +1,39 @@ +--- +--- + +# Header + +The application [header](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/header) has three main elements. + +1. [Logo and Color](#logo-and-color) +2. [Search](#search) +3. [Current User](#current-user) + +![Header](../images/header.png) + +## Logo and Color + +Logo & app primary color - logo and color are configurable by updating the +[app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) file in the root folder of the project. +Please refer to the [Application Configuration](/getting-started/configuration) documentation for more information on how to change the logo and color. + +## Search + +The application [Search](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search) - +uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search). +The app provides a 'live' search feature, where users can open files and folders directly from the Search API results. + +![Search Input](../images/search.png) + +If you type `Enter` in the text input area, you are going to see +[Search Results](/features/search-results) page +with advanced filtering and faceted search. + +## Current User + +[Current User](https://github.com/Alfresco/alfresco-content-app/tree/development/src/app/components/current-user) - +displays the user's name, and a menu where users can logout. +Optionally through updating the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) +a language switching menu can be displayed. + +![Current User](../images/current-user.png) diff --git a/docs/features/info-drawer.md b/docs/features/info-drawer.md new file mode 100644 index 0000000000..62aa2162b6 --- /dev/null +++ b/docs/features/info-drawer.md @@ -0,0 +1,20 @@ +--- +--- + +# Info Drawer + +The Info Drawer displays node information in the right sidebar panel. It is created by using the [InfoDrawerComponent](https://alfresco.github.io/adf-component-catalog/components/InfoDrawerComponent.html). This info is available for both folder and file nodes. + +Currently, there are 2 tabs available: Properties and Versions. + +## Properties tab + +The Properties tab displays the node's metadata info by using the [ContentMetadataCardComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataCardComponent.html). + +![](images/content-metadata.png) + +For more information, please check also the [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html). + +## Comments tab + +The Comments tab displays all comments made on the selected node in the respoistory by using the [CommentsComponent](https://alfresco.github.io/adf-component-catalog/components/CommentsComponent.html). Users can post new comments that will be displayed immediately. diff --git a/docs/features/search-results.md b/docs/features/search-results.md new file mode 100644 index 0000000000..6c5aafe0b5 --- /dev/null +++ b/docs/features/search-results.md @@ -0,0 +1,19 @@ +--- +--- + +# Search Results + +Once you type the text in the Search Input component you are going to see the Search Results page + +![Search Results](../images/aca-search-results.png) + +This page consists of the following ADF components: + +- [Search Filter](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-filter.component.md) +- [Search Chip List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-chip-list.component.md) +- [Search Sorting Picker](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-sorting-picker.component.md) +- [Document List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/document-list.component.md) with custom layout template +- [Info Drawer](/features/info-drawer) with Metadata and [Version Management](#version-manager) +- [Toolbar with basic actions](/features/document-list-layout#actions-and-the-actions-toolbar) like `Preview`, `Download`, `Favorite`, `Copy`, etc. + +And also the Info Drawer, Toolbar and Node Selector dialogs for copy and move operations. diff --git a/docs/features/side-navigation.md b/docs/features/side-navigation.md new file mode 100644 index 0000000000..4d5e4c14d8 --- /dev/null +++ b/docs/features/side-navigation.md @@ -0,0 +1,30 @@ +--- +--- + +# Side Navigation + +The application [side navigation](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/sidenav) has two features: +a button menu and navigation links. + +![Side Navigation](../images/side-nav.png) + +## New button + +The New button displays a menu which provides three actions: + +- Create a new folder - provides a dialog which allows the creation of a new folder, the folder name is mandatory and the description is optional. +- Upload a file - invokes the operating system file browser and allows a user to select file(s) to upload into their current location in the content repository. +- Upload a folder - invokes the operating system folder browser and allows a user to select a folder to upload to their current location in the content repository. + +When an upload starts the [upload component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/upload) +is displayed which shows the user the progress of the uploads they have started. +The upload dialog persists on the screen and can be minimized; users are able to continue using the application whilst uploads are in progress +and uploads can be canceled which will stop uploads in progress or permanently delete already completed uploads. + +![Uploader](../images/uploader.png) + +## Navigation + +The navigation links are configurable via the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json). +Default configuration creates two sections. +See [Navigation](/getting-started/navigation) for more information about configuring the side navigation. diff --git a/docs/features/user-interface-layout.md b/docs/features/user-interface-layout.md new file mode 100644 index 0000000000..aa8f6b5b54 --- /dev/null +++ b/docs/features/user-interface-layout.md @@ -0,0 +1,12 @@ +--- +--- + +# User Interface - layout + +There are three main areas of the application controlled by the [Layout component](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/layout): + +1. [Application Header](/features/header) +2. [Side Navigation](/features/side-navigation) +3. [Document List](/features/document-list-layout) + +![Features](../images/features-01.png) diff --git a/docs/features/version-manager.md b/docs/features/version-manager.md new file mode 100644 index 0000000000..22dff20ec3 --- /dev/null +++ b/docs/features/version-manager.md @@ -0,0 +1,42 @@ +--- +--- + +# Version Manager + +The versions of a file can be viewed and managed by using the [VersionManagerComponent](https://alfresco.github.io/adf-component-catalog/components/VersionManagerComponent.html). + +There are 2 ways users can access the Version Manager: + +1) From the 'Manage Versions' option of the 'More actions' menu (check [Actions and the Actions Toolbar](/features/document-list-layout#actions-and-the-actions-toolbar)): + +![Version Manager Menu](../images/version-manager-action.png) +![Version Manager Dialog](../images/version-manager-dialog.png) + +2) From the [Info Drawer](/features/info-drawer) (the Details right panel): + +![Version Manager Inline](../images/version-manager-tab.png) + +## Upload new version + +A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mime type as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments. + +Please also check the [UploadVersionButtonComponent](https://alfresco.github.io/adf-component-catalog/components/UploadVersionButtonComponent.html). + +## Actions Menu + +Each item in the version list has a couple of actions available: Restore, Download and Delete. These are displayed if user has permission to do that specific action. The 'Download' and 'Delete' can be also disabled from the app.config. + +In the app.config.json file, these are the current settings for the ACA version manager: + +```json +{ + "adf-version-manager": { + "allowComments": true, + "allowDownload": true + } +} +``` + +Set the allowComments to false if the version comments should not be displayed on the version list. + +Clicking to delete a version of a file triggers a confirmation dialog. Please see the [ConfirmDialogComponent](https://alfresco.github.io/adf-component-catalog/components/ConfirmDialogComponent.html) for more info. diff --git a/docs/getting-started/navigation.md b/docs/getting-started/navigation.md index 6a2858bb6c..b6275c89d5 100644 --- a/docs/getting-started/navigation.md +++ b/docs/getting-started/navigation.md @@ -163,4 +163,4 @@ Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` defin ![](../images/navigation-03.png) -For more information about the content of a custom page see [Document List Layout](/#document-list-layout) section. +For more information about the content of a custom page see [Document List Layout](/features/document-list-layout) section. diff --git a/docs/help.md b/docs/help.md index 6c4f91632f..926f1aeba0 100644 --- a/docs/help.md +++ b/docs/help.md @@ -12,12 +12,6 @@ Visit Alfresco Community for resources that will help you find information to ge along with blog posts from the Alfresco developers and much more: https://community.alfresco.com/community/application-development-framework -## Alfresco ADF Gitter - -Join the vibrant community in Gitter where you can chat with experienced developers, -including the Alfresco employees working directly on the ADF and this example application project: -https://gitter.im/Alfresco/alfresco-ng2-components - ## Alfresco Developer Support Developer Support is a remote subscription-based support team that offers diff --git a/docs/index.html b/docs/index.html index c4f7d641ed..4ca46be5d5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,13 +20,21 @@ title: 'Home', path: '/' }, + { + title: 'App features', + path: '/features/' + }, { title: 'Getting Started', path: '/getting-started/' }, { title: 'Extending', - path: '/extending' + path: '/extending/' + }, + { + title: 'Tutorials', + path: '/tutorials/' }, { title: 'Get Help', diff --git a/docs/toc.md b/docs/toc.md index d3cc280ff3..653415e0b8 100644 --- a/docs/toc.md +++ b/docs/toc.md @@ -1,46 +1,39 @@ - [Home](/) - - [Introduction](/#introduction) - - [Features](/#features) - - [User interface layout](/#user-interface-layout) - - [Header](/#header) - - [Side navigation](/#side-navigation) - - [Document List Layout](/#document-list-layout) - - [File Viewer](/#file-viewer) - - [Info Drawer](/#info-drawer) - - [Version Manager](/#version-manager) - - [Search results](/#search-results1) - - [How to contribute](/#how-to-contribute) + - [Documentation](/#documentation) + - [How to contribute](/#how-to-contribute) +- [App features](/features/) + - [User interface layout](/features/user-interface-layout) + - [Header](/features/header) + - [Side navigation](/features/side-navigation) + - [Document List Layout](/features/document-list-layout) + - [File Viewer](/features/file-viewer) + - [Info Drawer](/features/info-drawer) + - [Version Manager](/features/version-manager) + - [Search results](/features/search-results) - [Getting started](/getting-started/) - - [Prerequisites](/getting-started/prerequisites) - - [Building from source](/getting-started/building-from-source) - - [Internationalization (i18n)](/getting-started/internationalization) - - [CORS](/getting-started/cors) - - [Configuration](/getting-started/configuration) - - [Navigation](/getting-started/navigation) - - [Docker](/getting-started/docker) -- [Extending](/extending) - - [Format](/extending#format) - - [Schema](/extending#schema) - - [Multiple files](/extending#multiple-files) - - [Startup behaviour](/extending#startup-behaviour) - - [Disabling content](/extending#disabling-content) - - [Routes](/extending#routes) - - [Components](/extending#components) - - [Actions](/extending#actions) - - [Application actions](/extending#application-actions) - - [Rules](/extending#rules) - - [Core evaluators](/extending#core-evaluators) - - [Application evaluators](/extending#application-evaluators) - - [Navigation evaluators](/extending#navigation-evaluators) - - [Application features](/extending#application-features) - - [Content actions](/extending#content-actions) - - [Create menu](/extending#create-menu) - - [Navigation bar](/extending#navigation-bar) - - [Sidebar (Info Drawer)](/extending#sidebar-info-drawer) - - [Toolbar](/extending#toolbar) - - [Context menu](/extending#context-menu) - - [Viewer](/extending#viewer) - - [Registration](/extending#registration) - - [Creating custom evaluator](/extending#creating-custom-evaluator) - - [Tutorials](/extending#tutorials) + - [Prerequisites](/getting-started/prerequisites) + - [Building from source](/getting-started/building-from-source) + - [Internationalization (i18n)](/getting-started/internationalization) + - [CORS](/getting-started/cors) + - [Configuration](/getting-started/configuration) + - [Navigation](/getting-started/navigation) + - [Docker](/getting-started/docker) +- [Extending](/extending/) + - [Extensibility features](/extending/extensibility-features) + - [Extension format](/extending/extension-format) + - [Routes](/extending/routes) + - [Components](/extending/components) + - [Actions](/extending/actions) + - [Application actions](/extending/application-actions) + - [Rules](/extending/rules) + - [Application features](/extending/application-features) + - [Custom icons](/extending/icons) + - [Registration](/extending/registration) + - [Creating custom evaluators](/extending/creating-custom-evaluators) + - [Tutorials](/extending/tutorials) + - [Redistributable libraries](/extending/redistributable-libraries) +- [Tutorials](/tutorials/) + - [Introduction to extending ACA](/tutorials/introduction-to-extending) + - [Custom route with parameters](/tutorials/custom-route-with-parameters) + - [Dialog actions](/tutorials/dialog-actions) - [Get help](/help) diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md new file mode 100644 index 0000000000..1758fcc80c --- /dev/null +++ b/docs/tutorials/README.md @@ -0,0 +1,12 @@ +--- +--- + +# Tutorials + +Learn more about developing with the Alfresco Content Application. + +## Contents + +- [Introduction to extending ACA](/tutorials/introduction-to-extending) +- [Custom route with parameters](/tutorials/custom-route-with-parameters) +- [Dialog actions](/tutorials/dialog-actions) \ No newline at end of file diff --git a/docs/tutorials/custom-route-with-parameters.md b/docs/tutorials/custom-route-with-parameters.md new file mode 100644 index 0000000000..cba3b730d7 --- /dev/null +++ b/docs/tutorials/custom-route-with-parameters.md @@ -0,0 +1,123 @@ +--- +--- + +# Custom route with parameters + +In this tutorial, we are going to implement the following features: + +- Update the **Trashcan** component to receive and log route parameters. +- Create a new route that points to the **Trashcan** component and uses the main layout. +- Create an action reference that allows redirecting to the new route. +- Create a button in the **New** menu that invokes an action. + +Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body: + +```typescript +import { ActivatedRoute, Params } from '@angular/router'; + +@Component({...}) +export class TrashcanComponent { + + constructor( + // ... + private route: ActivatedRoute + ) {} + + ngOnInit() { + // ... + + this.route.params.subscribe(({ nodeId }: Params) => { + console.log('node: ', nodeId); + }); + } + +} +``` + +The code above logs the current route parameters to the browser console +and is proof the integration works as expected. + +Next, add a new route definition as in the example below: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [ + { + "id": "custom.routes.trashcan", + "path": "ext/trashcan/:nodeId", + "component": "your.component.id", + "layout": "app.layout.main", + "auth": ["app.auth"] + } + ] +} +``` + +The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter. + +Also, we are going to use the default application layout (`app.layout.main`) +and authentication guards (`app.auth`). + +Next, create an action reference for the `NAVIGATE_ROUTE` application action +and pass route parameters: `/ext/trashcan` for the path, and `10` for the `nodeId` value. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [...], + + "actions": [ + { + "id": "custom.actions.trashcan", + "type": "NAVIGATE_ROUTE", + "payload": "$(['/ext/trashcan', '10'])" + } + ] +} +``` + +Finally, declare a new menu item for the `NEW` button and use the `custom.actions.trashcan` action created above. + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + + "routes": [...], + "actions": [...], + + "features": { + "create": [ + { + "id": "custom.create.trashcan", + "type": "default", + "icon": "build", + "title": "Custom trashcan route", + "actions": { + "click": "custom.actions.trashcan" + } + } + ] + } +} +``` + +Now, if you run the application, you should see a new menu item called "Custom Trashcan Route" in the "NEW" dropdown. +Upon clicking this item you should navigate to the `/ext/trashcan/10` route containing a **Trashcan** component. + +Check the browser console output and ensure you have the following output: + +```text +node: 10 +``` + +You have successfully created a new menu button that invokes your custom action +and redirects you to the extra application route. diff --git a/docs/tutorials/dialog-actions.md b/docs/tutorials/dialog-actions.md new file mode 100644 index 0000000000..0773afe4b7 --- /dev/null +++ b/docs/tutorials/dialog-actions.md @@ -0,0 +1,153 @@ +--- +--- + +# Dialog actions + +In this tutorial, we are going to create an action that invokes a custom material dialog. + +Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview) + +## Create a dialog + +```sh +ng g component dialogs/my-extension-dialog --module=app +``` + +According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module. + +Update the `src/app/app.module.ts` file according to the example below: + +```ts +@NgModule({ + imports: [...], + declarations: [ + ..., + MyExtensionDialogComponent + ], + entryComponents: [ + ..., + MyExtensionDialogComponent + ] +}) +``` + +Update `my-extension-dialog.component.ts`: + +```ts +import { Component } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; + +@Component({ + selector: 'aca-my-extension-dialog', + templateUrl: './my-extension-dialog.component.html', + styleUrls: ['./my-extension-dialog.component.scss'] +}) +export class MyExtensionDialogComponent { + constructor(public dialogRef: MatDialogRef) {} +} +``` + +Update `my-extension-dialog.component.html`: + +```html +

Delete all

+Are you sure? + + + + + +``` + +## Create an action + +Append the following code to the `src/app/store/actions/app.actions.ts`: + +```ts +export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG'; + +export class ShowMydDialogAction implements Action { + readonly type = SHOW_MY_DIALOG; +} +``` + +See also: + +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) + +## Create an effect + +Update `src/app/store/effects/app.effects.ts`: + +```ts +import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions'; + +@Injectable() +export class AppEffects { + constructor(...) {} + + @Effect({ dispatch: false }) + showMyDialog$ = this.actions$.pipe( + ofType(SHOW_MY_DIALOG), + map(() => {}) + ); + + // ... +} +``` + +See also: + +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) + +Update to raise a dialog + +```ts +import { MatDialog } from '@angular/material'; +import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component'; + +@Injectable() +export class AppEffects { + constructor( + ..., + private dialog: MatDialog + ) {} + + @Effect({ dispatch: false }) + showMyDialog$ = this.actions$.pipe( + ofType(SHOW_MY_DIALOG), + map(() => { + this.dialog.open(MyExtensionDialogComponent) + }) + ); + + ... + +} +``` + +## Register a toolbar action + +Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section: + +```json +{ + ..., + + "features": { + "toolbar": [ + { + "id": "my.custom.toolbar.button", + "order": 10, + "title": "Custom action", + "icon": "extension", + "actions": { + "click": "SHOW_MY_DIALOG" + } + } + ] + } +} +``` + +Now, once you run the application, you should see an extra button that invokes your dialog on every click. diff --git a/docs/tutorials/introduction-to-extending.md b/docs/tutorials/introduction-to-extending.md new file mode 100644 index 0000000000..fb850d25ec --- /dev/null +++ b/docs/tutorials/introduction-to-extending.md @@ -0,0 +1,6 @@ +--- +--- + +# Introduction to extending the Alfresco Content Application + +An introductory tutorial will appear here shortly. \ No newline at end of file diff --git a/e2e/components/breadcrumb/breadcrumb.ts b/e2e/components/breadcrumb/breadcrumb.ts index 2d50185b92..662e72743f 100755 --- a/e2e/components/breadcrumb/breadcrumb.ts +++ b/e2e/components/breadcrumb/breadcrumb.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { ElementFinder, ElementArrayFinder, by, promise } from 'protractor'; +import { ElementFinder, ElementArrayFinder, by } from 'protractor'; import { Component } from '../component'; export class Breadcrumb extends Component { diff --git a/e2e/components/data-table/data-table.ts b/e2e/components/data-table/data-table.ts index a96ba158ae..645e7273b9 100755 --- a/e2e/components/data-table/data-table.ts +++ b/e2e/components/data-table/data-table.ts @@ -46,6 +46,7 @@ export class DataTable extends Component { cell: '.adf-data-table-cell', locationLink: '.aca-location-link', nameLink: '.dl-link', + libraryRole: 'app-library-role-column', selectedIcon: '.mat-icon', @@ -76,11 +77,11 @@ export class DataTable extends Component { // Wait methods (waits for elements) waitForHeader() { - try { - return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT); - } catch (error) { - console.log('----- wait for header catch : ', error); - } + return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT, '--- timeout waitForHeader ---'); + } + + waitForBody() { + return browser.wait(EC.presenceOf(this.body), BROWSER_WAIT_TIMEOUT, '--- timeout waitForBody ---'); } async waitForEmptyState() { @@ -199,7 +200,6 @@ export class DataTable extends Component { async selectItem(name: string) { try{ const item = this.getRowFirstCell(name); - // await Utils.waitUntilElementClickable(item); await item.click(); } catch (e) { @@ -315,4 +315,8 @@ export class DataTable extends Component { const count = await this.menu.getItemsCount(); return count > 0; } + + async getLibraryRole(name: string) { + return await this.getRowByName(name).element(by.css(DataTable.selectors.libraryRole)).getText(); + } } diff --git a/e2e/components/datetime-picker/datetime-picker.ts b/e2e/components/datetime-picker/datetime-picker.ts index 6cf4e7367b..9b746a520e 100755 --- a/e2e/components/datetime-picker/datetime-picker.ts +++ b/e2e/components/datetime-picker/datetime-picker.ts @@ -26,6 +26,7 @@ import { ElementFinder, by, browser, ExpectedConditions as EC } from 'protractor'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; import { Component } from '../component'; +import * as moment from 'moment'; export class DateTimePicker extends Component { private static selectors = { @@ -72,11 +73,12 @@ export class DateTimePicker extends Component { } async setDefaultDay() { - const today = await this.dayPicker.element(by.css(DateTimePicker.selectors.today)).getText(); - const tomorrow = (parseInt(today, 10) + 1).toString(); + const today = moment(); + const tomorrow = today.add(1, 'day'); + const dayOfTomorrow = tomorrow.date(); const date = await this.getDate(); const year = await this.getYear(); - const elem = this.dayPicker.element(by.cssContainingText(DateTimePicker.selectors.firstActiveDay, tomorrow)); + const elem = this.dayPicker.element(by.cssContainingText(DateTimePicker.selectors.firstActiveDay, `${dayOfTomorrow}`)); await elem.click(); return `${date} ${year}`; } diff --git a/e2e/components/dialog/create-edit-folder-dialog.ts b/e2e/components/dialog/create-edit-folder-dialog.ts index 1536d2e803..d3d4cd5ec2 100755 --- a/e2e/components/dialog/create-edit-folder-dialog.ts +++ b/e2e/components/dialog/create-edit-folder-dialog.ts @@ -57,7 +57,11 @@ export class CreateOrEditFolderDialog extends Component { } async waitForDialogToClose() { - await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT); + await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT, '---- timeout waiting for dialog to close ----'); + } + + async isDialogOpen() { + return await browser.isElementPresent(by.css(CreateOrEditFolderDialog.selectors.root)); } async getTitle() { diff --git a/e2e/components/dialog/create-library-dialog.ts b/e2e/components/dialog/create-library-dialog.ts new file mode 100755 index 0000000000..074d5beeba --- /dev/null +++ b/e2e/components/dialog/create-library-dialog.ts @@ -0,0 +1,173 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { ElementFinder, by, browser, protractor, ExpectedConditions as EC } from 'protractor'; +import { BROWSER_WAIT_TIMEOUT } from '../../configs'; +import { Component } from '../component'; +import { Utils } from '../../utilities/utils'; + +export class CreateLibraryDialog extends Component { + private static selectors = { + root: 'app-library-dialog', + + title: '.mat-dialog-title', + nameInput: 'input[placeholder="Name" i]', + libraryIdInput: 'input[placeholder="Library ID" i]', + descriptionTextArea: 'textarea[placeholder="Description" i]', + button: '.mat-dialog-actions button', + radioButton: '.mat-radio-label', + radioChecked: 'mat-radio-checked', + errorMessage: '.mat-error' + }; + + title: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.title)); + nameInput: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.nameInput)); + libraryIdInput: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.libraryIdInput)); + descriptionTextArea: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.descriptionTextArea)); + visibilityPublic: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Public')); + visibilityModerated: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Moderated')); + visibilityPrivate: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Private')); + createButton: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.button, 'Create')); + cancelButton: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.button, 'Cancel')); + errorMessage: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.errorMessage)); + + constructor(ancestor?: ElementFinder) { + super(CreateLibraryDialog.selectors.root, ancestor); + } + + async waitForDialogToOpen() { + await browser.wait(EC.presenceOf(this.title), BROWSER_WAIT_TIMEOUT); + await browser.wait(EC.presenceOf(browser.element(by.css('.cdk-overlay-backdrop'))), BROWSER_WAIT_TIMEOUT); + } + + async waitForDialogToClose() { + await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT); + } + + async isDialogOpen() { + return await browser.isElementPresent(by.css(CreateLibraryDialog.selectors.root)); + } + + async getTitle() { + return await this.title.getText(); + } + + async isErrorMessageDisplayed() { + return await this.errorMessage.isDisplayed(); + } + + async getErrorMessage() { + await this.isErrorMessageDisplayed(); + return await this.errorMessage.getText(); + } + + async isNameDisplayed() { + return await this.nameInput.isDisplayed(); + } + + async isLibraryIdDisplayed() { + return await this.libraryIdInput.isDisplayed(); + } + + async isDescriptionDisplayed() { + return await this.descriptionTextArea.isDisplayed(); + } + + async isPublicDisplayed() { + return await this.visibilityPublic.isDisplayed(); + } + + async isModeratedDisplayed() { + return await this.visibilityModerated.isDisplayed(); + } + + async isPrivateDisplayed() { + return await this.visibilityPrivate.isDisplayed(); + } + + async enterName(name: string) { + await this.nameInput.clear(); + await Utils.typeInField(this.nameInput, name); + } + + async enterLibraryId(id: string) { + await this.libraryIdInput.clear(); + await Utils.typeInField(this.libraryIdInput, id); + } + + async enterDescription(description: string) { + await this.descriptionTextArea.clear(); + await Utils.typeInField(this.descriptionTextArea, description); + } + + async deleteNameWithBackspace() { + await this.nameInput.clear(); + await this.nameInput.sendKeys(' ', protractor.Key.CONTROL, 'a', protractor.Key.NULL, protractor.Key.BACK_SPACE); + } + + async isCreateEnabled() { + return await this.createButton.isEnabled(); + } + + async isCancelEnabled() { + return await this.cancelButton.isEnabled(); + } + + async clickCreate() { + await this.createButton.click(); + } + + async clickCancel() { + await this.cancelButton.click(); + await this.waitForDialogToClose(); + } + + async isPublicChecked() { + const elemClass = await this.visibilityPublic.element(by.xpath('..')).getAttribute('class'); + return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked); + } + + async isModeratedChecked() { + const elemClass = await this.visibilityModerated.element(by.xpath('..')).getAttribute('class'); + return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked); + } + + async isPrivateChecked() { + const elemClass = await this.visibilityPrivate.element(by.xpath('..')).getAttribute('class'); + return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked); + } + + async selectPublic() { + await this.visibilityPublic.click(); + } + + async selectModerated() { + await this.visibilityModerated.click(); + } + + async selectPrivate() { + await this.visibilityPrivate.click(); + } +} diff --git a/e2e/components/header/header.ts b/e2e/components/header/header.ts index 1b37b6c2ba..b14777c062 100755 --- a/e2e/components/header/header.ts +++ b/e2e/components/header/header.ts @@ -26,48 +26,30 @@ import { ElementFinder, by, browser } from 'protractor'; import { Component } from '../component'; import { UserInfo } from './user-info'; -import { protractor } from 'protractor'; -import { Utils } from '../../utilities/utils'; import { Menu } from '../menu/menu'; import { Toolbar } from './../toolbar/toolbar'; +import { SearchInput } from '../search/search-input'; export class Header extends Component { private locators = { root: 'app-header', logoLink: by.css('.app-menu__title'), userInfo: by.css('aca-current-user'), - searchButton: by.css('#adf-search-button'), - searchBar: by.css('#adf-control-input'), moreActions: by.id('app.header.more') }; logoLink: ElementFinder = this.component.element(this.locators.logoLink); userInfo: UserInfo = new UserInfo(this.component); - searchButton: ElementFinder = this.component.element(this.locators.searchButton); - searchBar: ElementFinder = this.component.element(this.locators.searchBar); moreActions: ElementFinder = browser.element(this.locators.moreActions); menu: Menu = new Menu(); toolbar: Toolbar = new Toolbar(); + searchInput: SearchInput = new SearchInput(); constructor(ancestor?: ElementFinder) { super('adf-layout-header', ancestor); } - async searchForText(text: string) { - await this.searchBar.clear(); - await this.searchBar.sendKeys(text); - await this.searchBar.sendKeys(protractor.Key.ENTER); - } - - async waitForSearchButton() { - await Utils.waitUntilElementClickable(this.searchButton); - } - - async waitForSearchBar() { - await Utils.waitUntilElementClickable(this.searchBar); - } - async openMoreMenu() { await this.moreActions.click(); await this.menu.waitForMenuToOpen(); diff --git a/e2e/components/info-drawer/info-drawer.ts b/e2e/components/info-drawer/info-drawer.ts index 3c99c3d21c..ba4a7a5fc4 100755 --- a/e2e/components/info-drawer/info-drawer.ts +++ b/e2e/components/info-drawer/info-drawer.ts @@ -26,6 +26,7 @@ import { ElementFinder, ElementArrayFinder, by, browser, ExpectedConditions as EC } from 'protractor'; import { Component } from '../component'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; +import { Utils } from './../../utilities/utils'; export class InfoDrawer extends Component { private static selectors = { @@ -40,10 +41,25 @@ export class InfoDrawer extends Component { activeTabContent: '.mat-tab-body-active .mat-tab-body-content adf-dynamic-tab', next: '.mat-tab-header-pagination-after .mat-tab-header-pagination-chevron', - previous: '.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron' + previous: '.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron', + + headerTitle: '.adf-info-drawer-layout-header-title', + + // metadata card + metadataTabContent: '.app-metadata-tab .mat-card-content', + metadataTabAction: '.app-metadata-tab .mat-card-actions .mat-button', + field: '.mat-form-field', + fieldLabelWrapper: '.mat-form-field-label-wrapper', + fieldInput: '.mat-input-element', + dropDown: '.mat-select', + visibilityOption: '.mat-option .mat-option-text', + + hint: '.mat-hint', + error: '.mat-error' }; header: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.header)); + headerTitle: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.headerTitle)); tabLabel: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.tabLabel)); tabLabelsList: ElementArrayFinder = this.component.all(by.css(InfoDrawer.selectors.tabLabel)); tabActiveLabel: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.tabActiveLabel)); @@ -53,6 +69,19 @@ export class InfoDrawer extends Component { nextButton: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.next)); previousButton: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.previous)); + metadataTabContent: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.metadataTabContent)); + metadataTabAction: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.metadataTabAction)); + fieldLabelWrapper: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.fieldLabelWrapper)); + fieldInput: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.fieldInput)); + + visibilityDropDown: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.dropDown)); + visibilityPublic: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Public')); + visibilityPrivate: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Private')); + visibilityModerated: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Moderated')); + + hint: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.hint)); + error: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.error)); + constructor(ancestor?: ElementFinder) { super(InfoDrawer.selectors.root, ancestor); } @@ -61,6 +90,10 @@ export class InfoDrawer extends Component { return await browser.wait(EC.presenceOf(this.header), BROWSER_WAIT_TIMEOUT); } + async isOpen() { + return await browser.isElementPresent(this.header); + } + async isEmpty() { if (await browser.isElementPresent(by.css(InfoDrawer.selectors.tabs))) { return false; @@ -91,5 +124,179 @@ export class InfoDrawer extends Component { async getComponentIdOfTab() { return await this.tabActiveContent.getAttribute('data-automation-id'); } + + async getHeaderTitle() { + return await this.headerTitle.getText(); + } + + getLabelWrapper(label: string) { + return this.component.element(by.cssContainingText(InfoDrawer.selectors.fieldLabelWrapper, label)); + } + + getFieldByName(fieldName: string) { + const wrapper = this.getLabelWrapper(fieldName); + return wrapper.element(by.xpath('..')).element(by.css(InfoDrawer.selectors.fieldInput)); + } + + async isFieldDisplayed(fieldName: string) { + return await browser.isElementPresent(this.getFieldByName(fieldName)); + } + + async isInputEnabled(fieldName: string) { + return this.getFieldByName(fieldName).isEnabled(); + } + + async isVisibilityEnabled() { + const wrapper = this.getLabelWrapper('Visibility'); + const field = wrapper.element(by.xpath('..')).element(by.css(InfoDrawer.selectors.dropDown)); + return await field.isEnabled(); + } + + async getValueOfField(fieldName: string) { + return await this.getFieldByName(fieldName).getText(); + } + + async enterTextInInput(fieldName: string, text: string) { + const input = this.getFieldByName(fieldName); + await input.clear(); + return await input.sendKeys(text); + } + + async typeTextInInput(fieldName: string, text: string) { + const input = this.getFieldByName(fieldName); + await input.clear(); + return await Utils.typeInField(input, text); + } + + getButton(button: string) { + return this.component.element(by.cssContainingText(InfoDrawer.selectors.metadataTabAction, button)); + } + + async isButtonDisplayed(button: string) { + return browser.isElementPresent(this.getButton(button)); + } + + async isButtonEnabled(button: string) { + return await this.getButton(button).isEnabled(); + } + + async clickButton(button: string) { + return await this.getButton(button).click(); + } + + async isButtonDisabled(button: string) { + try { + const disabled = await this.getButton(button).getAttribute('disabled'); + return disabled; + } catch (error) { + console.log('----- isButtonDisabled catch: ', error); + } + } + + async isMessageDisplayed() { + return await browser.isElementPresent(this.hint); + } + + async getMessage() { + return await this.hint.getText(); + } + + async isErrorDisplayed() { + return await browser.isElementPresent(this.error); + } + + async getError() { + return await this.error.getText(); + } + + + // --------------- + async isNameDisplayed() { + return await this.isFieldDisplayed('Name'); + } + + async isDescriptionDisplayed() { + return await this.isFieldDisplayed('Description'); + } + + async isVisibilityDisplayed() { + return await this.isFieldDisplayed('Visibility'); + } + + async isLibraryIdDisplayed() { + return await this.isFieldDisplayed('Library ID'); + } + + async getName() { + return await this.getValueOfField('Name'); + } + + async getVisibility() { + return await this.getValueOfField('Visibility'); + } + + async getLibraryId() { + return await this.getValueOfField('Library ID'); + } + + async getDescription() { + return await this.getValueOfField('Description'); + } + + async isNameEnabled() { + return await this.isInputEnabled('Name'); + } + + async isLibraryIdEnabled() { + return await this.isInputEnabled('Library ID'); + } + + async isDescriptionEnabled() { + return await this.isInputEnabled('Description'); + } + + async enterName(name: string) { + return await this.enterTextInInput('Name', name); + } + + async typeName(name: string) { + return await this.typeTextInInput('Name', name); + } + + async enterDescription(desc: string) { + return await this.enterTextInInput('Description', desc); + } + + async typeDescription(desc: string) { + return await this.typeTextInInput('Description', desc); + } + + async waitForDropDownToOpen() { + await browser.wait(EC.presenceOf(this.visibilityDropDown), BROWSER_WAIT_TIMEOUT); + } + + async waitForDropDownToClose() { + await browser.wait(EC.stalenessOf(browser.$('.mat-option .mat-option-text')), BROWSER_WAIT_TIMEOUT); + } + + async setVisibility(visibility: string) { + const val = visibility.toLowerCase(); + + await this.visibilityDropDown.click(); + await this.waitForDropDownToOpen(); + + if (val === 'public') { + await this.visibilityPublic.click(); + } else if (val === 'private') { + await this.visibilityPrivate.click(); + } else if (val === 'moderated') { + await this.visibilityModerated.click(); + } else { + console.log('----- invalid visibility', val); + } + + await this.waitForDropDownToClose(); + } + } diff --git a/e2e/components/menu/menu.ts b/e2e/components/menu/menu.ts index 59d5fd23ec..4edfa7633b 100755 --- a/e2e/components/menu/menu.ts +++ b/e2e/components/menu/menu.ts @@ -35,7 +35,9 @@ export class Menu extends Component { icon: '.mat-icon', uploadFiles: 'app-upload-files', - submenu: 'app-context-menu-item .mat-menu-item' + submenu: 'app-context-menu-item .mat-menu-item', + + share: `[data-automation-id='share-action-button']` }; items: ElementArrayFinder = this.component.all(by.css(Menu.selectors.item)); @@ -43,6 +45,9 @@ export class Menu extends Component { uploadFiles: ElementFinder = browser.element(by.id(Menu.selectors.uploadFiles)); submenus: ElementArrayFinder = browser.element.all(by.css(Menu.selectors.submenu)); + shareAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.share, 'Share')); + shareEditAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.share, 'Shared link settings')); + constructor(ancestor?: ElementFinder) { super(Menu.selectors.root, ancestor); } @@ -58,11 +63,6 @@ export class Menu extends Component { } async closeMenu() { - // if (await this.backdrop.isPresent()) { - // return await this.backdrop.click(); - // } else { - // return await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - // } return Utils.pressEscape(); } @@ -148,7 +148,7 @@ export class Menu extends Component { } async isMenuItemPresent(title: string) { - return await this.component.element(by.cssContainingText(Menu.selectors.item, title)).isPresent(); + return await browser.element(by.cssContainingText(Menu.selectors.item, title)).isPresent(); } async isSubMenuItemPresent(title: string) { @@ -172,4 +172,14 @@ export class Menu extends Component { uploadFile() { return this.uploadFiles; } + + async clickShareAction() { + const action = this.shareAction; + await action.click(); + } + + async clickShareEditAction() { + const action = this.shareEditAction; + await action.click(); + } } diff --git a/e2e/components/search/search-input.ts b/e2e/components/search/search-input.ts new file mode 100755 index 0000000000..5bf99c1650 --- /dev/null +++ b/e2e/components/search/search-input.ts @@ -0,0 +1,146 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { ElementFinder, browser, by, until, protractor } from 'protractor'; +import { BROWSER_WAIT_TIMEOUT } from '../../configs'; +import { Component } from '../component'; + +export class SearchInput extends Component { + private static selectors = { + root: 'aca-search-input', + searchContainer: '.app-search-container', + searchButton: '.app-search-button', + searchControl: '.app-search-control', + searchInput: 'app-control-input', + searchOptionsArea: 'search-options', + optionCheckbox: '.mat-checkbox' + }; + + searchButton: ElementFinder = this.component.element(by.css(SearchInput.selectors.searchButton)); + searchContainer: ElementFinder = browser.element(by.css(SearchInput.selectors.searchContainer)); + searchBar: ElementFinder = browser.element(by.id(SearchInput.selectors.searchInput)); + searchOptionsArea: ElementFinder = browser.element(by.id(SearchInput.selectors.searchOptionsArea)); + searchFilesOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Files')); + searchFoldersOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Folders')); + searchLibrariesOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Libraries')); + + constructor(ancestor?: ElementFinder) { + super(SearchInput.selectors.root, ancestor); + } + + async isSearchContainerDisplayed() { + return (await this.searchContainer.isDisplayed()) && (await this.searchButton.isDisplayed()); + } + + async clickSearchButton() { + await this.searchButton.click(); + } + + async isOptionsAreaDisplayed() { + await browser.wait(until.elementLocated(by.css(SearchInput.selectors.searchControl)), BROWSER_WAIT_TIMEOUT); + return await browser.isElementPresent(this.searchOptionsArea); + } + + async clickFilesOption() { + return await this.searchFilesOption.click(); + } + + async clickFoldersOption() { + return await this.searchFoldersOption.click(); + } + + async clickLibrariesOption() { + return await this.searchLibrariesOption.click(); + } + + async isFilesOptionEnabled() { + const optClass = await this.searchFilesOption.getAttribute('class'); + return !optClass.includes('mat-checkbox-disabled'); + } + + async isFoldersOptionEnabled() { + const optClass = await this.searchFoldersOption.getAttribute('class'); + return !optClass.includes('mat-checkbox-disabled'); + } + + async isLibrariesOptionEnabled() { + const optClass = await this.searchLibrariesOption.getAttribute('class'); + return !optClass.includes('mat-checkbox-disabled'); + } + + async isFilesOptionChecked() { + const optClass = await this.searchFilesOption.getAttribute('class'); + return optClass.includes('mat-checkbox-checked'); + } + + async isFoldersOptionChecked() { + const optClass = await this.searchFoldersOption.getAttribute('class'); + return optClass.includes('mat-checkbox-checked'); + } + + async isLibrariesOptionChecked() { + const optClass = await this.searchLibrariesOption.getAttribute('class'); + return optClass.includes('mat-checkbox-checked'); + } + + async clearOptions() { + if (await this.isFilesOptionChecked()) { + await this.clickFilesOption(); + } + if (await this.isFoldersOptionChecked()) { + await this.clickFoldersOption(); + } + if (await this.isLibrariesOptionChecked()) { + await this.clickLibrariesOption(); + } + } + + async checkOnlyFiles() { + await this.clearOptions(); + await this.clickFilesOption(); + } + + async checkOnlyFolders() { + await this.clearOptions(); + await this.clickFoldersOption(); + } + + async checkFilesAndFolders() { + await this.clearOptions(); + await this.clickFilesOption(); + await this.clickFoldersOption(); + } + + async checkLibraries() { + await this.clearOptions(); + await this.clickLibrariesOption(); + } + + async searchFor(text: string) { + await this.searchBar.clear(); + await this.searchBar.sendKeys(text); + await this.searchBar.sendKeys(protractor.Key.ENTER); + } +} diff --git a/e2e/components/sidenav/sidenav.ts b/e2e/components/sidenav/sidenav.ts index 2dc92966ba..145c772e4b 100755 --- a/e2e/components/sidenav/sidenav.ts +++ b/e2e/components/sidenav/sidenav.ts @@ -23,7 +23,8 @@ * along with Alfresco. If not, see . */ -import { ElementFinder, ElementArrayFinder, by } from 'protractor'; +import { ElementFinder, ElementArrayFinder, by, element } from 'protractor'; +import { SIDEBAR_LABELS } from '../../configs'; import { Menu } from '../menu/menu'; import { Component } from '../component'; import { Utils } from '../../utilities/utils'; @@ -31,9 +32,12 @@ import { Utils } from '../../utilities/utils'; export class Sidenav extends Component { private static selectors = { root: 'app-sidenav', - link: '.sidenav-menu__item', - label: '.menu__item--label', - activeLink: '.menu__item--active', + link: '.menu__item', + label: '.item--label', + expansion_panel: ".mat-expansion-panel-header", + expansion_panel_content: ".mat-expansion-panel-body", + active: 'item--active', + activeLink: '.item--active', newButton: '[data-automation-id="create-button"]' }; @@ -47,6 +51,23 @@ export class Sidenav extends Component { super(Sidenav.selectors.root, ancestor); } + private async expandMenu(name: string) { + try{ + + if (await element(by.cssContainingText('.mat-expanded', name)).isPresent()) { + return Promise.resolve(); + } else { + const link = this.getLink(name); + await Utils.waitUntilElementClickable(link); + await link.click(); + await element(by.css(Sidenav.selectors.expansion_panel_content)).isPresent(); + } + + } catch (e) { + console.log('---- sidebar navigation catch expandMenu: ', e); + } + } + async openNewMenu() { const { menu, newButton } = this; @@ -54,37 +75,59 @@ export class Sidenav extends Component { await menu.waitForMenuToOpen(); } - async openCreateDialog() { + async openCreateFolderDialog() { await this.openNewMenu(); await this.menu.clickMenuItem('Create folder'); } - async isActiveByLabel(label: string) { - const className = await this.getLinkByLabel(label).getAttribute('class'); - return className.includes(Sidenav.selectors.activeLink.replace('.', '')); + async openCreateLibraryDialog() { + await this.openNewMenu(); + await this.menu.clickMenuItem('Create Library'); + } + + async isActive(name: string) { + const className = await this.getLinkLabel(name).getAttribute('class'); + return className.includes(Sidenav.selectors.active); } - getLink(label: string) { - return this.component.element(by.cssContainingText(Sidenav.selectors.link, label)); + async childIsActive(name: string) { + const childClass = await this.getLinkLabel(name).element(by.css('span')).getAttribute('class'); + return childClass.includes(Sidenav.selectors.active); } - getLinkByLabel(label: string) { - return this.component.element(by.cssContainingText(Sidenav.selectors.label, label)); - // return browser.element(by.xpath(`.//*[.="${label}" and class="${Sidenav.selectors.label}"]`)) + getLink(name: string) { + return this.getLinkLabel(name).element(by.xpath('..')); } - async getLinkTooltip(label: string) { - return await this.getLink(label).getAttribute('title'); + getLinkLabel(name: string) { + return this.component.element(by.cssContainingText(Sidenav.selectors.label, name)); } - async navigateToLinkByLabel(label: string) { + getActiveLink() { + return this.activeLink; + } + + async getLinkTooltip(name: string) { + return await this.getLink(name).getAttribute('title'); + } + + async navigateToLink(name: string) { try{ - const link = this.getLinkByLabel(label); + const link = this.getLinkLabel(name); await Utils.waitUntilElementClickable(link); return await link.click(); } catch (e){ - console.log('---- sidebar navigation catch : ', e); + console.log('---- sidebar navigation catch navigateToLink: ', e); } } + + async isFileLibrariesMenuExpanded() { + return await element(by.cssContainingText('.mat-expanded', SIDEBAR_LABELS.FILE_LIBRARIES)).isPresent(); + } + + async expandFileLibraries() { + return await this.expandMenu(SIDEBAR_LABELS.FILE_LIBRARIES); + } + } diff --git a/e2e/components/toolbar/toolbar.ts b/e2e/components/toolbar/toolbar.ts index 0a0a7e2095..d337b8f6cb 100755 --- a/e2e/components/toolbar/toolbar.ts +++ b/e2e/components/toolbar/toolbar.ts @@ -30,11 +30,16 @@ import { Component } from '../component'; export class Toolbar extends Component { private static selectors = { root: '.adf-toolbar', - button: '.mat-icon-button' + button: 'button', + + share: `.mat-icon-button[title='Share']`, + shareEdit: `.mat-icon-button[title='Shared link settings']` }; menu: Menu = new Menu(); buttons: ElementArrayFinder = this.component.all(by.css(Toolbar.selectors.button)); + shareButton: ElementFinder = this.component.element(by.css(Toolbar.selectors.share)); + shareEditButton: ElementFinder = this.component.element(by.css(Toolbar.selectors.shareEdit)); constructor(ancestor?: ElementFinder) { super(Toolbar.selectors.root, ancestor); @@ -81,4 +86,22 @@ export class Toolbar extends Component { const btn = this.getButtonByTitleAttribute(title); await btn.click(); } + + async clickShareButton() { + const btn = this.shareButton; + await btn.click(); + } + + async isShareButtonPresent() { + return await browser.isElementPresent(this.shareButton); + } + + async clickShareEditButton() { + const btn = this.shareEditButton; + await btn.click(); + } + + async isShareEditButtonPresent() { + return await browser.isElementPresent(this.shareEditButton); + } } diff --git a/e2e/configs.ts b/e2e/configs.ts index acb00f33c1..18f23a8859 100755 --- a/e2e/configs.ts +++ b/e2e/configs.ts @@ -45,7 +45,8 @@ export const E2E_ROOT_PATH = __dirname; // Application Routes export const APP_ROUTES = { FAVORITES: '/favorites', - FILE_LIBRARIES: '/libraries', + MY_LIBRARIES: '/libraries', + FAVORITE_LIBRARIES: '/favorite/libraries', LOGIN: '/login', LOGOUT: '/logout', PERSONAL_FILES: '/personal-files', @@ -58,6 +59,8 @@ export const APP_ROUTES = { export const SIDEBAR_LABELS = { PERSONAL_FILES: 'Personal Files', FILE_LIBRARIES: 'File Libraries', + MY_LIBRARIES: 'My Libraries', + FAVORITE_LIBRARIES: 'Favorite Libraries', SHARED_FILES: 'Shared', RECENT_FILES: 'Recent Files', FAVORITES: 'Favorites', @@ -67,7 +70,9 @@ export const SIDEBAR_LABELS = { // Page titles export const PAGE_TITLES = { VIEWER: 'Preview', - SEARCH: 'Search Results' + SEARCH: 'Search Results', + MY_LIBRARIES: 'My Libraries', + FAVORITE_LIBRARIES: 'Favorite Libraries' }; // Site visibility @@ -79,10 +84,22 @@ export const SITE_VISIBILITY = { // Site roles export const SITE_ROLES = { - SITE_CONSUMER: 'SiteConsumer', - SITE_COLLABORATOR: 'SiteCollaborator', - SITE_CONTRIBUTOR: 'SiteContributor', - SITE_MANAGER: 'SiteManager' + SITE_CONSUMER: { + ROLE: 'SiteConsumer', + LABEL: 'Consumer' + }, + SITE_COLLABORATOR: { + ROLE: 'SiteCollaborator', + LABEL: 'Collaborator' + }, + SITE_CONTRIBUTOR: { + ROLE: 'SiteContributor', + LABEL: 'Contributor' + }, + SITE_MANAGER: { + ROLE: 'SiteManager', + LABEL: 'Manager' + } }; export const FILES = { diff --git a/e2e/pages/browsing-page.ts b/e2e/pages/browsing-page.ts index dfd75628ce..31b25dd903 100755 --- a/e2e/pages/browsing-page.ts +++ b/e2e/pages/browsing-page.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { promise } from 'protractor'; import { Header, DataTable, Pagination, Toolbar, Breadcrumb, Sidenav } from '../components/components'; +import { SIDEBAR_LABELS } from './../configs'; import { Page } from './page'; export class BrowsingPage extends Page { @@ -38,4 +38,80 @@ export class BrowsingPage extends Page { async signOut() { await this.header.userInfo.signOut(); } + + + // helper methods + + async clickPersonalFilesAndWait() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.PERSONAL_FILES); + await this.dataTable.waitForHeader(); + } + + async clickPersonalFiles() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.PERSONAL_FILES); + } + + + async clickFileLibrariesAndWait() { + await this.sidenav.expandFileLibraries(); + await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES); + await this.dataTable.waitForHeader(); + } + + async clickFileLibraries() { + await this.sidenav.expandFileLibraries(); + await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES); + } + + async goToFavoriteLibraries() { + await this.sidenav.expandFileLibraries(); + await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITE_LIBRARIES); + } + + async goToMyLibraries() { + if ( !(await this.sidenav.isFileLibrariesMenuExpanded()) ) { + await this.sidenav.expandFileLibraries(); + } + await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES); + } + + async clickRecentFilesAndWait() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.RECENT_FILES); + await this.dataTable.waitForHeader(); + } + + async clickRecentFiles() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.RECENT_FILES); + } + + + async clickSharedFilesAndWait() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.SHARED_FILES); + await this.dataTable.waitForHeader(); + } + + async clickSharedFiles() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.SHARED_FILES); + } + + + async clickFavoritesAndWait() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITES); + await this.dataTable.waitForHeader(); + } + + async clickFavorites() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITES); + } + + + async clickTrashAndWait() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.TRASH); + await this.dataTable.waitForHeader(); + } + + async clickTrash() { + await this.sidenav.navigateToLink(SIDEBAR_LABELS.TRASH); + } + } diff --git a/e2e/pages/page.ts b/e2e/pages/page.ts index 4697185e71..ac8c67d652 100755 --- a/e2e/pages/page.ts +++ b/e2e/pages/page.ts @@ -23,22 +23,16 @@ * along with Alfresco. If not, see . */ -import { - browser, - element, - by, - ElementFinder, - ExpectedConditions as EC -} from 'protractor'; +import { browser, by, ElementFinder, ExpectedConditions as EC, until } from 'protractor'; import { BROWSER_WAIT_TIMEOUT, USE_HASH_STRATEGY } from './../configs'; export abstract class Page { - private locators = { - app: by.css('app-root'), - layout: by.css('app-layout'), - overlay: by.css('.cdk-overlay-container'), - dialogContainer: by.css('.mat-dialog-container'), - snackBarContainer: '.cdk-overlay-pane .mat-snack-bar-container', + private static locators = { + app: 'app-root', + layout: 'app-layout', + overlay: '.cdk-overlay-container', + dialogContainer: '.mat-dialog-container', + snackBarContainer: '.mat-snack-bar-container', snackBar: '.mat-simple-snackbar', snackBarAction: '.mat-simple-snackbar-action button', @@ -47,17 +41,17 @@ export abstract class Page { genericErrorTitle: '.generic-error__title' }; - public app: ElementFinder = element(this.locators.app); - public layout: ElementFinder = element(this.locators.layout); - public overlay: ElementFinder = element(this.locators.overlay); - snackBar: ElementFinder = browser.$(this.locators.snackBar); - dialogContainer: ElementFinder = element(this.locators.dialogContainer); - snackBarContainer: ElementFinder = browser.$(this.locators.snackBarContainer); - snackBarAction: ElementFinder = browser.$(this.locators.snackBarAction); + app: ElementFinder = browser.element(by.css(Page.locators.app)); + layout: ElementFinder = browser.element(by.css(Page.locators.layout)); + overlay: ElementFinder = browser.element(by.css(Page.locators.overlay)); + snackBar: ElementFinder = browser.element(by.css(Page.locators.snackBar)); + dialogContainer: ElementFinder = browser.element(by.css(Page.locators.dialogContainer)); + snackBarContainer: ElementFinder = browser.element(by.css(Page.locators.snackBarContainer)); + snackBarAction: ElementFinder = browser.element(by.css(Page.locators.snackBarAction)); - genericError: ElementFinder = browser.$(this.locators.genericError); - genericErrorIcon: ElementFinder = browser.$(this.locators.genericErrorIcon); - genericErrorTitle: ElementFinder = browser.$(this.locators.genericErrorTitle); + genericError: ElementFinder = browser.element(by.css(Page.locators.genericError)); + genericErrorIcon: ElementFinder = browser.element(by.css(Page.locators.genericErrorIcon)); + genericErrorTitle: ElementFinder = browser.element(by.css(Page.locators.genericErrorTitle)); constructor(public url: string = '') {} @@ -76,7 +70,7 @@ export abstract class Page { } waitForSnackBarToAppear() { - return browser.wait(EC.visibilityOf(this.snackBarContainer), BROWSER_WAIT_TIMEOUT); + return browser.wait(until.elementLocated(by.css('.mat-snack-bar-container')), BROWSER_WAIT_TIMEOUT, '------- timeout waiting for snackbar to appear'); } async waitForSnackBarToClose() { @@ -87,37 +81,20 @@ export abstract class Page { await browser.wait(EC.visibilityOf(this.dialogContainer), BROWSER_WAIT_TIMEOUT); } - async waitForDialogToClose() { - await browser.wait(EC.not(EC.visibilityOf(this.dialogContainer)), BROWSER_WAIT_TIMEOUT); - } - async refresh() { await browser.refresh(); await this.waitForApp(); } - getDialogActionByLabel(label) { - return element(by.cssContainingText('.mat-button-wrapper', label)); - } - - async isSnackBarDisplayed() { - return await this.snackBar.isDisplayed(); - } - async getSnackBarMessage() { - await this.waitForSnackBarToAppear(); - return await this.snackBar.getAttribute('innerText'); + const elem = await this.waitForSnackBarToAppear(); + return await elem.getAttribute('innerText'); } async clickSnackBarAction() { try { - - // await this.waitForSnackBarToAppear(); - - // return browser.executeScript(function (elem) { - // elem.click(); - // }, this.snackBarAction); - return await this.snackBarAction.click(); + const action = browser.wait(until.elementLocated(by.css('.mat-simple-snackbar-action button')), BROWSER_WAIT_TIMEOUT, '------- timeout waiting for snack action to appear'); + return await action.click(); } catch (e) { console.log(e, '.......failed on click snack bar action.........'); } @@ -130,4 +107,5 @@ export abstract class Page { async getGenericErrorTitle() { return await this.genericErrorTitle.getText(); } + } diff --git a/e2e/pages/pages.ts b/e2e/pages/pages.ts index 196228230b..cc8b384433 100755 --- a/e2e/pages/pages.ts +++ b/e2e/pages/pages.ts @@ -25,4 +25,4 @@ export * from './browsing-page'; export * from './login-page'; -export * from './logout-page'; +export * from './search-results-page'; diff --git a/e2e/pages/search-results-page.ts b/e2e/pages/search-results-page.ts new file mode 100755 index 0000000000..76c0b3ee93 --- /dev/null +++ b/e2e/pages/search-results-page.ts @@ -0,0 +1,55 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser, ElementFinder, by, ExpectedConditions as EC } from 'protractor'; +import { BrowsingPage } from './browsing-page'; +import { BROWSER_WAIT_TIMEOUT } from '../configs'; + +export class SearchResultsPage extends BrowsingPage { + + private static selectors = { + root: 'aca-search-results', + filter: 'adf-search-filter', + expansionPanel: 'mat-expansion-panel', + size: '#expansion-panel-SEARCH.CATEGORIES.SIZE', + createdDate: '#expansion-panel-SEARCH.CATEGORIES.CREATED_DATE', + modifiedDate: '#expansion-panel-SEARCH.CATEGORIES.MODIFIED_DATE', + fileType: '#expansion-panel-SEARCH.FACET_FIELDS.FILE_TYPE', + creator: '#expansion-panel-SEARCH.CATEGORIES.CREATOR', + modifier: '#expansion-panel-SEARCH.CATEGORIES.MODIFIER', + location: '#expansion-panel-SEARCH.CATEGORIES.LOCATION', + + resultsContent: 'adf-search-results__content', + resultsContentHeader: 'adf-search-results__content-header', + resultsInfoText: 'adf-search-results--info-text', + resultsFacets: 'adf-search-results__facets', + + sortingPicker: 'adf-sorting-picker' + }; + + root: ElementFinder = browser.$(SearchResultsPage.selectors.root); + + +} diff --git a/e2e/suites/actions/context-menu-multiple-selection.test.ts b/e2e/suites/actions/context-menu-multiple-selection.test.ts index 232fc7ab1b..85a472b040 100755 --- a/e2e/suites/actions/context-menu-multiple-selection.test.ts +++ b/e2e/suites/actions/context-menu-multiple-selection.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -54,7 +54,6 @@ describe('Context menu actions - multiple selection : ', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable } = page; const contextMenu = dataTable.menu; @@ -67,7 +66,7 @@ describe('Context menu actions - multiple selection : ', () => { folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id; await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - const docLibId = (await apis.user.sites.getDocLibId(siteName)); + const docLibId = await apis.user.sites.getDocLibId(siteName); await apis.user.nodes.createFile(file1Site, docLibId); await apis.user.nodes.createFile(file2Site, docLibId); await apis.user.nodes.createFolder(folder1Site, docLibId); @@ -94,7 +93,6 @@ describe('Context menu actions - multiple selection : ', () => { await apis.user.nodes.deleteNodesById([ file1Id, file2Id, folder1Id, folder2Id ]); await apis.user.sites.deleteSite(siteName); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -103,15 +101,13 @@ describe('Context menu actions - multiple selection : ', () => { describe('Generic tests', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.clearSelection(); done(); }); it('Context menu appears on right click on a multiple selection of items - [C286268]', async () => { await dataTable.selectMultipleItems([ file1, file2 ]); - // await dataTable.rightClickOnItem(file1); await dataTable.rightClickOnMultipleSelection(); expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); }); @@ -131,8 +127,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('on Personal Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -179,8 +174,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('on File Libraries', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.clearSelection(); @@ -230,8 +224,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('on Shared Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -253,8 +246,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('Recent Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -276,8 +268,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('Favorites', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.clearSelection(); done(); }); @@ -328,8 +319,7 @@ describe('Context menu actions - multiple selection : ', () => { describe('Trash', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); await dataTable.clearSelection(); done(); }); diff --git a/e2e/suites/actions/context-menu-single-selection.test.ts b/e2e/suites/actions/context-menu-single-selection.test.ts index b3e28f48e8..96cdfd9346 100755 --- a/e2e/suites/actions/context-menu-single-selection.test.ts +++ b/e2e/suites/actions/context-menu-single-selection.test.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -import { browser, protractor } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -41,13 +40,15 @@ describe('Context menu actions - single selection : ', () => { const fileSiteUser = `fileUser-${Utils.random()}.txt`; const folderSiteUser = `folderUser-${Utils.random()}`; + const adminPublic = `admin-public-${Utils.random()}`; + const adminModerated = `admin-moderated-${Utils.random()}`; + const apis = { admin: new RepoClient(), user: new RepoClient(username, username) }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable } = page; const contextMenu = dataTable.menu; @@ -75,6 +76,12 @@ describe('Context menu actions - single selection : ', () => { await apis.user.favorites.addFavoriteById('folder', folderUserId); await apis.user.favorites.waitForApi({ expect: 3 }); + await apis.admin.sites.createSite(adminPublic); + await apis.admin.sites.createSite(adminModerated, SITE_VISIBILITY.MODERATED); + await apis.user.favorites.addFavoriteById('site', adminPublic); + await apis.user.favorites.addFavoriteById('site', adminModerated); + await apis.user.sites.requestToJoin(adminModerated); + await loginPage.loginWith(username); done(); }); @@ -83,8 +90,9 @@ describe('Context menu actions - single selection : ', () => { await apis.user.nodes.deleteNodeById(fileUserId); await apis.user.nodes.deleteNodeById(folderUserId); await apis.user.sites.deleteSite(siteName); + await apis.admin.sites.deleteSite(adminPublic); + await apis.admin.sites.deleteSite(adminModerated); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -93,8 +101,7 @@ describe('Context menu actions - single selection : ', () => { describe('Generic tests', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -126,23 +133,15 @@ describe('Context menu actions - single selection : ', () => { it('Context menu closes when clicking away from it - [C280619]', async () => { await dataTable.rightClickOnItem(fileUser); expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); - await page.sidenav.activeLink.click(); + await page.sidenav.getActiveLink().click(); expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed'); }); - - it('Context menu does not appear for a library - [C286276]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.rightClickOnItem(siteName); - expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed for a site'); - }); }); describe('on Personal Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -177,11 +176,10 @@ describe('Context menu actions - single selection : ', () => { }); }); - describe('File Libraries', () => { + describe('on File Libraries', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); done(); @@ -217,11 +215,54 @@ describe('Context menu actions - single selection : ', () => { }); }); - describe('Shared Files', () => { + describe('on a library', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await dataTable.clearSelection(); + done(); + }); + + it('Available actions when a library is selected - My Libraries - [C290080]', async () => { + await page.goToMyLibraries(); + await dataTable.rightClickOnItem(siteName); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + expect(await contextMenu.isMenuItemPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user is a member - [C290081]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.rightClickOnItem(siteName); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + expect(await contextMenu.isMenuItemPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user is not a member - [C290082]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.rightClickOnItem(adminPublic); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + expect(await contextMenu.isMenuItemPresent('Join')).toBe(true, `Join is not displayed for ${adminPublic}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminPublic}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminPublic}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user requested to join - [C290089]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.rightClickOnItem(adminModerated); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + expect(await contextMenu.isMenuItemPresent('Cancel join')).toBe(true, `Cancel join is not displayed for ${adminModerated}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminModerated}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminModerated}`); + }); + }); + + describe('on Shared Files', () => { + beforeEach(async (done) => { + await Utils.pressEscape(); + await page.clickSharedFilesAndWait(); done(); }); @@ -233,8 +274,7 @@ describe('Context menu actions - single selection : ', () => { expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); - // TODO: enable this when the action is properly implemented: ACA-92 - // expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Shared link settings')).toBe(true, `Shared link settings is not displayed for ${fileUser}`); expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${fileUser}`); expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`); expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); @@ -242,11 +282,10 @@ describe('Context menu actions - single selection : ', () => { }); }); - describe('Recent Files', () => { + describe('on Recent Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -266,11 +305,10 @@ describe('Context menu actions - single selection : ', () => { }); }); - describe('Favorites', () => { + describe('on Favorites', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -306,11 +344,10 @@ describe('Context menu actions - single selection : ', () => { }); }); - describe('Trash', () => { + describe('on Trash', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); diff --git a/e2e/suites/actions/create-folder.test.ts b/e2e/suites/actions/create-folder.test.ts index 5da62fe48a..76601f9eab 100755 --- a/e2e/suites/actions/create-folder.test.ts +++ b/e2e/suites/actions/create-folder.test.ts @@ -23,12 +23,9 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; - -import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog'; -import { Menu } from '../../components/menu/menu'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -42,7 +39,12 @@ describe('Create folder', () => { const duplicateFolderName = `folder-${Utils.random()}`; const nameWithSpaces = ` folder-${Utils.random()} `; - const siteName = `site-private-${Utils.random()}`; + const sitePrivate = `site-private-${Utils.random()}`; + const siteName = `site-${Utils.random()}`; + + const folderSite = `folder-site-${Utils.random()}`; + const duplicateFolderSite = `folder-${Utils.random()}`; + let docLibUserSite; const apis = { admin: new RepoClient(), @@ -50,35 +52,39 @@ describe('Create folder', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const personalFilesPage = new BrowsingPage(); + const page = new BrowsingPage(); const createDialog = new CreateOrEditFolderDialog(); - const { dataTable } = personalFilesPage; - const menu = new Menu(); + const { dataTable } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); - const docLibId = (await apis.admin.sites.getDocLibId(siteName)); + + await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); + const docLibId = await apis.admin.sites.getDocLibId(sitePrivate); await apis.admin.nodes.createFolder(folderName1, docLibId); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE); + parentId = (await apis.user.nodes.createFolder(parent)).entry.id; await apis.user.nodes.createFolder(duplicateFolderName, parentId); + + await apis.user.sites.createSite(siteName); + docLibUserSite = await apis.user.sites.getDocLibId(siteName); + await apis.user.nodes.createFolder(duplicateFolderSite, docLibUserSite); + await loginPage.loginWith(username); done(); }); afterAll(async (done) => { - await apis.admin.sites.deleteSite(siteName); + await apis.admin.sites.deleteSite(sitePrivate); + await apis.user.sites.deleteSite(siteName); await apis.user.nodes.deleteNodeById(parentId); - await logoutPage.load(); done(); }); describe('on Personal Files', () => { beforeEach(async (done) => { - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -87,16 +93,9 @@ describe('Create folder', () => { done(); }); - it('option is enabled when having enough permissions - [C216339]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openNewMenu(); - const isEnabled = await menu.getItemByLabel('Create folder').isEnabled(); - expect(isEnabled).toBe(true, 'Create folder is not enabled'); - }); - it('creates new folder with name - [C216341]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(folderName1); await createDialog.clickCreate(); @@ -107,8 +106,8 @@ describe('Create folder', () => { }); it('creates new folder with name and description - [C216340]', async (done) => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(folderName2); await createDialog.enterDescription(folderDescription); @@ -116,21 +115,14 @@ describe('Create folder', () => { await createDialog.waitForDialogToClose(); await dataTable.waitForHeader(); expect(await dataTable.getRowByName(folderName2).isPresent()).toBe(true, 'Folder not displayed'); - const desc = await apis.user.nodes.getNodeDescription(folderName2, parent); + const desc = await apis.user.nodes.getNodeDescription(folderName2, parentId); expect(desc).toEqual(folderDescription); done(); }); - it('enabled option tooltip - [C216342]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openNewMenu(); - await browser.actions().mouseMove(menu.getItemByLabel('Create folder')).perform(); - expect(await menu.getItemTooltip('Create folder')).toContain('Create new folder'); - }); - it('dialog UI elements - [C216345]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); const dialogTitle = await createDialog.getTitle(); const isFolderNameDisplayed = await createDialog.nameInput.isDisplayed(); @@ -146,8 +138,8 @@ describe('Create folder', () => { }); it('with empty folder name - [C216346]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.deleteNameWithBackspace(); const isCreateEnabled = await createDialog.createButton.isEnabled(); @@ -158,8 +150,8 @@ describe('Create folder', () => { }); it('with folder name ending with a dot "." - [C216348]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName('folder-name.'); const isCreateEnabled = await createDialog.createButton.isEnabled(); @@ -172,8 +164,8 @@ describe('Create folder', () => { it('with folder name containing special characters - [C216347]', async () => { const namesWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); for (const name of namesWithSpecialChars) { @@ -184,8 +176,8 @@ describe('Create folder', () => { }); it('with folder name containing only spaces - [C280406]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(' '); const isCreateEnabled = await createDialog.createButton.isEnabled(); @@ -196,29 +188,29 @@ describe('Create folder', () => { }); it('cancel folder creation - [C216349]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName('test'); await createDialog.enterDescription('test description'); await createDialog.clickCancel(); - expect(await createDialog.component.isPresent()).not.toBe(true, 'dialog is not closed'); + expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); }); it('duplicate folder name - [C216350]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(duplicateFolderName); await createDialog.clickCreate(); - const message = await personalFilesPage.getSnackBarMessage(); + const message = await page.getSnackBarMessage(); expect(message).toEqual(`There's already a folder with this name. Try a different name.`); - expect(await createDialog.component.isPresent()).toBe(true, 'dialog is not present'); + expect(await createDialog.isDialogOpen()).toBe(true, 'dialog is not present'); }); it('trim ending spaces from folder name - [C216351]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(parent); - await personalFilesPage.sidenav.openCreateDialog(); + await page.dataTable.doubleClickOnRowByName(parent); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(nameWithSpaces); await createDialog.clickCreate(); @@ -233,8 +225,7 @@ describe('Create folder', () => { const fileLibrariesPage = new BrowsingPage(); beforeEach(async (done) => { - await fileLibrariesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await fileLibrariesPage.clickFileLibrariesAndWait(); done(); }); @@ -243,21 +234,39 @@ describe('Create folder', () => { done(); }); - it('option is disabled when not enough permissions - [C280397]', async () => { - await fileLibrariesPage.dataTable.doubleClickOnRowByName(siteName); - await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderName1); - await fileLibrariesPage.sidenav.openNewMenu(); - const isEnabled = await menu.getItemByLabel('Create folder').isEnabled(); - expect(isEnabled).toBe(false, 'Create folder is not disabled'); + it('creates new folder with name and description - [C280394]', async () => { + await page.dataTable.doubleClickOnRowByName(siteName); + await page.sidenav.openCreateFolderDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(folderSite); + await createDialog.enterDescription(folderDescription); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + await dataTable.waitForHeader(); + expect(await dataTable.getRowByName(folderSite).isPresent()).toBe(true, 'Folder not displayed'); + const desc = await apis.user.nodes.getNodeDescription(folderSite, docLibUserSite); + expect(desc).toEqual(folderDescription); }); - it('disabled option tooltip - [C280398]', async () => { - await fileLibrariesPage.dataTable.doubleClickOnRowByName(siteName); - await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderName1); - await fileLibrariesPage.sidenav.openNewMenu(); - await browser.actions().mouseMove(menu.getItemByLabel('Create folder')).perform(); - const tooltip = await menu.getItemTooltip('Create folder'); - expect(tooltip).toContain(`Folders cannot be created whilst viewing the current items`); + it('cancel folder creation - [C280403]', async () => { + await page.dataTable.doubleClickOnRowByName(siteName); + await page.sidenav.openCreateFolderDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName('test'); + await createDialog.enterDescription('test description'); + await createDialog.clickCancel(); + expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); + }); + + it('duplicate folder name - [C280404]', async () => { + await page.dataTable.doubleClickOnRowByName(siteName); + await page.sidenav.openCreateFolderDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(duplicateFolderSite); + await createDialog.clickCreate(); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`There's already a folder with this name. Try a different name.`); + expect(await createDialog.isDialogOpen()).toBe(true, 'dialog is not present'); }); }); diff --git a/e2e/suites/actions/create-library.test.ts b/e2e/suites/actions/create-library.test.ts new file mode 100755 index 0000000000..1d1fabc1ce --- /dev/null +++ b/e2e/suites/actions/create-library.test.ts @@ -0,0 +1,215 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { CreateLibraryDialog } from '../../components/dialog/create-library-dialog'; +import { Utils } from '../../utilities/utils'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; + +describe('Create library', () => { + const username = `user-${Utils.random()}`; + + const site1Name = `site1-${Utils.random()}`; + const site2Name = `site2-${Utils.random()}`; + const site3Name = `site3-${Utils.random()}`; + + const site4 = { + name: `site4-${Utils.random()}`, + id: `site4-id-${Utils.random()}`, + description: 'site4 description' + } + + const duplicateSite = { + name: `duplicate-${Utils.random()}`, + id: `duplicate-${Utils.random()}` + } + + const siteInTrash = { + name: `site-trash-${Utils.random()}`, + id: `site-trash-id-${Utils.random()}` + } + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const createDialog = new CreateLibraryDialog(); + const { dataTable } = page; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + await apis.user.sites.createSite(duplicateSite.name, SITE_VISIBILITY.PRIVATE, '', duplicateSite.id); + await apis.user.sites.createSite(siteInTrash.name, SITE_VISIBILITY.PUBLIC, '', siteInTrash.id); + await apis.user.sites.deleteSite(siteInTrash.id, false); + + await loginPage.loginWith(username); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.sites.deleteAllUserSites(); + done(); + }); + + it('Create Library dialog UI - [C280024]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + expect(await createDialog.getTitle()).toMatch('Create Library'); + expect(await createDialog.isNameDisplayed()).toBe(true, 'Name input is not displayed'); + expect(await createDialog.isLibraryIdDisplayed()).toBe(true, 'Library ID input is not displayed'); + expect(await createDialog.isDescriptionDisplayed()).toBe(true, 'Description field is not displayed'); + expect(await createDialog.isPublicDisplayed()).toBe(true, 'Public option is not displayed'); + expect(await createDialog.isModeratedDisplayed()).toBe(true, 'Moderated option is not displayed'); + expect(await createDialog.isPrivateDisplayed()).toBe(true, 'Private option is not displayed'); + expect(await createDialog.isPublicChecked()).toBe(true, 'Public option not checked'); + expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button is not disabled'); + expect(await createDialog.isCancelEnabled()).toBe(true, 'Cancel button is not enabled'); + }); + + it('Create a public library - [C280025]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(site1Name); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + const current = await page.breadcrumb.getCurrentItemName(); + expect(current).toEqual(site1Name, `Not navigated into ${site1Name}`); + await page.clickFileLibrariesAndWait(); + expect(await dataTable.getRowByName(site1Name).isPresent()).toBe(true, `${site1Name} not in the list`); + expect(await apis.user.sites.getVisibility(site1Name)).toEqual(SITE_VISIBILITY.PUBLIC); + }); + + it('Create a moderated library - [C289880]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(site2Name); + await createDialog.selectModerated(); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + const current = await page.breadcrumb.getCurrentItemName(); + expect(current).toEqual(site2Name, `Not navigated into ${site2Name}`); + await page.clickFileLibrariesAndWait(); + expect(await dataTable.getRowByName(site2Name).isPresent()).toBe(true, `${site2Name} not in the list`); + expect(await apis.user.sites.getVisibility(site2Name)).toEqual(SITE_VISIBILITY.MODERATED); + }); + + it('Create a private library - [C289881]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(site3Name); + await createDialog.selectPrivate(); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + const current = await page.breadcrumb.getCurrentItemName(); + expect(current).toEqual(site3Name, `Not navigated into ${site3Name}`); + await page.clickFileLibrariesAndWait(); + expect(await dataTable.getRowByName(site3Name).isPresent()).toBe(true, `${site3Name} not in the list`); + expect(await apis.user.sites.getVisibility(site3Name)).toEqual(SITE_VISIBILITY.PRIVATE); + }); + + it('Create a library with a given ID and description - [C289882]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(site4.name); + await createDialog.enterLibraryId(site4.id); + await createDialog.enterDescription(site4.description); + await createDialog.selectPublic(); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + const current = await page.breadcrumb.getCurrentItemName(); + expect(current).toEqual(site4.name, `Not navigated into ${site4.name}`); + await page.clickFileLibrariesAndWait(); + expect(await dataTable.getRowByName(site4.name).isPresent()).toBe(true, `${site4.name} not in the list`); + expect(await apis.user.sites.getVisibility(site4.id)).toEqual(SITE_VISIBILITY.PUBLIC); + expect(await apis.user.sites.getDescription(site4.id)).toEqual(site4.description); + }); + + it('Duplicate library ID - [C280027]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(duplicateSite.name); + await createDialog.enterLibraryId(duplicateSite.id); + expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button not disabled'); + const err = await createDialog.getErrorMessage(); + expect(err).toEqual(`This Library ID isn't available. Try a different Library ID.`); + }); + + it('Create library using the ID of a library from the Trashcan - [C280028]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(siteInTrash.name); + await createDialog.enterLibraryId(siteInTrash.id); + await createDialog.clickCreate(); + const err = await createDialog.getErrorMessage(); + expect(err).toEqual(`This Library ID is already used. Check the trashcan.`); + }); + + it('Cancel button - [C280029]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName('test site'); + await createDialog.enterDescription('test description'); + await createDialog.clickCancel(); + expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); + }); + + it('Library ID cannot contain special characters - [C280026]', async () => { + const idWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; + + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName('test site'); + + for (const id of idWithSpecialChars) { + await createDialog.enterLibraryId(id); + expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button is not disabled'); + expect(await createDialog.getErrorMessage()).toContain(`Use numbers and letters only`); + } + }); + + it('Create 2 libraries with same name but different IDs - [C280030]', async () => { + await page.sidenav.openCreateLibraryDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(duplicateSite.name); + await createDialog.enterLibraryId(`${duplicateSite.id}-2`); + await createDialog.clickCreate(); + await createDialog.waitForDialogToClose(); + const current = await page.breadcrumb.getCurrentItemName(); + expect(current).toEqual(duplicateSite.name, `Not navigated into ${duplicateSite.name}`); + await page.clickFileLibrariesAndWait(); + expect(await dataTable.getRowByName(`${duplicateSite.name} (${duplicateSite.id}-2)`).isPresent()).toBe(true, `${duplicateSite.name} not in the list`); + expect(await apis.user.sites.getTitle(`${duplicateSite.id}-2`)).toEqual(duplicateSite.name); + }); + +}); diff --git a/e2e/suites/actions/delete-undo-delete.test.ts b/e2e/suites/actions/delete-undo-delete.test.ts index 451fc85f6e..d82173311f 100755 --- a/e2e/suites/actions/delete-undo-delete.test.ts +++ b/e2e/suites/actions/delete-undo-delete.test.ts @@ -24,8 +24,7 @@ */ import { browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -38,7 +37,6 @@ describe('Delete and undo delete', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; @@ -48,7 +46,7 @@ describe('Delete and undo delete', () => { }); afterAll(async (done) => { - await apis.admin.trashcan.emptyTrash(); + await apis.user.trashcan.emptyTrash(); done(); }); @@ -61,17 +59,24 @@ describe('Delete and undo delete', () => { const file4 = `file4-${Utils.random()}.txt`; let file4Id; const folder1 = `folder1-${Utils.random()}`; let folder1Id; const folder2 = `folder2-${Utils.random()}`; let folder2Id; - const fileLocked1 = `fileLocked-${Utils.random()}.txt`; let fileLocked1Id; + const folder3 = `folder3-${Utils.random()}`; let folder3Id; + const fileLocked1 = `fileLocked1-${Utils.random()}.txt`; let fileLocked1Id; + const fileLocked2 = `fileLocked2-${Utils.random()}.txt`; let fileLocked2Id; beforeAll(async (done) => { file1Id = (await apis.user.nodes.createFile(file1)).entry.id; file2Id = (await apis.user.nodes.createFile(file2)).entry.id; folder1Id = (await apis.user.nodes.createFolder(folder1)).entry.id; + folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id; await apis.user.nodes.createFile(file3, folder1Id); file4Id = (await apis.user.nodes.createFile(file4, folder2Id)).entry.id; await apis.user.nodes.lockFile(file4Id); + folder3Id = (await apis.user.nodes.createFolder(folder3)).entry.id; + fileLocked2Id = (await apis.user.nodes.createFile(fileLocked2, folder3Id)).entry.id; + await apis.user.nodes.lockFile(fileLocked2Id); + fileLocked1Id = (await apis.user.nodes.createFile(fileLocked1)).entry.id; await apis.user.nodes.lockFile(fileLocked1Id); @@ -80,8 +85,7 @@ describe('Delete and undo delete', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -93,8 +97,8 @@ describe('Delete and undo delete', () => { afterAll(async (done) => { await apis.user.nodes.unlockFile(file4Id); await apis.user.nodes.unlockFile(fileLocked1Id); - await logoutPage.load(); - await apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, fileLocked1Id]); + await apis.user.nodes.unlockFile(fileLocked2Id); + await apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, folder3Id, fileLocked1Id]); await apis.user.search.waitForApi(username, {expect: 0}); done(); }); @@ -110,7 +114,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item is not in trash'); await apis.user.trashcan.restore(file1Id); @@ -128,7 +132,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} was not removed from list`); items = items - 2; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} is not in trash`); expect(await dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} is not in trash`); @@ -145,7 +149,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item is not in trash'); expect(await dataTable.getRowByName(file3).isPresent()).toBe(false, 'Item is in trash'); @@ -159,7 +163,7 @@ describe('Delete and undo delete', () => { const message = await page.getSnackBarMessage(); expect(message).toContain(`${folder2} couldn't be deleted`); expect(await dataTable.getRowByName(folder2).isPresent()).toBe(true, 'Item was removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(folder2).isPresent()).toBe(false, 'Item is in trash'); expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, 'Item is in trash'); }); @@ -175,7 +179,7 @@ describe('Delete and undo delete', () => { }); it('notification on multiple items deletion - all items fail to delete - [C217130]', async () => { - await dataTable.selectMultipleItems([fileLocked1, folder2]); + await dataTable.selectMultipleItems([folder3, folder2]); await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); @@ -258,8 +262,7 @@ describe('Delete and undo delete', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -269,7 +272,6 @@ describe('Delete and undo delete', () => { }); afterAll(async (done) => { - await logoutPage.load(); await apis.user.nodes.deleteNodesById([sharedFile1Id, sharedFile2Id, sharedFile3Id, sharedFile4Id]); await apis.user.search.waitForApi(username, {expect: 0}); done(); @@ -282,7 +284,7 @@ describe('Delete and undo delete', () => { const message = await page.getSnackBarMessage(); expect(message).toContain(`${sharedFile1} deleted`); expect(await dataTable.getRowByName(sharedFile1).isPresent()).toBe(false, 'Item was not removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(sharedFile1).isPresent()).toBe(true, 'Item is not in trash'); await apis.user.trashcan.restore(sharedFile1Id); @@ -298,7 +300,7 @@ describe('Delete and undo delete', () => { expect(message).toContain(`Deleted 2 items`); expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, `${sharedFile2} was not removed from list`); expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not removed from list`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(true, `${sharedFile2} is not in trash`); expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(true, `${sharedFile3} is not in trash`); @@ -323,7 +325,7 @@ describe('Delete and undo delete', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, 'Item was not restored'); }); @@ -332,7 +334,7 @@ describe('Delete and undo delete', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not restored`); expect(await dataTable.getRowByName(sharedFile4).isPresent()).toBe(false, `${sharedFile4} was not restored`); }); @@ -345,31 +347,35 @@ describe('Delete and undo delete', () => { const favoriteFile4 = `favFile4-${Utils.random()}.txt`; let favoriteFile4Id; const favoriteFolder1 = `favFolder1-${Utils.random()}`; let favoriteFolder1Id; const favoriteFolder2 = `favFolder2-${Utils.random()}`; let favoriteFolder2Id; - const favoriteFileLocked1 = `favFileLocked-${Utils.random()}.txt`; let favoriteFileLocked1Id; + const favoriteFolder3 = `favFolder3-${Utils.random()}`; let favoriteFolder3Id; + const favoriteFileLocked1 = `favFileLocked1-${Utils.random()}.txt`; let favoriteFileLocked1Id; + const favoriteFileLocked2 = `favFileLocked2-${Utils.random()}.txt`; let favoriteFileLocked2Id; beforeAll(async (done) => { favoriteFile1Id = (await apis.user.nodes.createFile(favoriteFile1)).entry.id; favoriteFile2Id = (await apis.user.nodes.createFile(favoriteFile2)).entry.id; favoriteFolder1Id = (await apis.user.nodes.createFolder(favoriteFolder1)).entry.id; favoriteFolder2Id = (await apis.user.nodes.createFolder(favoriteFolder2)).entry.id; + favoriteFolder3Id = (await apis.user.nodes.createFolder(favoriteFolder3)).entry.id; await apis.user.nodes.createFile(favoriteFile3, favoriteFolder1Id); favoriteFile4Id = (await apis.user.nodes.createFile(favoriteFile4, favoriteFolder2Id)).entry.id; + favoriteFileLocked2Id = (await apis.user.nodes.createFile(favoriteFileLocked2, favoriteFolder3Id)).entry.id; await apis.user.nodes.lockFile(favoriteFile4Id); + await apis.user.nodes.lockFile(favoriteFileLocked2Id); favoriteFileLocked1Id = (await apis.user.nodes.createFile(favoriteFileLocked1)).entry.id; await apis.user.nodes.lockFile(favoriteFileLocked1Id); await apis.user.favorites.addFavoritesByIds('file', [ favoriteFile1Id, favoriteFile2Id, favoriteFileLocked1Id ]); - await apis.user.favorites.addFavoritesByIds('folder', [ favoriteFolder1Id, favoriteFolder2Id ]); - await apis.user.favorites.waitForApi({ expect: 5 }); + await apis.user.favorites.addFavoritesByIds('folder', [ favoriteFolder1Id, favoriteFolder2Id, favoriteFolder3Id ]); + await apis.user.favorites.waitForApi({ expect: 6 }); await loginPage.loginWith(username); done(); }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -381,9 +387,9 @@ describe('Delete and undo delete', () => { afterAll(async (done) => { await apis.user.nodes.unlockFile(favoriteFile4Id); await apis.user.nodes.unlockFile(favoriteFileLocked1Id); - await logoutPage.load(); + await apis.user.nodes.unlockFile(favoriteFileLocked2Id); await apis.user.nodes.deleteNodesById([ - favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id + favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id, favoriteFolder3Id ]); await apis.user.search.waitForApi(username, {expect: 0}); done(); @@ -400,7 +406,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item is not in trash'); await apis.user.trashcan.restore(favoriteFile1Id); @@ -418,7 +424,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(favoriteFile2).isPresent()).toBe(false, `${favoriteFile2} was not removed from list`); items = items - 2; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} is not in trash`); expect(await dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} is not in trash`); @@ -434,7 +440,7 @@ describe('Delete and undo delete', () => { expect(await dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item is not in trash'); expect(await dataTable.getRowByName(favoriteFile3).isPresent()).toBe(false, 'Item is in trash'); @@ -448,7 +454,7 @@ describe('Delete and undo delete', () => { const message = await page.getSnackBarMessage(); expect(message).toContain(`${favoriteFolder2} couldn't be deleted`); expect(await dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(true, 'Item was removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(false, 'Item is in trash'); expect(await dataTable.getRowByName(favoriteFile4).isPresent()).toBe(false, 'Item is in trash'); }); @@ -464,7 +470,7 @@ describe('Delete and undo delete', () => { }); it('notification on multiple items deletion - all items fail to delete - [C280521]', async () => { - await dataTable.selectMultipleItems([favoriteFileLocked1, favoriteFolder2]); + await dataTable.selectMultipleItems([favoriteFolder3, favoriteFolder2]); await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); @@ -542,7 +548,7 @@ describe('Delete and undo delete', () => { await loginPage.loginWith(username); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await page.clickRecentFiles(); const empty = await dataTable.isEmptyList(); if (empty) { await browser.sleep(6000); @@ -553,8 +559,7 @@ describe('Delete and undo delete', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -564,7 +569,6 @@ describe('Delete and undo delete', () => { }); afterAll(async (done) => { - await logoutPage.load(); await apis.user.nodes.deleteNodesById([recentFile1Id, recentFile2Id, recentFile3Id, recentFile4Id]); done(); }); @@ -576,7 +580,7 @@ describe('Delete and undo delete', () => { const message = await page.getSnackBarMessage(); expect(message).toContain(`${recentFile1} deleted`); expect(await dataTable.getRowByName(recentFile1).isPresent()).toBe(false, 'Item was not removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(recentFile1).isPresent()).toBe(true, 'Item is not in trash'); await apis.user.trashcan.restore(recentFile1Id); @@ -591,7 +595,7 @@ describe('Delete and undo delete', () => { expect(message).toContain(`Deleted 2 items`); expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(false, `${recentFile2} was not removed from list`); expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} was not removed from list`); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(true, `${recentFile2} is not in trash`); expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(true, `${recentFile3} is not in trash`); @@ -620,7 +624,7 @@ describe('Delete and undo delete', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(false, 'Item is in Trash'); }); @@ -633,7 +637,7 @@ describe('Delete and undo delete', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} is in Trash`); expect(await dataTable.getRowByName(recentFile4).isPresent()).toBe(false, `${recentFile4} is in Trash`); }); diff --git a/e2e/suites/actions/edit-folder.test.ts b/e2e/suites/actions/edit-folder.test.ts index aabceefa92..5342bac45b 100755 --- a/e2e/suites/actions/edit-folder.test.ts +++ b/e2e/suites/actions/edit-folder.test.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -import { protractor, browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog'; import { Utils } from '../../utilities/utils'; @@ -43,7 +42,17 @@ describe('Edit folder', () => { const folderNameEdited = `folder-${Utils.random()}`; const folderDescriptionEdited = 'description edited'; - const siteName = `site-private-${Utils.random()}`; + const sitePrivate = `site-private-${Utils.random()}`; + const siteName = `site-${Utils.random()}`; + + const folderSite = `folder-site-${Utils.random()}`; + const folderSiteToEdit = `folder-site-${Utils.random()}`; let folderSiteToEditId; + const duplicateFolderSite = `folder-${Utils.random()}`; + let docLibUserSite; + + const folderFavorite = `folder-fav-${Utils.random()}`; let folderFavoriteId; + const folderFavoriteToEdit = `folder-fav-${Utils.random()}`; let folderFavoriteToEditId; + const folderFavoriteDuplicate = `folder-fav-${Utils.random()}`; let folderFavoriteDuplicateId; const apis = { admin: new RepoClient(), @@ -51,51 +60,57 @@ describe('Edit folder', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const personalFilesPage = new BrowsingPage(); + const page = new BrowsingPage(); const editDialog = new CreateOrEditFolderDialog(); - const { dataTable } = personalFilesPage; - const editButton = personalFilesPage.toolbar.getButtonByTitleAttribute('Edit'); + const { dataTable } = page; + const editButton = page.toolbar.getButtonByTitleAttribute('Edit'); beforeAll(async (done) => { await apis.admin.people.createUser({ username }); - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); - const docLibId = await apis.admin.sites.getDocLibId(siteName); + await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); + const docLibId = await apis.admin.sites.getDocLibId(sitePrivate); await apis.admin.nodes.createFolder(folderName, docLibId); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE); parentId = (await apis.user.nodes.createFolder(parent)).entry.id; await apis.user.nodes.createFolder(folderName, parentId, '', folderDescription); await apis.user.nodes.createFolder(folderNameToEdit, parentId); await apis.user.nodes.createFolder(duplicateFolderName, parentId); - await loginPage.loginWith(username); - done(); - }); + await apis.user.sites.createSite(siteName); + docLibUserSite = await apis.user.sites.getDocLibId(siteName); + await apis.user.nodes.createFolder(folderSite, docLibUserSite); + folderSiteToEditId = (await apis.user.nodes.createFolder(folderSiteToEdit, docLibUserSite)).entry.id; + await apis.user.nodes.createFolder(duplicateFolderSite, docLibUserSite); - beforeEach(async (done) => { - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(parent); - await dataTable.waitForHeader(); - done(); - }); + folderFavoriteId = (await apis.user.nodes.createFolder(folderFavorite)).entry.id; + folderFavoriteToEditId = (await apis.user.nodes.createFolder(folderFavoriteToEdit)).entry.id; + folderFavoriteDuplicateId = (await apis.user.nodes.createFolder(folderFavoriteDuplicate)).entry.id; - afterEach(async (done) => { - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await apis.user.favorites.addFavoriteById('folder', folderFavoriteId); + await apis.user.favorites.addFavoriteById('folder', folderFavoriteToEditId); + await apis.user.favorites.addFavoriteById('folder', folderFavoriteDuplicateId); + + await loginPage.loginWith(username); done(); }); afterAll(async (done) => { await Promise.all([ - apis.admin.sites.deleteSite(siteName), - apis.user.nodes.deleteNodeById(parentId), - logoutPage.load() + apis.admin.sites.deleteSite(sitePrivate), + apis.user.sites.deleteSite(siteName), + apis.user.nodes.deleteNodesById([ parentId, folderFavoriteToEditId, folderFavoriteDuplicateId ]) ]); done(); }); + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + it('dialog UI defaults - [C216331]', async () => { + await dataTable.doubleClickOnRowByName(parent); await dataTable.selectItem(folderName); await editButton.click(); expect(await editDialog.getTitle()).toEqual('Edit folder'); @@ -105,77 +120,153 @@ describe('Edit folder', () => { expect(await editDialog.cancelButton.isEnabled()).toBe(true, 'cancel button is not enabled'); }); - it('properties are modified when pressing OK - [C216335]', async (done) => { - await dataTable.selectItem(folderNameToEdit); - await editButton.click(); - await editDialog.waitForDialogToOpen(); - await editDialog.enterDescription(folderDescriptionEdited); - await editDialog.enterName(folderNameEdited); - await editDialog.clickUpdate(); - await editDialog.waitForDialogToClose(); - await dataTable.waitForHeader(); - expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed'); - const desc = await apis.user.nodes.getNodeDescription(folderNameEdited, parent); - expect(desc).toEqual(folderDescriptionEdited); - done(); - }); + describe('on Personal Files', () => { + beforeEach(async (done) => { + await page.clickPersonalFilesAndWait(); + await dataTable.doubleClickOnRowByName(parent); + await dataTable.waitForHeader(); + done(); + }); - it('with empty folder name - [C216332]', async () => { - await dataTable.selectItem(folderName); - await editButton.click(); - await editDialog.deleteNameWithBackspace(); - expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled'); - expect(await editDialog.getValidationMessage()).toMatch('Folder name is required'); - }); + it('properties are modified when pressing OK - [C216335]', async (done) => { + await dataTable.selectItem(folderNameToEdit); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterDescription(folderDescriptionEdited); + await editDialog.enterName(folderNameEdited); + await editDialog.clickUpdate(); + await editDialog.waitForDialogToClose(); + await dataTable.waitForHeader(); + expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed'); + const desc = await apis.user.nodes.getNodeDescription(folderNameEdited, parentId); + expect(desc).toEqual(folderDescriptionEdited); + done(); + }); - it('with name with special characters - [C216333]', async () => { - const namesWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; + it('with empty folder name - [C216332]', async () => { + await dataTable.selectItem(folderName); + await editButton.click(); + await editDialog.deleteNameWithBackspace(); + expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled'); + expect(await editDialog.getValidationMessage()).toMatch('Folder name is required'); + }); - await dataTable.selectItem(folderName); - await editButton.click(); + it('with name with special characters - [C216333]', async () => { + const namesWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; - for (const name of namesWithSpecialChars) { - await editDialog.enterName(name); - expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not disabled'); - expect(await editDialog.getValidationMessage()).toContain(`Folder name can't contain these characters`); - } - }); + await dataTable.selectItem(folderName); + await editButton.click(); - it('with name ending with a dot - [C216334]', async () => { - await dataTable.selectItem(folderName); - await editButton.click(); - await editDialog.waitForDialogToOpen(); - await editDialog.nameInput.sendKeys('.'); - expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled'); - expect(await editDialog.getValidationMessage()).toMatch(`Folder name can't end with a period .`); - }); + for (const name of namesWithSpecialChars) { + await editDialog.enterName(name); + expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not disabled'); + expect(await editDialog.getValidationMessage()).toContain(`Folder name can't contain these characters`); + } + }); - it('Cancel button - [C216336]', async () => { - await dataTable.selectItem(folderName); - await editButton.click(); - await editDialog.waitForDialogToOpen(); - await editDialog.clickCancel(); - expect(await editDialog.component.isPresent()).not.toBe(true, 'dialog is not closed'); + it('with name ending with a dot - [C216334]', async () => { + await dataTable.selectItem(folderName); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.nameInput.sendKeys('.'); + expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled'); + expect(await editDialog.getValidationMessage()).toMatch(`Folder name can't end with a period .`); + }); + + it('Cancel button - [C216336]', async () => { + await dataTable.selectItem(folderName); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.clickCancel(); + expect(await editDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); + }); + + it('with duplicate folder name - [C216337]', async () => { + await dataTable.selectItem(folderName); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterName(duplicateFolderName); + await editDialog.clickUpdate(); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`There's already a folder with this name. Try a different name.`); + expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present'); + }); + + it('trim ending spaces - [C216338]', async () => { + await dataTable.selectItem(folderName); + await editButton.click(); + await editDialog.nameInput.sendKeys(' '); + await editDialog.clickUpdate(); + await editDialog.waitForDialogToClose(); + expect(await page.snackBar.isPresent()).not.toBe(true, 'notification appears'); + expect(await dataTable.getRowByName(folderName).isPresent()).toBe(true, 'Folder not displayed in list view'); + }); }); - it('with duplicate folder name - [C216337]', async () => { - await dataTable.selectItem(folderName); - await editButton.click(); - await editDialog.waitForDialogToOpen(); - await editDialog.enterName(duplicateFolderName); - await editDialog.clickUpdate(); - const message = await personalFilesPage.getSnackBarMessage(); - expect(message).toEqual(`There's already a folder with this name. Try a different name.`); - expect(await editDialog.component.isPresent()).toBe(true, 'dialog is not present'); + describe('on Favorites', () => { + beforeEach(async (done) => { + await page.clickFavoritesAndWait(); + done(); + }); + + it('properties are modified when pressing OK - [C280384]', async (done) => { + await dataTable.selectItem(folderFavoriteToEdit); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterDescription(folderDescriptionEdited); + await editDialog.enterName(folderNameEdited); + await editDialog.clickUpdate(); + await editDialog.waitForDialogToClose(); + await dataTable.waitForHeader(); + expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed'); + const desc = await apis.user.nodes.getNodeProperty(folderFavoriteToEditId, 'cm:description'); + expect(desc).toEqual(folderDescriptionEdited); + done(); + }); + + it('with duplicate folder name - [C280386]', async () => { + await dataTable.selectItem(folderFavorite); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterName(folderFavoriteDuplicate); + await editDialog.clickUpdate(); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`There's already a folder with this name. Try a different name.`); + expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present'); + }); }); - it('trim ending spaces - [C216338]', async () => { - await dataTable.selectItem(folderName); - await editButton.click(); - await editDialog.nameInput.sendKeys(' '); - await editDialog.clickUpdate(); - await editDialog.waitForDialogToClose(); - expect(await personalFilesPage.snackBar.isPresent()).not.toBe(true, 'notification appears'); - expect(await dataTable.getRowByName(folderName).isPresent()).toBe(true, 'Folder not displayed in list view'); + describe('on My Libraries', () => { + beforeEach(async (done) => { + await page.clickFileLibrariesAndWait(); + await dataTable.doubleClickOnRowByName(siteName); + done(); + }); + + it('properties are modified when pressing OK - [C280509]', async (done) => { + await dataTable.selectItem(folderSiteToEdit); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterDescription(folderDescriptionEdited); + await editDialog.enterName(folderNameEdited); + await editDialog.clickUpdate(); + await editDialog.waitForDialogToClose(); + await dataTable.waitForHeader(); + expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed'); + const desc = await apis.user.nodes.getNodeProperty(folderSiteToEditId, 'cm:description'); + expect(desc).toEqual(folderDescriptionEdited); + done(); + }); + + it('with duplicate folder name - [C280511]', async () => { + await dataTable.selectItem(folderSite); + await editButton.click(); + await editDialog.waitForDialogToOpen(); + await editDialog.enterName(duplicateFolderSite); + await editDialog.clickUpdate(); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`There's already a folder with this name. Try a different name.`); + expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present'); + }); }); }); diff --git a/e2e/suites/actions/library-actions.test.ts b/e2e/suites/actions/library-actions.test.ts new file mode 100755 index 0000000000..7a32a55903 --- /dev/null +++ b/e2e/suites/actions/library-actions.test.ts @@ -0,0 +1,187 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { ConfirmDialog } from './../../components/components'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; + +describe('Library actions', () => { + const username = `user-${Utils.random()}`; + + const sitePublic1Admin = `admin-public1-${Utils.random()}`; + const sitePublic2Admin = `admin-public2-${Utils.random()}`; + const sitePublic3Admin = `admin-public3-${Utils.random()}`; + const sitePublic4Admin = `admin-public4-${Utils.random()}`; + const siteModerated1Admin = `admin-moderated1-${Utils.random()}`; + const siteModerated2Admin = `admin-moderated2-${Utils.random()}`; + + const sitePublicUser = `user-public-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const { dataTable, toolbar } = page; + + const confirmDialog = new ConfirmDialog(); + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + await apis.admin.sites.createSite(sitePublic1Admin); + await apis.admin.sites.createSite(sitePublic2Admin); + await apis.admin.sites.createSite(sitePublic3Admin); + await apis.admin.sites.createSite(sitePublic4Admin); + await apis.admin.sites.createSite(siteModerated1Admin, SITE_VISIBILITY.MODERATED); + await apis.admin.sites.createSite(siteModerated2Admin, SITE_VISIBILITY.MODERATED); + + await apis.user.sites.createSite(sitePublicUser); + + await apis.admin.sites.addSiteMember(sitePublic2Admin, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); + await apis.admin.sites.addSiteMember(sitePublic3Admin, username, SITE_ROLES.SITE_MANAGER.ROLE); + await apis.admin.sites.addSiteMember(sitePublic4Admin, username, SITE_ROLES.SITE_MANAGER.ROLE); + + await apis.user.favorites.addFavoriteById('site', sitePublic1Admin); + await apis.user.favorites.addFavoriteById('site', sitePublic3Admin); + await apis.user.favorites.addFavoriteById('site', siteModerated1Admin); + await apis.user.favorites.addFavoriteById('site', siteModerated2Admin); + + await apis.user.sites.requestToJoin(siteModerated2Admin); + + await loginPage.loginWith(username); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + await dataTable.clearSelection(); + done(); + }); + + afterAll(async (done) => { + await apis.admin.sites.deleteSite(sitePublic1Admin); + await apis.admin.sites.deleteSite(sitePublic2Admin); + await apis.admin.sites.deleteSite(sitePublic3Admin); + await apis.admin.sites.deleteSite(sitePublic4Admin); + await apis.admin.sites.deleteSite(siteModerated1Admin); + await apis.admin.sites.deleteSite(siteModerated2Admin); + await apis.user.sites.deleteSite(sitePublicUser); + done(); + }); + + it('Join a public library - Favorite Libraries - [C290105]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(sitePublic1Admin); + await toolbar.clickButton('Join'); + const role = await dataTable.getLibraryRole(sitePublic1Admin); + expect(role).toEqual('Consumer'); + }); + + it('Join a moderated library - Favorite Libraries - [C290109]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(siteModerated1Admin); + await toolbar.clickButton('Join'); + const role = await dataTable.getLibraryRole(siteModerated1Admin); + expect(role).toEqual(''); + const hasJoinRequest = await apis.user.sites.hasMembershipRequest(siteModerated1Admin); + expect(hasJoinRequest).toBe(true, `Join request does not exist on ${siteModerated1Admin}`); + }); + + it('Leave a library - My Libraries - [C290106]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(sitePublic2Admin); + await toolbar.clickButton('Leave library'); + await page.waitForDialog(); + await confirmDialog.clickButton('OK'); + const text = await page.getSnackBarMessage(); + + expect(text).toEqual(`You have left the library`); + expect(await dataTable.getRowByName(sitePublic2Admin).isPresent()).toBe(false, `${sitePublic2Admin} is displayed`); + }); + + it('Leave a library - Favorite Libraries - [C290110]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(sitePublic3Admin); + await toolbar.clickButton('Leave library'); + await page.waitForDialog(); + await confirmDialog.clickButton('OK'); + const text = await page.getSnackBarMessage(); + + expect(text).toEqual(`You have left the library`); + expect(await dataTable.getRowByName(sitePublic3Admin).isPresent()).toBe(true, `${sitePublic3Admin} is not displayed`); + }); + + it('Confirmation dialog UI - [C290136]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(sitePublic4Admin); + await toolbar.clickButton('Leave library'); + await page.waitForDialog(); + + expect(await confirmDialog.isDialogOpen()).toBe(true, 'Confirm delete dialog not open'); + expect(await confirmDialog.getTitle()).toContain('Leave this library?'); + expect(await confirmDialog.getText()).toContain('Leaving will remove your access.'); + expect(await confirmDialog.isButtonEnabled('OK')).toBe(true, 'OK button is not enabled'); + expect(await confirmDialog.isButtonEnabled('Cancel')).toBe(true, 'Cancel button is not enabled'); + }); + + it('Cancel Leave library - [C290111]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(sitePublic4Admin); + await toolbar.clickButton('Leave library'); + await page.waitForDialog(); + + expect(await confirmDialog.isButtonEnabled('Cancel')).toBe(true, 'Cancel button is not enabled'); + await confirmDialog.clickButton('Cancel'); + expect(await dataTable.getRowByName(sitePublic4Admin).isPresent()).toBe(true, `${sitePublic4Admin} was deleted`); + }); + + it('Leave a library - failure notification - [C290107]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(sitePublicUser); + await toolbar.clickButton('Leave library'); + await page.waitForDialog(); + await confirmDialog.clickButton('OK'); + const text = await page.getSnackBarMessage(); + + expect(text).toEqual(`Cannot leave this library`); + }); + + it('Cancel join - Favorite Libraries - [C290108]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(siteModerated2Admin); + await toolbar.clickButton('Cancel join request'); + + const text = await page.getSnackBarMessage(); + expect(text).toEqual(`Canceled the request to join the library`); + + const hasJoinRequest = await apis.user.sites.hasMembershipRequest(siteModerated2Admin); + expect(hasJoinRequest).toBe(false, `Join request exists on ${siteModerated2Admin}`); + }); + +}); diff --git a/e2e/suites/actions/mark-favorite.test.ts b/e2e/suites/actions/mark-favorite.test.ts index 20a04097d7..3f4afadcb2 100644 --- a/e2e/suites/actions/mark-favorite.test.ts +++ b/e2e/suites/actions/mark-favorite.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -52,7 +52,6 @@ describe('Mark items as favorites', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; @@ -90,8 +89,7 @@ describe('Mark items as favorites', () => { afterAll(async (done) => { await Promise.all([ apis.user.nodes.deleteNodesById([ file1Id, file2Id, file3Id, file4Id, folder1Id]), - apis.user.sites.deleteSite(siteName), - logoutPage.load() + apis.user.sites.deleteSite(siteName) ]); done(); }); @@ -101,8 +99,7 @@ describe('Mark items as favorites', () => { describe('on Personal Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -208,8 +205,7 @@ describe('Mark items as favorites', () => { describe('on Recent Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -290,8 +286,7 @@ describe('Mark items as favorites', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -368,8 +363,7 @@ describe('Mark items as favorites', () => { beforeEach(async (done) => { await Utils.pressEscape(); await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -413,8 +407,7 @@ describe('Mark items as favorites', () => { describe ('on File Libraries', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await page.dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await page.dataTable.doubleClickOnRowByName(siteName); await page.dataTable.waitForHeader(); done(); @@ -501,4 +494,60 @@ describe('Mark items as favorites', () => { await apis.user.favorites.waitForApi({ expect: 5 }); }); }); + + describe('on a library', () => { + const adminSite1 = `adminSite1-${Utils.random()}`; + const adminSite2 = `adminSite2-${Utils.random()}`; + const adminSite3 = `adminSite3-${Utils.random()}`; + + beforeAll(async (done) => { + await apis.admin.sites.createSite(adminSite1); + await apis.admin.sites.createSite(adminSite2); + await apis.admin.sites.createSite(adminSite3); + await apis.admin.sites.addSiteMember(adminSite1, username, SITE_ROLES.SITE_CONSUMER.ROLE); + await apis.admin.sites.addSiteMember(adminSite2, username, SITE_ROLES.SITE_CONSUMER.ROLE); + await apis.admin.sites.addSiteMember(adminSite3, username, SITE_ROLES.SITE_CONSUMER.ROLE); + + await apis.user.favorites.addFavoriteById('site', adminSite2); + await apis.user.favorites.addFavoriteById('site', adminSite3); + done(); + }); + + beforeEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.admin.sites.deleteSite(adminSite1); + await apis.admin.sites.deleteSite(adminSite2); + await apis.admin.sites.deleteSite(adminSite3); + done(); + }); + + it('Mark a library as favorite - [C289974]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(adminSite1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); + expect(await apis.user.favorites.isFavoriteWithRetry(adminSite1, { expect: true })).toBe(true, `${adminSite1} not favorite`); + }); + + it('Remove a library from favorites - on My Libraries - [C289975]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(adminSite2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); + expect(await apis.user.favorites.isFavoriteWithRetry(adminSite2, { expect: false })).toBe(false, `${adminSite2} still favorite`); + }); + + it('Remove a library from favorites - on Favorite Libraries - [C289976]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(adminSite3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); + expect(await dataTable.getRowByName(adminSite3).isPresent()).toBe(false, `${adminSite3} is displayed`); + expect(await apis.user.favorites.isFavoriteWithRetry(adminSite3, { expect: false })).toBe(false, `${adminSite3} still favorite`); + }); + }); }); diff --git a/e2e/suites/actions/new-menu.test.ts b/e2e/suites/actions/new-menu.test.ts new file mode 100755 index 0000000000..5ea766fe42 --- /dev/null +++ b/e2e/suites/actions/new-menu.test.ts @@ -0,0 +1,181 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_ROLES } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('New menu', () => { + const username = `user-${Utils.random()}`; + + const siteUser = `site-user-${Utils.random()}`; + const siteAdmin = `site-admin-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const { dataTable, sidenav } = page; + + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + await apis.admin.sites.createSite(siteAdmin); + await apis.admin.sites.addSiteMember(siteAdmin, username, SITE_ROLES.SITE_CONSUMER.ROLE); + + await apis.user.sites.createSite(siteUser); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await apis.user.sites.deleteSite(siteUser); + await apis.admin.sites.deleteSite(siteAdmin); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + it('has correct actions - [C286524]', async () => { + await page.clickPersonalFiles(); + await sidenav.openNewMenu(); + + expect(await sidenav.menu.isMenuItemPresent('Create folder')).toBe(true, 'Create Folder option not present'); + expect(await sidenav.menu.isMenuItemPresent('Create Library')).toBe(true, 'Create Library option not present'); + expect(await sidenav.menu.isMenuItemPresent('Upload file')).toBe(true, 'Upload File option not present'); + expect(await sidenav.menu.isMenuItemPresent('Upload folder')).toBe(true, 'Upload Folder option not present'); + + expect(await sidenav.menu.getItemByLabel('Create Library').isEnabled()).toBe(true, 'Create Library is not enabled'); + }); + + it('Create folder is enabled when having enough permissions - Personal Files - [C216339]', async () => { + await page.clickPersonalFiles(); + await page.sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled(); + expect(isEnabled).toBe(true, 'Create folder is not enabled'); + }); + + it('Create folder is enabled when having enough permissions - File Libraries - [C280393]', async () => { + await page.clickFileLibraries(); + await page.dataTable.doubleClickOnRowByName(siteUser); + await page.sidenav.openNewMenu(); + const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled(); + expect(isEnabled).toBe(true, 'Create folder is not enabled'); + }); + + it('Create folder is disabled when not enough permissions - [C280397]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteAdmin); + await sidenav.openNewMenu(); + const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled(); + expect(isEnabled).toBe(false, 'Create folder is not disabled'); + }); + + it('Upload File option is enabled when having enough permissions - on Personal Files - [C217145]', async () => { + await page.clickPersonalFiles(); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled(); + expect(isEnabled).toBe(true, 'Upload file is not enabled in Personal Files'); + }); + + it('Upload File option is enabled when having permissions - on File Libraries - [C290142]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteUser); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled(); + expect(isEnabled).toBe(true, 'Upload file is not enabled in File Libraries'); + }); + + it('Upload File option is disabled when user cannot create content in that location - [C217146]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteAdmin); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled(); + expect(isEnabled).toBe(false, 'Upload file is not disabled'); + }); + + it('Upload Folder option is enabled when having enough permissions - on Personal Files - [C213196]', async () => { + await page.clickPersonalFiles(); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled(); + expect(isEnabled).toBe(true, 'Upload folder is not enabled in Personal Files'); + }); + + it('Upload Folder option is enabled when having permissions - on File Libraries - [C290146]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteUser); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled(); + expect(isEnabled).toBe(true, 'Upload folder is not enabled in File Libraries'); + }); + + it('Upload Folder option is disabled when user cannot create content in that location - [C213193]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteAdmin); + await sidenav.openNewMenu(); + + const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled(); + expect(isEnabled).toBe(false, 'Upload folder is not disabled'); + }); + + it('Create folder enabled button tooltip - [C216342]', async () => { + await page.clickPersonalFiles(); + await sidenav.openNewMenu(); + + const tooltip = await sidenav.menu.getItemTooltip('Create folder'); + expect(tooltip).toContain('Create new folder'); + }); + + it('Create folder disabled button tooltip - [C280398]', async () => { + await page.clickFileLibraries(); + await dataTable.doubleClickOnRowByName(siteAdmin); + await sidenav.openNewMenu(); + + const tooltip = await sidenav.menu.getItemTooltip('Create folder'); + expect(tooltip).toContain(`Folders cannot be created whilst viewing the current items`); + }); + + it('Create Library button tooltip - [C286526]', async () => { + await sidenav.openNewMenu(); + + const tooltip = await sidenav.menu.getItemTooltip('Create Library'); + expect(tooltip).toContain('Create a new File Library'); + }); + +}); diff --git a/e2e/suites/actions/permanently-delete.test.ts b/e2e/suites/actions/permanently-delete.test.ts index 9cf21b4a47..68427f3af6 100755 --- a/e2e/suites/actions/permanently-delete.test.ts +++ b/e2e/suites/actions/permanently-delete.test.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { ConfirmDialog } from './../../components/components'; -import { SIDEBAR_LABELS } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -41,15 +40,16 @@ describe('Permanently delete from Trash', () => { const folder2 = `folder2-${Utils.random()}`; let foldersIds; + const site = `site-${Utils.random()}`; + const apis = { admin: new RepoClient(), user: new RepoClient(username, username) }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const trashPage = new BrowsingPage(); - const { dataTable, toolbar } = trashPage; + const page = new BrowsingPage(); + const { dataTable, toolbar } = page; const confirmDialog = new ConfirmDialog(); @@ -57,61 +57,65 @@ describe('Permanently delete from Trash', () => { await apis.admin.people.createUser({ username }); filesIds = (await apis.user.nodes.createFiles([ file1, file2, file3 ])).list.entries.map(entries => entries.entry.id); foldersIds = (await apis.user.nodes.createFolders([ folder1, folder2 ])).list.entries.map(entries => entries.entry.id); + await apis.user.sites.createSite(site); + await apis.user.nodes.deleteNodesById(filesIds, false); await apis.user.nodes.deleteNodesById(foldersIds, false); + await apis.user.sites.deleteSite(site, false); await loginPage.loginWith(username); done(); }); beforeEach(async (done) => { - await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); afterAll(async (done) => { - await Promise.all([ - apis.user.trashcan.emptyTrash(), - logoutPage.load() - ]); + await apis.user.trashcan.emptyTrash(); done(); }); - it('delete file - [C217091]', async () => { + it('delete a file - [C217091]', async () => { await dataTable.selectItem(file1); await toolbar.getButtonByTitleAttribute('Permanently delete').click(); - await trashPage.waitForDialog(); - // await trashPage.getDialogActionByLabel('Delete').click(); - // await trashPage.waitForDialogToClose(); + await page.waitForDialog(); await confirmDialog.clickButton('Delete'); - const text = await trashPage.getSnackBarMessage(); + const text = await page.getSnackBarMessage(); expect(text).toEqual(`${file1} deleted`); expect(await dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not deleted'); }); - it('delete folder - [C280416]', async () => { + it('delete a folder - [C280416]', async () => { await dataTable.selectItem(folder1); await toolbar.getButtonByTitleAttribute('Permanently delete').click(); - await trashPage.waitForDialog(); - // await trashPage.getDialogActionByLabel('Delete').click(); - // await trashPage.waitForDialogToClose(); + await page.waitForDialog(); await confirmDialog.clickButton('Delete'); - const text = await trashPage.getSnackBarMessage(); + const text = await page.getSnackBarMessage(); expect(text).toEqual(`${folder1} deleted`); expect(await dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not deleted'); }); + it('delete a library - [C290103]', async () => { + await dataTable.selectItem(site); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); + await page.waitForDialog(); + await confirmDialog.clickButton('Delete'); + const text = await page.getSnackBarMessage(); + + expect(text).toEqual(`${site} deleted`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} was not deleted`); + }); + it('delete multiple items - [C280417]', async () => { await dataTable.selectMultipleItems([ file2, folder2 ]); await toolbar.getButtonByTitleAttribute('Permanently delete').click(); - await trashPage.waitForDialog(); - // await trashPage.getDialogActionByLabel('Delete').click(); - // await trashPage.waitForDialogToClose(); + await page.waitForDialog(); await confirmDialog.clickButton('Delete'); - const text = await trashPage.getSnackBarMessage(); + const text = await page.getSnackBarMessage(); expect(text).toEqual(`2 items deleted`); expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, 'Item was not deleted'); @@ -121,7 +125,7 @@ describe('Permanently delete from Trash', () => { it('Confirmation dialog UI - [C269113]', async () => { await dataTable.selectItem(file3); await toolbar.getButtonByTitleAttribute('Permanently delete').click(); - await trashPage.waitForDialog(); + await page.waitForDialog(); expect(await confirmDialog.isDialogOpen()).toBe(true, 'Confirm delete dialog not open'); expect(await confirmDialog.getTitle()).toContain('Delete from trash'); @@ -136,7 +140,7 @@ describe('Permanently delete from Trash', () => { it('Keep action cancels the deletion - [C269115]', async () => { await dataTable.selectItem(file3); await toolbar.getButtonByTitleAttribute('Permanently delete').click(); - await trashPage.waitForDialog(); + await page.waitForDialog(); expect(await confirmDialog.isButtonEnabled('Keep')).toBe(true, 'KEEP button is not enabled'); await confirmDialog.clickButton('Keep'); diff --git a/e2e/suites/actions/restore.test.ts b/e2e/suites/actions/restore.test.ts index 6092775642..7d4499c192 100755 --- a/e2e/suites/actions/restore.test.ts +++ b/e2e/suites/actions/restore.test.ts @@ -24,8 +24,8 @@ */ import { browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { APP_ROUTES, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { APP_ROUTES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -38,7 +38,6 @@ describe('Restore from Trash', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; @@ -49,31 +48,29 @@ describe('Restore from Trash', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.admin.trashcan.emptyTrash(), - logoutPage.load() - ]); + await apis.user.trashcan.emptyTrash(); done(); }); xit(''); describe('successful restore', () => { - const file = `file-${Utils.random()}.txt`; - let fileId; - const folder = `folder-${Utils.random()}`; - let folderId; + const file = `file-${Utils.random()}.txt`; let fileId; + const folder = `folder-${Utils.random()}`; let folderId; + const site = `site-${Utils.random()}`; beforeAll(async (done) => { fileId = (await apis.user.nodes.createFile(file)).entry.id; folderId = (await apis.user.nodes.createFolder(folder)).entry.id; + await apis.user.sites.createSite(site); + await apis.user.nodes.deleteNodesById([fileId, folderId], false); + await apis.user.sites.deleteSite(site, false); done(); }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); @@ -89,8 +86,7 @@ describe('Restore from Trash', () => { expect(text).toContain(`${file} restored`); expect(text).toContain(`View`); expect(await dataTable.getRowByName(file).isPresent()).toBe(false, 'Item was not removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); expect(await page.dataTable.getRowByName(file).isPresent()).toBe(true, 'Item not displayed in list'); await apis.user.nodes.deleteNodeById(fileId, false); @@ -103,13 +99,23 @@ describe('Restore from Trash', () => { expect(text).toContain(`${folder} restored`); expect(text).toContain(`View`); expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, 'Item was not removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); expect(await page.dataTable.getRowByName(folder).isPresent()).toBe(true, 'Item not displayed in list'); await apis.user.nodes.deleteNodeById(folderId, false); }); + it('restore library - [C290104]', async () => { + await dataTable.selectItem(site); + await toolbar.getButtonByTitleAttribute('Restore').click(); + const text = await page.getSnackBarMessage(); + expect(text).toContain(`${site} restored`); + expect(text).toContain(`View`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} was not removed from list`); + await page.clickFileLibrariesAndWait(); + expect(await page.dataTable.getRowByName(site).isPresent()).toBe(true, `${site} not displayed in list`); + }); + it('restore multiple items - [C217182]', async () => { await dataTable.selectMultipleItems([file, folder]); await toolbar.getButtonByTitleAttribute('Restore').click(); @@ -118,8 +124,7 @@ describe('Restore from Trash', () => { expect(text).not.toContain(`View`); expect(await dataTable.getRowByName(file).isPresent()).toBe(false, 'Item was not removed from list'); expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, 'Item was not removed from list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); expect(await page.dataTable.getRowByName(file).isPresent()).toBe(true, 'Item not displayed in list'); expect(await page.dataTable.getRowByName(folder).isPresent()).toBe(true, 'Item not displayed in list'); @@ -131,7 +136,7 @@ describe('Restore from Trash', () => { await toolbar.getButtonByTitleAttribute('Restore').click(); await page.clickSnackBarAction(); await page.dataTable.waitForHeader(); - expect(await page.sidenav.isActiveByLabel('Personal Files')).toBe(true, 'Personal Files sidebar link not active'); + expect(await page.sidenav.isActive('Personal Files')).toBe(true, 'Personal Files sidebar link not active'); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); await apis.user.nodes.deleteNodeById(fileId, false); @@ -164,8 +169,7 @@ describe('Restore from Trash', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); @@ -178,7 +182,7 @@ describe('Restore from Trash', () => { }); it('Restore a file when another file with same name exists on the restore location - [C217178]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrashAndWait(); await dataTable.selectItem(file1); await toolbar.getButtonByTitleAttribute('Restore').click(); const text = await page.getSnackBarMessage(); @@ -186,7 +190,7 @@ describe('Restore from Trash', () => { }); it('Restore a file when original location no longer exists - [C217179]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrashAndWait(); await dataTable.selectItem(file2); await toolbar.getButtonByTitleAttribute('Restore').click(); const text = await page.getSnackBarMessage(); @@ -239,16 +243,12 @@ describe('Restore from Trash', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); afterAll(async (done) => { - await Promise.all([ - apis.user.trashcan.emptyTrash(), - logoutPage.load() - ]); + await apis.user.trashcan.emptyTrash(); done(); }); diff --git a/e2e/suites/actions/share-file.test.ts b/e2e/suites/actions/share-file.test.ts index 7237149f14..607ee4eec3 100755 --- a/e2e/suites/actions/share-file.test.ts +++ b/e2e/suites/actions/share-file.test.ts @@ -24,8 +24,8 @@ */ import { browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { ShareDialog } from '../../components/dialog/share-dialog'; import { Viewer } from '../../components/viewer/viewer'; @@ -44,7 +44,6 @@ describe('Share a file', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; const shareDialog = new ShareDialog(); @@ -61,10 +60,7 @@ describe('Share a file', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.user.nodes.deleteNodeById(parentId), - logoutPage.load() - ]); + await apis.user.nodes.deleteNodeById(parentId); done(); }); @@ -97,8 +93,7 @@ describe('Share a file', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.waitForHeader(); done(); @@ -125,8 +120,7 @@ describe('Share a file', () => { it('Share dialog default values - [C286327]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); @@ -142,8 +136,7 @@ describe('Share a file', () => { it('Close dialog - [C286328]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); @@ -153,8 +146,7 @@ describe('Share a file', () => { it('Share a file - [C286329]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -164,14 +156,13 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); }); it('Copy shared file URL - [C286330]', async () => { await dataTable.selectItem(file4); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); expect(url).toContain('/preview/s/'); @@ -188,8 +179,7 @@ describe('Share a file', () => { it('Share a file with expiration date - [C286332]', async () => { await dataTable.selectItem(file5); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); await shareDialog.clickExpirationToggle(); @@ -210,8 +200,7 @@ describe('Share a file', () => { it('Expire date is displayed correctly - [C286337]', async () => { await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); @@ -222,8 +211,7 @@ describe('Share a file', () => { it('Disable the share link expiration - [C286333]', async () => { await dataTable.selectItem(file7); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); @@ -239,8 +227,7 @@ describe('Share a file', () => { it('Shared file URL is not changed when Share dialog is closed and opened again - [C286335]', async () => { await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url1 = await shareDialog.getLinkUrl(); await shareDialog.clickClose(); @@ -248,8 +235,7 @@ describe('Share a file', () => { await page.dataTable.clearSelection(); await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url2 = await shareDialog.getLinkUrl(); @@ -258,7 +244,7 @@ describe('Share a file', () => { it('Share a file from the context menu - [C286345]', async () => { await dataTable.rightClickOnItem(file9); - await contextMenu.clickMenuItem('Share'); + await contextMenu.clickShareAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -268,7 +254,7 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); }); }); @@ -309,8 +295,7 @@ describe('Share a file', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.doubleClickOnRowByName(parentInSite); @@ -331,8 +316,7 @@ describe('Share a file', () => { it('Share dialog default values - [C286639]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); @@ -348,8 +332,7 @@ describe('Share a file', () => { it('Close dialog - [C286640]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); @@ -359,8 +342,7 @@ describe('Share a file', () => { it('Share a file - [C286641]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -370,14 +352,13 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); }); it('Copy shared file URL - [C286642]', async () => { await dataTable.selectItem(file4); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); expect(url).toContain('/preview/s/'); @@ -394,8 +375,7 @@ describe('Share a file', () => { it('Share a file with expiration date - [C286643]', async () => { await dataTable.selectItem(file5); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); await shareDialog.clickExpirationToggle(); @@ -416,8 +396,7 @@ describe('Share a file', () => { it('Expire date is displayed correctly - [C286644]', async () => { await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); @@ -428,8 +407,7 @@ describe('Share a file', () => { it('Disable the share link expiration - [C286645]', async () => { await dataTable.selectItem(file7); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); @@ -445,8 +423,7 @@ describe('Share a file', () => { it('Shared file URL is not changed when Share dialog is closed and opened again - [C286646]', async () => { await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url1 = await shareDialog.getLinkUrl(); await shareDialog.clickClose(); @@ -454,8 +431,7 @@ describe('Share a file', () => { await page.dataTable.clearSelection(); await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url2 = await shareDialog.getLinkUrl(); @@ -464,7 +440,7 @@ describe('Share a file', () => { it('Share a file from the context menu - [C286647]', async () => { await dataTable.rightClickOnItem(file9); - await contextMenu.clickMenuItem('Share'); + await contextMenu.clickShareAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -474,7 +450,7 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); }); }); @@ -509,8 +485,7 @@ describe('Share a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -535,8 +510,7 @@ describe('Share a file', () => { it('Share dialog default values - [C286657]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); @@ -552,8 +526,7 @@ describe('Share a file', () => { it('Close dialog - [C286658]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); @@ -563,8 +536,7 @@ describe('Share a file', () => { it('Share a file - [C286659]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -574,14 +546,13 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); }); it('Copy shared file URL - [C286660]', async () => { await dataTable.selectItem(file4); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); expect(url).toContain('/preview/s/'); @@ -598,8 +569,7 @@ describe('Share a file', () => { it('Share a file with expiration date - [C286661]', async () => { await dataTable.selectItem(file5); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); await shareDialog.clickExpirationToggle(); @@ -620,8 +590,7 @@ describe('Share a file', () => { it('Expire date is displayed correctly - [C286662]', async () => { await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); @@ -632,8 +601,7 @@ describe('Share a file', () => { it('Disable the share link expiration - [C286663]', async () => { await dataTable.selectItem(file7); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); @@ -649,8 +617,7 @@ describe('Share a file', () => { it('Shared file URL is not changed when Share dialog is closed and opened again - [C286664]', async () => { await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url1 = await shareDialog.getLinkUrl(); await shareDialog.clickClose(); @@ -658,8 +625,7 @@ describe('Share a file', () => { await page.dataTable.clearSelection(); await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url2 = await shareDialog.getLinkUrl(); @@ -668,7 +634,7 @@ describe('Share a file', () => { it('Share a file from the context menu - [C286665]', async () => { await dataTable.rightClickOnItem(file9); - await contextMenu.clickMenuItem('Share'); + await contextMenu.clickShareAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -678,7 +644,7 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); }); }); @@ -715,8 +681,7 @@ describe('Share a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -739,8 +704,7 @@ describe('Share a file', () => { it('Share dialog default values - [C286648]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); @@ -756,8 +720,7 @@ describe('Share a file', () => { it('Close dialog - [C286649]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); @@ -767,8 +730,7 @@ describe('Share a file', () => { it('Copy shared file URL - [C286651]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); expect(url).toContain('/preview/s/'); @@ -785,8 +747,7 @@ describe('Share a file', () => { it('Expire date is displayed correctly - [C286653]', async () => { await dataTable.selectItem(file4); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const expireProperty = await apis.user.nodes.getNodeProperty(file4Id, 'qshare:expiryDate'); @@ -797,8 +758,7 @@ describe('Share a file', () => { it('Disable the share link expiration - [C286654]', async () => { await dataTable.selectItem(file5); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); @@ -814,8 +774,7 @@ describe('Share a file', () => { it('Shared file URL is not changed when Share dialog is closed and opened again - [C286655]', async () => { await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url1 = await shareDialog.getLinkUrl(); await shareDialog.clickClose(); @@ -823,8 +782,7 @@ describe('Share a file', () => { await page.dataTable.clearSelection(); await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url2 = await shareDialog.getLinkUrl(); @@ -833,7 +791,7 @@ describe('Share a file', () => { it('Open Share dialog from context menu - [C286656]', async () => { await dataTable.rightClickOnItem(file7); - await contextMenu.clickMenuItem('Shared link settings'); + await contextMenu.clickShareEditAction(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file7}`); @@ -890,8 +848,7 @@ describe('Share a file', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -916,8 +873,7 @@ describe('Share a file', () => { it('Share dialog default values - [C286666]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); @@ -933,8 +889,7 @@ describe('Share a file', () => { it('Close dialog - [C286667]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); @@ -944,8 +899,7 @@ describe('Share a file', () => { it('Share a file - [C286668]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); @@ -955,14 +909,13 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); }); it('Copy shared file URL - [C286669]', async () => { await dataTable.selectItem(file4); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); expect(url).toContain('/preview/s/'); @@ -979,8 +932,7 @@ describe('Share a file', () => { it('Share a file with expiration date - [C286670]', async () => { await dataTable.selectItem(file5); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); await shareDialog.clickExpirationToggle(); @@ -1001,8 +953,7 @@ describe('Share a file', () => { it('Expire date is displayed correctly - [C286671]', async () => { await dataTable.selectItem(file6); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); @@ -1013,8 +964,7 @@ describe('Share a file', () => { it('Disable the share link expiration - [C286672]', async () => { await dataTable.selectItem(file7); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); @@ -1030,8 +980,7 @@ describe('Share a file', () => { it('Shared file URL is not changed when Share dialog is closed and opened again - [C286673]', async () => { await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url1 = await shareDialog.getLinkUrl(); await shareDialog.clickClose(); @@ -1039,8 +988,7 @@ describe('Share a file', () => { await page.dataTable.clearSelection(); await dataTable.selectItem(file8); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url2 = await shareDialog.getLinkUrl(); @@ -1059,7 +1007,7 @@ describe('Share a file', () => { expect(url).toContain(sharedId); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); }); }); diff --git a/e2e/suites/actions/single-click.test.ts b/e2e/suites/actions/single-click.test.ts index 85f96ce2de..6e53f78f23 100755 --- a/e2e/suites/actions/single-click.test.ts +++ b/e2e/suites/actions/single-click.test.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Viewer } from '../../components/viewer/viewer'; -import { SIDEBAR_LABELS } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -47,7 +46,6 @@ describe('Single click on item name', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, breadcrumb } = page; const viewer = new Viewer(); @@ -82,13 +80,11 @@ describe('Single click on item name', () => { await apis.user.nodes.deleteNodeById(folder1Id); await apis.user.nodes.deleteNodeById(file1Id); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); it('Hyperlink does not appear for items in the Trash - [C284899]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.hasLinkOnName(deletedFile1)).toBe(false, 'Link on name is present'); expect(await dataTable.hasLinkOnName(deletedFolder1)).toBe(false, 'Link on name is present'); @@ -96,8 +92,7 @@ describe('Single click on item name', () => { describe('on Personal Files', () => { beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -122,8 +117,7 @@ describe('Single click on item name', () => { describe('on File Libraries', () => { beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); done(); }); @@ -141,8 +135,7 @@ describe('Single click on item name', () => { describe('on Shared Files', () => { beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -161,8 +154,7 @@ describe('Single click on item name', () => { describe('on Recent Files', () => { beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -181,8 +173,7 @@ describe('Single click on item name', () => { describe('on Favorites', () => { beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); diff --git a/e2e/suites/actions/special-permissions-available-actions.test.ts b/e2e/suites/actions/special-permissions-available-actions.test.ts index 1328f480b7..72816829b4 100755 --- a/e2e/suites/actions/special-permissions-available-actions.test.ts +++ b/e2e/suites/actions/special-permissions-available-actions.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS, FILES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES, FILES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; import { Viewer } from '../../components/viewer/viewer'; @@ -56,7 +56,6 @@ describe('Granular permissions available actions : ', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; const contextMenu = dataTable.menu; @@ -78,10 +77,10 @@ describe('Granular permissions available actions : ', () => { docxFileId = (await apis.admin.upload.uploadFile(docxFile, docLibId)).entry.id; - await apis.admin.sites.addSiteMember(siteName, userManager, SITE_ROLES.SITE_MANAGER); - await apis.admin.sites.addSiteMember(siteName, userConsumer, SITE_ROLES.SITE_CONSUMER); + await apis.admin.sites.addSiteMember(siteName, userManager, SITE_ROLES.SITE_MANAGER.ROLE); + await apis.admin.sites.addSiteMember(siteName, userConsumer, SITE_ROLES.SITE_CONSUMER.ROLE); - await apis.admin.nodes.setGranularPermission(file3Id, false, userConsumer, SITE_ROLES.SITE_MANAGER); + await apis.admin.nodes.setGranularPermission(file3Id, false, userConsumer, SITE_ROLES.SITE_MANAGER.ROLE); await apis.userConsumer.shared.shareFileById(file1Id); await apis.userConsumer.shared.shareFileById(file2Id); @@ -99,7 +98,6 @@ describe('Granular permissions available actions : ', () => { afterAll(async done => { await apis.admin.sites.deleteSite(siteName); - await logoutPage.load(); done(); }); @@ -112,8 +110,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280476]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([file1, file2]); @@ -129,8 +126,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Shared Files - [C280477]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -144,8 +140,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C280478]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -167,8 +162,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280455]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectItem(file1); @@ -186,8 +180,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Shared Files - [C280456]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await page.dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await page.dataTable.selectItem(file1); expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); @@ -203,8 +196,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C213121]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectItem(file1); expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); @@ -227,8 +219,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280444]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectItem(folder1); @@ -246,8 +237,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286266]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectItem(folder1); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is not displayed for ${folder1}`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); @@ -272,8 +262,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280464]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([file1, file2]); @@ -289,8 +278,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Shared Files - [C286284]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -304,8 +292,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286285]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -328,8 +315,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280465]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([folder1, folder2]); @@ -345,8 +331,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286286]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([folder1, folder2]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -369,8 +354,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280466]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([file1, folder1]); @@ -386,8 +370,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286287]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([file1, folder1]); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); @@ -409,8 +392,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280599]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.rightClickOnItem(file1); @@ -428,8 +410,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Shared Files - [C286264]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.rightClickOnItem(file1); expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`); @@ -446,8 +427,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286262]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.rightClickOnItem(file1); expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`); @@ -472,8 +452,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280600]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.rightClickOnItem(folder1); @@ -490,8 +469,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286263]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.rightClickOnItem(folder1); expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folder1}`); // enable when ACA-1737 is done @@ -517,8 +495,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280647]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([file1, file2]); @@ -533,8 +510,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Shared Files - [C286283]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.selectMultipleItems([file1, file2]); await dataTable.rightClickOnMultipleSelection(); expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); @@ -547,8 +523,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286280]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([file1, file2]); await dataTable.rightClickOnMultipleSelection(); expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); @@ -570,8 +545,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280666]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([folder1, folder2]); @@ -586,8 +560,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286281]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([folder1, folder2]); await dataTable.rightClickOnMultipleSelection(); expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); @@ -609,8 +582,7 @@ describe('Granular permissions available actions : ', () => { }); it('on File Libraries - [C280669]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([file1, folder1]); @@ -625,8 +597,7 @@ describe('Granular permissions available actions : ', () => { }); it('on Favorites - [C286282]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectMultipleItems([file1, folder1]); await dataTable.rightClickOnMultipleSelection(); expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); @@ -647,8 +618,7 @@ describe('Granular permissions available actions : ', () => { }); it('file from File Libraries - [C268128]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.doubleClickOnRowByName(docxFile); @@ -659,10 +629,11 @@ describe('Granular permissions available actions : ', () => { expect(await viewerToolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed`); expect(await viewerToolbar.isButtonPresent('Print')).toBe(true, `Print is not displayed`); expect(await viewerToolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `Full screen is not displayed`); + expect(await viewerToolbar.isShareEditButtonPresent()).toBe(true, 'Shared link settings is not displayed'); expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`); await viewerToolbar.openMoreMenu(); expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`); + expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(false, `Share is displayed in More actions`); expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); expect(await viewerToolbar.menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); @@ -672,8 +643,7 @@ describe('Granular permissions available actions : ', () => { }); it('file from Shared Files - [C286310]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await page.dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.doubleClickOnRowByName(docxFile); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); @@ -694,8 +664,7 @@ describe('Granular permissions available actions : ', () => { }); it('file from Favorites - [C286311]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.doubleClickOnRowByName(docxFile); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); @@ -704,10 +673,11 @@ describe('Granular permissions available actions : ', () => { expect(await viewerToolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed`); expect(await viewerToolbar.isButtonPresent('Print')).toBe(true, `Print is not displayed`); expect(await viewerToolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `Full screen is not displayed`); + expect(await viewerToolbar.isShareEditButtonPresent()).toBe(true, 'Shared link settings is not displayed'); expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`); await viewerToolbar.openMoreMenu(); expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`); + expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(false, `Share is displayed in More actions`); expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); // TODO: enable when ACA-1737 is done // expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); diff --git a/e2e/suites/actions/toolbar-multiple-selection.test.ts b/e2e/suites/actions/toolbar-multiple-selection.test.ts index abebeaa5ee..3773204e6c 100755 --- a/e2e/suites/actions/toolbar-multiple-selection.test.ts +++ b/e2e/suites/actions/toolbar-multiple-selection.test.ts @@ -24,8 +24,8 @@ */ import { browser, protractor } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -63,7 +63,6 @@ describe('Toolbar actions - multiple selection : ', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; @@ -105,8 +104,7 @@ describe('Toolbar actions - multiple selection : ', () => { await Promise.all([ apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id]), apis.user.trashcan.emptyTrash(), - apis.user.sites.deleteSite(siteName), - logoutPage.load() + apis.user.sites.deleteSite(siteName) ]); done(); }); @@ -116,8 +114,7 @@ describe('Toolbar actions - multiple selection : ', () => { describe('Personal Files', () => { beforeEach(async done => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -187,10 +184,8 @@ describe('Toolbar actions - multiple selection : ', () => { describe('File Libraries', () => { beforeEach(async done => { - // await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.clearSelection(); @@ -238,8 +233,7 @@ describe('Toolbar actions - multiple selection : ', () => { beforeEach(async done => { // await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -259,10 +253,8 @@ describe('Toolbar actions - multiple selection : ', () => { describe('Recent Files', () => { beforeEach(async done => { - // await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -284,8 +276,7 @@ describe('Toolbar actions - multiple selection : ', () => { beforeEach(async done => { // await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.clearSelection(); done(); }); @@ -330,8 +321,7 @@ describe('Toolbar actions - multiple selection : ', () => { describe('Trash', () => { beforeEach(async done => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); await dataTable.clearSelection(); done(); }); diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts index 77ffeb6543..607b01c921 100755 --- a/e2e/suites/actions/toolbar-single-selection.test.ts +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -40,13 +40,15 @@ describe('Toolbar actions - single selection : ', () => { const fileInSite = `fileAdmin-${Utils.random()}.txt`; const folderInSite = `folderAdmin-${Utils.random()}`; + const adminPublic = `admin-public-${Utils.random()}`; + const adminModerated = `admin-moderated-${Utils.random()}`; + const apis = { admin: new RepoClient(), user: new RepoClient(username, username) }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; @@ -67,13 +69,18 @@ describe('Toolbar actions - single selection : ', () => { await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); const docLibId = await apis.user.sites.getDocLibId(siteName); - await apis.user.nodes.createFile(fileInSite, docLibId); await apis.user.nodes.createFolder(folderInSite, docLibId); await apis.user.nodes.deleteNodeById(fileForDeleteId, false); await apis.user.nodes.deleteNodeById(folderForDeleteId, false); + await apis.admin.sites.createSite(adminPublic); + await apis.admin.sites.createSite(adminModerated, SITE_VISIBILITY.MODERATED); + await apis.user.favorites.addFavoriteById('site', adminPublic); + await apis.user.favorites.addFavoriteById('site', adminModerated); + await apis.user.sites.requestToJoin(adminModerated); + await loginPage.loginWith(username); done(); }); @@ -83,8 +90,9 @@ describe('Toolbar actions - single selection : ', () => { apis.user.nodes.deleteNodeById(fileUserId), apis.user.nodes.deleteNodeById(folderUserId), apis.user.sites.deleteSite(siteName), - apis.user.trashcan.emptyTrash(), - logoutPage.load() + apis.admin.sites.deleteSite(adminPublic), + apis.admin.sites.deleteSite(adminModerated), + apis.user.trashcan.emptyTrash() ]); done(); }); @@ -92,26 +100,23 @@ describe('Toolbar actions - single selection : ', () => { xit(''); describe('General tests', () => { - it('actions not displayed for top level of File Libraries - [C213135]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.selectItem(siteName); - expect(await toolbar.isEmpty()).toBe(true, 'toolbar not empty'); + beforeEach(async (done) => { + await Utils.pressEscape(); + await dataTable.clearSelection(); + done(); }); it('selected row is marked with a check circle icon - [C213134]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.selectItem(fileUser); expect(await dataTable.hasCheckMarkIcon(fileUser)).toBe(true, 'check mark missing'); }); }); - describe('Personal Files', () => { + describe('on Personal Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -149,11 +154,10 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('File Libraries', () => { + describe('on File Libraries', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.clearSelection(); @@ -193,11 +197,62 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('Shared Files', () => { + describe('on a library', () => { + beforeEach(async (done) => { + await Utils.pressEscape(); + await dataTable.clearSelection(); + done(); + }); + + it('Available actions when a library is selected - My Libraries - [C213135]', async () => { + await page.goToMyLibraries(); + await dataTable.selectItem(siteName); + expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed'); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${siteName}`); + expect(await toolbar.isButtonPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`); + await toolbar.openMoreMenu(); + expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`); + expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user is a member - [C289892]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(siteName); + expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed'); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${siteName}`); + expect(await toolbar.isButtonPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`); + await toolbar.openMoreMenu(); + expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`); + expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user is not a member - [C290090]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(adminPublic); + expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed'); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${adminPublic}`); + expect(await toolbar.isButtonPresent('Join')).toBe(true, `Join is not displayed for ${adminPublic}`); + await toolbar.openMoreMenu(); + expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminPublic}`); + expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminPublic}`); + }); + + it('Available actions when a library is selected - Favorite Libraries - user requested to join - [C290091]', async () => { + await page.goToFavoriteLibraries(); + await dataTable.selectItem(adminModerated); + expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed'); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${adminModerated}`); + expect(await toolbar.isButtonPresent('Cancel join request')).toBe(true, `Cancel join is not displayed for ${adminModerated}`); + await toolbar.openMoreMenu(); + expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminModerated}`); + expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminModerated}`); + }); + }); + + describe('on Shared Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await page.dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -212,8 +267,8 @@ describe('Toolbar actions - single selection : ', () => { expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + expect(await toolbar.isShareEditButtonPresent()).toBe(true, `Shared link settings is not displayed for ${fileUser}`); await toolbar.openMoreMenu(); - expect(await toolbar.menu.isMenuItemPresent('Shared link settings')).toBe(true, `Shared is not displayed for ${fileUser}`); expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); @@ -222,11 +277,10 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('Recent Files', () => { + describe('on Recent Files', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); await dataTable.clearSelection(); done(); }); @@ -250,11 +304,10 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('Favorites', () => { + describe('on Favorites', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.clearSelection(); done(); }); @@ -292,11 +345,10 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('Trash', () => { + describe('on Trash', () => { beforeEach(async (done) => { await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); await dataTable.clearSelection(); done(); }); diff --git a/e2e/suites/actions/unshare-file.test.ts b/e2e/suites/actions/unshare-file.test.ts index 11279c523e..34e6781cf3 100755 --- a/e2e/suites/actions/unshare-file.test.ts +++ b/e2e/suites/actions/unshare-file.test.ts @@ -24,8 +24,8 @@ */ import { browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { ShareDialog } from '../../components/dialog/share-dialog'; import { ConfirmDialog } from '../../components/dialog/confirm-dialog'; @@ -43,7 +43,6 @@ describe('Unshare a file', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, toolbar } = page; const shareDialog = new ShareDialog(); @@ -59,10 +58,7 @@ describe('Unshare a file', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.user.nodes.deleteNodeById(parentId), - logoutPage.load() - ]); + await apis.user.nodes.deleteNodeById(parentId); done(); }); @@ -88,8 +84,7 @@ describe('Unshare a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.waitForHeader(); done(); @@ -111,8 +106,7 @@ describe('Unshare a file', () => { it('Unshare dialog UI - [C286339]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); @@ -127,8 +121,7 @@ describe('Unshare a file', () => { it('Unshare a file - [C286340]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -140,7 +133,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`); await browser.get(url); @@ -152,8 +145,7 @@ describe('Unshare a file', () => { it('Cancel the Unshare action - [C286341]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const urlBefore = await shareDialog.getLinkUrl(); @@ -170,7 +162,7 @@ describe('Unshare a file', () => { it('Unshare a file from the context menu - [C286359]', async () => { await dataTable.rightClickOnItem(file4); - await contextMenu.clickMenuItem('Shared link settings'); + await contextMenu.clickShareEditAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -182,7 +174,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`); await browser.get(url); @@ -222,8 +214,7 @@ describe('Unshare a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.doubleClickOnRowByName(parentInSite); @@ -244,8 +235,7 @@ describe('Unshare a file', () => { it('Unshare dialog UI - [C286679]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); @@ -260,8 +250,7 @@ describe('Unshare a file', () => { it('Unshare a file - [C286680]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -273,7 +262,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`); await browser.get(url); @@ -285,8 +274,7 @@ describe('Unshare a file', () => { it('Cancel the Unshare action - [C286681]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const urlBefore = await shareDialog.getLinkUrl(); @@ -303,7 +291,7 @@ describe('Unshare a file', () => { it('Unshare a file from the context menu - [C286683]', async () => { await dataTable.rightClickOnItem(file4); - await contextMenu.clickMenuItem('Shared link settings'); + await contextMenu.clickShareEditAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -315,7 +303,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`); await browser.get(url); @@ -348,8 +336,7 @@ describe('Unshare a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -369,8 +356,7 @@ describe('Unshare a file', () => { it('Unshare dialog UI - [C286689]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); @@ -385,8 +371,7 @@ describe('Unshare a file', () => { it('Unshare a file - [C286690]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -398,7 +383,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`); await browser.get(url); @@ -410,8 +395,7 @@ describe('Unshare a file', () => { it('Cancel the Unshare action - [C286691]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const urlBefore = await shareDialog.getLinkUrl(); @@ -428,7 +412,7 @@ describe('Unshare a file', () => { it('Unshare a file from the context menu - [C286693]', async () => { await dataTable.rightClickOnItem(file4); - await contextMenu.clickMenuItem('Shared link settings'); + await contextMenu.clickShareEditAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -440,7 +424,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`); await browser.get(url); @@ -473,8 +457,7 @@ describe('Unshare a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -494,8 +477,7 @@ describe('Unshare a file', () => { it('Unshare dialog UI - [C286684]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); @@ -510,8 +492,7 @@ describe('Unshare a file', () => { it('Unshare a file - [C286685]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -534,8 +515,7 @@ describe('Unshare a file', () => { it('Cancel the Unshare action - [C286686]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); const urlBefore = await shareDialog.getLinkUrl(); @@ -552,7 +532,7 @@ describe('Unshare a file', () => { it('Unshare a file from the context menu - [C286688]', async () => { await dataTable.rightClickOnItem(file4); - await contextMenu.clickMenuItem('Shared link settings'); + await contextMenu.clickShareEditAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -603,8 +583,7 @@ describe('Unshare a file', () => { beforeEach(async (done) => { await page.refresh(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -624,8 +603,9 @@ describe('Unshare a file', () => { it('Unshare dialog UI - [C286694]', async () => { await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); @@ -640,8 +620,9 @@ describe('Unshare a file', () => { it('Unshare a file - [C286695]', async () => { await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -653,7 +634,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`); await browser.get(url); @@ -665,8 +646,9 @@ describe('Unshare a file', () => { it('Cancel the Unshare action - [C286696]', async () => { await dataTable.selectItem(file3); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); const urlBefore = await shareDialog.getLinkUrl(); @@ -683,7 +665,9 @@ describe('Unshare a file', () => { it('Unshare a file from the context menu - [C286698]', async () => { await dataTable.rightClickOnItem(file4); - await contextMenu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await contextMenu.clickShareAction(); await shareDialog.waitForDialogToOpen(); const url = await shareDialog.getLinkUrl(); await shareDialog.clickShareToggle(); @@ -695,7 +679,7 @@ describe('Unshare a file', () => { expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`); // TODO: disable check cause api is slow to update - // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // await page.clickSharedFiles(); // expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`); await browser.get(url); @@ -720,7 +704,7 @@ describe('Unshare a file', () => { file1Id = (await apis.admin.nodes.createFile(file1, docLibId)).entry.id; file2Id = (await apis.admin.nodes.createFile(file2, docLibId)).entry.id; - await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE); await apis.admin.shared.shareFileById(file1Id); await apis.user.shared.shareFileById(file2Id); @@ -749,70 +733,62 @@ describe('Unshare a file', () => { }); it('on File Libraries - file shared by other user - [C286682]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(sitePrivate); await dataTable.waitForHeader(); await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); }); it('on File Libraries - file shared by the user - [C286701]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(sitePrivate); await dataTable.waitForHeader(); await dataTable.selectItem(file2); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); }); it('on Shared Files - file shared by other user - [C286687]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); }); it('on Shared Files - file shared by the user - [C286702]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Shared link settings'); + await toolbar.clickShareEditButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); }); it('on Favorites - file shared by other user - [C286697]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); }); it('on Favorites - file shared by the user - [C286703]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.selectItem(file1); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + // TODO: remove workaround for favorites + // await toolbar.clickShareEditButton(); + await toolbar.clickShareButton(); await shareDialog.waitForDialogToOpen(); expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer'); diff --git a/e2e/suites/actions/upload-file.test.ts b/e2e/suites/actions/upload-file.test.ts index 3b2e4f8bbf..e444582271 100755 --- a/e2e/suites/actions/upload-file.test.ts +++ b/e2e/suites/actions/upload-file.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -// import { browser, protractor, promise } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -40,7 +38,6 @@ describe('Upload files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable } = page; @@ -53,16 +50,12 @@ describe('Upload files', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); afterAll(async (done) => { - await Promise.all([ - apis.user.nodes.deleteNodeById(folder1Id), - logoutPage.load() - ]); + await apis.user.nodes.deleteNodeById(folder1Id); done(); }); diff --git a/e2e/suites/application/general.test.ts b/e2e/suites/application/general.test.ts index a91a1eac1b..f1ffc6c769 100644 --- a/e2e/suites/application/general.test.ts +++ b/e2e/suites/application/general.test.ts @@ -24,14 +24,13 @@ */ import { browser } from 'protractor'; -import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages'; +import { BrowsingPage, LoginPage } from '../../pages/pages'; import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; describe('General', () => { const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const createDialog = new CreateOrEditFolderDialog(); const adminApi = new RepoClient(); @@ -48,26 +47,31 @@ describe('General', () => { afterAll(async (done) => { await nodesApi.deleteNodeById(folderId); - await logoutPage.load(); done(); }); it('should close opened dialogs - [C286473]', async () => { await loginPage.loginWithAdmin(); - await page.sidenav.openCreateDialog(); + await page.sidenav.openCreateFolderDialog(); await createDialog.waitForDialogToOpen(); await createDialog.enterName(folder); await authApi.logout(); await createDialog.clickCreate(); - expect(await browser.getTitle()).toContain('Sign in'); + const message = await page.getSnackBarMessage(); expect(message).toEqual('The action was unsuccessful. Try again or contact your IT Team.'); - await createDialog.waitForDialogToClose(); - expect(createDialog.component.isPresent()).not.toBe(true, 'dialog is present'); + expect(await browser.getTitle()).toContain('Sign in'); + + try { + await createDialog.waitForDialogToClose(); + } catch (error) { + console.log('err: ', error); + } + expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is present'); }); }); }); diff --git a/e2e/suites/application/page-titles.test.ts b/e2e/suites/application/page-titles.test.ts index cbe11c01fa..cdb636c34a 100755 --- a/e2e/suites/application/page-titles.test.ts +++ b/e2e/suites/application/page-titles.test.ts @@ -24,25 +24,22 @@ */ import { browser } from 'protractor'; - import { SIDEBAR_LABELS, PAGE_TITLES } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; describe('Page titles', () => { const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const adminApi = new RepoClient(); const { nodes: nodesApi } = adminApi; const file = `file-${Utils.random()}.txt`; let fileId; - const header = page.header; + const { searchInput } = page.header; xit(''); - describe('on Login / Logout pages', () => { it('on Login page - [C217155]', async () => { await loginPage.load(); @@ -71,69 +68,65 @@ describe('Page titles', () => { }); afterAll(async (done) => { - await Promise.all([ - logoutPage.load(), - adminApi.nodes.deleteNodeById(fileId) - ]); + await adminApi.nodes.deleteNodeById(fileId); done(); }); it('Personal Files page - [C217157]', async () => { const label = SIDEBAR_LABELS.PERSONAL_FILES; - await page.sidenav.navigateToLinkByLabel(label); + await page.sidenav.navigateToLink(label); expect(await browser.getTitle()).toContain(label); }); - it('File Libraries page - [C217158]', async () => { - const label = SIDEBAR_LABELS.FILE_LIBRARIES; + it('My Libraries page - [C217158]', async () => { + await page.goToMyLibraries(); + expect(await browser.getTitle()).toContain(PAGE_TITLES.MY_LIBRARIES); + }); - await page.sidenav.navigateToLinkByLabel(label); - expect(await browser.getTitle()).toContain(label); + it('Favorite Libraries page - [C289907]', async () => { + await page.goToFavoriteLibraries(); + expect(await browser.getTitle()).toContain(PAGE_TITLES.FAVORITE_LIBRARIES); }); it('Shared Files page - [C217159]', async () => { const label = SIDEBAR_LABELS.SHARED_FILES; - await page.sidenav.navigateToLinkByLabel(label); + await page.sidenav.navigateToLink(label); expect(await browser.getTitle()).toContain(label); }); it('Recent Files page - [C217160]', async () => { const label = SIDEBAR_LABELS.RECENT_FILES; - await page.sidenav.navigateToLinkByLabel(label); + await page.sidenav.navigateToLink(label); expect(await browser.getTitle()).toContain(label); }); it('Favorites page - [C217161]', async () => { const label = SIDEBAR_LABELS.FAVORITES; - await page.sidenav.navigateToLinkByLabel(label); + await page.sidenav.navigateToLink(label); expect(await browser.getTitle()).toContain(label); }); it('Trash page - [C217162]', async () => { const label = SIDEBAR_LABELS.TRASH; - await page.sidenav.navigateToLinkByLabel(label); + await page.sidenav.navigateToLink(label); expect(await browser.getTitle()).toContain(label); }); it('File Preview page - [C280415]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(file); expect(await browser.getTitle()).toContain(PAGE_TITLES.VIEWER); await Utils.pressEscape(); }); it('Search Results page - [C280413]', async () => { - await header.waitForSearchButton(); - await header.searchButton.click(); - await page.dataTable.waitForHeader(); - await header.waitForSearchBar(); - await header.searchForText(file); + await searchInput.clickSearchButton(); + await searchInput.searchFor(file); expect(await browser.getTitle()).toContain(PAGE_TITLES.SEARCH); }); }); diff --git a/e2e/suites/authentication/login.test.ts b/e2e/suites/authentication/login.test.ts index e39e8118eb..85b280eef0 100755 --- a/e2e/suites/authentication/login.test.ts +++ b/e2e/suites/authentication/login.test.ts @@ -26,7 +26,7 @@ import { browser } from 'protractor'; import { APP_ROUTES } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { navigate } from '../../utilities/browser-utils'; @@ -34,7 +34,6 @@ import { navigate } from '../../utilities/browser-utils'; describe('Login', () => { const peopleApi = new RepoClient().people; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); /* cspell:disable-next-line */ const testUser = `user-${Utils.random()}@alfness`; @@ -70,7 +69,6 @@ describe('Login', () => { }); afterEach(async (done) => { - await logoutPage.load(); await Utils.clearLocalStorage(); done(); }); @@ -147,7 +145,6 @@ describe('Login', () => { it('user is able to login after changing his password - [C213104]', async () => { await loginPage.loginWith(testUser2.username, testUser2.password); - await logoutPage.load(); await peopleApi.changePassword(testUser2.username, newPassword); await loginPage.loginWith(testUser2.username, newPassword); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); diff --git a/e2e/suites/authentication/logout.test.ts b/e2e/suites/authentication/logout.test.ts index 1d0aabc7c2..e0bef04bbf 100755 --- a/e2e/suites/authentication/logout.test.ts +++ b/e2e/suites/authentication/logout.test.ts @@ -24,7 +24,7 @@ */ import { browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { APP_ROUTES } from '../../configs'; @@ -32,7 +32,6 @@ import { APP_ROUTES } from '../../configs'; describe('Logout', () => { const page = new BrowsingPage(); const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const peopleApi = new RepoClient().people; @@ -48,11 +47,6 @@ describe('Logout', () => { done(); }); - afterEach(async (done) => { - await logoutPage.load(); - done(); - }); - it('Sign out option is available - [C213143]', async () => { await page.header.userInfo.openMenu(); expect(await page.header.userInfo.menu.isMenuItemPresent('Sign out')).toBe(true, 'Sign out option not displayed'); diff --git a/e2e/suites/extensions/ext-context-submenus.test.ts b/e2e/suites/extensions/ext-context-submenus.test.ts index a191e7d74c..9dc1768be8 100644 --- a/e2e/suites/extensions/ext-context-submenus.test.ts +++ b/e2e/suites/extensions/ext-context-submenus.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { EXTENSIBILITY_CONFIGS, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { EXTENSIBILITY_CONFIGS } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; @@ -52,9 +52,8 @@ describe('Extensions - Context submenu', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const personalFilesPage = new BrowsingPage(); - const {dataTable} = personalFilesPage; + const page = new BrowsingPage(); + const {dataTable} = page; const contextMenu = dataTable.menu; beforeAll(async (done) => { @@ -63,7 +62,7 @@ describe('Extensions - Context submenu', () => { folderId = (await apis.user.nodes.createFolder(folder)).entry.id; await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.CONTEXT_SUBMENUS); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.CONTEXT_SUBMENUS); await loginPage.loginWith(username); done(); @@ -72,15 +71,13 @@ describe('Extensions - Context submenu', () => { beforeEach(async (done) => { await Utils.pressEscape(); await dataTable.clearSelection(); - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); afterAll(async (done) => { await apis.user.nodes.deleteNodeById(fileId, true); await apis.user.nodes.deleteNodeById(folderId, true); - await logoutPage.load(); done(); }); diff --git a/e2e/suites/extensions/ext-document-list.test.ts b/e2e/suites/extensions/ext-document-list.test.ts index adab8815aa..e0d1d95cd6 100644 --- a/e2e/suites/extensions/ext-document-list.test.ts +++ b/e2e/suites/extensions/ext-document-list.test.ts @@ -23,9 +23,9 @@ * along with Alfresco. If not, see . */ -import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages'; +import { BrowsingPage, LoginPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; -import { EXTENSIBILITY_CONFIGS, SIDEBAR_LABELS } from '../../configs'; +import { EXTENSIBILITY_CONFIGS } from '../../configs'; import { Utils } from '../../utilities/utils'; describe('Extensions - DocumentList presets', () => { @@ -63,31 +63,27 @@ describe('Extensions - DocumentList presets', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const personalFilesPage = new BrowsingPage(); - const { dataTable } = personalFilesPage; + const page = new BrowsingPage(); + const { dataTable } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); fileId = (await apis.user.nodes.createFile(file)).entry.id; await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.DOCUMENT_LIST_PRESETS); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.DOCUMENT_LIST_PRESETS); await loginPage.loginWith(username); done(); }); beforeEach(async done => { - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); - + await page.clickPersonalFilesAndWait(); done(); }); afterAll(async (done) => { await apis.user.nodes.deleteNodeById(fileId); - await logoutPage.load(); done(); }); diff --git a/e2e/suites/extensions/ext-header.test.ts b/e2e/suites/extensions/ext-header.test.ts index 0b21edfc94..df26a843fb 100755 --- a/e2e/suites/extensions/ext-header.test.ts +++ b/e2e/suites/extensions/ext-header.test.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage } from '../../pages/pages'; +import { LoginPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { EXTENSIBILITY_CONFIGS } from '../../configs'; import { Utils } from '../../utilities/utils'; @@ -56,12 +56,11 @@ describe('Extensions - Info Drawer', () => { const toolbarMenu = new Menu(); const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); beforeAll(async (done) => { await apis.admin.people.createUser({ username }); await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.HEADER); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.HEADER); await loginPage.loginWith(username); done(); }); @@ -71,11 +70,6 @@ describe('Extensions - Info Drawer', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('Add a new button in the header - [C286474]', async () => { await header.openMoreMenu(); expect(await toolbarMenu.isMenuItemPresent(enabledMenu.title)).toBe(true, 'menu item not present'); diff --git a/e2e/suites/extensions/ext-info-drawer.test.ts b/e2e/suites/extensions/ext-info-drawer.test.ts index f03134b895..b8c88a5176 100755 --- a/e2e/suites/extensions/ext-info-drawer.test.ts +++ b/e2e/suites/extensions/ext-info-drawer.test.ts @@ -23,10 +23,10 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { InfoDrawer } from './../../components/info-drawer/info-drawer'; import { RepoClient } from '../../utilities/repo-client/repo-client'; -import { SIDEBAR_LABELS, EXTENSIBILITY_CONFIGS } from '../../configs'; +import { EXTENSIBILITY_CONFIGS } from '../../configs'; import { Utils } from '../../utilities/utils'; describe('Extensions - Info Drawer', () => { @@ -65,7 +65,6 @@ describe('Extensions - Info Drawer', () => { const infoDrawer = new InfoDrawer(); const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); beforeAll(async (done) => { @@ -82,23 +81,17 @@ describe('Extensions - Info Drawer', () => { describe('', () => { beforeAll(async (done) => { await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.INFO_DRAWER); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.INFO_DRAWER); await loginPage.loginWith(username); done(); }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.clearSelection(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('Add a new tab with icon and title - [C284646]', async () => { await page.dataTable.selectItem(file); await page.toolbar.getButtonByTitleAttribute('View details').click(); @@ -106,7 +99,7 @@ describe('Extensions - Info Drawer', () => { const val = await infoDrawer.getTabTitle(custom_tab.order); expect(await infoDrawer.isTabPresent(custom_tab.title)).toBe(true, `${custom_tab.title} tab is not present`); - expect(val).toEqual(`${custom_tab.icon}\n${custom_tab.title}`); + expect(val.trim()).toEqual(`${custom_tab.icon}\n${custom_tab.title}`.trim()); }); it('Remove existing tab - [C284647]', async () => { @@ -132,7 +125,7 @@ describe('Extensions - Info Drawer', () => { await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabPresent(no_title_tab.title)).toBe(true, `${no_title_tab.title} tab is not present`); - expect(await infoDrawer.getTabTitle(no_title_tab.order)).toEqual(`${no_title_tab.icon}`); + expect((await infoDrawer.getTabTitle(no_title_tab.order)).trim()).toEqual(`${no_title_tab.icon}`.trim()); }); it('Insert new component in tab - [C284651]', async () => { @@ -149,15 +142,9 @@ describe('Extensions - Info Drawer', () => { describe('', () => { beforeAll(async (done) => { await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.INFO_DRAWER_EMPTY); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.INFO_DRAWER_EMPTY); await loginPage.loginWith(username); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); + await page.clickPersonalFilesAndWait(); done(); }); diff --git a/e2e/suites/extensions/ext-metadata.test.ts b/e2e/suites/extensions/ext-metadata.test.ts index a61dfe7275..7f73656d51 100644 --- a/e2e/suites/extensions/ext-metadata.test.ts +++ b/e2e/suites/extensions/ext-metadata.test.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages'; +import { BrowsingPage, LoginPage } from '../../pages/pages'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { EXTENSIBILITY_CONFIGS } from '../../configs'; import { Utils } from '../../utilities/utils'; @@ -66,7 +66,6 @@ describe('Extensions - Metadata presets', () => { const metadataCard = new MetadataCard(); const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); beforeAll(async done => { @@ -74,7 +73,7 @@ describe('Extensions - Metadata presets', () => { fileId = (await apis.user.nodes.createImage(file)).entry.id; await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.METADATA_PRESETS); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.METADATA_PRESETS); await loginPage.loginWith(username); done(); @@ -96,7 +95,6 @@ describe('Extensions - Metadata presets', () => { afterAll(async done => { await apis.user.nodes.deleteNodeById(fileId); - await logoutPage.load(); done(); }); diff --git a/e2e/suites/extensions/ext-viewer.test.ts b/e2e/suites/extensions/ext-viewer.test.ts index 0c9c4e483a..d1b66929f4 100755 --- a/e2e/suites/extensions/ext-viewer.test.ts +++ b/e2e/suites/extensions/ext-viewer.test.ts @@ -23,10 +23,10 @@ * along with Alfresco. If not, see . */ -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Viewer } from './../../components/components'; import { RepoClient } from '../../utilities/repo-client/repo-client'; -import { SIDEBAR_LABELS, EXTENSIBILITY_CONFIGS, FILES } from '../../configs'; +import { EXTENSIBILITY_CONFIGS, FILES } from '../../configs'; import { Utils } from '../../utilities/utils'; describe('Extensions - Viewer', () => { @@ -72,7 +72,6 @@ describe('Extensions - Viewer', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const viewer = new Viewer(); @@ -84,20 +83,18 @@ describe('Extensions - Viewer', () => { docxFileId = (await apis.user.upload.uploadFile(docxFile.file_name)).entry.id; await loginPage.load(); - await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.VIEWER); + await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.VIEWER); await loginPage.loginWith(username); done(); }); afterAll(async (done) => { await apis.user.nodes.deleteNodesById([ pdfFileId, docxFileId ]); - await logoutPage.load(); done(); }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); done(); }); diff --git a/e2e/suites/info-drawer/library-properties.test.ts b/e2e/suites/info-drawer/library-properties.test.ts new file mode 100755 index 0000000000..63a7de6f3d --- /dev/null +++ b/e2e/suites/info-drawer/library-properties.test.ts @@ -0,0 +1,262 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { InfoDrawer } from './../../components/info-drawer/info-drawer'; +import { Utils } from '../../utilities/utils'; + +describe('Library properties', () => { + const username = `user1-${Utils.random()}`; + const user2 = `user2-${Utils.random()}`; + const user3 = `user3-${Utils.random()}`; + + const site = { + name: `site1-${Utils.random()}`, + id: `site-id-${Utils.random()}`, + visibility: SITE_VISIBILITY.MODERATED, + description: 'my site description' + }; + + const siteForUpdate = { + name: `site2-${Utils.random()}`, + id: `site-id-${Utils.random()}`, + visibility: SITE_VISIBILITY.MODERATED, + description: 'my initial description' + }; + + const siteUpdated = { + name: `site-for-rename-${Utils.random()}`, + visibility: SITE_VISIBILITY.PRIVATE, + description: 'new description' + } + + const siteDup = `site3-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const infoDrawer = new InfoDrawer(); + + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + await apis.admin.people.createUser({ username: user2 }); + await apis.admin.people.createUser({ username: user3 }); + await apis.user.sites.createSite(site.name, site.visibility, site.description, site.id); + await apis.user.sites.createSite(siteForUpdate.name, siteForUpdate.visibility, siteForUpdate.description, siteForUpdate.id); + await apis.user.sites.createSite(siteDup); + + await apis.user.sites.addSiteMember(site.id, user2, SITE_ROLES.SITE_COLLABORATOR.ROLE); + await apis.user.sites.addSiteMember(site.id, user3, SITE_ROLES.SITE_MANAGER.ROLE); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await apis.user.sites.deleteSite(site.id); + await apis.user.sites.deleteSite(siteForUpdate.id); + await apis.user.sites.deleteSite(siteDup); + done(); + }); + + beforeEach(async (done) => { + await page.clickFileLibrariesAndWait(); + done(); + }); + + afterEach(async done => { + if (await infoDrawer.isOpen()) { + await page.toolbar.getButtonByTitleAttribute('View details').click(); + } + done(); + }); + + it('Info drawer opens for a library - [C289336]', async () => { + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + + expect(await infoDrawer.getHeaderTitle()).toEqual('Details'); + expect(await infoDrawer.isTabPresent('About')).toBe(true, 'About tab is not displayed'); + expect(await infoDrawer.isNameDisplayed()).toBe(true, 'Name field not displayed'); + expect(await infoDrawer.isLibraryIdDisplayed()).toBe(true, 'Library ID field not displayed'); + expect(await infoDrawer.isVisibilityDisplayed()).toBe(true, 'Visibility field not displayed'); + expect(await infoDrawer.isDescriptionDisplayed()).toBe(true, 'Description field not displayed'); + + expect(await infoDrawer.getName()).toEqual(site.name); + expect(await infoDrawer.getLibraryId()).toEqual(site.id); + expect((await infoDrawer.getVisibility()).toLowerCase()).toEqual((site.visibility).toLowerCase()); + expect(await infoDrawer.getDescription()).toEqual(site.description); + + expect(await infoDrawer.isButtonDisplayed('Edit')).toBe(true, 'Edit action is not displayed'); + }); + + it('Editable properties - [C289338]', async () => { + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + + expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled'); + await infoDrawer.clickButton('Edit'); + + expect(await infoDrawer.isNameEnabled()).toBe(true, 'Name field not enabled'); + expect(await infoDrawer.isLibraryIdEnabled()).toBe(false, 'Library ID field not disabled'); + expect(await infoDrawer.isVisibilityEnabled()).toBe(true, 'Visibility field not enabled'); + expect(await infoDrawer.isDescriptionEnabled()).toBe(true, 'Description field not enabled'); + + expect(await infoDrawer.isButtonDisplayed('Cancel')).toBe(true, 'Cancel button not displayed'); + expect(await infoDrawer.isButtonDisplayed('Update')).toBe(true, 'Update button not displayed'); + expect(await infoDrawer.isButtonEnabled('Cancel')).toBe(true, 'Cancel button not enabled'); + expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled'); + }); + + it('Edit site details - [C289339]', async () => { + await dataTable.selectItem(siteForUpdate.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + + expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled'); + await infoDrawer.clickButton('Edit'); + + await infoDrawer.enterName(siteUpdated.name); + await infoDrawer.enterDescription(siteUpdated.description); + await infoDrawer.setVisibility(siteUpdated.visibility); + expect(await infoDrawer.isButtonEnabled('Update')).toBe(true, 'Update button not enabled'); + + await infoDrawer.clickButton('Update'); + + expect(await page.getSnackBarMessage()).toEqual('Library properties updated'); + expect(await dataTable.getRowByName(siteUpdated.name).isPresent()).toBe(true, 'New site name not displayed in the list'); + expect(await infoDrawer.isOpen()).toBe(false, 'Info drawer still open'); + + expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.title).toEqual(siteUpdated.name); + expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.description).toEqual(siteUpdated.description); + expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.visibility).toEqual(siteUpdated.visibility); + }); + + it('Cancel editing a site - [C289340]', async () => { + const newName = `new-name-${Utils.random}`; + const newDesc = `new desc ${Utils.random}`; + + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + + expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled'); + await infoDrawer.clickButton('Edit'); + + await infoDrawer.enterName(newName); + await infoDrawer.enterDescription(newDesc); + await infoDrawer.setVisibility(SITE_VISIBILITY.MODERATED); + + await infoDrawer.clickButton('Cancel'); + + expect(await dataTable.getRowByName(newName).isPresent()).toBe(false, 'New site name is displayed in the list'); + expect(await dataTable.getRowByName(site.name).isPresent()).toBe(true, 'Original site name not displayed in the list'); + expect(await infoDrawer.isOpen()).toBe(true, 'Info drawer not open'); + }); + + it('Warning appears when editing the name of the library by entering an existing name - [C289341]', async () => { + await apis.user.queries.waitForApi(site.name, { expect: 1 }); + + await dataTable.selectItem(siteDup); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + await infoDrawer.clickButton('Edit'); + + await infoDrawer.enterName(site.name); + expect(await infoDrawer.isMessageDisplayed()).toBe(true, 'Message not displayed'); + expect(await infoDrawer.getMessage()).toEqual('Library name already in use'); + }); + + it('Site name too long - [C289342]', async () => { + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + await infoDrawer.clickButton('Edit'); + + await infoDrawer.enterName(Utils.string257); + await Utils.pressTab(); + expect(await infoDrawer.isErrorDisplayed()).toBe(true, 'Message not displayed'); + expect(await infoDrawer.getError()).toEqual('Use 256 characters or less for title'); + expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled'); + }); + + it('Site description too long - [C289343]', async () => { + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + await infoDrawer.clickButton('Edit'); + + await infoDrawer.enterDescription(Utils.string513); + await Utils.pressTab(); + expect(await infoDrawer.isErrorDisplayed()).toBe(true, 'Message not displayed'); + expect(await infoDrawer.getError()).toEqual('Use 512 characters or less for description'); + expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled'); + }); + + describe('Non manager', () => { + afterAll(async done => { + await loginPage.loginWith(username); + done(); + }); + + it('Edit button is not displayed when user is not the library manager - [C289337]', async () => { + await loginPage.loginWith(user2); + + await page.clickFileLibrariesAndWait(); + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + expect(await infoDrawer.isButtonDisplayed('Edit')).toBe(false, 'Edit action is displayed'); + }); + + it('Error notification - [C289344]', async () => { + await loginPage.loginWith(user3); + + await page.clickFileLibrariesAndWait(); + await dataTable.selectItem(site.name); + await page.toolbar.getButtonByTitleAttribute('View details').click(); + await infoDrawer.waitForInfoDrawerToOpen(); + await infoDrawer.clickButton('Edit'); + + await apis.user.sites.updateSiteMember(site.id, user3, SITE_ROLES.SITE_CONSUMER.ROLE); + + await infoDrawer.enterDescription('new description'); + await infoDrawer.clickButton('Update'); + + expect(await page.getSnackBarMessage()).toEqual('There was an error updating library properties'); + }); + }); + +}); diff --git a/e2e/suites/list-views/empty-list.test.ts b/e2e/suites/list-views/empty-list.test.ts index 5b87df664d..879f12a497 100755 --- a/e2e/suites/list-views/empty-list.test.ts +++ b/e2e/suites/list-views/empty-list.test.ts @@ -23,8 +23,7 @@ * along with Alfresco. If not, see . */ -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -38,9 +37,9 @@ describe('Empty list views', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { dataTable } = page; + const { dataTable, pagination } = page; + const { searchInput } = page.header; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); @@ -48,50 +47,127 @@ describe('Empty list views', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('empty Personal Files - [C280131]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await page.clickPersonalFiles(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyDragAndDropText()).toContain('Drag and drop'); }); - it('empty File Libraries - [C217099]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + it('empty My Libraries - [C217099]', async () => { + await page.goToMyLibraries(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyStateTitle()).toContain(`You aren't a member of any File Libraries yet`); expect(await dataTable.getEmptyStateSubtitle()).toContain('Join libraries to upload, view, and share files.'); }); + it('empty Favorite Libraries - [C289911]', async () => { + await page.goToFavoriteLibraries(); + expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(await dataTable.getEmptyStateTitle()).toContain(`No Favorite Libraries`); + expect(await dataTable.getEmptyStateSubtitle()).toContain('Favorite a library that you want to find easily later.'); + }); + it('empty Shared Files - [C280132]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await page.clickSharedFiles(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyStateTitle()).toContain('No shared files or folders'); expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you share using the Share option are shown here.'); }); it('empty Recent Files - [C213169]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) + await page.clickRecentFiles(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyStateTitle()).toContain('No recent files'); - expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you upload or edit in the last 30 days are shown here.'); + expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you uploaded or edited in the last 30 days are shown here.'); }); it('empty Favorites - [C280133]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await page.clickFavorites(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyStateTitle()).toContain('No favorite files or folders'); expect(await dataTable.getEmptyStateSubtitle()).toContain('Favorite items that you want to easily find later.'); }); it('empty Trash - [C280134]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty'); expect(await dataTable.getEmptyStateTitle()).toContain('Trash is empty'); expect(await dataTable.getEmptyStateText()).toContain('Items you delete are moved to the Trash.'); expect(await dataTable.getEmptyStateText()).toContain('Empty Trash to permanently delete items.'); }); + + it('Favorites - pagination controls not displayed - [C280111]', async () => { + await page.clickFavorites(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('File Libraries - pagination controls not displayed - [C280084]', async () => { + await page.clickFileLibraries(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('Personal Files - pagination controls not displayed - [C280075]', async () => { + await page.clickPersonalFiles(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('Recent Files - pagination controls not displayed - [C280102]', async () => { + await page.clickRecentFiles(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('Shared Files - pagination controls not displayed - [C280094]', async () => { + await page.clickSharedFiles(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('Trash - pagination controls not displayed - [C280120]', async () => { + await page.clickTrash(); + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); + + it('Search results - pagination controls not displayed - [C290123]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkOnlyFiles(); + /* cspell:disable-next-line */ + await searchInput.searchFor('qwertyuiop'); + await dataTable.waitForBody(); + + expect(await pagination.range.isPresent()).toBe(false); + expect(await pagination.maxItems.isPresent()).toBe(false); + expect(await pagination.currentPage.isPresent()).toBe(false); + expect(await pagination.totalPages.isPresent()).toBe(false); + expect(await pagination.previousButton.isPresent()).toBe(false); + expect(await pagination.nextButton.isPresent()).toBe(false); + }); }); diff --git a/e2e/suites/list-views/favorites.test.ts b/e2e/suites/list-views/favorites.test.ts index fcd331e0a2..d210203e32 100755 --- a/e2e/suites/list-views/favorites.test.ts +++ b/e2e/suites/list-views/favorites.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -45,16 +45,15 @@ describe('Favorites', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const favoritesPage = new BrowsingPage(); - const { dataTable, breadcrumb } = favoritesPage; + const page = new BrowsingPage(); + const { dataTable, breadcrumb } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); const docLibId = await apis.admin.sites.getDocLibId(siteName); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER); + await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER.ROLE); const file1Id = (await apis.admin.nodes.createFile(fileName1, docLibId)).entry.id; const folderId = (await apis.user.nodes.createFolder(favFolderName)).entry.id; @@ -77,16 +76,14 @@ describe('Favorites', () => { }); beforeEach(async (done) => { - await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); afterAll(async (done) => { await apis.admin.sites.deleteSite(siteName); await apis.user.nodes.deleteNodes([ favFolderName, parentFolder ]); - await apis.admin.trashcan.emptyTrash(); - await logoutPage.load(); + await apis.user.trashcan.emptyTrash(); done(); }); @@ -138,7 +135,7 @@ describe('Favorites', () => { it('Location column redirect - file in site - [C280485]', async () => { await dataTable.clickItemLocation(fileName1); - expect(breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]); + expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]); }); it('Navigate into folder from Favorites - [C213230]', async () => { diff --git a/e2e/suites/list-views/file-libraries.test.ts b/e2e/suites/list-views/file-libraries.test.ts index 09841128fa..5de73cc6e4 100755 --- a/e2e/suites/list-views/file-libraries.test.ts +++ b/e2e/suites/list-views/file-libraries.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -32,13 +32,21 @@ describe('File Libraries', () => { const username = `user-${Utils.random()}`; const password = username; - const sitePrivate = `private-${Utils.random()}`; - const siteModerated = `moderated-${Utils.random()}`; - const sitePublic = `public-${Utils.random()}`; + const userSitePrivate = `user-private-${Utils.random()}`; + const userSiteModerated = `user-moderated-${Utils.random()}`; + const userSitePublic = `user-public-${Utils.random()}`; + const siteName = `siteName-${Utils.random()}`; + const siteId1 = Utils.random(); const siteId2 = Utils.random(); - const adminSite = `admin-${Utils.random()}`; + + const adminSite1 = `admin1-${Utils.random()}`; + const adminSite2 = `admin2-${Utils.random()}`; + const adminSite3 = `admin3-${Utils.random()}`; + const adminSite4 = `admin4-${Utils.random()}`; + const adminSite5 = `admin5-${Utils.random()}`; + const adminSite6 = `admin6-${Utils.random()}`; const siteDescription = 'my site description'; @@ -48,94 +56,219 @@ describe('File Libraries', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const fileLibrariesPage = new BrowsingPage(); - const { dataTable } = fileLibrariesPage; + const page = new BrowsingPage(); + const { dataTable } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); - await apis.admin.sites.createSite(sitePublic, SITE_VISIBILITY.PUBLIC); - await apis.admin.sites.createSite(siteModerated, SITE_VISIBILITY.MODERATED, siteDescription); - await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE, null); - await apis.admin.sites.createSite(adminSite, SITE_VISIBILITY.PUBLIC); - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId1); - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId2); - await apis.admin.sites.addSiteMember(sitePublic, username, SITE_ROLES.SITE_CONSUMER); - await apis.admin.sites.addSiteMember(siteModerated, username, SITE_ROLES.SITE_MANAGER); - await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONTRIBUTOR); - await apis.admin.sites.addSiteMember(siteId1, username, SITE_ROLES.SITE_CONTRIBUTOR); - await apis.admin.sites.addSiteMember(siteId2, username, SITE_ROLES.SITE_CONTRIBUTOR); - await loginPage.loginWith(username); - done(); - }); + await apis.user.sites.createSite(userSitePublic, SITE_VISIBILITY.PUBLIC); + await apis.user.sites.createSite(userSiteModerated, SITE_VISIBILITY.MODERATED, siteDescription); + await apis.user.sites.createSite(userSitePrivate, SITE_VISIBILITY.PRIVATE, null); - beforeEach(async (done) => { - await fileLibrariesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await apis.admin.sites.createSite(adminSite1, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite2, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite3, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite4, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite5, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite6, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.addSiteMember(adminSite1, username, SITE_ROLES.SITE_CONSUMER.ROLE); + await apis.admin.sites.addSiteMember(adminSite2, username, SITE_ROLES.SITE_CONTRIBUTOR.ROLE); + await apis.admin.sites.addSiteMember(adminSite3, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); + await apis.admin.sites.addSiteMember(adminSite4, username, SITE_ROLES.SITE_MANAGER.ROLE); + await apis.admin.sites.addSiteMember(adminSite6, username, SITE_ROLES.SITE_CONSUMER.ROLE); + + await apis.user.favorites.addFavoriteById('site', adminSite1); + await apis.user.favorites.addFavoriteById('site', adminSite2); + await apis.user.favorites.addFavoriteById('site', adminSite3); + await apis.user.favorites.addFavoriteById('site', adminSite4); + + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId1); + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId2); + + await loginPage.loginWith(username); done(); }); afterAll(async (done) => { - await apis.admin.sites.deleteSites([ sitePublic, siteModerated, sitePrivate, adminSite, siteId1, siteId2 ]); - await logoutPage.load(); + await apis.user.sites.deleteSites([ userSitePublic, userSiteModerated, userSitePrivate, siteId1, siteId2 ]); + await apis.admin.sites.deleteSites([ adminSite1, adminSite2, adminSite3, adminSite4, adminSite5, adminSite6 ]); done(); }); - it('has the correct columns - [C217095]', async () => { - const labels = [ 'Title', 'Status' ]; - const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + xit(''); + + describe('My Libraries', () => { + beforeEach(async (done) => { + await page.goToMyLibraries(); + done(); + }); - expect(await dataTable.getColumnHeaders().count()).toBe(2 + 1, 'Incorrect number of columns'); + it('has the correct columns - [C217095]', async () => { + const labels = [ 'Name', 'My Role', 'Visibility' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); - await elements.forEach(async (element, index) => { - expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns'); + + await elements.forEach(async (element, index) => { + expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); }); - }); - it('User can see only the sites he is a member of - [C280501]', async () => { - const sitesCount = await dataTable.countRows(); + it('User can see only the sites he is a member of - [C280501]', async () => { + const sitesCount = await dataTable.countRows(); + + expect(sitesCount).toEqual(10, 'Incorrect number of sites displayed'); + expect(await dataTable.getRowByName(adminSite5).isPresent()).toBe(false, `${adminSite5} should not appear in the list`); + }); - const expectedSites = { - [sitePrivate]: SITE_VISIBILITY.PRIVATE, - [siteModerated]: SITE_VISIBILITY.MODERATED, - [sitePublic]: SITE_VISIBILITY.PUBLIC - }; + it('Library visibility is correctly displayed - [C289905]', async () => { + const expectedSitesVisibility = { + [userSitePrivate]: SITE_VISIBILITY.PRIVATE, + [userSiteModerated]: SITE_VISIBILITY.MODERATED, + [userSitePublic]: SITE_VISIBILITY.PUBLIC + }; - expect(sitesCount).toEqual(5, 'Incorrect number of sites displayed'); - expect(await dataTable.getRowByName(adminSite).isPresent()).toBe(false, 'Incorrect site appears in list'); + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[3].toUpperCase(); + return acc; + }, {}); - const rowCells = await dataTable.getRows().map((row) => { - return row.all(dataTable.cell).map(async cell => await cell.getText()); + Object.keys(expectedSitesVisibility).forEach((site) => { + expect(sitesList[site]).toEqual(expectedSitesVisibility[site]); + }); }); - const sitesList = rowCells.reduce((acc, cell) => { - acc[cell[1]] = cell[2].toUpperCase(); - return acc; - }, {}); - Object.keys(expectedSites).forEach((site) => { - expect(sitesList[site]).toEqual(expectedSites[site]); + it('User role is correctly displayed - [C289903]', async () => { + const expectedSitesRoles = { + [adminSite1]: SITE_ROLES.SITE_CONSUMER.LABEL, + [adminSite2]: SITE_ROLES.SITE_CONTRIBUTOR.LABEL, + [adminSite3]: SITE_ROLES.SITE_COLLABORATOR.LABEL, + [adminSite4]: SITE_ROLES.SITE_MANAGER.LABEL + }; + + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[2]; + return acc; + }, {}); + + Object.keys(expectedSitesRoles).forEach((site) => { + expect(sitesList[site]).toEqual(expectedSitesRoles[site]); + }); }); - }); - it('Site ID is displayed when two sites have the same name - [C217098]', async () => { - const expectedSites = [ - `${siteName} (${siteId1})`, - `${siteName} (${siteId2})` - ]; - const cells = await dataTable.getCellsContainingName(siteName); - const expectedJSON = JSON.stringify(expectedSites.sort()); - const actualJSON = JSON.stringify(cells.sort()); - expect(actualJSON).toEqual(expectedJSON); - }); + it('Site ID is displayed when two sites have the same name - [C217098]', async () => { + const expectedSites = [ + `${siteName} (${siteId1})`, + `${siteName} (${siteId2})` + ]; + const cells = await dataTable.getCellsContainingName(siteName); + const expectedJSON = JSON.stringify(expectedSites.sort()); + const actualJSON = JSON.stringify(cells.sort()); + expect(actualJSON).toEqual(expectedJSON); + }); + + it('Tooltip for sites without description - [C217096]', async () => { + const tooltip = await dataTable.getItemNameTooltip(userSitePrivate); + expect(tooltip).toBe(`${userSitePrivate}`); + }); - it('Tooltip for sites without description - [C217096]', async () => { - const tooltip = await dataTable.getItemNameTooltip(sitePrivate); - expect(tooltip).toBe(`${sitePrivate}`); + it('Tooltip for sites with description - [C217097]', async () => { + const tooltip = await dataTable.getItemNameTooltip(userSiteModerated); + expect(tooltip).toBe(`${siteDescription}`); + }); }); - it('Tooltip for sites with description - [C217097]', async () => { - const tooltip = await dataTable.getItemNameTooltip(siteModerated); - expect(tooltip).toBe(`${siteDescription}`); + describe('Favorite Libraries', () => { + beforeEach(async (done) => { + await page.goToFavoriteLibraries(); + done(); + }); + + it('has the correct columns - [C289893]', async () => { + const labels = [ 'Name', 'My Role', 'Visibility' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns'); + + await elements.forEach(async (element, index) => { + expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('User can see only his favorite sites - [C289897]', async () => { + const sitesCount = await dataTable.countRows(); + + expect(sitesCount).toEqual(9, 'Incorrect number of sites displayed'); + expect(await dataTable.getRowByName(adminSite6).isPresent()).toBe(false, `${adminSite6} should not appear`); + }); + + it('Library visibility is correctly displayed - [C289906]', async () => { + const expectedSitesVisibility = { + [userSitePrivate]: SITE_VISIBILITY.PRIVATE, + [userSiteModerated]: SITE_VISIBILITY.MODERATED, + [userSitePublic]: SITE_VISIBILITY.PUBLIC + }; + + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[3].toUpperCase(); + return acc; + }, {}); + + Object.keys(expectedSitesVisibility).forEach((site) => { + expect(sitesList[site]).toEqual(expectedSitesVisibility[site]); + }); + }); + + it('User role is correctly displayed - [C289904]', async () => { + const expectedSitesRoles = { + [adminSite1]: SITE_ROLES.SITE_CONSUMER.LABEL, + [adminSite2]: SITE_ROLES.SITE_CONTRIBUTOR.LABEL, + [adminSite3]: SITE_ROLES.SITE_COLLABORATOR.LABEL, + [adminSite4]: SITE_ROLES.SITE_MANAGER.LABEL + }; + + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[2]; + return acc; + }, {}); + + Object.keys(expectedSitesRoles).forEach((site) => { + expect(sitesList[site]).toEqual(expectedSitesRoles[site]); + }); + }); + + it('Site ID is displayed when two sites have the same name - [C289896]', async () => { + const expectedSites = [ + `${siteName} (${siteId1})`, + `${siteName} (${siteId2})` + ]; + const cells = await dataTable.getCellsContainingName(siteName); + const expectedJSON = JSON.stringify(expectedSites.sort()); + const actualJSON = JSON.stringify(cells.sort()); + expect(actualJSON).toEqual(expectedJSON); + }); + + it('Tooltip for sites without description - [C289894]', async () => { + const tooltip = await dataTable.getItemNameTooltip(userSitePrivate); + expect(tooltip).toBe(`${userSitePrivate}`); + }); + + it('Tooltip for sites with description - [C289895]', async () => { + const tooltip = await dataTable.getItemNameTooltip(userSiteModerated); + expect(tooltip).toBe(`${siteDescription}`); + }); }); }); diff --git a/e2e/suites/list-views/generic-errors.test.ts b/e2e/suites/list-views/generic-errors.test.ts index bb6e397913..7f5690c17e 100755 --- a/e2e/suites/list-views/generic-errors.test.ts +++ b/e2e/suites/list-views/generic-errors.test.ts @@ -24,8 +24,7 @@ */ import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -44,7 +43,6 @@ describe('Generic errors', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable } = page; @@ -62,13 +60,11 @@ describe('Generic errors', () => { afterAll(async (done) => { await apis.user.nodes.deleteNodeById(parentId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); it('File / folder not found - [C217313]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.doubleClickOnRowByName(file1); const URL = await browser.getCurrentUrl(); @@ -76,31 +72,28 @@ describe('Generic errors', () => { await browser.get(URL); expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`); }); it('Invalid URL - [C217315]', async () => { await page.load('/invalid page'); expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`); }); it('Permission denied - [C217314]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.doubleClickOnRowByName(file2); const URL = await browser.getCurrentUrl(); - await logoutPage.load(); await loginPage.loginWith(username2); await browser.get(URL); expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`); - await logoutPage.load(); await loginPage.loginWith(username); }); }); diff --git a/e2e/suites/list-views/permissions.test.ts b/e2e/suites/list-views/permissions.test.ts index 88379efb45..a4e1fb82e9 100755 --- a/e2e/suites/list-views/permissions.test.ts +++ b/e2e/suites/list-views/permissions.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -38,11 +38,8 @@ describe('Special permissions', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const recentFilesPage = new BrowsingPage(); - const favoritesPage = new BrowsingPage(); - const sharedPage = new BrowsingPage(); - const { dataTable } = recentFilesPage; + const page = new BrowsingPage(); + const { dataTable } = page; xit(''); @@ -58,7 +55,7 @@ describe('Special permissions', () => { beforeAll(async (done) => { await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); - await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); const docLibId = await apis.admin.sites.getDocLibId(sitePrivate); fileId = (await apis.admin.nodes.createFile(fileName, docLibId)).entry.id; await apis.user.favorites.addFavoriteById('file', fileId); @@ -73,42 +70,36 @@ describe('Special permissions', () => { }); afterEach(async (done) => { - await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); done(); }); afterAll(async (done) => { - await Promise.all([ - apis.admin.sites.deleteSite(sitePrivate), - logoutPage.load() - ]); + await apis.admin.sites.deleteSite(sitePrivate); done(); }); it('on Recent Files - [C213173]', async () => { - await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); await apis.admin.sites.deleteSiteMember(sitePrivate, username); - await recentFilesPage.refresh(); + await page.refresh(); expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items'); }); it('on Favorites - [C213227]', async () => { - await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); await apis.admin.sites.deleteSiteMember(sitePrivate, username); - await favoritesPage.refresh(); + await page.refresh(); expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items'); }); it('on Shared Files - [C213116]', async () => { - await sharedPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); await apis.admin.sites.deleteSiteMember(sitePrivate, username); - await sharedPage.refresh(); + await page.refresh(); expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items'); }); }); @@ -120,7 +111,7 @@ describe('Special permissions', () => { beforeAll(async (done) => { await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); - await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR); + await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); const docLibId = await apis.admin.sites.getDocLibId(sitePrivate); fileId = (await apis.user.nodes.createFile(fileName, docLibId)).entry.id; await apis.user.favorites.addFavoriteById('file', fileId); @@ -133,32 +124,26 @@ describe('Special permissions', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.admin.sites.deleteSite(sitePrivate), - logoutPage.load() - ]); + await apis.admin.sites.deleteSite(sitePrivate); done(); }); it(`on Recent Files - [C213178]`, async () => { - await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); - expect(await dataTable.getItemLocation(fileName)).toEqual(''); + expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); }); it(`on Favorites - [C213672]`, async () => { - await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); - expect(await dataTable.getItemLocation(fileName)).toEqual(''); + expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); }); it(`on Shared Files - [C213668]`, async () => { - await sharedPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items'); - expect(await dataTable.getItemLocation(fileName)).toEqual(''); + expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); }); }); }); diff --git a/e2e/suites/list-views/personal-files.test.ts b/e2e/suites/list-views/personal-files.test.ts index 5534665087..7bb0752115 100755 --- a/e2e/suites/list-views/personal-files.test.ts +++ b/e2e/suites/list-views/personal-files.test.ts @@ -25,8 +25,8 @@ import { browser } from 'protractor'; -import { SIDEBAR_LABELS, APP_ROUTES } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { APP_ROUTES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -39,9 +39,8 @@ describe('Personal Files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const personalFilesPage = new BrowsingPage(); - const { dataTable } = personalFilesPage; + const page = new BrowsingPage(); + const { dataTable } = page; const adminFolder = `admin-folder-${Utils.random()}`; @@ -75,19 +74,13 @@ describe('Personal Files', () => { }); beforeEach(async (done) => { - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); + await page.clickPersonalFilesAndWait(); done(); }); it('has Data Dictionary and created content - [C213241]', async () => { - expect(await dataTable.getRowByName('Data Dictionary').isPresent()).toBe(true); - expect(await dataTable.getRowByName(adminFolder).isPresent()).toBe(true); + expect(await dataTable.getRowByName('Data Dictionary').isPresent()).toBe(true, 'Data Dictionary not displayed'); + expect(await dataTable.getRowByName(adminFolder).isPresent()).toBe(true, 'admin folder not displayed'); }); }); @@ -98,13 +91,7 @@ describe('Personal Files', () => { }); beforeEach(async (done) => { - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); + await page.clickPersonalFilesAndWait(); done(); }); @@ -124,7 +111,7 @@ describe('Personal Files', () => { }); it('has user created content - [C213242]', async () => { - expect(await dataTable.getRowByName(userFolder).isPresent()).toBe(true); + expect(await dataTable.getRowByName(userFolder).isPresent()).toBe(true, 'user folder not displayed'); }); it('navigates to folder - [C213244]', async () => { @@ -138,20 +125,20 @@ describe('Personal Files', () => { }); it('redirects to Personal Files on clicking the link from sidebar - [C213245]', async () => { - await personalFilesPage.dataTable.doubleClickOnRowByName(userFolder); - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await page.dataTable.doubleClickOnRowByName(userFolder); + await page.clickPersonalFiles(); const url = await browser.getCurrentUrl(); expect(url.endsWith(APP_ROUTES.PERSONAL_FILES)).toBe(true, 'incorrect url'); }); it('page loads correctly after browser refresh - [C213246]', async () => { - await personalFilesPage.refresh(); + await page.refresh(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); it('page load by URL - [C213247]', async () => { const url = await browser.getCurrentUrl(); - await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); await browser.get(url); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); diff --git a/e2e/suites/list-views/recent-files.test.ts b/e2e/suites/list-views/recent-files.test.ts index e396ae8367..44de6ae22a 100755 --- a/e2e/suites/list-views/recent-files.test.ts +++ b/e2e/suites/list-views/recent-files.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -46,9 +46,8 @@ describe('Recent Files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const recentFilesPage = new BrowsingPage(); - const { dataTable, breadcrumb } = recentFilesPage; + const page = new BrowsingPage(); + const { dataTable, breadcrumb } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); @@ -70,16 +69,14 @@ describe('Recent Files', () => { }); beforeEach(async (done) => { - await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); afterAll(async (done) => { await apis.user.nodes.deleteNodesById([ folderId, file2Id ]); await apis.user.sites.deleteSite(siteName); - await apis.admin.trashcan.emptyTrash(); - await logoutPage.load(); + await apis.user.trashcan.emptyTrash(); done(); }); @@ -134,6 +131,6 @@ describe('Recent Files', () => { it('Location column redirect - file in site - [C280487]', async () => { await dataTable.clickItemLocation(fileSite); - expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName, folderSite ]); + expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName, folderSite ]); }); }); diff --git a/e2e/suites/list-views/shared-files.test.ts b/e2e/suites/list-views/shared-files.test.ts index 34a7c4f22f..94896fe2bf 100755 --- a/e2e/suites/list-views/shared-files.test.ts +++ b/e2e/suites/list-views/shared-files.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -47,14 +47,13 @@ describe('Shared Files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const sharedFilesPage = new BrowsingPage(); - const { dataTable, breadcrumb } = sharedFilesPage; + const page = new BrowsingPage(); + const { dataTable, breadcrumb } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER); + await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER.ROLE); const docLibId = await apis.admin.sites.getDocLibId(siteName); const nodeId = (await apis.admin.nodes.createFile(fileAdmin, docLibId)).entry.id; await apis.admin.shared.shareFileById(nodeId); @@ -76,8 +75,7 @@ describe('Shared Files', () => { }); beforeEach(async (done) => { - await sharedFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -85,7 +83,6 @@ describe('Shared Files', () => { await apis.admin.sites.deleteSite(siteName); await apis.user.nodes.deleteNodeById(folderId); await apis.user.nodes.deleteNodeById(file4Id); - await logoutPage.load(); done(); }); @@ -134,7 +131,7 @@ describe('Shared Files', () => { it('Location column redirect - file in site - [C280491]', async () => { await dataTable.clickItemLocation(fileAdmin); - expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]); + expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]); }); it('Location column displays a tooltip with the entire path of the file - [C213667]', async () => { diff --git a/e2e/suites/list-views/tooltips.test.ts b/e2e/suites/list-views/tooltips.test.ts index 058d095f5e..dfb60346be 100755 --- a/e2e/suites/list-views/tooltips.test.ts +++ b/e2e/suites/list-views/tooltips.test.ts @@ -23,8 +23,7 @@ * along with Alfresco. If not, see . */ -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -52,7 +51,6 @@ describe('File / folder tooltips', () => { const fileDescription = 'file description'; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable } = page; @@ -82,8 +80,7 @@ describe('File / folder tooltips', () => { afterAll(async (done) => { await Promise.all([ apis.user.nodes.deleteNodes([ parent ]), - apis.user.trashcan.emptyTrash(), - logoutPage.load() + apis.user.trashcan.emptyTrash() ]); done(); }); @@ -92,7 +89,7 @@ describe('File / folder tooltips', () => { describe('on Personal Files', () => { beforeAll(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); done(); }); @@ -133,7 +130,7 @@ describe('File / folder tooltips', () => { describe('on Recent Files', () => { beforeAll(async (done) => { await apis.user.search.waitForApi(username, { expect: 8 }); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await page.clickRecentFilesAndWait(); done(); }); @@ -174,7 +171,7 @@ describe('File / folder tooltips', () => { xdescribe('on Shared Files', () => { beforeAll(async (done) => { await apis.user.shared.waitForApi({ expect: 8 }); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await page.clickSharedFilesAndWait(); done(); }); @@ -213,8 +210,7 @@ describe('File / folder tooltips', () => { describe('on Favorites', () => { beforeAll(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -271,8 +267,7 @@ describe('File / folder tooltips', () => { file1TrashId, file2TrashId, file3TrashId, file4TrashId, file5TrashId, file6TrashId, file7TrashId, file8TrashId ], false); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); diff --git a/e2e/suites/list-views/trash.test.ts b/e2e/suites/list-views/trash.test.ts index 9ae5f93e0f..8ad7a4437c 100755 --- a/e2e/suites/list-views/trash.test.ts +++ b/e2e/suites/list-views/trash.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -52,16 +52,15 @@ describe('Trash', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const trashPage = new BrowsingPage(); - const { dataTable, breadcrumb } = trashPage; + const page = new BrowsingPage(); + const { dataTable, breadcrumb } = page; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); fileAdminId = (await apis.admin.nodes.createFiles([ fileAdmin ])).entry.id; folderAdminId = (await apis.admin.nodes.createFolders([ folderAdmin ])).entry.id; await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER); + await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER.ROLE); const docLibId = await apis.admin.sites.getDocLibId(siteName); fileSiteId = (await apis.admin.nodes.createFile(fileSite, docLibId)).entry.id; fileUserId = (await apis.user.nodes.createFiles([ fileUser ])).entry.id; @@ -97,13 +96,7 @@ describe('Trash', () => { }); beforeEach(async (done) => { - await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); + await page.clickTrashAndWait(); done(); }); @@ -136,13 +129,7 @@ describe('Trash', () => { }); beforeEach(async (done) => { - await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); + await page.clickTrashAndWait(); done(); }); @@ -199,7 +186,7 @@ describe('Trash', () => { it('Location column redirect - file in site - [C280497]', async () => { await dataTable.clickItemLocation(fileSite); - expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]); + expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]); }); }); }); diff --git a/e2e/suites/navigation/breadcrumb.test.ts b/e2e/suites/navigation/breadcrumb.test.ts index 769b41e62b..5e5abf4ac2 100755 --- a/e2e/suites/navigation/breadcrumb.test.ts +++ b/e2e/suites/navigation/breadcrumb.test.ts @@ -25,8 +25,8 @@ import { browser } from 'protractor'; -import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -45,7 +45,6 @@ describe('Breadcrumb', () => { const folder1Renamed = `renamed-${Utils.random()}`; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { breadcrumb } = page; @@ -79,51 +78,55 @@ describe('Breadcrumb', () => { await Promise.all([ apis.user.nodes.deleteNodeById(parentId), apis.user.nodes.deleteNodeById(parent2Id), - apis.user.sites.deleteSite(siteName), - logoutPage.load() + apis.user.sites.deleteSite(siteName) ]); done(); }); it('Personal Files breadcrumb main node - [C260964]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await page.clickPersonalFiles(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); expect(await breadcrumb.getCurrentItemName()).toBe('Personal Files'); }); - it('File Libraries breadcrumb main node - [C260966]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + it('My Libraries breadcrumb main node - [C260966]', async () => { + await page.goToMyLibraries(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); - expect(await breadcrumb.getCurrentItemName()).toBe('File Libraries'); + expect(await breadcrumb.getCurrentItemName()).toBe('My Libraries'); + }); + + it('Favorite Libraries breadcrumb main node - [C289891]', async () => { + await page.goToFavoriteLibraries(); + expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); + expect(await breadcrumb.getCurrentItemName()).toBe('Favorite Libraries'); }); it('Recent Files breadcrumb main node - [C260971]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await page.clickRecentFiles(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); expect(await breadcrumb.getCurrentItemName()).toBe('Recent Files'); }); it('Shared Files breadcrumb main node - [C260972]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await page.clickSharedFiles(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); expect(await breadcrumb.getCurrentItemName()).toBe('Shared Files'); }); it('Favorites breadcrumb main node - [C260973]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await page.clickFavorites(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); expect(await breadcrumb.getCurrentItemName()).toBe('Favorites'); }); it('Trash breadcrumb main node - [C260974]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items'); expect(await breadcrumb.getCurrentItemName()).toBe('Trash'); }); it('Personal Files breadcrumb for a folder hierarchy - [C260965]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(parent); await page.dataTable.doubleClickOnRowByName(subFolder1); await page.dataTable.doubleClickOnRowByName(subFolder2); @@ -132,19 +135,17 @@ describe('Breadcrumb', () => { }); it('File Libraries breadcrumb for a folder hierarchy - [C260967]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await page.dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await page.dataTable.doubleClickOnRowByName(siteName); await page.dataTable.doubleClickOnRowByName(parent); await page.dataTable.doubleClickOnRowByName(subFolder1); await page.dataTable.doubleClickOnRowByName(subFolder2); - const expectedItems = [ 'File Libraries', siteName, parent, subFolder1, subFolder2 ]; + const expectedItems = [ 'My Libraries', siteName, parent, subFolder1, subFolder2 ]; expect(await breadcrumb.getAllItems()).toEqual(expectedItems); }); it('User can navigate to any location by clicking on a step from the breadcrumb - [C213235]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(parent); await page.dataTable.doubleClickOnRowByName(subFolder1); await page.dataTable.doubleClickOnRowByName(subFolder2); @@ -154,8 +155,7 @@ describe('Breadcrumb', () => { }); it('Tooltip appears on hover on a step in breadcrumb - [C213237]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(parent); await page.dataTable.doubleClickOnRowByName(subFolder1); await page.dataTable.doubleClickOnRowByName(subFolder2); @@ -163,8 +163,7 @@ describe('Breadcrumb', () => { }); it('Breadcrumb updates correctly when folder is renamed - [C213238]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(parent2); await page.dataTable.doubleClickOnRowByName(folder1); await page.dataTable.wait(); @@ -175,12 +174,11 @@ describe('Breadcrumb', () => { }); it('Browser back navigates to previous location regardless of breadcrumb steps - [C213240]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await page.dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await page.dataTable.doubleClickOnRowByName(parent); await page.dataTable.doubleClickOnRowByName(subFolder1); await page.dataTable.doubleClickOnRowByName(subFolder2); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); await page.dataTable.waitForEmptyState(); await browser.navigate().back(); const expectedBreadcrumb = [ 'Personal Files', parent, subFolder1, subFolder2 ]; @@ -194,7 +192,6 @@ describe('Breadcrumb', () => { const user2Api = new RepoClient(user2, user2); beforeAll(async (done) => { - await logoutPage.load(); await apis.admin.people.createUser({ username: user2 }); userFolderId = (await user2Api.nodes.createFolder(userFolder)).entry.id; await loginPage.loginWithAdmin(); @@ -202,10 +199,7 @@ describe('Breadcrumb', () => { }); afterAll(async (done) => { - await Promise.all([ - user2Api.nodes.deleteNodeById(userFolderId), - logoutPage.load() - ]); + await user2Api.nodes.deleteNodeById(userFolderId); done(); }); diff --git a/e2e/suites/navigation/sidebar.test.ts b/e2e/suites/navigation/sidebar.test.ts index f943cb2833..0c9700206c 100755 --- a/e2e/suites/navigation/sidebar.test.ts +++ b/e2e/suites/navigation/sidebar.test.ts @@ -26,11 +26,10 @@ import { browser } from 'protractor'; import { APP_ROUTES, SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; describe('Sidebar', () => { const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { sidenav } = page; @@ -39,85 +38,114 @@ describe('Sidebar', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('has "Personal Files" as default - [C217149]', async () => { expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); - expect(await sidenav.isActiveByLabel('Personal Files')).toBe(true, 'Active link'); + expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Active link'); + }); + + it('File Libraries has correct sub-categories - [C217150]', async () => { + await page.clickFileLibraries(); + expect(await sidenav.isFileLibrariesMenuExpanded()).toBe(true, 'File Libraries not expanded'); + expect(await sidenav.getLink(SIDEBAR_LABELS.MY_LIBRARIES).isPresent()).toBe(true, 'My Libraries link not present'); + expect(await sidenav.getLink(SIDEBAR_LABELS.FAVORITE_LIBRARIES).isPresent()).toBe(true, 'Favorite Libraries link not present'); + }); + + it('My Libraries is automatically selected on expanding File Libraries - [C289900]', async () => { + await page.clickFileLibraries(); + expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES); + expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active'); + expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active'); }); - it('navigates to "File Libraries" - [C217150]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FILE_LIBRARIES); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true); + it('navigate to Favorite Libraries - [C289902]', async () => { + await page.goToFavoriteLibraries(); + expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITE_LIBRARIES); + expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active'); + expect(await sidenav.childIsActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toBe(true, 'Favorite Libraries link not active'); + }); + + it('navigate to My Libraries - [C289901]', async () => { + await page.goToMyLibraries(); + expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES); + expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active'); + expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active'); }); it('navigates to "Personal Files" - [C280409]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await page.clickPersonalFiles(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true); + expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Personal Files link not active'); }); it('navigates to "Shared Files" - [C213110]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await page.clickSharedFiles(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.SHARED_FILES); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.SHARED_FILES)).toBe(true); + expect(await sidenav.isActive(SIDEBAR_LABELS.SHARED_FILES)).toBe(true, 'Shared Files link not active'); }); it('navigates to "Recent Files" - [C213166]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await page.clickRecentFiles(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.RECENT_FILES); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.RECENT_FILES)).toBe(true); + expect(await sidenav.isActive(SIDEBAR_LABELS.RECENT_FILES)).toBe(true, 'Recent Files link not active'); }); it('navigates to "Favorites" - [C213225]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await page.clickFavorites(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITES); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.FAVORITES)).toBe(true); + expect(await sidenav.isActive(SIDEBAR_LABELS.FAVORITES)).toBe(true, 'Favorites link not active'); }); it('navigates to "Trash" - [C213216]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await page.clickTrash(); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.TRASHCAN); - expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.TRASH)).toBe(true); + expect(await sidenav.isActive(SIDEBAR_LABELS.TRASH)).toBe(true, 'Trash link not active'); }); // TODO: incomplete test - xit('Personal Files tooltip - [C217151]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + it('Personal Files tooltip - [C217151]', async () => { + await page.clickPersonalFiles(); expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.PERSONAL_FILES)).toContain('View your Personal Files'); }); // TODO: incomplete test - xit('File Libraries tooltip - [C217152]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FILE_LIBRARIES)).toContain('Access File Libraries'); + it('File Libraries tooltip - [C217152]', async () => { + await page.clickFileLibraries(); + expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FILE_LIBRARIES)).toContain('File Libraries'); + }); + + // TODO: incomplete test + it('My Libraries tooltip - [C289916]', async () => { + await page.goToMyLibraries(); + expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.MY_LIBRARIES)).toContain('Access my libraries'); + }); + + // TODO: incomplete test + it('Favorite Libraries tooltip - [C289917]', async () => { + await page.goToFavoriteLibraries(); + expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toContain('Access my favorite libraries'); }); // TODO: incomplete test - xit('Shared Files tooltip - [C213111]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + it('Shared Files tooltip - [C213111]', async () => { + await page.clickSharedFiles(); expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.SHARED_FILES)).toContain('View files that have been shared'); }); // TODO: incomplete test - xit('Recent Files tooltip - [C213167]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + it('Recent Files tooltip - [C213167]', async () => { + await page.clickRecentFiles(); expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.RECENT_FILES)).toContain('View files you recently edited'); }); // TODO: incomplete test - xit('Favorites tooltip - [C217153]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + it('Favorites tooltip - [C217153]', async () => { + await page.clickFavorites(); expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITES)).toContain('View your favorite files and folders'); }); // TODO: incomplete test - xit('Trash tooltip - [C217154]', async () => { - await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + it('Trash tooltip - [C217154]', async () => { + await page.clickTrash(); expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.TRASH)).toContain('View deleted files in the trash'); }); }); diff --git a/e2e/suites/pagination/pag-empty-page.test.ts b/e2e/suites/pagination/pag-empty-page.test.ts deleted file mode 100755 index 59908fa597..0000000000 --- a/e2e/suites/pagination/pag-empty-page.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2018 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { Utils } from '../../utilities/utils'; -import { RepoClient } from '../../utilities/repo-client/repo-client'; - -describe('Pagination on empty page', () => { - - const username = `user-${Utils.random()}`; - - const adminApi = new RepoClient(); - - const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); - const page = new BrowsingPage(); - const { pagination } = page; - - beforeAll(async (done) => { - await adminApi.people.createUser({ username }); - await loginPage.loginWith(username); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - - it('Favorites - pagination controls not displayed - [C280111]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - expect(await pagination.range.isPresent()).toBe(false); - expect(await pagination.maxItems.isPresent()).toBe(false); - expect(await pagination.currentPage.isPresent()).toBe(false); - expect(await pagination.totalPages.isPresent()).toBe(false); - expect(await pagination.previousButton.isPresent()).toBe(false); - expect(await pagination.nextButton.isPresent()).toBe(false); - }); - - it('File Libraries - pagination controls not displayed - [C280084]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - expect(await pagination.range.isPresent()).toBe(false); - expect(await pagination.maxItems.isPresent()).toBe(false); - expect(await pagination.currentPage.isPresent()).toBe(false); - expect(await pagination.totalPages.isPresent()).toBe(false); - expect(await pagination.previousButton.isPresent()).toBe(false); - expect(await pagination.nextButton.isPresent()).toBe(false); - }); - - it('Personal Files - pagination controls not displayed - [C280075]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - expect(await pagination.range.isPresent()).toBe(false); - expect(await pagination.maxItems.isPresent()).toBe(false); - expect(await pagination.currentPage.isPresent()).toBe(false); - expect(await pagination.totalPages.isPresent()).toBe(false); - expect(await pagination.previousButton.isPresent()).toBe(false); - expect(await pagination.nextButton.isPresent()).toBe(false); - }); - - it('Recent Files - pagination controls not displayed - [C280102]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - expect(await pagination.range.isPresent()).toBe(false); - expect(await pagination.maxItems.isPresent()).toBe(false); - expect(await pagination.currentPage.isPresent()).toBe(false); - expect(await pagination.totalPages.isPresent()).toBe(false); - expect(await pagination.previousButton.isPresent()).toBe(false); - expect(await pagination.nextButton.isPresent()).toBe(false); - }); - - it('Shared Files - pagination controls not displayed - [C280094]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - expect(await pagination.range.isPresent()).toBe(false); - expect(await pagination.maxItems.isPresent()).toBe(false); - expect(await pagination.currentPage.isPresent()).toBe(false); - expect(await pagination.totalPages.isPresent()).toBe(false); - expect(await pagination.previousButton.isPresent()).toBe(false); - expect(await pagination.nextButton.isPresent()).toBe(false); - }); - - it('Trash - pagination controls not displayed - [C280120]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - expect(await pagination.range.isPresent()).toBe(false); - expect(pagination.maxItems.isPresent()).toBe(false); - expect(pagination.currentPage.isPresent()).toBe(false); - expect(pagination.totalPages.isPresent()).toBe(false); - expect(pagination.previousButton.isPresent()).toBe(false); - expect(pagination.nextButton.isPresent()).toBe(false); - }); -}); diff --git a/e2e/suites/pagination/pag-favorites.test.ts b/e2e/suites/pagination/pag-favorites.test.ts index 8d4e955f5e..5eb438bfef 100755 --- a/e2e/suites/pagination/pag-favorites.test.ts +++ b/e2e/suites/pagination/pag-favorites.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -45,7 +43,6 @@ describe('Pagination on multiple pages on Favorites', () => { let filesIds; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -60,8 +57,7 @@ describe('Pagination on multiple pages on Favorites', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -72,7 +68,6 @@ describe('Pagination on multiple pages on Favorites', () => { afterAll(async (done) => { await apis.user.nodes.deleteNodeById(parentId); - await logoutPage.load(); await apis.user.favorites.waitForApi({ expect: 0 }); done(); }); diff --git a/e2e/suites/pagination/pag-file-libraries.test.ts b/e2e/suites/pagination/pag-file-libraries.test.ts index 1405237c2a..0146330140 100755 --- a/e2e/suites/pagination/pag-file-libraries.test.ts +++ b/e2e/suites/pagination/pag-file-libraries.test.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -37,7 +36,6 @@ describe('Pagination on multiple pages on File Libraries', () => { user: new RepoClient(username, username) }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -54,21 +52,17 @@ describe('Pagination on multiple pages on File Libraries', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - done(); + await page.clickFileLibrariesAndWait(); + done(); }); afterEach(async (done) => { - await Utils.pressEscape(); - done(); + await Utils.pressEscape(); + done(); }); afterAll(async (done) => { - await Promise.all([ - apis.user.sites.deleteSites(sites), - logoutPage.load() - ]); + await apis.user.sites.deleteSites(sites); done(); }) diff --git a/e2e/suites/pagination/pag-personal-files.test.ts b/e2e/suites/pagination/pag-personal-files.test.ts index ce816b48ef..488783b174 100755 --- a/e2e/suites/pagination/pag-personal-files.test.ts +++ b/e2e/suites/pagination/pag-personal-files.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -43,7 +41,6 @@ describe('Pagination on multiple pages on Personal Files', () => { .map((name, index): string => `${name}-${index + 1}.txt`); const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -56,8 +53,7 @@ describe('Pagination on multiple pages on Personal Files', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); done(); }); @@ -68,10 +64,7 @@ describe('Pagination on multiple pages on Personal Files', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.user.nodes.deleteNodeById(parentId), - logoutPage.load() - ]); + await apis.user.nodes.deleteNodeById(parentId); done(); }); diff --git a/e2e/suites/pagination/pag-recent-files.test.ts b/e2e/suites/pagination/pag-recent-files.test.ts index 28a3c6fc8f..cd09fad939 100755 --- a/e2e/suites/pagination/pag-recent-files.test.ts +++ b/e2e/suites/pagination/pag-recent-files.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -43,7 +41,6 @@ describe('Pagination on multiple pages on Recent Files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -57,8 +54,7 @@ describe('Pagination on multiple pages on Recent Files', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -68,10 +64,7 @@ describe('Pagination on multiple pages on Recent Files', () => { }); afterAll(async (done) => { - await Promise.all([ - apis.user.nodes.deleteNodeById(parentId), - logoutPage.load() - ]); + await apis.user.nodes.deleteNodeById(parentId); done(); }); diff --git a/e2e/suites/pagination/pag-search-results.test.ts b/e2e/suites/pagination/pag-search-results.test.ts new file mode 100755 index 0000000000..b5e64e92ae --- /dev/null +++ b/e2e/suites/pagination/pag-search-results.test.ts @@ -0,0 +1,160 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; + +describe('Pagination on multiple pages on Search Results', () => { + const username = `user-${Utils.random()}`; + + const parent = `parent-${Utils.random()}`; let parentId; + const files = Array(101) + .fill('myFile') + .map((name, index): string => `${name}-${index + 1}.txt`); + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const { dataTable, pagination } = page; + const { searchInput } = page.header; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + parentId = (await apis.user.nodes.createFolder(parent)).entry.id; + await apis.user.nodes.createFiles(files, parent); + await apis.user.search.waitForApi(username, { expect: 101 }); + await loginPage.loginWith(username); + + await searchInput.clickSearchButton(); + await searchInput.checkOnlyFiles(); + await searchInput.searchFor('myFile-'); + await dataTable.waitForBody(); + + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.nodes.deleteNodeById(parentId); + done(); + }); + + it('Pagination control default values - [C290125]', async () => { + expect(await pagination.range.getText()).toContain('1-25 of 101'); + expect(await pagination.maxItems.getText()).toContain('25'); + expect(await pagination.currentPage.getText()).toContain('Page 1'); + expect(await pagination.totalPages.getText()).toContain('of 5'); + expect(await pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(await pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + }); + + it('Items per page values - [C290126]', async () => { + await pagination.openMaxItemsMenu(); + const [ first, second, third ] = [1, 2, 3] + .map(async nth => await pagination.menu.getNthItem(nth).getText()); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); + await pagination.menu.closeMenu(); + }); + + it('current page menu items - [C290127]', async () => { + await pagination.openMaxItemsMenu(); + await pagination.menu.clickMenuItem('25'); + expect(await pagination.getText(pagination.maxItems)).toContain('25'); + expect(await pagination.getText(pagination.totalPages)).toContain('of 5'); + await pagination.openCurrentPageMenu(); + expect(await pagination.menu.getItemsCount()).toBe(5); + await pagination.menu.closeMenu(); + + await pagination.openMaxItemsMenu(); + await pagination.menu.clickMenuItem('50'); + expect(await pagination.getText(pagination.maxItems)).toContain('50'); + expect(await pagination.getText(pagination.totalPages)).toContain('of 3'); + await pagination.openCurrentPageMenu(); + expect(await pagination.menu.getItemsCount()).toBe(3); + await pagination.menu.closeMenu(); + + await pagination.openMaxItemsMenu(); + await pagination.menu.clickMenuItem('100'); + expect(await pagination.getText(pagination.maxItems)).toContain('100'); + expect(await pagination.getText(pagination.totalPages)).toContain('of 2'); + await pagination.openCurrentPageMenu(); + expect(await pagination.menu.getItemsCount()).toBe(2); + await pagination.menu.closeMenu(); + + await pagination.resetToDefaultPageSize(); + }); + + it('change the current page from menu - [C290128]', async () => { + await pagination.openCurrentPageMenu(); + await pagination.menu.clickNthItem(3); + await dataTable.waitForBody(); + expect(await pagination.range.getText()).toContain('51-75 of 101'); + expect(await pagination.currentPage.getText()).toContain('Page 3'); + expect(await pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(await pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + + await pagination.resetToDefaultPageNumber(); + }); + + it('navigate to next and previous pages - [C290131]', async () => { + await pagination.nextButton.click(); + await dataTable.waitForBody(); + expect(await pagination.range.getText()).toContain('26-50 of 101'); + await pagination.resetToDefaultPageNumber(); + + await pagination.openCurrentPageMenu(); + await pagination.menu.clickNthItem(2); + await dataTable.waitForBody(); + await pagination.previousButton.click(); + await dataTable.waitForBody(); + expect(pagination.range.getText()).toContain('1-25 of 101'); + + await pagination.resetToDefaultPageNumber(); + }); + + it('Previous button is disabled on first page - [C290129]', async () => { + expect(await pagination.currentPage.getText()).toContain('Page 1'); + expect(await pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled on first page'); + }); + + it('Next button is disabled on last page - [C290130]', async () => { + await pagination.openCurrentPageMenu(); + await pagination.menu.clickNthItem(5); + expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(await pagination.currentPage.getText()).toContain('Page 5'); + expect(await pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled on last page'); + }); +}); diff --git a/e2e/suites/pagination/pag-shared-files.test.ts b/e2e/suites/pagination/pag-shared-files.test.ts index f83f29ad2f..7d4597590a 100755 --- a/e2e/suites/pagination/pag-shared-files.test.ts +++ b/e2e/suites/pagination/pag-shared-files.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -44,7 +42,6 @@ describe('Pagination on multiple pages on Shared Files', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -60,8 +57,7 @@ describe('Pagination on multiple pages on Shared Files', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -72,7 +68,6 @@ describe('Pagination on multiple pages on Shared Files', () => { afterAll(async (done) => { await apis.user.nodes.deleteNodeById(parentId); - await logoutPage.load(); done(); }); diff --git a/e2e/suites/pagination/pag-single-page.test.ts b/e2e/suites/pagination/pag-single-page.test.ts index a790c3dcc9..bffffc44a9 100755 --- a/e2e/suites/pagination/pag-single-page.test.ts +++ b/e2e/suites/pagination/pag-single-page.test.ts @@ -23,8 +23,7 @@ * along with Alfresco. If not, see . */ -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -42,9 +41,9 @@ describe('Pagination on single page', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { dataTable, pagination } = page; + const { pagination, dataTable } = page; + const { searchInput } = page.header; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); @@ -75,45 +74,46 @@ describe('Pagination on single page', () => { await Promise.all([ apis.user.nodes.deleteNodeById(fileId), apis.user.sites.deleteSite(siteId), - apis.user.trashcan.emptyTrash(), - logoutPage.load() + apis.user.trashcan.emptyTrash() ]); done(); }); it('page selector not displayed on Favorites - [C280112]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); it('page selector not displayed on File Libraries - [C280085]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); it('page selector not displayed on Personal Files - [C280076]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); it('page selector not displayed on Recent Files - [C280103]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); it('page selector not displayed on Shared Files - [C280094]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); it('page selector not displayed on Trash - [C280121]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); + expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); + }); + + it('page selector not displayed on Search results - [C290124]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkOnlyFiles(); + await searchInput.searchFor(file); + await dataTable.waitForBody(); expect(await pagination.pagesButton.isPresent()).toBe(false, 'page selector displayed'); }); diff --git a/e2e/suites/pagination/pag-trash.test.ts b/e2e/suites/pagination/pag-trash.test.ts index 403bd1d69e..93abb03fba 100755 --- a/e2e/suites/pagination/pag-trash.test.ts +++ b/e2e/suites/pagination/pag-trash.test.ts @@ -23,9 +23,7 @@ * along with Alfresco. If not, see . */ -import { browser } from 'protractor'; -import { SIDEBAR_LABELS } from '../../configs'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; @@ -42,7 +40,6 @@ describe('Pagination on multiple pages on Trash', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const { dataTable, pagination } = page; @@ -56,8 +53,7 @@ describe('Pagination on multiple pages on Trash', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); done(); }); @@ -68,7 +64,6 @@ describe('Pagination on multiple pages on Trash', () => { afterAll(async (done) => { await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); diff --git a/e2e/suites/search/search-input.test.ts b/e2e/suites/search/search-input.test.ts new file mode 100644 index 0000000000..3dfb6e0644 --- /dev/null +++ b/e2e/suites/search/search-input.test.ts @@ -0,0 +1,88 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { BrowsingPage, LoginPage } from '../../pages/pages'; +// import { Utils } from '../../utilities/utils'; +import { browser } from 'protractor'; + +describe('Search input', () => { + const loginPage = new LoginPage(); + const page = new BrowsingPage(); + const { searchInput } = page.header; + + beforeAll(async (done) => { + await loginPage.loginWithAdmin(); + done(); + }); + + beforeEach(async (done) => { + // await Utils.pressEscape(); + await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); + done(); + }); + + it('Search input is displayed in the app header - [C289847]', async () => { + expect(await searchInput.isSearchContainerDisplayed()).toBe(true, 'search controls not displayed'); + }); + + it('Search options are displayed when clicking in the search input - [C289848]', async () => { + await searchInput.clickSearchButton(); + expect(await searchInput.isOptionsAreaDisplayed()).toBe(true, '1. Search options not displayed'); + expect(await searchInput.isFilesOptionEnabled()).toBe(true, '2. Files option not enabled'); + expect(await searchInput.isFoldersOptionEnabled()).toBe(true, '3. Folders option not enabled'); + expect(await searchInput.isLibrariesOptionEnabled()).toBe(true, '4. Libraries option not enabled'); + expect(await searchInput.isFilesOptionChecked()).toBe(false, '5. Files option is checked'); + expect(await searchInput.isFoldersOptionChecked()).toBe(false, '6. Folders option is checked'); + expect(await searchInput.isLibrariesOptionChecked()).toBe(false, '7. Libraries option is checked'); + }); + + it('Search options are correctly enabled / disabled - [C289849]', async () => { + await searchInput.clickSearchButton(); + + await searchInput.clickFilesOption(); + expect(await searchInput.isFoldersOptionEnabled()).toBe(true, 'Folders option not enabled'); + expect(await searchInput.isLibrariesOptionEnabled()).toBe(false, 'Libraries option not disabled'); + + await searchInput.clickFilesOption(); + expect(await searchInput.isFoldersOptionEnabled()).toBe(true, 'Folders option not enabled'); + expect(await searchInput.isLibrariesOptionEnabled()).toBe(true, 'Folders option not enabled'); + + await searchInput.clickFoldersOption(); + expect(await searchInput.isFilesOptionEnabled()).toBe(true, 'Files option not enabled'); + expect(await searchInput.isLibrariesOptionEnabled()).toBe(false, 'Libraries option not disabled'); + + await searchInput.clickFoldersOption(); + expect(await searchInput.isFilesOptionEnabled()).toBe(true, 'Files option not enabled'); + expect(await searchInput.isLibrariesOptionEnabled()).toBe(true, 'Libraries option not enabled'); + + await searchInput.clickLibrariesOption(); + expect(await searchInput.isFilesOptionEnabled()).toBe(false, 'Files option not disabled'); + expect(await searchInput.isFoldersOptionEnabled()).toBe(false, 'Folders option not disabled'); + + await searchInput.clickLibrariesOption(); + expect(await searchInput.isFilesOptionEnabled()).toBe(true, 'Files option not enabled'); + expect(await searchInput.isFoldersOptionEnabled()).toBe(true, 'Folders option not enabled'); + }); +}); diff --git a/e2e/suites/search/search-results-libraries.test.ts b/e2e/suites/search/search-results-libraries.test.ts new file mode 100644 index 0000000000..e54d49c88e --- /dev/null +++ b/e2e/suites/search/search-results-libraries.test.ts @@ -0,0 +1,254 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LoginPage, SearchResultsPage } from '../../pages/pages'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; +import { SITE_VISIBILITY, SITE_ROLES } from './../../configs'; + +describe('Search results - libraries', () => { + const username = `user-${Utils.random()}`; + + const site1 = { + name: `lib-1-${Utils.random()}`, + id: `site1-${Utils.random()}` + }; + const site2 = { + name: `site-2-${Utils.random()}`, + id: `site1-${Utils.random()}` + }; + const site3 = { + name: `my-lib-${Utils.random()}`, + id: `site3-${Utils.random()}` + }; + const site4 = { + name: `my-site-${Utils.random()}`, + id: `site4-${Utils.random()}`, + description: 'site description' + }; + + const userSitePrivate = `user-site-private-${Utils.random()}`; + const userSiteModerated = `user-site-moderated-${Utils.random()}`; + const userSitePublic = `user-site-public-${Utils.random()}`; + + const siteRussian = { + /* cspell:disable-next-line */ + name: `любимый-сайт-${Utils.random()}`, + id: `site-russian-id-${Utils.random()}` + }; + + const adminSite1 = `admin-site-${Utils.random()}`; + const adminSite2 = `admin-site-${Utils.random()}`; + const adminSite3 = `admin-site-${Utils.random()}`; + const adminSite4 = `admin-site-${Utils.random()}`; + + const adminPrivate = `admin-site-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new SearchResultsPage(); + const { searchInput } = page.header; + const dataTable = page.dataTable; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + + await apis.user.sites.createSite(site1.name, SITE_VISIBILITY.PUBLIC, '', site1.id); + await apis.user.sites.createSite(site2.name, SITE_VISIBILITY.PUBLIC, '', site2.id); + await apis.user.sites.createSite(site3.name, SITE_VISIBILITY.PUBLIC, '', site3.id); + await apis.user.sites.createSite(site4.name, SITE_VISIBILITY.PUBLIC, site4.description, site4.id); + + await apis.user.sites.createSite(userSitePublic, SITE_VISIBILITY.PUBLIC); + await apis.user.sites.createSite(userSiteModerated, SITE_VISIBILITY.MODERATED); + await apis.user.sites.createSite(userSitePrivate, SITE_VISIBILITY.PRIVATE); + + await apis.user.sites.createSite(siteRussian.name, SITE_VISIBILITY.PUBLIC, '', siteRussian.id); + + await apis.admin.sites.createSite(adminSite1, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite2, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite3, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.createSite(adminSite4, SITE_VISIBILITY.PUBLIC); + await apis.admin.sites.addSiteMember(adminSite1, username, SITE_ROLES.SITE_CONSUMER.ROLE); + await apis.admin.sites.addSiteMember(adminSite2, username, SITE_ROLES.SITE_CONTRIBUTOR.ROLE); + await apis.admin.sites.addSiteMember(adminSite3, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); + await apis.admin.sites.addSiteMember(adminSite4, username, SITE_ROLES.SITE_MANAGER.ROLE); + + await apis.admin.sites.createSite(adminPrivate, SITE_VISIBILITY.PRIVATE); + + await apis.user.sites.waitForApi({ expect: 12 }); + await apis.user.queries.waitForApi('lib', { expect: 2 }); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.admin.sites.deleteSites([ adminSite1, adminSite2, adminSite3, adminSite4, adminPrivate ]), + apis.user.sites.deleteSites([ site1.id, site2.id, site3.id, site4.id, userSitePublic, userSiteModerated, userSitePrivate, siteRussian.id ]) + ]); + done(); + }); + + beforeEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + it('Search library - full name match - [C290012]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor(site1.name); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(site1.name).isPresent()).toBe(true, `${site1.name} not displayed`); + expect(await dataTable.getRowByName(site2.name).isPresent()).toBe(false, `${site2.name} displayed`); + expect(await dataTable.getRowByName(site3.name).isPresent()).toBe(false, `${site3.name} displayed`); + expect(await dataTable.getRowByName(site4.name).isPresent()).toBe(false, `${site4.name} displayed`); + }); + + it('Search library - partial name match - [C290013]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('lib'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(site1.name).isPresent()).toBe(true, `${site1.name} not displayed`); + expect(await dataTable.getRowByName(site2.name).isPresent()).toBe(false, `${site2.name} displayed`); + expect(await dataTable.getRowByName(site3.name).isPresent()).toBe(true, `${site3.name} not displayed`); + expect(await dataTable.getRowByName(site4.name).isPresent()).toBe(false, `${site4.name} displayed`); + }); + + it('Search library - description match - [C290014]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor(site4.description); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(site1.name).isPresent()).toBe(false, `${site1.name} displayed`); + expect(await dataTable.getRowByName(site2.name).isPresent()).toBe(false, `${site2.name} displayed`); + expect(await dataTable.getRowByName(site3.name).isPresent()).toBe(false, `${site3.name} displayed`); + expect(await dataTable.getRowByName(site4.name).isPresent()).toBe(true, `${site4.name} not displayed`); + }); + + it('Results page title - [C290015]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('lib'); + await dataTable.waitForBody(); + + expect(await page.breadcrumb.getCurrentItemName()).toEqual('Libraries found...'); + }); + + it('Results page columns - [C290016]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor(site1.name); + await dataTable.waitForBody(); + + const labels = [ 'Name', 'My Role', 'Visibility' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns'); + + await elements.forEach(async (element, index) => { + expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('Library visibility is correctly displayed - [C290017]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('user-site'); + await dataTable.waitForBody(); + + const expectedSitesVisibility = { + [userSitePrivate]: SITE_VISIBILITY.PRIVATE, + [userSiteModerated]: SITE_VISIBILITY.MODERATED, + [userSitePublic]: SITE_VISIBILITY.PUBLIC + }; + + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[3].toUpperCase(); + return acc; + }, {}); + + Object.keys(expectedSitesVisibility).forEach((expectedSite) => { + expect(sitesList[expectedSite]).toEqual(expectedSitesVisibility[expectedSite]); + }); + }); + + it('User role is correctly displayed - [C290018]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('admin-site'); + await dataTable.waitForBody(); + + const expectedSitesRoles = { + [adminSite1]: SITE_ROLES.SITE_CONSUMER.LABEL, + [adminSite2]: SITE_ROLES.SITE_CONTRIBUTOR.LABEL, + [adminSite3]: SITE_ROLES.SITE_COLLABORATOR.LABEL, + [adminSite4]: SITE_ROLES.SITE_MANAGER.LABEL + }; + + const rowCells = await dataTable.getRows().map((row) => { + return row.all(dataTable.cell).map(async cell => await cell.getText()); + }); + const sitesList = rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[2]; + return acc; + }, {}); + + Object.keys(expectedSitesRoles).forEach((expectedSite) => { + expect(sitesList[expectedSite]).toEqual(expectedSitesRoles[expectedSite]); + }); + }); + + it('Private sites are not displayed when user is not a member - [C290019]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('admin-site'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(adminPrivate).isPresent()).toBe(false, `${adminPrivate} is displayed`); + }); + + it('Search libraries with special characters - [C290028]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor(siteRussian.name); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(siteRussian.name).isPresent()).toBe(true, `${siteRussian.name} not displayed`); + }); + +}); diff --git a/e2e/suites/search/search-results.test.ts b/e2e/suites/search/search-results.test.ts new file mode 100644 index 0000000000..bae09fb1b5 --- /dev/null +++ b/e2e/suites/search/search-results.test.ts @@ -0,0 +1,119 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LoginPage, SearchResultsPage } from '../../pages/pages'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Search results', () => { + const username = `user-${Utils.random()}`; + + const file = `test-file-${Utils.random()}.txt`; let fileId; + const folder = `test-folder-${Utils.random()}`; let folderId; + const site = `test-site-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const page = new SearchResultsPage(); + const { searchInput } = page.header; + const dataTable = page.dataTable; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + + fileId = (await apis.user.nodes.createFile(file)).entry.id; + folderId = (await apis.user.nodes.createFolder(folder)).entry.id; + await apis.user.sites.createSite(site); + + await apis.user.search.waitForApi(username, { expect: 1 }); + await apis.user.queries.waitForApi(site, { expect: 1 }); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.deleteNodeById(fileId), + apis.user.nodes.deleteNodeById(folderId), + apis.user.sites.deleteSite(site) + ]); + done(); + }); + + beforeEach(async (done) => { + await page.refresh(); + done(); + }); + + it('Only files are returned when Files option is the only one checked - [C290005]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkOnlyFiles(); + await searchInput.searchFor('test'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(file).isPresent()).toBe(true, `${file} not displayed`); + expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, `${folder} is displayed`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} is displayed`); + }); + + it('Only folders are returned when Folders option is the only one checked - [C290006]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkOnlyFolders(); + await searchInput.searchFor('test'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(file).isPresent()).toBe(false, `${file} is displayed`); + expect(await dataTable.getRowByName(folder).isPresent()).toBe(true, `${folder} not displayed`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} is displayed`); + }); + + it('Files and folders are returned when both Files and Folders options are checked - [C290007]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkFilesAndFolders(); + await searchInput.searchFor('test'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(file).isPresent()).toBe(true, `${file} not displayed`); + expect(await dataTable.getRowByName(folder).isPresent()).toBe(true, `${folder} not displayed`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} is displayed`); + }); + + it('Only libraries are returned when Libraries option is checked - [C290008]', async () => { + await searchInput.clickSearchButton(); + await searchInput.checkLibraries(); + await searchInput.searchFor('test'); + await dataTable.waitForBody(); + + expect(await dataTable.getRowByName(file).isPresent()).toBe(false, `${file} is displayed`); + expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, `${folder} is displayed`); + expect(await dataTable.getRowByName(site).isPresent()).toBe(true, `${site} not displayed`); + }); + +}); diff --git a/e2e/suites/viewer/viewer-actions.test.ts b/e2e/suites/viewer/viewer-actions.test.ts index 4a1d82a416..1274f8653b 100755 --- a/e2e/suites/viewer/viewer-actions.test.ts +++ b/e2e/suites/viewer/viewer-actions.test.ts @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . */ -import { LoginPage, BrowsingPage, LogoutPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, FILES } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { FILES } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; import { Viewer } from '../../components/viewer/viewer'; @@ -45,7 +45,6 @@ describe('Viewer actions', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const dataTable = page.dataTable; const viewer = new Viewer(); @@ -81,8 +80,7 @@ describe('Viewer actions', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.waitForHeader(); done(); @@ -97,7 +95,6 @@ describe('Viewer actions', () => { await apis.user.nodes.deleteNodeById(parentId); await apis.user.nodes.deleteNodeById(destinationId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -108,17 +105,17 @@ describe('Viewer actions', () => { expect(await toolbar.isEmpty()).toBe(false, `viewer toolbar is empty`); expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed`); expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed`); - expect(await toolbar.isButtonPresent('Print')).toBe(true, `print`); - expect(await toolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `full screen`); - expect(await toolbar.isButtonPresent('View details')).toBe(true, `view details`); - await toolbar.openMoreMenu(); - expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `favorite`); - expect(await toolbar.menu.isMenuItemPresent('Share')).toBe(true, `share`); - expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `copy`); - expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `move`); - expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `delete`); - expect(await toolbar.menu.isMenuItemPresent('Manage Versions')).toBe(true, `manage versions`); - expect(await toolbar.menu.isMenuItemPresent('Permissions')).toBe(true, `permissions`); + expect(await toolbar.isButtonPresent('Print')).toBe(true, `Print is not displayed`); + expect(await toolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `Full screen is not displayed`); + expect(await toolbar.isShareButtonPresent()).toBe(true, `Share is not displayed`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `view details is not displayed`); + await toolbar.openMoreMenu(); + expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await toolbar.menu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage versions is not displayed`); + expect(await toolbar.menu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); await toolbar.closeMoreMenu(); }); @@ -143,8 +140,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Copied 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(docxPersonalFiles).isPresent()).toBe(true, 'Item is not in the list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(docxPersonalFiles).isPresent()).toBe(true, 'Item is not present in destination'); @@ -165,8 +161,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Moved 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(xlsxPersonalFiles).isPresent()).toBe(false, 'Item was not moved'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(xlsxPersonalFiles).isPresent()).toBe(true, 'Item is not present in destination'); }); @@ -178,8 +173,7 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Favorite'); await viewer.clickClose(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await apis.user.favorites.isFavorite(docxFileId)).toBe(true, 'Item is not favorite'); expect(await dataTable.getRowByName(docxPersonalFiles).isPresent()).toBe(true, 'Item is not present in Favorites list'); }); @@ -191,11 +185,9 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); expect(await page.getSnackBarMessage()).toContain(`${pdfPersonalFiles} deleted`); - // TODO: enable this when ACA-1806 is fixed - // expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); + expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.getRowByName(pdfPersonalFiles).isPresent()).toBe(true, 'Item is not present in Trash'); }); @@ -215,8 +207,7 @@ describe('Viewer actions', () => { await dataTable.doubleClickOnRowByName(docxPersonalFiles); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await shareDialog.clickClose(); }); @@ -236,8 +227,7 @@ describe('Viewer actions', () => { await dataTable.doubleClickOnRowByName(docxPersonalFiles); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await Utils.pressEscape(); expect(await shareDialog.isDialogOpen()).toBe(false, 'Dialog is still open'); @@ -266,8 +256,7 @@ describe('Viewer actions', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); done(); @@ -282,7 +271,6 @@ describe('Viewer actions', () => { await apis.user.sites.deleteSite(siteName); await apis.user.nodes.deleteNodeById(destinationId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -307,8 +295,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Copied 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(docxLibraries).isPresent()).toBe(true, 'Item is not in the list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(docxLibraries).isPresent()).toBe(true, 'Item is not present in destination'); @@ -329,8 +316,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Moved 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(xlsxLibraries).isPresent()).toBe(false, 'Item was not moved'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(xlsxLibraries).isPresent()).toBe(true, 'Item is not present in destination'); }); @@ -342,8 +328,7 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Favorite'); await viewer.clickClose(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await apis.user.favorites.isFavorite(docxFileId)).toBe(true, `${docxLibraries} is not favorite`); expect(await dataTable.getRowByName(docxLibraries).isPresent()).toBe(true, `${docxLibraries} is not present in Favorites list`); }); @@ -355,11 +340,9 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); expect(await page.getSnackBarMessage()).toContain(`${pdfLibraries} deleted`); - // TODO: enable this when ACA-1806 is fixed - // expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); + expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.getRowByName(pdfLibraries).isPresent()).toBe(true, 'Item is not present in Trash'); }); @@ -367,8 +350,7 @@ describe('Viewer actions', () => { await dataTable.doubleClickOnRowByName(docxLibraries); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await shareDialog.clickClose(); }); @@ -407,8 +389,7 @@ describe('Viewer actions', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); done(); }); @@ -421,7 +402,6 @@ describe('Viewer actions', () => { await apis.user.nodes.deleteNodeById(parentId); await apis.user.nodes.deleteNodeById(destinationId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -446,8 +426,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Copied 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(docxRecentFiles).isPresent()).toBe(true, 'Item is not in the list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(docxRecentFiles).isPresent()).toBe(true, 'Item is not present in destination'); @@ -469,8 +448,7 @@ describe('Viewer actions', () => { await viewer.clickClose(); expect(await dataTable.getRowByName(xlsxRecentFiles).isPresent()).toBe(true, 'Item is not in the list'); expect(await dataTable.getItemLocationTileAttr(xlsxRecentFiles)).toContain(destination, 'Item was not moved'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(xlsxRecentFiles).isPresent()).toBe(true, 'Item is not present in destination'); }); @@ -482,8 +460,7 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Favorite'); await viewer.clickClose(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await apis.user.favorites.isFavorite(docxFileId)).toBe(true, 'Item is not favorite'); expect(await dataTable.getRowByName(docxRecentFiles).isPresent()).toBe(true, 'Item is not present in Favorites list'); }); @@ -495,11 +472,9 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); expect(await page.getSnackBarMessage()).toContain(`${pdfRecentFiles} deleted`); - // TODO: enable this when ACA-1806 is fixed - // expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); + expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.getRowByName(pdfRecentFiles).isPresent()).toBe(true, 'Item is not present in Trash'); }); @@ -507,8 +482,7 @@ describe('Viewer actions', () => { await dataTable.doubleClickOnRowByName(docxRecentFiles); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await shareDialog.clickClose(); }); @@ -547,8 +521,7 @@ describe('Viewer actions', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); done(); }); @@ -561,7 +534,6 @@ describe('Viewer actions', () => { await apis.user.nodes.deleteNodeById(parentId); await apis.user.nodes.deleteNodeById(destinationId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -586,8 +558,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Copied 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(docxSharedFiles).isPresent()).toBe(true, 'Item is not in the list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(docxSharedFiles).isPresent()).toBe(true, 'Item is not present in destination'); @@ -609,8 +580,7 @@ describe('Viewer actions', () => { await viewer.clickClose(); expect(await dataTable.getRowByName(xlsxSharedFiles).isPresent()).toBe(true, 'Item is not in the list'); expect(await dataTable.getItemLocationTileAttr(xlsxSharedFiles)).toContain(destination, 'Item was not moved'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(xlsxSharedFiles).isPresent()).toBe(true, 'Item is not present in destination'); }); @@ -622,8 +592,7 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Favorite'); await viewer.clickClose(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await apis.user.favorites.isFavorite(docxFileId)).toBe(true, 'Item is not favorite'); expect(await dataTable.getRowByName(docxSharedFiles).isPresent()).toBe(true, 'Item is not present in Favorites list'); }); @@ -635,21 +604,17 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); expect(await page.getSnackBarMessage()).toContain(`${pdfSharedFiles} deleted`); - // TODO: enable this when ACA-1806 is fixed - // expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); + expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.getRowByName(pdfSharedFiles).isPresent()).toBe(true, 'Item is not present in Trash'); }); - // TODO: enable tis when Unshare is implemented - ACA-122 - xit('Share action - [C286381]', async () => { + it('Share action - [C286381]', async () => { await dataTable.doubleClickOnRowByName(docxSharedFiles); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareEditButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await shareDialog.clickClose(); }); @@ -690,8 +655,7 @@ describe('Viewer actions', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); done(); }); @@ -704,7 +668,6 @@ describe('Viewer actions', () => { await apis.user.nodes.deleteNodeById(parentId); await apis.user.nodes.deleteNodeById(destinationId); await apis.user.trashcan.emptyTrash(); - await logoutPage.load(); done(); }); @@ -729,8 +692,7 @@ describe('Viewer actions', () => { expect(await page.getSnackBarMessage()).toContain('Copied 1 item'); await viewer.clickClose(); expect(await dataTable.getRowByName(docxFavorites).isPresent()).toBe(true, 'Item is not in the list'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(docxFavorites).isPresent()).toBe(true, 'Item is not present in destination'); @@ -752,8 +714,7 @@ describe('Viewer actions', () => { await viewer.clickClose(); expect(await dataTable.getRowByName(xlsxFavorites).isPresent()).toBe(true, 'Item is not in the list'); expect(await dataTable.getItemLocationTileAttr(xlsxFavorites)).toContain(destination, 'Item was not moved'); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(destination); expect(await dataTable.getRowByName(xlsxFavorites).isPresent()).toBe(true, 'Item is not present in destination'); }); @@ -765,8 +726,7 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Favorite'); await viewer.clickClose(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); expect(await apis.user.favorites.isFavorite(xlsxFileId)).toBe(false, 'Item is still favorite'); expect(await dataTable.getRowByName(xlsxFavorites).isPresent()).toBe(false, 'Item is still present in Favorites list'); }); @@ -778,11 +738,9 @@ describe('Viewer actions', () => { await toolbar.openMoreMenu(); await toolbar.menu.clickMenuItem('Delete'); expect(await page.getSnackBarMessage()).toContain(`${pdfFavorites} deleted`); - // TODO: enable this when ACA-1806 is fixed - // expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); + expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); await Utils.pressEscape(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); - await dataTable.waitForHeader(); + await page.clickTrashAndWait(); expect(await dataTable.getRowByName(pdfFavorites).isPresent()).toBe(true, 'Item is not present in Trash'); }); @@ -790,8 +748,7 @@ describe('Viewer actions', () => { await dataTable.doubleClickOnRowByName(docxFavorites); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - await toolbar.openMoreMenu(); - await toolbar.menu.clickMenuItem('Share'); + await toolbar.clickShareButton(); expect(await shareDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); await shareDialog.clickClose(); }); diff --git a/e2e/suites/viewer/viewer-general.test.ts b/e2e/suites/viewer/viewer-general.test.ts index aa4a839a19..2a5f72ba55 100755 --- a/e2e/suites/viewer/viewer-general.test.ts +++ b/e2e/suites/viewer/viewer-general.test.ts @@ -24,8 +24,8 @@ */ import { protractor, browser } from 'protractor'; -import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SIDEBAR_LABELS, FILES, SITE_VISIBILITY } from '../../configs'; +import { LoginPage, BrowsingPage } from '../../pages/pages'; +import { FILES, SITE_VISIBILITY } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; import { Viewer } from '../../components/viewer/viewer'; @@ -49,7 +49,6 @@ describe('Viewer general', () => { }; const loginPage = new LoginPage(); - const logoutPage = new LogoutPage(); const page = new BrowsingPage(); const dataTable = page.dataTable; const viewer = new Viewer(); @@ -77,8 +76,7 @@ describe('Viewer general', () => { }); beforeEach(async (done) => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); - await dataTable.waitForHeader(); + await page.clickPersonalFilesAndWait(); await dataTable.doubleClickOnRowByName(parent); await dataTable.waitForHeader(); done(); @@ -93,7 +91,6 @@ describe('Viewer general', () => { await apis.user.nodes.deleteNodeById(parentId); await apis.admin.sites.deleteSite(siteAdmin); await apis.user.sites.deleteSite(siteUser); - await logoutPage.load(); done(); }); @@ -142,8 +139,7 @@ describe('Viewer general', () => { }); it('Viewer opens for a file from File Libraries - [C284633]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); + await page.clickFileLibrariesAndWait(); await dataTable.doubleClickOnRowByName(siteUser); await dataTable.waitForHeader(); await dataTable.doubleClickOnRowByName(fileInSite); @@ -154,8 +150,7 @@ describe('Viewer general', () => { }); it('Viewer opens for a file from Recent Files - [C284636]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); - await dataTable.waitForHeader(); + await page.clickRecentFilesAndWait(); await dataTable.doubleClickOnRowByName(xlsxFile); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); @@ -164,8 +159,7 @@ describe('Viewer general', () => { }); it('Viewer opens for a file from Shared Files - [C284635]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); + await page.clickSharedFilesAndWait(); await dataTable.doubleClickOnRowByName(xlsxFile); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); @@ -174,8 +168,7 @@ describe('Viewer general', () => { }); it('Viewer opens for a file from Favorites - [C284634]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); + await page.clickFavoritesAndWait(); await dataTable.doubleClickOnRowByName(xlsxFile); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); diff --git a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts index 9636ab66d7..f4fb0d1ad8 100755 --- a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts +++ b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts @@ -45,19 +45,26 @@ export class FavoritesApi extends RepoApi { return await this.alfrescoJsApi.core.favoritesApi.addFavorite('-me-', data); } - async addFavoriteById(nodeType: 'file' | 'folder', id: string) { + async addFavoriteById(nodeType: 'file' | 'folder' | 'site', id: string) { + let guid; + await this.apiAuth(); + + if ( nodeType === 'site' ) { + guid = (await this.alfrescoJsApi.core.sitesApi.getSite(id)).entry.guid; + } else { + guid = id; + } const data = { target: { [nodeType]: { - guid: id + guid: guid } } }; - await this.apiAuth(); return await this.alfrescoJsApi.core.favoritesApi.addFavorite('-me-', data); } - async addFavoritesByIds(nodeType: 'file' | 'folder', ids: string[]) { + async addFavoritesByIds(nodeType: 'file' | 'folder' | 'site', ids: string[]) { await this.apiAuth(); return await ids.reduce(async (previous, current) => { await previous; @@ -72,13 +79,32 @@ export class FavoritesApi extends RepoApi { async getFavoriteById(nodeId: string) { await this.apiAuth(); - return await this.alfrescoJsApi.core.favoritesApi.getFavorite('-me', nodeId); + return await this.alfrescoJsApi.core.favoritesApi.getFavorite('-me-', nodeId); } async isFavorite(nodeId: string) { return JSON.stringify((await this.getFavorites()).list.entries).includes(nodeId); } + async isFavoriteWithRetry(nodeId: string, data) { + let isFavorite; + try { + const favorite = async () => { + isFavorite = JSON.stringify((await this.getFavorites()).list.entries).includes(nodeId); + if ( isFavorite !== data.expect ) { + return Promise.reject(isFavorite); + } else { + return Promise.resolve(isFavorite); + } + }; + + return await Utils.retryCall(favorite); + } catch (error) { + console.log('-----> catch isFavoriteWithRetry: ', error); + } + return isFavorite; + } + async removeFavoriteById(nodeId: string) { await this.apiAuth(); return await this.alfrescoJsApi.core.peopleApi.removeFavoriteSite('-me-', nodeId); diff --git a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts index 58a7934aca..9a60adc789 100755 --- a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts +++ b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts @@ -46,12 +46,9 @@ export class NodesApi extends RepoApi { return await this.alfrescoJsApi.core.nodesApi.getNode(id); } - async getNodeDescription(name: string, relativePath: string = '/') { - relativePath = (relativePath === '/') - ? `${name}` - : `${relativePath}/${name}`; - - return (await this.getNodeByPath(`${relativePath}`)).entry.properties['cm:description']; + async getNodeDescription(name: string, parentId: string) { + const children = (await this.getNodeChildren(parentId)).list.entries; + return children.find(elem => elem.entry.name === name).entry.properties['cm:description']; } async getNodeProperty(nodeId: string, property: string) { @@ -93,8 +90,11 @@ export class NodesApi extends RepoApi { // children async getNodeChildren(nodeId: string) { + const opts = { + include: [ 'properties' ] + }; await this.apiAuth(); - return await this.alfrescoJsApi.core.nodesApi.getNodeChildren(nodeId); + return await this.alfrescoJsApi.core.nodesApi.getNodeChildren(nodeId, opts); } async deleteNodeChildren(parentId: string) { diff --git a/e2e/utilities/repo-client/apis/queries/queries-api.ts b/e2e/utilities/repo-client/apis/queries/queries-api.ts new file mode 100755 index 0000000000..87aba7bc45 --- /dev/null +++ b/e2e/utilities/repo-client/apis/queries/queries-api.ts @@ -0,0 +1,61 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { RepoApi } from '../repo-api'; +import { Utils } from '../../../../utilities/utils'; + +export class QueriesApi extends RepoApi { + + constructor(username?, password?) { + super(username, password); + } + + async findSites(searchTerm: string) { + const data = { + term: searchTerm, + fields: ['title'] + }; + + await this.apiAuth(); + return this.alfrescoJsApi.core.queriesApi.findSites(searchTerm, data); + } + + async waitForApi(searchTerm, data) { + try { + const sites = async () => { + const totalItems = (await this.findSites(searchTerm)).list.pagination.totalItems; + if ( totalItems !== data.expect ) { + return Promise.reject(totalItems); + } else { + return Promise.resolve(totalItems); + } + }; + + return await Utils.retryCall(sites); + } catch (error) { + console.log('-----> catch queries findSites: ', error); + } + } +} diff --git a/e2e/utilities/repo-client/apis/sites/sites-api.ts b/e2e/utilities/repo-client/apis/sites/sites-api.ts index 386e11544d..6bb05507af 100755 --- a/e2e/utilities/repo-client/apis/sites/sites-api.ts +++ b/e2e/utilities/repo-client/apis/sites/sites-api.ts @@ -49,6 +49,21 @@ export class SitesApi extends RepoApi { return (await this.alfrescoJsApi.core.sitesApi.getSiteContainers(siteId)).list.entries[0].entry.id; } + async getVisibility(siteId: string) { + const site = await this.getSite(siteId); + return site.entry.visibility; + } + + async getDescription(siteId: string) { + const site = await this.getSite(siteId); + return site.entry.description; + } + + async getTitle(siteId: string) { + const site = await this.getSite(siteId); + return site.entry.title; + } + async createSite(title: string, visibility?: string, description?: string, siteId?: string) { const site = { title, @@ -80,6 +95,15 @@ export class SitesApi extends RepoApi { }, Promise.resolve()); } + async deleteAllUserSites(permanent: boolean = true) { + const siteIds = (await this.getSites()).list.entries.map(entries => entries.entry.id); + + return await siteIds.reduce(async (previous, current) => { + await previous; + return await this.deleteSite(current, permanent); + }, Promise.resolve()); + } + async updateSiteMember(siteId: string, userId: string, role: string) { const siteRole = { role: role @@ -104,6 +128,24 @@ export class SitesApi extends RepoApi { return await this.alfrescoJsApi.core.sitesApi.removeSiteMember(siteId, userId); } + async requestToJoin(siteId: string) { + const body = { + id: siteId + }; + await this.apiAuth(); + try { + return await this.alfrescoJsApi.core.peopleApi.addSiteMembershipRequest('-me-', body); + } catch (error) { + console.log('====== requestToJoin catch ', error); + }; + } + + async hasMembershipRequest(siteId: string) { + await this.apiAuth(); + const requests = (await this.alfrescoJsApi.core.peopleApi.getSiteMembershipRequests('-me-')).list.entries.map(e => e.entry.id); + return requests.includes(siteId); + } + async waitForApi(data) { try { const sites = async () => { diff --git a/e2e/utilities/repo-client/repo-client.ts b/e2e/utilities/repo-client/repo-client.ts index ffe73a7609..d56ca241ae 100755 --- a/e2e/utilities/repo-client/repo-client.ts +++ b/e2e/utilities/repo-client/repo-client.ts @@ -29,6 +29,7 @@ import { PeopleApi } from './apis/people/people-api'; import { NodesApi } from './apis/nodes/nodes-api'; import { SitesApi } from './apis/sites/sites-api'; import { FavoritesApi } from './apis/favorites/favorites-api'; +import { QueriesApi } from './apis/queries/queries-api'; import { SharedLinksApi } from './apis/shared-links/shared-links-api'; import { TrashcanApi } from './apis/trashcan/trashcan-api'; import { SearchApi } from './apis/search/search-api'; @@ -74,6 +75,10 @@ export class RepoClient { return new SearchApi(this.auth.username, this.auth.password); } + get queries() { + return new QueriesApi(this.auth.username, this.auth.password); + } + get upload() { return new UploadApi(this.auth.username, this.auth.password); } diff --git a/e2e/utilities/utils.ts b/e2e/utilities/utils.ts index 7adbaa1eb3..ec2c4c9929 100755 --- a/e2e/utilities/utils.ts +++ b/e2e/utilities/utils.ts @@ -29,6 +29,14 @@ const path = require('path'); const fs = require('fs'); export class Utils { + static string257 = 'assembly doctor offender limit clearance inspiration baker fraud active apples trait brainstorm concept breaks down presidential \ + reluctance summary communication patience books opponent banana economist head develop project swear unanimous read conservation'; + + static string513 = 'great indirect brain tune other expectation fun silver drain tumble rhythm harmful wander picture distribute opera complication copyright \ + explosion snack ride pool machinery pair frog joint wrestle video referee drive window cage falsify happen tablet horror thank conception \ + extension decay dismiss platform respect ceremony applaud absorption presentation dominate race courtship soprano body \ + lighter track cinema tread tick climate lend summit singer radical flower visual negotiation promises cooperative live'; + // generate a random value static random() { return Math.random().toString(36).substring(5, 10).toLowerCase(); @@ -45,20 +53,20 @@ export class Utils { } static getSessionStorage() { - return browser.executeScript('return window.sessionStorage.getItem("aca.extension.config");'); + return browser.executeScript('return window.sessionStorage.getItem("app.extension.config");'); } - static setSessionStorageFromConfig(key: string, configFileName: string) { + static setSessionStorageFromConfig(configFileName: string) { const configFile = `${E2E_ROOT_PATH}/resources/extensibility-configs/${configFileName}`; const fileContent = JSON.stringify(fs.readFileSync(configFile, { encoding: 'utf8' })); - return browser.executeScript(`window.sessionStorage.setItem(${key}, ${fileContent});`); + return browser.executeScript(`window.sessionStorage.setItem('app.extension.config', ${fileContent});`); } static resetExtensionConfig() { const defConfig = `${E2E_ROOT_PATH}/resources/extensibility-configs/${EXTENSIBILITY_CONFIGS.DEFAULT_EXTENSIONS_CONFIG}`; - return this.setSessionStorageFromConfig('"aca.extension.config"', defConfig); + return this.setSessionStorageFromConfig(defConfig); } static retryCall(fn: () => Promise, retry: number = 30, delay: number = 1000): Promise { @@ -71,8 +79,6 @@ export class Utils { static async waitUntilElementClickable(element: ElementFinder) { return await browser.wait(EC.elementToBeClickable(element), BROWSER_WAIT_TIMEOUT).catch(Error); - // static waitUntilElementClickable(element: ElementFinder) { - // return browser.wait(EC.elementToBeClickable(element), BROWSER_WAIT_TIMEOUT); } static async typeInField(elem: ElementFinder, value: string) { @@ -112,6 +118,10 @@ export class Utils { return browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); } + static pressTab() { + return browser.actions().sendKeys(protractor.Key.TAB).perform(); + } + static getBrowserLog() { return browser.manage().logs().get('browser'); } @@ -119,4 +129,5 @@ export class Utils { static formatDate(date: string) { return new Date(date).toLocaleDateString('en-US'); } + } diff --git a/extension.schema.json b/extension.schema.json index cc3e86642c..5d2786a0ba 100644 --- a/extension.schema.json +++ b/extension.schema.json @@ -173,7 +173,7 @@ }, "navBarLinkRef": { "type": "object", - "required": ["id", "icon", "title", "route"], + "required": ["id", "icon", "title"], "properties": { "id": { "description": "Unique identifier", @@ -199,6 +199,40 @@ "description": "Element order", "type": "number" }, + "children": { + "description": "Navigation children items", + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "id", + "title", + "route" + ], + "properties": { + "id": { + "description": "Unique identifier", + "type": "string" + }, + "title": { + "description": "Element title", + "type": "string" + }, + "route": { + "description": "Route reference identifier", + "type": "string" + } + }, + "not": { + "required": ["children"] + } + } + ] + }, + "minItems": 1 + }, "rules": { "description": "Element rules", "type": "object", @@ -209,7 +243,21 @@ } } } - } + }, + "oneOf": [ + { + "required": ["route"], + "not": { + "required": ["children"] + } + }, + { + "required": ["children"], + "not": { + "required": ["route"] + } + } + ] }, "navBarGroupRef": { "type": "object", @@ -515,12 +563,34 @@ "type": "boolean" } } + }, + "iconRef": { + "type": "object", + "required": ["id", "value"], + "properties": { + "id": { + "description": "Unique identifier. Must be in the format '[namespace]:[name]'.", + "type": "string" + }, + "value": { + "description": "Icon path relative to the application root.", + "type": "string" + }, + "disabled": { + "description": "Toggles the disabled state", + "type": "boolean" + } + } } }, "type": "object", - "required": ["$name", "$version"], + "required": ["$id", "$name", "$version", "$vendor", "$license", "$runtime"], "properties": { + "$id": { + "description": "Unique identifier", + "type": "string" + }, "$name": { "description": "Extension name", "type": "string" @@ -529,8 +599,21 @@ "description": "Extension version", "type": "string" }, + "$vendor": { + "description": "Extension owner", + "type": "string" + }, + "$license": { + "description": "Indicates the license of the extension.", + "type": "string" + }, + "$runtime": { + "description": "Minimal extension runtime supported by extension", + "type": "string" + }, "$description": { - "description": "Brief description on what the extension does" + "description": "Brief description on what the extension does", + "type": "string" }, "$references": { "description": "References to external files", @@ -538,7 +621,7 @@ "items": { "type": "string" }, - "minItems": 1, + "minItems": 0, "uniqueItems": true }, "rules": { @@ -563,6 +646,12 @@ "description": "Application-specific features and extensions", "type": "object", "properties": { + "icons": { + "description": "Custom icons", + "type": "array", + "items": { "$ref": "#/definitions/iconRef" }, + "minItems": 1 + }, "header": { "description": "Application header extensions", "type": "array", diff --git a/nginx.conf b/nginx.conf index ea9434b3de..c75236b3f0 100644 --- a/nginx.conf +++ b/nginx.conf @@ -18,6 +18,8 @@ http { gzip_proxied expired no-cache no-store private auth; gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + access_log off; + location / { try_files $uri $uri/ /index.html; } diff --git a/package-lock.json b/package-lock.json index 36000eca13..e25bbfd101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,144 +1,205 @@ { "name": "alfresco-content-app", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@alfresco/adf-content-services": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-2.6.0.tgz", - "integrity": "sha512-yho1Z67xRSdbjRxGmU5HZ9NjZCti7tNP1eqFS5w5qhgQUFIQx8UjeUy4AqxAySNuwb37IicXahu0heK1rE2K3g==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-2.6.1.tgz", + "integrity": "sha512-mQkACzwpY4Go4MIwAMnuYp8yaQ+baiHOPfX6nJ4i+CIVnViocw8uq/lgDmSSmM4vQRr7KITh1ZOsat6rgePg6g==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-2.6.0.tgz", - "integrity": "sha512-9cTV27M7nAtVOTWqQi9+LXIzUxzzfnGb8BegkC8ptrvSxQZy+Ep3bNyash3g38XyFHDzZYkvM4c2cassSg/Xdw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-2.6.1.tgz", + "integrity": "sha512-ehNQ6VNHWgj9CKrIKVuS5/7N/zqblImtXol9qZtOtHcPNvybBqdKfcbxpaedmi9eh9h4A9uEmtMJ1PvWCmfPQg==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-extensions": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-2.6.0.tgz", - "integrity": "sha512-9/NinDRC0pqNJYkX0U6rkWXMQmXpB5Efyx5y75mYdm7k3kQb7cxt/uGYThDVVl64QW4AT/mnd2oykHtjsgDCGw==", + "version": "3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6.tgz", + "integrity": "sha512-pAI4DoDMqek2G1sEFeHpgZ0OkUhSTmp95XWC+b3OoUroztwOC0y2a/PqLUvDgm5aTe4hVsA+r6IRMkp7aA1KAA==", "requires": { "tslib": "^1.9.0" } }, "@angular-devkit/architect": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.0.tgz", - "integrity": "sha512-rgyucHNPzJawBYqM9LSnOWNm9KM6E8ovzvQgzF8SePderqn6TGFAlrDvaZasgLtrihCAR//lNhKUm8w8ft3rgQ==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.11.0.tgz", + "integrity": "sha512-HiMrXZ6pj4OUHmDKnLj+CIzZmr92aklBvi20QBmHv6h82l/pSs9VG5R90Dr6zHZ04cKQgKaDFJTxNQld+hHUpw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", - "rxjs": "~6.2.0" - }, - "dependencies": { - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } + "@angular-devkit/core": "7.1.0", + "rxjs": "6.3.3" } }, "@angular-devkit/build-angular": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.8.0.tgz", - "integrity": "sha512-/UMI+f4qBCnDnhJOH/gcFUcKu8Cu062IM61NkWmDhJ9uSYrCDO0ZjHjWSOSo4AQoNRy+kCvbnc4PH9eR6L2nkQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.8.0", - "@angular-devkit/build-optimizer": "0.8.0", - "@angular-devkit/build-webpack": "0.8.0", - "@angular-devkit/core": "0.8.0", - "@ngtools/webpack": "6.2.0", - "ajv": "~6.4.0", - "autoprefixer": "^8.4.1", - "circular-dependency-plugin": "^5.0.2", - "clean-css": "^4.1.11", - "copy-webpack-plugin": "^4.5.2", - "file-loader": "^1.1.11", - "glob": "^7.0.3", - "html-webpack-plugin": "^3.0.6", - "istanbul": "^0.4.5", - "istanbul-instrumenter-loader": "^3.0.1", - "karma-source-map-support": "^1.2.0", - "less": "^3.7.1", - "less-loader": "^4.1.0", - "license-webpack-plugin": "^1.3.1", - "loader-utils": "^1.1.0", - "mini-css-extract-plugin": "~0.4.0", - "minimatch": "^3.0.4", - "node-sass": "^4.9.3", - "opn": "^5.1.0", - "parse5": "^4.0.0", - "portfinder": "^1.0.13", - "postcss": "^6.0.22", - "postcss-import": "^11.1.0", - "postcss-loader": "^2.1.5", - "postcss-url": "^7.3.2", - "raw-loader": "^0.5.1", - "rxjs": "~6.2.0", - "sass-loader": "^7.1.0", - "semver": "^5.5.0", - "source-map-loader": "^0.2.3", - "source-map-support": "^0.5.0", - "stats-webpack-plugin": "^0.6.2", - "style-loader": "^0.21.0", - "stylus": "^0.54.5", - "stylus-loader": "^3.0.2", - "tree-kill": "^1.2.0", - "uglifyjs-webpack-plugin": "^1.2.5", - "url-loader": "^1.0.1", - "webpack": "^4.15.1", - "webpack-dev-middleware": "^3.1.3", - "webpack-dev-server": "^3.1.4", - "webpack-merge": "^4.1.2", - "webpack-sources": "^1.1.0", - "webpack-subresource-integrity": "^1.1.0-rc.4" + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.11.0.tgz", + "integrity": "sha512-KNbVqApCfLNw7qG0i7nbirisePzcUBmqb5YJGMOEoB1pWASwGke2H/3NMTyJEDHtGDg5DdWuVvyoyXaGEKMoJg==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.11.0", + "@angular-devkit/build-optimizer": "0.11.0", + "@angular-devkit/build-webpack": "0.11.0", + "@angular-devkit/core": "7.1.0", + "@ngtools/webpack": "7.1.0", + "ajv": "6.5.3", + "autoprefixer": "9.3.1", + "circular-dependency-plugin": "5.0.2", + "clean-css": "4.2.1", + "copy-webpack-plugin": "4.5.4", + "file-loader": "2.0.0", + "glob": "7.1.3", + "istanbul": "0.4.5", + "istanbul-instrumenter-loader": "3.0.1", + "karma-source-map-support": "1.3.0", + "less": "3.8.1", + "less-loader": "4.1.0", + "license-webpack-plugin": "2.0.2", + "loader-utils": "1.1.0", + "mini-css-extract-plugin": "0.4.4", + "minimatch": "3.0.4", + "node-sass": "4.10.0", + "opn": "5.3.0", + "parse5": "4.0.0", + "portfinder": "1.0.17", + "postcss": "7.0.5", + "postcss-import": "12.0.0", + "postcss-loader": "3.0.0", + "raw-loader": "0.5.1", + "rxjs": "6.3.3", + "sass-loader": "7.1.0", + "semver": "5.5.1", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.9", + "speed-measure-webpack-plugin": "1.2.3", + "stats-webpack-plugin": "0.7.0", + "style-loader": "0.23.1", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "terser-webpack-plugin": "1.1.0", + "tree-kill": "1.2.0", + "webpack": "4.23.1", + "webpack-dev-middleware": "3.4.0", + "webpack-dev-server": "3.1.10", + "webpack-merge": "4.1.4", + "webpack-sources": "1.3.0", + "webpack-subresource-integrity": "1.1.0-rc.6" }, "dependencies": { "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "autoprefixer": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.3.1.tgz", + "integrity": "sha512-DY9gOh8z3tnCbJ13JIWaeQsoYncTGdsrgCceBaQSIL4nvdrLxgbRSBPevg2XbX7u4QCSfLheSJEEIUUSlkbx6Q==", + "dev": true, + "requires": { + "browserslist": "^4.3.3", + "caniuse-lite": "^1.0.30000898", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.5", + "postcss-value-parser": "^3.3.1" + } + }, + "caniuse-lite": { + "version": "1.0.30000912", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000912.tgz", + "integrity": "sha512-M3zAtV36U+xw5mMROlTXpAHClmPAor6GPKAMD5Yi7glCB5sbMPFtnQ3rGpk4XqPdUrrTIaVYSJZxREZWNy8QJg==", "dev": true }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, "requires": { - "tslib": "^1.9.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "node-sass": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.10.0.tgz", + "integrity": "sha512-fDQJfXszw6vek63Fe/ldkYXmRYK/QS6NbvM3i5oEo9ntPDy4XX7BcKZyTKv+/kSSxRtXXc7l+MSwEmYc0CSy6Q==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -155,96 +216,107 @@ "source-map": "^0.6.0" } }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "optional": true } } }, "@angular-devkit/build-ng-packagr": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.8.0.tgz", - "integrity": "sha512-noFWHRLASwO3fdj/vYsJ7tdA19+3J5D/ED4Z0TREjoB07oC9u2Op20JmN9jPhWOCCEswal7FRCgMk6cOhcSR2g==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.11.0.tgz", + "integrity": "sha512-+VO9LOFABIWEgtN92TkEiTwId++u1C+nBOHAbE+6ukfWeCnoBTHQoMX+HM92aklosSio53EuzRWiSkQphxbTcQ==", "dev": true, "requires": { - "@angular-devkit/architect": "0.8.0", - "@angular-devkit/core": "0.8.0", - "rxjs": "~6.2.0", - "semver": "^5.3.0" + "@angular-devkit/architect": "0.11.0", + "@angular-devkit/core": "7.1.0", + "rxjs": "6.3.3", + "semver": "5.5.1" }, "dependencies": { - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true } } }, "@angular-devkit/build-optimizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.8.0.tgz", - "integrity": "sha512-xg1Lh+sRZJdN4foQ1w3d2aen+Q08j+MTQntfa+MppJmGt94f7Om7cYC4tOUJM0vywrx9oGu3MLeFGi09MA5L2w==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "source-map": "^0.5.6", - "typescript": "~2.9.2", - "webpack-sources": "^1.1.0" - } - }, - "@angular-devkit/build-webpack": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.8.0.tgz", - "integrity": "sha512-l0UXiiWhRcj3wBuFiw3RzMRT0xTLZbadjRWZuvNnCZ/0GYQLM0bN3Lw/1xeuQhLJf8i39t9oYjZQsQ2MbcapCg==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.11.0.tgz", + "integrity": "sha512-a7nIw6bN/kO77NnWoLzuoEep8jVSDxDyXZZMjvv2+bdcnua1rsScuJKII5PjGIjIucLNUJRwdHQFovVDXRMCPQ==", "dev": true, "requires": { - "@angular-devkit/architect": "0.8.0", - "@angular-devkit/core": "0.8.0", - "rxjs": "~6.2.0" + "loader-utils": "1.1.0", + "source-map": "0.5.6", + "typescript": "3.1.6", + "webpack-sources": "1.2.0" }, "dependencies": { - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "webpack-sources": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", + "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } } } }, + "@angular-devkit/build-webpack": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.11.0.tgz", + "integrity": "sha512-AGkpHv9k9pjVEe1IihtHBWpYPSBYDEui5tFaXE6zEuXl8EbPRVW6fP4SpfEOefgCRwrUe3VP9+q5IlGgKVOXlg==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.11.0", + "@angular-devkit/core": "7.1.0", + "rxjs": "6.3.3" + } + }, "@angular-devkit/core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", - "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.0.tgz", + "integrity": "sha512-mR0YNRBEWfK3y5JfPmENw6Qy8kk6jaJTjDOso1uOwRKWQDe642tnK0P1HTmZ+WBgp+RhYD4pHbKePqOHw/tsdQ==", "dev": true, "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "~6.2.0", - "source-map": "^0.5.6" + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" }, "dependencies": { "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "anymatch": { @@ -456,12 +528,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -576,12 +642,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -609,58 +669,47 @@ "to-regex": "^3.0.2" } }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, "@angular-devkit/schematics": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.4.tgz", - "integrity": "sha512-Wt2JE/PnwtJiy8yCPEKP//1FnCumXiXX3HU/FosWufDtMga3qc2MWFwrNWlm4GCbpfNbBXj0LuJwfcwi27Hhdg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.1.0.tgz", + "integrity": "sha512-MIK6eT3x6EppUcz7KFwJ63z3gUVmi5dQPiN8p+kTpHE2SorZCQvQ6+YKUMw9VZ6WLEQOZYJfoQozKyEWllNlsw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.4", - "rxjs": "~6.2.0" + "@angular-devkit/core": "7.1.0", + "rxjs": "6.3.3" }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.4.tgz", - "integrity": "sha512-oqMHezbIZMApud9JZDupWaxJeczTA17hLFGJ1qyAaPBRADtjnuguygXLcBLzYYAhzHKstrHwPJ4R1jj3oG28Ow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.0.tgz", + "integrity": "sha512-mR0YNRBEWfK3y5JfPmENw6Qy8kk6jaJTjDOso1uOwRKWQDe642tnK0P1HTmZ+WBgp+RhYD4pHbKePqOHw/tsdQ==", "dev": true, "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "~6.2.0", - "source-map": "^0.5.6" + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" } }, "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "anymatch": { @@ -872,12 +921,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -992,12 +1035,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -1025,101 +1062,89 @@ "to-regex": "^3.0.2" } }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, "@angular/animations": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.10.tgz", - "integrity": "sha512-dd/lq7kw3uwfHPICan8psu2nthuUpp7PvMLuNIm0XxObZ4oNs0ls6uxKEDPnEkRKoGdiJpvmsyzZZN9ACMPEAA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.1.1.tgz", + "integrity": "sha512-iTNxhPPraCZsE4rgM23lguT1kDV4mfYAr+Bsi5J0+v9ZJA+VaKvi6eRW8ZGrx4/rDz6hzTnBn1jgPppHFbsOcw==", "requires": { "tslib": "^1.9.0" } }, "@angular/cdk": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.7.tgz", - "integrity": "sha512-18x0U66fLD5kGQWZ9n3nb75xQouXlWs7kUDaTd8HTrHpT1s2QIAqlLd1KxfrYiVhsEC2jPQaoiae7VnBlcvkBg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.1.0.tgz", + "integrity": "sha512-dY740pKcIRtKr6n6NomrgqfdEj988urTZ9I/bfJjxF5fdhnSjyhEvDlB55EHsrF+bTTZbZXRmv7AwOQ9GJnD9w==", "requires": { + "parse5": "^5.0.0", "tslib": "^1.7.1" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "optional": true + } } }, "@angular/cli": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.4.tgz", - "integrity": "sha512-Jl902wCgiV/tI3QM4W6aadEb2LPIS0t30wfAr+ikz7n2V3j3Ovf9iWM0ds4zZMreZfeoz4rCU/4FaKg1UbJvBA==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.8.4", - "@angular-devkit/core": "0.8.4", - "@angular-devkit/schematics": "0.8.4", - "@schematics/angular": "0.8.4", - "@schematics/update": "0.8.4", - "json-schema-traverse": "^0.4.1", - "opn": "^5.3.0", - "rxjs": "~6.2.0", - "semver": "^5.1.0", - "symbol-observable": "^1.2.0", - "yargs-parser": "^10.0.0" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.1.0.tgz", + "integrity": "sha512-G7WZvClrZjfo0VL6eFxwzqPffUQr3XbdkdCUcVbzJVnkFLrBG5Q2jFOJaZ4uFeRW4z5UM+8u/4N9N1Z6MH2QAQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.11.0", + "@angular-devkit/core": "7.1.0", + "@angular-devkit/schematics": "7.1.0", + "@schematics/angular": "7.1.0", + "@schematics/update": "0.11.0", + "inquirer": "6.2.0", + "opn": "5.3.0", + "semver": "5.5.1", + "symbol-observable": "1.2.0" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.4.tgz", - "integrity": "sha512-Xg/HgFgE3zu1jwI6quCRaGQK0dWyaK2GOYp2n1a+yQ8E+yngQqFW1EsdQE9L8EV1XtVL8njunZ/oOEL/KI6Oxw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.11.0.tgz", + "integrity": "sha512-HiMrXZ6pj4OUHmDKnLj+CIzZmr92aklBvi20QBmHv6h82l/pSs9VG5R90Dr6zHZ04cKQgKaDFJTxNQld+hHUpw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.4", - "rxjs": "~6.2.0" + "@angular-devkit/core": "7.1.0", + "rxjs": "6.3.3" } }, "@angular-devkit/core": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.4.tgz", - "integrity": "sha512-oqMHezbIZMApud9JZDupWaxJeczTA17hLFGJ1qyAaPBRADtjnuguygXLcBLzYYAhzHKstrHwPJ4R1jj3oG28Ow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.0.tgz", + "integrity": "sha512-mR0YNRBEWfK3y5JfPmENw6Qy8kk6jaJTjDOso1uOwRKWQDe642tnK0P1HTmZ+WBgp+RhYD4pHbKePqOHw/tsdQ==", "dev": true, "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "~6.2.0", - "source-map": "^0.5.6" + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" } }, "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" - }, - "dependencies": { - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - } + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "anymatch": { @@ -1331,12 +1356,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1478,156 +1497,282 @@ "to-regex": "^3.0.2" } }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, "@angular/common": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.10.tgz", - "integrity": "sha512-73xxTSYJNKfiJ7C1Ajg+sz5l8y+blb/vNgHYg7O3yem5zLBnfPpidJ1UGg4W4d2Y+jwUVJbZKh8SKJarqAJVUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.1.1.tgz", + "integrity": "sha512-SngekFx9v39sjgi9pON0Wehxpu+NdUk7OEebw4Fa8dKqTgydTkuhmnNH+9WQe264asoeCt51oufPRjIqMLNohA==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.10.tgz", - "integrity": "sha512-FPIb2j3zfoBwb6vo/u0gQeu70h8InGlSisBr3xMACs/35/pwB6kbQR+JQiUr0D7k6QApg7AuMkvq8aFNelg0aw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.1.1.tgz", + "integrity": "sha512-oJvBe8XZ+DXF/W/DxWBTbBcixJTuPeZWdkcZIGWhJoQP7K5GnGnj8ffP9Lp6Dh4TKv85awtC6OfIKhbHxa650Q==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.10.tgz", - "integrity": "sha512-GCWdyeNQSnF4RfzO4A0+WHsNEgxKpl5arg4ldLSWMNkj/DrhMD4TnmxhR+IVY+7ieMkUBwpcuWRnjdOdnbmV+w==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.1.1.tgz", + "integrity": "sha512-4NXlkDhOEQgaP3Agigqw93CvXJvsfnXa0xiglq9e/wjL+6XbtM9WcDb5lfRQz41N9RSkO3pEHGvKMweKZGgogA==", "dev": true, "requires": { + "canonical-path": "1.0.0", "chokidar": "^1.4.2", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "magic-string": "^0.25.0", "minimist": "^1.2.0", "reflect-metadata": "^0.1.2", - "tsickle": "^0.32.1" + "shelljs": "^0.8.1", + "source-map": "^0.6.1", + "tslib": "^1.9.0", + "yargs": "9.0.1" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", + "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } }, "@angular/core": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.10.tgz", - "integrity": "sha512-61l3rIQTVdT45eOf6/fBJIeVmV10mcrxqS4N/1OWkuDT29YSJTZSxGcv8QjAyyutuhcqWWpO6gVRkN07rWmkPg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.1.1.tgz", + "integrity": "sha512-Osig5SRgDRQ+Hec/liN7nq/BCJieB+4/pqRh9rFbOXezb2ptgRZqdXOXN8P17i4AwPVf308Mh55V0niJ5Eu3Rw==", "requires": { "tslib": "^1.9.0" } }, "@angular/flex-layout": { - "version": "6.0.0-beta.18", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-6.0.0-beta.18.tgz", - "integrity": "sha512-1Alv3YSIZYp0CTUIESIaSQLoSVyLzuNKPa5bGM/RzOmeSrndm5plVgI9wopGfJUDiwM18R97rq/4XjDvNT/+ig==", + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.19.tgz", + "integrity": "sha512-MXq+zZ6/s5/+GsL9fZ42mKL0LjZ/+L0sVU5FaQuSAJ57soLl5QAGWvdxVmROtqcHd3Htp35R49nKSZBJ0nfAjg==", "requires": { "tslib": "^1.7.1" } }, "@angular/forms": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.10.tgz", - "integrity": "sha512-zAPx2kMV1/FbP5DrY472Sd/ze1m+GS6T5ullZCtP392r62p2RkwzDCXieR51YiRJjZj3M6c3AcRND7PWBdXT7A==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.1.1.tgz", + "integrity": "sha512-yCWuPjpu23Wc3XUw7v/ACNn/e249oT0bYlM8aaMQ1F5OwrmmC4NJC12Rpl9Ihza61RIHIKzNcHVEgzc7WhcSag==", "requires": { "tslib": "^1.9.0" } }, "@angular/http": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.10.tgz", - "integrity": "sha512-LDsSqyexh8fj23y+G2oSGLWSZVhbxBBo2ehYHnRgH/jlp0pmZVLRaGgUMNSCVtZc1rxLzpEjZjtw+P+qlutAtw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.1.1.tgz", + "integrity": "sha512-pRk+c/kz9aJ8te5xzCxlPLpFnwB0d/E9YkOo3/ydaXF9vZw13RTzk00YyzJ41PDzJf8oPDdXtueTQ+vtJ7Srtw==", "requires": { "tslib": "^1.9.0" } }, "@angular/language-service": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.10.tgz", - "integrity": "sha512-nN29Ovomg21eL8acwOSUFAYwWFI1TuFwUgUu37ZssfVQrYdaV+BFx3yv3P0nKU90h3Hp+oIkWHd8U34UYrvBCg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.1.1.tgz", + "integrity": "sha512-X+5g20PMtNRGZIa3svMv4PLJdJehn4wqrS8nwOtzH5XkSn5vA3IxjsJVdSzAy2AN0/sKKJK5jmQorPtKO4saJg==", "dev": true }, "@angular/material": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.4.7.tgz", - "integrity": "sha512-SdNx7Xovi24Kw9eU6lkLhY/7f2M7L9F+/uh6XuPr4jbGgCUVVpeeVI5ztZhsZRbj1sN+/r1p5w8u62apWWl5Ww==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.1.0.tgz", + "integrity": "sha512-bgotNpSfGLjNZ1AcTyhs6XS7trF4I7UHwQmfa0l8y3Gf9plwErPDfQe2XqnayRyG9nTwHj9f1lQ45X5mr3/0/A==", "requires": { - "parse5": "^5.0.0", "tslib": "^1.7.1" - }, - "dependencies": { - "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", - "optional": true - } } }, "@angular/material-moment-adapter": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.7.tgz", - "integrity": "sha512-OGdDtpu/yRioOQXhJFCNuiOF2OgiL9VUj8ewFPi1lDtFGUFfVwU2h3hWkKLn+yuPW+DBVYla11tCNsn5dLElmA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-7.1.0.tgz", + "integrity": "sha512-4YBxsgunmbaTN+Ciefh2C3XVru1xP9OvbV1nMLz7lXvpd8bW8kuD213+rCwLOFwtpNdsGWhZYTXuhSrpoyYWhw==", "requires": { "tslib": "^1.7.1" } }, "@angular/platform-browser": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.10.tgz", - "integrity": "sha512-CB7pqMwtgb7KjdHDAJlsXcs0rrU+2xQVaoOaqEfJtUrKhtGMLaZh8Qoic5l92SoGattkOw7SYarAOsWlAsVfvw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.1.1.tgz", + "integrity": "sha512-I6OPjecynGJSbPtzu0gvEgSmIR6X6/xEAhg4L9PycW1ryjzptTC9klWRTWIqsIBqMxhVnY44uKLeRNrDwMOwyA==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.10.tgz", - "integrity": "sha512-DmBSUyFPoyKqkmBXyJ2CrP1oXDioeoBlPA8lmWUDUv2yBuoHIzIkdY/OkTZbdyu/QYa1hK2Jl9OlfoeoenKddg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.1.tgz", + "integrity": "sha512-ZIu48Vn4S6gjD7CMbGlKGaPQ8v9rYkWzlNYi4vTYzgiqKKNC3hqLsVESU3mSvr5oeQBxSIBidTdHSyafHFrA2w==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.10.tgz", - "integrity": "sha512-tekI3dkdvd65oMoxjjgRA+16uDgPUBWHhYxids6pgO8vobZNtCo8VaVlcDyLUhdmtS5kONELx0iL5E2M0Y2Bag==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@denysvuika/aca-dev-tools": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@denysvuika/aca-dev-tools/-/aca-dev-tools-0.1.5.tgz", - "integrity": "sha512-O+r6L+yZX1TB8ORaGpjowlcWq/1u/HeSpalQtfBJyuyUzWo0NdWNQtWfs2wQ5e8Bvwyu+u8qAIxYJgmIzqWL1w==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.1.1.tgz", + "integrity": "sha512-jbnqEq/1iDBkeH8Vn13hauGPTzhwllWM+MLfmdNGTiMzGRx4pmkWa57seDOeBF/GNYBL9JjkWTCrkKFAc2FJKw==", "requires": { - "@ngstack/code-editor": "^0.4.3", - "showdown": "^1.8.6", "tslib": "^1.9.0" } }, @@ -1648,32 +1793,24 @@ } }, "@ngrx/effects": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-6.1.0.tgz", - "integrity": "sha512-euR/VHn0cEOkdZ3SqCzUvYHT0P5FcTBm4/xSE8gH85ORP0//owD3L6o8X3etfWLRS+BAELk1JcXkFeAVG9CXiQ==" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-6.1.2.tgz", + "integrity": "sha512-RUuQ5/7ofxGEZnRRdlC1oE9ugVlTYGm92MVj7c6IirHrVN9W5yQjjMTYEYceVCDOYsiXP7Pyw0dcPp6J5wD2EQ==" }, "@ngrx/router-store": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-6.1.0.tgz", - "integrity": "sha512-uMC0TykeVi0fPuX+iwanCAX5mBrb+LHgJGtI4CL+sEjviXe9C/84UDmApoG9Goe0xuCtALqOqGDP0UoNSxVA3g==" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-6.1.2.tgz", + "integrity": "sha512-sj083ZYrx0aY+vU/t8Ub0KYDHcMpatXJIOJR/eDNSuH54fPiBM9MrdI3hs/XHoXHxSaHOJoZ7f6I8XcUeptxyA==" }, "@ngrx/store": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-6.1.0.tgz", - "integrity": "sha512-H5BGym1WtAX84/R4pTQ2MrrP87qYfXc6CoPghCZCK9LYxCodsI7KeQfpyNCg5qapxdH2EDqlHXTBJfMTLRiRGg==" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-6.1.2.tgz", + "integrity": "sha512-W9MbXrwhIRmN1BlINF9BT+rHR046e1HNk7GqykcDJrK9wW74PJW3aE5iuPb2sTPipBMjPHsXzc73E4U/+OTAyw==" }, "@ngrx/store-devtools": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-6.1.0.tgz", - "integrity": "sha512-Uc0g/NCbJIbzvIMuCy3skiZVD5hoIrOAAvaninXkVHt7bXpbsSAdvJlmnozuGQqTbC0UQhYRwAR7InRSrzIbMQ==" - }, - "@ngstack/code-editor": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@ngstack/code-editor/-/code-editor-0.4.3.tgz", - "integrity": "sha512-uH1f/GUKi6r2f2WTQ2dEAgPbkMEkMbzn9hqHHetrNTssP0i4Vosob8Y0Ff21J7xU6a5xq2vp+XqZzVOEsb3hlA==", - "requires": { - "tslib": "^1.9.0" - } + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-6.1.2.tgz", + "integrity": "sha512-hvWMKcRIAtAFb2lb4woRenPHPgOiLFjy8R2PtCiw4uP3WrBVB4JHqUuP230/iRMcU5XmySp+LhNqhkk1zsoUqQ==" }, "@ngtools/json-schema": { "version": "1.1.0", @@ -1682,24 +1819,32 @@ "dev": true }, "@ngtools/webpack": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.2.0.tgz", - "integrity": "sha512-X25EhXPnb5XKfnyK1WHKXoH59GH3ciUP4s9ZqsH3ao9f1FMMDdyL1Os9kVxKsC5WZYsm674iJMn3iZPTHY+bzg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.1.0.tgz", + "integrity": "sha512-U4+2fPEEdvQN6/SmdlNYiuuwWbSEUP3Rpfbkzj7hYOHMQHnWA90u6EfZLjoyE77qqhF3EGXszjmZnYls78/c7Q==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", - "rxjs": "~6.2.0", - "tree-kill": "^1.0.0", - "webpack-sources": "^1.1.0" + "@angular-devkit/core": "7.1.0", + "enhanced-resolve": "4.1.0", + "rxjs": "6.3.3", + "tree-kill": "1.2.0", + "webpack-sources": "1.2.0" }, "dependencies": { - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", + "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" } } } @@ -1713,47 +1858,48 @@ } }, "@phenomnomnominal/tsquery": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-2.1.0.tgz", - "integrity": "sha512-GfVvugSE0ZJTrscg6UqVpi0Kg+C/w+Alm5wGI+mzjsBLvf3ggiBSNqOzEXwb1MHpwbdfOQ8F2wgRWjCQ/8Jfzw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-3.0.0.tgz", + "integrity": "sha512-SW8lKitBHWJ9fAYkJ9kJivuctwNYCh3BUxLdH0+XiR1GPBiu+7qiZzh8p8jqlj1LgVC1TbvfNFroaEsmYlL8Iw==", "dev": true, "requires": { "esquery": "^1.0.1" } }, "@schematics/angular": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.4.tgz", - "integrity": "sha512-a85I7I7gYsmHRRnoNsCAa8ZlznIqnFjnOabVO6Z41XjUh57kOeWfLl7BQpd1CubvHu7q+juQXjBu1J+F4H/cOw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.1.0.tgz", + "integrity": "sha512-BLRTHlhYXgP49OwDyoolwolf7LqxOAPuc8lpgH0HEmYjkXmufZ4urngyFKY1IuBwaAR4PLjDx3U/ofszyV0taw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.4", - "@angular-devkit/schematics": "0.8.4", - "typescript": ">=2.6.2 <2.10" + "@angular-devkit/core": "7.1.0", + "@angular-devkit/schematics": "7.1.0", + "typescript": "3.1.6" }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.4.tgz", - "integrity": "sha512-oqMHezbIZMApud9JZDupWaxJeczTA17hLFGJ1qyAaPBRADtjnuguygXLcBLzYYAhzHKstrHwPJ4R1jj3oG28Ow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.0.tgz", + "integrity": "sha512-mR0YNRBEWfK3y5JfPmENw6Qy8kk6jaJTjDOso1uOwRKWQDe642tnK0P1HTmZ+WBgp+RhYD4pHbKePqOHw/tsdQ==", "dev": true, "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "~6.2.0", - "source-map": "^0.5.6" + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" } }, "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "anymatch": { @@ -1965,12 +2111,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2085,12 +2225,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -2118,62 +2252,53 @@ "to-regex": "^3.0.2" } }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, "@schematics/update": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.4.tgz", - "integrity": "sha512-iJkJZurP0FV1PPRUBmlrxxdp4JgDnzm573y4WAnleHb5IJlDME79WoGC8wNemxeuHQUMvZbZQrzgmdmzqU83GA==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.8.4", - "@angular-devkit/schematics": "0.8.4", - "npm-registry-client": "^8.5.1", - "rxjs": "~6.2.0", - "semver": "^5.3.0", - "semver-intersect": "^1.1.2" + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.11.0.tgz", + "integrity": "sha512-Zrt4MQOM8DjK7fYVrzx08KhQ7jSj/at0/uF+Ca+ObZJIiC67IY8NXlc1TETXpB4A2UYrclvc9mTpZrvgIoEcYA==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.1.0", + "@angular-devkit/schematics": "7.1.0", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "pacote": "9.1.1", + "rxjs": "6.3.3", + "semver": "5.5.1", + "semver-intersect": "1.4.0" }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.4.tgz", - "integrity": "sha512-oqMHezbIZMApud9JZDupWaxJeczTA17hLFGJ1qyAaPBRADtjnuguygXLcBLzYYAhzHKstrHwPJ4R1jj3oG28Ow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.0.tgz", + "integrity": "sha512-mR0YNRBEWfK3y5JfPmENw6Qy8kk6jaJTjDOso1uOwRKWQDe642tnK0P1HTmZ+WBgp+RhYD4pHbKePqOHw/tsdQ==", "dev": true, "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "~6.2.0", - "source-map": "^0.5.6" + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" } }, "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "anymatch": { @@ -2385,12 +2510,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2505,12 +2624,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -2538,23 +2651,17 @@ "to-regex": "^3.0.2" } }, - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, @@ -2610,196 +2717,203 @@ "dev": true }, "@webassemblyjs/ast": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", - "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.10.tgz", + "integrity": "sha512-wTUeaByYN2EA6qVqhbgavtGc7fLTOx0glG2IBsFlrFG51uXIGlYBTyIZMf4SPLo3v1bgV/7lBN3l7Z0R6Hswew==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/wast-parser": "1.5.13", - "debug": "^3.1.0", - "mamacro": "^0.0.3" + "@webassemblyjs/helper-module-context": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/wast-parser": "1.7.10" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", - "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.10.tgz", + "integrity": "sha512-gMsGbI6I3p/P1xL2UxqhNh1ga2HCsx5VBB2i5VvJFAaqAjd2PBTRULc3BpTydabUQEGlaZCzEUQhLoLG7TvEYQ==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", - "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.10.tgz", + "integrity": "sha512-DoYRlPWtuw3yd5BOr9XhtrmB6X1enYF0/54yNvQWGXZEPDF5PJVNI7zQ7gkcKfTESzp8bIBWailaFXEK/jjCsw==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", - "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", - "dev": true, - "requires": { - "debug": "^3.1.0" - } + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.10.tgz", + "integrity": "sha512-+RMU3dt/dPh4EpVX4u5jxsOlw22tp3zjqE0m3ftU2tsYxnPULb4cyHlgaNd2KoWuwasCQqn8Mhr+TTdbtj3LlA==", + "dev": true }, "@webassemblyjs/helper-code-frame": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", - "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.10.tgz", + "integrity": "sha512-UiytbpKAULOEab2hUZK2ywXen4gWJVrgxtwY3Kn+eZaaSWaRM8z/7dAXRSoamhKFiBh1uaqxzE/XD9BLlug3gw==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.5.13" + "@webassemblyjs/wast-printer": "1.7.10" } }, "@webassemblyjs/helper-fsm": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", - "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.10.tgz", + "integrity": "sha512-w2vDtUK9xeSRtt5+RnnlRCI7wHEvLjF0XdnxJpgx+LJOvklTZPqWkuy/NhwHSLP19sm9H8dWxKeReMR7sCkGZA==", "dev": true }, "@webassemblyjs/helper-module-context": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", - "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "mamacro": "^0.0.3" - } + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.10.tgz", + "integrity": "sha512-yE5x/LzZ3XdPdREmJijxzfrf+BDRewvO0zl8kvORgSWmxpRrkqY39KZSq6TSgIWBxkK4SrzlS3BsMCv2s1FpsQ==", + "dev": true }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", - "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.10.tgz", + "integrity": "sha512-u5qy4SJ/OrxKxZqJ9N3qH4ZQgHaAzsopsYwLvoWJY6Q33r8PhT3VPyNMaJ7ZFoqzBnZlCcS/0f4Sp8WBxylXfg==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", - "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.10.tgz", + "integrity": "sha512-Ecvww6sCkcjatcyctUrn22neSJHLN/TTzolMGG/N7S9rpbsTZ8c6Bl98GpSpV77EvzNijiNRHBG0+JO99qKz6g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "debug": "^3.1.0" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10" } }, "@webassemblyjs/ieee754": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", - "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.10.tgz", + "integrity": "sha512-HRcWcY+YWt4+s/CvQn+vnSPfRaD4KkuzQFt5MNaELXXHSjelHlSEA8ZcqT69q0GTIuLWZ6JaoKar4yWHVpZHsQ==", "dev": true, "requires": { - "ieee754": "^1.1.11" + "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", - "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.10.tgz", + "integrity": "sha512-og8MciYlA8hvzCLR71hCuZKPbVBfLQeHv7ImKZ4nlyxrYbG7uJHYtHiHu6OV9SqrGuD03H/HtXC4Bgdjfm9FHw==", "dev": true, "requires": { - "long": "4.0.0" - }, - "dependencies": { - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true - } + "@xtuc/long": "4.2.1" } }, "@webassemblyjs/utf8": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", - "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.10.tgz", + "integrity": "sha512-Ng6Pxv6siyZp635xCSnH3mKmIFgqWPCcGdoo0GBYgyGdxu7cUj4agV7Uu1a8REP66UYUFXJLudeGgd4RvuJAnQ==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", - "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/helper-wasm-section": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "@webassemblyjs/wasm-opt": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", - "@webassemblyjs/wast-printer": "1.5.13", - "debug": "^3.1.0" + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.10.tgz", + "integrity": "sha512-e9RZFQlb+ZuYcKRcW9yl+mqX/Ycj9+3/+ppDI8nEE/NCY6FoK8f3dKBcfubYV/HZn44b+ND4hjh+4BYBt+sDnA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/helper-wasm-section": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10", + "@webassemblyjs/wasm-opt": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10", + "@webassemblyjs/wast-printer": "1.7.10" } }, "@webassemblyjs/wasm-gen": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", - "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.10.tgz", + "integrity": "sha512-M0lb6cO2Y0PzDye/L39PqwV+jvO+2YxEG5ax+7dgq7EwXdAlpOMx1jxyXJTScQoeTpzOPIb+fLgX/IkLF8h2yw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/ieee754": "1.5.13", - "@webassemblyjs/leb128": "1.5.13", - "@webassemblyjs/utf8": "1.5.13" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/ieee754": "1.7.10", + "@webassemblyjs/leb128": "1.7.10", + "@webassemblyjs/utf8": "1.7.10" } }, "@webassemblyjs/wasm-opt": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", - "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.10.tgz", + "integrity": "sha512-R66IHGCdicgF5ZliN10yn5HaC7vwYAqrSVJGjtJJQp5+QNPBye6heWdVH/at40uh0uoaDN/UVUfXK0gvuUqtVg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", - "debug": "^3.1.0" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10" } }, "@webassemblyjs/wasm-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", - "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.10.tgz", + "integrity": "sha512-AEv8mkXVK63n/iDR3T693EzoGPnNAwKwT3iHmKJNBrrALAhhEjuPzo/lTE4U7LquEwyvg5nneSNdTdgrBaGJcA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-api-error": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/ieee754": "1.5.13", - "@webassemblyjs/leb128": "1.5.13", - "@webassemblyjs/utf8": "1.5.13" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-api-error": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/ieee754": "1.7.10", + "@webassemblyjs/leb128": "1.7.10", + "@webassemblyjs/utf8": "1.7.10" } }, "@webassemblyjs/wast-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", - "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.10.tgz", + "integrity": "sha512-YTPEtOBljkCL0VjDp4sHe22dAYSm3ZwdJ9+2NTGdtC7ayNvuip1wAhaAS8Zt9Q6SW9E5Jf5PX7YE3XWlrzR9cw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/floating-point-hex-parser": "1.5.13", - "@webassemblyjs/helper-api-error": "1.5.13", - "@webassemblyjs/helper-code-frame": "1.5.13", - "@webassemblyjs/helper-fsm": "1.5.13", - "long": "^3.2.0", - "mamacro": "^0.0.3" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/floating-point-hex-parser": "1.7.10", + "@webassemblyjs/helper-api-error": "1.7.10", + "@webassemblyjs/helper-code-frame": "1.7.10", + "@webassemblyjs/helper-fsm": "1.7.10", + "@xtuc/long": "4.2.1" } }, "@webassemblyjs/wast-printer": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", - "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.10.tgz", + "integrity": "sha512-mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/wast-parser": "1.5.13", - "long": "^3.2.0" + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/wast-parser": "1.7.10", + "@xtuc/long": "4.2.1" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" } }, "abbrev": { @@ -2819,9 +2933,9 @@ } }, "acorn": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", - "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true }, "acorn-dynamic-import": { @@ -2854,6 +2968,15 @@ "es6-promisify": "^5.0.0" } }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, "ajv": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", @@ -2877,9 +3000,9 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" }, "alfresco-js-api": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.6.0.tgz", - "integrity": "sha512-ua+RBHViTeWiFoL3ncPO68GVHsUUQth8LxutuxvGA0Sbino1oJi/CcSiHjIungSI8G23Im9X7e0x6FP9XcuTdA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.6.1.tgz", + "integrity": "sha512-E1maHlxlFS3DAmYWG9ueerMWgrcbJSVFO52Bfk2XGx3atEnH3iBFuG0ZczfCJCGIbtv22VliR5qQ90Cufuzhkw==", "requires": { "event-emitter": "0.3.4", "superagent": "3.8.2" @@ -2955,9 +3078,15 @@ } }, "ansi-colors": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.0.5.tgz", - "integrity": "sha512-VVjWpkfaphxUBFarydrQ3n26zX5nIK7hcbT3/ielrvwDDyBBjuh2vuSw1P9zkPq0cfqvdw7lkYHnu+OLSfIBsg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.1.tgz", + "integrity": "sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ==", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, "ansi-html": { @@ -2969,7 +3098,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "3.2.1", @@ -3222,16 +3352,16 @@ "dev": true }, "autoprefixer": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", - "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.1.5.tgz", + "integrity": "sha512-kk4Zb6RUc58ld7gdosERHMF3DzIYJc2fp5sX46qEsGXQQy5bXsu8qyLjoxuY1NuQ/cJuCYnx99BfjwnRggrYIw==", "dev": true, "requires": { - "browserslist": "^3.2.8", - "caniuse-lite": "^1.0.30000864", + "browserslist": "^4.1.0", + "caniuse-lite": "^1.0.30000884", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^6.0.23", + "postcss": "^7.0.2", "postcss-value-parser": "^3.2.3" } }, @@ -3610,12 +3740,6 @@ "multicast-dns-service-types": "^1.1.0" } }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -3762,13 +3886,37 @@ } }, "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.3.3.tgz", + "integrity": "sha512-6h84UD1mmHeuQ9IucX6yzBc+KBYcBBTLYt2CXtY7GYCra6iE5kOm7oM+zuGw/0tjGtbJxjm58OvxSBmogEMCRQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" + "caniuse-lite": "^1.0.30000898", + "electron-to-chromium": "^1.3.81", + "node-releases": "^1.0.0-alpha.15" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30000899", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000899.tgz", + "integrity": "sha512-enC3zKfUCJxxwvUIsBkbHd54CtJw1KtIWvrK0JZxWD/fEN2knHaai45lndJ4xXAkyRAPyk60J3yagkKDWhfeMA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.82", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.82.tgz", + "integrity": "sha512-NI4nB2IWGcU4JVT1AE8kBb/dFor4zjLHMLsOROPahppeHrR0FG5uslxMmkp/thO1MvPjM2xhlKoY29/I60s0ew==", + "dev": true + }, + "node-releases": { + "version": "1.0.0-alpha.15", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.15.tgz", + "integrity": "sha512-hKG6hd/g6a9OV/ARt2qrxbRhe/4WEMFohTLOB9PNyTYvvI59gICZFzt9/mMgpYUTts06qXlN8H6UjfbIRdnW8A==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + } } }, "browserstack": { @@ -3905,22 +4053,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } + "dev": true }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true }, "camelcase-keys": { "version": "2.1.0", @@ -3946,6 +4085,12 @@ "integrity": "sha512-cXKbYwpxBLd7qHyej16JazPoUacqoVuDhvR61U7Fr5vSxMUiodzcYa1rQYRYfZ5GexV03vGZHd722vNPLjPJGQ==", "dev": true }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, "capture-stack-trace": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", @@ -3980,6 +4125,12 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -3998,9 +4149,9 @@ } }, "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true }, "chrome-remote-interface": { @@ -4031,9 +4182,9 @@ } }, "ci-info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", - "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", "dev": true }, "cipher-base": { @@ -4110,6 +4261,21 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -4165,12 +4331,13 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "codelyzer": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.4.4.tgz", - "integrity": "sha512-JgFMudx0n50IuE/ydAfnkksCwQkWSVWgYvhDPHZgDUbmsiYC22VuEXKu5l8Hhx9UJsLgjWDLjTAFGj2WaW5DUA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", + "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", "dev": true, "requires": { "app-root-path": "^2.1.0", @@ -4267,12 +4434,20 @@ "dev": true }, "compressible": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz", - "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", + "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", "dev": true, "requires": { - "mime-db": ">= 1.34.0 < 2" + "mime-db": ">= 1.36.0 < 2" + }, + "dependencies": { + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + } } }, "compression": { @@ -4476,9 +4651,9 @@ "dev": true }, "copy-webpack-plugin": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", - "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.4.tgz", + "integrity": "sha512-0lstlEyj74OAtYMrDxlNZsU7cwFijAI3Ofz2fD6Mpo9r4xCv4yegfa3uHIKvZY1NSuOtE9nvG6TAhJ+uz9gDaQ==", "dev": true, "requires": { "cacache": "^10.0.4", @@ -4805,22 +4980,10 @@ "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", "dev": true }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", "dev": true, "requires": { "cssesc": "^0.1.0", @@ -4828,12 +4991,6 @@ "regexpu-core": "^1.0.0" } }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, "cssauron": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", @@ -4924,7 +5081,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decode-uri-component": { "version": "0.2.0", @@ -5007,15 +5165,6 @@ } } }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -5123,6 +5272,12 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "dev": true + }, "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", @@ -5212,23 +5367,6 @@ "buffer-indexof": "^1.0.0" } }, - "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", - "dev": true, - "requires": { - "utila": "~0.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, "dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -5241,55 +5379,12 @@ "void-elements": "^2.0.0" } }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -5306,9 +5401,9 @@ "dev": true }, "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -5334,18 +5429,6 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.63.tgz", - "integrity": "sha512-Ec35NNY040HKuSxMAzBMgz/uUI78amSWpBUD9x2gN7R7gkb/wgAcClngWklcLP0/lm/g0UUYHnC/tUIlZj8UvQ==", - "dev": true - }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", @@ -5372,6 +5455,15 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -5444,10 +5536,10 @@ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", "dev": true }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", "dev": true }, "errno": { @@ -5468,30 +5560,6 @@ "is-arrayish": "^0.2.1" } }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, "es5-ext": { "version": "0.10.45", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", @@ -5697,12 +5765,12 @@ "dev": true }, "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", "dev": true, "requires": { - "original": ">=0.0.5" + "original": "^1.0.0" } }, "evp_bytestokey": { @@ -5719,6 +5787,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -5733,6 +5802,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -5810,14 +5880,14 @@ } }, "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", "dev": true, "requires": { "accepts": "~1.3.5", "array-flatten": "1.1.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "content-disposition": "0.5.2", "content-type": "~1.0.4", "cookie": "0.3.1", @@ -5834,10 +5904,10 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", @@ -5853,6 +5923,24 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5862,17 +5950,17 @@ "ms": "2.0.0" } }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } } } }, @@ -5902,6 +5990,28 @@ } } }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -5934,9 +6044,9 @@ "dev": true }, "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", "dev": true }, "faye-websocket": { @@ -5948,14 +6058,42 @@ "websocket-driver": ">=0.5.1" } }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", + "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", "dev": true, "requires": { "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } } }, "filename-regex": { @@ -6034,6 +6172,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, "requires": { "locate-path": "^2.0.0" } @@ -6144,6 +6283,15 @@ "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -6769,12 +6917,6 @@ "rimraf": "2" } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -6800,6 +6942,12 @@ "globule": "^1.0.0" } }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, "gensequence": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-2.1.1.tgz", @@ -6809,7 +6957,8 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true }, "get-stdin": { "version": "4.0.1", @@ -6820,7 +6969,8 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true }, "get-value": { "version": "2.0.6", @@ -7078,15 +7228,6 @@ } } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -7211,12 +7352,6 @@ "minimalistic-assert": "^1.0.1" } }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -7242,130 +7377,37 @@ "requires": { "parse-passwd": "^1.0.0" } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.20", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz", - "integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.1.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - } - } - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -7385,9 +7427,9 @@ } }, "http-parser-js": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", - "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", "dev": true }, "http-proxy": { @@ -7401,6 +7443,16 @@ "requires-port": "^1.0.0" } }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, "http-proxy-middleware": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", @@ -7740,6 +7792,15 @@ "debug": "^3.1.0" } }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, "hunspell-reader": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hunspell-reader/-/hunspell-reader-2.0.3.tgz", @@ -7781,6 +7842,15 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -7936,6 +8006,60 @@ "integrity": "sha512-zHI+E+dM0PXix5FFTO1Y4/UOyAzE7zG1l/QwAn4jchTThOoBq+UYRFK4AVG7lQgFL+go62SbrzSsjXy9DFEZUg==", "dev": true }, + "inquirer": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "internal-ip": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", @@ -7946,6 +8070,12 @@ "ipaddr.js": "^1.5.2" } }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -7958,7 +8088,8 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true }, "ip": { "version": "1.1.5", @@ -8017,19 +8148,13 @@ "builtin-modules": "^1.0.0" } }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, "is-ci": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.0.tgz", - "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "requires": { - "ci-info": "^1.3.0" + "ci-info": "^1.5.0" } }, "is-data-descriptor": { @@ -8041,12 +8166,6 @@ "kind-of": "^3.0.2" } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -8112,6 +8231,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8215,21 +8335,18 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", @@ -8239,12 +8356,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-typedarray": { @@ -8297,7 +8409,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "2.1.0", @@ -8549,15 +8662,6 @@ "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", "dev": true }, - "jasmine-diff": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/jasmine-diff/-/jasmine-diff-0.1.3.tgz", - "integrity": "sha1-k8zC3MQQKMXd1GBlWAdIOfLe6qg=", - "dev": true, - "requires": { - "diff": "^3.2.0" - } - }, "jasmine-reporters": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-2.3.2.tgz", @@ -8724,6 +8828,12 @@ "graceful-fs": "^4.1.6" } }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -9261,183 +9371,78 @@ "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "less": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/less/-/less-3.8.0.tgz", - "integrity": "sha512-746DPDyL+Wsjo7h/Z3t+A3Mg/mpDTaxW4puZyLhCQJjWJJvHggN735orjuCLIYgo7jKqv1zWLiQrxkuUOg5oGA==", - "dev": true, - "requires": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "optional": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } - }, + } + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.8.1.tgz", + "integrity": "sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9496,12 +9501,12 @@ } }, "license-webpack-plugin": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.4.0.tgz", - "integrity": "sha512-iwuNFMWbXS76WiQXJBTs8/7Tby4NQnY8AIkBMuJG5El79UT8zWrJQMfpW+KRXt4Y2Bs5uk+Myg/MO7ROSF8jzA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.0.2.tgz", + "integrity": "sha512-GsomZw5VoT20ST8qH2tOjBgbyhn6Pgs9M94g0mbvfBIV1VXufm1iKY+4dbgfTObj1Mp6nSRE3Zf74deOZr0KwA==", "dev": true, "requires": { - "ejs": "^2.5.7" + "webpack-sources": "^1.2.0" } }, "lie": { @@ -9535,9 +9540,9 @@ } }, "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz", + "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==", "dev": true }, "loader-utils": { @@ -9554,6 +9559,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -9614,12 +9620,6 @@ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", - "dev": true - }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -9645,12 +9645,6 @@ "signal-exit": "^3.0.0" } }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -9661,18 +9655,19 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, "magic-string": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", - "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz", + "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==", "dev": true, "requires": { - "vlq": "^0.2.2" + "sourcemap-codec": "^1.4.1" } }, "make-dir": { @@ -9690,16 +9685,90 @@ "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true + "make-fetch-happen": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", + "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + }, + "dependencies": { + "cacache": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.1.tgz", + "integrity": "sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + } + } }, "map-age-cleaner": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", - "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { "p-defer": "^1.0.0" @@ -9733,13 +9802,14 @@ "dev": true }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "media-typer": { @@ -9752,6 +9822,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, "requires": { "mimic-fn": "^1.0.0" } @@ -9855,12 +9926,13 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true }, "mini-css-extract-plugin": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.2.tgz", - "integrity": "sha512-ots7URQH4wccfJq9Ssrzu2+qupbncAce4TmTzunI9CIwlQMp2XI+WNUw6xWF6MMAGAm1cbUVINrSjATaVMyKXg==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz", + "integrity": "sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ==", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -9916,6 +9988,33 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", + "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, "mississippi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", @@ -10030,6 +10129,12 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", @@ -10082,9 +10187,9 @@ "dev": true }, "neo-async": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", - "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, "next-tick": { @@ -10093,9 +10198,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "ng-packagr": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.1.1.tgz", - "integrity": "sha512-sl3tsc5uT0azDJNAYcjgW+5HLJRDon2AzdvShNJXakLYo3srdgR6lsw9W5TZB6/IJwWtuCAGSCI4UzdV5wuDjw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.4.0.tgz", + "integrity": "sha512-dLpC/kmQsdbkL96ZclGjNRhq/J4MwpPKwPYNom74lvXqFC2jbbT/fnwmxX9WKXjvE8MEGsg2D2x8MsRURiNscg==", "dev": true, "requires": { "@ngtools/json-schema": "^1.1.0", @@ -10116,8 +10221,9 @@ "postcss-url": "^8.0.0", "read-pkg-up": "^4.0.0", "rimraf": "^2.6.1", - "rollup": "^0.64.0", + "rollup": "^0.66.0", "rollup-plugin-commonjs": "^9.1.3", + "rollup-plugin-json": "^3.1.0", "rollup-plugin-node-resolve": "^3.0.0", "rollup-plugin-sourcemaps": "^0.4.2", "rxjs": "^6.0.0", @@ -10148,20 +10254,6 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "autoprefixer": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.1.5.tgz", - "integrity": "sha512-kk4Zb6RUc58ld7gdosERHMF3DzIYJc2fp5sX46qEsGXQQy5bXsu8qyLjoxuY1NuQ/cJuCYnx99BfjwnRggrYIw==", - "dev": true, - "requires": { - "browserslist": "^4.1.0", - "caniuse-lite": "^1.0.30000884", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.2", - "postcss-value-parser": "^3.2.3" - } - }, "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", @@ -10191,17 +10283,6 @@ } } }, - "browserslist": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.1.1.tgz", - "integrity": "sha512-VBorw+tgpOtZ1BYhrVSVTzTt/3+vSE3eFUh0N2GCFK1HffceOaf32YS/bs6WiFhjDAblAFrx85jMy3BG9fBK2Q==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000884", - "electron-to-chromium": "^1.3.62", - "node-releases": "^1.0.0-alpha.11" - } - }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -10543,12 +10624,6 @@ "to-regex": "^3.0.2" } }, - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - }, "p-limit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", @@ -10583,30 +10658,6 @@ "json-parse-better-errors": "^1.0.1" } }, - "postcss": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.2.tgz", - "integrity": "sha512-fmaUY5370keLUTx+CnwRxtGiuFTcNBLQBqr1oE3WZ/euIYmGAo0OAgOhVJ3ByDnVmOR3PK+0V9VebzfjRIUcqw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "postcss-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-8.0.0.tgz", - "integrity": "sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw==", - "dev": true, - "requires": { - "mime": "^2.3.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.0", - "postcss": "^7.0.2", - "xxhashjs": "^0.2.1" - } - }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -10628,12 +10679,6 @@ "read-pkg": "^3.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -10648,20 +10693,22 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, "node-ensure": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=" }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, "node-forge": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", @@ -10735,15 +10782,6 @@ } } }, - "node-releases": { - "version": "1.0.0-alpha.11", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.11.tgz", - "integrity": "sha512-CaViu+2FqTNYOYNihXa5uPS/zry92I3vPU4nCB6JB3OeZ2UGtOpF5gRwuN4+m3hbEcL47bOXyun1jX2iC+3uEQ==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, "node-sass": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz", @@ -10871,6 +10909,12 @@ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, + "npm-bundled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", + "dev": true + }, "npm-package-arg": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", @@ -10883,30 +10927,46 @@ "validate-npm-package-name": "^3.0.0" } }, - "npm-registry-client": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz", - "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", + "npm-packlist": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", + "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-fetch": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz", + "integrity": "sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==", "dev": true, "requires": { - "concat-stream": "^1.5.2", - "graceful-fs": "^4.1.6", - "normalize-package-data": "~1.0.1 || ^2.0.0", - "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", - "npmlog": "2 || ^3.1.0 || ^4.0.0", - "once": "^1.3.3", - "request": "^2.74.0", - "retry": "^0.10.0", - "safe-buffer": "^5.1.1", - "semver": "2 >=2.2.1 || 3.x || 4 || 5", - "slide": "^1.1.3", - "ssri": "^5.2.4" + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "npm-package-arg": "^6.1.0" } }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { "path-key": "^2.0.0" } @@ -10923,15 +10983,6 @@ "set-blocking": "~2.0.0" } }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, "null-check": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", @@ -10947,7 +10998,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, "oauth-sign": { "version": "0.8.2", @@ -10989,12 +11041,6 @@ } } }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -11012,16 +11058,6 @@ } } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -11079,6 +11115,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, "opn": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", @@ -11175,7 +11220,8 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-is-promise": { "version": "1.1.0", @@ -11187,6 +11233,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "requires": { "p-try": "^1.0.0" } @@ -11195,6 +11242,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, "requires": { "p-limit": "^1.1.0" } @@ -11208,7 +11256,8 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true }, "package-json": { "version": "4.0.1", @@ -11222,6 +11271,158 @@ "semver": "^5.1.0" } }, + "pacote": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.1.1.tgz", + "integrity": "sha512-f28Rq5ozzKAA9YwIKw61/ipwAatUZseYmVssDbHHaexF0wRIVotapVEZPAjOT7Eu3LYVqEp0NVpNizoAnYBUaA==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "cacache": "^11.2.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.1.0", + "npm-registry-fetch": "^3.8.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.6", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "cacache": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.1.tgz", + "integrity": "sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", @@ -11239,15 +11440,6 @@ "readable-stream": "^2.1.5" } }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, "parse-asn1": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", @@ -11339,7 +11531,8 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -11356,7 +11549,8 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-parse": { "version": "1.0.5", @@ -11380,9 +11574,9 @@ } }, "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -11472,14 +11666,14 @@ "dev": true }, "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.5.tgz", + "integrity": "sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ==", "dev": true, "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^5.5.0" }, "dependencies": { "source-map": { @@ -11487,16 +11681,25 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, "postcss-import": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.0.tgz", + "integrity": "sha512-3KqKRZcaZAvxbY8DVLdd81tG5uKzbUQuiWIvy0o0fzEC42bKacqPYFWbfCQyw6L4LWUaqPz/idvIdbhpgQ32eQ==", "dev": true, "requires": { - "postcss": "^6.0.1", + "postcss": "^7.0.1", "postcss-value-parser": "^3.2.3", "read-cache": "^1.0.0", "resolve": "^1.1.7" @@ -11513,28 +11716,49 @@ } }, "postcss-loader": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz", - "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", "dev": true, "requires": { "loader-utils": "^1.1.0", - "postcss": "^6.0.0", + "postcss": "^7.0.0", "postcss-load-config": "^2.0.0", - "schema-utils": "^0.4.0" + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } } }, "postcss-url": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz", - "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-8.0.0.tgz", + "integrity": "sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw==", "dev": true, "requires": { - "mime": "^1.4.1", + "mime": "^2.3.1", "minimatch": "^3.0.4", "mkdirp": "^0.5.0", - "postcss": "^6.0.1", + "postcss": "^7.0.2", "xxhashjs": "^0.2.1" + }, + "dependencies": { + "mime": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "dev": true + } } }, "postcss-value-parser": { @@ -11562,21 +11786,11 @@ "dev": true }, "prettier": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", - "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.2.tgz", + "integrity": "sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug==", "dev": true }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -11604,6 +11818,25 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, "protractor": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.0.tgz", @@ -11767,7 +12000,8 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "psl": { "version": "1.1.29", @@ -11776,16 +12010,17 @@ "dev": true }, "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "pump": { @@ -11844,9 +12079,9 @@ "dev": true }, "querystringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", - "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", + "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", "dev": true }, "randomatic": { @@ -12073,6 +12308,15 @@ "set-immediate-shim": "^1.0.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -12173,39 +12417,12 @@ } } }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, - "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", - "dev": true, - "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.1", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "~0.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", @@ -12357,7 +12574,8 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -12368,7 +12586,8 @@ "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true }, "requires-port": { "version": "1.0.0", @@ -12406,6 +12625,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -12454,9 +12683,9 @@ } }, "rollup": { - "version": "0.64.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.64.1.tgz", - "integrity": "sha512-+ThdVXrvonJdOTzyybMBipP0uz605Z8AnzWVY3rf+cSGnLO7uNkJBlN+9jXqWOomkvumXfm/esmBpA5d53qm7g==", + "version": "0.66.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.66.6.tgz", + "integrity": "sha512-J7/SWanrcb83vfIHqa8+aVVGzy457GcjA6GVZEnD0x2u4OnOd0Q1pCrEoNe8yLwM6z6LZP02zBT2uW0yh5TqOw==", "dev": true, "requires": { "@types/estree": "0.0.39", @@ -12464,15 +12693,24 @@ } }, "rollup-plugin-commonjs": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.6.tgz", - "integrity": "sha512-J7GOJm9uzEeLqkVxYSgjyoieh34hATWpa9G2M1ilGzWOLYGfQx5IDQ9ewG8QUj/Z2dzgV+d0/AyloAzElkABAA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz", + "integrity": "sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA==", + "dev": true, + "requires": { + "estree-walker": "^0.5.2", + "magic-string": "^0.25.1", + "resolve": "^1.8.1", + "rollup-pluginutils": "^2.3.3" + } + }, + "rollup-plugin-json": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-json/-/rollup-plugin-json-3.1.0.tgz", + "integrity": "sha512-BlYk5VspvGpjz7lAwArVzBXR60JK+4EKtPkCHouAWg39obk9S61hZYJDBfMK+oitPdoe11i69TlxKlMQNFC/Uw==", "dev": true, "requires": { - "estree-walker": "^0.5.1", - "magic-string": "^0.22.4", - "resolve": "^1.5.0", - "rollup-pluginutils": "^2.0.1" + "rollup-pluginutils": "^2.3.1" } }, "rollup-plugin-node-resolve": { @@ -12505,15 +12743,24 @@ } }, "rollup-pluginutils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.1.tgz", - "integrity": "sha512-JZS8aJMHEHhqmY2QVPMXwKP6lsD1ShkrcGYjhAIvqKKdXQyPHw/9NF0tl3On/xOJ4ACkxfeG7AF+chfCN1NpBg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz", + "integrity": "sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==", "dev": true, "requires": { "estree-walker": "^0.5.2", "micromatch": "^2.3.11" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -12544,12 +12791,12 @@ "dev": true }, "rxjs-tslint-rules": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/rxjs-tslint-rules/-/rxjs-tslint-rules-4.8.0.tgz", - "integrity": "sha512-ouk50f5Epj/l2E/5p2extPjFyQ7r+Zoax4inENsMqAi7kdvYjiET4qMCdKq1dTiyCWOJruLhovlJ8oc8NfnV8w==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/rxjs-tslint-rules/-/rxjs-tslint-rules-4.11.0.tgz", + "integrity": "sha512-db6M2YXcFydih7dLHcPgI73iYkui5QoZXLkKgpuZrs9WRS03JCbESwed4XeL3ZsOaS/JftqfLC6Q2RcOHxBs7w==", "dev": true, "requires": { - "@phenomnomnominal/tsquery": "^2.0.0", + "@phenomnomnominal/tsquery": "^3.0.0", "decamelize": "^2.0.0", "resolve": "^1.4.0", "tslib": "^1.8.0", @@ -12566,9 +12813,9 @@ } }, "tsutils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.0.0.tgz", - "integrity": "sha512-LjHBWR0vWAUHWdIAoTjoqi56Kz+FDKBgVEuL+gVPG/Pv7QW5IdaDDeK9Txlr6U0Cmckp5EgCIq1T25qe3J6hyw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.4.0.tgz", + "integrity": "sha512-lJOYR+rsCnso8k7C/D1IVSgOmJS9Ds8U4P2H7CxBtTf1w227Zg2nk+Fod8AsUzu3msg8AjZVsNBJb5QgCZsBjQ==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -12703,9 +12950,9 @@ } }, "selfsigned": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.3.tgz", - "integrity": "sha512-vmZenZ+8Al3NLHkWnhBQ0x6BkML1eCP2xEi3JE+f3D9wW9fipD9NNJHYtE9XJM4TsPaHGZJIamrSI6MTg1dU2Q==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", "dev": true, "requires": { "node-forge": "0.7.5" @@ -12829,7 +13076,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "set-immediate-shim": { "version": "1.0.1", @@ -12905,6 +13153,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -12912,106 +13161,25 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, - "showdown": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz", - "integrity": "sha1-kepO47elRIqspoIKTifmkMatdxw=", + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, "requires": { - "yargs": "^10.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - }, - "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" - } - }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "requires": { - "camelcase": "^4.1.0" - } - } + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" } }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "slash": { "version": "1.0.0", @@ -13019,10 +13187,10 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "smart-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", + "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", "dev": true }, "snapdragon": { @@ -13214,26 +13382,26 @@ } }, "sockjs-client": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", - "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", "dev": true, "requires": { - "debug": "^2.6.6", - "eventsource": "0.1.6", - "faye-websocket": "~0.11.0", - "inherits": "^2.0.1", + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", "json3": "^3.3.2", - "url-parse": "^1.1.8" + "url-parse": "^1.4.3" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "faye-websocket": { @@ -13244,13 +13412,39 @@ "requires": { "websocket-driver": ">=0.5.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, + "socks": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.2.tgz", + "integrity": "sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, "source-map": { @@ -13297,6 +13491,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.3.tgz", + "integrity": "sha512-vFrY/x/NdsD7Yc8mpTJXuao9S8lq08Z/kOITHz6b7YbfI9xL8Spe5EvSQUHOI7SbpY8bRPr0U3kKSsPuqEGSfA==", + "dev": true + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", @@ -13355,9 +13555,9 @@ } }, "spdy-transport": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz", + "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==", "dev": true, "requires": { "debug": "^2.6.8", @@ -13380,6 +13580,15 @@ } } }, + "speed-measure-webpack-plugin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.3.tgz", + "integrity": "sha512-p+taQ69VkRUXYMoZOx2nxV/Tz8tt79ahctoZJyJDHWP7fEYvwFNf5Pd73k5kZ6auu0pTsPNLEUwWpM8mCk85Zw==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -13451,9 +13660,9 @@ } }, "stats-webpack-plugin": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.6.2.tgz", - "integrity": "sha1-LFlJtTHgf4eojm6k3PrFOqjHWis=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.7.0.tgz", + "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==", "dev": true, "requires": { "lodash": "^4.17.4" @@ -13529,6 +13738,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -13547,6 +13757,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -13563,7 +13774,8 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "strip-indent": { "version": "1.0.1", @@ -13581,13 +13793,26 @@ "dev": true }, "style-loader": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", - "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", "dev": true, "requires": { "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } } }, "stylus": { @@ -13673,9 +13898,9 @@ "dev": true }, "tapable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", - "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", "dev": true }, "tar": { @@ -13698,6 +13923,198 @@ "execa": "^0.7.0" } }, + "terser": { + "version": "3.10.12", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.10.12.tgz", + "integrity": "sha512-3ODPC1eVt25EVNb04s/PkHxOmzKBQUF6bwwuR6h2DbEF8/j265Y1UkwNtOk9am/pRxfJ5HPapOlUlO6c16mKQQ==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.6" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz", + "integrity": "sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.8.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "cacache": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.1.tgz", + "integrity": "sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + } + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -13715,9 +14132,9 @@ } }, "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", "dev": true }, "timed-out": { @@ -13813,12 +14230,6 @@ "hoek": "5.x.x" } }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", @@ -13941,16 +14352,14 @@ } }, "tsickle": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.32.1.tgz", - "integrity": "sha512-JW9j+W0SaMSZGejIFZBk0AiPfnhljK3oLx5SaqxrJhjlvzFyPml5zqG1/PuScUj6yTe1muEqwk5CnDK0cOZmKw==", + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.33.1.tgz", + "integrity": "sha512-SpW2G3PvDGs4a5sMXPlWnCWHWRviWjSlI3U0734e3fU3U39VAE0NPr8M3W1cuL/OU/YXheYipGeEwtIJ5k0NHQ==", "dev": true, "requires": { - "jasmine-diff": "^0.1.3", "minimist": "^1.2.0", "mkdirp": "^0.5.1", - "source-map": "^0.6.0", - "source-map-support": "^0.5.0" + "source-map": "^0.7.3" }, "dependencies": { "minimist": { @@ -13960,20 +14369,10 @@ "dev": true }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true - }, - "source-map-support": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.8.tgz", - "integrity": "sha512-WqAEWPdb78u25RfKzOF0swBpY0dKrNdjc4GvLwm7ScX/o9bj8Eh/YL8mcMhBHYDGl87UkkSXDOFnW4G7GhWhGg==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } } } }, @@ -14059,21 +14458,27 @@ "dev": true }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", "dev": true }, "uglify-js": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.6.tgz", - "integrity": "sha512-O1D7L6WcOzS1qW2ehopEm4cWm5yA6bQBozlks8jO8ODxYCy4zv+bR/la4Lwp01tpkYGNonnpXvUpYtrvSu8Yzg==", + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", "dev": true, "requires": { - "commander": "~2.16.0", + "commander": "~2.17.1", "source-map": "~0.6.1" }, "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14171,18 +14576,18 @@ } }, "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { "unique-slug": "^2.0.0" } }, "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -14285,12 +14690,6 @@ "xdg-basedir": "^3.0.0" } }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -14323,46 +14722,10 @@ } } }, - "url-join": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", - "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", - "dev": true - }, - "url-loader": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz", - "integrity": "sha512-vugEeXjyYFBCUOpX+ZuaunbK3QXMKaQ3zUnRfIpRBlGkY7QizCnzyyn2ASfcxsvyU3ef+CJppVywnl3Kgf13Gg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, "url-parse": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", - "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", + "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", "dev": true, "requires": { "querystringify": "^2.0.0", @@ -14416,22 +14779,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -14497,12 +14844,6 @@ } } }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -14951,16 +15292,15 @@ } }, "webpack": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.2.tgz", - "integrity": "sha512-hCK8FPco2Paz9FVMlo3ZdVd7Jsr7qxoiEwhd7f4dMaWBLZtc7E+/9QNee4CYHlVSvpmspWBnhFpx4MiWSl3nNg==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.1.tgz", + "integrity": "sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-module-context": "1.5.13", - "@webassemblyjs/wasm-edit": "1.5.13", - "@webassemblyjs/wasm-opt": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-module-context": "1.7.10", + "@webassemblyjs/wasm-edit": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10", "acorn": "^5.6.2", "acorn-dynamic-import": "^3.0.0", "ajv": "^6.1.0", @@ -14977,10 +15317,10 @@ "neo-async": "^2.5.0", "node-libs-browser": "^2.0.0", "schema-utils": "^0.4.4", - "tapable": "^1.0.0", + "tapable": "^1.1.0", "uglifyjs-webpack-plugin": "^1.2.4", "watchpack": "^1.5.0", - "webpack-sources": "^1.2.0" + "webpack-sources": "^1.3.0" }, "dependencies": { "arr-diff": { @@ -15296,32 +15636,29 @@ } }, "webpack-dev-middleware": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.2.0.tgz", - "integrity": "sha512-YJLMF/96TpKXaEQwaLEo+Z4NDK8aV133ROF6xp9pe3gQoS7sxfpXh4Rv9eC+8vCvWfmDjRQaMSlRPbO+9G6jgA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", + "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", "dev": true, "requires": { - "loud-rejection": "^1.6.0", "memory-fs": "~0.4.1", "mime": "^2.3.1", - "path-is-absolute": "^1.0.0", "range-parser": "^1.0.3", - "url-join": "^4.0.0", "webpack-log": "^2.0.0" }, "dependencies": { "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true } } }, "webpack-dev-server": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.8.tgz", - "integrity": "sha512-c+tcJtDqnPdxCAzEEZKdIPmg3i5i7cAHe+B+0xFNK0BlCc2HF/unYccbU7xTgfGc5xxhCztCQzFmsqim+KhI+A==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz", + "integrity": "sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -15345,11 +15682,11 @@ "selfsigned": "^1.9.1", "serve-index": "^1.7.2", "sockjs": "0.3.19", - "sockjs-client": "1.1.5", + "sockjs-client": "1.3.0", "spdy": "^3.4.1", "strip-ansi": "^3.0.0", "supports-color": "^5.1.0", - "webpack-dev-middleware": "3.2.0", + "webpack-dev-middleware": "3.4.0", "webpack-log": "^2.0.0", "yargs": "12.0.2" }, @@ -15941,9 +16278,9 @@ } }, "webpack-sources": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", - "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", "dev": true, "requires": { "source-list-map": "^2.0.0", @@ -15959,9 +16296,9 @@ } }, "webpack-subresource-integrity": { - "version": "1.1.0-rc.4", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.4.tgz", - "integrity": "sha1-xcTj1pD50vZKlVDgeodn+Xlqpdg=", + "version": "1.1.0-rc.6", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz", + "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", "dev": true, "requires": { "webpack-core": "^0.6.8" @@ -15993,6 +16330,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -16013,9 +16351,9 @@ } }, "widest-line": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", - "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { "string-width": "^2.1.1" @@ -16089,6 +16427,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -16194,7 +16533,8 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true }, "yargs": { "version": "7.1.0", diff --git a/package.json b/package.json index 62b0bab019..aa8f6abdd7 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "name": "alfresco-content-app", - "version": "1.5.0", + "version": "1.6.0", "license": "LGPL-3.0", "scripts": { "ng": "ng", - "start": "npm run server-versions && ng serve --open", - "start:prod": "npm run server-versions && ng serve --prod --open", - "build": "npm run server-versions && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", - "build:dev": "npm run server-versions && ng build", + "start": "ng serve --open", + "start:prod": "ng serve --prod --open", + "build": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", + "build:dev": "ng build", + "build.e2e": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --configuration=e2e", "test": "ng test app --code-coverage", "test:ci": "ng test app --code-coverage --watch=false", "lint": "ng lint", - "server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0", "wd:update": "webdriver-manager update --gecko=false", "e2e": "npm run wd:update && protractor --baseUrl=http://localhost:4000", "e2e.local": "npm run wd:update && protractor --baseUrl=http://localhost:4200", @@ -30,31 +30,30 @@ }, "private": true, "dependencies": { - "@alfresco/adf-content-services": "2.6.0", - "@alfresco/adf-core": "2.6.0", - "@alfresco/adf-extensions": "2.6.0", - "@angular/animations": "6.1.10", - "@angular/cdk": "^6.4.7", - "@angular/common": "6.1.10", - "@angular/compiler": "6.1.10", - "@angular/core": "6.1.10", - "@angular/flex-layout": "^6.0.0-beta.18", - "@angular/forms": "6.1.10", - "@angular/http": "6.1.10", - "@angular/material": "^6.4.7", - "@angular/material-moment-adapter": "^6.4.7", - "@angular/platform-browser": "6.1.10", - "@angular/platform-browser-dynamic": "6.1.10", - "@angular/router": "6.1.10", - "@denysvuika/aca-dev-tools": "^0.1.5", + "@alfresco/adf-content-services": "2.6.1", + "@alfresco/adf-core": "2.6.1", + "@alfresco/adf-extensions": "3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "@angular/animations": "7.1.1", + "@angular/cdk": "^7.1.0", + "@angular/common": "7.1.1", + "@angular/compiler": "7.1.1", + "@angular/core": "7.1.1", + "@angular/flex-layout": "^7.0.0-beta.19", + "@angular/forms": "7.1.1", + "@angular/http": "7.1.1", + "@angular/material": "^7.1.0", + "@angular/material-moment-adapter": "^7.1.0", + "@angular/platform-browser": "7.1.1", + "@angular/platform-browser-dynamic": "7.1.1", + "@angular/router": "7.1.1", "@mat-datetimepicker/core": "^2.0.1", "@mat-datetimepicker/moment": "^2.0.1", - "@ngrx/effects": "^6.1.0", - "@ngrx/router-store": "^6.1.0", - "@ngrx/store": "^6.1.0", - "@ngrx/store-devtools": "^6.1.0", + "@ngrx/effects": "^6.1.2", + "@ngrx/router-store": "^6.1.2", + "@ngrx/store": "^6.1.2", + "@ngrx/store-devtools": "^6.1.2", "@ngx-translate/core": "^10.0.2", - "alfresco-js-api": "2.6.0", + "alfresco-js-api": "2.6.1", "core-js": "^2.5.7", "hammerjs": "2.0.8", "minimatch-browser": "^1.0.0", @@ -65,18 +64,18 @@ "zone.js": "0.8.26" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.8.0", - "@angular-devkit/build-ng-packagr": "~0.8.0", - "@angular/cli": "^6.2.4", - "@angular/compiler-cli": "6.1.10", - "@angular/language-service": "6.1.10", + "@angular-devkit/build-angular": "~0.11.0", + "@angular-devkit/build-ng-packagr": "~0.11.0", + "@angular/cli": "^7.1.0", + "@angular/compiler-cli": "7.1.1", + "@angular/language-service": "7.1.1", "@types/jasmine": "^2.5.53", "@types/jasminewd2": "^2.0.2", "@types/node": "9.3.0", "@types/selenium-webdriver": "^3.0.8", "alfresco-js-api-node": "^2.6.0", "chrome-remote-interface": "^0.26.1", - "codelyzer": "^4.4.4", + "codelyzer": "^4.5.0", "cspell": "^3.1.3", "jasmine-core": "~2.8.0", "jasmine-reporters": "^2.2.1", @@ -89,17 +88,17 @@ "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", - "ng-packagr": "^4.1.1", - "prettier": "^1.14.2", + "ng-packagr": "^4.4.0", + "prettier": "^1.15.2", "protractor": "^5.4.0", "rimraf": "2.6.2", - "rxjs-tslint-rules": "^4.8.0", + "rxjs-tslint-rules": "^4.11.0", "selenium-webdriver": "4.0.0-alpha.1", "ts-node": "~4.1.0", - "tsickle": ">=0.29.0", + "tsickle": "0.33.1", "tslib": "^1.9.0", "tslint": "~5.11.0", - "typescript": "^2.9.2", + "typescript": "^3.1.6", "wait-on": "^3.0.1" } } diff --git a/protractor.conf.js b/protractor.conf.js index 5ba0e29243..336b17908c 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -44,9 +44,10 @@ exports.config = { './e2e/suites/application/*.test.ts', './e2e/suites/navigation/*.test.ts', './e2e/suites/pagination/*.test.ts', + './e2e/suites/search/*.test.ts', './e2e/suites/actions/*.test.ts', './e2e/suites/viewer/*.test.ts', - + './e2e/suites/info-drawer/*.test.ts', './e2e/suites/extensions/*.test.ts' ], diff --git a/src/app.config.json b/src/app.config.json index 495c82513f..0b6d1f9335 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -15,15 +15,11 @@ "redirectUriLogout": "/logout" }, "application": { - "name": "Alfresco", + "name": "Alfresco Content Application", "logo": "assets/images/alfresco-logo-flower.svg", "copyright": "© 2017 - 2018 Alfresco Software, Inc. All rights reserved." }, - "experimental": { - "cardview": false, - "extensions": false, - "external-plugins": false - }, + "experimental": {}, "headerColor": "#2196F3", "languagePicker": false, "pagination": { @@ -212,7 +208,8 @@ "query": "-TYPE:'dl:dataList' AND -TYPE:'dl:todoList' AND -TYPE:'dl:issue'" }, { "query": "-TYPE:'fm:topic' AND -TYPE:'fm:post'" }, - { "query": "-TYPE:'lnk:link'" } + { "query": "-TYPE:'lnk:link'" }, + { "query": "-PNAME:'0/wiki'" } ], "facetFields": { "expanded": true, @@ -220,22 +217,22 @@ { "field": "content.mimetype", "mincount": 0, - "label": "FileType" + "label": "SEARCH.FACET_FIELDS.FILE_TYPE" }, { "field": "creator", "mincount": 0, - "label": "Creator" + "label": "SEARCH.FACET_FIELDS.CREATOR" }, { "field": "modifier", "mincount": 0, - "label": "Modifier" + "label": "SEARCH.FACET_FIELDS.MODIFIER" }, { "field": "SITE", "mincount": 0, - "label": "Location" + "label": "SEARCH.FACET_FIELDS.LOCATION" } ] }, diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index f13febce50..a99d54b51c 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -26,7 +26,72 @@ import { AppComponent } from './app.component'; describe('AppComponent', () => { - it('should be defined', () => { - expect(AppComponent).toBeDefined(); + let component; + const storeMock = { + dispatch: jasmine.createSpy('dispatch') + }; + + beforeAll(() => { + component = new AppComponent( + null, + null, + null, + storeMock, + null, + null, + null, + null, + null, + null, + null + ); + }); + + describe('onFileUploadedError', () => { + afterEach(() => { + storeMock.dispatch['calls'].reset(); + }); + + it('should dispatch 403 error message', () => { + component.onFileUploadedError({ error: { status: 403 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.403' + ); + }); + + it('should dispatch 404 error message', () => { + component.onFileUploadedError({ error: { status: 404 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.404' + ); + }); + + it('should dispatch 409 error message', () => { + component.onFileUploadedError({ error: { status: 409 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT' + ); + }); + + it('should dispatch 500 error message', () => { + component.onFileUploadedError({ error: { status: 500 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.500' + ); + }); + + it('should dispatch 504 error message', () => { + component.onFileUploadedError({ error: { status: 504 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.504' + ); + }); + + it('should dispatch generic error message', () => { + component.onFileUploadedError({ error: { status: 999 } }); + expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe( + 'APP.MESSAGES.UPLOAD.ERROR.GENERIC' + ); + }); }); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 44d073ec7f..85c3cdb474 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -167,6 +167,14 @@ export class AppComponent implements OnInit, OnDestroy { onFileUploadedError(error: FileUploadErrorEvent) { let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC'; + if (error.error.status === 403) { + message = 'APP.MESSAGES.UPLOAD.ERROR.403'; + } + + if (error.error.status === 404) { + message = 'APP.MESSAGES.UPLOAD.ERROR.404'; + } + if (error.error.status === 409) { message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT'; } @@ -175,6 +183,10 @@ export class AppComponent implements OnInit, OnDestroy { message = 'APP.MESSAGES.UPLOAD.ERROR.500'; } + if (error.error.status === 504) { + message = 'APP.MESSAGES.UPLOAD.ERROR.504'; + } + this.store.dispatch(new SnackbarErrorAction(message)); } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 50ebf8b0dd..89fdb98643 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,7 +26,10 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule, RouteReuseStrategy } from '@angular/router'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { + BrowserAnimationsModule, + NoopAnimationsModule +} from '@angular/platform-browser/animations'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TRANSLATION_PROVIDER, @@ -34,13 +37,17 @@ import { AppConfigService, DebugAppConfigService } from '@alfresco/adf-core'; -import { ContentModule } from '@alfresco/adf-content-services'; +import { + ContentModule, + CustomResourcesService +} from '@alfresco/adf-content-services'; import { AppComponent } from './app.component'; import { APP_ROUTES } from './app.routes'; import { FilesComponent } from './components/files/files.component'; import { LibrariesComponent } from './components/libraries/libraries.component'; +import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component'; import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog'; import { LibraryDialogComponent } from './dialogs/library/library.dialog'; @@ -65,11 +72,13 @@ import { AppSearchInputModule } from './components/search/search-input.module'; import { AppSearchResultsModule } from './components/search/search-results.module'; import { AppLoginModule } from './components/login/login.module'; import { AppHeaderModule } from './components/header/header.module'; +import { environment } from '../environments/environment'; +import { AppDataService } from './services/data.service'; @NgModule({ imports: [ BrowserModule, - BrowserAnimationsModule, + environment.e2e ? NoopAnimationsModule : BrowserAnimationsModule, FormsModule, ReactiveFormsModule, RouterModule.forRoot(APP_ROUTES, { @@ -103,12 +112,14 @@ import { AppHeaderModule } from './components/header/header.module'; AppComponent, FilesComponent, LibrariesComponent, + FavoriteLibrariesComponent, NodeVersionsDialogComponent, LibraryDialogComponent ], providers: [ { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, { provide: AppConfigService, useClass: DebugAppConfigService }, + { provide: CustomResourcesService, useClass: AppDataService }, { provide: TRANSLATION_PROVIDER, multi: true, diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index c1a2e52b7e..e80b7149be 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -24,11 +24,13 @@ */ import { Routes } from '@angular/router'; -import { LayoutComponent } from './components/layout/layout.component'; +import { AppLayoutComponent } from './components/layout/app-layout/app-layout.component'; import { FilesComponent } from './components/files/files.component'; import { LibrariesComponent } from './components/libraries/libraries.component'; +import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component'; import { GenericErrorComponent } from './components/common/generic-error/generic-error.component'; import { SearchResultsComponent } from './components/search/search-results/search-results.component'; +import { SearchLibrariesResultsComponent } from './components/search/search-libraries-results/search-libraries-results.component'; import { LoginComponent } from './components/login/login.component'; import { AppAuthGuard } from './guards/auth.guard'; import { AppSharedRuleGuard } from './guards/shared.guard'; @@ -43,17 +45,16 @@ export const APP_ROUTES: Routes = [ }, { path: 'settings', - loadChildren: - 'src/app/components/settings/settings.module#AppSettingsModule' + loadChildren: './components/settings/settings.module#AppSettingsModule' }, { path: 'preview/s/:id', loadChildren: - 'src/app/components/shared-link-view/shared-link-view.module#AppSharedLinkViewModule' + './components/shared-link-view/shared-link-view.module#AppSharedLinkViewModule' }, { path: '', - component: LayoutComponent, + component: AppLayoutComponent, children: [ { path: '', @@ -66,12 +67,11 @@ export const APP_ROUTES: Routes = [ { path: '', loadChildren: - 'src/app/components/favorites/favorites.module#AppFavoritesModule' + './components/favorites/favorites.module#AppFavoritesModule' }, { path: 'preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'favorites' } @@ -99,14 +99,21 @@ export const APP_ROUTES: Routes = [ }, { path: ':folderId/preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'libraries' } } ] }, + { + path: 'favorite/libraries', + component: FavoriteLibrariesComponent, + data: { + title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE', + sortingPreferenceKey: 'favorite-libraries' + } + }, { path: 'personal-files', data: { @@ -130,16 +137,14 @@ export const APP_ROUTES: Routes = [ }, { path: 'preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'personal-files' } }, { path: ':folderId/preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'personal-files' } @@ -155,12 +160,11 @@ export const APP_ROUTES: Routes = [ { path: '', loadChildren: - 'src/app/components/recent-files/recent-files.module#AppRecentFilesModule' + './components/recent-files/recent-files.module#AppRecentFilesModule' }, { path: 'preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'recent-files' } @@ -173,12 +177,11 @@ export const APP_ROUTES: Routes = [ { path: '', loadChildren: - 'src/app/components/shared-files/shared-files.module#AppSharedFilesModule' + './components/shared-files/shared-files.module#AppSharedFilesModule' }, { path: 'preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'shared' } @@ -189,12 +192,11 @@ export const APP_ROUTES: Routes = [ }, { path: 'trashcan', - loadChildren: - 'src/app/components/trashcan/trashcan.module#AppTrashcanModule' + loadChildren: './components/trashcan/trashcan.module#AppTrashcanModule' }, { path: 'about', - loadChildren: 'src/app/components/about/about.module#AboutModule' + loadChildren: './components/about/about.module#AboutModule' }, { path: 'search', @@ -209,8 +211,26 @@ export const APP_ROUTES: Routes = [ }, { path: 'preview/:nodeId', - loadChildren: - 'src/app/components/preview/preview.module#PreviewModule', + loadChildren: './components/preview/preview.module#PreviewModule', + data: { + navigateSource: 'search' + } + } + ] + }, + { + path: 'search-libraries', + children: [ + { + path: '', + component: SearchLibrariesResultsComponent, + data: { + title: 'APP.BROWSE.SEARCH.TITLE' + } + }, + { + path: 'preview/:nodeId', + loadChildren: './components/preview/preview.module#PreviewModule', data: { navigateSource: 'search' } diff --git a/src/app/components/about/about.component.html b/src/app/components/about/about.component.html index b83e9331c8..80891c6d25 100644 --- a/src/app/components/about/about.component.html +++ b/src/app/components/about/about.component.html @@ -1,36 +1,47 @@ -
-
-
-
-
Alfresco Content Application
-

version: {{ releaseVersion }}

+ + +
+
+
{{ 'application.name' | adfAppConfig }}
+

{{ 'APP.ABOUT.VERSION' | translate }} {{ releaseVersion }}

-
-
Alfresco Content Services
-

version: {{ repository.edition }} {{ repository.version.display }}

-
+ +
+
{{ 'APP.ABOUT.PLUGINS.TITLE' | translate }}
+ +
+
-
-
License
- -
+ +
+
Alfresco Content Services
+

+ {{ 'APP.ABOUT.VERSION' | translate }} {{ repository.edition }} + {{ repository.version.display }} +

+
-
-
Status
- -
+
+
{{ 'APP.ABOUT.LICENSE.TITLE' | translate }}
+ +
-
-
Modules
- -
+
+
{{ 'APP.ABOUT.STATUS.TITLE' | translate }}
+ +
-
-
Packages
- Current project is using the following ADF libraries: - +
+
{{ 'APP.ABOUT.MODULES.TITLE' | translate }}
+ +
+ + +
+
{{ 'APP.ABOUT.PACKAGES.TITLE' | translate }}
+
-
-
+ + diff --git a/src/app/components/about/about.component.theme.scss b/src/app/components/about/about.component.theme.scss index ef30a8e734..beb373af3f 100644 --- a/src/app/components/about/about.component.theme.scss +++ b/src/app/components/about/about.component.theme.scss @@ -1,39 +1,29 @@ @mixin aca-about-component-theme($theme) { $foreground: map-get($theme, foreground); - article { - color: mat-color($foreground, text, 0.54); - } - - article:first-of-type { - padding-bottom: 0; - } - - article:last-of-type { - margin-bottom: 50px; - } - - header { - line-height: 24px; - font-size: 14px; - font-weight: 800; - letter-spacing: -0.2px; - } - - a { - text-decoration: none; - color: mat-color($foreground, text, 0.87); - } - - .padding { - padding: 25px; - } - - .padding-top-bottom { - padding: 25px 0 25px 0; - } - - .padding-left-right { - padding: 0 25px 0 25px; + .app-about { + .main-content { + padding: 10px; + + article { + color: mat-color($foreground, text, 0.54); + padding: 25px 0 25px 0; + + & > header { + line-height: 24px; + font-size: 14px; + font-weight: 800; + letter-spacing: -0.2px; + } + } + + article:first-of-type { + padding-bottom: 0; + } + + article:last-of-type { + margin-bottom: 50px; + } + } } } diff --git a/src/app/components/about/about.component.ts b/src/app/components/about/about.component.ts index b305c0c441..63075faf7a 100644 --- a/src/app/components/about/about.component.ts +++ b/src/app/components/about/about.component.ts @@ -23,164 +23,64 @@ * along with Alfresco. If not, see . */ -import { Component, OnInit } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { ObjectDataTableAdapter } from '@alfresco/adf-core'; -import { ContentApiService } from '../../services/content-api.service'; +import { ExtensionRef } from '@alfresco/adf-extensions'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { RepositoryInfo } from 'alfresco-js-api'; +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; - +import { AppExtensionService } from '../../extensions/extension.service'; +import { ContentApiService } from '../../services/content-api.service'; +import { version, dependencies } from '../../../../package.json'; @Component({ selector: 'app-about', - templateUrl: './about.component.html' + templateUrl: './about.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-about' } }) export class AboutComponent implements OnInit { repository: RepositoryInfo; - data: ObjectDataTableAdapter; - status: ObjectDataTableAdapter; - license: ObjectDataTableAdapter; - modules: ObjectDataTableAdapter; - releaseVersion = ''; + releaseVersion = version; + extensions$: Observable; + dependencyEntries: Array<{ name: string; version: string }>; + statusEntries: Array<{ property: string; value: string }>; + licenseEntries: Array<{ property: string; value: string }>; constructor( private contentApi: ContentApiService, - private http: HttpClient - ) {} + appExtensions: AppExtensionService + ) { + this.extensions$ = appExtensions.references$; + } ngOnInit() { + this.dependencyEntries = Object.keys(dependencies).map(key => { + return { + name: key, + version: dependencies[key] + }; + }); + this.contentApi .getRepositoryInformation() .pipe(map(node => node.entry.repository)) .subscribe(repository => { this.repository = repository; - this.modules = new ObjectDataTableAdapter(repository.modules, [ - { type: 'text', key: 'id', title: 'ID', sortable: true }, - { type: 'text', key: 'title', title: 'Title', sortable: true }, - { - type: 'text', - key: 'version', - title: 'Description', - sortable: true - }, - { - type: 'date', - key: 'installDate', - title: 'Install Date', - sortable: true - }, - { - type: 'text', - key: 'installState', - title: 'Install State', - sortable: true - }, - { - type: 'text', - key: 'versionMin', - title: 'Version Minor', - sortable: true - }, - { - type: 'text', - key: 'versionMax', - title: 'Version Max', - sortable: true - } - ]); - - this.status = new ObjectDataTableAdapter( - [repository.status], - [ - { - type: 'text', - key: 'isReadOnly', - title: 'Read Only', - sortable: true - }, - { - type: 'text', - key: 'isAuditEnabled', - title: 'Audit Enable', - sortable: true - }, - { - type: 'text', - key: 'isQuickShareEnabled', - title: 'Quick Shared Enable', - sortable: true - }, - { - type: 'text', - key: 'isThumbnailGenerationEnabled', - title: 'Thumbnail Generation', - sortable: true - } - ] - ); + this.statusEntries = Object.keys(repository.status).map(key => { + return { + property: key, + value: repository.status[key] + }; + }); if (repository.license) { - this.license = new ObjectDataTableAdapter( - [repository.license], - [ - { - type: 'date', - key: 'issuedAt', - title: 'Issued At', - sortable: true - }, - { - type: 'date', - key: 'expiresAt', - title: 'Expires At', - sortable: true - }, - { - type: 'text', - key: 'remainingDays', - title: 'Remaining Days', - sortable: true - }, - { type: 'text', key: 'holder', title: 'Holder', sortable: true }, - { type: 'text', key: 'mode', title: 'Type', sortable: true }, - { - type: 'text', - key: 'isClusterEnabled', - title: 'Cluster Enabled', - sortable: true - }, - { - type: 'text', - key: 'isCryptodocEnabled', - title: 'Cryptodoc Enable', - sortable: true - } - ] - ); + this.licenseEntries = Object.keys(repository.license).map(key => { + return { + property: key, + value: repository.license[key] + }; + }); } }); - - this.http.get('/versions.json').subscribe((response: any) => { - const regexp = new RegExp('^(@alfresco|alfresco-)'); - - const alfrescoPackagesTableRepresentation = Object.keys( - response.dependencies - ) - .filter(val => regexp.test(val)) - .map(val => ({ - name: val, - version: response.dependencies[val].version - })); - - this.data = new ObjectDataTableAdapter( - alfrescoPackagesTableRepresentation, - [ - { type: 'text', key: 'name', title: 'Name', sortable: true }, - { type: 'text', key: 'version', title: 'Version', sortable: true } - ] - ); - - this.releaseVersion = response.version; - }); } } diff --git a/src/app/components/about/about.module.ts b/src/app/components/about/about.module.ts index 8a25b33348..157b9174aa 100644 --- a/src/app/components/about/about.module.ts +++ b/src/app/components/about/about.module.ts @@ -28,6 +28,13 @@ import { Routes, RouterModule } from '@angular/router'; import { AboutComponent } from './about.component'; import { CommonModule } from '@angular/common'; import { CoreModule } from '@alfresco/adf-core'; +import { AppLayoutModule } from '../layout/layout.module'; +import { MatTableModule } from '@angular/material'; +import { PackageListComponent } from './package-list/package-list.component'; +import { ExtensionListComponent } from './extension-list/extension-list.component'; +import { StatusListComponent } from './status-list/status-list.component'; +import { ModuleListComponent } from './module-list/module-list.component'; +import { LicenseListComponent } from './license-list/license-list.component'; const routes: Routes = [ { @@ -40,7 +47,20 @@ const routes: Routes = [ ]; @NgModule({ - imports: [CommonModule, CoreModule.forChild(), RouterModule.forChild(routes)], - declarations: [AboutComponent] + imports: [ + CommonModule, + CoreModule.forChild(), + RouterModule.forChild(routes), + AppLayoutModule, + MatTableModule + ], + declarations: [ + AboutComponent, + PackageListComponent, + ExtensionListComponent, + StatusListComponent, + ModuleListComponent, + LicenseListComponent + ] }) export class AboutModule {} diff --git a/src/app/components/about/extension-list/extension-list.component.html b/src/app/components/about/extension-list/extension-list.component.html new file mode 100644 index 0000000000..906e757a17 --- /dev/null +++ b/src/app/components/about/extension-list/extension-list.component.html @@ -0,0 +1,14 @@ + + + + {{ column.header | translate }} + + {{ column.cell(row) }} + + + + + diff --git a/src/app/components/about/extension-list/extension-list.component.ts b/src/app/components/about/extension-list/extension-list.component.ts new file mode 100644 index 0000000000..05977060d7 --- /dev/null +++ b/src/app/components/about/extension-list/extension-list.component.ts @@ -0,0 +1,78 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; +import { ExtensionRef } from '@alfresco/adf-extensions'; + +@Component({ + selector: 'app-extension-list', + templateUrl: './extension-list.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ExtensionListComponent { + columns = [ + { + columnDef: 'id', + header: 'APP.ABOUT.PLUGINS.ID', + cell: (row: ExtensionRef) => `${row.$id}` + }, + { + columnDef: 'name', + header: 'APP.ABOUT.PLUGINS.NAME', + cell: (row: ExtensionRef) => `${row.$name}` + }, + { + columnDef: 'version', + header: 'APP.ABOUT.PLUGINS.VERSION', + cell: (row: ExtensionRef) => `${row.$version}` + }, + { + columnDef: 'vendor', + header: 'APP.ABOUT.PLUGINS.VENDOR', + cell: (row: ExtensionRef) => `${row.$vendor}` + }, + { + columnDef: 'license', + header: 'APP.ABOUT.PLUGINS.LICENSE', + cell: (row: ExtensionRef) => `${row.$license}` + }, + { + columnDef: 'runtime', + header: 'APP.ABOUT.PLUGINS.RUNTIME', + cell: (row: ExtensionRef) => `${row.$runtime}` + } + ]; + + displayedColumns = this.columns.map(x => x.columnDef); + + @Input() + data: Array = []; +} diff --git a/src/app/components/about/license-list/license-list.component.html b/src/app/components/about/license-list/license-list.component.html new file mode 100644 index 0000000000..906e757a17 --- /dev/null +++ b/src/app/components/about/license-list/license-list.component.html @@ -0,0 +1,14 @@ + + + + {{ column.header | translate }} + + {{ column.cell(row) }} + + + + + diff --git a/src/app/components/about/license-list/license-list.component.ts b/src/app/components/about/license-list/license-list.component.ts new file mode 100644 index 0000000000..6a2157d60c --- /dev/null +++ b/src/app/components/about/license-list/license-list.component.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +export interface LicenseData { + property: string; + value: string; +} + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; + +@Component({ + selector: 'app-license-list', + templateUrl: './license-list.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class LicenseListComponent { + columns = [ + { + columnDef: 'property', + header: 'APP.ABOUT.LICENSE.PROPERTY', + cell: (row: LicenseData) => `${row.property}` + }, + { + columnDef: 'value', + header: 'APP.ABOUT.LICENSE.VALUE', + cell: (row: LicenseData) => `${row.value}` + } + ]; + + displayedColumns = this.columns.map(x => x.columnDef); + + @Input() + data: Array = []; +} diff --git a/src/app/components/about/module-list/module-list.component.html b/src/app/components/about/module-list/module-list.component.html new file mode 100644 index 0000000000..906e757a17 --- /dev/null +++ b/src/app/components/about/module-list/module-list.component.html @@ -0,0 +1,14 @@ + + + + {{ column.header | translate }} + + {{ column.cell(row) }} + + + + + diff --git a/src/app/components/about/module-list/module-list.component.ts b/src/app/components/about/module-list/module-list.component.ts new file mode 100644 index 0000000000..f4d5aa7cf3 --- /dev/null +++ b/src/app/components/about/module-list/module-list.component.ts @@ -0,0 +1,63 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; +import { ModuleInfo } from 'alfresco-js-api'; + +@Component({ + selector: 'app-module-list', + templateUrl: './module-list.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ModuleListComponent { + columns = [ + { + columnDef: 'id', + header: 'APP.ABOUT.MODULES.ID', + cell: (row: ModuleInfo) => `${row.id}` + }, + { + columnDef: 'title', + header: 'APP.ABOUT.MODULES.NAME', + cell: (row: ModuleInfo) => `${row.title}` + }, + { + columnDef: 'version', + header: 'APP.ABOUT.MODULES.VERSION', + cell: (row: ModuleInfo) => `${row.version}` + } + ]; + + displayedColumns = this.columns.map(x => x.columnDef); + + @Input() + data: Array = []; +} diff --git a/src/app/components/about/package-list/package-list.component.html b/src/app/components/about/package-list/package-list.component.html new file mode 100644 index 0000000000..906e757a17 --- /dev/null +++ b/src/app/components/about/package-list/package-list.component.html @@ -0,0 +1,14 @@ + + + + {{ column.header | translate }} + + {{ column.cell(row) }} + + + + + diff --git a/src/app/components/about/package-list/package-list.component.ts b/src/app/components/about/package-list/package-list.component.ts new file mode 100644 index 0000000000..606df66f99 --- /dev/null +++ b/src/app/components/about/package-list/package-list.component.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; + +export interface PackageInfo { + name: string; + version: string; +} + +@Component({ + selector: 'app-package-list', + templateUrl: './package-list.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PackageListComponent { + columns = [ + { + columnDef: 'title', + header: 'APP.ABOUT.PACKAGES.NAME', + cell: (row: PackageInfo) => `${row.name}` + }, + { + columnDef: 'version', + header: 'APP.ABOUT.PACKAGES.VERSION', + cell: (row: PackageInfo) => `${row.version}` + } + ]; + + displayedColumns = this.columns.map(x => x.columnDef); + + @Input() + data: Array = []; +} diff --git a/src/app/components/about/status-list/status-list.component.html b/src/app/components/about/status-list/status-list.component.html new file mode 100644 index 0000000000..906e757a17 --- /dev/null +++ b/src/app/components/about/status-list/status-list.component.html @@ -0,0 +1,14 @@ + + + + {{ column.header | translate }} + + {{ column.cell(row) }} + + + + + diff --git a/src/app/components/about/status-list/status-list.component.ts b/src/app/components/about/status-list/status-list.component.ts new file mode 100644 index 0000000000..0223b3d681 --- /dev/null +++ b/src/app/components/about/status-list/status-list.component.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; + +export interface StatusData { + property: string; + value: string; +} + +@Component({ + selector: 'app-status-list', + templateUrl: './status-list.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class StatusListComponent { + columns = [ + { + columnDef: 'property', + header: 'APP.ABOUT.STATUS.PROPERTY', + cell: (row: StatusData) => `${row.property}` + }, + { + columnDef: 'value', + header: 'APP.ABOUT.STATUS.VALUE', + cell: (row: StatusData) => `${row.value}` + } + ]; + + displayedColumns = this.columns.map(x => x.columnDef); + + @Input() + data: Array = []; +} diff --git a/src/app/components/common/common.module.ts b/src/app/components/common/common.module.ts index 9261d681cf..9dd9d1429d 100644 --- a/src/app/components/common/common.module.ts +++ b/src/app/components/common/common.module.ts @@ -31,19 +31,24 @@ import { LocationLinkComponent } from './location-link/location-link.component'; import { NameColumnComponent } from './name-column/name-column.component'; import { LibraryNameColumnComponent } from './library-name-column/library-name-column.component'; import { LibraryStatusColumnComponent } from './library-status-column/library-status-column.component'; +import { LibraryRoleColumnComponent } from './library-role-column/library-role-column.component'; import { TrashcanNameColumnComponent } from './trashcan-name-column/trashcan-name-column.component'; import { DynamicColumnComponent } from './dynamic-column/dynamic-column.component'; +import { IconComponent } from './icon/icon.component'; +import { MatIconModule } from '@angular/material'; @NgModule({ - imports: [CommonModule, CoreModule.forChild()], + imports: [CommonModule, CoreModule.forChild(), MatIconModule], declarations: [ GenericErrorComponent, LocationLinkComponent, NameColumnComponent, LibraryNameColumnComponent, LibraryStatusColumnComponent, + LibraryRoleColumnComponent, TrashcanNameColumnComponent, - DynamicColumnComponent + DynamicColumnComponent, + IconComponent ], exports: [ GenericErrorComponent, @@ -51,14 +56,17 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen NameColumnComponent, LibraryNameColumnComponent, LibraryStatusColumnComponent, + LibraryRoleColumnComponent, TrashcanNameColumnComponent, - DynamicColumnComponent + DynamicColumnComponent, + IconComponent ], entryComponents: [ LocationLinkComponent, NameColumnComponent, LibraryNameColumnComponent, LibraryStatusColumnComponent, + LibraryRoleColumnComponent, TrashcanNameColumnComponent ] }) diff --git a/src/app/components/common/dynamic-column/dynamic-column.component.ts b/src/app/components/common/dynamic-column/dynamic-column.component.ts index 913d4895f3..4ad17fae52 100644 --- a/src/app/components/common/dynamic-column/dynamic-column.component.ts +++ b/src/app/components/common/dynamic-column/dynamic-column.component.ts @@ -41,7 +41,9 @@ import { ExtensionService } from '@alfresco/adf-extensions'; @Component({ selector: 'app-dynamic-column', - template: ``, + template: ` + + `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'app-dynamic-column' }, diff --git a/src/app/components/common/icon/icon.component.html b/src/app/components/common/icon/icon.component.html new file mode 100644 index 0000000000..ba5b060d27 --- /dev/null +++ b/src/app/components/common/icon/icon.component.html @@ -0,0 +1,7 @@ + + + + + + {{ value }} + diff --git a/src/app/components/common/icon/icon.component.scss b/src/app/components/common/icon/icon.component.scss new file mode 100644 index 0000000000..ab73482199 --- /dev/null +++ b/src/app/components/common/icon/icon.component.scss @@ -0,0 +1,4 @@ +.adf-icon { + display: inline-flex; + vertical-align: middle; +} diff --git a/src/app/components/common/icon/icon.component.ts b/src/app/components/common/icon/icon.component.ts new file mode 100644 index 0000000000..d27b3bd733 --- /dev/null +++ b/src/app/components/common/icon/icon.component.ts @@ -0,0 +1,58 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + Input, + ViewEncapsulation, + ChangeDetectionStrategy +} from '@angular/core'; + +@Component({ + selector: 'adf-icon', + templateUrl: './icon.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'adf-icon' }, + styleUrls: ['./icon.component.scss'] +}) +export class IconComponent { + private _value = ''; + private _isCustom = false; + + get value(): string { + return this._value; + } + + @Input() + set value(value: string) { + this._value = value || 'settings'; + this._isCustom = this._value.includes(':'); + } + + get isCustom(): boolean { + return this._isCustom; + } +} diff --git a/src/app/components/common/library-name-column/library-name-column.component.ts b/src/app/components/common/library-name-column/library-name-column.component.ts index e057d0ba93..8ebf3d3e45 100644 --- a/src/app/components/common/library-name-column/library-name-column.component.ts +++ b/src/app/components/common/library-name-column/library-name-column.component.ts @@ -37,9 +37,7 @@ import { ShareDataRow } from '@alfresco/adf-content-services'; @Component({ selector: 'app-library-name-column', template: ` - + {{ displayText }} `, diff --git a/src/app/components/common/library-role-column/library-role-column.component.spec.ts b/src/app/components/common/library-role-column/library-role-column.component.spec.ts new file mode 100644 index 0000000000..5219430d05 --- /dev/null +++ b/src/app/components/common/library-role-column/library-role-column.component.spec.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { LibraryRoleColumnComponent } from './library-role-column.component'; +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +describe('LibraryNameColumnComponent', () => { + let fixture: ComponentFixture; + let component: LibraryRoleColumnComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [LibraryRoleColumnComponent], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(LibraryRoleColumnComponent); + component = fixture.componentInstance; + }); + + it('should render Manager', () => { + component.context = { row: { node: { entry: { role: 'SiteManager' } } } }; + fixture.detectChanges(); + expect(component.displayText).toBe('APP.SITES_ROLE.MANAGER'); + }); + + it('should render Collaborator', () => { + component.context = { + row: { node: { entry: { role: 'SiteCollaborator' } } } + }; + fixture.detectChanges(); + expect(component.displayText).toBe('APP.SITES_ROLE.COLLABORATOR'); + }); + + it('should render Contributor', () => { + component.context = { + row: { node: { entry: { role: 'SiteContributor' } } } + }; + fixture.detectChanges(); + expect(component.displayText).toBe('APP.SITES_ROLE.CONTRIBUTOR'); + }); + + it('should render Consumer', () => { + component.context = { + row: { node: { entry: { role: 'SiteConsumer' } } } + }; + fixture.detectChanges(); + expect(component.displayText).toBe('APP.SITES_ROLE.CONSUMER'); + }); + + it('should not render text for unknown', () => { + component.context = { + row: { node: { entry: { role: 'ROLE' } } } + }; + fixture.detectChanges(); + expect(component.displayText).toBe(''); + }); +}); diff --git a/src/app/components/common/library-role-column/library-role-column.component.ts b/src/app/components/common/library-role-column/library-role-column.component.ts new file mode 100644 index 0000000000..194b2b12ec --- /dev/null +++ b/src/app/components/common/library-role-column/library-role-column.component.ts @@ -0,0 +1,65 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, OnInit, Input } from '@angular/core'; + +@Component({ + selector: 'app-library-role-column', + template: ` + + {{ displayText | translate }} + + ` +}) +export class LibraryRoleColumnComponent implements OnInit { + @Input() + context: any; + + displayText: string; + + ngOnInit() { + const node = this.context.row.node; + if (node && node.entry) { + const role: string = node.entry.role; + switch (role) { + case 'SiteManager': + this.displayText = 'APP.SITES_ROLE.MANAGER'; + break; + case 'SiteCollaborator': + this.displayText = 'APP.SITES_ROLE.COLLABORATOR'; + break; + case 'SiteContributor': + this.displayText = 'APP.SITES_ROLE.CONTRIBUTOR'; + break; + case 'SiteConsumer': + this.displayText = 'APP.SITES_ROLE.CONSUMER'; + break; + default: + this.displayText = ''; + break; + } + } + } +} diff --git a/src/app/components/common/location-link/location-link.component.ts b/src/app/components/common/location-link/location-link.component.ts index 56085f7eb3..018e21e324 100644 --- a/src/app/components/common/location-link/location-link.component.ts +++ b/src/app/components/common/location-link/location-link.component.ts @@ -42,10 +42,10 @@ import { ContentApiService } from '../../../services/content-api.service'; @Component({ selector: 'aca-location-link', template: ` - - {{ displayText | async }} - - `, + + {{ displayText | async | translate }} + + `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'aca-location-link adf-location-cell' } @@ -53,7 +53,7 @@ import { ContentApiService } from '../../../services/content-api.service'; export class LocationLinkComponent implements OnInit { private _path: PathInfo; - nodeLocation$ = new BehaviorSubject(null); + nodeLocation$ = new BehaviorSubject(''); @Input() context: any; @@ -93,6 +93,8 @@ export class LocationLinkComponent implements OnInit { if (path && path.name && path.elements) { this.displayText = this.getDisplayText(path); this._path = path; + } else { + this.displayText = of('APP.BROWSE.SEARCH.UNKNOWN_LOCATION'); } } } @@ -142,6 +144,10 @@ export class LocationLinkComponent implements OnInit { // todo: review once 5.2.3 is out private getTooltip(path: PathInfo) { + if (!path) { + return; + } + let result: string = null; const elements = path.elements.map(e => Object.assign({}, e)); diff --git a/src/app/components/common/name-column/name-column.component.ts b/src/app/components/common/name-column/name-column.component.ts index 8a47af70e8..9f328689ec 100644 --- a/src/app/components/common/name-column/name-column.component.ts +++ b/src/app/components/common/name-column/name-column.component.ts @@ -36,9 +36,7 @@ import { MinimalNodeEntity } from 'alfresco-js-api'; @Component({ selector: 'app-name-column', template: ` - + {{ displayText }} `, diff --git a/src/app/components/common/trashcan-name-column/trashcan-name-column.component.spec.ts b/src/app/components/common/trashcan-name-column/trashcan-name-column.component.spec.ts index 180579bbe6..cfc7129c52 100644 --- a/src/app/components/common/trashcan-name-column/trashcan-name-column.component.spec.ts +++ b/src/app/components/common/trashcan-name-column/trashcan-name-column.component.spec.ts @@ -26,7 +26,89 @@ import { TrashcanNameColumnComponent } from './trashcan-name-column.component'; describe('TrashcanNameColumnComponent', () => { - it('should be defined', () => { - expect(TrashcanNameColumnComponent).toBeDefined(); + let component; + + beforeEach(() => { + component = new TrashcanNameColumnComponent(); + }); + + it('should set displayText for content files', () => { + const context = { + data: { rows: [] }, + row: { + node: { + entry: { + name: 'contentName', + nodeType: 'content' + } + } + } + }; + + component.context = context; + component.ngOnInit(); + + expect(component.displayText).toBe('contentName'); + }); + + it('should set displayText for library', () => { + const context = { + data: { + rows: [] + }, + row: { + node: { + entry: { + nodeType: 'st:site', + properties: { + 'cm:title': 'libraryTitle' + } + } + } + } + }; + + component.context = context; + component.ngOnInit(); + + expect(component.displayText).toBe('libraryTitle'); + }); + + it('should set custom displayText for libraries with same name', () => { + const context = { + data: { + rows: [ + { + node: { + entry: { + id: 'id1', + name: 'name1', + nodeType: 'st:site', + properties: { + 'cm:title': 'bogus' + } + } + } + } + ] + }, + row: { + node: { + entry: { + id: 'id2', + name: 'name1', + nodeType: 'st:site', + properties: { + 'cm:title': 'bogus' + } + } + } + } + }; + + component.context = context; + component.ngOnInit(); + + expect(component.displayText).toBe('bogus (name1)'); }); }); diff --git a/src/app/components/common/trashcan-name-column/trashcan-name-column.component.ts b/src/app/components/common/trashcan-name-column/trashcan-name-column.component.ts index 2703069776..627c246fce 100644 --- a/src/app/components/common/trashcan-name-column/trashcan-name-column.component.ts +++ b/src/app/components/common/trashcan-name-column/trashcan-name-column.component.ts @@ -30,15 +30,18 @@ import { OnInit, Input } from '@angular/core'; +import { ShareDataRow } from '@alfresco/adf-content-services'; import { MinimalNodeEntity } from 'alfresco-js-api'; @Component({ selector: 'app-trashcan-name-column', template: ` - - {{ displayText }} - + + {{ displayText }} + + + {{ displayText }} + `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, @@ -48,13 +51,45 @@ export class TrashcanNameColumnComponent implements OnInit { @Input() context: any; + isLibrary = false; displayText: string; + displayTooltip: string; node: MinimalNodeEntity; ngOnInit() { this.node = this.context.row.node; + const rows: Array = this.context.data.rows || []; + if (this.node && this.node.entry) { - this.displayText = this.node.entry.name || this.node.entry.id; + this.isLibrary = this.node.entry.nodeType === 'st:site'; + + if (this.isLibrary) { + const { properties } = this.node.entry; + + this.displayText = this.makeLibraryTitle(this.node.entry, rows); + this.displayTooltip = + properties['cm:description'] || properties['cm:title']; + } else { + this.displayText = this.node.entry.name || this.node.entry.id; + } } } + + makeLibraryTitle(library: any, rows: Array): string { + const entries = rows.map((r: ShareDataRow) => r.node.entry); + const { id } = library; + const title = library.properties['cm:title']; + + let isDuplicate = false; + + if (entries) { + isDuplicate = entries.some((entry: any) => { + return entry.id !== id && entry.properties['cm:title'] === title; + }); + } + + return isDuplicate + ? `${library.properties['cm:title']} (${library.name})` + : `${library.properties['cm:title']}`; + } } diff --git a/src/app/components/context-menu/context-menu-item.component.html b/src/app/components/context-menu/context-menu-item.component.html index cf3b25f0c4..ee46997db6 100644 --- a/src/app/components/context-menu/context-menu-item.component.html +++ b/src/app/components/context-menu/context-menu-item.component.html @@ -1,39 +1,38 @@
- + + + - - - - - - - - - - - - + + + + + - - - + + + + + + - - - + + -
\ No newline at end of file + +
diff --git a/src/app/components/context-menu/context-menu-item.component.spec.ts b/src/app/components/context-menu/context-menu-item.component.spec.ts index adb4567b8a..581b1b9349 100644 --- a/src/app/components/context-menu/context-menu-item.component.spec.ts +++ b/src/app/components/context-menu/context-menu-item.component.spec.ts @@ -75,7 +75,9 @@ describe('ContextMenuComponent', () => { fixture.detectChanges(); const buttonElement = fixture.nativeElement.querySelector('button'); - expect(buttonElement.innerText.trim()).toBe(contextItem.title); + expect(buttonElement.querySelector('span').innerText.trim()).toBe( + contextItem.title + ); }); it('should not run action when entry has no click attribute defined', () => { diff --git a/src/app/components/context-menu/context-menu-outside-event.directive.ts b/src/app/components/context-menu/context-menu-outside-event.directive.ts index ff352239bc..c840eb32e9 100644 --- a/src/app/components/context-menu/context-menu-outside-event.directive.ts +++ b/src/app/components/context-menu/context-menu-outside-event.directive.ts @@ -6,7 +6,7 @@ import { OnDestroy } from '@angular/core'; import { fromEvent, Subscription } from 'rxjs'; -import { delay } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; @Directive({ selector: '[acaContextMenuOutsideEvent]' @@ -22,7 +22,7 @@ export class OutsideEventDirective implements OnInit, OnDestroy { ngOnInit() { this.subscriptions = this.subscriptions.concat([ fromEvent(document.body, 'click') - .pipe(delay(1)) + .pipe(filter(event => !this.findAncestor(event.target as Element))) .subscribe(() => this.clickOutside.next()) ]); } @@ -31,4 +31,15 @@ export class OutsideEventDirective implements OnInit, OnDestroy { this.subscriptions.forEach(subscription => subscription.unsubscribe()); this.subscriptions = []; } + + private findAncestor(el: Element): boolean { + const className = 'aca-context-menu'; + + if (el.classList.contains(className)) { + return true; + } + // tslint:disable-next-line:curly + while ((el = el.parentElement) && !el.classList.contains(className)); + return !!el; + } } diff --git a/src/app/components/context-menu/context-menu.component.html b/src/app/components/context-menu/context-menu.component.html index 96eb4a2103..7283cb02c7 100644 --- a/src/app/components/context-menu/context-menu.component.html +++ b/src/app/components/context-menu/context-menu.component.html @@ -1,41 +1,50 @@ - + - - - - - + + + + + - - - + + + - - + + - - - - - + + + + + - - - + + + diff --git a/src/app/components/context-menu/context-menu.component.spec.ts b/src/app/components/context-menu/context-menu.component.spec.ts index 46e2dae633..7eb44d24fa 100644 --- a/src/app/components/context-menu/context-menu.component.spec.ts +++ b/src/app/components/context-menu/context-menu.component.spec.ts @@ -111,7 +111,9 @@ describe('ContextMenuComponent', () => { .querySelectorAll('button'); expect(contextMenuElements.length).toBe(1); - expect(contextMenuElements[0].innerText).toBe(contextItem.title); + expect(contextMenuElements[0].querySelector('span').innerText).toBe( + contextItem.title + ); })); it('should run action with provided action id', fakeAsync(() => { diff --git a/src/app/components/context-menu/context-menu.directive.ts b/src/app/components/context-menu/context-menu.directive.ts index 2acc26031a..5fb5af91cf 100644 --- a/src/app/components/context-menu/context-menu.directive.ts +++ b/src/app/components/context-menu/context-menu.directive.ts @@ -23,20 +23,24 @@ * along with Alfresco. If not, see . */ -import { Directive, HostListener, Input } from '@angular/core'; +import { + Directive, + HostListener, + Input, + OnInit, + OnDestroy +} from '@angular/core'; import { ContextMenuOverlayRef } from './context-menu-overlay'; import { ContextMenuService } from './context-menu.service'; -import { DocumentListComponent } from '@alfresco/adf-content-services'; -import { SetSelectedNodesAction } from '../../store/actions'; -import { Store } from '@ngrx/store'; -import { AppStore } from '../../store/states/app.state'; -import { DataRow } from '@alfresco/adf-core'; -import { MinimalNodeEntity } from 'alfresco-js-api'; +import { debounceTime } from 'rxjs/operators'; +import { Subject, fromEvent, Subscription } from 'rxjs'; @Directive({ selector: '[acaContextActions]' }) -export class ContextActionsDirective { +export class ContextActionsDirective implements OnInit, OnDestroy { + private execute$: Subject = new Subject(); + private subscriptions: Subscription[] = []; private overlayRef: ContextMenuOverlayRef = null; // tslint:disable-next-line:no-input-rename @@ -49,40 +53,44 @@ export class ContextActionsDirective { event.preventDefault(); if (this.enabled) { - this.execute(event); + const target = this.getTarget(event); + if (target) { + this.execute(event, target); + } } } } - constructor( - private documentList: DocumentListComponent, - private store: Store, - private contextMenuService: ContextMenuService - ) {} + constructor(private contextMenuService: ContextMenuService) {} - private execute(event: MouseEvent) { - // todo: review this in ADF - const selected = this.getSelectedRow(event); + ngOnInit() { + this.subscriptions.push( + fromEvent(document.body, 'contextmenu').subscribe(() => { + if (this.overlayRef) { + this.overlayRef.close(); + } + }), - if (selected) { - if (!this.isInSelection(selected)) { - this.clearSelection(); - - this.documentList.dataTable.selectRow(selected, true); - this.documentList.selection.push((selected).node); + this.execute$.pipe(debounceTime(300)).subscribe((event: MouseEvent) => { + this.render(event); + }) + ); + } - this.updateSelection(); - } + ngOnDestroy() { + this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions = []; + this.execute$ = null; + } - this.render(event); + execute(event: MouseEvent, target: Element) { + if (!this.isSelected(target)) { + target.dispatchEvent(new MouseEvent('click')); } + this.execute$.next(event); } private render(event: MouseEvent) { - if (this.overlayRef) { - this.overlayRef.close(); - } - this.overlayRef = this.contextMenuService.open({ source: event, hasBackdrop: false, @@ -91,46 +99,24 @@ export class ContextActionsDirective { }); } - private updateSelection() { - this.store.dispatch( - new SetSelectedNodesAction(this.documentList.selection) - ); - } - - private isInSelection(row: DataRow): MinimalNodeEntity { - return this.documentList.selection.find( - selected => row.getValue('name') === selected.entry.name - ); + private getTarget(event: MouseEvent): Element { + return this.findAncestor(event.target, 'adf-datatable-table-cell'); } - private getSelectedRow(event): DataRow { - const rowElement = this.findAncestor( - event.target, - 'adf-datatable-row' - ); - - if (!rowElement) { - return null; + private isSelected(target): boolean { + if (!target) { + return false; } - const rowName = rowElement - .querySelector('.adf-data-table-cell--text .adf-datatable-cell') - .textContent.trim(); - - return this.documentList.data - .getRows() - .find((row: DataRow) => row.getValue('name') === rowName); - } - - private clearSelection() { - this.documentList.data.getRows().map((row: DataRow) => { - return this.documentList.dataTable.selectRow(row, false); - }); - - this.documentList.selection = []; + return this.findAncestor(target, 'adf-datatable-row').classList.contains( + 'is-selected' + ); } private findAncestor(el: Element, className: string): Element { + if (el.classList.contains(className)) { + return el; + } // tslint:disable-next-line:curly while ((el = el.parentElement) && !el.classList.contains(className)); return el; diff --git a/src/app/components/context-menu/context-menu.directives.spec.ts b/src/app/components/context-menu/context-menu.directives.spec.ts index 88516a83f7..13d57e0f14 100644 --- a/src/app/components/context-menu/context-menu.directives.spec.ts +++ b/src/app/components/context-menu/context-menu.directives.spec.ts @@ -24,39 +24,40 @@ */ import { ContextActionsDirective } from './context-menu.directive'; +import { fakeAsync, tick } from '@angular/core/testing'; describe('ContextActionsDirective', () => { let directive; const contextMenuServiceMock = { open: jasmine.createSpy('open') }; - const storeMock = {}; - const documentListMock = {}; beforeEach(() => { - directive = new ContextActionsDirective( - documentListMock, - storeMock, - contextMenuServiceMock - ); + directive = new ContextActionsDirective(contextMenuServiceMock); }); - it('should not render context menu when disable property is false', () => { + it('should not render context menu when `enabled` property is false', () => { directive.enabled = false; - spyOn(directive, 'getSelectedRow').and.returnValue({}); - directive.onContextMenuEvent(new MouseEvent('contextmenu')); expect(contextMenuServiceMock.open).not.toHaveBeenCalled(); }); - it('should render context menu when disable property is true', () => { - directive.enabled = true; - spyOn(directive, 'getSelectedRow').and.returnValue({}); - spyOn(directive, 'isInSelection').and.returnValue(true); + it('should call service to render context menu', fakeAsync(() => { + const el = document.createElement('div'); + el.className = + 'adf-data-table-cell adf-datatable-table-cell adf-datatable-row'; - directive.onContextMenuEvent(new MouseEvent('contextmenu')); + const fragment = document.createDocumentFragment(); + fragment.appendChild(el); + const target = fragment.querySelector('div'); + + directive.ngOnInit(); + + directive.onContextMenuEvent({ preventDefault: () => {}, target }); + + tick(500); expect(contextMenuServiceMock.open).toHaveBeenCalled(); - }); + })); }); diff --git a/src/app/components/context-menu/context-menu.module.ts b/src/app/components/context-menu/context-menu.module.ts index c49341a35e..ab267b9cd5 100644 --- a/src/app/components/context-menu/context-menu.module.ts +++ b/src/app/components/context-menu/context-menu.module.ts @@ -38,6 +38,7 @@ import { ContextMenuComponent } from './context-menu.component'; import { ExtensionsModule } from '@alfresco/adf-extensions'; import { OutsideEventDirective } from './context-menu-outside-event.directive'; import { ContextMenuItemComponent } from './context-menu-item.component'; +import { AppCommonModule } from '../common/common.module'; @NgModule({ imports: [ @@ -47,6 +48,7 @@ import { ContextMenuItemComponent } from './context-menu-item.component'; MatButtonModule, CoreExtensionsModule.forChild(), CoreModule.forChild(), + AppCommonModule, ExtensionsModule ], declarations: [ diff --git a/src/app/components/create-menu/create-menu.component.html b/src/app/components/create-menu/create-menu.component.html index 38f7b924e7..411c58632d 100644 --- a/src/app/components/create-menu/create-menu.component.html +++ b/src/app/components/create-menu/create-menu.component.html @@ -11,11 +11,14 @@ diff --git a/src/app/components/create-menu/create-menu.component.scss b/src/app/components/create-menu/create-menu.component.scss index 519f628e78..c289908923 100644 --- a/src/app/components/create-menu/create-menu.component.scss +++ b/src/app/components/create-menu/create-menu.component.scss @@ -3,6 +3,10 @@ $accent: map-get($theme, accent); $primary: map-get($theme, primary); + .app-create-menu:focus { + outline: none; + } + .app-create-menu { width: 100%; @@ -44,6 +48,7 @@ } &--collapsed { + outline: none; color: mat-color($foreground, text, 0.54); cursor: pointer; &:hover { diff --git a/src/app/components/current-user/current-user.component.theme.scss b/src/app/components/current-user/current-user.component.theme.scss index 645a9e0ac8..34601f49c0 100644 --- a/src/app/components/current-user/current-user.component.theme.scss +++ b/src/app/components/current-user/current-user.component.theme.scss @@ -22,7 +22,7 @@ } .current-user__full-name { - width: 100px; + max-width: 100px; text-align: right; white-space: nowrap; overflow: hidden; diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.html b/src/app/components/favorite-libraries/favorite-libraries.component.html new file mode 100644 index 0000000000..b4be5c2ad0 --- /dev/null +++ b/src/app/components/favorite-libraries/favorite-libraries.component.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
\ No newline at end of file diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts b/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts new file mode 100644 index 0000000000..9c311719da --- /dev/null +++ b/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts @@ -0,0 +1,238 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { UserPreferencesService } from '@alfresco/adf-core'; +import { Router } from '@angular/router'; +import { + AlfrescoApiService, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DataTableComponent, + AppConfigPipe +} from '@alfresco/adf-core'; +import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { FavoriteLibrariesComponent } from './favorite-libraries.component'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { ContentApiService } from '../../services/content-api.service'; +import { ExperimentalDirective } from '../../directives/experimental.directive'; +import { ContentManagementService } from '../../services/content-management.service'; +import { EffectsModule } from '@ngrx/effects'; +import { LibraryEffects, RouterEffects } from '../../store/effects'; +import { of, throwError } from 'rxjs'; + +describe('FavoriteLibrariesComponent', () => { + let fixture: ComponentFixture; + let component: FavoriteLibrariesComponent; + let alfrescoApi: AlfrescoApiService; + let userPreference: UserPreferencesService; + let contentApiService: ContentApiService; + let router: Router; + let page; + let contentManagementService; + + beforeEach(() => { + page = { + list: { + entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], + pagination: { data: 'data' } + } + }; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + AppTestingModule, + EffectsModule.forRoot([RouterEffects, LibraryEffects]) + ], + declarations: [ + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + FavoriteLibrariesComponent, + AppConfigPipe, + ExperimentalDirective + ], + providers: [ContentManagementService, UserPreferencesService], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(FavoriteLibrariesComponent); + component = fixture.componentInstance; + + alfrescoApi = TestBed.get(AlfrescoApiService); + contentApiService = TestBed.get(ContentApiService); + userPreference = TestBed.get(UserPreferencesService); + contentManagementService = TestBed.get(ContentManagementService); + alfrescoApi.reset(); + router = TestBed.get(Router); + + spyOn(contentApiService, 'getNode').and.returnValue( + of({ entry: { id: 'libraryId' } }) + ); + }); + + describe('on initialization', () => { + it('should set data', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + fixture.detectChanges(); + + expect(component.list).toBe(page); + expect(component.pagination).toBe(page.list.pagination); + }); + + it('should get data with user preference pagination size', () => { + userPreference.paginationSize = 1; + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + + fixture.detectChanges(); + + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith( + '-me-', + { maxItems: userPreference.paginationSize } + ); + }); + + it('should set data on error', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + throwError('error') + ); + fixture.detectChanges(); + + expect(component.list).toBe(null); + expect(component.pagination).toBe(null); + expect(component.isLoading).toBe(false); + }); + }); + + describe('Node navigation', () => { + it('does not navigate when id is not passed', () => { + spyOn(router, 'navigate').and.stub(); + component.navigateTo(null); + + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('does not navigate when id is not passed', () => { + spyOn(router, 'navigate').and.stub(); + component.navigateTo({ entry: { guid: 'guid' } }); + + expect(router.navigate).toHaveBeenCalledWith(['libraries', 'libraryId']); + }); + }); + + describe('Reload on actions', () => { + beforeEach(() => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + fixture.detectChanges(); + }); + + it('should reload on libraryDeleted action', () => { + contentManagementService.libraryDeleted.next(); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); + }); + + it('should reload on libraryUpdated action', () => { + contentManagementService.libraryUpdated.next(); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); + }); + + it('should reload on favoriteLibraryToggle action', () => { + contentManagementService.favoriteLibraryToggle.next(); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); + }); + + it('should reload on libraryJoined action', () => { + contentManagementService.libraryJoined.next(); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); + }); + + it('should reload on libraryLeft action', () => { + contentManagementService.libraryLeft.next(); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); + }); + }); + + describe('Pagination', () => { + let pagination; + + beforeEach(() => { + pagination = { + count: 100, + hasMoreItems: true, + totalItems: 300, + skipCount: 25, + maxItems: 25 + }; + }); + + it('should get list with pagination data onChange event', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + + component.onChange(pagination); + + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith( + '-me-', + pagination + ); + }); + + it('should get list with pagination data onChangePageSize event', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + + component.onChangePageSize(pagination); + + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith( + '-me-', + pagination + ); + }); + + it('should set preference page size onChangePageSize event', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue( + of(page) + ); + + component.onChangePageSize(pagination); + + expect(userPreference.paginationSize).toBe(pagination.maxItems); + }); + }); +}); diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.ts b/src/app/components/favorite-libraries/favorite-libraries.component.ts new file mode 100644 index 0000000000..d76d317ca4 --- /dev/null +++ b/src/app/components/favorite-libraries/favorite-libraries.component.ts @@ -0,0 +1,115 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { SiteEntry, FavoritePaging, Pagination } from 'alfresco-js-api'; +import { AppExtensionService } from '../../extensions/extension.service'; +import { ContentManagementService } from '../../services/content-management.service'; +import { ContentApiService } from '../../services/content-api.service'; +import { NavigateLibraryAction } from '../../store/actions'; +import { AppStore } from '../../store/states/app.state'; +import { PageComponent } from '../page.component'; +import { UserPreferencesService } from '@alfresco/adf-core'; +@Component({ + templateUrl: './favorite-libraries.component.html' +}) +export class FavoriteLibrariesComponent extends PageComponent + implements OnInit { + pagination: Pagination; + isLoading = false; + list: FavoritePaging; + isSmallScreen = false; + columns: any[] = []; + + constructor( + content: ContentManagementService, + store: Store, + extensions: AppExtensionService, + private contentApiService: ContentApiService, + private breakpointObserver: BreakpointObserver, + private preferences: UserPreferencesService + ) { + super(store, extensions, content); + } + + ngOnInit() { + super.ngOnInit(); + + this.getList({ maxItems: this.preferences.paginationSize }); + + this.subscriptions = this.subscriptions.concat([ + this.content.libraryDeleted.subscribe(() => this.reloadList()), + this.content.libraryUpdated.subscribe(() => this.reloadList()), + this.content.libraryJoined.subscribe(() => this.reloadList()), + this.content.libraryLeft.subscribe(() => this.reloadList()), + this.content.favoriteLibraryToggle.subscribe(() => this.reloadList()), + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ]); + this.columns = this.extensions.documentListPresets.favoriteLibraries || []; + } + + navigateTo(node: SiteEntry) { + if (node && node.entry && node.entry.guid) { + this.store.dispatch(new NavigateLibraryAction(node.entry.guid)); + } + } + + onChangePageSize(pagination: Pagination) { + this.preferences.paginationSize = pagination.maxItems; + this.getList(pagination); + } + + onChange(pagination: Pagination) { + this.getList(pagination); + } + + private getList(pagination: Pagination) { + this.isLoading = true; + this.contentApiService.getFavoriteLibraries('-me-', pagination).subscribe( + (favoriteLibraries: FavoritePaging) => { + this.list = favoriteLibraries; + this.pagination = favoriteLibraries.list.pagination; + this.isLoading = false; + }, + () => { + this.list = null; + this.pagination = null; + this.isLoading = false; + } + ); + } + + private reloadList() { + this.reload(); + this.getList(this.pagination); + } +} diff --git a/src/app/components/favorites/favorites.component.html b/src/app/components/favorites/favorites.component.html index 375b1ebeba..79c1bcb55e 100644 --- a/src/app/components/favorites/favorites.component.html +++ b/src/app/components/favorites/favorites.component.html @@ -1,80 +1,81 @@ -
-
- - + - - + + + - - - - -
-
-
- + + + + + + - - - - - - + +
+ + + + + + + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - - + + + - - -
+ + +
-
- -
+ -
+ + + diff --git a/src/app/components/favorites/favorites.component.ts b/src/app/components/favorites/favorites.component.ts index 1b42b5f8dc..3240304c5f 100644 --- a/src/app/components/favorites/favorites.component.ts +++ b/src/app/components/favorites/favorites.component.ts @@ -85,8 +85,10 @@ export class FavoritesComponent extends PageComponent implements OnInit { // TODO: rework as it will fail on non-English setups const isSitePath = (path: PathInfo): boolean => { - return path.elements.some( - ({ name }: PathElementEntity) => name === 'Sites' + return ( + path && + path.elements && + path.elements.some(({ name }: PathElementEntity) => name === 'Sites') ); }; diff --git a/src/app/components/favorites/favorites.module.ts b/src/app/components/favorites/favorites.module.ts index cb53932813..ac257a54eb 100644 --- a/src/app/components/favorites/favorites.module.ts +++ b/src/app/components/favorites/favorites.module.ts @@ -34,6 +34,7 @@ import { AppCommonModule } from '../common/common.module'; import { AppToolbarModule } from '../toolbar/toolbar.module'; import { ContextMenuModule } from '../context-menu/context-menu.module'; import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; +import { AppLayoutModule } from '../layout/layout.module'; const routes: Routes = [ { @@ -56,7 +57,8 @@ const routes: Routes = [ AppCommonModule, AppToolbarModule, ContextMenuModule, - AppInfoDrawerModule + AppInfoDrawerModule, + AppLayoutModule ], declarations: [FavoritesComponent], exports: [FavoritesComponent] diff --git a/src/app/components/files/files.component.html b/src/app/components/files/files.component.html index 89ea55f080..0b8c2e4ad4 100644 --- a/src/app/components/files/files.component.html +++ b/src/app/components/files/files.component.html @@ -1,84 +1,85 @@ -
-
- - + - - - - - - -
+ + + -
- -
+ + + + + +
-
-
- + + + - + +
+ - - + - - - - - - - - + + - - - - + + + + + + + + - - - + + + + - - - -
+ + +
-
- -
+ + +
-
+ + + + + diff --git a/src/app/components/files/files.component.ts b/src/app/components/files/files.component.ts index ccdb6ba571..22c367f772 100644 --- a/src/app/components/files/files.component.ts +++ b/src/app/components/files/files.component.ts @@ -41,7 +41,8 @@ import { ContentApiService } from '../../services/content-api.service'; import { AppExtensionService } from '../../extensions/extension.service'; import { SetCurrentFolderAction } from '../../store/actions'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { debounceTime } from 'rxjs/operators'; +import { debounceTime, takeUntil } from 'rxjs/operators'; +import { isAdmin } from '../../store/selectors/app.selectors'; @Component({ templateUrl: './files.component.html' @@ -49,6 +50,7 @@ import { debounceTime } from 'rxjs/operators'; export class FilesComponent extends PageComponent implements OnInit, OnDestroy { isValidPath = true; isSmallScreen = false; + isAdmin = false; private nodePath: PathElement[]; @@ -118,6 +120,13 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { }) ]); + this.store + .select(isAdmin) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(value => { + this.isAdmin = value; + }); + this.columns = this.extensions.documentListPresets.files || []; } @@ -238,7 +247,9 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { if (elements.length > 1) { if (elements[1].name === 'User Homes') { - elements.splice(0, 2); + if (!this.isAdmin) { + elements.splice(0, 2); + } } else if (elements[1].name === 'Sites') { await this.normalizeSitePath(node); } diff --git a/src/app/components/header/header.component.html b/src/app/components/header/header.component.html index 4cfee6db66..809debb4bd 100644 --- a/src/app/components/header/header.component.html +++ b/src/app/components/header/header.component.html @@ -6,12 +6,11 @@ [title]="appName$ | async" (clicked)="toggleClicked.emit($event)"> -
+
- - - - + + +
diff --git a/src/app/components/header/header.component.ts b/src/app/components/header/header.component.ts index 5814b000ec..65c110b26b 100644 --- a/src/app/components/header/header.component.ts +++ b/src/app/components/header/header.component.ts @@ -31,16 +31,15 @@ import { OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { AppStore } from 'src/app/store/states'; import { Observable } from 'rxjs'; import { selectHeaderColor, selectAppName, selectLogoPath -} from 'src/app/store/selectors/app.selectors'; -import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; +} from '../../store/selectors/app.selectors'; import { ContentActionRef } from '@alfresco/adf-extensions'; import { AppExtensionService } from '../../extensions/extension.service'; +import { AppStore } from '../../store/states'; @Component({ selector: 'app-header', @@ -56,12 +55,10 @@ export class AppHeaderComponent implements OnInit { headerColor$: Observable; logo$: Observable; - isSmallScreen = false; actions: Array = []; constructor( store: Store, - private breakpointObserver: BreakpointObserver, private appExtensions: AppExtensionService ) { this.headerColor$ = store.select(selectHeaderColor); @@ -71,12 +68,6 @@ export class AppHeaderComponent implements OnInit { ngOnInit() { this.actions = this.appExtensions.getHeaderActions(); - - this.breakpointObserver - .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }); } trackByActionId(index: number, action: ContentActionRef) { diff --git a/src/app/components/info-drawer/comments-tab/comments-tab.component.ts b/src/app/components/info-drawer/comments-tab/comments-tab.component.ts index a02e5a19d8..91273d28ba 100644 --- a/src/app/components/info-drawer/comments-tab/comments-tab.component.ts +++ b/src/app/components/info-drawer/comments-tab/comments-tab.component.ts @@ -30,8 +30,11 @@ import { NodePermissionService } from '../../../services/node-permission.service @Component({ selector: 'app-comments-tab', template: ` - - ` + + ` }) export class CommentsTabComponent { @Input() diff --git a/src/app/components/info-drawer/info-drawer.component.spec.ts b/src/app/components/info-drawer/info-drawer.component.spec.ts index c11483713d..dc2311361e 100644 --- a/src/app/components/info-drawer/info-drawer.component.spec.ts +++ b/src/app/components/info-drawer/info-drawer.component.spec.ts @@ -22,11 +22,137 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ - +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { InfoDrawerComponent } from './info-drawer.component'; +import { TestBed, ComponentFixture, async } from '@angular/core/testing'; +import { Store } from '@ngrx/store'; +import { SetInfoDrawerStateAction } from '../../store/actions'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { AppExtensionService } from '../../extensions/extension.service'; +import { ContentApiService } from '../../services/content-api.service'; +import { of } from 'rxjs'; describe('InfoDrawerComponent', () => { - it('should be defined', () => { - expect(InfoDrawerComponent).toBeDefined(); + let fixture: ComponentFixture; + let component: InfoDrawerComponent; + let contentApiService; + let tab; + let appExtensionService; + const storeMock = { + dispatch: jasmine.createSpy('dispatch') + }; + const extensionServiceMock = { + getSidebarTabs: () => {} + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [InfoDrawerComponent], + providers: [ + ContentApiService, + { provide: AppExtensionService, useValue: extensionServiceMock }, + { provide: Store, useValue: storeMock } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(InfoDrawerComponent); + component = fixture.componentInstance; + appExtensionService = TestBed.get(AppExtensionService); + contentApiService = TestBed.get(ContentApiService); + + tab = { title: 'tab1' }; + spyOn(appExtensionService, 'getSidebarTabs').and.returnValue([tab]); + }); + + it('should get tabs configuration on initialization', () => { + fixture.detectChanges(); + + expect(component.tabs).toEqual([tab]); + }); + + it('should set state to false OnDestroy event', () => { + fixture.detectChanges(); + component.ngOnDestroy(); + + expect(storeMock.dispatch).toHaveBeenCalledWith( + new SetInfoDrawerStateAction(false) + ); + }); + + it('should set displayNode when node is from personal list', () => { + spyOn(contentApiService, 'getNodeInfo'); + const nodeMock = { entry: { id: 'nodeId' } }; + component.node = nodeMock; + + fixture.detectChanges(); + component.ngOnChanges(); + + expect(component.displayNode).toBe(nodeMock.entry); + expect(contentApiService.getNodeInfo).not.toHaveBeenCalled(); }); + + it('should set displayNode when node is library', async(() => { + spyOn(contentApiService, 'getNodeInfo'); + const nodeMock = { + entry: { id: 'nodeId' }, + isLibrary: true + }; + component.node = nodeMock; + + fixture.detectChanges(); + component.ngOnChanges(); + + expect(component.displayNode).toBe(nodeMock); + expect(contentApiService.getNodeInfo).not.toHaveBeenCalled(); + })); + + it('should call getNodeInfo() when node is a shared file', async(() => { + const response = { entry: { id: 'nodeId' } }; + spyOn(contentApiService, 'getNodeInfo').and.returnValue(of(response)); + const nodeMock = { entry: { nodeId: 'nodeId' }, isLibrary: false }; + component.node = nodeMock; + + fixture.detectChanges(); + component.ngOnChanges(); + + expect(component.displayNode).toBe(response); + expect(contentApiService.getNodeInfo).toHaveBeenCalled(); + })); + + it('should call getNodeInfo() when node is a favorite file', async(() => { + const response = { entry: { id: 'nodeId' } }; + spyOn(contentApiService, 'getNodeInfo').and.returnValue(of(response)); + const nodeMock = { + entry: { id: 'nodeId', guid: 'guidId' }, + isLibrary: false + }; + component.node = nodeMock; + + fixture.detectChanges(); + component.ngOnChanges(); + + expect(component.displayNode).toBe(response); + expect(contentApiService.getNodeInfo).toHaveBeenCalled(); + })); + + it('should call getNodeInfo() when node is a recent file', async(() => { + const response = { entry: { id: 'nodeId' } }; + spyOn(contentApiService, 'getNodeInfo').and.returnValue(of(response)); + const nodeMock = { + entry: { + id: 'nodeId', + content: { mimeType: 'image/jpeg' } + }, + isLibrary: false + }; + component.node = nodeMock; + + fixture.detectChanges(); + component.ngOnChanges(); + + expect(component.displayNode).toBe(response); + expect(contentApiService.getNodeInfo).toHaveBeenCalled(); + })); }); diff --git a/src/app/components/info-drawer/info-drawer.component.ts b/src/app/components/info-drawer/info-drawer.component.ts index d5d77767ec..9572d5140f 100644 --- a/src/app/components/info-drawer/info-drawer.component.ts +++ b/src/app/components/info-drawer/info-drawer.component.ts @@ -23,27 +23,35 @@ * along with Alfresco. If not, see . */ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; -import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { Component, Input, OnChanges, OnInit, OnDestroy } from '@angular/core'; +import { + MinimalNodeEntity, + MinimalNodeEntryEntity, + SiteEntry +} from 'alfresco-js-api'; import { ContentApiService } from '../../services/content-api.service'; import { AppExtensionService } from '../../extensions/extension.service'; import { SidebarTabRef } from '@alfresco/adf-extensions'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../store/states/app.state'; +import { SetInfoDrawerStateAction } from '../../store/actions'; @Component({ selector: 'aca-info-drawer', templateUrl: './info-drawer.component.html' }) -export class InfoDrawerComponent implements OnChanges, OnInit { +export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy { @Input() nodeId: string; @Input() node: MinimalNodeEntity; isLoading = false; - displayNode: MinimalNodeEntryEntity; + displayNode: MinimalNodeEntryEntity | SiteEntry; tabs: Array = []; constructor( + private store: Store, private contentApi: ContentApiService, private extensions: AppExtensionService ) {} @@ -52,22 +60,31 @@ export class InfoDrawerComponent implements OnChanges, OnInit { this.tabs = this.extensions.getSidebarTabs(); } + ngOnDestroy() { + this.store.dispatch(new SetInfoDrawerStateAction(false)); + } + ngOnChanges() { if (this.node) { const entry = this.node.entry; - if (entry.nodeId) { - this.loadNodeInfo(entry.nodeId); - } else if ((entry).guid) { - // workaround for Favorite files - this.loadNodeInfo(entry.id); - } else { - // workaround Recent - if (this.isTypeImage(entry) && !this.hasAspectNames(entry)) { - this.loadNodeInfo(this.node.entry.id); - } else { - this.setDisplayNode(this.node.entry); - } + + if (this.isLibraryListNode(this.node)) { + return this.setDisplayNode(this.node); + } + + if (this.isSharedFilesNode(this.node)) { + return this.loadNodeInfo(entry.nodeId); + } + + if (this.isFavoriteListNode(this.node)) { + return this.loadNodeInfo(entry.id); + } + + if (this.isRecentListFileNode(this.node)) { + return this.loadNodeInfo(entry.id); } + + this.setDisplayNode(entry); } } @@ -96,7 +113,23 @@ export class InfoDrawerComponent implements OnChanges, OnInit { } } - private setDisplayNode(node: MinimalNodeEntryEntity) { + private setDisplayNode(node: MinimalNodeEntryEntity | SiteEntry) { this.displayNode = node; } + + private isLibraryListNode(node: SiteEntry): boolean { + return (node).isLibrary; + } + + private isFavoriteListNode(node: MinimalNodeEntity): boolean { + return !this.isLibraryListNode(node) && (node).entry.guid; + } + + private isSharedFilesNode(node: MinimalNodeEntity): boolean { + return !!node.entry.nodeId; + } + + private isRecentListFileNode(node: MinimalNodeEntity): boolean { + return this.isTypeImage(node.entry) && !this.hasAspectNames(node.entry); + } } diff --git a/src/app/components/info-drawer/info.drawer.module.ts b/src/app/components/info-drawer/info.drawer.module.ts index 4511f357d6..5e26a34a64 100644 --- a/src/app/components/info-drawer/info.drawer.module.ts +++ b/src/app/components/info-drawer/info.drawer.module.ts @@ -36,6 +36,8 @@ import { MaterialModule } from '../../material.module'; import { CommentsTabComponent } from './comments-tab/comments-tab.component'; import { InfoDrawerComponent } from './info-drawer.component'; import { MetadataTabComponent } from './metadata-tab/metadata-tab.component'; +import { LibraryMetadataTabComponent } from './library-metadata-tab/library-metadata-tab.component'; +import { LibraryMetadataFormComponent } from './library-metadata-tab/library-metadata-form.component'; import { VersionsTabComponent } from './versions-tab/versions-tab.component'; export function components() { @@ -43,7 +45,9 @@ export function components() { InfoDrawerComponent, MetadataTabComponent, CommentsTabComponent, - VersionsTabComponent + VersionsTabComponent, + LibraryMetadataTabComponent, + LibraryMetadataFormComponent ]; } diff --git a/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.html b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.html new file mode 100644 index 0000000000..ac0b0a38bf --- /dev/null +++ b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.html @@ -0,0 +1,125 @@ + + +
+
+
+
+ + + {{ 'LIBRARY.DIALOG.FORM.NAME' | translate }} + + + + + {{ form.controls.title.value }} + +
+
+
+
+ +
+
+
+
+ + + {{ 'LIBRARY.DIALOG.FORM.SITE_ID' | translate }} + + + + + {{ form.controls.id.value }} + +
+
+
+
+ +
+
+
+
+ + + {{ 'LIBRARY.DIALOG.FORM.VISIBILITY' | translate }} + + + + + {{ (getVisibilityLabel(form.controls.visibility.value)) | translate }} + +
+
+
+
+ +
+
+
+
+ + + {{ 'LIBRARY.DIALOG.FORM.DESCRIPTION' | translate }} + + + + + {{ form.controls.description?.value }} + +
+
+
+
+
+ + + + + + +
+ + + + {{ 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate }} + + {{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }} + + + + + + + + + + + {{ type.label | translate }} + + + + + + + + + {{ 'LIBRARY.ERRORS.DESCRIPTION_TOO_LONG' | translate }} + + +
+
+ + + + + +
\ No newline at end of file diff --git a/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts new file mode 100644 index 0000000000..ab7652fb68 --- /dev/null +++ b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts @@ -0,0 +1,319 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +import { LibraryMetadataFormComponent } from './library-metadata-form.component'; +import { + TestBed, + ComponentFixture, + fakeAsync, + tick +} from '@angular/core/testing'; +import { Store } from '@ngrx/store'; +import { UpdateLibraryAction } from '../../../store/actions'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Site, SiteBody } from 'alfresco-js-api'; +import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-core'; + +describe('LibraryMetadataFormComponent', () => { + let fixture: ComponentFixture; + let component: LibraryMetadataFormComponent; + let alfrescoApiService: AlfrescoApiService; + const storeMock = { + dispatch: jasmine.createSpy('dispatch') + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [LibraryMetadataFormComponent], + providers: [ + { provide: Store, useValue: storeMock }, + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(LibraryMetadataFormComponent); + component = fixture.componentInstance; + alfrescoApiService = TestBed.get(AlfrescoApiService); + }); + + afterEach(() => { + storeMock.dispatch.calls.reset(); + }); + + it('should initialize form with node data', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + fixture.detectChanges(); + + expect(component.form.value).toEqual(siteEntryModel); + }); + + it('should update form data when node data changes', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + + const newSiteEntryModel = { + title: 'libraryTitle2', + description: 'description2', + visibility: 'PUBLIC' + }; + + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + + expect(component.form.value).toEqual(siteEntryModel); + + component.node = { + entry: { + id: 'libraryId', + ...newSiteEntryModel + } + }; + + component.ngOnChanges(); + + expect(component.form.value).toEqual(newSiteEntryModel); + }); + + it('should update library node if form is valid', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + component.node = { + entry: { + id: 'libraryId', + role: 'SiteManager', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + + component.update(); + + expect(storeMock.dispatch).toHaveBeenCalledWith( + new UpdateLibraryAction(siteEntryModel) + ); + }); + + it('should not update library node if it has no permission', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + component.node = { + entry: { + id: 'libraryId', + role: 'Consumer', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + + component.update(); + + expect(storeMock.dispatch).not.toHaveBeenCalledWith( + new UpdateLibraryAction(siteEntryModel) + ); + }); + + it('should not update library node if form is invalid', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + component.node = { + entry: { + id: 'libraryId', + role: 'SiteManager', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + + component.form.controls['title'].setErrors({ maxlength: true }); + + component.update(); + + expect(storeMock.dispatch).not.toHaveBeenCalledWith( + new UpdateLibraryAction(siteEntryModel) + ); + }); + + it('should toggle edit mode', () => { + component.edit = false; + + component.toggleEdit(); + expect(component.edit).toBe(true); + + component.toggleEdit(); + expect(component.edit).toBe(false); + }); + + it('should cancel from changes', () => { + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + fixture.detectChanges(); + + expect(component.form.value).toEqual(siteEntryModel); + + component.form.controls.title.setValue('libraryTitle-edit'); + + expect(component.form.value.title).toBe('libraryTitle-edit'); + + component.cancel(); + + expect(component.form.value).toEqual(siteEntryModel); + }); + + it('should warn if library name input is used by another library', fakeAsync(() => { + const title = 'some-title'; + spyOn( + alfrescoApiService.getInstance().core.queriesApi, + 'findSites' + ).and.returnValue( + Promise.resolve({ + list: { entries: [{ entry: { title } }] } + }) + ); + + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + component.form.controls.title.setValue(title); + fixture.detectChanges(); + + tick(500); + expect(component.libraryTitleExists).toBe(true); + })); + + it('should not warn if library name input is the same with library node data', fakeAsync(() => { + spyOn( + alfrescoApiService.getInstance().core.queriesApi, + 'findSites' + ).and.returnValue( + Promise.resolve({ + list: { entries: [{ entry: { title: 'libraryTitle' } }] } + }) + ); + + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + component.form.controls.title.setValue('libraryTitle'); + fixture.detectChanges(); + + tick(500); + expect(component.libraryTitleExists).toBe(false); + })); + + it('should not warn if library name is unique', fakeAsync(() => { + spyOn( + alfrescoApiService.getInstance().core.queriesApi, + 'findSites' + ).and.returnValue( + Promise.resolve({ + list: { entries: [] } + }) + ); + + const siteEntryModel = { + title: 'libraryTitle', + description: 'description', + visibility: 'PRIVATE' + }; + + component.node = { + entry: { + id: 'libraryId', + ...siteEntryModel + } + }; + + fixture.detectChanges(); + component.form.controls.title.setValue('some-name'); + fixture.detectChanges(); + + tick(500); + expect(component.libraryTitleExists).toBe(false); + })); +}); diff --git a/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts new file mode 100644 index 0000000000..c9a707d9e6 --- /dev/null +++ b/src/app/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts @@ -0,0 +1,152 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, Input, OnInit, OnChanges, OnDestroy } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { SiteEntry, SitePaging } from 'alfresco-js-api'; +import { Store } from '@ngrx/store'; +import { UpdateLibraryAction } from '../../../store/actions'; +import { AppStore } from '../../../store/states/app.state'; +import { debounceTime, mergeMap, takeUntil } from 'rxjs/operators'; +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { Observable, from, Subject } from 'rxjs'; + +@Component({ + selector: 'app-library-metadata-form', + templateUrl: './library-metadata-form.component.html' +}) +export class LibraryMetadataFormComponent + implements OnInit, OnChanges, OnDestroy { + @Input() + node: SiteEntry; + + edit: boolean; + libraryTitleExists = false; + + libraryType = [ + { value: 'PUBLIC', label: 'LIBRARY.VISIBILITY.PUBLIC' }, + { value: 'PRIVATE', label: 'LIBRARY.VISIBILITY.PRIVATE' }, + { value: 'MODERATED', label: 'LIBRARY.VISIBILITY.MODERATED' } + ]; + + form: FormGroup = new FormGroup({ + id: new FormControl({ value: '', disabled: true }), + title: new FormControl({ value: '' }, [ + Validators.required, + Validators.maxLength(256) + ]), + description: new FormControl({ value: '' }, [Validators.maxLength(512)]), + visibility: new FormControl(this.libraryType[0].value) + }); + + onDestroy$: Subject = new Subject(); + + constructor( + private alfrescoApiService: AlfrescoApiService, + protected store: Store + ) {} + + get canUpdateLibrary() { + return ( + this.node && this.node.entry && this.node.entry.role === 'SiteManager' + ); + } + + getVisibilityLabel(value) { + return this.libraryType.find(type => type.value === value).label; + } + + toggleEdit() { + this.edit = !this.edit; + } + + cancel() { + this.updateForm(this.node); + this.toggleEdit(); + } + + ngOnInit() { + this.updateForm(this.node); + + this.form.controls['title'].valueChanges + .pipe( + debounceTime(300), + mergeMap(title => this.findLibraryByTitle(title)), + takeUntil(this.onDestroy$) + ) + .subscribe(result => { + const { entries } = result.list; + + if (entries.length) { + if (this.form.controls.title.value === this.node.entry.title) { + this.libraryTitleExists = false; + } else { + this.libraryTitleExists = + this.form.controls.title.value === entries[0].entry.title; + } + } else { + this.libraryTitleExists = false; + } + }); + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + ngOnChanges() { + this.updateForm(this.node); + } + + update() { + if (this.canUpdateLibrary && this.form.valid) { + this.store.dispatch(new UpdateLibraryAction(this.form.value)); + } + } + + private updateForm(node: SiteEntry) { + const { entry } = node; + + this.form.setValue({ + id: entry.id, + title: entry.title, + description: entry.description || '', + visibility: entry.visibility + }); + } + + private findLibraryByTitle(libraryTitle: string): Observable { + return from( + this.alfrescoApiService + .getInstance() + .core.queriesApi.findSites(libraryTitle, { + maxItems: 1, + fields: ['title'] + }) + .catch(() => ({ list: { entries: [] } })) + ); + } +} diff --git a/e2e/pages/logout-page.ts b/src/app/components/info-drawer/library-metadata-tab/library-metadata-tab.component.ts old mode 100755 new mode 100644 similarity index 75% rename from e2e/pages/logout-page.ts rename to src/app/components/info-drawer/library-metadata-tab/library-metadata-tab.component.ts index da309446b9..317d15a3fa --- a/e2e/pages/logout-page.ts +++ b/src/app/components/info-drawer/library-metadata-tab/library-metadata-tab.component.ts @@ -23,20 +23,16 @@ * along with Alfresco. If not, see . */ -import { Page } from './page'; -import { APP_ROUTES } from '../configs'; -import { Utils } from '../utilities/utils'; +import { Component, Input } from '@angular/core'; +import { SiteEntry } from 'alfresco-js-api'; -export class LogoutPage extends Page { - /** @override */ - constructor() { - super(APP_ROUTES.LOGIN); - } - - /** @override */ - load() { - // await Utils.clearLocalStorage(); - // await Utils.clearSessionStorage(); - return super.load(); - } +@Component({ + selector: 'app-metadata-tab', + template: + '', + host: { class: 'app-metadata-tab' } +}) +export class LibraryMetadataTabComponent { + @Input() + node: SiteEntry; } diff --git a/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts b/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts index d0b46c0128..6aa52c59bb 100644 --- a/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts +++ b/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts @@ -36,9 +36,10 @@ import { AppConfigService } from '@alfresco/adf-core'; [readOnly]="!canUpdateNode" [displayEmpty]="canUpdateNode" [preset]="'custom'" - [node]="node"> + [node]="node" + > - `, + `, encapsulation: ViewEncapsulation.None, host: { class: 'app-metadata-tab' } }) @@ -64,6 +65,25 @@ export class MetadataTabComponent { } get canUpdateNode() { - return this.node && this.permission.check(this.node, ['update']); + if (this.node) { + if (this.fileIsLocked()) { + return false; + } + return this.permission.check(this.node, ['update']); + } + + return false; + } + + private fileIsLocked() { + if (!this.node.isFile) { + return false; + } + + return ( + this.node.isLocked || + (this.node.properties && + this.node.properties['cm:lockType'] === 'READ_ONLY_LOCK') + ); } } diff --git a/src/app/components/info-drawer/versions-tab/versions-tab.component.ts b/src/app/components/info-drawer/versions-tab/versions-tab.component.ts index 1a7faddb11..c610cd271a 100644 --- a/src/app/components/info-drawer/versions-tab/versions-tab.component.ts +++ b/src/app/components/info-drawer/versions-tab/versions-tab.component.ts @@ -29,21 +29,26 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api'; @Component({ selector: 'app-versions-tab', template: ` - - - - + + + + - -
- face - {{ 'VERSION.SELECTION.EMPTY' | translate }} -
-
- ` + +
+ face + {{ 'VERSION.SELECTION.EMPTY' | translate }} +
+
+ ` }) export class VersionsTabComponent implements OnInit, OnChanges { @Input() diff --git a/src/app/components/layout/app-layout/app-layout.component.html b/src/app/components/layout/app-layout/app-layout.component.html new file mode 100644 index 0000000000..63b169c002 --- /dev/null +++ b/src/app/components/layout/app-layout/app-layout.component.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/components/layout/layout.component.spec.ts b/src/app/components/layout/app-layout/app-layout.component.spec.ts similarity index 59% rename from src/app/components/layout/layout.component.spec.ts rename to src/app/components/layout/app-layout/app-layout.component.spec.ts index 2c873de7c6..56c5c6b80f 100644 --- a/src/app/components/layout/layout.component.spec.ts +++ b/src/app/components/layout/app-layout/app-layout.component.spec.ts @@ -26,25 +26,54 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core'; -import { LayoutComponent } from './layout.component'; -import { AppTestingModule } from '../../testing/app-testing.module'; - -describe('LayoutComponent', () => { - let fixture: ComponentFixture; - let component: LayoutComponent; +import { AppLayoutComponent } from './app-layout.component'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../../store/states'; +import { SetSelectedNodesAction } from '../../../store/actions'; +import { Router, NavigationStart } from '@angular/router'; +import { appSelection } from '../../../store/selectors/app.selectors'; +import { Subject } from 'rxjs'; + +class MockRouter { + private url = 'some-url'; + private subject = new Subject(); + events = this.subject.asObservable(); + routerState = { snapshot: { url: this.url } }; + + navigateByUrl(url: string) { + const navigationStart = new NavigationStart(0, url); + this.subject.next(navigationStart); + } +} + +describe('AppLayoutComponent', () => { + let fixture: ComponentFixture; + let component: AppLayoutComponent; let appConfig: AppConfigService; let userPreference: UserPreferencesService; + let store: Store; + let router: Router; beforeEach(() => { TestBed.configureTestingModule({ imports: [AppTestingModule], - declarations: [LayoutComponent], + providers: [ + Store, + { + provide: Router, + useClass: MockRouter + } + ], + declarations: [AppLayoutComponent], schemas: [NO_ERRORS_SCHEMA] }); - fixture = TestBed.createComponent(LayoutComponent); + fixture = TestBed.createComponent(AppLayoutComponent); component = fixture.componentInstance; appConfig = TestBed.get(AppConfigService); + store = TestBed.get(Store); + router = TestBed.get(Router); userPreference = TestBed.get(UserPreferencesService); }); @@ -108,4 +137,30 @@ describe('LayoutComponent', () => { expect(component.expandedSidenav).toBe(false); }); }); + + it('should reset selection before navigation', done => { + fixture.detectChanges(); + const selection = [{ entry: { id: 'nodeId', name: 'name' } }]; + store.dispatch(new SetSelectedNodesAction(selection)); + + router.navigateByUrl('somewhere/over/the/rainbow'); + fixture.detectChanges(); + store.select(appSelection).subscribe(state => { + expect(state.isEmpty).toBe(true); + done(); + }); + }); + + it('should not reset selection if route is `/search`', done => { + fixture.detectChanges(); + const selection = [{ entry: { id: 'nodeId', name: 'name' } }]; + store.dispatch(new SetSelectedNodesAction(selection)); + + router.navigateByUrl('/search;q='); + fixture.detectChanges(); + store.select(appSelection).subscribe(state => { + expect(state.isEmpty).toBe(false); + done(); + }); + }); }); diff --git a/src/app/components/layout/layout.component.ts b/src/app/components/layout/app-layout/app-layout.component.ts similarity index 85% rename from src/app/components/layout/layout.component.ts rename to src/app/components/layout/app-layout/app-layout.component.ts index 59925adcf9..7ad898d830 100644 --- a/src/app/components/layout/layout.component.ts +++ b/src/app/components/layout/app-layout/app-layout.component.ts @@ -35,23 +35,23 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; -import { NavigationEnd, Router } from '@angular/router'; +import { NavigationEnd, Router, NavigationStart } from '@angular/router'; import { Store } from '@ngrx/store'; import { Subject, Observable } from 'rxjs'; import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators'; -import { NodePermissionService } from '../../services/node-permission.service'; -import { currentFolder } from '../../store/selectors/app.selectors'; -import { AppStore } from '../../store/states'; +import { NodePermissionService } from '../../../services/node-permission.service'; +import { currentFolder } from '../../../store/selectors/app.selectors'; +import { AppStore } from '../../../store/states'; import { BreakpointObserver } from '@angular/cdk/layout'; +import { SetSelectedNodesAction } from '../../../store/actions'; @Component({ selector: 'app-layout', - templateUrl: './layout.component.html', - styleUrls: ['./layout.component.scss'], + templateUrl: './app-layout.component.html', encapsulation: ViewEncapsulation.None, host: { class: 'app-layout' } }) -export class LayoutComponent implements OnInit, OnDestroy { +export class AppLayoutComponent implements OnInit, OnDestroy { @ViewChild('layout') layout: SidenavLayoutComponent; @@ -114,7 +114,7 @@ export class LayoutComponent implements OnInit, OnDestroy { takeUntil(this.onDestroy$) ) .subscribe(() => { - this.layout.container.toggleMenu(); + this.layout.container.sidenav.close(); }); this.router.events @@ -132,6 +132,19 @@ export class LayoutComponent implements OnInit, OnDestroy { this.updateState(); }); + + this.router.events + .pipe( + filter(event => { + return ( + event instanceof NavigationStart && + // search employs reuse route strategy + !event.url.startsWith('/search;') + ); + }), + takeUntil(this.onDestroy$) + ) + .subscribe(() => this.store.dispatch(new SetSelectedNodesAction([]))); } ngOnDestroy() { diff --git a/src/app/components/layout/app-layout/app-layout.theme.scss b/src/app/components/layout/app-layout/app-layout.theme.scss new file mode 100644 index 0000000000..db0104dc71 --- /dev/null +++ b/src/app/components/layout/app-layout/app-layout.theme.scss @@ -0,0 +1,17 @@ +@mixin app-layout-theme($theme) { + .app-layout { + @include flex-column; + } + + @media screen and (max-width: 599px) { + .adf-app-title { + display: none; + } + } + + @media screen and (max-width: 719px) { + .adf-app-logo { + display: none; + } + } +} diff --git a/src/app/components/layout/layout.component.html b/src/app/components/layout/layout.component.html deleted file mode 100644 index 732ad97071..0000000000 --- a/src/app/components/layout/layout.component.html +++ /dev/null @@ -1,40 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/app/components/layout/layout.component.scss b/src/app/components/layout/layout.component.scss deleted file mode 100644 index e1a3024ff8..0000000000 --- a/src/app/components/layout/layout.component.scss +++ /dev/null @@ -1,16 +0,0 @@ -:host { - display: flex; - flex: 1; -} - -@media screen and (max-width: 599px) { - .adf-app-title { - display: none; - } -} - -@media screen and (max-width: 719px) { - .adf-app-logo { - display: none; - } -} diff --git a/src/app/components/layout/layout.module.ts b/src/app/components/layout/layout.module.ts index 8d179ee6b5..4ce8ba5d05 100644 --- a/src/app/components/layout/layout.module.ts +++ b/src/app/components/layout/layout.module.ts @@ -26,12 +26,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreModule } from '@alfresco/adf-core'; -import { LayoutComponent } from './layout.component'; +import { AppLayoutComponent } from './app-layout/app-layout.component'; import { ContentModule } from '@alfresco/adf-content-services'; import { RouterModule } from '@angular/router'; import { AppSidenavModule } from '../sidenav/sidenav.module'; import { AppCommonModule } from '../common/common.module'; import { AppHeaderModule } from '../header/header.module'; +import { AppUploadingDialogModule } from '../upload-dialog/upload-dialog.module'; +import { PageLayoutComponent } from './page-layout/page-layout.component'; +import { PageLayoutHeaderComponent } from './page-layout/page-layout-header.component'; +import { PageLayoutContentComponent } from './page-layout/page-layout-content.component'; +import { PageLayoutErrorComponent } from './page-layout/page-layout-error.component'; +import { HttpClientModule } from '@angular/common/http'; @NgModule({ imports: [ @@ -41,9 +47,23 @@ import { AppHeaderModule } from '../header/header.module'; ContentModule.forChild(), AppCommonModule, AppSidenavModule, - AppHeaderModule + AppHeaderModule, + HttpClientModule, + AppUploadingDialogModule ], - declarations: [LayoutComponent], - exports: [LayoutComponent] + declarations: [ + AppLayoutComponent, + PageLayoutComponent, + PageLayoutHeaderComponent, + PageLayoutContentComponent, + PageLayoutErrorComponent + ], + exports: [ + AppLayoutComponent, + PageLayoutComponent, + PageLayoutHeaderComponent, + PageLayoutContentComponent, + PageLayoutErrorComponent + ] }) export class AppLayoutModule {} diff --git a/src/app/components/layout/layout.theme.scss b/src/app/components/layout/layout.theme.scss new file mode 100644 index 0000000000..e9914e6103 --- /dev/null +++ b/src/app/components/layout/layout.theme.scss @@ -0,0 +1,7 @@ +@import './app-layout/app-layout.theme.scss'; +@import './page-layout/page-layout.theme.scss'; + +@mixin layout-theme($theme) { + @include app-layout-theme($theme); + @include app-page-layout-theme($theme); +} diff --git a/src/app/components/layout/page-layout/page-layout-content.component.ts b/src/app/components/layout/page-layout/page-layout-content.component.ts new file mode 100644 index 0000000000..ad6eac2555 --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout-content.component.ts @@ -0,0 +1,47 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input, + HostBinding +} from '@angular/core'; + +@Component({ + selector: 'app-page-layout-content', + template: ` + + `, + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'app-page-layout-content' } +}) +export class PageLayoutContentComponent { + @Input() + @HostBinding('class.scrollable') + scrollable = false; +} diff --git a/src/app/components/layout/page-layout/page-layout-error.component.ts b/src/app/components/layout/page-layout/page-layout-error.component.ts new file mode 100644 index 0000000000..48f626a97c --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout-error.component.ts @@ -0,0 +1,41 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy +} from '@angular/core'; + +@Component({ + selector: 'app-page-layout-error', + template: ` + + `, + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'app-page-layout-error' } +}) +export class PageLayoutErrorComponent {} diff --git a/src/app/components/layout/page-layout/page-layout-header.component.ts b/src/app/components/layout/page-layout/page-layout-header.component.ts new file mode 100644 index 0000000000..acdf2b06e7 --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout-header.component.ts @@ -0,0 +1,39 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy +} from '@angular/core'; + +@Component({ + selector: 'app-page-layout-header', + template: '', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'app-page-layout-header' } +}) +export class PageLayoutHeaderComponent {} diff --git a/src/app/components/layout/page-layout/page-layout.component.html b/src/app/components/layout/page-layout/page-layout.component.html new file mode 100644 index 0000000000..cee7682aca --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout.component.html @@ -0,0 +1,3 @@ + + + diff --git a/src/app/components/layout/page-layout/page-layout.component.ts b/src/app/components/layout/page-layout/page-layout.component.ts new file mode 100644 index 0000000000..2e0eb02c1c --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout.component.ts @@ -0,0 +1,43 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy, + Input +} from '@angular/core'; + +@Component({ + selector: 'app-page-layout', + templateUrl: 'page-layout.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-page-layout' }, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PageLayoutComponent { + @Input() + hasError = false; +} diff --git a/src/app/components/layout/page-layout/page-layout.theme.scss b/src/app/components/layout/page-layout/page-layout.theme.scss new file mode 100644 index 0000000000..4571bd774e --- /dev/null +++ b/src/app/components/layout/page-layout/page-layout.theme.scss @@ -0,0 +1,46 @@ +@mixin app-page-layout-theme($theme) { + $foreground: map-get($theme, foreground); + + .app-page-layout { + @include flex-column; + + .app-page-layout-header { + display: flex; + align-items: center; + flex: 0 0 65px; + flex-basis: 48px; + background: #fafafa; + border-bottom: 1px solid mat-color($foreground, text, 0.07); + padding: 0 24px; + } + + .app-page-layout-content { + @include flex-row; + } + + .app-page-layout-error { + @include flex-row; + } + + .main-content { + @include flex-column; + border-right: 1px solid mat-color($foreground, text, 0.07); + } + + .scrollable { + overflow: auto !important; + + .main-content { + overflow: auto !important; + } + } + + .sidebar { + display: block; + height: 100%; + overflow-y: scroll; + max-width: 350px; + width: 350px; + } + } +} diff --git a/src/app/components/layout/sidenav-views-manager.directive.spec.ts b/src/app/components/layout/sidenav-views-manager.directive.spec.ts deleted file mode 100644 index 2b31144781..0000000000 --- a/src/app/components/layout/sidenav-views-manager.directive.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2018 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -/* -import { SidenavViewsManagerDirective } from './sidenav-views-manager.directive'; -import { NavigationEnd } from '@angular/router'; -import { Subject } from 'rxjs'; - -class RouterMock { - private subject = new Subject(); - public events = this.subject.asObservable(); - - navigate(url = '') { - const navigationEnd = new NavigationEnd(0, '', url); - this.subject.next(navigationEnd); - } - - destroy() { - this.subject.next(null); - this.subject.complete(); - this.subject = null; - } -} - -describe('SidenavViewsManagerDirective', () => { - let component; - let router; - - beforeEach(() => { - router = new RouterMock(); - component = new SidenavViewsManagerDirective(router, null, null); - }); - - afterEach(() => { - router.destroy(); - }); - - describe('Router events', () => { - it('should set minimizeSidenav to true when url is in minimizeConditions', () => { - router.navigate('/search/'); - expect(component.minimizeSidenav).toBe(true); - }); - - it('should set minimizeSidenav to false when url is not in minimizeConditions', () => { - router.navigate('/somewhere/'); - expect(component.minimizeSidenav).toBe(false); - }); - - it('should set hideSidenav property to true when url is in hideConditions', () => { - router.navigate('/preview/'); - expect(component.hideSidenav).toBe(true); - }); - - it('should set hideSidenav property to false when url is not in hideConditions', () => { - router.navigate('somewhere'); - expect(component.hideSidenav).toBe(false); - }); - }); -}); -*/ diff --git a/src/app/components/libraries/libraries.component.html b/src/app/components/libraries/libraries.component.html index 29f769e1dc..3070cbdcf0 100644 --- a/src/app/components/libraries/libraries.component.html +++ b/src/app/components/libraries/libraries.component.html @@ -1,78 +1,81 @@ -
-
- - + - - + + + - - - - - - -
- -
-
- + + + + + + - - - - - - + +
+ - - + + + + + + - - - - - - - - + + - - - - + + + + + + + + + + + - - - - -
-
-
+ + + + + + +
+ + + + diff --git a/src/app/components/libraries/libraries.component.spec.ts b/src/app/components/libraries/libraries.component.spec.ts index ed03b529e8..e2b23fde62 100644 --- a/src/app/components/libraries/libraries.component.spec.ts +++ b/src/app/components/libraries/libraries.component.spec.ts @@ -39,7 +39,7 @@ import { LibrariesComponent } from './libraries.component'; import { AppTestingModule } from '../../testing/app-testing.module'; import { ExperimentalDirective } from '../../directives/experimental.directive'; import { EffectsModule } from '@ngrx/effects'; -import { LibraryEffects } from 'src/app/store/effects'; +import { LibraryEffects } from '../../store/effects'; describe('LibrariesComponent', () => { let fixture: ComponentFixture; diff --git a/src/app/components/libraries/libraries.component.ts b/src/app/components/libraries/libraries.component.ts index d8e4e988cc..377c1f32a4 100644 --- a/src/app/components/libraries/libraries.component.ts +++ b/src/app/components/libraries/libraries.component.ts @@ -23,15 +23,15 @@ * along with Alfresco. If not, see . */ -import { Component, OnInit } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { PageComponent } from '../page.component'; +import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { AppStore } from '../../store/states/app.state'; import { SiteEntry } from 'alfresco-js-api'; -import { ContentManagementService } from '../../services/content-management.service'; import { AppExtensionService } from '../../extensions/extension.service'; -import { NavigateLibraryAction } from 'src/app/store/actions'; +import { ContentManagementService } from '../../services/content-management.service'; +import { NavigateLibraryAction } from '../../store/actions'; +import { AppStore } from '../../store/states/app.state'; +import { PageComponent } from '../page.component'; @Component({ templateUrl: './libraries.component.html' @@ -55,6 +55,8 @@ export class LibrariesComponent extends PageComponent implements OnInit { this.subscriptions.push( this.content.libraryDeleted.subscribe(() => this.reload()), + this.content.libraryUpdated.subscribe(() => this.reload()), + this.content.libraryLeft.subscribe(() => this.reload()), this.breakpointObserver .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts index 01b1b7c303..3184392c43 100644 --- a/src/app/components/page.component.ts +++ b/src/app/components/page.component.ts @@ -72,6 +72,17 @@ export abstract class PageComponent implements OnInit, OnDestroy { ); } + static isLibrary(entry) { + return ( + (entry.guid && + entry.id && + entry.preset && + entry.title && + entry.visibility) || + entry.nodeType === 'st:site' + ); + } + constructor( protected store: Store, protected extensions: AppExtensionService, @@ -129,6 +140,11 @@ export abstract class PageComponent implements OnInit, OnDestroy { if (PageComponent.isLockedNode(entry)) { return 'assets/images/ic_lock_black_24dp_1x.png'; } + + if (PageComponent.isLibrary(entry)) { + return 'assets/images/baseline-library_books-24px.svg'; + } + return null; } diff --git a/src/app/components/permissions/permission-manager/permission-manager.component.html b/src/app/components/permissions/permission-manager/permission-manager.component.html index 555ca38c92..3ef3989e11 100644 --- a/src/app/components/permissions/permission-manager/permission-manager.component.html +++ b/src/app/components/permissions/permission-manager/permission-manager.component.html @@ -1,20 +1,21 @@
- - -
- -
- - + +
+ + diff --git a/src/app/components/permissions/permission-manager/permission-manager.component.theme.scss b/src/app/components/permissions/permission-manager/permission-manager.component.theme.scss index e80839473d..9b94eb5590 100644 --- a/src/app/components/permissions/permission-manager/permission-manager.component.theme.scss +++ b/src/app/components/permissions/permission-manager/permission-manager.component.theme.scss @@ -6,6 +6,19 @@ height: 400px; } + // ADF FIXES + adf-permission-list { + .adf-display-permission-container { + flex: 1; + + .adf-datatable-permission { + .adf-data-table-cell--icon { + width: auto; + } + } + } + } + .aca-node-permissions-dialog { .mat-dialog-title { font-size: 20px; diff --git a/src/app/components/permissions/permission-manager/permission-manager.component.ts b/src/app/components/permissions/permission-manager/permission-manager.component.ts index d08435e8df..0edb0f35a8 100644 --- a/src/app/components/permissions/permission-manager/permission-manager.component.ts +++ b/src/app/components/permissions/permission-manager/permission-manager.component.ts @@ -68,7 +68,7 @@ export class PermissionsManagerComponent implements OnInit { this.store.dispatch(new SnackbarErrorAction(errorMessage)); } - onUpdate(event) { + onUpdate() { this.permissionList.reload(); } diff --git a/src/app/components/preview/preview-extension.component.ts b/src/app/components/preview/preview-extension.component.ts index e4a72df489..5e2f07c82c 100644 --- a/src/app/components/preview/preview-extension.component.ts +++ b/src/app/components/preview/preview-extension.component.ts @@ -39,7 +39,9 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api'; @Component({ selector: 'app-preview-extension', - template: `
` + template: ` +
+ ` }) export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('content', { read: ViewContainerRef }) diff --git a/src/app/components/preview/preview.component.html b/src/app/components/preview/preview.component.html index adef146a5a..4e9ade2958 100644 --- a/src/app/components/preview/preview.component.html +++ b/src/app/components/preview/preview.component.html @@ -3,7 +3,7 @@ + (navigateNext)="onNavigateNext()" + [ngClass]="hasRightSidebar ? 'hide-last-divider': ''"> - + - + @@ -30,9 +31,9 @@ - + - + diff --git a/src/app/components/preview/preview.component.scss b/src/app/components/preview/preview.component.scss index 714aad589d..69d1ddb3c7 100644 --- a/src/app/components/preview/preview.component.scss +++ b/src/app/components/preview/preview.component.scss @@ -2,3 +2,23 @@ width: 100%; height: 100%; } + +.hide-last-divider .adf-viewer-toolbar .mat-toolbar { + > adf-toolbar-divider:last-of-type { + display: none; + } + + > button:last-of-type { + right: 40px; + } +} + +#adf-viewer-moreactions { + right: -40px; +} + +.adf-viewer-toolbar-actions { + display: flex; + flex-direction: row; + align-items: center; +} diff --git a/src/app/components/preview/preview.component.ts b/src/app/components/preview/preview.component.ts index d773d5766b..b6283e7e2e 100644 --- a/src/app/components/preview/preview.component.ts +++ b/src/app/components/preview/preview.component.ts @@ -23,7 +23,13 @@ * along with Alfresco. If not, see . */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { + Component, + OnInit, + OnDestroy, + ViewEncapsulation, + HostListener +} from '@angular/core'; import { ActivatedRoute, Router, @@ -41,6 +47,8 @@ import { ContentApiService } from '../../services/content-api.service'; import { AppExtensionService } from '../../extensions/extension.service'; import { ContentManagementService } from '../../services/content-management.service'; import { ContentActionRef, ViewerExtensionRef } from '@alfresco/adf-extensions'; +import { SearchRequest } from 'alfresco-js-api'; +import { AppDataService } from '../../services/data.service'; @Component({ selector: 'app-preview', @@ -49,7 +57,8 @@ import { ContentActionRef, ViewerExtensionRef } from '@alfresco/adf-extensions'; encapsulation: ViewEncapsulation.None, host: { class: 'app-preview' } }) -export class PreviewComponent extends PageComponent implements OnInit { +export class PreviewComponent extends PageComponent + implements OnInit, OnDestroy { previewLocation: string = null; routesSkipNavigation = ['shared', 'recent-files', 'favorites']; navigateSource: string = null; @@ -67,10 +76,12 @@ export class PreviewComponent extends PageComponent implements OnInit { navigateMultiple = false; openWith: Array = []; contentExtensions: Array = []; + hasRightSidebar = true; constructor( private contentApi: ContentApiService, private preferences: UserPreferencesService, + private appDataService: AppDataService, private route: ActivatedRoute, private router: Router, store: Store, @@ -108,10 +119,20 @@ export class PreviewComponent extends PageComponent implements OnInit { } }); + this.subscriptions = this.subscriptions.concat([ + this.content.nodesDeleted.subscribe(() => + this.navigateToFileLocation(true) + ) + ]); + this.openWith = this.extensions.openWithActions; this.contentExtensions = this.extensions.viewerContentExtensions; } + ngOnDestroy() { + super.ngOnDestroy(); + } + /** * Loads the particular node into the Viewer * @param id Unique identifier for the Node to display @@ -142,16 +163,33 @@ export class PreviewComponent extends PageComponent implements OnInit { } } + @HostListener('document:keydown', ['$event']) + handleKeyboardEvent(event: KeyboardEvent) { + const key = event.keyCode; + const rightArrow = 39; + const leftArrow = 37; + + if (key === rightArrow || key === leftArrow) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + } + /** * Handles the visibility change of the Viewer component. * @param isVisible Indicator whether Viewer is visible or hidden. */ onVisibilityChanged(isVisible: boolean): void { + const shouldNavigate = !isVisible; + this.navigateToFileLocation(shouldNavigate); + } + + navigateToFileLocation(shouldNavigate: boolean) { const shouldSkipNavigation = this.routesSkipNavigation.includes( this.previewLocation ); - if (!isVisible) { + if (shouldNavigate) { const route = this.getNavigationCommands(this.previewLocation); if (!shouldSkipNavigation && this.folderId) { @@ -301,29 +339,29 @@ export class PreviewComponent extends PageComponent implements OnInit { const sortingDirection = this.preferences.get('recent-files.sorting.direction') || 'desc'; - const nodes = await this.contentApi - .search({ - query: { - query: '*', - language: 'afts' - }, - filterQueries: [ - { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, - { query: `cm:modifier:${username} OR cm:creator:${username}` }, - { - query: `TYPE:"content" AND -TYPE:"app:filelink" AND -TYPE:"fm:post"` - } - ], - fields: ['id', this.getRootField(sortingKey)], - sort: [ - { - type: 'FIELD', - field: 'cm:modified', - ascending: false - } - ] - }) - .toPromise(); + const query: SearchRequest = { + query: { + query: '*', + language: 'afts' + }, + filterQueries: [ + { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, + { query: `cm:modifier:${username} OR cm:creator:${username}` }, + { + query: this.appDataService.recentFileFilters.join(' AND ') + } + ], + fields: ['id', this.getRootField(sortingKey)], + include: ['path', 'properties', 'allowableOperations'], + sort: [ + { + type: 'FIELD', + field: 'cm:modified', + ascending: false + } + ] + }; + const nodes = await this.contentApi.search(query).toPromise(); const entries = nodes.list.entries.map(obj => obj.entry); this.sort(entries, sortingKey, sortingDirection); diff --git a/src/app/components/recent-files/recent-files.component.html b/src/app/components/recent-files/recent-files.component.html index 253d80d041..5ac7c50af1 100644 --- a/src/app/components/recent-files/recent-files.component.html +++ b/src/app/components/recent-files/recent-files.component.html @@ -1,83 +1,82 @@ -
-
- - + - - + + + - - - - -
+ + + + + + -
-
- + +
+ - - - - - - + + + + + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - + - - -
+ + +
-
- -
+ -
+ + diff --git a/src/app/components/recent-files/recent-files.module.ts b/src/app/components/recent-files/recent-files.module.ts index 4277928e04..f3e3e46808 100644 --- a/src/app/components/recent-files/recent-files.module.ts +++ b/src/app/components/recent-files/recent-files.module.ts @@ -34,6 +34,7 @@ import { AppToolbarModule } from '../toolbar/toolbar.module'; import { ContextMenuModule } from '../context-menu/context-menu.module'; import { RecentFilesComponent } from './recent-files.component'; import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; +import { AppLayoutModule } from '../layout/layout.module'; const routes: Routes = [ { @@ -55,7 +56,8 @@ const routes: Routes = [ AppCommonModule, AppToolbarModule, ContextMenuModule, - AppInfoDrawerModule + AppInfoDrawerModule, + AppLayoutModule ], declarations: [RecentFilesComponent], exports: [RecentFilesComponent] diff --git a/src/app/components/search/search-input-control/search-input-control.component.html b/src/app/components/search/search-input-control/search-input-control.component.html index 51be3eb873..69aaaccd03 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.html +++ b/src/app/components/search/search-input-control/search-input-control.component.html @@ -1,94 +1,21 @@ -
-
- - - - - - - -
- clear - -
-
-
+
+ + + +
+ clear +
+
- - - - - - - - - -

- {{ item?.entry.name }} -

- -

-
-

{{item?.entry?.createdByUser?.displayName}}

-
- - - - -

{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}

-
-
-
-
-
diff --git a/src/app/components/search/search-input-control/search-input-control.component.scss b/src/app/components/search/search-input-control/search-input-control.component.scss index a88d9d0db8..ebc1e2abc8 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.scss +++ b/src/app/components/search/search-input-control/search-input-control.component.scss @@ -1,8 +1,57 @@ -.adf-clear-search-icon-wrapper { - width: 1em; +$top-margin: 12px; + +.app-search-container { + margin-top: -$top-margin; + padding-top: 2px; + font-size: 16px; + padding-left: 15px; + box-sizing: border-box; + + .mat-form-field-underline { + display: none; + } + + .mat-form-field-label-wrapper { + cursor: text; + } + + // fixes pointer event on FF + &.searchMenuTrigger .mat-form-field-label-wrapper { + pointer-events: auto; + } + + .app-input-form-field { + letter-spacing: -0.7px; + width: calc(100% - 56px); + + .mat-input-element { + letter-spacing: -0.7px; + } + } + + .app-search-button.mat-icon-button { + top: -2px; + left: -8px; + + .mat-icon { + font-size: 24px; + padding-right: 0; + } + } +} + +.app-suffix-search-icon-wrapper { + height: 6px; + margin: 14px 1px; + float: left; .mat-icon { - font-size: 100%; + font-size: 24px; cursor: pointer; } + + .app-clear-icon { + font-size: 18px; + margin: 3px; + } } diff --git a/src/app/components/search/search-input-control/search-input-control.component.spec.ts b/src/app/components/search/search-input-control/search-input-control.component.spec.ts index 3d60858034..fa24e52f16 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.spec.ts +++ b/src/app/components/search/search-input-control/search-input-control.component.spec.ts @@ -24,9 +24,73 @@ */ import { SearchInputControlComponent } from './search-input-control.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('SearchInputControlComponent', () => { - it('should be defined', () => { - expect(SearchInputControlComponent).toBeDefined(); + let fixture: ComponentFixture; + let component: SearchInputControlComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [SearchInputControlComponent], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents() + .then(() => { + fixture = TestBed.createComponent(SearchInputControlComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + })); + + it('should emit submit event on searchSubmit', async () => { + const keyboardEvent = { target: { value: 'a' } }; + + let eventArgs = null; + component.submit.subscribe(args => (eventArgs = args)); + + await component.searchSubmit(keyboardEvent); + expect(eventArgs).toBe(keyboardEvent); + }); + + it('should emit searchChange event on inputChange', async () => { + const searchTerm = 'b'; + + let eventArgs = null; + component.searchChange.subscribe(args => (eventArgs = args)); + + await component.inputChange(searchTerm); + expect(eventArgs).toBe(searchTerm); + }); + + it('should emit searchChange event on clear', async () => { + let eventArgs = null; + component.searchChange.subscribe(args => (eventArgs = args)); + + await component.clear(); + expect(eventArgs).toBe(''); + }); + + it('should clear searchTerm', async () => { + component.searchTerm = 'c'; + fixture.detectChanges(); + + await component.clear(); + expect(component.searchTerm).toBe(''); + }); + + it('should check if searchTerm has a length less than 2', () => { + expect(component.isTermTooShort()).toBe(false); + + component.searchTerm = 'd'; + fixture.detectChanges(); + expect(component.isTermTooShort()).toBe(true); + + component.searchTerm = 'dd'; + fixture.detectChanges(); + expect(component.isTermTooShort()).toBe(false); }); }); diff --git a/src/app/components/search/search-input-control/search-input-control.component.ts b/src/app/components/search/search-input-control/search-input-control.component.ts index cb49534c48..66a2093cde 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.ts +++ b/src/app/components/search/search-input-control/search-input-control.component.ts @@ -23,99 +23,32 @@ * along with Alfresco. If not, see . */ -import { ThumbnailService } from '@alfresco/adf-core'; -import { - animate, - state, - style, - transition, - trigger -} from '@angular/animations'; import { Component, EventEmitter, Input, OnDestroy, - OnInit, Output, - QueryList, ViewEncapsulation, ViewChild, - ViewChildren, - ElementRef, - TemplateRef, - ContentChild + ElementRef } from '@angular/core'; -import { MinimalNodeEntity, QueryBody } from 'alfresco-js-api'; import { Subject } from 'rxjs'; -import { MatListItem } from '@angular/material'; -import { debounceTime, filter, takeUntil } from 'rxjs/operators'; -import { - EmptySearchResultComponent, - SearchComponent -} from '@alfresco/adf-content-services'; @Component({ selector: 'app-search-input-control', templateUrl: './search-input-control.component.html', styleUrls: ['./search-input-control.component.scss'], - animations: [ - trigger('transitionMessages', [ - state( - 'active', - style({ transform: 'translateX(0%)', 'margin-left': '13px' }) - ), - state('inactive', style({ transform: 'translateX(81%)' })), - state( - 'no-animation', - style({ transform: 'translateX(0%)', width: '100%' }) - ), - transition( - 'inactive => active', - animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)') - ), - transition( - 'active => inactive', - animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)') - ) - ]) - ], encapsulation: ViewEncapsulation.None, - host: { class: 'adf-search-control' } + host: { class: 'app-search-control' } }) -export class SearchInputControlComponent implements OnInit, OnDestroy { +export class SearchInputControlComponent implements OnDestroy { onDestroy$: Subject = new Subject(); - /** Toggles whether to use an expanding search control. If false - * then a regular input is used. - */ - @Input() - expandable = true; - - /** Toggles highlighting of the search term in the results. */ - @Input() - highlight = false; - /** Type of the input field to render, e.g. "search" or "text" (default). */ @Input() inputType = 'text'; - /** Toggles auto-completion of the search input field. */ - @Input() - autocomplete = false; - - /** Toggles "find-as-you-type" suggestions for possible matches. */ - @Input() - liveSearchEnabled = true; - - /** Maximum number of results to show in the live search. */ - @Input() - liveSearchMaxResults = 5; - - /** @deprecated in 2.1.0 */ - @Input() - customQueryBody: QueryBody; - /** Emitted when the search is submitted pressing ENTER button. * The search term is provided as value of the event. */ @@ -130,73 +63,10 @@ export class SearchInputControlComponent implements OnInit, OnDestroy { @Output() searchChange: EventEmitter = new EventEmitter(); - /** Emitted when a file item from the list of "find-as-you-type" results is selected. */ - @Output() - optionClicked: EventEmitter = new EventEmitter(); - - @ViewChild('search') - searchAutocomplete: SearchComponent; - @ViewChild('searchInput') searchInput: ElementRef; - @ViewChildren(MatListItem) - private listResultElement: QueryList; - - @ContentChild(EmptySearchResultComponent) - emptySearchTemplate: EmptySearchResultComponent; - searchTerm = ''; - subscriptAnimationState: string; - noSearchResultTemplate: TemplateRef = null; - skipToggle = false; - toggleDebounceTime = 200; - - private toggleSearch = new Subject(); - private focusSubject = new Subject(); - - constructor(private thumbnailService: ThumbnailService) { - this.toggleSearch - .asObservable() - .pipe( - debounceTime(this.toggleDebounceTime), - takeUntil(this.onDestroy$) - ) - .subscribe(() => { - if (this.expandable && !this.skipToggle) { - this.subscriptAnimationState = - this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive'; - - if (this.subscriptAnimationState === 'inactive') { - this.searchTerm = ''; - this.searchAutocomplete.resetResults(); - if ( - document.activeElement.id === this.searchInput.nativeElement.id - ) { - this.searchInput.nativeElement.blur(); - } - } - } - this.skipToggle = false; - }); - } - - applySearchFocus(animationDoneEvent) { - if (animationDoneEvent.toState === 'active') { - this.searchInput.nativeElement.focus(); - } - } - - ngOnInit() { - this.subscriptAnimationState = this.expandable - ? 'inactive' - : 'no-animation'; - this.setupFocusEventHandlers(); - } - - isNoSearchTemplatePresent(): boolean { - return this.emptySearchTemplate ? true : false; - } ngOnDestroy(): void { this.onDestroy$.next(true); @@ -205,117 +75,18 @@ export class SearchInputControlComponent implements OnInit, OnDestroy { searchSubmit(event: any) { this.submit.emit(event); - this.toggleSearchBar(); } inputChange(event: any) { this.searchChange.emit(event); } - getAutoComplete(): string { - return this.autocomplete ? 'on' : 'off'; - } - - getMimeTypeIcon(node: MinimalNodeEntity): string { - let mimeType; - - if (node.entry.content && node.entry.content.mimeType) { - mimeType = node.entry.content.mimeType; - } - if (node.entry.isFolder) { - mimeType = 'folder'; - } - - return this.thumbnailService.getMimeTypeIcon(mimeType); - } - - isSearchBarActive() { - return this.subscriptAnimationState === 'active' && this.liveSearchEnabled; - } - - toggleSearchBar() { - if (this.toggleSearch) { - this.toggleSearch.next(); - } - } - - elementClicked(item: any) { - if (item.entry) { - this.optionClicked.next(item); - this.toggleSearchBar(); - } - } - - onFocus($event): void { - this.focusSubject.next($event); - } - - onBlur($event): void { - this.focusSubject.next($event); - } - - activateToolbar() { - if (!this.isSearchBarActive()) { - this.toggleSearchBar(); - } - } - - selectFirstResult() { - if (this.listResultElement && this.listResultElement.length > 0) { - const firstElement: MatListItem = ( - this.listResultElement.first - ); - firstElement._getHostElement().focus(); - } - } - - onRowArrowDown($event: KeyboardEvent): void { - const nextElement: any = this.getNextElementSibling($event.target); - if (nextElement) { - nextElement.focus(); - } - } - - onRowArrowUp($event: KeyboardEvent): void { - const previousElement: any = this.getPreviousElementSibling(( - $event.target - )); - if (previousElement) { - previousElement.focus(); - } else { - this.searchInput.nativeElement.focus(); - this.focusSubject.next(new FocusEvent('focus')); - } - } - - private setupFocusEventHandlers() { - this.focusSubject - .pipe( - debounceTime(50), - filter(($event: any) => { - return ( - this.isSearchBarActive() && - ($event.type === 'blur' || $event.type === 'focusout') - ); - }), - takeUntil(this.onDestroy$) - ) - .subscribe(() => { - this.toggleSearchBar(); - }); - } - - clear(event: any) { + clear() { this.searchTerm = ''; this.searchChange.emit(''); - this.skipToggle = true; - } - - private getNextElementSibling(node: Element): Element { - return node.nextElementSibling; } - private getPreviousElementSibling(node: Element): Element { - return node.previousElementSibling; + isTermTooShort() { + return !!(this.searchTerm && this.searchTerm.length < 2); } } diff --git a/src/app/components/search/search-input/search-input.component.html b/src/app/components/search/search-input/search-input.component.html index 7366738913..d349860ffc 100644 --- a/src/app/components/search/search-input/search-input.component.html +++ b/src/app/components/search/search-input/search-input.component.html @@ -1,8 +1,40 @@ - - +
+ + + + +
+ arrow_drop_down +
+
+
+ + + + + {{ 'SEARCH.INPUT.HINT' | translate }} + +
+ + {{ option.key | translate }} + +
+
diff --git a/src/app/components/search/search-input/search-input.component.spec.ts b/src/app/components/search/search-input/search-input.component.spec.ts index cfa35b5f8c..b0d4e290e8 100644 --- a/src/app/components/search/search-input/search-input.component.spec.ts +++ b/src/app/components/search/search-input/search-input.component.spec.ts @@ -35,63 +35,159 @@ import { import { SearchInputComponent } from './search-input.component'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { Actions, ofType } from '@ngrx/effects'; -import { - NAVIGATE_FOLDER, - NavigateToFolder, - VIEW_FILE, - ViewFileAction -} from '../../../store/actions'; +import { SEARCH_BY_TERM, SearchByTermAction } from '../../../store/actions'; import { map } from 'rxjs/operators'; +import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; +import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service'; +import { ContentManagementService } from '../../../services/content-management.service'; describe('SearchInputComponent', () => { let fixture: ComponentFixture; let component: SearchInputComponent; let actions$: Actions; + let content: ContentManagementService; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [AppTestingModule], declarations: [SearchInputComponent], - schemas: [NO_ERRORS_SCHEMA] + schemas: [NO_ERRORS_SCHEMA], + providers: [SearchQueryBuilderService, SearchLibrariesQueryBuilderService] }) .compileComponents() .then(() => { actions$ = TestBed.get(Actions); fixture = TestBed.createComponent(SearchInputComponent); + content = TestBed.get(ContentManagementService); component = fixture.componentInstance; fixture.detectChanges(); }); })); - describe('onItemClicked()', () => { - it('opens preview if node is file', fakeAsync(done => { + it('should change flag on library400Error event', () => { + expect(component.has400LibraryError).toBe(false); + content.library400Error.next(); + + expect(component.has400LibraryError).toBe(true); + }); + + it('should have no library constraint by default', () => { + expect(component.hasLibraryConstraint()).toBe(false); + }); + + it('should have library constraint on 400 error received', () => { + const libItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('libraries') > 0 + ); + libItem.value = true; + content.library400Error.next(); + + expect(component.hasLibraryConstraint()).toBe(true); + }); + + describe('onSearchSubmit()', () => { + it('should call search action with correct search options', fakeAsync(done => { + const searchedTerm = 's'; + const currentSearchOptions = [{ key: 'test' }]; actions$.pipe( - ofType(VIEW_FILE), + ofType(SEARCH_BY_TERM), map(action => { - expect(action.payload.entry.id).toBe('node-id'); + expect(action.searchOptions[0].key).toBe(currentSearchOptions[0].key); done(); }) ); - - const node = { - entry: { isFile: true, id: 'node-id', parentId: 'parent-id' } - }; - - component.onItemClicked(node); + component.onSearchSubmit({ target: { value: searchedTerm } }); tick(); })); - it('navigates if node is folder', fakeAsync(done => { + it('should call search action with correct searched term', fakeAsync(done => { + const searchedTerm = 's'; actions$.pipe( - ofType(NAVIGATE_FOLDER), + ofType(SEARCH_BY_TERM), map(action => { - expect(action.payload.entry.id).toBe('folder-id'); + expect(action.payload).toBe(searchedTerm); done(); }) ); - const node = { entry: { id: 'folder-id', isFolder: true } }; - component.onItemClicked(node); + component.onSearchSubmit({ target: { value: searchedTerm } }); tick(); })); }); + + describe('onSearchChange()', () => { + it('should call search action with correct search options', fakeAsync(done => { + const searchedTerm = 's'; + const currentSearchOptions = [{ key: 'test' }]; + actions$.pipe( + ofType(SEARCH_BY_TERM), + map(action => { + expect(action.searchOptions[0].key).toBe(currentSearchOptions[0].key); + done(); + }) + ); + component.onSearchChange(searchedTerm); + tick(1000); + })); + + it('should call search action with correct searched term', fakeAsync(done => { + const searchedTerm = 's'; + actions$.pipe( + ofType(SEARCH_BY_TERM), + map(action => { + expect(action.payload).toBe(searchedTerm); + done(); + }) + ); + component.onSearchChange(searchedTerm); + tick(1000); + })); + }); + + describe('isLibrariesChecked()', () => { + it('should return false by default', () => { + expect(component.isLibrariesChecked()).toBe(false); + }); + + it('should return true when libraries checked', () => { + const libItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('libraries') > 0 + ); + libItem.value = true; + expect(component.isLibrariesChecked()).toBe(true); + }); + }); + + describe('isContentChecked()', () => { + it('should return false by default', () => { + expect(component.isContentChecked()).toBe(false); + }); + + it('should return true when files checked', () => { + const filesItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('files') > 0 + ); + filesItem.value = true; + expect(component.isContentChecked()).toBe(true); + }); + + it('should return true when folders checked', () => { + const foldersItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('folders') > 0 + ); + foldersItem.value = true; + expect(component.isContentChecked()).toBe(true); + }); + + it('should return true when both files and folders checked', () => { + const filesItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('files') > 0 + ); + filesItem.value = true; + const foldersItem = component.searchOptions.find( + item => item.key.toLowerCase().indexOf('folders') > 0 + ); + foldersItem.value = true; + expect(component.isContentChecked()).toBe(true); + }); + }); }); diff --git a/src/app/components/search/search-input/search-input.component.theme.scss b/src/app/components/search/search-input/search-input.component.theme.scss index 36a9249b00..a9d6409d7f 100644 --- a/src/app/components/search/search-input/search-input.component.theme.scss +++ b/src/app/components/search/search-input/search-input.component.theme.scss @@ -1,37 +1,165 @@ +$search-width: 594px; +$search-height: 40px; +$search-background: rgba(#efefef, 0.54); +$search-border-radius: 4px; +$top-margin: 12px; + @mixin aca-search-input-theme($theme) { $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $border: 1px solid mat-color($foreground, divider, 0.07); - .aca-search-input { - display: flex; - box-sizing: border-box; - padding: 0; - flex-direction: row; - align-items: center; - white-space: nowrap; - - .adf-search-control { - color: mat-color($background, card); - - .mat-form-field-underline { - background-color: mat-color($background, card); - } + .app-search-container { + color: mat-color($foreground, text, 0.54); + + .app-input-form-field { + .mat-input-element { + caret-color: mat-color($foreground, text, 0.54); - .adf-input-form-field-divider { - font-size: 14px; + &:disabled { + color: mat-color($foreground, text, 0.54); + } } } - .adf-search-button.mat-icon-button { - left: -15px; - margin-left: 15px; - align-items: flex-start; - font: 400 11px system-ui; - color: mat-color($background, card); + .mat-focused label.mat-form-field-label { + display: none; + } + } + + .app-search-options-menu { + &.mat-menu-panel { + background-color: mat-color($background, dialog); + } + } + #search-options { + color: mat-color($foreground, text, 0.54); + border-top: $border; + } + + mat-checkbox { + .mat-checkbox-frame { + border-color: mat-color($foreground, text, 0.54); + } + } +} + +.aca-search-input { + width: 100%; + max-width: $search-width; + background-color: $search-background; + border-radius: $search-border-radius; + height: $search-height; +} + +.app-search-container { + width: 100%; + max-width: $search-width; + height: $search-height + $top-margin; +} + +.app-search-control { + margin-top: -$top-margin; +} + +.app-search-options-menu { + &.mat-menu-panel { + width: $search-width; + max-width: unset; + border-radius: $search-border-radius; + margin-top: $top-margin; + } + + .mat-menu-content:not(:empty) { + padding-top: 0; + padding-bottom: 0; + } +} + +#search-options { + padding: 20px 0; + font-size: 16px; + letter-spacing: -0.7px; + + mat-checkbox { + padding: 3px 24px 3px 19px; + + .mat-checkbox-inner-container { + height: 18px; + width: 18px; + } + + .mat-checkbox-label { + padding: 0 0 0 11px; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .mat-checkbox-layout { + max-width: 155px; + } +} + +.app-search-hint { + position: absolute; + font-size: 12px; + padding-left: 17px; +} + +@media screen and ($mat-small) { + $search-width-small: 400px; + + .aca-search-input { + max-width: $search-width-small; + } + + .app-search-container { + max-width: $search-width-small; + } + + .app-search-options-menu { + &.mat-menu-panel { + width: $search-width-small; + } + } + + #search-options { + padding-left: 20px; - .mat-icon { - font-size: 24px; - padding-right: 0; + mat-checkbox { + padding: 3px 20px 3px 0; + + .mat-checkbox-label { + padding: 0; } } + + .mat-checkbox-layout { + max-width: 105px; + } + } +} + +@media screen and ($mat-xsmall) { + $search-width-xsmall: 220px; + + .aca-search-input { + max-width: $search-width-xsmall; + } + + .app-search-container { + max-width: $search-width-xsmall; + } + + .app-search-options-menu { + &.mat-menu-panel { + width: $search-width-xsmall; + margin-top: 9px; + } + } + + #search-options .mat-checkbox-layout { + max-width: 180px; } } diff --git a/src/app/components/search/search-input/search-input.component.ts b/src/app/components/search/search-input/search-input.component.ts index 379715ac3d..1bf5121305 100644 --- a/src/app/components/search/search-input/search-input.component.ts +++ b/src/app/components/search/search-input/search-input.component.ts @@ -23,7 +23,13 @@ * along with Alfresco. If not, see . */ -import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { NavigationEnd, PRIMARY_OUTLET, @@ -33,16 +39,21 @@ import { UrlSegmentGroup, UrlTree } from '@angular/router'; -import { MinimalNodeEntity } from 'alfresco-js-api'; import { SearchInputControlComponent } from '../search-input-control/search-input-control.component'; import { Store } from '@ngrx/store'; import { AppStore } from '../../../store/states/app.state'; -import { - SearchByTermAction, - NavigateToFolder, - ViewFileAction -} from '../../../store/actions'; -import { filter } from 'rxjs/operators'; +import { SearchByTermAction } from '../../../store/actions'; +import { filter, takeUntil } from 'rxjs/operators'; +import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { Subject } from 'rxjs'; +import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service'; + +export enum SearchOptionIds { + Files = 'content', + Folders = 'folder', + Libraries = 'libraries' +} @Component({ selector: 'aca-search-input', @@ -50,70 +61,92 @@ import { filter } from 'rxjs/operators'; encapsulation: ViewEncapsulation.None, host: { class: 'aca-search-input' } }) -export class SearchInputComponent implements OnInit { +export class SearchInputComponent implements OnInit, OnDestroy { + onDestroy$: Subject = new Subject(); hasOneChange = false; hasNewChange = false; navigationTimer: any; - enableLiveSearch = true; + has400LibraryError = false; + + searchedWord = null; + searchOptions: Array = [ + { + id: SearchOptionIds.Files, + key: 'SEARCH.INPUT.FILES', + value: false, + shouldDisable: this.isLibrariesChecked.bind(this) + }, + { + id: SearchOptionIds.Folders, + key: 'SEARCH.INPUT.FOLDERS', + value: false, + shouldDisable: this.isLibrariesChecked.bind(this) + }, + { + id: SearchOptionIds.Libraries, + key: 'SEARCH.INPUT.LIBRARIES', + value: false, + shouldDisable: this.isContentChecked.bind(this) + } + ]; @ViewChild('searchInputControl') searchInputControl: SearchInputControlComponent; - constructor(private router: Router, private store: Store) {} + constructor( + private queryBuilder: SearchQueryBuilderService, + private queryLibrariesBuilder: SearchLibrariesQueryBuilderService, + private content: ContentManagementService, + private router: Router, + private store: Store + ) {} ngOnInit() { this.showInputValue(); this.router.events + .pipe(takeUntil(this.onDestroy$)) .pipe(filter(e => e instanceof RouterEvent)) .subscribe(event => { if (event instanceof NavigationEnd) { this.showInputValue(); } }); + + this.content.library400Error + .pipe(takeUntil(this.onDestroy$)) + .subscribe(() => { + this.has400LibraryError = true; + }); } showInputValue() { - if (this.onSearchResults) { - let searchedWord = null; + this.has400LibraryError = false; + this.searchedWord = ''; + + if (this.onSearchResults || this.onLibrariesSearchResults) { const urlTree: UrlTree = this.router.parseUrl(this.router.url); const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET]; if (urlSegmentGroup) { const urlSegments: UrlSegment[] = urlSegmentGroup.segments; - searchedWord = urlSegments[0].parameters['q']; - } - - if (this.searchInputControl) { - this.enableLiveSearch = false; - this.searchInputControl.searchTerm = searchedWord; - this.searchInputControl.subscriptAnimationState = 'no-animation'; - } - } else { - if (this.searchInputControl.subscriptAnimationState === 'no-animation') { - this.searchInputControl.subscriptAnimationState = 'active'; - this.searchInputControl.searchTerm = ''; - this.searchInputControl.toggleSearchBar(); + this.searchedWord = urlSegments[0].parameters['q'] || ''; } + } - if (!this.enableLiveSearch) { - setTimeout(() => { - this.enableLiveSearch = true; - }, this.searchInputControl.toggleDebounceTime + 100); - } + if (this.searchInputControl) { + this.searchInputControl.searchTerm = this.searchedWord; } } - onItemClicked(node: MinimalNodeEntity) { - if (node && node.entry) { - const { isFile, isFolder } = node.entry; - if (isFile) { - this.store.dispatch(new ViewFileAction(node)); - } else if (isFolder) { - this.store.dispatch(new NavigateToFolder(node)); - } - } + ngOnDestroy(): void { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + onMenuOpened() { + this.searchInputControl.searchInput.nativeElement.focus(); } /** @@ -122,35 +155,123 @@ export class SearchInputComponent implements OnInit { * @param event Parameters relating to the search */ onSearchSubmit(event: KeyboardEvent) { + this.has400LibraryError = false; const searchTerm = (event.target as HTMLInputElement).value; if (searchTerm) { - this.store.dispatch(new SearchByTermAction(searchTerm)); + this.searchedWord = searchTerm; + + this.searchByOption(); } } onSearchChange(searchTerm: string) { - if (this.onSearchResults) { - if (this.hasOneChange) { - this.hasNewChange = true; - } else { - this.hasOneChange = true; + this.has400LibraryError = false; + this.searchedWord = searchTerm; + + if (this.hasOneChange) { + this.hasNewChange = true; + } else { + this.hasOneChange = true; + } + + if (this.hasNewChange) { + clearTimeout(this.navigationTimer); + this.hasNewChange = false; + } + + this.navigationTimer = setTimeout(() => { + if (searchTerm) { + this.store.dispatch( + new SearchByTermAction(searchTerm, this.searchOptions) + ); } + this.hasOneChange = false; + }, 1000); + } - if (this.hasNewChange) { - clearTimeout(this.navigationTimer); - this.hasNewChange = false; + searchByOption() { + this.has400LibraryError = false; + if (this.isLibrariesChecked()) { + if (this.searchedWord && !this.onLibrariesSearchResults) { + this.store.dispatch( + new SearchByTermAction(this.searchedWord, this.searchOptions) + ); + } else { + this.queryLibrariesBuilder.update(); + } + } else { + if (this.isFoldersChecked() && !this.isFilesChecked()) { + this.filterContent(SearchOptionIds.Folders); + } else if (this.isFilesChecked() && !this.isFoldersChecked()) { + this.filterContent(SearchOptionIds.Files); + } else { + this.removeContentFilters(); } - this.navigationTimer = setTimeout(() => { - if (searchTerm) { - this.store.dispatch(new SearchByTermAction(searchTerm)); - } - this.hasOneChange = false; - }, 1000); + if (this.onSearchResults) { + this.queryBuilder.update(); + } else if (this.searchedWord) { + this.store.dispatch( + new SearchByTermAction(this.searchedWord, this.searchOptions) + ); + } } } + get onLibrariesSearchResults() { + return this.router.url.indexOf('/search-libraries') === 0; + } + get onSearchResults() { - return this.router.url.indexOf('/search') === 0; + return ( + !this.onLibrariesSearchResults && this.router.url.indexOf('/search') === 0 + ); + } + + isFilesChecked(): boolean { + return this.isOptionChecked(SearchOptionIds.Files); + } + + isFoldersChecked(): boolean { + return this.isOptionChecked(SearchOptionIds.Folders); + } + + isLibrariesChecked(): boolean { + return this.isOptionChecked(SearchOptionIds.Libraries); + } + + isOptionChecked(optionId: string): boolean { + const libItem = this.searchOptions.find(item => item.id === optionId); + return !!libItem && libItem.value; + } + + isContentChecked(): boolean { + return this.isFilesChecked() || this.isFoldersChecked(); + } + + hasLibraryConstraint(): boolean { + if (this.isLibrariesChecked()) { + return ( + this.has400LibraryError || this.searchInputControl.isTermTooShort() + ); + } + return false; + } + + filterContent(option: SearchOptionIds.Folders | SearchOptionIds.Files) { + const oppositeOption = + option === SearchOptionIds.Folders + ? SearchOptionIds.Files + : SearchOptionIds.Folders; + + this.queryBuilder.addFilterQuery(`+TYPE:'cm:${option}'`); + this.queryBuilder.removeFilterQuery(`+TYPE:'cm:${oppositeOption}'`); + } + + removeContentFilters() { + this.queryBuilder.removeFilterQuery(`+TYPE:'cm:${SearchOptionIds.Files}'`); + this.queryBuilder.removeFilterQuery( + `+TYPE:'cm:${SearchOptionIds.Folders}'` + ); } } diff --git a/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.spec.ts b/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.spec.ts new file mode 100644 index 0000000000..a6ad36dfc7 --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.spec.ts @@ -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 { TestBed } from '@angular/core/testing'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { SearchLibrariesQueryBuilderService } from './search-libraries-query-builder.service'; + +describe('SearchLibrariesQueryBuilderService', () => { + let apiService: AlfrescoApiService; + let builder: SearchLibrariesQueryBuilderService; + let queriesApi; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule] + }); + + apiService = TestBed.get(AlfrescoApiService); + apiService.reset(); + queriesApi = apiService.getInstance().core.queriesApi; + builder = new SearchLibrariesQueryBuilderService(apiService); + }); + + it('should have empty user query by default', () => { + expect(builder.userQuery).toBe(''); + }); + + it('should trim user query value', () => { + builder.userQuery = ' something '; + expect(builder.userQuery).toEqual('something'); + }); + + it('should build query and raise an event on update', async () => { + const query = {}; + spyOn(builder, 'buildQuery').and.returnValue(query); + + let eventArgs = null; + builder.updated.subscribe(args => (eventArgs = args)); + + await builder.update(); + expect(eventArgs).toBe(query); + }); + + it('should build query and raise an event on execute', async () => { + const data = {}; + spyOn(queriesApi, 'findSites').and.returnValue(Promise.resolve(data)); + + const query = {}; + spyOn(builder, 'buildQuery').and.returnValue(query); + + let eventArgs = null; + builder.executed.subscribe(args => (eventArgs = args)); + + await builder.execute(); + expect(eventArgs).toBe(data); + }); + + it('should require a query fragment to build query', () => { + const compiled = builder.buildQuery(); + expect(compiled).toBeNull(); + }); + + it('should build query when there is a useQuery value', () => { + const searchedTerm = 'test'; + + builder.userQuery = searchedTerm; + + const compiled = builder.buildQuery(); + expect(compiled.term).toBe(searchedTerm); + }); + + it('should use pagination settings', () => { + const searchedTerm = 'test'; + + builder.paging = { maxItems: 5, skipCount: 5 }; + builder.userQuery = searchedTerm; + + const compiled = builder.buildQuery(); + expect(compiled.opts).toEqual({ maxItems: 5, skipCount: 5 }); + }); + + it('should raise an event on error', async () => { + const err = '{"error": {"statusCode": 400}}'; + spyOn(queriesApi, 'findSites').and.returnValue(Promise.reject(err)); + + const query = {}; + spyOn(builder, 'buildQuery').and.returnValue(query); + + let eventArgs = null; + builder.hadError.subscribe(args => (eventArgs = args)); + + await builder.execute(); + expect(eventArgs).toBe(err); + }); +}); diff --git a/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.ts b/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.ts new file mode 100644 index 0000000000..8960416669 --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-query-builder.service.ts @@ -0,0 +1,92 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { Injectable } from '@angular/core'; +import { SitePaging } from 'alfresco-js-api'; +import { Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class SearchLibrariesQueryBuilderService { + private _userQuery = ''; + + updated: Subject = new Subject(); + executed: Subject = new Subject(); + hadError: Subject = new Subject(); + + paging: { maxItems?: number; skipCount?: number } = null; + + get userQuery(): string { + return this._userQuery; + } + + set userQuery(value: string) { + this._userQuery = value ? value.trim() : ''; + } + + constructor(private alfrescoApiService: AlfrescoApiService) {} + + update(): void { + const query = this.buildQuery(); + if (query) { + this.updated.next(query); + } + } + + async execute() { + const query = this.buildQuery(); + if (query) { + const data = await this.findLibraries(query); + this.executed.next(data); + } + } + + buildQuery(): any { + const query = this.userQuery; + if (query && query.length > 1) { + const resultQuery = { + term: query, + opts: { + skipCount: this.paging && this.paging.skipCount, + maxItems: this.paging && this.paging.maxItems + } + }; + return resultQuery; + } + return null; + } + + private findLibraries(libraryQuery): Promise { + return this.alfrescoApiService + .getInstance() + .core.queriesApi.findSites(libraryQuery.term, libraryQuery.opts) + .catch(err => { + this.hadError.next(err); + return { list: { pagination: { totalItems: 0 }, entries: [] } }; + }); + } +} diff --git a/src/app/components/search/search-libraries-results/search-libraries-results.component.html b/src/app/components/search/search-libraries-results/search-libraries-results.component.html new file mode 100644 index 0000000000..766e62f57a --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-results.component.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + +
+
+
+ + +
+
+
{{ 'APP.BROWSE.SEARCH_LIBRARIES.FOUND_RESULTS' | translate: { number: totalResults } }}
+
{{ 'APP.BROWSE.SEARCH_LIBRARIES.FOUND_ONE_RESULT' | translate: { number: totalResults } }}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ {{ 'APP.BROWSE.SEARCH.NO_RESULTS' | translate }} +

+
+
+
+
+
+ + + +
+
+
+ +
+ +
diff --git a/src/app/components/search/search-libraries-results/search-libraries-results.component.scss b/src/app/components/search/search-libraries-results/search-libraries-results.component.scss new file mode 100644 index 0000000000..4ebf0b5963 --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-results.component.scss @@ -0,0 +1,42 @@ +@import 'mixins'; + +.adf-search-results { + @include flex-row; + + &__content { + @include flex-column; + border-left: 1px solid #eee; + } + + &__content-header { + display: flex; + padding: 0 25px 0 25px; + flex-direction: row; + align-items: center; + border-bottom: 1px solid #eee; + } + + &--info-text { + flex: 1; + font-size: 16px; + color: rgba(0, 0, 0, 0.54); + } + + .text--bold { + font-weight: 600; + } + + .content { + @include flex-row; + flex: unset; + height: unset; + padding-top: 8px; + padding-bottom: 8px; + flex-wrap: wrap; + + &__side--left { + @include flex-column; + height: unset; + } + } +} diff --git a/src/app/components/search/search-libraries-results/search-libraries-results.component.spec.ts b/src/app/components/search/search-libraries-results/search-libraries-results.component.spec.ts new file mode 100644 index 0000000000..16668e377a --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-results.component.spec.ts @@ -0,0 +1,38 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { AppConfigPipe, DataTableComponent } from '@alfresco/adf-core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { SearchLibrariesResultsComponent } from './search-libraries-results.component'; +import { SearchLibrariesQueryBuilderService } from './search-libraries-query-builder.service'; +import { DocumentListComponent } from '@alfresco/adf-content-services'; + +describe('SearchLibrariesResultsComponent', () => { + let component: SearchLibrariesResultsComponent; + let fixture: ComponentFixture; + + const emptyPage = { list: { pagination: { totalItems: 0 }, entries: [] } }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + DataTableComponent, + DocumentListComponent, + SearchLibrariesResultsComponent, + AppConfigPipe + ], + schemas: [NO_ERRORS_SCHEMA], + providers: [SearchLibrariesQueryBuilderService] + }); + + fixture = TestBed.createComponent(SearchLibrariesResultsComponent); + component = fixture.componentInstance; + }); + + it('should show empty page by default', async () => { + spyOn(component, 'onSearchResultLoaded').and.callThrough(); + fixture.detectChanges(); + + expect(component.onSearchResultLoaded).toHaveBeenCalledWith(emptyPage); + }); +}); diff --git a/src/app/components/search/search-libraries-results/search-libraries-results.component.ts b/src/app/components/search/search-libraries-results/search-libraries-results.component.ts new file mode 100644 index 0000000000..582d839022 --- /dev/null +++ b/src/app/components/search/search-libraries-results/search-libraries-results.component.ts @@ -0,0 +1,167 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, OnInit } from '@angular/core'; +import { NodePaging, Pagination, SiteEntry } from 'alfresco-js-api'; +import { ActivatedRoute, Params } from '@angular/router'; +import { PageComponent } from '../../page.component'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../../store/states/app.state'; +import { NavigateLibraryAction } from '../../../store/actions'; +import { AppExtensionService } from '../../../extensions/extension.service'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { SearchLibrariesQueryBuilderService } from './search-libraries-query-builder.service'; +import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; + +@Component({ + selector: 'aca-search-results', + templateUrl: './search-libraries-results.component.html', + styleUrls: ['./search-libraries-results.component.scss'] +}) +export class SearchLibrariesResultsComponent extends PageComponent + implements OnInit { + isSmallScreen = false; + searchedWord: string; + queryParamName = 'q'; + data: NodePaging; + totalResults = 0; + isLoading = false; + columns: any[] = []; + + constructor( + private breakpointObserver: BreakpointObserver, + private librariesQueryBuilder: SearchLibrariesQueryBuilderService, + private route: ActivatedRoute, + store: Store, + extensions: AppExtensionService, + content: ContentManagementService + ) { + super(store, extensions, content); + + librariesQueryBuilder.paging = { + skipCount: 0, + maxItems: 25 + }; + } + + ngOnInit() { + super.ngOnInit(); + + this.columns = this.extensions.documentListPresets.searchLibraries || []; + + this.subscriptions.push( + this.content.libraryJoined.subscribe(() => + this.librariesQueryBuilder.update() + ), + this.content.libraryDeleted.subscribe(() => + this.librariesQueryBuilder.update() + ), + this.content.libraryLeft.subscribe(() => + this.librariesQueryBuilder.update() + ), + + this.librariesQueryBuilder.updated.subscribe(() => { + this.isLoading = true; + + this.librariesQueryBuilder.execute(); + }), + + this.librariesQueryBuilder.executed.subscribe(data => { + this.onSearchResultLoaded(data); + this.isLoading = false; + }), + + this.librariesQueryBuilder.hadError.subscribe(err => { + try { + const { + error: { statusCode } + } = JSON.parse(err.message); + if (statusCode === 400) { + this.content.library400Error.next(); + } + } catch (e) {} + }), + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ); + + if (this.route) { + this.route.params.forEach((params: Params) => { + this.searchedWord = params.hasOwnProperty(this.queryParamName) + ? params[this.queryParamName] + : null; + const query = this.formatSearchQuery(this.searchedWord); + + if (query && query.length > 1) { + this.librariesQueryBuilder.paging.skipCount = 0; + this.librariesQueryBuilder.userQuery = query; + this.librariesQueryBuilder.update(); + } else { + this.librariesQueryBuilder.userQuery = null; + this.librariesQueryBuilder.executed.next({ + list: { pagination: { totalItems: 0 }, entries: [] } + }); + } + }); + } + } + + private formatSearchQuery(userInput: string) { + if (!userInput) { + return null; + } + return userInput.trim(); + } + + onSearchResultLoaded(nodePaging: NodePaging) { + this.data = nodePaging; + this.totalResults = this.getNumberOfResults(); + } + + getNumberOfResults() { + if (this.data && this.data.list && this.data.list.pagination) { + return this.data.list.pagination.totalItems; + } + return 0; + } + + onPaginationChanged(pagination: Pagination) { + this.librariesQueryBuilder.paging = { + maxItems: pagination.maxItems, + skipCount: pagination.skipCount + }; + this.librariesQueryBuilder.update(); + } + + navigateTo(node: SiteEntry) { + if (node && node.entry && node.entry.guid) { + this.store.dispatch(new NavigateLibraryAction(node.entry.guid)); + } + } +} diff --git a/src/app/components/search/search-results.module.ts b/src/app/components/search/search-results.module.ts index 53cbe5b3d1..b28786f65e 100644 --- a/src/app/components/search/search-results.module.ts +++ b/src/app/components/search/search-results.module.ts @@ -29,10 +29,13 @@ import { CoreModule } from '@alfresco/adf-core'; import { ContentModule } from '@alfresco/adf-content-services'; import { SearchResultsComponent } from './search-results/search-results.component'; import { SearchResultsRowComponent } from './search-results-row/search-results-row.component'; +import { SearchLibrariesResultsComponent } from './search-libraries-results/search-libraries-results.component'; import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; import { AppToolbarModule } from '../toolbar/toolbar.module'; import { AppCommonModule } from '../common/common.module'; import { DirectivesModule } from '../../directives/directives.module'; +import { AppLayoutModule } from '../layout/layout.module'; +import { ContextMenuModule } from '../context-menu/context-menu.module'; @NgModule({ imports: [ @@ -42,9 +45,19 @@ import { DirectivesModule } from '../../directives/directives.module'; AppCommonModule, AppInfoDrawerModule, AppToolbarModule, - DirectivesModule + DirectivesModule, + AppLayoutModule, + ContextMenuModule ], - declarations: [SearchResultsComponent, SearchResultsRowComponent], - exports: [SearchResultsComponent, SearchResultsRowComponent] + declarations: [ + SearchResultsComponent, + SearchLibrariesResultsComponent, + SearchResultsRowComponent + ], + exports: [ + SearchResultsComponent, + SearchLibrariesResultsComponent, + SearchResultsRowComponent + ] }) export class AppSearchResultsModule {} diff --git a/src/app/components/search/search-results/search-results.component.html b/src/app/components/search/search-results/search-results.component.html index cd7f83dca3..a3795886f8 100644 --- a/src/app/components/search/search-results/search-results.component.html +++ b/src/app/components/search/search-results/search-results.component.html @@ -1,89 +1,94 @@ -
-
- - - - - - - -
+ + + + + + + + + + + -
-
-
- -
- - -
-
-
{{ 'APP.BROWSE.SEARCH.FOUND_RESULTS' | translate: { number: totalResults } }}
+ +
+
+ +
+ + +
+
+
{{ 'APP.BROWSE.SEARCH.FOUND_RESULTS' | translate: { number: totalResults } }}
+
{{ 'APP.BROWSE.SEARCH.FOUND_ONE_RESULT' | translate: { number: totalResults } }}
-
- -
-
+
+ +
+
- -
+ +
- + - - - + + + - - - - - + + + + + - + - - - -
-

- {{ 'APP.BROWSE.SEARCH.NO_RESULTS' | translate }} -

-
-
-
-
-
+ + + +
+

+ {{ 'APP.BROWSE.SEARCH.NO_RESULTS' | translate }} +

+
+
+
+
+
- - -
-
-
-
- -
+ + +
+
-
+ + + +
diff --git a/src/app/components/search/search-results/search-results.component.ts b/src/app/components/search/search-results/search-results.component.ts index 5794585720..e5b14adc2a 100644 --- a/src/app/components/search/search-results/search-results.component.ts +++ b/src/app/components/search/search-results/search-results.component.ts @@ -42,8 +42,7 @@ import { AppConfigService } from '@alfresco/adf-core'; @Component({ selector: 'aca-search-results', templateUrl: './search-results.component.html', - styleUrls: ['./search-results.component.scss'], - providers: [SearchQueryBuilderService] + styleUrls: ['./search-results.component.scss'] }) export class SearchResultsComponent extends PageComponent implements OnInit { @ViewChild('search') diff --git a/src/app/components/shared-files/shared-files.component.html b/src/app/components/shared-files/shared-files.component.html index d04428c008..7e10414ee3 100644 --- a/src/app/components/shared-files/shared-files.component.html +++ b/src/app/components/shared-files/shared-files.component.html @@ -1,80 +1,78 @@ -
-
- - + + + + - - + + + + + + - - - - -
+ +
+ -
-
- + + + + + + - - - - - - + + - - + + + + + + + + - - - - - - - - + + + + - - - - + + + - - - + + +
- - -
- -
- -
-
-
+ + + diff --git a/src/app/components/shared-files/shared-files.module.ts b/src/app/components/shared-files/shared-files.module.ts index 288ae91990..878e3dd841 100644 --- a/src/app/components/shared-files/shared-files.module.ts +++ b/src/app/components/shared-files/shared-files.module.ts @@ -34,6 +34,7 @@ import { AppCommonModule } from '../common/common.module'; import { AppToolbarModule } from '../toolbar/toolbar.module'; import { ContextMenuModule } from '../context-menu/context-menu.module'; import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; +import { AppLayoutModule } from '../layout/layout.module'; const routes: Routes = [ { @@ -56,7 +57,8 @@ const routes: Routes = [ AppCommonModule, AppToolbarModule, ContextMenuModule, - AppInfoDrawerModule + AppInfoDrawerModule, + AppLayoutModule ], declarations: [SharedFilesComponent], exports: [SharedFilesComponent] diff --git a/src/app/components/shared/content-node-share/content-node-share.dialog.scss b/src/app/components/shared/content-node-share/content-node-share.dialog.scss index e85ac357a2..897e7968ff 100644 --- a/src/app/components/shared/content-node-share/content-node-share.dialog.scss +++ b/src/app/components/shared/content-node-share/content-node-share.dialog.scss @@ -1,3 +1,17 @@ +@mixin aca-share-dialog-theme($theme) { + $accent: map-get($theme, accent); + + // fixes [ACA-2069] + .mat-slide-toggle.mat-primary.mat-checked:not(.mat-disabled) { + .mat-slide-toggle-thumb { + background-color: mat-color($accent); + } + .mat-slide-toggle-bar { + background-color: mat-color($accent, 0.54); + } + } +} + @mixin adf-share-link-typography { letter-spacing: -0.4px; line-height: 2; diff --git a/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts b/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts index 2db6f161cd..9891b77570 100644 --- a/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts +++ b/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts @@ -137,7 +137,7 @@ describe('ShareDialogComponent', () => { ).toContain('mat-checked'); })); - it(`should copy shared link and notify on button event`, async(() => { + xit(`should copy shared link and notify on button event`, async(() => { node.entry.properties['qshare:sharedId'] = 'sharedId'; spyOn(document, 'execCommand').and.callThrough(); diff --git a/src/app/components/shared/toggle-shared/toggle-shared.component.html b/src/app/components/shared/toggle-shared/toggle-shared.component.html index 7b844329fa..d1459abda7 100644 --- a/src/app/components/shared/toggle-shared/toggle-shared.component.html +++ b/src/app/components/shared/toggle-shared/toggle-shared.component.html @@ -1,12 +1,16 @@ - - + + - {{ 'APP.ACTIONS.SHARE' | translate }} - \ No newline at end of file + {{ 'APP.ACTIONS.SHARE' | translate }} + diff --git a/src/app/components/shared/toggle-shared/toggle-shared.component.ts b/src/app/components/shared/toggle-shared/toggle-shared.component.ts index 57b37b89bc..bc037df33b 100644 --- a/src/app/components/shared/toggle-shared/toggle-shared.component.ts +++ b/src/app/components/shared/toggle-shared/toggle-shared.component.ts @@ -46,11 +46,16 @@ export class ToggleSharedComponent implements OnInit { isShared(selection) { // workaround for shared files - if (selection.first.entry && selection.first.entry.sharedByUser) { + if ( + selection.first && + selection.first.entry && + selection.first.entry.sharedByUser + ) { return true; } return ( + selection.first && selection.first.entry && selection.first.entry.properties && !!selection.first.entry.properties['qshare:sharedId'] diff --git a/src/app/components/sidenav/expansion-panel.directive.spec.ts b/src/app/components/sidenav/expansion-panel.directive.spec.ts new file mode 100644 index 0000000000..c7d64d7846 --- /dev/null +++ b/src/app/components/sidenav/expansion-panel.directive.spec.ts @@ -0,0 +1,119 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NavigationEnd } from '@angular/router'; +import { AcaExpansionPanelDirective } from './expansion-panel.directive'; +import { Subject } from 'rxjs'; + +class RouterStub { + url; + private subject = new Subject(); + events = this.subject.asObservable(); + + constructor(url = 'some-url') { + this.url = url; + } + + navigate(nextUrl: string) { + const navigationEnd = new NavigationEnd(0, this.url, nextUrl); + this.subject.next(navigationEnd); + } +} + +describe('AcaExpansionPanel', () => { + const item = { + children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }] + }; + + it('should set panel as selected on initialization if url contains child url', () => { + const router: any = new RouterStub('dummy-route-2'); + const directive = new AcaExpansionPanelDirective(router, null); + + directive.acaExpansionPanel = item; + directive.ngOnInit(); + + expect(directive.selected).toBe(true); + }); + + it('should not set panel as selected on initialization if url does not contain child url', () => { + const router: any = new RouterStub('dummy-route-other'); + const directive = new AcaExpansionPanelDirective(router, null); + + directive.acaExpansionPanel = item; + directive.ngOnInit(); + + expect(directive.selected).toBe(false); + }); + + it('should go on first child url when expended and url does not contain any child url', () => { + const router: any = new RouterStub(); + spyOn(router, 'navigate'); + const expansionPanelInstance: any = { expanded: true }; + const directive = new AcaExpansionPanelDirective( + router, + expansionPanelInstance + ); + directive.acaExpansionPanel = item; + + directive.ngOnInit(); + directive.onClick(); + + expect(router.navigate).toHaveBeenCalledWith(['dummy-route-1']); + }); + + it('should not go on first child url when expended and url contains any child url', () => { + const router: any = new RouterStub('dummy-route-2'); + spyOn(router, 'navigate'); + const expansionPanelInstance: any = { expanded: true }; + const directive = new AcaExpansionPanelDirective( + router, + expansionPanelInstance + ); + directive.acaExpansionPanel = item; + + directive.ngOnInit(); + directive.onClick(); + + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should set panel selected on navigation change', done => { + const router: any = new RouterStub(); + const directive = new AcaExpansionPanelDirective(router, null); + directive.acaExpansionPanel = item; + + directive.ngOnInit(); + + router.navigate('dummy-route-1'); + done(); + + expect(directive.selected).toBe(true); + + router.navigate('some-url'); + done(); + + expect(directive.selected).toBe(false); + }); +}); diff --git a/src/app/components/sidenav/expansion-panel.directive.ts b/src/app/components/sidenav/expansion-panel.directive.ts new file mode 100644 index 0000000000..ad3fa38649 --- /dev/null +++ b/src/app/components/sidenav/expansion-panel.directive.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Directive, + OnInit, + Input, + HostListener, + OnDestroy +} from '@angular/core'; +import { Router, NavigationEnd } from '@angular/router'; +import { filter, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { MatExpansionPanel } from '@angular/material/expansion'; + +@Directive({ + selector: '[acaExpansionPanel]', + exportAs: 'acaExpansionPanel' +}) +export class AcaExpansionPanelDirective implements OnInit, OnDestroy { + @Input() acaExpansionPanel; + selected = false; + + private onDestroy$: Subject = new Subject(); + + @HostListener('click') + onClick() { + if (this.expansionPanel.expanded && !this.selected) { + this.router.navigate([this.acaExpansionPanel.children[0].url]); + } + } + + constructor( + private router: Router, + private expansionPanel: MatExpansionPanel + ) {} + + ngOnInit() { + this.setSelected(this.router.url); + + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + takeUntil(this.onDestroy$) + ) + .subscribe((event: NavigationEnd) => { + this.setSelected(event.urlAfterRedirects); + }); + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + private setSelected(url: string) { + this.selected = this.acaExpansionPanel.children.some(child => + url.startsWith(child.url) + ); + } +} diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html index 101ad1bda3..b0b9bef5c0 100644 --- a/src/app/components/sidenav/sidenav.component.html +++ b/src/app/components/sidenav/sidenav.component.html @@ -1,40 +1,155 @@
-
- +
+
-
-
    -
  • +
  • -
+ #routerLink="routerLinkActive"> + + + + + + + + + + + {{ item.icon }} + + {{ item.title | translate }} + + + +
+ + + + + + {{ child.title | translate }} + + + + + + +
+
+
+
+ + + + + + + + + + + + + + +
+
-
+
\ No newline at end of file diff --git a/src/app/components/sidenav/sidenav.component.scss b/src/app/components/sidenav/sidenav.component.scss index 7a0b9e0d9e..824418e894 100644 --- a/src/app/components/sidenav/sidenav.component.scss +++ b/src/app/components/sidenav/sidenav.component.scss @@ -4,32 +4,29 @@ flex-direction: column; height: 100%; - &__section:last-child { + .section:last-child { border-bottom: 0; } - &_action-menu { + .section { + padding: 8px 14px; + } + + .action-menu { display: flex; - padding: 16px 24px; height: 40px; justify-content: center; align-items: center; } - &__section { - padding: 8px 14px; - position: relative; - } - - &-menu { - display: inline-flex; + .menu { + display: flex; flex-direction: column; padding: 0; margin: 0; - list-style-type: none; } - &-menu__item { + .menu__item { padding: 12px 0; flex-direction: row; display: flex; @@ -37,15 +34,46 @@ text-decoration: none; text-decoration: none; height: 24px; + user-select: none; + } + + .item--parent { + font-weight: 600; } - .menu__item--label { + .item--label { cursor: pointer; width: 240px; padding-left: 10px; } - .menu__item--label:focus { + .item--child { + padding-left: 25px; + } + + .item--label:focus { outline: none; } + + .item--label__trigger { + padding-left: 0; + } + + .mat-expansion-panel-header { + padding: 0 8px !important; + } + + .mat-expansion-panel-header-title span { + margin-left: 8px; + } + + .mat-expansion-panel-body { + padding: 0 24px 0px; + } + + .mat-expansion-panel-header-title { + display: flex; + flex-direction: row; + align-items: center; + } } diff --git a/src/app/components/sidenav/sidenav.component.theme.scss b/src/app/components/sidenav/sidenav.component.theme.scss index 7c0541ac7e..85ff9c878d 100644 --- a/src/app/components/sidenav/sidenav.component.theme.scss +++ b/src/app/components/sidenav/sidenav.component.theme.scss @@ -11,23 +11,41 @@ background-color: mat-color($background, background); + .mat-expansion-panel { + background-color: unset; + color: mat-color($primary, 0.87) !important; + } + + .mat-expansion-panel { + box-shadow: none !important; + } + + .mat-expansion-panel:not(.mat-expanded) + .mat-expansion-panel-header:not([aria-disabled='true']):hover { + background: none !important; + } + + .mat-expansion-panel-header { + font-size: 14px !important; + } + .adf-sidebar-action-menu-button { background-color: mat-color($accent); } - &__section { + .section { border-bottom: $border; } - .menu__item--label:not(.menu__item--active):hover { + .item--label:not(.item--active):hover { color: mat-color($foreground, text); } - .menu__item--active { + .item--active { color: mat-color($accent); } - .menu__item--default { + .item--default { color: mat-color($primary, 0.87); } } diff --git a/src/app/components/sidenav/sidenav.module.ts b/src/app/components/sidenav/sidenav.module.ts index fdceebc4e6..a29e628370 100644 --- a/src/app/components/sidenav/sidenav.module.ts +++ b/src/app/components/sidenav/sidenav.module.ts @@ -29,6 +29,7 @@ import { CommonModule } from '@angular/common'; import { SidenavComponent } from './sidenav.component'; import { CoreModule } from '@alfresco/adf-core'; import { RouterModule } from '@angular/router'; +import { AcaExpansionPanelDirective } from './expansion-panel.directive'; @NgModule({ imports: [ @@ -37,7 +38,7 @@ import { RouterModule } from '@angular/router'; RouterModule, AppCreateMenuModule ], - declarations: [SidenavComponent], - exports: [SidenavComponent] + declarations: [SidenavComponent, AcaExpansionPanelDirective], + exports: [SidenavComponent, AcaExpansionPanelDirective] }) export class AppSidenavModule {} diff --git a/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts b/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts index d9c2f748ed..c0af458d4a 100644 --- a/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts +++ b/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts @@ -33,10 +33,7 @@ import { ToggleDocumentDisplayMode } from '../../../store/actions'; @Component({ selector: 'app-document-display-mode', template: ` - diff --git a/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts new file mode 100644 index 0000000000..a6aae0c754 --- /dev/null +++ b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts @@ -0,0 +1,113 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { TestBed } from '@angular/core/testing'; +import { + CoreModule, + AlfrescoApiService, + AlfrescoApiServiceMock +} from '@alfresco/adf-core'; +import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library.component'; +import { LibraryFavoriteDirective } from '../../../directives/library-favorite.directive'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { of } from 'rxjs'; +import { Router } from '@angular/router'; + +describe('ToggleFavoriteLibraryComponent', () => { + let fixture; + let component; + let contentManagementService; + const selection = { library: { entry: { id: 'libraryId' } } }; + const mockRouter = { + url: '' + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CoreModule, AppTestingModule], + declarations: [ToggleFavoriteLibraryComponent, LibraryFavoriteDirective], + providers: [ + { + provide: Router, + useValue: mockRouter + }, + { + provide: AlfrescoApiService, + useClass: AlfrescoApiServiceMock + }, + { + provide: Store, + useValue: { + dispatch: () => {}, + select: () => of(selection) + } + }, + ContentManagementService + ], + schemas: [NO_ERRORS_SCHEMA] + }); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ToggleFavoriteLibraryComponent); + component = fixture.componentInstance; + + contentManagementService = TestBed.get(ContentManagementService); + const api = TestBed.get(AlfrescoApiService); + spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve()); + }); + + it('should get library selection from Store', done => { + fixture.detectChanges(); + component.selection$.subscribe(selected => { + expect(selected.library.entry.id).toEqual(selection.library.entry.id); + done(); + }); + }); + + it('should mark selection as favorite when on favorite libraries route', done => { + mockRouter.url = '/favorite/libraries'; + fixture.detectChanges(); + + component.selection$.subscribe(selected => { + expect(selected.library.isFavorite).toBe(true); + done(); + }); + }); + + it('should emit onToggleEvent() event', () => { + fixture.detectChanges(); + spyOn(contentManagementService.favoriteLibraryToggle, 'next'); + + component.onToggleEvent(); + + expect( + contentManagementService.favoriteLibraryToggle.next + ).toHaveBeenCalled(); + }); +}); diff --git a/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts new file mode 100644 index 0000000000..e586cae1d7 --- /dev/null +++ b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts @@ -0,0 +1,88 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, ViewEncapsulation, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../../store/states'; +import { appSelection } from '../../../store/selectors/app.selectors'; +import { Observable } from 'rxjs'; +import { SelectionState } from '@alfresco/adf-extensions'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { distinctUntilChanged, map } from 'rxjs/operators'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-toggle-favorite-library', + template: ` + + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toggle-favorite-library' } +}) +export class ToggleFavoriteLibraryComponent implements OnInit { + selection$: Observable; + + constructor( + private store: Store, + private content: ContentManagementService, + private router: Router + ) {} + + ngOnInit() { + const isFavoriteLibraries = this.router.url.startsWith( + '/favorite/libraries' + ); + + this.selection$ = this.store.select(appSelection).pipe( + distinctUntilChanged(), + map(selection => { + // favorite libraries list should already be marked as favorite + if (selection.library && isFavoriteLibraries) { + (selection.library).isFavorite = true; + return selection; + } + return selection; + }) + ); + } + + onToggleEvent() { + this.content.favoriteLibraryToggle.next(); + } +} diff --git a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts index 55a547cedd..47f047e4d7 100644 --- a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts +++ b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts @@ -35,15 +35,16 @@ import { ContentManagementService } from '../../../services/content-management.s selector: 'app-toggle-favorite', template: ` - `, + `, encapsulation: ViewEncapsulation.None, host: { class: 'app-toggle-favorite' } }) diff --git a/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts b/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts index 4d9f609ec0..144c8f0e17 100644 --- a/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts +++ b/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts @@ -33,14 +33,15 @@ import { ToggleInfoDrawerAction } from '../../../store/actions'; @Component({ selector: 'app-toggle-info-drawer', template: ` - - `, + + `, encapsulation: ViewEncapsulation.None, host: { class: 'app-toggle-info-drawer' } }) diff --git a/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts b/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts new file mode 100644 index 0000000000..0d0b2bafc8 --- /dev/null +++ b/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts @@ -0,0 +1,95 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, ViewEncapsulation } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../../store/states'; +import { appSelection } from '../../../store/selectors/app.selectors'; +import { Observable } from 'rxjs'; +import { SelectionState } from '@alfresco/adf-extensions'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { + SnackbarErrorAction, + SnackbarInfoAction +} from '../../../store/actions/snackbar.actions'; +import { SetSelectedNodesAction } from '../../../store/actions/node.actions'; + +@Component({ + selector: 'app-toggle-join-library-button', + template: ` + + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toggle-join-library' } +}) +export class ToggleJoinLibraryButtonComponent { + selection$: Observable; + + constructor( + private store: Store, + private content: ContentManagementService + ) { + this.selection$ = this.store.select(appSelection); + } + + onToggleEvent(event) { + this.store.dispatch(new SnackbarInfoAction(event.i18nKey)); + + if (event.shouldReload) { + this.content.libraryJoined.next(); + } else { + if (event.updatedEntry) { + this.store.dispatch( + new SetSelectedNodesAction([ + { entry: event.updatedEntry, isLibrary: true } + ]) + ); + } + this.content.joinLibraryToggle.next(); + } + } + + onErrorEvent(event) { + this.store.dispatch(new SnackbarErrorAction(event.i18nKey)); + } +} diff --git a/src/app/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts b/src/app/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts new file mode 100644 index 0000000000..82450a0819 --- /dev/null +++ b/src/app/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts @@ -0,0 +1,66 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, ViewEncapsulation } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../../store/states'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { ToggleJoinLibraryButtonComponent } from './toggle-join-library-button.component'; + +@Component({ + selector: 'app-toggle-join-library-menu', + template: ` + + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toggle-join-library' } +}) +export class ToggleJoinLibraryMenuComponent extends ToggleJoinLibraryButtonComponent { + constructor(store: Store, content: ContentManagementService) { + super(store, content); + } +} diff --git a/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts b/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts new file mode 100644 index 0000000000..7899f82cb8 --- /dev/null +++ b/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts @@ -0,0 +1,134 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { of } from 'rxjs'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-core'; +import { LibraryMembershipDirective } from '../../../directives/library-membership.directive'; +import { Store } from '@ngrx/store'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { + SnackbarErrorAction, + SnackbarInfoAction +} from '../../../store/actions/snackbar.actions'; +import { AppTestingModule } from '../../../testing/app-testing.module'; +import { ContentManagementService } from '../../../services/content-management.service'; +import { ToggleJoinLibraryButtonComponent } from './toggle-join-library-button.component'; + +describe('ToggleJoinLibraryComponent', () => { + let component: ToggleJoinLibraryButtonComponent; + let fixture: ComponentFixture; + let alfrescoApi: AlfrescoApiService; + let contentManagementService: ContentManagementService; + let entry; + + const storeMock = { + select: () => of({ library: { entry, isLibrary: true } }), + dispatch: jasmine.createSpy('dispatch') + }; + + beforeEach(() => { + entry = { + id: 'lib-id', + joinRequested: true, + title: 'test', + visibility: 'MODERATED' + }; + + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + ToggleJoinLibraryButtonComponent, + LibraryMembershipDirective + ], + providers: [ + { provide: Store, useValue: storeMock }, + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(ToggleJoinLibraryButtonComponent); + component = fixture.componentInstance; + alfrescoApi = TestBed.get(AlfrescoApiService); + contentManagementService = TestBed.get(ContentManagementService); + + spyOn(alfrescoApi.peopleApi, 'getSiteMembershipRequest').and.stub(); + }); + + afterEach(() => { + fixture.destroy(); + storeMock.dispatch.calls.reset(); + }); + + it('should get Store selection entry on initialization', done => { + component.selection$.subscribe(selection => { + expect(selection.library.entry).toEqual(entry); + done(); + }); + }); + + it('should dispatch `SnackbarErrorAction` action on error', () => { + const event = { event: {}, i18nKey: 'ERROR_i18nKey' }; + component.onErrorEvent(event); + + expect(storeMock.dispatch).toHaveBeenCalledWith( + new SnackbarErrorAction(event.i18nKey) + ); + }); + + it('should dispatch `SnackbarInfoAction` action on onToggleEvent', () => { + const event = { shouldReload: true, i18nKey: 'SOME_i18nKey' }; + component.onToggleEvent(event); + + expect(storeMock.dispatch).toHaveBeenCalledWith( + new SnackbarInfoAction(event.i18nKey) + ); + }); + + it('should call libraryJoined.next on contentManagementService onToggleEvent', done => { + spyOn(contentManagementService.libraryJoined, 'next').and.callThrough(); + + contentManagementService.libraryJoined.subscribe(() => { + expect(contentManagementService.libraryJoined.next).toHaveBeenCalled(); + done(); + }); + const event = { shouldReload: true }; + component.onToggleEvent(event); + }); + + it('should call joinLibraryToggle.next on contentManagementService onToggleEvent', done => { + spyOn(contentManagementService.joinLibraryToggle, 'next').and.callThrough(); + + contentManagementService.joinLibraryToggle.subscribe(() => { + expect( + contentManagementService.joinLibraryToggle.next + ).toHaveBeenCalled(); + done(); + }); + const event = { shouldReload: false }; + component.onToggleEvent(event); + }); +}); diff --git a/src/app/components/toolbar/toolbar-button/toolbar-button.component.html b/src/app/components/toolbar/toolbar-button/toolbar-button.component.html index 5511768f06..abe65578f7 100644 --- a/src/app/components/toolbar/toolbar-button/toolbar-button.component.html +++ b/src/app/components/toolbar/toolbar-button/toolbar-button.component.html @@ -1,15 +1,16 @@ - - - - - - + + + + + + diff --git a/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html index a60e4b3e9d..0e043883a5 100644 --- a/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html +++ b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html @@ -1,19 +1,21 @@ - - + - + @@ -30,17 +32,15 @@ mat-menu-item color="primary" [disabled]="actionRef.disabled" - [attr.title]="( - actionRef.disabled - ? actionRef['description-disabled'] - : actionRef.description || actionRef.title - ) | translate" - (click)="runAction()"> - {{ actionRef.icon }} + [attr.title]=" + (actionRef.disabled + ? actionRef['description-disabled'] + : actionRef.description || actionRef.title) | translate + " + (click)="runAction()" + > + {{ actionRef.title | translate }} - - - diff --git a/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html index 2fde4514b9..3e7a298fa3 100644 --- a/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html +++ b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html @@ -2,9 +2,10 @@ [id]="actionRef.id" [color]="color" mat-icon-button - [attr.title]="(actionRef.description || actionRef.title) | translate" - [matMenuTriggerFor]="menu"> - {{ actionRef.icon }} + [attr.title]="actionRef.description || actionRef.title | translate" + [matMenuTriggerFor]="menu" +> + diff --git a/src/app/components/toolbar/toolbar.module.ts b/src/app/components/toolbar/toolbar.module.ts index fe1302b36c..4bbd9f2957 100644 --- a/src/app/components/toolbar/toolbar.module.ts +++ b/src/app/components/toolbar/toolbar.module.ts @@ -34,6 +34,11 @@ import { ToolbarActionComponent } from './toolbar-action/toolbar-action.componen import { ExtensionsModule } from '@alfresco/adf-extensions'; import { ToolbarMenuItemComponent } from './toolbar-menu-item/toolbar-menu-item.component'; import { ToolbarMenuComponent } from './toolbar-menu/toolbar-menu.component'; +import { ToggleJoinLibraryButtonComponent } from './toggle-join-library/toggle-join-library-button.component'; +import { ToggleJoinLibraryMenuComponent } from './toggle-join-library/toggle-join-library-menu.component'; +import { DirectivesModule } from '../../directives/directives.module'; +import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library/toggle-favorite-library.component'; +import { AppCommonModule } from '../common/common.module'; export function components() { return [ @@ -43,12 +48,21 @@ export function components() { ToolbarButtonComponent, ToolbarActionComponent, ToolbarMenuItemComponent, - ToolbarMenuComponent + ToolbarMenuComponent, + ToggleJoinLibraryButtonComponent, + ToggleJoinLibraryMenuComponent, + ToggleFavoriteLibraryComponent ]; } @NgModule({ - imports: [CommonModule, CoreModule.forChild(), ExtensionsModule], + imports: [ + CommonModule, + CoreModule.forChild(), + AppCommonModule, + ExtensionsModule, + DirectivesModule + ], declarations: components(), exports: components(), entryComponents: components() diff --git a/src/app/components/trashcan/trashcan.component.html b/src/app/components/trashcan/trashcan.component.html index 0b4d259467..f505b29efe 100644 --- a/src/app/components/trashcan/trashcan.component.html +++ b/src/app/components/trashcan/trashcan.component.html @@ -1,83 +1,84 @@ -
-
- - + - - + + + - - - - -
- -
-
- + + + + + + - - - -

{{ 'APP.BROWSE.TRASHCAN.EMPTY_STATE.FIRST_TEXT' | translate }}

-

{{ 'APP.BROWSE.TRASHCAN.EMPTY_STATE.SECOND_TEXT' | translate }}

-
-
-
+ +
+ - - + + + +

{{ 'APP.BROWSE.TRASHCAN.EMPTY_STATE.FIRST_TEXT' | translate }}

+

{{ 'APP.BROWSE.TRASHCAN.EMPTY_STATE.SECOND_TEXT' | translate }}

+
+
+
- - - - - - - - + + - - - - - - + + + + + + + + + + [key]="column.key" + [title]="column.title" + [type]="column.type" + [format]="column.format" + [class]="column.class" + [sortable]="column.sortable"> - -
+ + + - - -
+ + + +
+ + +
-
+ + + diff --git a/src/app/components/trashcan/trashcan.component.ts b/src/app/components/trashcan/trashcan.component.ts index 2ee8a11683..8c05affda1 100644 --- a/src/app/components/trashcan/trashcan.component.ts +++ b/src/app/components/trashcan/trashcan.component.ts @@ -59,7 +59,6 @@ export class TrashcanComponent extends PageComponent implements OnInit { this.subscriptions.push( this.content.nodesRestored.subscribe(() => this.reload()), this.content.nodesPurged.subscribe(() => this.reload()), - this.content.nodesRestored.subscribe(() => this.reload()), this.breakpointObserver .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) diff --git a/src/app/components/trashcan/trashcan.module.ts b/src/app/components/trashcan/trashcan.module.ts index ee88ac94bc..ef6e507f24 100644 --- a/src/app/components/trashcan/trashcan.module.ts +++ b/src/app/components/trashcan/trashcan.module.ts @@ -33,6 +33,7 @@ import { AppCommonModule } from '../common/common.module'; import { AppToolbarModule } from '../toolbar/toolbar.module'; import { DirectivesModule } from '../../directives/directives.module'; import { ContextMenuModule } from '../context-menu/context-menu.module'; +import { AppLayoutModule } from '../layout/layout.module'; const routes: Routes = [ { @@ -54,7 +55,8 @@ const routes: Routes = [ DirectivesModule, AppCommonModule, AppToolbarModule, - ContextMenuModule + ContextMenuModule, + AppLayoutModule ], declarations: [TrashcanComponent], exports: [TrashcanComponent] diff --git a/src/app/components/upload-dialog/file-uploading-dialog.component.html b/src/app/components/upload-dialog/file-uploading-dialog.component.html new file mode 100644 index 0000000000..c4f2460795 --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-dialog.component.html @@ -0,0 +1,116 @@ +
+
+ + + + {{ 'FILE_UPLOAD.MESSAGES.UPLOAD_COMPLETED' + | translate: { + completed: totalCompleted, + total: filesUploadingList.length + } + }} + + + + {{ 'FILE_UPLOAD.MESSAGES.UPLOAD_CANCELED' | translate }} + +
+ +
+ {{ + (totalErrors > 1 + ? 'FILE_UPLOAD.MESSAGES.UPLOAD_ERRORS' + : 'FILE_UPLOAD.MESSAGES.UPLOAD_ERROR') + | translate: { total: totalErrors } + }} +
+ +
+ + + + + + + +
+

+ {{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TITLE' | translate }} +

+ +

+ {{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TEXT' | translate }} +

+
+
+ +
+ + + +
+ +
+ + + +
+
diff --git a/src/app/components/upload-dialog/file-uploading-dialog.component.ts b/src/app/components/upload-dialog/file-uploading-dialog.component.ts new file mode 100644 index 0000000000..c2f4615d97 --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-dialog.component.ts @@ -0,0 +1,195 @@ +/*! + * @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 { + FileModel, + FileUploadCompleteEvent, + FileUploadDeleteEvent, + FileUploadErrorEvent, + FileUploadStatus, + UploadService +} from '@alfresco/adf-core'; +import { + ChangeDetectorRef, + Component, + Input, + Output, + EventEmitter, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core'; +import { Subscription, merge } from 'rxjs'; +import { FileUploadingListComponent } from './file-uploading-list.component'; + +// @deprecated file-uploading-dialog TODO remove in 3.0.0 +@Component({ + selector: 'app-file-uploading-dialog', + templateUrl: './file-uploading-dialog.component.html' +}) +export class FileUploadingDialogComponent implements OnInit, OnDestroy { + @ViewChild('uploadList') + uploadList: FileUploadingListComponent; + + /** Dialog position. Can be 'left' or 'right'. */ + @Input() + position = 'right'; + + /** Emitted when a file in the list has an error. */ + @Output() + error: EventEmitter = new EventEmitter(); + + filesUploadingList: FileModel[] = []; + isDialogActive = false; + totalCompleted = 0; + totalErrors = 0; + isDialogMinimized = false; + isConfirmation = false; + + private listSubscription: Subscription; + private counterSubscription: Subscription; + private fileUploadSubscription: Subscription; + private errorSubscription: Subscription; + private errors = []; + + constructor( + private uploadService: UploadService, + private changeDetector: ChangeDetectorRef + ) {} + + ngOnInit() { + this.listSubscription = this.uploadService.queueChanged.subscribe( + (fileList: FileModel[]) => { + this.filesUploadingList = fileList; + + if (this.filesUploadingList.length) { + this.isDialogActive = true; + } + } + ); + + this.counterSubscription = merge( + this.uploadService.fileUploadComplete, + this.uploadService.fileUploadDeleted + ).subscribe((event: FileUploadDeleteEvent | FileUploadCompleteEvent) => { + this.totalCompleted = event.totalComplete; + this.changeDetector.detectChanges(); + }); + + // todo: move to ADF ACA-2051 + this.errorSubscription = this.uploadService.fileUploadError.subscribe( + (event: FileUploadErrorEvent) => { + const statusCode = (event.error || {}).response + ? event.error.response.statusCode + : null; + this.errors.push({ + fileName: event.file.name, + status: statusCode, + message: this.getUploadErrorMessage(statusCode) + }); + this.totalErrors = event.totalError; + this.changeDetector.detectChanges(); + } + ); + + this.fileUploadSubscription = this.uploadService.fileUpload.subscribe( + event => { + this.changeDetector.detectChanges(); + } + ); + + this.uploadService.fileDeleted.subscribe(objId => { + if (this.filesUploadingList) { + const file = this.filesUploadingList.find(item => { + return item.data.entry.id === objId; + }); + if (file) { + file.status = FileUploadStatus.Cancelled; + this.changeDetector.detectChanges(); + } + } + }); + } + + getFileUploadError(file) { + return this.errors.find(error => (error.fileName = file.name)); + } + + /** + * Toggle confirmation message. + */ + toggleConfirmation() { + this.isConfirmation = !this.isConfirmation; + + if (this.isDialogMinimized) { + this.isDialogMinimized = false; + } + } + + /** + * Cancel uploads and hide confirmation + */ + cancelAllUploads() { + this.toggleConfirmation(); + + this.uploadList.cancelAllFiles(); + } + + /** + * Toggle dialog minimized state. + */ + toggleMinimized(): void { + this.isDialogMinimized = !this.isDialogMinimized; + this.changeDetector.detectChanges(); + } + + /** + * Dismiss dialog + */ + close(): void { + this.isConfirmation = false; + this.totalCompleted = 0; + this.totalErrors = 0; + this.filesUploadingList = []; + this.isDialogActive = false; + this.isDialogMinimized = false; + this.uploadService.clearQueue(); + this.changeDetector.detectChanges(); + this.errors = []; + } + + ngOnDestroy() { + this.uploadService.clearQueue(); + this.listSubscription.unsubscribe(); + this.counterSubscription.unsubscribe(); + this.fileUploadSubscription.unsubscribe(); + this.errorSubscription.unsubscribe(); + } + + // todo: move to ADF ACA-2051 + private getUploadErrorMessage(status) { + const messages = { + 500: 'APP.MESSAGES.UPLOAD.ERROR.500', + 504: 'APP.MESSAGES.UPLOAD.ERROR.504', + 403: 'APP.MESSAGES.UPLOAD.ERROR.403', + 404: 'APP.MESSAGES.UPLOAD.ERROR.404', + generic: 'APP.MESSAGES.UPLOAD.ERROR.GENERIC' + }; + + return messages[status] || messages['generic']; + } +} diff --git a/src/app/components/upload-dialog/file-uploading-list-row.component.html b/src/app/components/upload-dialog/file-uploading-list-row.component.html new file mode 100644 index 0000000000..57cba25fde --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-list-row.component.html @@ -0,0 +1,81 @@ +
+ + insert_drive_file + + + + {{ file.name }} + + +
+ + {{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }} + + + + clear + +
+ +
+ + check_circle + + + + remove_circle + +
+ +
+ + schedule + + + + remove_circle + +
+ + +
+ + report_problem + +
+ +
+ {{ 'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate }} +
+
diff --git a/src/app/components/upload-dialog/file-uploading-list-row.component.ts b/src/app/components/upload-dialog/file-uploading-list-row.component.ts new file mode 100644 index 0000000000..7bb564eccd --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-list-row.component.ts @@ -0,0 +1,47 @@ +/*! + * @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 { FileModel, FileUploadStatus } from '@alfresco/adf-core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'app-file-uploading-list-row', + templateUrl: './file-uploading-list-row.component.html' +}) +export class FileUploadingListRowComponent { + @Input() + file: FileModel; + + @Input() + error: any; + + @Output() + cancel: EventEmitter = new EventEmitter(); + + @Output() + remove: EventEmitter = new EventEmitter(); + + FileUploadStatus = FileUploadStatus; + + onCancel(file: FileModel): void { + this.cancel.emit(file); + } + + onRemove(file: FileModel): void { + this.remove.emit(file); + } +} diff --git a/src/app/components/upload-dialog/file-uploading-list.component.html b/src/app/components/upload-dialog/file-uploading-list.component.html new file mode 100644 index 0000000000..91a4d358d8 --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-list.component.html @@ -0,0 +1,7 @@ +
+ + +
diff --git a/src/app/components/upload-dialog/file-uploading-list.component.ts b/src/app/components/upload-dialog/file-uploading-list.component.ts new file mode 100644 index 0000000000..f70e07e035 --- /dev/null +++ b/src/app/components/upload-dialog/file-uploading-list.component.ts @@ -0,0 +1,180 @@ +/*! + * @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 { + FileModel, + FileUploadStatus, + NodesApiService, + TranslationService, + UploadService +} from '@alfresco/adf-core'; +import { + Component, + ContentChild, + Input, + Output, + TemplateRef, + EventEmitter +} from '@angular/core'; +import { Observable, forkJoin, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; + +@Component({ + selector: 'app-file-uploading-list', + templateUrl: './file-uploading-list.component.html' +}) +export class FileUploadingListComponent { + FileUploadStatus = FileUploadStatus; + + @ContentChild(TemplateRef) + template: any; + + @Input() + files: FileModel[] = []; + + /** Emitted when a file in the list has an error. */ + @Output() + error: EventEmitter = new EventEmitter(); + + constructor( + private uploadService: UploadService, + private nodesApi: NodesApiService, + private translateService: TranslationService + ) {} + + /** + * Cancel file upload + * + * @param file File model to cancel upload for. + * + * @memberOf FileUploadingListComponent + */ + cancelFile(file: FileModel): void { + this.uploadService.cancelUpload(file); + } + + removeFile(file: FileModel): void { + this.deleteNode(file).subscribe(() => { + if (file.status === FileUploadStatus.Error) { + this.notifyError(file); + } + + this.uploadService.cancelUpload(file); + }); + } + + /** + * Call the appropriate method for each file, depending on state + */ + cancelAllFiles(): void { + this.getUploadingFiles().forEach(file => + this.uploadService.cancelUpload(file) + ); + + const deletedFiles = this.files + .filter(file => file.status === FileUploadStatus.Complete) + .map(file => this.deleteNode(file)); + + forkJoin(...deletedFiles).subscribe((files: FileModel[]) => { + const errors = files.filter( + file => file.status === FileUploadStatus.Error + ); + + if (errors.length) { + this.notifyError(...errors); + } + + this.uploadService.cancelUpload(...files); + }); + } + + /** + * Checks if all the files are uploaded false if there is at least one file in Progress | Starting | Pending + */ + isUploadCompleted(): boolean { + return ( + !this.isUploadCancelled() && + Boolean(this.files.length) && + !this.files.some( + ({ status }) => + status === FileUploadStatus.Starting || + status === FileUploadStatus.Progress || + status === FileUploadStatus.Pending + ) + ); + } + + /** + * Check if all the files are Cancelled | Aborted | Error. false if there is at least one file in uploading states + */ + isUploadCancelled(): boolean { + return ( + !!this.files.length && + this.files.every( + ({ status }) => + status === FileUploadStatus.Aborted || + status === FileUploadStatus.Cancelled || + status === FileUploadStatus.Deleted + ) + ); + } + + private deleteNode(file: FileModel): Observable { + const { id } = file.data.entry; + + return this.nodesApi.deleteNode(id, { permanent: true }).pipe( + map(() => { + file.status = FileUploadStatus.Deleted; + return file; + }), + catchError(() => { + file.status = FileUploadStatus.Error; + return of(file); + }) + ); + } + + private notifyError(...files: FileModel[]) { + let messageError: string = null; + + if (files.length === 1) { + messageError = this.translateService.instant( + 'FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR', + { fileName: files[0].name } + ); + } else { + messageError = this.translateService.instant( + 'FILE_UPLOAD.MESSAGES.REMOVE_FILES_ERROR', + { total: files.length } + ); + } + + this.error.emit(messageError); + } + + private getUploadingFiles() { + return this.files.filter(item => { + if ( + item.status === FileUploadStatus.Pending || + item.status === FileUploadStatus.Progress || + item.status === FileUploadStatus.Starting + ) { + return item; + } + }); + } +} diff --git a/src/app/components/upload-dialog/upload-dialog.module.ts b/src/app/components/upload-dialog/upload-dialog.module.ts new file mode 100644 index 0000000000..24606c41e5 --- /dev/null +++ b/src/app/components/upload-dialog/upload-dialog.module.ts @@ -0,0 +1,46 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CoreModule } from '@alfresco/adf-core'; +import { FileUploadingDialogComponent } from './file-uploading-dialog.component'; +import { FileUploadingListRowComponent } from './file-uploading-list-row.component'; +import { FileUploadingListComponent } from './file-uploading-list.component'; + +@NgModule({ + imports: [CommonModule, CoreModule.forChild()], + declarations: [ + FileUploadingDialogComponent, + FileUploadingListRowComponent, + FileUploadingListComponent + ], + exports: [ + FileUploadingDialogComponent, + FileUploadingListRowComponent, + FileUploadingListComponent + ] +}) +export class AppUploadingDialogModule {} diff --git a/src/app/dialogs/library/form.validators.ts b/src/app/dialogs/library/form.validators.ts deleted file mode 100644 index 97937f9b3e..0000000000 --- a/src/app/dialogs/library/form.validators.ts +++ /dev/null @@ -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 { AbstractControl, FormControl } from '@angular/forms'; -import { ContentApiService } from '../../services/content-api.service'; - -export class SiteIdValidator { - static createValidator(contentApiService: ContentApiService) { - let timer; - - return (control: AbstractControl) => { - if (timer) { - clearTimeout(timer); - } - - return new Promise(resolve => { - timer = setTimeout(() => { - return contentApiService - .getSite(control.value) - .subscribe( - () => resolve({ message: 'LIBRARY.ERRORS.EXISTENT_SITE' }), - () => resolve(null) - ); - }, 300); - }); - }; - } -} - -export function forbidSpecialCharacters({ value }: FormControl) { - const validCharacters: RegExp = /[^A-Za-z0-9-]/; - const isValid: boolean = !validCharacters.test(value); - - return isValid - ? null - : { - message: 'LIBRARY.ERRORS.ILLEGAL_CHARACTERS' - }; -} diff --git a/src/app/dialogs/library/library.dialog.html b/src/app/dialogs/library/library.dialog.html index 6cce48c101..2314c585b3 100644 --- a/src/app/dialogs/library/library.dialog.html +++ b/src/app/dialogs/library/library.dialog.html @@ -1,79 +1,90 @@ -

- {{ createTitle | translate }} -

+

{{ createTitle | translate }}

-
- - + + + - - {{ 'LIBRARY.ERRORS.DESCRIPTION_TOO_LONG' | translate }} - - + {{ + 'LIBRARY.HINTS.SITE_TITLE_EXISTS' | translate + }} - - + + {{ 'LIBRARY.ERRORS.TITLE_TOO_LONG' | translate }} + - - {{ form.controls['id'].errors?.message | translate }} - + + {{ form.controls['title'].errors?.message | translate }} + - - {{ 'LIBRARY.ERRORS.ID_TOO_LONG' | translate }} - - + - - + + - - {{ 'LIBRARY.ERRORS.DESCRIPTION_TOO_LONG' | translate }} - - + + {{ form.controls['id'].errors?.message | translate }} + - - - {{ option.label | translate }} - - - + + {{ 'LIBRARY.ERRORS.ID_TOO_LONG' | translate }} + +
+ + + + + + {{ 'LIBRARY.ERRORS.DESCRIPTION_TOO_LONG' | translate }} + + + + + + {{ option.label | translate }} + + +
- + - + diff --git a/src/app/dialogs/library/library.dialog.scss b/src/app/dialogs/library/library.dialog.scss index 50bd2f7f5e..5c8abdba9c 100644 --- a/src/app/dialogs/library/library.dialog.scss +++ b/src/app/dialogs/library/library.dialog.scss @@ -1,19 +1,29 @@ -.mat-radio-group { - display: flex; - flex-direction: column; - margin: 0 0 20px 0; -} +.app-library-dialog { + .mat-radio-group { + display: flex; + flex-direction: column; + margin: 0 0 20px 0; + } -.mat-radio-group .mat-radio-button { - margin: 10px 0; -} + .mat-radio-group .mat-radio-button { + margin: 10px 0; + } -.mat-form-field { - width: 100%; -} + .mat-form-field { + width: 100%; + } + + mat-form-field { + padding-top: 20px; + } + + .actions-buttons { + display: flex; + flex-direction: row; + justify-content: flex-end; -.actions-buttons { - display: flex; - flex-direction: row; - justify-content: flex-end; + .mat-button { + text-transform: uppercase; + } + } } diff --git a/src/app/dialogs/library/library.dialog.spec.ts b/src/app/dialogs/library/library.dialog.spec.ts index a5c0f3fa36..01808c1452 100644 --- a/src/app/dialogs/library/library.dialog.spec.ts +++ b/src/app/dialogs/library/library.dialog.spec.ts @@ -23,10 +23,226 @@ * along with Alfresco. If not, see . */ +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ReactiveFormsModule } from '@angular/forms'; +import { CoreModule } from '@alfresco/adf-core'; import { LibraryDialogComponent } from './library.dialog'; +import { TestBed, fakeAsync, tick, flush } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; +import { + AlfrescoApiService, + AlfrescoApiServiceMock, + setupTestBed +} from '@alfresco/adf-core'; describe('LibraryDialogComponent', () => { - it('should be defined', () => { - expect(LibraryDialogComponent).toBeDefined(); + let fixture; + let component; + let alfrescoApi; + const dialogRef = { + close: jasmine.createSpy('close') + }; + + setupTestBed({ + imports: [NoopAnimationsModule, CoreModule, ReactiveFormsModule], + declarations: [LibraryDialogComponent], + providers: [ + { + provide: AlfrescoApiService, + useClass: AlfrescoApiServiceMock + }, + { provide: MatDialogRef, useValue: dialogRef } + ], + schemas: [NO_ERRORS_SCHEMA] }); + + beforeEach(() => { + fixture = TestBed.createComponent(LibraryDialogComponent); + component = fixture.componentInstance; + alfrescoApi = TestBed.get(AlfrescoApiService); + + spyOn( + alfrescoApi.getInstance().core.queriesApi, + 'findSites' + ).and.returnValue( + Promise.resolve({ + list: { entries: [] } + }) + ); + }); + + it('should set library id automatically on title input', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('libraryTitle'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe('libraryTitle'); + })); + + it('should translate library title space character to dash for library id', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('library title'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe('library-title'); + })); + + it('should not translate library title if value is not a valid id', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('@@@####'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe(null); + })); + + it('should translate library title partially for library id', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('@@@####library'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe('library'); + })); + + it('should translate library title multiple space character to one dash for library id', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('library title'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe('library-title'); + })); + + it('should not change custom library id on title input', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.id.setValue('custom-id'); + component.form.controls.id.markAsDirty(); + tick(500); + flush(); + fixture.detectChanges(); + + component.form.controls.title.setValue('library title'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.value).toBe('custom-id'); + })); + + it('should invalidate form when library id already exists', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'getSite').and.returnValue(Promise.resolve()); + + fixture.detectChanges(); + component.form.controls.id.setValue('existingLibrary'); + tick(500); + flush(); + fixture.detectChanges(); + + expect(component.form.controls.id.errors).toEqual({ + message: 'LIBRARY.ERRORS.EXISTENT_SITE' + }); + expect(component.form.valid).toBe(false); + })); + + it('should create site when form is valid', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'createSite').and.returnValue( + Promise.resolve() + ); + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('library title'); + tick(500); + flush(); + fixture.detectChanges(); + + component.submit(); + fixture.detectChanges(); + flush(); + + expect(alfrescoApi.sitesApi.createSite).toHaveBeenCalledWith({ + id: 'library-title', + title: 'library title', + description: '', + visibility: 'PUBLIC' + }); + })); + + it('should not create site when form is invalid', fakeAsync(() => { + spyOn(alfrescoApi.sitesApi, 'createSite').and.returnValue( + Promise.resolve({}) + ); + spyOn(alfrescoApi.sitesApi, 'getSite').and.returnValue(Promise.resolve()); + + fixture.detectChanges(); + component.form.controls.title.setValue('existingLibrary'); + tick(500); + flush(); + fixture.detectChanges(); + + component.submit(); + fixture.detectChanges(); + flush(); + + expect(alfrescoApi.sitesApi.createSite).not.toHaveBeenCalled(); + })); + + it('should notify on 409 conflict error (might be in trash)', fakeAsync(() => { + const error = { message: '{ "error": { "statusCode": 409 } }' }; + spyOn(alfrescoApi.sitesApi, 'createSite').and.callFake(() => { + return new Promise((resolve, reject) => reject(error)); + }); + spyOn(alfrescoApi.sitesApi, 'getSite').and.callFake(() => { + return new Promise((resolve, reject) => reject()); + }); + + fixture.detectChanges(); + component.form.controls.title.setValue('test'); + tick(500); + flush(); + fixture.detectChanges(); + + component.submit(); + fixture.detectChanges(); + flush(); + + expect(component.form.controls.id.errors).toEqual({ + message: 'LIBRARY.ERRORS.CONFLICT' + }); + })); }); diff --git a/src/app/dialogs/library/library.dialog.ts b/src/app/dialogs/library/library.dialog.ts index 2c74736bf2..7bfcbf8e98 100644 --- a/src/app/dialogs/library/library.dialog.ts +++ b/src/app/dialogs/library/library.dialog.ts @@ -15,28 +15,45 @@ * limitations under the License. */ -import { Observable } from 'rxjs'; -import { Component, OnInit, Output, EventEmitter } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Observable, Subject, from } from 'rxjs'; +import { + Component, + OnInit, + Output, + EventEmitter, + OnDestroy, + ViewEncapsulation +} from '@angular/core'; +import { + FormBuilder, + FormGroup, + Validators, + FormControl, + AbstractControl +} from '@angular/forms'; import { MatDialogRef } from '@angular/material'; -import { SiteBody, SiteEntry } from 'alfresco-js-api'; -import { ContentApiService } from '../../services/content-api.service'; -import { SiteIdValidator, forbidSpecialCharacters } from './form.validators'; -import { debounceTime } from 'rxjs/operators'; +import { SiteBody, SiteEntry, SitePaging } from 'alfresco-js-api'; +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { debounceTime, mergeMap, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-library-dialog', styleUrls: ['./library.dialog.scss'], - templateUrl: './library.dialog.html' + templateUrl: './library.dialog.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-library-dialog' } }) -export class LibraryDialogComponent implements OnInit { +export class LibraryDialogComponent implements OnInit, OnDestroy { @Output() error: EventEmitter = new EventEmitter(); @Output() success: EventEmitter = new EventEmitter(); + onDestroy$: Subject = new Subject(); + createTitle = 'LIBRARY.DIALOG.CREATE_TITLE'; + libraryTitleExists = false; form: FormGroup; visibilityOption: any; visibilityOptions = [ @@ -50,9 +67,9 @@ export class LibraryDialogComponent implements OnInit { ]; constructor( + private alfrescoApiService: AlfrescoApiService, private formBuilder: FormBuilder, - private dialog: MatDialogRef, - private contentApi: ContentApiService + private dialog: MatDialogRef ) {} ngOnInit() { @@ -60,34 +77,43 @@ export class LibraryDialogComponent implements OnInit { id: [ Validators.required, Validators.maxLength(72), - forbidSpecialCharacters + this.forbidSpecialCharacters + ], + title: [ + Validators.required, + this.forbidOnlySpaces, + Validators.maxLength(256) ], - title: [Validators.required, Validators.maxLength(256)], description: [Validators.maxLength(512)] }; this.form = this.formBuilder.group({ - title: ['', validators.title], - id: ['', validators.id, SiteIdValidator.createValidator(this.contentApi)], + title: [null, validators.title], + id: [null, validators.id, this.createSiteIdValidator()], description: ['', validators.description] }); this.visibilityOption = this.visibilityOptions[0].value; this.form.controls['title'].valueChanges - .pipe(debounceTime(300)) - .subscribe((titleValue: string) => { - if (!titleValue.trim().length) { - return; - } - - if (!this.form.controls['id'].dirty) { - this.form.patchValue({ id: this.sanitize(titleValue.trim()) }); + .pipe( + debounceTime(300), + mergeMap(title => this.checkLibraryNameExists(title), title => title), + takeUntil(this.onDestroy$) + ) + .subscribe((title: string) => { + if (!this.form.controls['id'].dirty && this.canGenerateId(title)) { + this.form.patchValue({ id: this.sanitize(title.trim()) }); this.form.controls['id'].markAsTouched(); } }); } + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + get title(): string { const { title } = this.form.value; @@ -131,7 +157,7 @@ export class LibraryDialogComponent implements OnInit { } private create(): Observable { - const { contentApi, title, id, description, visibility } = this; + const { title, id, description, visibility } = this; const siteBody = { id, title, @@ -139,11 +165,15 @@ export class LibraryDialogComponent implements OnInit { visibility }; - return contentApi.createSite(siteBody); + return from(this.alfrescoApiService.sitesApi.createSite(siteBody)); } private sanitize(input: string) { - return input.replace(/[\s]/g, '-').replace(/[^A-Za-z0-9-]/g, ''); + return input.replace(/[\s\s]+/g, '-').replace(/[^A-Za-z0-9-]/g, ''); + } + + private canGenerateId(title) { + return Boolean(title.replace(/[^A-Za-z0-9-]/g, '').length); } private handleError(error: any): any { @@ -159,4 +189,74 @@ export class LibraryDialogComponent implements OnInit { return error; } + + private async checkLibraryNameExists(libraryTitle: string) { + const { entries } = (await this.findLibraryByTitle(libraryTitle)).list; + + if (entries.length) { + this.libraryTitleExists = entries[0].entry.title === libraryTitle; + } else { + this.libraryTitleExists = false; + } + } + + private findLibraryByTitle(libraryTitle: string): Promise { + return this.alfrescoApiService + .getInstance() + .core.queriesApi.findSites(libraryTitle, { + maxItems: 1, + fields: ['title'] + }) + .catch(() => ({ list: { entries: [] } })); + } + + private forbidSpecialCharacters({ value }: FormControl) { + if (value === null || value.length === 0) { + return null; + } + + const validCharacters: RegExp = /[^A-Za-z0-9-]/; + const isValid: boolean = !validCharacters.test(value); + + return isValid + ? null + : { + message: 'LIBRARY.ERRORS.ILLEGAL_CHARACTERS' + }; + } + + private forbidOnlySpaces({ value }: FormControl) { + if (value === null || value.length === 0) { + return null; + } + + const isValid: boolean = !!(value || '').trim(); + + return isValid + ? null + : { + message: 'LIBRARY.ERRORS.ONLY_SPACES' + }; + } + + private createSiteIdValidator() { + let timer; + + return (control: AbstractControl) => { + if (timer) { + clearTimeout(timer); + } + + return new Promise(resolve => { + timer = setTimeout(() => { + return from( + this.alfrescoApiService.sitesApi.getSite(control.value) + ).subscribe( + () => resolve({ message: 'LIBRARY.ERRORS.EXISTENT_SITE' }), + () => resolve(null) + ); + }, 300); + }); + }; + } } diff --git a/src/app/directives/directives.module.ts b/src/app/directives/directives.module.ts index a3e1cb3807..f293dfdcc9 100644 --- a/src/app/directives/directives.module.ts +++ b/src/app/directives/directives.module.ts @@ -27,13 +27,21 @@ import { NgModule } from '@angular/core'; import { ExperimentalDirective } from './experimental.directive'; import { DocumentListDirective } from './document-list.directive'; import { PaginationDirective } from './pagination.directive'; +import { LibraryMembershipDirective } from './library-membership.directive'; +import { LibraryFavoriteDirective } from './library-favorite.directive'; -@NgModule({ - declarations: [ +export function directives() { + return [ ExperimentalDirective, DocumentListDirective, - PaginationDirective - ], - exports: [ExperimentalDirective, DocumentListDirective, PaginationDirective] + PaginationDirective, + LibraryMembershipDirective, + LibraryFavoriteDirective + ]; +} + +@NgModule({ + declarations: directives(), + exports: directives() }) export class DirectivesModule {} diff --git a/src/app/directives/document-list.directive.ts b/src/app/directives/document-list.directive.ts index cc06aa79f9..d096d29c28 100644 --- a/src/app/directives/document-list.directive.ts +++ b/src/app/directives/document-list.directive.ts @@ -25,7 +25,7 @@ import { Directive, OnDestroy, OnInit, HostListener } from '@angular/core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { UserPreferencesService } from '@alfresco/adf-core'; import { Subscription } from 'rxjs'; import { Store } from '@ngrx/store'; @@ -48,13 +48,17 @@ export class DocumentListDirective implements OnInit, OnDestroy { private store: Store, private documentList: DocumentListComponent, private preferences: UserPreferencesService, - private route: ActivatedRoute + private route: ActivatedRoute, + private router: Router ) {} ngOnInit() { this.documentList.includeFields = ['isFavorite', 'aspectNames']; - this.documentList.allowDropFiles = false; - this.isLibrary = this.documentList.currentFolderId === '-mysites-'; + this.isLibrary = + this.documentList.currentFolderId === '-mysites-' || + // workaround for custom node list + this.router.url.endsWith('/libraries') || + this.router.url.startsWith('/search-libraries'); if (this.sortingPreferenceKey) { const current = this.documentList.sorting; @@ -119,10 +123,12 @@ export class DocumentListDirective implements OnInit, OnDestroy { } private updateSelection() { - const selection = this.documentList.selection.map(entry => { - entry['isLibrary'] = this.isLibrary; - return entry; - }); + const selection = this.documentList.selection + .filter(node => !this.isLockedNode(node.entry)) + .map(node => { + node['isLibrary'] = this.isLibrary; + return node; + }); this.store.dispatch(new SetSelectedNodesAction(selection)); } diff --git a/src/app/directives/library-favorite.directive.spec.ts b/src/app/directives/library-favorite.directive.spec.ts new file mode 100644 index 0000000000..567591a90e --- /dev/null +++ b/src/app/directives/library-favorite.directive.spec.ts @@ -0,0 +1,144 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, ViewChild } from '@angular/core'; +import { LibraryFavoriteDirective } from './library-favorite.directive'; +import { + AlfrescoApiService, + AlfrescoApiServiceMock, + setupTestBed, + CoreModule +} from '@alfresco/adf-core'; +import { TestBed, async } from '@angular/core/testing'; + +@Component({ + selector: 'app-test-component', + template: ` + + ` +}) +class TestComponent { + @ViewChild('favoriteLibrary') + directive: LibraryFavoriteDirective; + + selection = null; +} + +describe('LibraryFavoriteDirective', () => { + let fixture; + let api; + let component; + let selection; + + setupTestBed({ + imports: [CoreModule], + declarations: [TestComponent, LibraryFavoriteDirective], + providers: [ + { + provide: AlfrescoApiService, + useClass: AlfrescoApiServiceMock + } + ] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + api = TestBed.get(AlfrescoApiService); + selection = { entry: { guid: 'guid', id: 'id' } }; + }); + + it('should not check for favorite if no selection exists', () => { + spyOn(api.peopleApi, 'getFavoriteSite'); + fixture.detectChanges(); + + expect(api.peopleApi.getFavoriteSite).not.toHaveBeenCalled(); + }); + + it('should mark selection as favorite when getFavoriteSite returns successfully', async(() => { + spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve()); + component.selection = selection; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(api.peopleApi.getFavoriteSite).toHaveBeenCalled(); + expect(component.directive.isFavorite()).toBe(true); + }); + })); + + it('should mark selection not favorite when getFavoriteSite errors', async(() => { + spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.reject()); + component.selection = selection; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(api.peopleApi.getFavoriteSite).toHaveBeenCalled(); + expect(component.directive.isFavorite()).toBe(false); + }); + })); + + it('should call addFavorite() on click event when selection is not a favorite', async(() => { + spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.reject()); + spyOn(api.peopleApi, 'addFavorite').and.returnValue(Promise.resolve()); + component.selection = selection; + fixture.detectChanges(); + + expect(component.directive.isFavorite()).toBeFalsy(); + + fixture.whenStable().then(() => { + fixture.nativeElement + .querySelector('button') + .dispatchEvent(new MouseEvent('click')); + + fixture.detectChanges(); + + expect(api.peopleApi.addFavorite).toHaveBeenCalled(); + }); + })); + + it('should call removeFavoriteSite() on click event when selection is not a favorite', async(() => { + spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve()); + spyOn(api.peopleApi, 'removeFavoriteSite').and.returnValue( + Promise.resolve() + ); + component.selection = selection; + fixture.detectChanges(); + + expect(component.directive.isFavorite()).toBeFalsy(); + + fixture.whenStable().then(() => { + fixture.nativeElement + .querySelector('button') + .dispatchEvent(new MouseEvent('click')); + + fixture.detectChanges(); + + expect(api.peopleApi.removeFavoriteSite).toHaveBeenCalled(); + }); + })); +}); diff --git a/src/app/directives/library-favorite.directive.ts b/src/app/directives/library-favorite.directive.ts new file mode 100644 index 0000000000..8d8026d497 --- /dev/null +++ b/src/app/directives/library-favorite.directive.ts @@ -0,0 +1,130 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Directive, + HostListener, + Input, + OnChanges, + Output, + EventEmitter +} from '@angular/core'; +import { SiteBody, FavoriteBody, FavoriteEntry, Site } from 'alfresco-js-api'; +import { AlfrescoApiService } from '@alfresco/adf-core'; + +interface LibraryEntity { + entry: Site; + isLibrary: boolean; + isFavorite: boolean; +} + +@Directive({ + selector: '[acaFavoriteLibrary]', + exportAs: 'favoriteLibrary' +}) +export class LibraryFavoriteDirective implements OnChanges { + @Input('acaFavoriteLibrary') + library: any = null; + + @Output() toggle: EventEmitter = new EventEmitter(); + @Output() error: EventEmitter = new EventEmitter(); + + private targetLibrary = null; + + @HostListener('click') + onClick() { + this.toggleFavorite(); + } + + constructor(private alfrescoApiService: AlfrescoApiService) {} + + ngOnChanges(changes) { + if (!changes.library.currentValue) { + this.targetLibrary = null; + return; + } + + this.targetLibrary = changes.library.currentValue; + this.markFavoriteLibrary(changes.library.currentValue); + } + + isFavorite() { + return this.targetLibrary && this.targetLibrary.isFavorite; + } + + toggleFavorite() { + if (this.targetLibrary.isFavorite) { + this.removeFavorite(this.targetLibrary.entry.guid); + } else { + const favoriteBody = this.createFavoriteBody(this.targetLibrary); + this.addFavorite(favoriteBody); + } + } + + private async markFavoriteLibrary(library: LibraryEntity) { + if (this.targetLibrary.isFavorite === undefined) { + await this.getFavoriteSite(library); + } else { + this.targetLibrary = library; + } + } + + private getFavoriteSite(library: LibraryEntity) { + this.alfrescoApiService.peopleApi + .getFavoriteSite('-me-', library.entry.id) + .then(() => (this.targetLibrary.isFavorite = true)) + .catch(() => (this.targetLibrary.isFavorite = false)); + } + + private createFavoriteBody(library: LibraryEntity): FavoriteBody { + return { + target: { + site: { + guid: library.entry.guid + } + } + }; + } + + private addFavorite(favoriteBody: FavoriteBody) { + this.alfrescoApiService.peopleApi + .addFavorite('-me-', favoriteBody) + .then((libraryEntry: FavoriteEntry) => { + this.targetLibrary.isFavorite = true; + this.toggle.emit(libraryEntry); + }) + .catch(error => this.error.emit(error)); + } + + private removeFavorite(favoriteId: string) { + this.alfrescoApiService.peopleApi + .removeFavoriteSite('-me-', favoriteId) + .then((libraryBody: SiteBody) => { + this.targetLibrary.isFavorite = false; + this.toggle.emit(libraryBody); + }) + .catch(error => this.error.emit(error)); + } +} diff --git a/src/app/directives/library-membership.directive.spec.ts b/src/app/directives/library-membership.directive.spec.ts new file mode 100644 index 0000000000..11e58ce188 --- /dev/null +++ b/src/app/directives/library-membership.directive.spec.ts @@ -0,0 +1,213 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { + AlfrescoApiService, + AlfrescoApiServiceMock, + AppConfigService, + CoreModule, + StorageService +} from '@alfresco/adf-core'; +import { AppTestingModule } from '../testing/app-testing.module'; +import { DirectivesModule } from './directives.module'; +import { LibraryMembershipDirective } from './library-membership.directive'; +import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core'; +import { throwError } from 'rxjs'; + +describe('LibraryMembershipDirective', () => { + let alfrescoApiService: AlfrescoApiService; + let directive: LibraryMembershipDirective; + let peopleApi; + let addMembershipSpy; + let getMembershipSpy; + let deleteMembershipSpy; + + const testSiteEntry = { + id: 'id-1', + guid: 'site-1', + title: 'aa t m', + visibility: 'MODERATED' + }; + const requestedMembershipResponse = { + id: testSiteEntry.id, + createdAt: '2018-11-14', + site: testSiteEntry + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule, DirectivesModule, CoreModule.forRoot()], + schemas: [NO_ERRORS_SCHEMA] + }); + alfrescoApiService = new AlfrescoApiServiceMock( + new AppConfigService(null), + new StorageService() + ); + peopleApi = alfrescoApiService.getInstance().core.peopleApi; + directive = new LibraryMembershipDirective(alfrescoApiService); + }); + + describe('markMembershipRequest', () => { + beforeEach(() => { + getMembershipSpy = spyOn( + peopleApi, + 'getSiteMembershipRequest' + ).and.returnValue( + Promise.resolve({ entry: requestedMembershipResponse }) + ); + }); + + it('should not check membership requests if no entry is selected', fakeAsync(() => { + const selection = {}; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + expect(getMembershipSpy).not.toHaveBeenCalled(); + })); + + it('should check if a membership request exists for the selected library', fakeAsync(() => { + const selection = { entry: {} }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + expect(getMembershipSpy.calls.count()).toBe(1); + })); + + it('should remember when a membership request exists for selected library', fakeAsync(() => { + const selection = { entry: testSiteEntry }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + expect(directive.targetSite.joinRequested).toBe(true); + })); + + it('should remember when a membership request is not found for selected library', fakeAsync(() => { + getMembershipSpy.and.returnValue(Promise.reject()); + + const selection = { entry: testSiteEntry }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + expect(directive.targetSite.joinRequested).toBe(false); + })); + }); + + describe('toggleMembershipRequest', () => { + beforeEach(() => { + getMembershipSpy = spyOn( + peopleApi, + 'getSiteMembershipRequest' + ).and.returnValue( + Promise.resolve({ entry: requestedMembershipResponse }) + ); + addMembershipSpy = spyOn( + peopleApi, + 'addSiteMembershipRequest' + ).and.returnValue( + Promise.resolve({ entry: requestedMembershipResponse }) + ); + deleteMembershipSpy = spyOn( + peopleApi, + 'removeSiteMembershipRequest' + ).and.returnValue(Promise.resolve({})); + }); + + it('should do nothing if there is no selected library ', fakeAsync(() => { + const selection = {}; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + directive.toggleMembershipRequest(); + tick(); + expect(addMembershipSpy).not.toHaveBeenCalled(); + expect(deleteMembershipSpy).not.toHaveBeenCalled(); + })); + + it('should delete membership request if there is one', fakeAsync(() => { + const selection = { entry: testSiteEntry }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + directive.toggleMembershipRequest(); + tick(); + expect(deleteMembershipSpy).toHaveBeenCalled(); + })); + + it('should call API to make a membership request if there is none', fakeAsync(() => { + const selection = { entry: { id: 'no-membership-requested' } }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + directive.toggleMembershipRequest(); + tick(); + expect(addMembershipSpy).toHaveBeenCalled(); + expect(deleteMembershipSpy).not.toHaveBeenCalled(); + })); + + it('should emit error when the request to join a library fails', fakeAsync(() => { + spyOn(directive.error, 'emit'); + addMembershipSpy.and.returnValue(throwError('err')); + + const selection = { entry: { id: 'no-membership-requested' } }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + directive.toggleMembershipRequest(); + tick(); + expect(directive.error.emit).toHaveBeenCalled(); + })); + + it('should emit specific error message on invalid email address server error', fakeAsync(() => { + const emitErrorSpy = spyOn(directive.error, 'emit'); + const selection = { entry: { id: 'no-membership-requested' } }; + const change = new SimpleChange(null, selection, true); + directive.ngOnChanges({ selection: change }); + tick(); + + const testData = [ + { + fixture: 'Failed to resolve sender mail address', + expected: 'APP.MESSAGES.ERRORS.INVALID_SENDER_EMAIL' + }, + { + fixture: 'All recipients for the mail action were invalid', + expected: 'APP.MESSAGES.ERRORS.INVALID_RECEIVER_EMAIL' + } + ]; + + testData.forEach(data => { + addMembershipSpy.and.returnValue(throwError({ message: data.fixture })); + emitErrorSpy.calls.reset(); + directive.toggleMembershipRequest(); + tick(); + expect(emitErrorSpy).toHaveBeenCalledWith({ + error: { message: data.fixture }, + i18nKey: data.expected + }); + }); + })); + }); +}); diff --git a/src/app/directives/library-membership.directive.ts b/src/app/directives/library-membership.directive.ts new file mode 100644 index 0000000000..8eb3e2b5ff --- /dev/null +++ b/src/app/directives/library-membership.directive.ts @@ -0,0 +1,202 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { + Directive, + EventEmitter, + HostListener, + Input, + OnChanges, + Output +} from '@angular/core'; +import { SiteEntry, SiteMembershipRequestBody } from 'alfresco-js-api'; +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { BehaviorSubject, from } from 'rxjs'; + +@Directive({ + selector: '[acaLibraryMembership]', + exportAs: 'libraryMembership' +}) +export class LibraryMembershipDirective implements OnChanges { + targetSite: any = null; + + isJoinRequested: BehaviorSubject = new BehaviorSubject( + false + ); + + /** Site for which to toggle the membership request. */ + @Input('acaLibraryMembership') + selection: SiteEntry = null; + + @Output() toggle: EventEmitter = new EventEmitter(); + @Output() error: EventEmitter = new EventEmitter(); + + @HostListener('click') + onClick() { + this.toggleMembershipRequest(); + } + + constructor(private alfrescoApiService: AlfrescoApiService) {} + + ngOnChanges(changes) { + if ( + !changes.selection.currentValue || + !changes.selection.currentValue.entry + ) { + this.targetSite = null; + + return; + } + this.targetSite = changes.selection.currentValue.entry; + this.markMembershipRequest(); + } + + toggleMembershipRequest() { + if (!this.targetSite) { + return; + } + + if (this.targetSite.joinRequested) { + this.cancelJoinRequest().subscribe( + () => { + this.targetSite.joinRequested = false; + this.isJoinRequested.next(false); + const info = { + updatedEntry: this.targetSite, + shouldReload: false, + i18nKey: 'APP.MESSAGES.INFO.JOIN_CANCELED' + }; + this.toggle.emit(info); + }, + error => { + const errWithMessage = { + error, + i18nKey: 'APP.MESSAGES.ERRORS.JOIN_CANCEL_FAILED' + }; + this.error.emit(errWithMessage); + } + ); + } + + if (!this.targetSite.joinRequested) { + this.joinLibraryRequest().subscribe( + createdMembership => { + this.targetSite.joinRequested = true; + this.isJoinRequested.next(true); + + if ( + createdMembership.entry && + createdMembership.entry.site && + createdMembership.entry.site.role + ) { + const info = { + shouldReload: true, + i18nKey: 'APP.MESSAGES.INFO.JOINED' + }; + this.toggle.emit(info); + } else { + const info = { + updatedEntry: this.targetSite, + shouldReload: false, + i18nKey: 'APP.MESSAGES.INFO.JOIN_REQUESTED' + }; + this.toggle.emit(info); + } + }, + error => { + const errWithMessage = { + error, + i18nKey: 'APP.MESSAGES.ERRORS.JOIN_REQUEST_FAILED' + }; + + const senderEmailCheck = 'Failed to resolve sender mail address'; + const receiverEmailCheck = + 'All recipients for the mail action were invalid'; + + if (error.message) { + if (error.message.includes(senderEmailCheck)) { + errWithMessage.i18nKey = + 'APP.MESSAGES.ERRORS.INVALID_SENDER_EMAIL'; + } else if (error.message.includes(receiverEmailCheck)) { + errWithMessage.i18nKey = + 'APP.MESSAGES.ERRORS.INVALID_RECEIVER_EMAIL'; + } + } + + this.error.emit(errWithMessage); + } + ); + } + } + + markMembershipRequest() { + if (!this.targetSite) { + return; + } + + this.getMembershipRequest().subscribe( + data => { + if (data.entry.id === this.targetSite.id) { + this.targetSite.joinRequested = true; + this.isJoinRequested.next(true); + } + }, + () => { + this.targetSite.joinRequested = false; + this.isJoinRequested.next(false); + } + ); + } + + private joinLibraryRequest() { + const memberBody = { + id: this.targetSite.id + }; + return from( + this.alfrescoApiService.peopleApi.addSiteMembershipRequest( + '-me-', + memberBody + ) + ); + } + + private cancelJoinRequest() { + return from( + this.alfrescoApiService.peopleApi.removeSiteMembershipRequest( + '-me-', + this.targetSite.id + ) + ); + } + + private getMembershipRequest() { + return from( + this.alfrescoApiService.peopleApi.getSiteMembershipRequest( + '-me-', + this.targetSite.id + ) + ); + } +} diff --git a/src/app/extensions.module.ts b/src/app/extensions.module.ts index 7480846a50..03cebd0282 100644 --- a/src/app/extensions.module.ts +++ b/src/app/extensions.module.ts @@ -1,10 +1,9 @@ import { NgModule } from '@angular/core'; -import { AcaDevToolsModule } from '@denysvuika/aca-dev-tools'; // Main entry point for external extensions only. // For any application-specific code use CoreExtensionsModule instead. @NgModule({ - imports: [AcaDevToolsModule] + imports: [] }) export class AppExtensionsModule {} diff --git a/src/app/extensions/app.interface.ts b/src/app/extensions/app.interface.ts index 5d60f4773f..8570fb9851 100644 --- a/src/app/extensions/app.interface.ts +++ b/src/app/extensions/app.interface.ts @@ -23,8 +23,7 @@ * along with Alfresco. If not, see . */ -import { RuleContext } from '@alfresco/adf-extensions'; -import { RepositoryState } from '../store/states'; +import { RuleContext, RepositoryState } from '@alfresco/adf-extensions'; export interface AppRuleContext extends RuleContext { repository: RepositoryState; diff --git a/src/app/extensions/core.extensions.module.ts b/src/app/extensions/core.extensions.module.ts index 461f1ceb2e..3322873e38 100644 --- a/src/app/extensions/core.extensions.module.ts +++ b/src/app/extensions/core.extensions.module.ts @@ -26,24 +26,30 @@ import { CoreModule } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; -import { LayoutComponent } from '../components/layout/layout.component'; +import { AppLayoutComponent } from '../components/layout/app-layout/app-layout.component'; import * as repository from './evaluators/repository.evaluators'; import * as app from './evaluators/app.evaluators'; import * as nav from './evaluators/navigation.evaluators'; import { AppExtensionService } from './extension.service'; import { ToggleInfoDrawerComponent } from '../components/toolbar/toggle-info-drawer/toggle-info-drawer.component'; import { ToggleFavoriteComponent } from '../components/toolbar/toggle-favorite/toggle-favorite.component'; +import { ToggleFavoriteLibraryComponent } from '../components/toolbar/toggle-favorite-library/toggle-favorite-library.component'; import { ToggleSharedComponent } from '../components/shared/toggle-shared/toggle-shared.component'; import { MetadataTabComponent } from '../components/info-drawer/metadata-tab/metadata-tab.component'; +import { LibraryMetadataTabComponent } from '../components/info-drawer/library-metadata-tab/library-metadata-tab.component'; import { CommentsTabComponent } from '../components/info-drawer/comments-tab/comments-tab.component'; import { VersionsTabComponent } from '../components/info-drawer/versions-tab/versions-tab.component'; import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions'; import { AppAuthGuard } from '../guards/auth.guard'; import { NameColumnComponent } from '../components/common/name-column/name-column.component'; import { LibraryNameColumnComponent } from '../components/common/library-name-column/library-name-column.component'; +import { LibraryRoleColumnComponent } from '../components/common/library-role-column/library-role-column.component'; import { LibraryStatusColumnComponent } from '../components/common/library-status-column/library-status-column.component'; import { TrashcanNameColumnComponent } from '../components/common/trashcan-name-column/trashcan-name-column.component'; import { LocationLinkComponent } from '../components/common/location-link/location-link.component'; +import { DocumentDisplayModeComponent } from '../components/toolbar/document-display-mode/document-display-mode.component'; +import { ToggleJoinLibraryButtonComponent } from '../components/toolbar/toggle-join-library/toggle-join-library-button.component'; +import { ToggleJoinLibraryMenuComponent } from '../components/toolbar/toggle-join-library/toggle-join-library-menu.component'; export function setupExtensions(service: AppExtensionService): Function { return () => service.load(); @@ -75,15 +81,21 @@ export class CoreExtensionsModule { constructor(extensions: ExtensionService) { extensions.setComponents({ - 'app.layout.main': LayoutComponent, + 'app.layout.main': AppLayoutComponent, 'app.components.tabs.metadata': MetadataTabComponent, + 'app.components.tabs.library.metadata': LibraryMetadataTabComponent, 'app.components.tabs.comments': CommentsTabComponent, 'app.components.tabs.versions': VersionsTabComponent, 'app.toolbar.toggleInfoDrawer': ToggleInfoDrawerComponent, 'app.toolbar.toggleFavorite': ToggleFavoriteComponent, + 'app.toolbar.toggleFavoriteLibrary': ToggleFavoriteLibraryComponent, + 'app.toolbar.toggleJoinLibrary': ToggleJoinLibraryButtonComponent, + 'app.toolbar.cardView': DocumentDisplayModeComponent, + 'app.menu.toggleJoinLibrary': ToggleJoinLibraryMenuComponent, 'app.shared-link.toggleSharedLink': ToggleSharedComponent, 'app.columns.name': NameColumnComponent, 'app.columns.libraryName': LibraryNameColumnComponent, + 'app.columns.libraryRole': LibraryRoleColumnComponent, 'app.columns.libraryStatus': LibraryStatusColumnComponent, 'app.columns.trashcanName': TrashcanNameColumnComponent, 'app.columns.location': LocationLinkComponent @@ -103,7 +115,12 @@ export class CoreExtensionsModule { 'app.selection.first.canUpdate': app.canUpdateSelectedNode, 'app.selection.file': app.hasFileSelected, 'app.selection.file.canShare': app.canShareFile, + 'app.selection.file.isShared': app.isShared, + 'app.selection.file.isLocked': app.hasLockedFiles, 'app.selection.library': app.hasLibrarySelected, + 'app.selection.isPrivateLibrary': app.isPrivateLibrary, + 'app.selection.hasLibraryRole': app.hasLibraryRole, + 'app.selection.hasNoLibraryRole': app.hasNoLibraryRole, 'app.selection.folder': app.hasFolderSelected, 'app.selection.folder.canUpdate': app.canUpdateSelectedFolder, diff --git a/src/app/extensions/evaluators/app.evaluators.ts b/src/app/extensions/evaluators/app.evaluators.ts index 8ae860f2cf..c677fcf751 100644 --- a/src/app/extensions/evaluators/app.evaluators.ts +++ b/src/app/extensions/evaluators/app.evaluators.ts @@ -75,6 +75,28 @@ export function canShareFile( return false; } +export function isShared( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + if (isSharedFiles(context, ...args) && !context.selection.isEmpty) { + return true; + } + + if ( + (isNotTrashcan(context, ...args), + !context.selection.isEmpty && context.selection.file) + ) { + return !!( + context.selection.file.entry && + context.selection.file.entry.properties && + context.selection.file.entry.properties['qshare:sharedId'] + ); + } + + return false; +} + export function canDeleteSelection( context: RuleContext, ...args: RuleParameter[] @@ -85,6 +107,10 @@ export function canDeleteSelection( isNotSearchResults(context, ...args) && !context.selection.isEmpty ) { + if (hasLockedFiles(context, ...args)) { + return false; + } + // temp workaround for Search api if (isFavorites(context, ...args)) { return true; @@ -178,6 +204,35 @@ export function hasLibrarySelected( return library ? true : false; } +export function isPrivateLibrary( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + const library = context.selection.library; + return library + ? !!( + library.entry && + library.entry.visibility && + library.entry.visibility === 'PRIVATE' + ) + : false; +} + +export function hasLibraryRole( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + const library = context.selection.library; + return library ? !!(library.entry && library.entry.role) : false; +} + +export function hasNoLibraryRole( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + return !hasLibraryRole(context, ...args); +} + export function hasFileSelected( context: RuleContext, ...args: RuleParameter[] @@ -193,6 +248,10 @@ export function canUpdateSelectedNode( if (context.selection && !context.selection.isEmpty) { const node = context.selection.first; + if (node.entry.isFile && hasLockedFiles(context, ...args)) { + return false; + } + if (node.entry.hasOwnProperty('allowableOperationsOnTarget')) { return context.permissions.check(node, ['update'], { target: 'allowableOperationsOnTarget' @@ -218,3 +277,20 @@ export function canUpdateSelectedFolder( } return false; } + +export function hasLockedFiles( + context: RuleContext, + ...args: RuleParameter[] +): boolean { + return context.selection.nodes.some(node => { + if (!node.entry.isFile) { + return false; + } + + return ( + node.entry.isLocked || + (node.entry.properties && + node.entry.properties['cm:lockType'] === 'READ_ONLY_LOCK') + ); + }); +} diff --git a/src/app/extensions/evaluators/navigation.evaluators.ts b/src/app/extensions/evaluators/navigation.evaluators.ts index 8775f300d4..8955864f11 100644 --- a/src/app/extensions/evaluators/navigation.evaluators.ts +++ b/src/app/extensions/evaluators/navigation.evaluators.ts @@ -75,7 +75,9 @@ export function isLibraries( ...args: RuleParameter[] ): boolean { const { url } = context.navigation; - return url && url.endsWith('/libraries'); + return ( + url && (url.endsWith('/libraries') || url.startsWith('/search-libraries')) + ); } export function isNotLibraries( diff --git a/src/app/extensions/extension.service.spec.ts b/src/app/extensions/extension.service.spec.ts index 4734aaa53d..505276de20 100644 --- a/src/app/extensions/extension.service.spec.ts +++ b/src/app/extensions/extension.service.spec.ts @@ -110,8 +110,12 @@ describe('AppExtensionService', () => { describe('actions', () => { beforeEach(() => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', actions: [ { id: 'aca:actions/create-folder', @@ -242,8 +246,12 @@ describe('AppExtensionService', () => { beforeEach(() => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', routes: [ { id: 'aca:routes/about', @@ -301,8 +309,12 @@ describe('AppExtensionService', () => { describe('content actions', () => { it('should load content actions from the config', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { toolbar: [ { @@ -326,8 +338,12 @@ describe('AppExtensionService', () => { it('should sort content actions by order', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { toolbar: [ { @@ -355,8 +371,12 @@ describe('AppExtensionService', () => { describe('open with', () => { it('should load [open with] actions for the viewer', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { viewer: { openWith: [ @@ -381,8 +401,12 @@ describe('AppExtensionService', () => { it('should load only enabled [open with] actions for the viewer', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { viewer: { openWith: [ @@ -418,8 +442,12 @@ describe('AppExtensionService', () => { it('should sort [open with] actions by order', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { viewer: { openWith: [ @@ -457,8 +485,12 @@ describe('AppExtensionService', () => { describe('create', () => { it('should load [create] actions from config', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { create: [ { @@ -477,8 +509,12 @@ describe('AppExtensionService', () => { it('should sort [create] actions by order', () => { applyConfig({ + $id: 'test', $name: 'test', $version: '1.0.0', + $license: 'MIT', + $vendor: 'Good company', + $runtime: '1.5.0', features: { create: [ { @@ -618,4 +654,25 @@ describe('AppExtensionService', () => { expect(result[0].id).toBe('1'); expect(result[1].id).toBe('3'); }); + + describe('getApplicationNavigation', () => { + it('should create navigation data', () => { + const navigation = service.getApplicationNavigation([ + { items: [{ route: 'route1' }, { route: 'route2' }] }, + { items: [{ children: [{ route: 'route3' }] }] } + ]); + + expect(navigation).toEqual([ + { + items: [ + { route: 'route1', url: '/route1' }, + { route: 'route2', url: '/route2' } + ] + }, + { + items: [{ children: [{ route: 'route3', url: '/route3' }] }] + } + ]); + }); + }); }); diff --git a/src/app/extensions/extension.service.ts b/src/app/extensions/extension.service.ts index a2224fa6be..18952f002a 100644 --- a/src/app/extensions/extension.service.ts +++ b/src/app/extensions/extension.service.ts @@ -26,7 +26,9 @@ import { Injectable, Type } from '@angular/core'; import { Store } from '@ngrx/store'; import { Route } from '@angular/router'; -import { AppStore, RepositoryState } from '../store/states'; +import { MatIconRegistry } from '@angular/material'; +import { DomSanitizer } from '@angular/platform-browser'; +import { AppStore } from '../store/states'; import { ruleContext } from '../store/selectors/app.selectors'; import { NodePermissionService } from '../services/node-permission.service'; import { @@ -46,15 +48,21 @@ import { reduceEmptyMenus, ExtensionService, ProfileState, - mergeObjects + mergeObjects, + RepositoryState, + ExtensionRef } from '@alfresco/adf-extensions'; import { AppConfigService } from '@alfresco/adf-core'; import { DocumentListPresetRef } from './document-list.extensions'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { IconRef } from './icon.extensions'; @Injectable({ providedIn: 'root' }) export class AppExtensionService implements RuleContext { + private _references = new BehaviorSubject([]); + defaults = { layout: 'app.layout.main', auth: ['app.auth'] @@ -75,17 +83,21 @@ export class AppExtensionService implements RuleContext { documentListPresets: { files: Array; libraries: Array; + favoriteLibraries: Array; shared: Array; recent: Array; favorites: Array; trashcan: Array; + searchLibraries: Array; } = { files: [], libraries: [], + favoriteLibraries: [], shared: [], recent: [], favorites: [], - trashcan: [] + trashcan: [], + searchLibraries: [] }; selection: SelectionState; @@ -93,13 +105,19 @@ export class AppExtensionService implements RuleContext { profile: ProfileState; repository: RepositoryState; + references$: Observable; + constructor( - private store: Store, - private loader: ExtensionLoaderService, - private extensions: ExtensionService, + protected store: Store, + protected loader: ExtensionLoaderService, + protected extensions: ExtensionService, public permissions: NodePermissionService, - private appConfig: AppConfigService + protected appConfig: AppConfigService, + protected matIconRegistry: MatIconRegistry, + protected sanitizer: DomSanitizer ) { + this.references$ = this._references.asObservable(); + this.store.select(ruleContext).subscribe(result => { this.selection = result.selection; this.navigation = result.navigation; @@ -160,11 +178,46 @@ export class AppExtensionService implements RuleContext { this.documentListPresets = { files: this.getDocumentListPreset(config, 'files'), libraries: this.getDocumentListPreset(config, 'libraries'), + favoriteLibraries: this.getDocumentListPreset( + config, + 'favoriteLibraries' + ), shared: this.getDocumentListPreset(config, 'shared'), recent: this.getDocumentListPreset(config, 'recent'), favorites: this.getDocumentListPreset(config, 'favorites'), - trashcan: this.getDocumentListPreset(config, 'trashcan') + trashcan: this.getDocumentListPreset(config, 'trashcan'), + searchLibraries: this.getDocumentListPreset(config, 'search-libraries') }; + + this.registerIcons(config); + + const references = (config.$references || []) + .filter(entry => typeof entry === 'object') + .map(entry => entry); + this._references.next(references); + } + + protected registerIcons(config: ExtensionConfig) { + const icons: Array = this.loader + .getElements(config, 'features.icons') + .filter(entry => !entry.disabled); + + for (const icon of icons) { + const [ns, id] = icon.id.split(':'); + const value = icon.value; + + if (!value) { + console.warn(`Missing icon value for "${icon.id}".`); + } else if (!ns || !id) { + console.warn(`Incorrect icon id format: "${icon.id}".`); + } else { + this.matIconRegistry.addSvgIconInNamespace( + ns, + id, + this.sanitizer.bypassSecurityTrustResourceUrl(value) + ); + } + } } protected loadNavBar(config: ExtensionConfig): Array { @@ -185,11 +238,31 @@ export class AppExtensionService implements RuleContext { return { ...group, items: (group.items || []) - .filter(item => { - return this.filterByRules(item); - }) + .filter(item => this.filterByRules(item)) .sort(sortByOrder) .map(item => { + if (item.children && item.children.length > 0) { + item.children = item.children + .filter(child => this.filterByRules(child)) + .sort(sortByOrder) + .map(child => { + const childRouteRef = this.extensions.getRouteById( + child.route + ); + const childUrl = `/${ + childRouteRef ? childRouteRef.path : child.route + }`; + return { + ...child, + url: childUrl + }; + }); + + return { + ...item + }; + } + const routeRef = this.extensions.getRouteById(item.route); const url = `/${routeRef ? routeRef.path : item.route}`; return { @@ -197,6 +270,7 @@ export class AppExtensionService implements RuleContext { url }; }) + .reduce(reduceEmptyMenus, []) }; }); } @@ -243,7 +317,7 @@ export class AppExtensionService implements RuleContext { } getSidebarTabs(): Array { - return this.sidebar; + return this.sidebar.filter(action => this.filterByRules(action)); } getComponentById(id: string): Type<{}> { @@ -316,6 +390,7 @@ export class AppExtensionService implements RuleContext { disabled }; }) + .sort(sortByOrder) .reduce(reduceEmptyMenus, []) .reduce(reduceSeparators, []); } @@ -333,6 +408,7 @@ export class AppExtensionService implements RuleContext { if (copy.children && copy.children.length > 0) { copy.children = copy.children .filter(childAction => this.filterByRules(childAction)) + .sort(sortByOrder) .reduce(reduceSeparators, []); } return copy; @@ -367,7 +443,9 @@ export class AppExtensionService implements RuleContext { const copy = this.copyAction(action); if (copy.children && copy.children.length > 0) { copy.children = copy.children + .filter(entry => !entry.disabled) .filter(childAction => this.filterByRules(childAction)) + .sort(sortByOrder) .reduce(reduceSeparators, []); } return copy; diff --git a/src/app/store/states/repository.state.ts b/src/app/extensions/icon.extensions.ts similarity index 87% rename from src/app/store/states/repository.state.ts rename to src/app/extensions/icon.extensions.ts index 1331f179ff..ba59078e59 100644 --- a/src/app/store/states/repository.state.ts +++ b/src/app/extensions/icon.extensions.ts @@ -23,9 +23,8 @@ * along with Alfresco. If not, see . */ -export interface RepositoryState { - isAuditEnabled?: boolean; - isQuickShareEnabled?: boolean; - isReadOnly?: boolean; - isThumbnailGenerationEnabled?: boolean; +import { ExtensionElement } from '@alfresco/adf-extensions'; + +export interface IconRef extends ExtensionElement { + value: string; } diff --git a/src/app/material.module.ts b/src/app/material.module.ts index ee901ed8d5..b95e06de0a 100644 --- a/src/app/material.module.ts +++ b/src/app/material.module.ts @@ -57,7 +57,7 @@ import { providers: [ { provide: MAT_DIALOG_DEFAULT_OPTIONS, - useValue: { closeOnNavigation: true, hasBackdrop: true } + useValue: { closeOnNavigation: true, hasBackdrop: true, autoFocus: true } } ] }) diff --git a/src/app/services/content-api.service.ts b/src/app/services/content-api.service.ts index 140e10de4b..df3696d1d6 100644 --- a/src/app/services/content-api.service.ts +++ b/src/app/services/content-api.service.ts @@ -42,6 +42,7 @@ import { SiteEntry, FavoriteBody } from 'alfresco-js-api'; +import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' @@ -185,6 +186,30 @@ export class ContentApiService { return from(this.api.favoritesApi.getFavorites(personId, opts)); } + getFavoriteLibraries( + personId: string = '-me-', + opts?: any + ): Observable { + return this.getFavorites(personId, { + ...opts, + where: '(EXISTS(target/site))' + }).pipe( + map((response: FavoritePaging) => { + return { + list: { + entries: response.list.entries.map(({ entry }: any) => { + entry.target.site.createdAt = entry.createdAt; + return { + entry: entry.target.site + }; + }), + pagination: response.list.pagination + } + }; + }) + ); + } + findSharedLinks(opts?: any): Observable { return from(this.api.sharedLinksApi.findSharedLinks(opts)); } @@ -201,6 +226,10 @@ export class ContentApiService { return from(this.api.sitesApi.deleteSite(siteId, opts)); } + leaveSite(siteId?: string): Observable { + return from(this.api.sitesApi.removeSiteMember(siteId, '-me-')); + } + createSite( siteBody: SiteBody, opts?: { @@ -219,6 +248,10 @@ export class ContentApiService { return from(this.api.sitesApi.getSite(siteId, opts)); } + updateLibrary(siteId: string, siteBody: SiteBody): Observable { + return from(this.api.sitesApi.updateSite(siteId, siteBody)); + } + addFavorite(nodes: Array): Observable { const payload: FavoriteBody[] = nodes.map(node => { const { isFolder, nodeId, id } = node.entry; diff --git a/src/app/services/content-management.service.spec.ts b/src/app/services/content-management.service.spec.ts index f33894ed55..f608425cd6 100644 --- a/src/app/services/content-management.service.spec.ts +++ b/src/app/services/content-management.service.spec.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { TestBed, fakeAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick, flush } from '@angular/core/testing'; import { of, throwError } from 'rxjs'; import { MatDialog, MatSnackBar } from '@angular/material'; import { Actions, ofType, EffectsModule } from '@ngrx/effects'; @@ -36,11 +36,14 @@ import { SNACKBAR_WARNING, PurgeDeletedNodesAction, RestoreDeletedNodesAction, + NavigateToParentFolder, NavigateRouteAction, NAVIGATE_ROUTE, DeleteNodesAction, MoveNodesAction, - CopyNodesAction + CopyNodesAction, + ShareNodeAction, + SetSelectedNodesAction } from '../store/actions'; import { map } from 'rxjs/operators'; import { NodeEffects } from '../store/effects/node.effects'; @@ -76,11 +79,6 @@ describe('ContentManagementService', () => { translationService = TestBed.get(TranslationService); dialog = TestBed.get(MatDialog); - spyOn(dialog, 'open').and.returnValue({ - afterClosed() { - return of(true); - } - }); }); describe('Copy node action', () => { @@ -988,6 +986,14 @@ describe('ContentManagementService', () => { }); describe('Permanent Delete', () => { + beforeEach(() => { + spyOn(dialog, 'open').and.returnValue({ + afterClosed() { + return of(true); + } + }); + }); + it('does not purge nodes if no selection', () => { spyOn(contentApi, 'purgeDeletedNode'); @@ -1205,6 +1211,45 @@ describe('ContentManagementService', () => { expect(contentApi.restoreNode).toHaveBeenCalled(); })); + it('should navigate to library folder when node is a library content', fakeAsync(() => { + spyOn(store, 'dispatch').and.callThrough(); + spyOn(contentApi, 'restoreNode').and.returnValue(of({})); + spyOn(contentApi, 'getDeletedNodes').and.returnValue( + of({ + list: { entries: [] } + }) + ); + + const path = { + elements: [ + { + id: '1-1', + name: 'Company Home' + }, + { + id: '1-2', + name: 'Sites' + } + ] + }; + + const selection = [ + { + entry: { + id: '1', + path + } + } + ]; + + store.dispatch(new RestoreDeletedNodesAction(selection)); + + expect( + store.dispatch['calls'].argsFor(1)[0].userAction.action instanceof + NavigateToParentFolder + ).toBe(true); + })); + describe('refresh()', () => { it('dispatch event on finish', fakeAsync(done => { spyOn(contentApi, 'restoreNode').and.returnValue(of({})); @@ -1444,4 +1489,68 @@ describe('ContentManagementService', () => { })); }); }); + + describe('Share Node', () => { + it('should open dialog for nodes without requesting getNodeInfo', fakeAsync(() => { + const node = { entry: { id: '1', name: 'name1' } }; + spyOn(contentApi, 'getNodeInfo').and.returnValue(of({})); + spyOn(dialog, 'open').and.returnValue({ + afterClosed() { + return of(null); + } + }); + + store.dispatch(new ShareNodeAction(node)); + + expect(contentApi.getNodeInfo).not.toHaveBeenCalled(); + expect(dialog.open).toHaveBeenCalled(); + })); + + it('should open dialog with getNodeInfo data when `id` property is missing', fakeAsync(() => { + const node = { entry: { nodeId: '1', name: 'name1' } }; + spyOn(contentApi, 'getNodeInfo').and.returnValue(of({})); + spyOn(dialog, 'open').and.returnValue({ + afterClosed() { + return of(null); + } + }); + + store.dispatch(new ShareNodeAction(node)); + + expect(contentApi.getNodeInfo).toHaveBeenCalled(); + expect(dialog.open).toHaveBeenCalled(); + })); + + it('should update node selection after dialog is closed', fakeAsync(() => { + const node = { entry: { id: '1', name: 'name1' } }; + spyOn(store, 'dispatch').and.callThrough(); + spyOn(dialog, 'open').and.returnValue({ + afterClosed() { + return of(null); + } + }); + + store.dispatch(new ShareNodeAction(node)); + + expect(store.dispatch['calls'].argsFor(1)[0]).toEqual( + new SetSelectedNodesAction([node]) + ); + })); + + it('should emit event when node is un-shared', fakeAsync(() => { + const node = { entry: { id: '1', name: 'name1' } }; + spyOn(contentManagementService.linksUnshared, 'next').and.callThrough(); + spyOn(dialog, 'open').and.returnValue({ + afterClosed: () => of(node) + }); + + store.dispatch(new ShareNodeAction(node)); + tick(); + flush(); + + expect(contentManagementService.linksUnshared.next).toHaveBeenCalledWith( + jasmine.any(Object) + ); + })); + }); }); diff --git a/src/app/services/content-management.service.ts b/src/app/services/content-management.service.ts index bb589860a3..31c02520c0 100644 --- a/src/app/services/content-management.service.ts +++ b/src/app/services/content-management.service.ts @@ -37,6 +37,7 @@ import { SnackbarAction, SnackbarWarningAction, NavigateRouteAction, + NavigateToParentFolder, SnackbarUserAction, UndoDeleteNodesAction, SetSelectedNodesAction @@ -49,7 +50,8 @@ import { Node, SiteEntry, DeletedNodesPaging, - PathInfoEntity + PathInfoEntity, + SiteBody } from 'alfresco-js-api'; import { NodePermissionService } from './node-permission.service'; import { NodeInfo, DeletedNodeInfo, DeleteStatus } from '../store/models'; @@ -80,10 +82,16 @@ export class ContentManagementService { folderCreated = new Subject(); libraryDeleted = new Subject(); libraryCreated = new Subject(); + libraryUpdated = new Subject(); + libraryJoined = new Subject(); + libraryLeft = new Subject(); + library400Error = new Subject(); + joinLibraryToggle = new Subject(); linksUnshared = new Subject(); favoriteAdded = new Subject>(); favoriteRemoved = new Subject>(); favoriteToggle = new Subject>(); + favoriteLibraryToggle = new Subject(); constructor( private store: Store, @@ -204,6 +212,9 @@ export class ContentManagementService { }) .afterClosed() .subscribe(deletedSharedLink => { + this.store.dispatch( + new SetSelectedNodesAction([deletedSharedLink || node]) + ); if (deletedSharedLink) { this.linksUnshared.next(deletedSharedLink); } @@ -295,6 +306,54 @@ export class ContentManagementService { ); } + leaveLibrary(siteId: string): void { + const dialogRef = this.dialogRef.open(ConfirmDialogComponent, { + data: { + title: 'APP.DIALOGS.CONFIRM_LEAVE.TITLE', + message: 'APP.DIALOGS.CONFIRM_LEAVE.MESSAGE', + yesLabel: 'APP.DIALOGS.CONFIRM_LEAVE.YES_LABEL', + noLabel: 'APP.DIALOGS.CONFIRM_LEAVE.NO_LABEL' + }, + minWidth: '250px' + }); + + dialogRef.afterClosed().subscribe(result => { + if (result === true) { + this.contentApi.leaveSite(siteId).subscribe( + () => { + this.libraryLeft.next(siteId); + this.store.dispatch( + new SnackbarInfoAction('APP.MESSAGES.INFO.LEFT_LIBRARY') + ); + }, + () => { + this.store.dispatch( + new SnackbarErrorAction( + 'APP.MESSAGES.ERRORS.LEAVE_LIBRARY_FAILED' + ) + ); + } + ); + } + }); + } + + updateLibrary(siteId: string, siteBody: SiteBody) { + this.contentApi.updateLibrary(siteId, siteBody).subscribe( + (siteEntry: SiteEntry) => { + this.libraryUpdated.next(siteEntry); + this.store.dispatch( + new SnackbarInfoAction('LIBRARY.SUCCESS.LIBRARY_UPDATED') + ); + }, + () => { + this.store.dispatch( + new SnackbarErrorAction('LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR') + ); + } + ); + } + async unshareNodes(links: Array) { const promises = links.map(link => this.contentApi.deleteSharedLink(link.entry.id).toPromise() @@ -819,9 +878,17 @@ export class ContentManagementService { const isSite = this.isSite(status.success[0].entry); const path: PathInfoEntity = status.success[0].entry.path; const parent = path.elements[path.elements.length - 1]; - const route = isSite ? ['/libraries'] : ['/personal-files', parent.id]; + const route = isSite + ? ['/libraries', parent.id] + : ['/personal-files', parent.id]; + + let navigate; - const navigate = new NavigateRouteAction(route); + if (this.isLibraryContent(path)) { + navigate = new NavigateToParentFolder(status.success[0]); + } else { + navigate = new NavigateRouteAction(route); + } message.userAction = new SnackbarUserAction( 'APP.ACTIONS.VIEW', @@ -837,6 +904,18 @@ export class ContentManagementService { return entry.nodeType === 'st:site'; } + private isLibraryContent(path: PathInfoEntity): boolean { + if ( + path && + path.elements.length >= 2 && + path.elements[1].name === 'Sites' + ) { + return true; + } + + return false; + } + private getRestoreMessage(status: DeleteStatus): SnackbarAction { if (status.someFailed && !status.oneFailed) { return new SnackbarErrorAction( diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts new file mode 100644 index 0000000000..18ad94c56c --- /dev/null +++ b/src/app/services/data.service.ts @@ -0,0 +1,119 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Injectable } from '@angular/core'; +import { CustomResourcesService } from '@alfresco/adf-content-services'; +import { + PaginationModel, + LogService, + AlfrescoApiService +} from '@alfresco/adf-core'; +import { Observable } from 'rxjs'; +import { PersonEntry, SearchRequest, ResultSetPaging } from 'alfresco-js-api'; + +@Injectable({ + providedIn: 'root' +}) +export class AppDataService extends CustomResourcesService { + recentFileFilters = [ + 'TYPE:"content"', + '-PNAME:"0/wiki"', + '-TYPE:"app:filelink"', + '-TYPE:"fm:post"', + '-TYPE:"cm:thumbnail"', + '-TYPE:"cm:failedThumbnail"', + '-TYPE:"cm:rating"', + '-TYPE:"dl:dataList"', + '-TYPE:"dl:todoList"', + '-TYPE:"dl:issue"', + '-TYPE:"dl:contact"', + '-TYPE:"dl:eventAgenda"', + '-TYPE:"dl:event"', + '-TYPE:"dl:task"', + '-TYPE:"dl:simpletask"', + '-TYPE:"dl:meetingAgenda"', + '-TYPE:"dl:location"', + '-TYPE:"fm:topic"', + '-TYPE:"fm:post"', + '-TYPE:"ia:calendarEvent"', + '-TYPE:"lnk:link"' + ]; + + constructor(private api: AlfrescoApiService, logService: LogService) { + super(api, logService); + } + + getRecentFiles( + personId: string, + pagination: PaginationModel + ): Observable { + return new Observable(observer => { + this.api.peopleApi.getPerson(personId).then( + (person: PersonEntry) => { + const username = person.entry.id; + const query: SearchRequest = { + query: { + query: '*', + language: 'afts' + }, + filterQueries: [ + { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, + { query: `cm:modifier:${username} OR cm:creator:${username}` }, + { + query: this.recentFileFilters.join(' AND ') + } + ], + include: ['path', 'properties', 'allowableOperations'], + sort: [ + { + type: 'FIELD', + field: 'cm:modified', + ascending: false + } + ], + paging: { + maxItems: pagination.maxItems, + skipCount: pagination.skipCount + } + }; + return this.api.searchApi.search(query).then( + searchResult => { + observer.next(searchResult); + observer.complete(); + }, + err => { + observer.error(err); + observer.complete(); + } + ); + }, + err => { + observer.error(err); + observer.complete(); + } + ); + }); + } +} diff --git a/src/app/services/node-actions.service.spec.ts b/src/app/services/node-actions.service.spec.ts index ba94b0cc71..5fb1abc6ee 100644 --- a/src/app/services/node-actions.service.spec.ts +++ b/src/app/services/node-actions.service.spec.ts @@ -41,7 +41,8 @@ class TestNode { isFile?: boolean, name?: string, permission?: string[], - nodeType?: string + nodeType?: string, + properties?: any ) { this.entry = {}; this.entry.id = id || 'node-id'; @@ -52,6 +53,9 @@ class TestNode { if (permission) { this.entry['allowableOperations'] = permission; } + if (properties) { + this.entry.properties = properties; + } } } @@ -305,6 +309,22 @@ describe('NodeActionsService', () => { }) ).toBe(true); }); + + it('should filter destination nodes and not show the restricted site content', () => { + const restrictedSiteContent = new TestNode( + 'blog-id', + !isFile, + 'blog', + [], + 'cm:folder', + { 'st:componentId': 'blog' } + ); + expect( + testContentNodeSelectorComponentData.data.rowFilter({ + node: restrictedSiteContent + }) + ).toBe(false); + }); }); describe('copyNodes', () => { diff --git a/src/app/services/node-actions.service.ts b/src/app/services/node-actions.service.ts index 612e0143a8..d0860d2b45 100644 --- a/src/app/services/node-actions.service.ts +++ b/src/app/services/node-actions.service.ts @@ -52,6 +52,14 @@ import { catchError, map, mergeMap } from 'rxjs/operators'; export class NodeActionsService { static SNACK_MESSAGE_DURATION_WITH_UNDO = 10000; static SNACK_MESSAGE_DURATION = 3000; + static restrictedSiteContent = [ + 'blog', + 'calendar', + 'dataLists', + 'discussions', + 'links', + 'wiki' + ]; contentCopied: Subject = new Subject< MinimalNodeEntity[] @@ -289,6 +297,17 @@ export class NodeActionsService { ); } + private isRestrictedSiteContent(entry) { + if (entry && entry.properties && entry.properties['st:componentId']) { + const restrictedItem = NodeActionsService.restrictedSiteContent.find( + restrictedId => entry.properties['st:componentId'] === restrictedId + ); + return !!restrictedItem; + } + + return false; + } + close() { this.dialog.closeAll(); } @@ -636,7 +655,11 @@ export class NodeActionsService { const node: MinimalNodeEntryEntity = row.node.entry; this.isSitesDestinationAvailable = !!node['guid']; - return !node.isFile && node.nodeType !== 'app:folderlink'; + return ( + !node.isFile && + node.nodeType !== 'app:folderlink' && + !this.isRestrictedSiteContent(node) + ); } // todo: review once 1.10-beta6 is out diff --git a/src/app/store/actions.ts b/src/app/store/actions.ts index 54c3448650..7e41eafa99 100644 --- a/src/app/store/actions.ts +++ b/src/app/store/actions.ts @@ -34,3 +34,4 @@ export * from './actions/library.actions'; export * from './actions/upload.actions'; export * from './actions/modals.actions'; export * from './actions/repository.actions'; +export * from './actions/info-drawer.actions'; diff --git a/src/app/dialogs/library/form.validators.spec.ts b/src/app/store/actions/info-drawer.actions.ts similarity index 82% rename from src/app/dialogs/library/form.validators.spec.ts rename to src/app/store/actions/info-drawer.actions.ts index 98c63f4c89..e34acbdb4a 100644 --- a/src/app/dialogs/library/form.validators.spec.ts +++ b/src/app/store/actions/info-drawer.actions.ts @@ -23,10 +23,11 @@ * along with Alfresco. If not, see . */ -import { SiteIdValidator } from './form.validators'; +import { Action } from '@ngrx/store'; -describe('SiteIdValidator', () => { - it('should be defined', () => { - expect(SiteIdValidator).toBeDefined(); - }); -}); +export const SET_INFO_DRAWER_STATE = 'SET_INFO_DRAWER_STATE'; + +export class SetInfoDrawerStateAction implements Action { + readonly type = SET_INFO_DRAWER_STATE; + constructor(public payload: boolean) {} +} diff --git a/src/app/store/actions/library.actions.ts b/src/app/store/actions/library.actions.ts index 7308194128..d44a393587 100644 --- a/src/app/store/actions/library.actions.ts +++ b/src/app/store/actions/library.actions.ts @@ -24,10 +24,13 @@ */ import { Action } from '@ngrx/store'; +import { SiteBody } from 'alfresco-js-api'; export const DELETE_LIBRARY = 'DELETE_LIBRARY'; export const CREATE_LIBRARY = 'CREATE_LIBRARY'; export const NAVIGATE_LIBRARY = 'NAVIGATE_LIBRARY'; +export const UPDATE_LIBRARY = 'UPDATE_LIBRARY'; +export const LEAVE_LIBRARY = 'LEAVE_LIBRARY'; export class DeleteLibraryAction implements Action { readonly type = DELETE_LIBRARY; @@ -43,3 +46,13 @@ export class NavigateLibraryAction implements Action { readonly type = NAVIGATE_LIBRARY; constructor(public payload?: string) {} } + +export class UpdateLibraryAction implements Action { + readonly type = UPDATE_LIBRARY; + constructor(public payload?: SiteBody) {} +} + +export class LeaveLibraryAction implements Action { + readonly type = LEAVE_LIBRARY; + constructor(public payload?: string) {} +} diff --git a/src/app/store/actions/repository.actions.ts b/src/app/store/actions/repository.actions.ts index d228c00e64..cbe368047a 100644 --- a/src/app/store/actions/repository.actions.ts +++ b/src/app/store/actions/repository.actions.ts @@ -24,7 +24,7 @@ */ import { Action } from '@ngrx/store'; -import { RepositoryState } from '../states'; +import { RepositoryState } from '@alfresco/adf-extensions'; export const SET_REPOSITORY_STATUS = 'SET_REPOSITORY_STATUS'; export const GET_REPOSITORY_STATUS = 'GET_REPOSITORY_STATUS'; diff --git a/src/app/store/actions/search.actions.ts b/src/app/store/actions/search.actions.ts index dea2f9e619..ba3ffafaeb 100644 --- a/src/app/store/actions/search.actions.ts +++ b/src/app/store/actions/search.actions.ts @@ -29,5 +29,5 @@ export const SEARCH_BY_TERM = 'SEARCH_BY_TERM'; export class SearchByTermAction implements Action { readonly type = SEARCH_BY_TERM; - constructor(public payload: string) {} + constructor(public payload: string, public searchOptions?: any) {} } diff --git a/src/app/store/effects/library.effects.ts b/src/app/store/effects/library.effects.ts index 4bddb284a8..1e9c66bae7 100644 --- a/src/app/store/effects/library.effects.ts +++ b/src/app/store/effects/library.effects.ts @@ -32,14 +32,20 @@ import { CreateLibraryAction, CREATE_LIBRARY, NavigateLibraryAction, - NAVIGATE_LIBRARY + NAVIGATE_LIBRARY, + UpdateLibraryAction, + UPDATE_LIBRARY, + LeaveLibraryAction, + LEAVE_LIBRARY, + NavigateRouteAction } from '../actions'; import { ContentManagementService } from '../../services/content-management.service'; import { Store } from '@ngrx/store'; import { AppStore } from '../states'; import { appSelection } from '../selectors/app.selectors'; import { ContentApiService } from '../../services/content-api.service'; -import { Router } from '@angular/router'; +import { SiteBody } from 'alfresco-js-api-node'; +import { SnackbarErrorAction } from '../actions/snackbar.actions'; @Injectable() export class LibraryEffects { @@ -47,8 +53,7 @@ export class LibraryEffects { private store: Store, private actions$: Actions, private content: ContentManagementService, - private contentApi: ContentApiService, - private router: Router + private contentApi: ContentApiService ) {} @Effect({ dispatch: false }) @@ -70,6 +75,25 @@ export class LibraryEffects { }) ); + @Effect({ dispatch: false }) + leaveLibrary$ = this.actions$.pipe( + ofType(LEAVE_LIBRARY), + map(action => { + if (action.payload) { + this.content.leaveLibrary(action.payload); + } else { + this.store + .select(appSelection) + .pipe(take(1)) + .subscribe(selection => { + if (selection && selection.library) { + this.content.leaveLibrary(selection.library.entry.id); + } + }); + } + }) + ); + @Effect() createLibrary$ = this.actions$.pipe( ofType(CREATE_LIBRARY), @@ -85,11 +109,42 @@ export class LibraryEffects { if (libraryId) { this.contentApi .getNode(libraryId, { relativePath: '/documentLibrary' }) - .pipe(map(node => node.entry)) - .subscribe(documentLibrary => { - this.router.navigate(['libraries', documentLibrary.id]); - }); + .pipe(map(node => node.entry.id)) + .subscribe( + id => { + this.store.dispatch(new NavigateRouteAction(['libraries', id])); + }, + () => { + this.store.dispatch( + new SnackbarErrorAction('APP.MESSAGES.ERRORS.MISSING_CONTENT') + ); + } + ); } }) ); + + @Effect({ dispatch: false }) + updateLibrary$ = this.actions$.pipe( + ofType(UPDATE_LIBRARY), + map(action => { + this.store + .select(appSelection) + .pipe(take(1)) + .subscribe(selection => { + if (selection && selection.library) { + const { id } = selection.library.entry; + const { title, description, visibility } = action.payload; + + const siteBody = { + title, + description, + visibility + }; + + this.content.updateLibrary(id, siteBody); + } + }); + }) + ); } diff --git a/src/app/store/effects/router.effects.ts b/src/app/store/effects/router.effects.ts index e99a73a565..82136d7bdd 100644 --- a/src/app/store/effects/router.effects.ts +++ b/src/app/store/effects/router.effects.ts @@ -36,12 +36,19 @@ import { NavigateToFolder, NAVIGATE_FOLDER, NavigateUrlAction, - NAVIGATE_URL + NAVIGATE_URL, + SnackbarErrorAction } from '../actions'; +import { AppStore } from '../states/app.state'; +import { Store } from '@ngrx/store'; @Injectable() export class RouterEffects { - constructor(private actions$: Actions, private router: Router) {} + constructor( + private store: Store, + private actions$: Actions, + private router: Router + ) {} @Effect({ dispatch: false }) navigateUrl$ = this.actions$.pipe( @@ -97,11 +104,13 @@ export class RouterEffects { // parent.id could be 'Site' folder or child as 'documentLibrary' link = [area, parent.name === 'Sites' ? {} : id]; } - } - setTimeout(() => { - this.router.navigate(link); - }, 10); + setTimeout(() => { + this.router.navigate(link); + }, 10); + } else { + this.router.navigate(['/personal-files', node.id]); + } } private navigateToParentFolder(node: MinimalNodeEntryEntity) { @@ -120,11 +129,15 @@ export class RouterEffects { // parent.id could be 'Site' folder or child as 'documentLibrary' link = [area, parent.name === 'Sites' ? {} : parent.id]; } - } - setTimeout(() => { - this.router.navigate(link); - }, 10); + setTimeout(() => { + this.router.navigate(link); + }, 10); + } else { + this.store.dispatch( + new SnackbarErrorAction('APP.MESSAGES.ERRORS.CANNOT_NAVIGATE_LOCATION') + ); + } } private isLibraryContent(path: PathInfoEntity): boolean { diff --git a/src/app/store/effects/search.effects.ts b/src/app/store/effects/search.effects.ts index 8d7887cc3c..2f5cc3673d 100644 --- a/src/app/store/effects/search.effects.ts +++ b/src/app/store/effects/search.effects.ts @@ -28,6 +28,7 @@ import { Injectable } from '@angular/core'; import { map } from 'rxjs/operators'; import { SEARCH_BY_TERM, SearchByTermAction } from '../actions/search.actions'; import { Router } from '@angular/router'; +import { SearchOptionIds } from '../../components/search/search-input/search-input.component'; @Injectable() export class SearchEffects { @@ -37,7 +38,19 @@ export class SearchEffects { searchByTerm$ = this.actions$.pipe( ofType(SEARCH_BY_TERM), map(action => { - this.router.navigateByUrl('/search;q=' + action.payload); + const libItem = action.searchOptions.find( + item => item.id === SearchOptionIds.Libraries + ); + const librarySelected = !!libItem && libItem.value; + if (librarySelected) { + this.router.navigateByUrl( + '/search-libraries;q=' + encodeURIComponent(action.payload) + ); + } else { + this.router.navigateByUrl( + '/search;q=' + encodeURIComponent(action.payload) + ); + } }) ); } diff --git a/src/app/store/effects/upload.effects.ts b/src/app/store/effects/upload.effects.ts index aa8c17b450..45a09f6ccb 100644 --- a/src/app/store/effects/upload.effects.ts +++ b/src/app/store/effects/upload.effects.ts @@ -91,7 +91,10 @@ export class UploadEffects { const files = FileUtils.toFileArray(input.files).map(file => { return new FileModel(file, { parentId: node.id, - path: (file.webkitRelativePath || '').replace(/\/[^\/]*$/, ''), + path: ((file).webkitRelativePath || '').replace( + /\/[^\/]*$/, + '' + ), nodeType: 'cm:content' }); }); diff --git a/src/app/store/reducers/app.reducer.ts b/src/app/store/reducers/app.reducer.ts index 2195479269..c1b8d9f86a 100644 --- a/src/app/store/reducers/app.reducer.ts +++ b/src/app/store/reducers/app.reducer.ts @@ -37,7 +37,9 @@ import { SET_CURRENT_FOLDER, SetCurrentFolderAction, SET_CURRENT_URL, - SetCurrentUrlAction + SetCurrentUrlAction, + SET_INFO_DRAWER_STATE, + SetInfoDrawerStateAction } from '../actions'; import { TOGGLE_INFO_DRAWER, @@ -76,6 +78,9 @@ export function appReducer( case TOGGLE_INFO_DRAWER: newState = updateInfoDrawer(state, action); break; + case SET_INFO_DRAWER_STATE: + newState = setInfoDrawer(state, action); + break; case TOGGLE_DOCUMENT_DISPLAY_MODE: newState = updateDocumentDisplayMode(state, ( action @@ -215,6 +220,12 @@ function updateSelectedNodes( return newState; } +function setInfoDrawer(state: AppState, action: SetInfoDrawerStateAction) { + const newState = Object.assign({}, state); + newState.infoDrawerOpened = action.payload; + return newState; +} + function updateRepositoryStatus( state: AppState, action: SetRepositoryStatusAction diff --git a/src/app/store/selectors/app.selectors.ts b/src/app/store/selectors/app.selectors.ts index e2c318af41..91478d5a40 100644 --- a/src/app/store/selectors/app.selectors.ts +++ b/src/app/store/selectors/app.selectors.ts @@ -27,47 +27,77 @@ import { createSelector } from '@ngrx/store'; import { AppStore } from '../states/app.state'; export const selectApp = (state: AppStore) => state.app; + export const selectHeaderColor = createSelector( selectApp, state => state.headerColor ); -export const selectAppName = createSelector(selectApp, state => state.appName); + +export const selectAppName = createSelector( + selectApp, + state => state.appName +); + export const selectLogoPath = createSelector( selectApp, state => state.logoPath ); -export const appSelection = createSelector(selectApp, state => state.selection); + +export const appSelection = createSelector( + selectApp, + state => state.selection +); + export const appLanguagePicker = createSelector( selectApp, state => state.languagePicker ); -export const selectUser = createSelector(selectApp, state => state.user); -export const sharedUrl = createSelector(selectApp, state => state.sharedUrl); + +export const selectUser = createSelector( + selectApp, + state => state.user +); + +export const sharedUrl = createSelector( + selectApp, + state => state.sharedUrl +); + export const appNavigation = createSelector( selectApp, state => state.navigation ); + export const currentFolder = createSelector( selectApp, state => state.navigation.currentFolder ); + export const infoDrawerOpened = createSelector( selectApp, state => state.infoDrawerOpened ); + export const documentDisplayMode = createSelector( selectApp, state => state.documentDisplayMode ); + export const repositoryStatus = createSelector( selectApp, state => state.repository ); + export const isQuickShareEnabled = createSelector( repositoryStatus, status => status.isQuickShareEnabled ); +export const isAdmin = createSelector( + selectApp, + state => state.user.isAdmin +); + export const ruleContext = createSelector( appSelection, appNavigation, diff --git a/src/app/store/states.ts b/src/app/store/states.ts index 393a3b17c8..ccca96164f 100644 --- a/src/app/store/states.ts +++ b/src/app/store/states.ts @@ -24,4 +24,3 @@ */ export * from './states/app.state'; -export * from './states/repository.state'; diff --git a/src/app/store/states/app.state.ts b/src/app/store/states/app.state.ts index 63059f25e4..dd2455ab21 100644 --- a/src/app/store/states/app.state.ts +++ b/src/app/store/states/app.state.ts @@ -26,9 +26,9 @@ import { SelectionState, ProfileState, - NavigationState + NavigationState, + RepositoryState } from '@alfresco/adf-extensions'; -import { RepositoryState } from '../states'; export interface AppState { appName: string; diff --git a/src/app/ui/application.scss b/src/app/ui/application.scss index a60e7ca1c9..2014b63a0f 100644 --- a/src/app/ui/application.scss +++ b/src/app/ui/application.scss @@ -16,9 +16,9 @@ body { } } +// todo: move this to corresponding component theme files app-root, app-about, -app-layout, adf-layout-container, aca-search-results, ng-component { diff --git a/src/app/ui/custom-theme.scss b/src/app/ui/custom-theme.scss index 2c20d22ac1..d8225826cd 100644 --- a/src/app/ui/custom-theme.scss +++ b/src/app/ui/custom-theme.scss @@ -11,6 +11,8 @@ @import '../components/context-menu/context-menu.component.theme'; @import '../dialogs/node-versions/node-versions.dialog.theme'; @import '../components/create-menu/create-menu.component.scss'; +@import '../components/layout/layout.theme.scss'; +@import '../components/shared/content-node-share/content-node-share.dialog.scss'; @import './overrides/adf-toolbar.theme'; @import './overrides/adf-search-filter.theme'; @@ -26,7 +28,6 @@ @import './overrides/adf-layout-header.theme'; @import './overrides/adf-version-manager.theme'; -@import 'layout'; @import 'snackbar'; $grey-scale: ( @@ -83,7 +84,7 @@ $custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent); @include adf-layout-header-theme($theme); @include adf-version-manager-theme($theme); - @include aca-layout-theme($theme); + @include layout-theme($theme); @include aca-search-input-theme($theme); @include aca-generic-error-theme($theme); @include app-permission-manager-theme($theme); @@ -95,4 +96,5 @@ $custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent); @include aca-current-user-theme($theme); @include aca-context-menu-theme($theme); @include app-create-menu-theme($theme); + @include aca-share-dialog-theme($theme); } diff --git a/src/app/ui/layout.scss b/src/app/ui/layout.scss deleted file mode 100644 index 09005ef66b..0000000000 --- a/src/app/ui/layout.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import 'mixins'; - -@mixin aca-layout-theme($theme) { - $foreground: map-get($theme, foreground); - - $app-layout--header-height: 65px; - $app-layout--side-width: 320px; - $app-inner-layout--header-height: 48px; - $app-inner-layout--footer-height: 48px; - $alfresco-divider-color: mat-color($foreground, text, 0.07); - $alfresco-gray-background: #fafafa; - - .layout { - @include flex-column; - } - - .inner-layout { - @include flex-column; - - &__header { - display: flex; - align-items: center; - flex: 0 0 $app-layout--header-height; - flex-basis: $app-inner-layout--header-height; - background: $alfresco-gray-background; - border-bottom: 1px solid $alfresco-divider-color; - padding: 0 24px; - } - - &__content { - @include flex-row; - } - - &__content--hide { - display: none !important; - } - - &__panel { - @include flex-column; - border-right: 1px solid mat-color($foreground, text, 0.07); - } - - &__side-panel { - display: block; - height: 100%; - overflow-y: scroll; - max-width: 350px; - width: 350px; - } - } - - .content--scroll { - overflow: auto !important; - } -} diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index 8181a665d5..5d42310792 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -1,14 +1,13 @@ { "$schema": "../../extension.schema.json", - "$name": "app", + "$id": "app.core", + "$name": "app.core", "$version": "1.0.0", - "$references": [ - "plugin1.json", - "dev.tools.json", - "app.header.json", - "app.create.json", - "metadata-plugin.json" - ], + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", + "$description": "Core application extensions and features", + "$references": [], "rules": [ { @@ -55,13 +54,28 @@ ] }, { - "id": "app.toolbar.canShare", + "id": "app.context.canShare", "type": "core.every", "parameters": [ { "type": "rule", "value": "app.selection.file.canShare" }, { "type": "rule", "value": "repository.isQuickShareEnabled" } ] }, + { + "id": "app.toolbar.canShare", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.file.canShare" }, + { "type": "rule", "value": "repository.isQuickShareEnabled" }, + { + "type": "rule", + "value": "core.not", + "parameters": [ + { "type": "rule", "value": "app.selection.file.isShared" } + ] + } + ] + }, { "id": "app.toolbar.favorite.canAdd", "type": "core.every", @@ -99,6 +113,36 @@ { "type": "rule", "value": "app.navigation.isNotTrashcan" } ] }, + { + "id": "app.libraries.toolbar", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.notEmpty" }, + { "type": "rule", "value": "app.selection.library" } + ] + }, + { + "id": "app.libraries.toolbar.canToggleJoin", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.library" }, + { "type": "rule", + "value": "core.not", + "parameters": [ + { "type": "rule", "value": "app.selection.isPrivateLibrary" } + ] + }, + { "type": "rule", "value": "app.selection.hasNoLibraryRole" } + ] + }, + { + "id": "app.libraries.toolbar.canLeaveLibrary", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.library" }, + { "type": "rule", "value": "app.selection.hasLibraryRole" } + ] + }, { "id": "app.toolbar.canCopyNode", "type": "core.every", @@ -121,7 +165,14 @@ "type": "core.every", "parameters": [ { "type": "rule", "value": "app.selection.file" }, - { "type": "rule", "value": "app.navigation.isNotTrashcan" } + { "type": "rule", "value": "app.navigation.isNotTrashcan" }, + { + "type": "rule", + "value": "core.not", + "parameters": [ + { "type": "rule", "value": "app.selection.file.isLocked" } + ] + } ] }, { @@ -169,6 +220,16 @@ ], "features": { + "icons": [ + { + "id": "adf:join_library", + "value": "./assets/images/join-library.svg" + }, + { + "id": "adf:move_file", + "value": "./assets/images/adf-move-file-24px.svg" + } + ], "create": [ { "id": "app.create.uploadFile", @@ -241,12 +302,27 @@ "route": "personal-files" }, { - "id": "app.navbar.libraries", + "id": "app.navbar.libraries.menu", "order": 200, - "icon": "group_work", + "icon": "library_books", "title": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.LABEL", "description": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.TOOLTIP", - "route": "libraries" + "children": [ + { + "id": "app.navbar.libraries.files", + "order": 100, + "title": "APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.SIDENAV_LINK.LABEL", + "description": "APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.SIDENAV_LINK.TOOLTIP", + "route": "libraries" + }, + { + "id": "app.navbar.libraries.favorite", + "order": 200, + "title": "APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.SIDENAV_LINK.LABEL", + "description": "APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.SIDENAV_LINK.TOOLTIP", + "route": "favorite/libraries" + } + ] } ] }, @@ -293,10 +369,34 @@ ], "toolbar": [ { - "id": "app.toolbar.preview", + "id": "app.toolbar.share", "order": 100, + "title": "APP.ACTIONS.SHARE", + "icon": "link", + "actions": { + "click": "SHARE_NODE" + }, + "rules": { + "visible": "app.toolbar.canShare" + } + }, + { + "id": "app.toolbar.share.edit", + "order": 101, + "title": "APP.ACTIONS.SHARE_EDIT", + "icon": "link", + "actions": { + "click": "SHARE_NODE" + }, + "rules": { + "visible": "app.selection.file.isShared" + } + }, + { + "id": "app.toolbar.preview", + "order": 300, "title": "APP.ACTIONS.VIEW", - "icon": "open_in_browser", + "icon": "visibility", "actions": { "click": "VIEW_FILE" }, @@ -318,7 +418,7 @@ }, { "id": "app.toolbar.editFolder", - "order": 300, + "order": 250, "title": "APP.ACTIONS.EDIT", "icon": "create", "actions": { @@ -353,7 +453,12 @@ } }, { - "id": "app.toolbar.info", + "id": "app.create.separator.2", + "type": "separator", + "order": 680 + }, + { + "id": "app.toolbar.info.infoDrawer", "type": "custom", "order": 700, "component": "app.toolbar.toggleInfoDrawer", @@ -361,6 +466,36 @@ "visible": "app.toolbar.info" } }, + { + "id": "app.libraries.toolbar.infoDrawer", + "type": "custom", + "order": 701, + "component": "app.toolbar.toggleInfoDrawer", + "rules": { + "visible": "app.libraries.toolbar" + } + }, + { + "id": "app.toolbar.joinLibrary", + "type": "custom", + "order": 704, + "component": "app.toolbar.toggleJoinLibrary", + "rules": { + "visible": "app.libraries.toolbar.canToggleJoin" + } + }, + { + "id": "app.toolbar.leaveLibrary", + "order": 705, + "title": "APP.ACTIONS.LEAVE", + "icon": "not_interested", + "actions": { + "click": "LEAVE_LIBRARY" + }, + "rules": { + "visible": "app.libraries.toolbar.canLeaveLibrary" + } + }, { "id": "app.toolbar.more", "type": "menu", @@ -378,6 +513,15 @@ "visible": "app.toolbar.favorite.canToggle" } }, + { + "id": "app.libraries.toolbar.toggleFavorite", + "type": "custom", + "order": 101, + "component": "app.toolbar.toggleFavoriteLibrary", + "rules": { + "visible": "app.libraries.toolbar" + } + }, { "id": "app.toolbar.favorite.add", "order": 200, @@ -402,6 +546,11 @@ "visible": "app.toolbar.favorite.canRemove" } }, + { + "id": "app.create.separator.3", + "type": "separator", + "order": 380 + }, { "id": "app.toolbar.copy", "order": 400, @@ -418,7 +567,7 @@ "id": "app.toolbar.move", "order": 500, "title": "APP.ACTIONS.MOVE", - "icon": "library_books", + "icon": "adf:move_file", "actions": { "click": "MOVE_NODES" }, @@ -426,15 +575,6 @@ "visible": "app.selection.canDelete" } }, - { - "id": "app.toolbar.share", - "type": "custom", - "order": 600, - "component": "app.shared-link.toggleSharedLink", - "rules": { - "visible": "app.toolbar.canShare" - } - }, { "id": "app.toolbar.delete", "order": 800, @@ -459,6 +599,11 @@ "visible": "app.selection.library" } }, + { + "id": "app.create.separator.4", + "type": "separator", + "order": 980 + }, { "id": "app.toolbar.versions", "order": 1000, @@ -488,8 +633,17 @@ ], "contextMenu": [ { - "id": "app.context.menu.download", + "id": "app.context.menu.share", + "type": "custom", "order": 100, + "component": "app.shared-link.toggleSharedLink", + "rules": { + "visible": "app.context.canShare" + } + }, + { + "id": "app.context.menu.download", + "order": 200, "title": "APP.ACTIONS.DOWNLOAD", "icon": "get_app", "actions": { @@ -501,9 +655,9 @@ }, { "id": "app.context.menu.preview", - "order": 200, + "order": 300, "title": "APP.ACTIONS.VIEW", - "icon": "open_in_browser", + "icon": "visibility", "actions": { "click": "VIEW_FILE" }, @@ -513,7 +667,7 @@ }, { "id": "app.context.menu.editFolder", - "order": 300, + "order": 400, "title": "APP.ACTIONS.EDIT", "icon": "create", "actions": { @@ -523,15 +677,6 @@ "visible": "app.toolbar.canEditFolder" } }, - { - "id": "app.context.menu.share", - "type": "custom", - "order": 400, - "component": "app.shared-link.toggleSharedLink", - "rules": { - "visible": "app.toolbar.canShare" - } - }, { "id": "app.context.menu.favorite.add", "title": "APP.ACTIONS.FAVORITE", @@ -560,16 +705,51 @@ "id": "app.context.menu.favorite", "comment": "workaround for Recent Files and Search API issue", "type": "custom", - "order": 501, + "order": 601, "component": "app.toolbar.toggleFavorite", "rules": { "visible": "app.toolbar.favorite.canToggle" } }, + { + "id": "app.context.menu.libraries.toggleFavorite", + "type": "custom", + "order": 602, + "component": "app.toolbar.toggleFavoriteLibrary", + "rules": { + "visible": "app.libraries.toolbar" + } + }, + { + "id": "app.context.menu.joinLibrary", + "type": "custom", + "order": 603, + "component": "app.menu.toggleJoinLibrary", + "rules": { + "visible": "app.libraries.toolbar.canToggleJoin" + } + }, + { + "id": "app.context.leaveLibrary", + "order": 703, + "title": "APP.ACTIONS.LEAVE", + "icon": "not_interested", + "actions": { + "click": "LEAVE_LIBRARY" + }, + "rules": { + "visible": "app.libraries.toolbar.canLeaveLibrary" + } + }, + { + "id": "app.create.separator.5", + "type": "separator", + "order": 720 + }, { "id": "app.context.menu.copy", "title": "APP.ACTIONS.COPY", - "order": 700, + "order": 750, "icon": "content_copy", "actions": { "click": "COPY_NODES" @@ -582,7 +762,7 @@ "id": "app.context.menu.move", "title": "APP.ACTIONS.MOVE", "order": 800, - "icon": "library_books", + "icon": "adf:move_file", "actions": { "click": "MOVE_NODES" }, @@ -602,6 +782,23 @@ "visible": "app.selection.canDelete" } }, + { + "id": "app.toolbar.deleteLibrary", + "order": 901, + "title": "APP.ACTIONS.DELETE", + "icon": "delete", + "actions": { + "click": "DELETE_LIBRARY" + }, + "rules": { + "visible": "app.libraries.toolbar" + } + }, + { + "id": "app.create.separator.6", + "type": "separator", + "order": 980 + }, { "id": "app.context.menu.versions", "title": "APP.ACTIONS.VERSIONS", @@ -654,8 +851,8 @@ "viewer": { "toolbarActions": [ { - "id": "app.toolbar.download", - "order": 100, + "id": "app.viewer.download", + "order": 300, "title": "APP.ACTIONS.DOWNLOAD", "icon": "get_app", "actions": { @@ -667,7 +864,7 @@ }, { "id": "app.viewer.print", - "order": 200, + "order": 400, "title": "APP.ACTIONS.PRINT", "icon": "print", "actions": { @@ -679,9 +876,9 @@ }, { "id": "app.viewer.share", - "order": 300, + "order": 200, "title": "APP.ACTIONS.SHARE", - "icon": "share", + "icon": "link", "actions": { "click": "SHARE_NODE" }, @@ -689,9 +886,21 @@ "visible": "app.toolbar.canShare" } }, + { + "id": "app.viewer.share.edit", + "order": 201, + "title": "APP.ACTIONS.SHARE_EDIT", + "icon": "link", + "actions": { + "click": "SHARE_NODE" + }, + "rules": { + "visible": "app.selection.file.isShared" + } + }, { "id": "app.viewer.fullscreen", - "order": 400, + "order": 100, "title": "APP.ACTIONS.FULLSCREEN", "icon": "fullscreen", "actions": { @@ -700,6 +909,11 @@ "rules": { "visible": "app.toolbar.canViewFile" } + }, + { + "id": "app.viewer.separator.1", + "type": "separator", + "order": 180 } ], "toolbarMoreMenu": [ @@ -738,13 +952,9 @@ } }, { - "id": "app.viewer.share", - "type": "custom", - "order": 300, - "component": "app.shared-link.toggleSharedLink", - "rules": { - "visible": "app.toolbar.canShare" - } + "id": "app.viewer.more.separator.1", + "type": "separator", + "order": 280 }, { "id": "app.viewer.copy", @@ -762,7 +972,7 @@ "id": "app.viewer.move", "order": 500, "title": "APP.ACTIONS.MOVE", - "icon": "library_books", + "icon": "adf:move_file", "actions": { "click": "MOVE_NODES" }, @@ -782,6 +992,11 @@ "visible": "app.selection.canDelete" } }, + { + "id": "app.viewer.more.separator.2", + "type": "separator", + "order": 680 + }, { "id": "app.viewer.versions", "order": 700, @@ -827,20 +1042,38 @@ "id": "app.sidebar.properties", "order": 100, "title": "APP.INFO_DRAWER.TABS.PROPERTIES", - "component": "app.components.tabs.metadata" + "component": "app.components.tabs.metadata", + "rules": { + "visible": "app.navigation.isNotLibraries" + } }, { "id": "app.sidebar.comments", "order": 200, "title": "APP.INFO_DRAWER.TABS.COMMENTS", - "component": "app.components.tabs.comments" + "component": "app.components.tabs.comments", + "rules": { + "visible": "app.navigation.isNotLibraries" + } }, { "id": "app.sidebar.versions", "order": 300, "disabled": true, "title": "APP.INFO_DRAWER.TABS.VERSIONS", - "component": "app.components.tabs.versions" + "component": "app.components.tabs.versions", + "rules": { + "visible": "app.navigation.isNotLibraries" + } + }, + { + "id": "app.sidebar.library.properties", + "order": 500, + "title": "APP.INFO_DRAWER.TABS.LIBRARY_PROPERTIES", + "component": "app.components.tabs.library.metadata", + "rules": { + "visible": "app.libraries.toolbar" + } } ], "content-metadata-presets": [ @@ -933,17 +1166,64 @@ { "id": "app.libraries.name", "key": "title", - "title": "APP.DOCUMENT_LIST.COLUMNS.TITLE", + "title": "APP.DOCUMENT_LIST.COLUMNS.NAME", "type": "text", "class": "adf-data-table-cell--ellipsis__name", "sortable": true, "template": "app.columns.libraryName", "desktopOnly": false }, + { + "id": "app.libraries.role", + "key": "role", + "title": "APP.DOCUMENT_LIST.COLUMNS.ROLE", + "type": "text", + "sortable": true, + "template": "app.columns.libraryRole", + "desktopOnly": false + }, { "id": "app.libraries.visibility", "key": "visibility", - "title": "APP.DOCUMENT_LIST.COLUMNS.STATUS", + "title": "APP.DOCUMENT_LIST.COLUMNS.VISIBILITY", + "type": "text", + "sortable": true, + "template": "app.columns.libraryStatus", + "desktopOnly": true + } + ], + "favoriteLibraries": [ + { + "id": "app.favorite.libraries.thumbnail", + "key": "$thumbnail", + "type": "image", + "class": "image-table-cell", + "sortable": false, + "desktopOnly": false + }, + { + "id": "app.favorite.libraries.name", + "key": "title", + "title": "APP.DOCUMENT_LIST.COLUMNS.NAME", + "type": "text", + "class": "adf-data-table-cell--ellipsis__name", + "sortable": true, + "template": "app.columns.libraryName", + "desktopOnly": false + }, + { + "id": "app.favorite.libraries.role", + "key": "role", + "title": "APP.DOCUMENT_LIST.COLUMNS.ROLE", + "type": "text", + "sortable": true, + "template": "app.columns.libraryRole", + "desktopOnly": false + }, + { + "id": "app.favorite.libraries.visibility", + "key": "visibility", + "title": "APP.DOCUMENT_LIST.COLUMNS.VISIBILITY", "type": "text", "sortable": true, "template": "app.columns.libraryStatus", @@ -1160,6 +1440,44 @@ "sortable": true, "desktopOnly": true } + ], + "search-libraries": [ + { + "id": "app.libraries.thumbnail", + "key": "$thumbnail", + "type": "image", + "class": "image-table-cell", + "sortable": false, + "desktopOnly": false + }, + { + "id": "app.libraries.name", + "key": "title", + "title": "APP.DOCUMENT_LIST.COLUMNS.NAME", + "type": "text", + "class": "adf-data-table-cell--ellipsis__name", + "sortable": true, + "template": "app.columns.libraryName", + "desktopOnly": false + }, + { + "id": "app.libraries.role", + "key": "role", + "title": "APP.DOCUMENT_LIST.COLUMNS.ROLE", + "type": "text", + "sortable": true, + "template": "app.columns.libraryRole", + "desktopOnly": false + }, + { + "id": "app.libraries.visibility", + "key": "visibility", + "title": "APP.DOCUMENT_LIST.COLUMNS.VISIBILITY", + "type": "text", + "sortable": true, + "template": "app.columns.libraryStatus", + "desktopOnly": true + } ] } } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index c7bca9d1a7..a19c0a06d3 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Version:", + "PLUGINS": { + "TITLE": "Erweiterungen", + "ID": "ID", + "NAME": "Name", + "VERSION": "Version", + "VENDOR": "Hersteller", + "LICENSE": "Lizenz", + "RUNTIME": "Laufzeit", + "DESCRIPTION": "Beschreibung" + }, + "LICENSE": { + "TITLE": "Lizenz", + "PROPERTY": "Eigenschaft", + "VALUE": "Wert" + }, + "STATUS": { + "TITLE": "Status", + "PROPERTY": "Eigenschaft", + "VALUE": "Wert" + }, + "MODULES": { + "TITLE": "Alfresco-Plattformmodule", + "ID": "ID", + "NAME": "Name", + "VERSION": "Version" + }, + "PACKAGES": { + "TITLE": "Pakete", + "NAME": "Name", + "VERSION": "Version" + } + }, "LANGUAGE": "Sprache", "SIGN_IN": "Anmelden", "SIGN_OUT": "Abmelden", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Neu", - "TOOLTIP": "Neue Dateien oder Ordner hinzufügen", + "TOOLTIP": "Neue Dateien und Ordner hinzufügen oder neue Dateibibliothek erstellen", "MENU_ITEMS": { "CREATE_FOLDER": "Ordner erstellen", "UPLOAD_FILE": "Datei hochladen", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Dateibibliotheken", + "TITLE": "Meine Bibliotheken", + "DESCRIPTION": "Auf Dateibibliotheken zugreifen", "SIDENAV_LINK": { "LABEL": "Dateibibliotheken", - "TOOLTIP": "Auf Dateibibliotheken zugreifen" + "TOOLTIP": "Dateibibliotheken" }, "EMPTY_STATE": { - "TITLE": "Sie sind noch kein Mitglied von Dateibibliotheken", - "TEXT": "Verknüpfen Sie Bibliotheken, um Dateien hochzuladen, anzuzeigen und freizugeben." + "FILE_LIBRARIES": { + "TITLE": "Sie sind noch kein Mitglied von Dateibibliotheken", + "TEXT": "Verknüpfen Sie Bibliotheken, um Dateien hochzuladen, anzuzeigen und freizugeben." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Keine Bibliotheken als Favoriten markiert", + "TEXT": "Markieren Sie eine Bibliothek als Favorit, um sie später leichter zu finden." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Meine Bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Meine Bibliotheken", + "TOOLTIP": "Auf meine Bibliotheken zugreifen" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Als Favoriten markierte Bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Als Favoriten markierte Bibliotheken", + "TOOLTIP": "Auf meine als Favoriten markierten Bibliotheken zugreifen" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Ergebnisse durchsuchen", "FOUND_RESULTS": "{{ number }} Ergebnisse gefunden", + "FOUND_ONE_RESULT": "{{ number }} Ergebnisse gefunden", "CUSTOM_ROW": { "MODIFIED": "Bearbeitet", "LOCATION": "Speicherort", "SIZE": "Größe" }, + "UNKNOWN_LOCATION": "Unbekannt", "NO_RESULTS": "Es wurden 0 Suchergebnisse gefunden" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Bibliotheken gefunden...", + "FOUND_RESULTS": "{{ number }} Ergebnisse", + "FOUND_ONE_RESULT": "{{ number }} Ergebnis" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Berechtigungen", "RESTORE": "Wiederherstellen", "FAVORITE": "Zu Favoriten", + "ADD_FAVORITE": "Favoriten hinzufügen", + "REMOVE_FAVORITE": "Favoriten entfernen", "UNSHARE": "Freigabe aufheben", "DETAILS": "Details anzeigen", "VERSIONS": "Versionen verwalten", @@ -133,7 +199,10 @@ "SHARE": "Freigeben", "SHARE_EDIT": "Einstellungen für freigegebene Links", "PRINT": "Drucken", - "FULLSCREEN": "Vollbildmodus aktivieren" + "FULLSCREEN": "Vollbildmodus aktivieren", + "JOIN": "Beitreten", + "CANCEL_JOIN": "Beitrittsanfrage zurückziehen", + "LEAVE": "Bibliothek verlassen" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Dadurch werden die ausgewählten Elemente dauerhaft gelöscht.", "YES_LABEL": "Löschen", "NO_LABEL": "Behalten" + }, + "CONFIRM_LEAVE": { + "TITLE": "Diese Bibliothek verlassen?", + "MESSAGE": "Beim Verlassen verlieren Sie Ihren Zugriff.", + "YES_LABEL": "OK", + "NO_LABEL": "Abbrechen" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Name", "SIZE": "Größe", "MODIFIED_ON": "Bearbeitet", "MODIFIED_BY": "Bearbeitet von", - "STATUS": "Status", + "VISIBILITY": "Sichtbarkeit", "TITLE": "Titel", "LOCATION": "Speicherort", "SHARED_BY": "Freigegeben von", "DELETED_ON": "Gelöscht", - "DELETED_BY": "Gelöscht von" + "DELETED_BY": "Gelöscht von", + "ROLE": "Meine Rolle" }, "TOOLBAR": { "CARDVIEW": "Kartenansichtsmodus", @@ -166,9 +243,16 @@ "MODERATED": "Moderiert", "PRIVATE": "Privat" }, + "SITES_ROLE": { + "MANAGER": "Manager", + "COLLABORATOR": "Mitarbeiter", + "CONTRIBUTOR": "Beitragender", + "CONSUMER": "Verbraucher" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Diese Datei oder diesen Ordner gibt es nicht mehr oder Sie verfügen nicht über die für die Anzeige nötigen Benutzerrechte.", + "CANNOT_NAVIGATE_LOCATION": "Öffnen dieses Speicherorts nicht möglich", + "MISSING_CONTENT": "Dieses Element gibt es nicht mehr oder Sie verfügen nicht über die für die Anzeige nötigen Benutzerrechte.", "GENERIC": "Die Aktion war nicht erfolgreich. Versuchen Sie es noch einmal oder wenden Sie sich an Ihr IT-Team.", "CONFLICT": "Dieser Name wird bereits verwendet. Versuchen Sie es mit einem anderen Namen.", "NODE_MOVE": "Verschiebung nicht erfolgreich. Es gibt bereits eine Datei mit demselben Namen.", @@ -190,13 +274,21 @@ "GENERIC": "Beim Wiederherstellen von {{ name }} ist ein Problem aufgetreten" } }, - "DELETE_LIBRARY_FAILED": "Bibliothek kann nicht gelöscht werden" + "DELETE_LIBRARY_FAILED": "Bibliothek kann nicht gelöscht werden", + "JOIN_REQUEST_FAILED": "Beitritt zur Bibliothek nicht möglich", + "JOIN_CANCEL_FAILED": "Ihre Beitrittsanfrage kann nicht zurückgezogen werden", + "LEAVE_LIBRARY_FAILED": "Sie können diese Bibliothek nicht verlassen", + "INVALID_SENDER_EMAIL": "Ihre E-Mail-Adresse muss gültig sein, bevor Sie den Beitritt zur Bibliothek anfordern.", + "INVALID_RECEIVER_EMAIL": "Empfänger-E-Mail-Addresse(n) nicht zulässig. Wenden Sie sich an Ihr IT-Team." }, "UPLOAD": { "ERROR": { - "GENERIC": "Es ist ein Problem aufgetreten.", + "GENERIC": "Beim Hochladen ist ein Problem aufgetreten. Wenden Sie sich an das IT-Team, wenn das Problem weiterhin besteht.", "CONFLICT": "Neue Version nicht hochgeladen. Es gibt bereits eine Datei mit demselben Namen.", - "500": "Beim Hochladen ist ein Problem aufgetreten." + "500": "Interner Serverfehler. Versuchen Sie es noch einmal oder wenden Sie sich an den IT-Support [500]", + "504": "Server-Timeout. Versuchen Sie es noch einmal oder wenden Sie sich an den IT-Support [504]", + "403": "Sie verfügen nicht die erforderlichen Benutzerrechte, um etwas an diesen Speicherort hochzuladen [403]", + "404": "Speicherort für Upload nicht mehr vorhanden [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} konnte nicht verschoben werden." } }, - "LIBRARY_DELETED": "Bibliothek gelöscht" + "LIBRARY_DELETED": "Bibliothek gelöscht", + "JOINED": "Beitritt zur Bibliothek abgeschlossen", + "JOIN_REQUESTED": "Anfrage zum Bibliotheksbeitritt abgeschickt", + "JOIN_CANCELED": "Anfrage zum Bibliotheksbeitritt zurückgezogen", + "LEFT_LIBRARY": "Sie haben die Bibliothek verlassen" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Details", "TABS": { "PROPERTIES": "Eigenschaften", + "LIBRARY_PROPERTIES": "Info", "VERSIONS": "Versionen", "COMMENTS": "Kommentare" } @@ -262,7 +359,8 @@ "TITLE": "Berechtigungen verwalten", "CLOSE": "Schließen", "INHERIT_PERMISSIONS_BUTTON": "Berechtigungen erben", - "INHERITED_PERMISSIONS_BUTTON": "Berechtigungen geerbt" + "INHERITED_PERMISSIONS_BUTTON": "Berechtigungen geerbt", + "ADD_USER_OR_GROUP": "Benutzer oder Gruppe hinzufügen" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Bibliothek erstellen", "CREATE": "Erstellen", + "UPDATE": "Aktualisieren", + "EDIT": "Bearbeiten", "CANCEL": "Abbrechen", "FORM": { "DESCRIPTION": "Beschreibung", "SITE_ID": "Bibliotheks-ID", - "NAME": "Name" + "NAME": "Name", + "VISIBILITY": "Sichtbarkeit" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Öffentlich", "MODERATED": "Moderiert" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Bibliotheksname wird bereits verwendet" + }, "ERRORS": { "GENERIC": "Es ist ein Problem aufgetreten", - "EXISTENT_SITE": "Diese Bibliotheks-ID ist nicht verfügbar. Versuchen Sie es mit einer anderen Bibliothek.", + "EXISTENT_SITE": "Diese Bibliotheks-ID ist nicht verfügbar. Versuchen Sie es mit einer anderen Bibliotheks-ID.", "CONFLICT": "Diese Bibliotheks-ID wird bereits verwendet. Sehen Sie im Papierkorb nach.", "ID_TOO_LONG": "Der URL-Name darf höchstens 72 Zeichen lang sein", "DESCRIPTION_TOO_LONG": "Die Beschreibung darf höchstens 512 Zeichen lang sein", "TITLE_TOO_LONG": "Der Titel darf höchstens 256 Zeichen lang sein", - "ILLEGAL_CHARACTERS": "Verwenden Sie nur Zahlen und Buchstaben" + "ILLEGAL_CHARACTERS": "Verwenden Sie nur Zahlen und Buchstaben", + "ONLY_SPACES": "Bibliotheksname darf nicht nur aus Leerzeichen bestehen.", + "LIBRARY_UPDATE_ERROR": "Beim Aktualisieren der Bibliothekseigenschaften ist ein Fehler aufgetreten" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Bibliothekseigenschaften aktualisiert" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Suche", + "FILES": "Dateien", + "FOLDERS": "Ordner", + "LIBRARIES": "Bibliotheken", + "HINT": "Die Sucheingabe muss mindestens zwei alphanumerische Zeichen umfassen." + }, "SORT": { "RELEVANCE": "Relevanz", "FILENAME": "Dateiname", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index cdc2fc30ca..06e5eac9dc 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Version:", + "PLUGINS": { + "TITLE": "Extensions", + "ID": "ID", + "NAME": "Name", + "VERSION": "Version", + "VENDOR": "Vendor", + "LICENSE": "License", + "RUNTIME": "Runtime", + "DESCRIPTION": "Description" + }, + "LICENSE": { + "TITLE": "License", + "PROPERTY": "Property", + "VALUE": "Value" + }, + "STATUS": { + "TITLE": "Status", + "PROPERTY": "Property", + "VALUE": "Value" + }, + "MODULES": { + "TITLE": "Alfresco platform modules", + "ID": "ID", + "NAME": "Name", + "VERSION": "Version" + }, + "PACKAGES": { + "TITLE": "Packages", + "NAME": "Name", + "VERSION": "Version" + } + }, "LANGUAGE": "Language", "SIGN_IN": "Sign in", "SIGN_OUT": "Sign out", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "New", - "TOOLTIP": "Add new files or folders", + "TOOLTIP": "Add new files and folders, or create a new File Library", "MENU_ITEMS": { "CREATE_FOLDER": "Create folder", "UPLOAD_FILE": "Upload file", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "File Libraries", + "TITLE": "My Libraries", + "DESCRIPTION": "Access File Libraries", "SIDENAV_LINK": { "LABEL": "File Libraries", - "TOOLTIP": "Access File Libraries" + "TOOLTIP": "File Libraries" }, "EMPTY_STATE": { - "TITLE": "You aren't a member of any File Libraries yet", - "TEXT": "Join libraries to upload, view, and share files." + "FILE_LIBRARIES": { + "TITLE": "You aren't a member of any File Libraries yet", + "TEXT": "Join libraries to upload, view, and share files." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "No Favorite Libraries", + "TEXT": "Favorite a library that you want to find easily later." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "My Libraries", + "SIDENAV_LINK": { + "LABEL": "My Libraries", + "TOOLTIP": "Access my libraries" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Favorite Libraries", + "SIDENAV_LINK": { + "LABEL": "Favorite Libraries", + "TOOLTIP": "Access my favorite libraries" + } + } } }, "SHARED": { @@ -73,7 +130,7 @@ }, "EMPTY_STATE": { "TITLE": "No recent files", - "TEXT": "Items you upload or edit in the last 30 days are shown here." + "TEXT": "Items you uploaded or edited in the last 30 days are shown here." } }, "FAVORITES": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Search Results", "FOUND_RESULTS": "{{ number }} results found", + "FOUND_ONE_RESULT": "{{ number }} result found", "CUSTOM_ROW": { "MODIFIED": "Modified", "LOCATION": "Location", "SIZE": "Size" }, + "UNKNOWN_LOCATION": "Unknown", "NO_RESULTS": "Your search returned 0 results" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Libraries found...", + "FOUND_RESULTS": "{{ number }} results", + "FOUND_ONE_RESULT": "{{ number }} result" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Permissions", "RESTORE": "Restore", "FAVORITE": "Favorite", + "ADD_FAVORITE": "Add favorite", + "REMOVE_FAVORITE": "Remove favorite", "UNSHARE": "Unshare", "DETAILS": "View details", "VERSIONS": "Manage Versions", @@ -133,7 +199,10 @@ "SHARE": "Share", "SHARE_EDIT": "Shared link settings", "PRINT": "Print", - "FULLSCREEN": "Activate full-screen mode" + "FULLSCREEN": "Activate full-screen mode", + "JOIN": "Join", + "CANCEL_JOIN": "Cancel join request", + "LEAVE": "Leave library" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "This will permanently remove the selected item(s).", "YES_LABEL": "Delete", "NO_LABEL": "Keep" + }, + "CONFIRM_LEAVE": { + "TITLE": "Leave this library?", + "MESSAGE": "Leaving will remove your access.", + "YES_LABEL": "OK", + "NO_LABEL": "Cancel" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Name", "SIZE": "Size", "MODIFIED_ON": "Modified", "MODIFIED_BY": "Modified by", - "STATUS": "Status", + "VISIBILITY": "Visibility", "TITLE": "Title", "LOCATION": "Location", "SHARED_BY": "Shared by", "DELETED_ON": "Deleted", - "DELETED_BY": "Deleted by" + "DELETED_BY": "Deleted by", + "ROLE": "My Role" }, "TOOLBAR": { "CARDVIEW": "Card view mode", @@ -166,9 +243,16 @@ "MODERATED": "Moderated", "PRIVATE": "Private" }, + "SITES_ROLE": { + "MANAGER": "Manager", + "COLLABORATOR": "Collaborator", + "CONTRIBUTOR": "Contributor", + "CONSUMER": "Consumer" + }, "MESSAGES": { "ERRORS":{ - "MISSING_CONTENT": "This file or folder no longer exists or you don't have permission to view it.", + "CANNOT_NAVIGATE_LOCATION": "Cannot open this location", + "MISSING_CONTENT": "This item no longer exists or you don't have permission to view it.", "GENERIC": "The action was unsuccessful. Try again or contact your IT Team.", "CONFLICT": "This name is already in use, try a different name.", "NODE_MOVE": "Move unsuccessful, a file with the same name already exists.", @@ -190,13 +274,21 @@ "GENERIC": "There was a problem restoring {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Cannot delete the library" + "DELETE_LIBRARY_FAILED": "Cannot delete the library", + "JOIN_REQUEST_FAILED": "Cannot join the library", + "JOIN_CANCEL_FAILED": "Cannot cancel your join request", + "LEAVE_LIBRARY_FAILED": "Cannot leave this library", + "INVALID_SENDER_EMAIL": "Your email address must be valid before requesting to join.", + "INVALID_RECEIVER_EMAIL": "Recipient(s) email address not valid, contact IT." }, "UPLOAD": { "ERROR": { - "GENERIC": "There was a problem", + "GENERIC": "Upload unsuccessful. Contact IT if this problem persists", "CONFLICT": "New version not uploaded, another file with the same name already exists", - "500": "There was a problem while uploading" + "500": "Internal server error, try again or contact IT support [500]", + "504": "The server timed out, try again or contact IT support [504]", + "403": "Insufficient permissions to upload in this location [403]", + "404": "Upload location no longer exists [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} couldn't be moved." } }, - "LIBRARY_DELETED": "Library deleted" + "LIBRARY_DELETED": "Library deleted", + "JOINED": "Library joined", + "JOIN_REQUESTED": "Request sent to join this library", + "JOIN_CANCELED": "Canceled the request to join the library", + "LEFT_LIBRARY": "You have left the library" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Details", "TABS": { "PROPERTIES": "Properties", + "LIBRARY_PROPERTIES": "About", "VERSIONS": "Versions", "COMMENTS": "Comments" } @@ -259,10 +356,11 @@ }, "PERMISSIONS": { "DIALOG": { - "TITLE": "Manage Permissons", + "TITLE": "Manage Permissions", "CLOSE": "Close", - "INHERIT_PERMISSIONS_BUTTON": "Inherit Permission", - "INHERITED_PERMISSIONS_BUTTON": "Permission Inherited" + "INHERIT_PERMISSIONS_BUTTON": "Inherit Permissions", + "INHERITED_PERMISSIONS_BUTTON": "Permissions Inherited", + "ADD_USER_OR_GROUP": "Add User or Group" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Create Library", "CREATE": "Create", + "UPDATE": "Update", + "EDIT": "Edit", "CANCEL": "Cancel", "FORM": { "DESCRIPTION": "Description", "SITE_ID": "Library ID", - "NAME": "Name" + "NAME": "Name", + "VISIBILITY": "Visibility" } }, "VISIBILITY": { @@ -294,18 +395,33 @@ "PUBLIC": "Public", "MODERATED": "Moderated" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Library name already in use" + }, "ERRORS": { "GENERIC": "We hit a problem", - "EXISTENT_SITE": "This Library ID isn't available. Try a different Library.", + "EXISTENT_SITE": "This Library ID isn't available. Try a different Library ID.", "CONFLICT": "This Library ID is already used. Check the trashcan.", "ID_TOO_LONG": "Use 72 characters or less for the URL name", "DESCRIPTION_TOO_LONG": "Use 512 characters or less for description", "TITLE_TOO_LONG": "Use 256 characters or less for title", - "ILLEGAL_CHARACTERS": "Use numbers and letters only" + "ILLEGAL_CHARACTERS": "Use numbers and letters only", + "ONLY_SPACES": "Library name can't contain only spaces", + "LIBRARY_UPDATE_ERROR": "There was an error updating library properties" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Library properties updated" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Search", + "FILES": "Files", + "FOLDERS": "Folders", + "LIBRARIES": "Libraries", + "HINT": "Search input must have at least 2 alphanumeric characters." + }, "SORT": { "RELEVANCE": "Relevance", "FILENAME": "Filename", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index a74ac474a7..f88a7ad6ff 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Versión:", + "PLUGINS": { + "TITLE": "Extensiones", + "ID": "ID", + "NAME": "Nombre", + "VERSION": "Versión", + "VENDOR": "Proveedor", + "LICENSE": "Licencia", + "RUNTIME": "Tiempo de ejecución", + "DESCRIPTION": "Descripción" + }, + "LICENSE": { + "TITLE": "Licencia", + "PROPERTY": "Propiedad", + "VALUE": "Valor" + }, + "STATUS": { + "TITLE": "Estado", + "PROPERTY": "Propiedad", + "VALUE": "Valor" + }, + "MODULES": { + "TITLE": "Módulos de la plataforma de Alfresco", + "ID": "ID", + "NAME": "Nombre", + "VERSION": "Versión" + }, + "PACKAGES": { + "TITLE": "Paquetes", + "NAME": "Nombre", + "VERSION": "Versión" + } + }, "LANGUAGE": "Idioma", "SIGN_IN": "Iniciar sesión", "SIGN_OUT": "Cerrar sesión", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Nuevo", - "TOOLTIP": "Añadir nuevos ficheros o carpetas", + "TOOLTIP": "Añadir nuevos ficheros o carpetas, o crear una nueva biblioteca de ficheros", "MENU_ITEMS": { "CREATE_FOLDER": "Crear carpeta", "UPLOAD_FILE": "Cargar fichero", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Bibliotecas de ficheros", + "TITLE": "Mis bibliotecas", + "DESCRIPTION": "Acceder a bibliotecas de ficheros", "SIDENAV_LINK": { "LABEL": "Bibliotecas de ficheros", - "TOOLTIP": "Acceder a las bibliotecas de ficheros" + "TOOLTIP": "Bibliotecas de ficheros" }, "EMPTY_STATE": { - "TITLE": "No es miembro de ninguna biblioteca de ficheros todavía", - "TEXT": "Únase a las bibliotecas para cargar, ver y compartir ficheros." + "FILE_LIBRARIES": { + "TITLE": "Todavía no es miembro de ninguna biblioteca de ficheros", + "TEXT": "Únase a las bibliotecas para cargar, ver y compartir ficheros." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "No hay bibliotecas favoritas", + "TEXT": "Marque como favorita una biblioteca para encontrarla más rápidamente." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Mis bibliotecas", + "SIDENAV_LINK": { + "LABEL": "Mis bibliotecas", + "TOOLTIP": "Acceder a mis bibliotecas" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Bibliotecas favoritas", + "SIDENAV_LINK": { + "LABEL": "Bibliotecas favoritas", + "TOOLTIP": "Acceder a mis bibliotecas favoritas" + } + } } }, "SHARED": { @@ -73,7 +130,7 @@ }, "EMPTY_STATE": { "TITLE": "No hay ficheros recientes", - "TEXT": "Los elementos que cargue o edite en los últimos 30 días se muestran aquí." + "TEXT": "Los elementos cargados o editados en los últimos 30 días se muestran aquí." } }, "FAVORITES": { @@ -84,7 +141,7 @@ }, "EMPTY_STATE": { "TITLE": "No hay ficheros ni carpetas favoritos", - "TEXT": "Lo elementos favoritos que quiera encontrar fácilmente después." + "TEXT": "Lo elementos favoritos que desee encontrar con facilidad posteriormente." } }, "TRASHCAN": { @@ -95,7 +152,7 @@ }, "EMPTY_STATE": { "TITLE": "La papelera está vacía", - "FIRST_TEXT": "Los elementos que elimine se mueven a la papelera.", + "FIRST_TEXT": "Los elementos que elimine se moverán a la papelera.", "SECOND_TEXT": "Vacíe la papelera para eliminar los elementos permanentemente." } }, @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Resultados de la búsqueda", "FOUND_RESULTS": "{{ number }} resultados encontrados", + "FOUND_ONE_RESULT": "{{ number }} resultado encontrado", "CUSTOM_ROW": { "MODIFIED": "Modificado", "LOCATION": "Ubicación", "SIZE": "Tamaño" }, + "UNKNOWN_LOCATION": "Desconocido", "NO_RESULTS": "Su búsqueda arrojó 0 resultados" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Bibliotecas encontradas...", + "FOUND_RESULTS": "{{ number }} resultados", + "FOUND_ONE_RESULT": "{{ number }} resultado" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Permisos", "RESTORE": "Restaurar", "FAVORITE": "Favorito", + "ADD_FAVORITE": "Añadir favorita", + "REMOVE_FAVORITE": "Eliminar favorita", "UNSHARE": "No compartir", "DETAILS": "Ver los detalles", "VERSIONS": "Gestionar versiones", @@ -133,7 +199,10 @@ "SHARE": "Compartir", "SHARE_EDIT": "Configuración de enlaces compartidos", "PRINT": "Imprimir", - "FULLSCREEN": "Activar el modo de pantalla completa" + "FULLSCREEN": "Activar el modo de pantalla completa", + "JOIN": "Unirse", + "CANCEL_JOIN": "Cancelar solicitud de unirse", + "LEAVE": "Abandonar biblioteca" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Esto eliminará permanentemente los elementos seleccionados.", "YES_LABEL": "Eliminar", "NO_LABEL": "Guardar" + }, + "CONFIRM_LEAVE": { + "TITLE": "¿Desea abandonar esta biblioteca?", + "MESSAGE": "Al abandonarla, perderá el acceso.", + "YES_LABEL": "Aceptar", + "NO_LABEL": "Cancelar" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Nombre", "SIZE": "Tamaño", "MODIFIED_ON": "Modificado", "MODIFIED_BY": "Modificado por", - "STATUS": "Estado", + "VISIBILITY": "Visibilidad", "TITLE": "Título", "LOCATION": "Ubicación", "SHARED_BY": "Compartido por", "DELETED_ON": "Eliminado", - "DELETED_BY": "Eliminado por" + "DELETED_BY": "Eliminado por", + "ROLE": "Mi función" }, "TOOLBAR": { "CARDVIEW": "Modo de vista de tarjeta", @@ -166,9 +243,16 @@ "MODERATED": "Moderado", "PRIVATE": "Lista privada" }, + "SITES_ROLE": { + "MANAGER": "Administrador", + "COLLABORATOR": "Colaborador", + "CONTRIBUTOR": "Contribuidor", + "CONSUMER": "Consumidor" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Este fichero o carpeta ya no existe o no tiene permiso para verlo.", + "CANNOT_NAVIGATE_LOCATION": "No puede abrirse esta ubicación", + "MISSING_CONTENT": "Este elemento ya no existe o no tiene permiso para verlo.", "GENERIC": "La acción no ha sido satisfactoria. Vuelva a intentarlo o póngase en contacto con el equipo de TI.", "CONFLICT": "Este nombre ya está en uso; pruebe con un nombre diferente.", "NODE_MOVE": "Error al mover el fichero; ya existe un fichero con el mismo nombre.", @@ -190,13 +274,21 @@ "GENERIC": "Error al restaurar {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "No se puede eliminar la biblioteca" + "DELETE_LIBRARY_FAILED": "No se puede eliminar la biblioteca", + "JOIN_REQUEST_FAILED": "No puede unirse a la biblioteca", + "JOIN_CANCEL_FAILED": "No puede cancelarse su solicitud de unirse", + "LEAVE_LIBRARY_FAILED": "No puede abandonar esta biblioteca", + "INVALID_SENDER_EMAIL": "Su dirección de correo electrónico debe ser válida antes de solicitar unirse.", + "INVALID_RECEIVER_EMAIL": "Dirección de correo electrónico de destinatario(s) no válida; póngase en contacto con el equipo de TI" }, "UPLOAD": { "ERROR": { - "GENERIC": "Se ha producido un problema", + "GENERIC": "Error de carga; póngase en contacto con el equipo de TI si el problema continúa", "CONFLICT": "No se ha cargado la nueva versión, ya existe otro fichero con el mismo nombre", - "500": "Se ha producido un problema durante la carga" + "500": "Error de servidor interno; vuelva a intentarlo o póngase en contacto con el equipo de TI", + "504": "Se ha agotado el tiempo de espera del servidor; póngase en contacto con el equipo de asistencia de TI [504]", + "403": "Permisos insuficientes para cargar en esta ubicación [403]", + "404": "La ubicación de carga ya no existe [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} no se ha podido mover." } }, - "LIBRARY_DELETED": "Biblioteca eliminada" + "LIBRARY_DELETED": "Biblioteca eliminada", + "JOINED": "Se ha unido a la biblioteca", + "JOIN_REQUESTED": "Se ha enviado una solicitud para unirse a esta biblioteca", + "JOIN_CANCELED": "Se ha cancelado la solicitud de unirse a esta biblioteca", + "LEFT_LIBRARY": "Ha abandonado la biblioteca" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Detalles", "TABS": { "PROPERTIES": "Propiedades", + "LIBRARY_PROPERTIES": "Acerca de", "VERSIONS": "Versiones", "COMMENTS": "Comentarios" } @@ -261,8 +358,9 @@ "DIALOG": { "TITLE": "Gestionar permisos", "CLOSE": "Cerrar", - "INHERIT_PERMISSIONS_BUTTON": "Heredar permiso", - "INHERITED_PERMISSIONS_BUTTON": "Permiso heredado" + "INHERIT_PERMISSIONS_BUTTON": "Heredar permisos", + "INHERITED_PERMISSIONS_BUTTON": "Permisos heredados", + "ADD_USER_OR_GROUP": "Añadir usuario o grupo" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Crear biblioteca", "CREATE": "Crear", + "UPDATE": "Actualizar", + "EDIT": "Editar", "CANCEL": "Cancelar", "FORM": { "DESCRIPTION": "Descripción", "SITE_ID": "ID de biblioteca", - "NAME": "Nombre" + "NAME": "Nombre", + "VISIBILITY": "Visibilidad" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Público", "MODERATED": "Moderado" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Nombre de biblioteca ya en uso" + }, "ERRORS": { - "GENERIC": "Se ha producido un error", - "EXISTENT_SITE": "La ID de biblioteca no está disponible. Pruebe una biblioteca diferente.", + "GENERIC": "Ha surgido un problema", + "EXISTENT_SITE": "Esta ID de biblioteca no está disponible. Pruebe con otra ID distinta.", "CONFLICT": "Esta ID de biblioteca ya está utilizada. Compruebe la papelera.", "ID_TOO_LONG": "Utilice como máximo 72 caracteres para el nombre de la URL", "DESCRIPTION_TOO_LONG": "Utilice como máximo 512 caracteres para la descripción", "TITLE_TOO_LONG": "Utilice como máximo 256 caracteres para el título", - "ILLEGAL_CHARACTERS": "Use solo números y letras" + "ILLEGAL_CHARACTERS": "Use solo números y letras", + "ONLY_SPACES": "El nombre de biblioteca no puede tener solo espacios", + "LIBRARY_UPDATE_ERROR": "Error al actualizar las propiedades de la biblioteca" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Propiedades de la biblioteca actualizadas" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Buscar", + "FILES": "Ficheros", + "FOLDERS": "Carpetas", + "LIBRARIES": "Bibliotecas", + "HINT": "La cadena de búsqueda debe contener al menos 2 caracteres alfanuméricos" + }, "SORT": { "RELEVANCE": "Relevancia", "FILENAME": "Nombre de fichero", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index af6072ef79..5b3533f766 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Version :", + "PLUGINS": { + "TITLE": "Extensions", + "ID": "ID", + "NAME": "Nom", + "VERSION": "Version", + "VENDOR": "Fournisseur", + "LICENSE": "Licence", + "RUNTIME": "Java Runtime", + "DESCRIPTION": "Description" + }, + "LICENSE": { + "TITLE": "Licence", + "PROPERTY": "Propriété", + "VALUE": "Valeur" + }, + "STATUS": { + "TITLE": "Statut", + "PROPERTY": "Propriété", + "VALUE": "Valeur" + }, + "MODULES": { + "TITLE": "Modules de la plateforme Alfresco", + "ID": "ID", + "NAME": "Nom", + "VERSION": "Version" + }, + "PACKAGES": { + "TITLE": "Packages", + "NAME": "Nom", + "VERSION": "Version" + } + }, "LANGUAGE": "Langue", "SIGN_IN": "Connexion", "SIGN_OUT": "Déconnexion", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Nouveau", - "TOOLTIP": "Ajouter de nouveaux fichiers ou dossiers", + "TOOLTIP": "Ajouter de nouveaux fichiers et dossiers ou créer une nouvelle bibliothèque de fichiers", "MENU_ITEMS": { "CREATE_FOLDER": "Créer un dossier", "UPLOAD_FILE": "Importer le fichier", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Bibliothèques de fichiers", + "TITLE": "Mes bibliothèques", + "DESCRIPTION": "Accéder aux bibliothèques de fichiers", "SIDENAV_LINK": { "LABEL": "Bibliothèques de fichiers", - "TOOLTIP": "Accéder aux bibliothèques de fichiers" + "TOOLTIP": "Bibliothèques de fichiers" }, "EMPTY_STATE": { - "TITLE": "Vous n'êtes actuellement membre d'aucune Bibliothèque de fichiers", - "TEXT": "Abonnez-vous à des Bibliothèques pour charger, afficher et partager des fichiers." + "FILE_LIBRARIES": { + "TITLE": "Vous n'êtes actuellement membre d'aucune Bibliothèque de fichiers", + "TEXT": "Abonnez-vous à des Bibliothèques pour charger, afficher et partager des fichiers." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Pas de bibliothèques favorites", + "TEXT": "Ajouter aux favoris une bibliothèque pour la retrouver facilement par la suite." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Mes bibliothèques", + "SIDENAV_LINK": { + "LABEL": "Mes bibliothèques", + "TOOLTIP": "Accéder à mes bibliothèques" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Bibliothèques favorites", + "SIDENAV_LINK": { + "LABEL": "Bibliothèques favorites", + "TOOLTIP": "Accéder à mes bibliothèques favorites" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Résultats de la recherche", "FOUND_RESULTS": "{{ number }} résultats trouvés", + "FOUND_ONE_RESULT": "{{ number }} résultat trouvé", "CUSTOM_ROW": { "MODIFIED": "Modifié", "LOCATION": "Emplacement", "SIZE": "Taille" }, + "UNKNOWN_LOCATION": "Inconnu", "NO_RESULTS": "Votre recherche a renvoyé 0 résultat" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Bibliothèques trouvées...", + "FOUND_RESULTS": "{{ number }} résultats", + "FOUND_ONE_RESULT": "{{ number }} résultat" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Permissions", "RESTORE": "Restaurer", "FAVORITE": "Favori", + "ADD_FAVORITE": "Ajouter le favori", + "REMOVE_FAVORITE": "Supprimer le favori", "UNSHARE": "Ne pas partager", "DETAILS": "Afficher les détails", "VERSIONS": "Gérer les versions", @@ -133,7 +199,10 @@ "SHARE": "Partager", "SHARE_EDIT": "Paramètres du lien partagé", "PRINT": "Imprimer", - "FULLSCREEN": "Activer le mode plein écran" + "FULLSCREEN": "Activer le mode plein écran", + "JOIN": "Rejoindre", + "CANCEL_JOIN": "Annuler la demande pour rejoindre", + "LEAVE": "Quitter la bibliothèque" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Le ou les éléments sélectionnés seront définitivement supprimés.", "YES_LABEL": "Supprimer", "NO_LABEL": "Garder" + }, + "CONFIRM_LEAVE": { + "TITLE": "Quitter cette bibliothèque ?", + "MESSAGE": "Si vous quittez, votre accès sera supprimé.", + "YES_LABEL": "OK", + "NO_LABEL": "Annuler" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Nom", "SIZE": "Taille", "MODIFIED_ON": "Modifié", "MODIFIED_BY": "Modifié par", - "STATUS": "Statut", + "VISIBILITY": "Visibilité", "TITLE": "Titre", "LOCATION": "Emplacement", "SHARED_BY": "Partagé(s) par", "DELETED_ON": "Supprimé", - "DELETED_BY": "Supprimé(s) par" + "DELETED_BY": "Supprimé(s) par", + "ROLE": "Mon rôle" }, "TOOLBAR": { "CARDVIEW": "Mode d'affichage Carte", @@ -166,9 +243,16 @@ "MODERATED": "Modéré", "PRIVATE": "Liste privée" }, + "SITES_ROLE": { + "MANAGER": "Gestionnaire", + "COLLABORATOR": "Collaborateur", + "CONTRIBUTOR": "Contributeur", + "CONSUMER": "Lecteur" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Ce fichier ou dossier n'existe plus ou vous n'avez pas les droits pour le consulter.", + "CANNOT_NAVIGATE_LOCATION": "Impossible d'ouvrir cet emplacement", + "MISSING_CONTENT": "Cet élément n'existe plus ou vous n'avez pas les droits pour le consulter.", "GENERIC": "L'action a échoué. Réessayez ou contactez le service informatique.", "CONFLICT": "Ce nom est déjà utilisé, essayez avec un nom différent.", "NODE_MOVE": "Echec du déplacement, un fichier du même nom existe déjà.", @@ -190,13 +274,21 @@ "GENERIC": "Un problème est survenu pendant la restauration de {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Impossible de supprimer la bibliothèque" + "DELETE_LIBRARY_FAILED": "Impossible de supprimer la bibliothèque", + "JOIN_REQUEST_FAILED": "Impossible de rejoindre la bibliothèque", + "JOIN_CANCEL_FAILED": "Impossible d'annuler la demande pour rejoindre", + "LEAVE_LIBRARY_FAILED": "Impossible de quitter cette bibliothèque", + "INVALID_SENDER_EMAIL": "Pour faire une demande pour rejoindre, vous devez avoir une adresse e-mail valide.", + "INVALID_RECEIVER_EMAIL": "Adresse e-mail du ou des destinataire(s) non valide. Contactez le support technique." }, "UPLOAD": { "ERROR": { - "GENERIC": "Une erreur est survenue", + "GENERIC": "L'importation a échoué. Contactez le support technique si le problème persiste", "CONFLICT": "La nouvelle version n'a pas été importée, un autre fichier du même nom existe déjà", - "500": "Une erreur est survenue lors de l'importation" + "500": "Erreur de serveur interne. Réessayez ou contactez le support technique [500]", + "504": "Le délai d'attente du serveur a expiré. Réessayez ou contactez le support technique [504]", + "403": "Droits d'accès insuffisants pour importer dans cet emplacement [403]", + "404": "L'emplacement de destination de l'importation n'existe plus [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} n'a/n'ont pas pu être déplacé(s)." } }, - "LIBRARY_DELETED": "Bibliothèque supprimée" + "LIBRARY_DELETED": "Bibliothèque supprimée", + "JOINED": "Vous avez rejoint la bibliothèque", + "JOIN_REQUESTED": "Demande envoyée pour rejoindre cette bibliothèque", + "JOIN_CANCELED": "La demande pour rejoindre la bibliothèque a été annulée", + "LEFT_LIBRARY": "Vous avez quitté la bibliothèque" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Détails", "TABS": { "PROPERTIES": "Propriétés", + "LIBRARY_PROPERTIES": "A propos de", "VERSIONS": "Versions", "COMMENTS": "Commentaires" } @@ -262,7 +359,8 @@ "TITLE": "Gérer les droits d'accès", "CLOSE": "Fermer", "INHERIT_PERMISSIONS_BUTTON": "Hériter les droits d'accès", - "INHERITED_PERMISSIONS_BUTTON": "Droits d'accès hérités" + "INHERITED_PERMISSIONS_BUTTON": "Droits d'accès hérités", + "ADD_USER_OR_GROUP": "Ajouter un utilisateur ou un groupe" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Créer une Bibliothèque", "CREATE": "Créer", + "UPDATE": "Mis à jour", + "EDIT": "Modifier", "CANCEL": "Annuler", "FORM": { "DESCRIPTION": "Description", "SITE_ID": "ID de Bibliothèque", - "NAME": "Nom" + "NAME": "Nom", + "VISIBILITY": "Visibilité" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Public", "MODERATED": "Modéré" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Ce nom de bibliothèque existe déjà" + }, "ERRORS": { - "GENERIC": "Une erreur est survenue", - "EXISTENT_SITE": "Cet ID de Bibliothèque est indisponible. Essayez avec une autre Bibliothèque.", + "GENERIC": "Un problème est survenu", + "EXISTENT_SITE": "Cet ID de bibliothèque est indisponible. Essayez avec un autre ID de bibliothèque.", "CONFLICT": "Cet ID de Bibliothèque est déjà utilisé. Vérifiez la corbeille.", "ID_TOO_LONG": "L'URL doit contenir 72 caractères maximum", "DESCRIPTION_TOO_LONG": "La description doit contenir 512 caractères maximum", "TITLE_TOO_LONG": "Le titre doit contenir 256 caractères maximum", - "ILLEGAL_CHARACTERS": "Utilisez uniquement des chiffres et des lettres" + "ILLEGAL_CHARACTERS": "Utilisez uniquement des chiffres et des lettres", + "ONLY_SPACES": "Un nom de bibliothèque ne peut pas contenir uniquement des espaces", + "LIBRARY_UPDATE_ERROR": "Une erreur est survenue pendant la mise à jour des propriétés de la bibliothèque" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Les propriétés de la bibliothèque ont été mises à jour" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Rechercher", + "FILES": "Fichiers", + "FOLDERS": "Dossiers", + "LIBRARIES": "Bibliothèques", + "HINT": "L'élément de recherche doit comporter au moins 2 caractères alphanumériques." + }, "SORT": { "RELEVANCE": "Pertinence", "FILENAME": "Nom de fichier", diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index 1bbcc8c817..162d5c1949 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Versione:", + "PLUGINS": { + "TITLE": "Estensioni", + "ID": "ID", + "NAME": "Nome", + "VERSION": "Versione", + "VENDOR": "Fornitore", + "LICENSE": "Licenza", + "RUNTIME": "Runtime", + "DESCRIPTION": "Descrizione" + }, + "LICENSE": { + "TITLE": "Licenza", + "PROPERTY": "Proprietà", + "VALUE": "Valore" + }, + "STATUS": { + "TITLE": "Stato", + "PROPERTY": "Proprietà", + "VALUE": "Valore" + }, + "MODULES": { + "TITLE": "Moduli della piattaforma Alfresco", + "ID": "ID", + "NAME": "Nome", + "VERSION": "Versione" + }, + "PACKAGES": { + "TITLE": "Pacchetti", + "NAME": "Nome", + "VERSION": "Versione" + } + }, "LANGUAGE": "Lingua", "SIGN_IN": "Accedi", "SIGN_OUT": "Disconnetti", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Nuovo", - "TOOLTIP": "Aggiungi nuovi file o cartelle", + "TOOLTIP": "Aggiungi nuovi file o cartelle oppure crea una nuova raccolta di file", "MENU_ITEMS": { "CREATE_FOLDER": "Crea cartella", "UPLOAD_FILE": "Carica file", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Raccolte file", + "TITLE": "Le mie raccolte", + "DESCRIPTION": "Accedi alle raccolte di file", "SIDENAV_LINK": { "LABEL": "Raccolte file", - "TOOLTIP": "Accedi alle raccolte file" + "TOOLTIP": "Raccolte file" }, "EMPTY_STATE": { - "TITLE": "L'utente non è ancora membro di alcuna Raccolta file", - "TEXT": "Unire le librerie per caricare, visualizzare e condividere file." + "FILE_LIBRARIES": { + "TITLE": "L'utente non è ancora membro di alcuna Raccolta file", + "TEXT": "Unire le librerie per caricare, visualizzare e condividere file." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Nessuna raccolta preferita", + "TEXT": "Imposta una raccolta come preferita per trovarla facilmente in seguito." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Le mie raccolte", + "SIDENAV_LINK": { + "LABEL": "Le mie raccolte", + "TOOLTIP": "Accedi alle raccolte" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Raccolte preferite", + "SIDENAV_LINK": { + "LABEL": "Raccolte preferite", + "TOOLTIP": "Accedi alle raccolte preferite" + } + } } }, "SHARED": { @@ -73,7 +130,7 @@ }, "EMPTY_STATE": { "TITLE": "Nessun file recente", - "TEXT": "Gli elementi caricati o modificati negli ultimi 30 giorni vengono mostrati qui." + "TEXT": "Gli elementi caricati o modificati negli ultimi 30 giorni sono mostrati qui." } }, "FAVORITES": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Risultati della ricerca", "FOUND_RESULTS": "{{ number }} risultati trovati", + "FOUND_ONE_RESULT": "{{ number }} risultato trovato", "CUSTOM_ROW": { "MODIFIED": "Modificato", "LOCATION": "Località", "SIZE": "Dimensione" }, + "UNKNOWN_LOCATION": "Sconosciuto", "NO_RESULTS": "La ricerca ha restituito 0 risultati" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Raccolte trovate...", + "FOUND_RESULTS": "{{ number }} risultati", + "FOUND_ONE_RESULT": "{{ number }} risultato" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Permessi", "RESTORE": "Ripristina", "FAVORITE": "Preferito", + "ADD_FAVORITE": "Aggiungi preferito", + "REMOVE_FAVORITE": "Rimuovi preferito", "UNSHARE": "Rimuovi condivisione", "DETAILS": "Visualizza dettagli", "VERSIONS": "Gestione versioni", @@ -133,7 +199,10 @@ "SHARE": "Condividi", "SHARE_EDIT": "Impostazioni di collegamento condiviso", "PRINT": "Stampa", - "FULLSCREEN": "Attiva modalità schermo intero" + "FULLSCREEN": "Attiva modalità schermo intero", + "JOIN": "Partecipa", + "CANCEL_JOIN": "Annulla richiesta di partecipazione", + "LEAVE": "Esci dalla raccolta" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Questa operazione eliminerà in maniera permanente gli elementi selezionati.", "YES_LABEL": "Elimina", "NO_LABEL": "Salvare" + }, + "CONFIRM_LEAVE": { + "TITLE": "Uscire dalla raccolta?", + "MESSAGE": "L'uscita comporta la rimozione dei dati di accesso.", + "YES_LABEL": "OK", + "NO_LABEL": "Annulla" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Nome", "SIZE": "Dimensione", "MODIFIED_ON": "Modificato", "MODIFIED_BY": "Modificato da", - "STATUS": "Stato", + "VISIBILITY": "Visibilità", "TITLE": "Titolo", "LOCATION": "Località", "SHARED_BY": "Condiviso da", "DELETED_ON": "Eliminato", - "DELETED_BY": "Eliminato da" + "DELETED_BY": "Eliminato da", + "ROLE": "Il mio ruolo" }, "TOOLBAR": { "CARDVIEW": "Modalità di visualizzazione scheda", @@ -166,9 +243,16 @@ "MODERATED": "Moderato", "PRIVATE": "Privato" }, + "SITES_ROLE": { + "MANAGER": "Manager", + "COLLABORATOR": "Collaboratore", + "CONTRIBUTOR": "Contributore", + "CONSUMER": "Consumatore" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Il file o la cartella non esistono più o non si dispone delle autorizzazioni per visualizzarli.", + "CANNOT_NAVIGATE_LOCATION": "Impossibile aprire questo percorso", + "MISSING_CONTENT": "Questo elemento non esiste più o non si dispone delle autorizzazioni per visualizzarlo.", "GENERIC": "Azione non eseguita correttamente. Riprovare o contattare il team IT.", "CONFLICT": "Nome già in uso, provare un nome diverso.", "NODE_MOVE": "Spostamento non eseguito correttamente, file con lo stesso nome già esistente.", @@ -190,13 +274,21 @@ "GENERIC": "Si è verificato un problema durante il ripristino di {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Impossibile eliminare la raccolta" + "DELETE_LIBRARY_FAILED": "Impossibile eliminare la raccolta", + "JOIN_REQUEST_FAILED": "Impossibile partecipare alla raccolta", + "JOIN_CANCEL_FAILED": "Impossibile annullare la richiesta di partecipazione", + "LEAVE_LIBRARY_FAILED": "Impossibile uscire dalla raccolta", + "INVALID_SENDER_EMAIL": "L'indirizzo email deve essere valido prima della richiesta di accesso.", + "INVALID_RECEIVER_EMAIL": "Indirizzo email del destinatario non valido. Contattare l'IT." }, "UPLOAD": { "ERROR": { - "GENERIC": "Si è verificato un problema", + "GENERIC": "Caricamento non riuscito. Se il problema persiste, contattare l'IT", "CONFLICT": "Nuova versione non caricata. Esiste già un fil con lo stesso nome.", - "500": "Si è verificato un problema durante il caricamento" + "500": "Errore interno del server. Riprovare o contattare il supporto IT [500]", + "504": "Timeout del server. Riprovare o contattare il supporto IT [504]", + "403": "Autorizzazioni insufficienti per caricare in questa posizione [403]", + "404": "La posizione di caricamento non esiste più [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "Impossibile spostare {{ failed }}." } }, - "LIBRARY_DELETED": "Raccolta eliminata" + "LIBRARY_DELETED": "Raccolta eliminata", + "JOINED": "Partecipazione alla raccolta confermata", + "JOIN_REQUESTED": "Richiesta di partecipazione alla raccolta inviata", + "JOIN_CANCELED": "Richiesta di partecipazione alla raccolta annullata", + "LEFT_LIBRARY": "Sei uscito dalla raccolta" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Dettagli", "TABS": { "PROPERTIES": "Proprietà", + "LIBRARY_PROPERTIES": "Informazioni su", "VERSIONS": "Versione", "COMMENTS": "Commenti" } @@ -262,7 +359,8 @@ "TITLE": "Gestione autorizzazioni", "CLOSE": "Chiudi", "INHERIT_PERMISSIONS_BUTTON": "Eredita permessi", - "INHERITED_PERMISSIONS_BUTTON": "Permesso ereditato" + "INHERITED_PERMISSIONS_BUTTON": "Permesso ereditato", + "ADD_USER_OR_GROUP": "Aggiungi utente o gruppo" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Crea libreria", "CREATE": "Crea", + "UPDATE": "Aggiorna", + "EDIT": "Modifica", "CANCEL": "Annulla", "FORM": { "DESCRIPTION": "Descrizione", "SITE_ID": "ID libreria", - "NAME": "Nome" + "NAME": "Nome", + "VISIBILITY": "Visibilità" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Pubblico", "MODERATED": "Moderato" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Nome raccolta già utilizzato" + }, "ERRORS": { - "GENERIC": "Si è verificato un errore", - "EXISTENT_SITE": "L'ID libreria specificato non è disponibile. Provare con un'altra libreria.", + "GENERIC": "Si è verificato un problema", + "EXISTENT_SITE": "L'ID raccolta non è disponibile. Provare con un altro ID raccolta.", "CONFLICT": "L'ID libreria specificato è già in uso. Verificare il cestino.", "ID_TOO_LONG": "Utilizzare 72 caratteri o meno per il nome URL", "DESCRIPTION_TOO_LONG": "Utilizzare 512 o meno per la descrizione", "TITLE_TOO_LONG": "Utilizzare 256 caratteri o meno per il titolo", - "ILLEGAL_CHARACTERS": "Utilizzare solo lettere e numeri" + "ILLEGAL_CHARACTERS": "Utilizzare solo lettere e numeri", + "ONLY_SPACES": "Il nome della raccolta non può contenere solo spazi", + "LIBRARY_UPDATE_ERROR": "Si è verificato un errore di caricamento delle proprietà della raccolta" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Proprietà della raccolta aggiornate" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Cerca", + "FILES": "File", + "FOLDERS": "Cartelle", + "LIBRARIES": "Raccolte", + "HINT": "La stringa di ricerca deve contenere almeno 2 caratteri alfanumerici." + }, "SORT": { "RELEVANCE": "Pertinenza", "FILENAME": "Nome file", diff --git a/src/assets/i18n/ja.json b/src/assets/i18n/ja.json index c379de920d..bfff9b4fa1 100644 --- a/src/assets/i18n/ja.json +++ b/src/assets/i18n/ja.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "バージョン:", + "PLUGINS": { + "TITLE": "拡張機能", + "ID": "ID", + "NAME": "名前", + "VERSION": "バージョン", + "VENDOR": "ベンダー", + "LICENSE": "ライセンス", + "RUNTIME": "ランタイム", + "DESCRIPTION": "説明" + }, + "LICENSE": { + "TITLE": "ライセンス", + "PROPERTY": "プロパティ", + "VALUE": "値" + }, + "STATUS": { + "TITLE": "ステータス", + "PROPERTY": "プロパティ", + "VALUE": "値" + }, + "MODULES": { + "TITLE": "Alfresco プラットフォームのモジュール", + "ID": "ID", + "NAME": "名前", + "VERSION": "バージョン" + }, + "PACKAGES": { + "TITLE": "パッケージ", + "NAME": "名前", + "VERSION": "バージョン" + } + }, "LANGUAGE": "言語", "SIGN_IN": "サインイン", "SIGN_OUT": "サインアウト", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "新規", - "TOOLTIP": "新しいフィールドまたはフォルダを追加します", + "TOOLTIP": "新しいフィールドやフォルダを追加したり、新しいファイルライブラリを作成できます。", "MENU_ITEMS": { "CREATE_FOLDER": "フォルダの作成", "UPLOAD_FILE": "ファイルのアップロード", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "ファイルライブラリ", + "TITLE": "あなたのライブラリ", + "DESCRIPTION": "ファイルライブラリにアクセスします", "SIDENAV_LINK": { "LABEL": "ファイルライブラリ", - "TOOLTIP": "ファイルライブラリにアクセスします" + "TOOLTIP": "ファイルライブラリ" }, "EMPTY_STATE": { - "TITLE": "あなたはまだファイルライブラリのメンバーではありません", - "TEXT": "ファイルをアップロード、表示、共有するライブラリに参加してください" + "FILE_LIBRARIES": { + "TITLE": "あなたはまだファイルライブラリのメンバーではありません", + "TEXT": "ファイルをアップロード、表示、共有するライブラリに参加してください" + }, + "FAVORITE_LIBRARIES": { + "TITLE": "お気に入りのライブラリはありません", + "TEXT": "後で簡単に見つけられるようにライブラリをお気に入りに追加します。" + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "あなたのライブラリ", + "SIDENAV_LINK": { + "LABEL": "あなたのライブラリ", + "TOOLTIP": "ライブラリにアクセス" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "お気に入りのライブラリ", + "SIDENAV_LINK": { + "LABEL": "お気に入りのライブラリ", + "TOOLTIP": "お気に入りのライブラリにアクセス" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "検索結果", "FOUND_RESULTS": "{{ number }} 件見つかりました", + "FOUND_ONE_RESULT": "{{ number }} 件見つかりました", "CUSTOM_ROW": { "MODIFIED": "変更日", "LOCATION": "場所", "SIZE": "サイズ" }, + "UNKNOWN_LOCATION": "不明", "NO_RESULTS": "検索結果は 0 件です" + }, + "SEARCH_LIBRARIES": { + "TITLE": "ライブラリが見つかりました...", + "FOUND_RESULTS": "検索結果: {{ number }} 件", + "FOUND_ONE_RESULT": "検索結果: {{ number }} 件" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "権限", "RESTORE": "復元", "FAVORITE": "お気に入り", + "ADD_FAVORITE": "お気に入りに追加", + "REMOVE_FAVORITE": "お気に入りの削除", "UNSHARE": "共有の解除", "DETAILS": "詳細の表示", "VERSIONS": "バージョンの管理", @@ -133,7 +199,10 @@ "SHARE": "共有", "SHARE_EDIT": "共有リンクの設定", "PRINT": "印刷", - "FULLSCREEN": "全画面表示モードをアクティブにする" + "FULLSCREEN": "全画面表示モードをアクティブにする", + "JOIN": "参加", + "CANCEL_JOIN": "参加リクエストのキャンセル", + "LEAVE": "ライブラリから脱退" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "この操作を行うと、選択したアイテムが完全に削除されます。", "YES_LABEL": "削除", "NO_LABEL": "残す" + }, + "CONFIRM_LEAVE": { + "TITLE": "このライブラリから脱退しますか?", + "MESSAGE": "脱退すると、アクセス権が削除されます。", + "YES_LABEL": "OK", + "NO_LABEL": "キャンセル" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "名前", "SIZE": "サイズ", "MODIFIED_ON": "変更日", "MODIFIED_BY": "変更者", - "STATUS": "ステータス", + "VISIBILITY": "公開レベル", "TITLE": "タイトル", "LOCATION": "場所", "SHARED_BY": "共有者", "DELETED_ON": "削除日", - "DELETED_BY": "削除者" + "DELETED_BY": "削除者", + "ROLE": "あなたの役割" }, "TOOLBAR": { "CARDVIEW": "カード表示モード", @@ -166,9 +243,16 @@ "MODERATED": "条件付き公開", "PRIVATE": "非公開" }, + "SITES_ROLE": { + "MANAGER": "マネージャ", + "COLLABORATOR": "共同作業者", + "CONTRIBUTOR": "投稿者", + "CONSUMER": "利用者" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "このファイルまたはフォルダがなくなったか、表示する権限がありません。", + "CANNOT_NAVIGATE_LOCATION": "この場所は開けません", + "MISSING_CONTENT": "このアイテムは削除されたか、このアイテムを表示する権限がありません。", "GENERIC": "処理が失敗しました。もう一度操作をやり直すか、IT 担当者に連絡してください。", "CONFLICT": "この名前は既に使用されています。別の名前を使用してください。", "NODE_MOVE": "移動できません。同じ名前のファイルが既に存在します。", @@ -190,13 +274,21 @@ "GENERIC": "{{ name }} の復元中に問題が発生しました" } }, - "DELETE_LIBRARY_FAILED": "このライブラリは削除できません" + "DELETE_LIBRARY_FAILED": "このライブラリは削除できません", + "JOIN_REQUEST_FAILED": "ライブラリに参加できません", + "JOIN_CANCEL_FAILED": "参加リクエストをキャンセルできません", + "LEAVE_LIBRARY_FAILED": "このライブラリから脱退できません", + "INVALID_SENDER_EMAIL": "参加リクエストを行うには、有効な E メールアドレスが必要です。", + "INVALID_RECEIVER_EMAIL": "受取人の E メールアドレスが無効です。IT 担当者に連絡してください。" }, "UPLOAD": { "ERROR": { - "GENERIC": "問題が発生しました", + "GENERIC": "アップロードできませんでした。問題が解決しない場合は、IT 担当者に連絡してください。", "CONFLICT": "新しいバージョンはアップロードされていません。同じ名前のファイルが既に存在します", - "500": "アップロード中に問題が発生しました" + "500": "内部サーバーエラーが発生しました。もう一度操作をやり直すか、IT 担当者に連絡してください。 [500]", + "504": "サーバーがタイムアウトになりました。もう一度操作をやり直すか、IT 担当者に連絡してください。[504]", + "403": "この場所にアップロードするための権限がありません [403]", + "404": "アップロード場所は削除されました [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} 件を移動できませんでした。" } }, - "LIBRARY_DELETED": "ライブラリが削除されました" + "LIBRARY_DELETED": "ライブラリが削除されました", + "JOINED": "ライブラリに参加しました", + "JOIN_REQUESTED": "このライブラリへの参加リクエストが送信されました", + "JOIN_CANCELED": "ライブラリへの参加リクエストをキャンセルしました", + "LEFT_LIBRARY": "このライブラリからは脱退しています" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "詳細", "TABS": { "PROPERTIES": "プロパティ", + "LIBRARY_PROPERTIES": "バージョン情報", "VERSIONS": "バージョン", "COMMENTS": "コメント" } @@ -262,7 +359,8 @@ "TITLE": "権限の管理", "CLOSE": "閉じる", "INHERIT_PERMISSIONS_BUTTON": "権限の継承", - "INHERITED_PERMISSIONS_BUTTON": "継承された権限" + "INHERITED_PERMISSIONS_BUTTON": "継承された権限", + "ADD_USER_OR_GROUP": "ユーザー/グループの追加" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "ライブラリの作成", "CREATE": "作成", + "UPDATE": "更新", + "EDIT": "編集", "CANCEL": "キャンセル", "FORM": { "DESCRIPTION": "説明", "SITE_ID": "ライブラリ ID", - "NAME": "名前" + "NAME": "名前", + "VISIBILITY": "公開レベル" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "公開", "MODERATED": "条件付き公開" }, + "HINTS": { + "SITE_TITLE_EXISTS": "ライブラリ名は既に使用されています" + }, "ERRORS": { "GENERIC": "問題が発生しました", - "EXISTENT_SITE": "このライブラリ ID は使用できません。別のライブラリを使用してください。", + "EXISTENT_SITE": "このライブラリ ID は使用できません。別のライブラリ ID を使用してください。", "CONFLICT": "このライブラリ ID は既に使われています。ごみ箱をチェックしてください。", "ID_TOO_LONG": "URL 名は 72 文字以内で入力してください", "DESCRIPTION_TOO_LONG": "説明は 512 文字以内で入力してください", "TITLE_TOO_LONG": "タイトルは 256 文字以内で入力してください", - "ILLEGAL_CHARACTERS": "英数字だけを使用してください" + "ILLEGAL_CHARACTERS": "英数字だけを使用してください", + "ONLY_SPACES": "ライブラリ名にスペースだけを含めることはできません", + "LIBRARY_UPDATE_ERROR": "ライブラリのプロパティの更新中にエラーが発生しました" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "ライブラリのプロパティが更新されました" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "検索", + "FILES": "ファイル", + "FOLDERS": "フォルダ", + "LIBRARIES": "ライブラリ", + "HINT": "検索する語句は、2 文字以上の英数字で入力してください。" + }, "SORT": { "RELEVANCE": "関連性", "FILENAME": "ファイル名", diff --git a/src/assets/i18n/nb.json b/src/assets/i18n/nb.json index f3b0e8ab3e..df283f3c50 100644 --- a/src/assets/i18n/nb.json +++ b/src/assets/i18n/nb.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Versjon:", + "PLUGINS": { + "TITLE": "Tillegg", + "ID": "ID", + "NAME": "Navn", + "VERSION": "Versjon", + "VENDOR": "Leverandør", + "LICENSE": "Lisens", + "RUNTIME": "Kjøretid", + "DESCRIPTION": "Beskrivelse" + }, + "LICENSE": { + "TITLE": "Lisens", + "PROPERTY": "Egenskap", + "VALUE": "Verdi" + }, + "STATUS": { + "TITLE": "Status", + "PROPERTY": "Egenskap", + "VALUE": "Verdi" + }, + "MODULES": { + "TITLE": "Alfresco plattformmoduler", + "ID": "ID", + "NAME": "Navn", + "VERSION": "Versjon" + }, + "PACKAGES": { + "TITLE": "Pakker", + "NAME": "Navn", + "VERSION": "Versjon" + } + }, "LANGUAGE": "Språk", "SIGN_IN": "Logg på", "SIGN_OUT": "Logg ut", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Ny", - "TOOLTIP": "Legg til nye filer eller mapper", + "TOOLTIP": "Legg til nye filer og mapper, eller opprett et nytt filbibliotek", "MENU_ITEMS": { "CREATE_FOLDER": "Opprett mappe", "UPLOAD_FILE": "Last opp fil", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Filbiblioteker", + "TITLE": "Mine biblioteker", + "DESCRIPTION": "Gå til filbiblioteker", "SIDENAV_LINK": { "LABEL": "Filbiblioteker", - "TOOLTIP": "Gå til filbiblioteker" + "TOOLTIP": "Filbiblioteker" }, "EMPTY_STATE": { - "TITLE": "Du er ikke medlem av noen filbiblioteker ennå", - "TEXT": "Slutt deg til biblioteker for å laste opp, vise og dele filer." + "FILE_LIBRARIES": { + "TITLE": "Du er ikke medlem av noen filbiblioteker ennå", + "TEXT": "Slutt deg til biblioteker for å laste opp, vise og dele filer." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Ingen favorittbiblioteker", + "TEXT": "Legg et bibliotek til som favoritt, slik at du lett kan finne det senere." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Mine biblioteker", + "SIDENAV_LINK": { + "LABEL": "Mine biblioteker", + "TOOLTIP": "Gå inn i mine biblioteker" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Favorittbiblioteker", + "SIDENAV_LINK": { + "LABEL": "Favorittbiblioteker", + "TOOLTIP": "Gå inn i mine favorittbiblioteker" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Søkeresultater", "FOUND_RESULTS": "{{ number }} resultater funnet", + "FOUND_ONE_RESULT": "{{ number }} resultat funnet", "CUSTOM_ROW": { "MODIFIED": "Endret", "LOCATION": "Sted", "SIZE": "Størrelse" }, + "UNKNOWN_LOCATION": "Ukjent", "NO_RESULTS": "Søket ditt ga 0 resultater" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Biblioteker funnet...", + "FOUND_RESULTS": "{{ number }} resultater", + "FOUND_ONE_RESULT": "{{ number }} resultat" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Tillatelser", "RESTORE": "Gjenopprett", "FAVORITE": "Favoritt", + "ADD_FAVORITE": "Legg til favoritt", + "REMOVE_FAVORITE": "Fjern favoritt", "UNSHARE": "Opphev deling", "DETAILS": "Visningsdetaljer", "VERSIONS": "Administrer versjoner", @@ -133,7 +199,10 @@ "SHARE": "Del", "SHARE_EDIT": "Innstillinger for delt kobling", "PRINT": "Skriv ut", - "FULLSCREEN": "Aktiver fullskjermmodus" + "FULLSCREEN": "Aktiver fullskjermmodus", + "JOIN": "Bli med", + "CANCEL_JOIN": "Avbryt forespørsel om tilslutning", + "LEAVE": "Forlat bibliotek" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Dette vil slette valgte elementer for godt.", "YES_LABEL": "Slett", "NO_LABEL": "Behold" + }, + "CONFIRM_LEAVE": { + "TITLE": "Vil du forlate dette biblioteket?", + "MESSAGE": "Hvis du går ut, mister du tilgangen.", + "YES_LABEL": "OK", + "NO_LABEL": "Avbryt" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Navn", "SIZE": "Størrelse", "MODIFIED_ON": "Endret", "MODIFIED_BY": "Endret av", - "STATUS": "Status", + "VISIBILITY": "Synlighet", "TITLE": "Tittel", "LOCATION": "Sted", "SHARED_BY": "Delt av", "DELETED_ON": "Slettet", - "DELETED_BY": "Slettet av" + "DELETED_BY": "Slettet av", + "ROLE": "Min rolle" }, "TOOLBAR": { "CARDVIEW": "Kortvisningsmodus", @@ -166,9 +243,16 @@ "MODERATED": "Moderert", "PRIVATE": "Privat" }, + "SITES_ROLE": { + "MANAGER": "Administrator", + "COLLABORATOR": "Medarbeider", + "CONTRIBUTOR": "Bidragsyter", + "CONSUMER": "Forbruker" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Denne filen eller mappen finnes ikke lenger, eller du har ikke tillatelse til å se den.", + "CANNOT_NAVIGATE_LOCATION": "Kan ikke åpne dette stedet", + "MISSING_CONTENT": "Dette elementet finnes ikke lenger, eller du har ikke tillatelse til å se det.", "GENERIC": "Handlingen var mislykket. Prøv på nytt, eller kontakt IT-teamet.", "CONFLICT": "Dette navnet er allerede i bruk, prøv et annet navn.", "NODE_MOVE": "Mislykket flytting, det finnes allerede en fil med samme navn.", @@ -190,13 +274,21 @@ "GENERIC": "Problem med å gjenopprette {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Kan ikke slette biblioteket" + "DELETE_LIBRARY_FAILED": "Kan ikke slette biblioteket", + "JOIN_REQUEST_FAILED": "Kan ikke slutte deg til biblioteket", + "JOIN_CANCEL_FAILED": "Kan ikke avbryte forespørselen din om tilslutning", + "LEAVE_LIBRARY_FAILED": "Kan ikke forlate dette biblioteket", + "INVALID_SENDER_EMAIL": "E-postadressen din må være gyldig før du ber om tilslutning.", + "INVALID_RECEIVER_EMAIL": "Mottakeres e-postadresse er ikke gyldig – kontakt IT." }, "UPLOAD": { "ERROR": { - "GENERIC": "Det oppstod et problem", + "GENERIC": "Opplasting mislyktes. Kontakt IT om dette problemet vedvarer", "CONFLICT": "Ny versjon er ikke lastet opp, en annen fil med samme navn finnes alt", - "500": "Det oppstod et problem under opplasting" + "500": "Intern serverfeil. Prøv på nytt eller kontakt IT-støtten [500]", + "504": "Serveren ble tidsavbrutt. Prøv på nytt eller kontakt IT-støtten [504]", + "403": "Ikke tilstrekkelige tillatelser til å laste opp på dette stedet [403]", + "404": "Opplastingsstedet finnes ikke lenger [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} kunne ikke flyttes." } }, - "LIBRARY_DELETED": "Bibliotek slettet" + "LIBRARY_DELETED": "Bibliotek slettet", + "JOINED": "Bibliotek tilsluttet", + "JOIN_REQUESTED": "Forespørsel sendt om tilslutning til dette biblioteket", + "JOIN_CANCELED": "Forespørselen om tilslutning til biblioteket er avbrutt", + "LEFT_LIBRARY": "Du har forlatt biblioteket" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Detaljer", "TABS": { "PROPERTIES": "Egenskaper", + "LIBRARY_PROPERTIES": "Om", "VERSIONS": "Versjoner", "COMMENTS": "Kommentarer" } @@ -261,8 +358,9 @@ "DIALOG": { "TITLE": "Administrer tillatelser", "CLOSE": "Lukk", - "INHERIT_PERMISSIONS_BUTTON": "Arv tillatelse", - "INHERITED_PERMISSIONS_BUTTON": "Tillatelse arvet" + "INHERIT_PERMISSIONS_BUTTON": "Arv tillatelser", + "INHERITED_PERMISSIONS_BUTTON": "Tillatelser arvet", + "ADD_USER_OR_GROUP": "Legg til bruker eller gruppe" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Opprett bibliotek", "CREATE": "Opprett", + "UPDATE": "Oppdater", + "EDIT": "Rediger", "CANCEL": "Avbryt", "FORM": { "DESCRIPTION": "Beskrivelse", "SITE_ID": "Biblioteks-ID", - "NAME": "Navn" + "NAME": "Navn", + "VISIBILITY": "Synlighet" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Offentlig", "MODERATED": "Moderert" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Biblioteknavn er allerede i bruk" + }, "ERRORS": { "GENERIC": "Vi støtte på et problem", - "EXISTENT_SITE": "Denne biblioteks-ID-en er ikke tilgjengelig. Prøv et annet bibliotek.", + "EXISTENT_SITE": "Denne biblioteks-ID-en er ikke tilgjengelig. Prøv en annen biblioteks-ID.", "CONFLICT": "Denne biblioteks-ID-en er alt brukt. Sjekk papirkurven.", "ID_TOO_LONG": "Bruk 72 tegn eller mindre i URL-navnet", "DESCRIPTION_TOO_LONG": "Bruk 512 tegn eller mindre i beskrivelsen", "TITLE_TOO_LONG": "Bruk 256 tegn eller mindre i tittelen", - "ILLEGAL_CHARACTERS": "Bruk kun tall og bokstaver" + "ILLEGAL_CHARACTERS": "Bruk kun tall og bokstaver", + "ONLY_SPACES": "Biblioteknavn kan ikke inneholde bare mellomrom", + "LIBRARY_UPDATE_ERROR": "Det oppstod en feil under oppdatering av bibliotekegenskaper" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Bibliotekegenskaper oppdatert" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Søk", + "FILES": "Filer", + "FOLDERS": "Mapper", + "LIBRARIES": "Biblioteker", + "HINT": "Inndata for søk må ha minst 2 alfanumeriske tegn." + }, "SORT": { "RELEVANCE": "Relevans", "FILENAME": "Filnavn", diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json index a61a791871..911747adbb 100644 --- a/src/assets/i18n/nl.json +++ b/src/assets/i18n/nl.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Versie:", + "PLUGINS": { + "TITLE": "Extensies", + "ID": "Id", + "NAME": "Naam", + "VERSION": "Versie", + "VENDOR": "Leverancier", + "LICENSE": "Licentie", + "RUNTIME": "Runtime", + "DESCRIPTION": "Beschrijving" + }, + "LICENSE": { + "TITLE": "Licentie", + "PROPERTY": "Eigenschap", + "VALUE": "Waarde" + }, + "STATUS": { + "TITLE": "Status", + "PROPERTY": "Eigenschap", + "VALUE": "Waarde" + }, + "MODULES": { + "TITLE": "Alfresco-platformmodules", + "ID": "Id", + "NAME": "Naam", + "VERSION": "Versie" + }, + "PACKAGES": { + "TITLE": "Pakketten", + "NAME": "Naam", + "VERSION": "Versie" + } + }, "LANGUAGE": "Taal", "SIGN_IN": "Aanmelden", "SIGN_OUT": "Afmelden", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Nieuw", - "TOOLTIP": "Nieuwe bestanden of mappen toevoegen", + "TOOLTIP": "Nieuwe bestanden en mappen toevoegen, of een nieuwe bestandsbibliotheek maken", "MENU_ITEMS": { "CREATE_FOLDER": "Map maken", "UPLOAD_FILE": "Bestand uploaden", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Bestandsbibliotheken", + "TITLE": "Mijn bibliotheken", + "DESCRIPTION": "Toegang krijgen tot bestandsbibliotheken", "SIDENAV_LINK": { "LABEL": "Bestandsbibliotheken", - "TOOLTIP": "Toegang krijgen tot bestandsbibliotheken" + "TOOLTIP": "Bestandsbibliotheken" }, "EMPTY_STATE": { - "TITLE": "U bent nog geen lid van een bestandsbibliotheek", - "TEXT": "Word lid van bibliotheken om bestanden te uploaden, weer te geven en te delen." + "FILE_LIBRARIES": { + "TITLE": "U bent nog geen lid van een bestandsbibliotheek", + "TEXT": "Word lid van bibliotheken om bestanden te uploaden, weer te geven en te delen." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Geen favoriete bibliotheken", + "TEXT": "Voeg een bibliotheek toe aan uw favorieten zodat u deze later gemakkelijk kunt vinden." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Mijn bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Mijn bibliotheken", + "TOOLTIP": "Toegang krijgen tot mijn bibliotheken" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Favoriete bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Favoriete bibliotheken", + "TOOLTIP": "Toegang krijgen tot mijn favoriete bibliotheken" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Zoekresultaten", "FOUND_RESULTS": "{{ number }} resultaten gevonden", + "FOUND_ONE_RESULT": "{{ number }} resultaat gevonden", "CUSTOM_ROW": { "MODIFIED": "Aangepast", "LOCATION": "Locatie", "SIZE": "Grootte" }, + "UNKNOWN_LOCATION": "Onbekend", "NO_RESULTS": "Uw zoekopdracht heeft 0 resultaten opgeleverd" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Bibliotheken gevonden...", + "FOUND_RESULTS": "{{ number }} resultaten", + "FOUND_ONE_RESULT": "{{ number }} resultaat" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Rechten", "RESTORE": "Herstellen", "FAVORITE": "Favoriet", + "ADD_FAVORITE": "Favoriet toevoegen", + "REMOVE_FAVORITE": "Favoriet verwijderen", "UNSHARE": "Delen ongedaan maken", "DETAILS": "Details weergeven", "VERSIONS": "Versies beheren", @@ -133,7 +199,10 @@ "SHARE": "Delen", "SHARE_EDIT": "Instellingen gedeelde koppeling", "PRINT": "Afdrukken", - "FULLSCREEN": "Modus voor volledig scherm activeren" + "FULLSCREEN": "Modus voor volledig scherm activeren", + "JOIN": "Lid worden", + "CANCEL_JOIN": "Verzoek om lid te worden annuleren", + "LEAVE": "Bibliotheek verlaten" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Hiermee worden de geselecteerde items permanent verwijderd.", "YES_LABEL": "Verwijderen", "NO_LABEL": "Bewaren" + }, + "CONFIRM_LEAVE": { + "TITLE": "Deze bibliotheek verlaten?", + "MESSAGE": "Als u de bibliotheek verlaat, wordt uw toegang verwijderd.", + "YES_LABEL": "OK", + "NO_LABEL": "Annuleren" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "Id", "NAME": "Naam", "SIZE": "Grootte", "MODIFIED_ON": "Aangepast", "MODIFIED_BY": "Aangepast door", - "STATUS": "Status", + "VISIBILITY": "Zichtbaarheid", "TITLE": "Titel", "LOCATION": "Locatie", "SHARED_BY": "Gedeeld door", "DELETED_ON": "Verwijderd", - "DELETED_BY": "Verwijderd door" + "DELETED_BY": "Verwijderd door", + "ROLE": "Mijn rol" }, "TOOLBAR": { "CARDVIEW": "Kaartweergavemodus", @@ -166,9 +243,16 @@ "MODERATED": "Onder toezicht", "PRIVATE": "Privé" }, + "SITES_ROLE": { + "MANAGER": "Beheerder", + "COLLABORATOR": "Medewerker", + "CONTRIBUTOR": "Bijdrager", + "CONSUMER": "Consument" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Dit bestand of deze map bestaat niet meer of u hebt geen rechten om het bestand of de map weer te geven.", + "CANNOT_NAVIGATE_LOCATION": "Kan deze locatie niet openen", + "MISSING_CONTENT": "Dit item bestaat niet meer of u hebt geen rechten om het weer te geven.", "GENERIC": "De actie is mislukt. Probeer het opnieuw of neem contact op met het IT-team.", "CONFLICT": "Deze naam wordt al gebruikt, probeer een andere naam.", "NODE_MOVE": "Verplaatsen is mislukt, er bestaat al een bestand met dezelfde naam.", @@ -190,13 +274,21 @@ "GENERIC": "Er is een probleem opgetreden bij het herstellen van {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "De bibliotheek kan niet worden verwijderd" + "DELETE_LIBRARY_FAILED": "De bibliotheek kan niet worden verwijderd", + "JOIN_REQUEST_FAILED": "Lid worden van de bibliotheek niet mogelijk", + "JOIN_CANCEL_FAILED": "Verzoek om lid te worden kan niet worden geannuleerd", + "LEAVE_LIBRARY_FAILED": "Kan deze bibliotheek niet verlaten", + "INVALID_SENDER_EMAIL": "U moet een geldig e-mailadres hebben voordat u kunt verzoeken om lid te worden.", + "INVALID_RECEIVER_EMAIL": "Het e-mailadres van de ontvanger(s) is niet geldig, neem contact op met IT." }, "UPLOAD": { "ERROR": { - "GENERIC": "Er is een probleem opgetreden", + "GENERIC": "Uploaden is mislukt. Neem contact op met IT als het probleem zich blijft voordoen", "CONFLICT": "Nieuwe versie is niet geüpload, er bestaat al een ander bestand met dezelfde naam", - "500": "Er is een probleem opgetreden tijdens het uploaden" + "500": "Interne serverfout, probeer het opnieuw of neem contact op met de IT-ondersteuning [500]", + "504": "Er is een time-out opgetreden in de server, probeer het opnieuw of neem contact op met de IT-ondersteuning [504]", + "403": "Onvoldoende rechten voor uploaden op deze locatie [403]", + "404": "Uploadlocatie bestaat niet meer [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "Kan {{ failed }} niet verplaatsen." } }, - "LIBRARY_DELETED": "Bibliotheek verwijderd" + "LIBRARY_DELETED": "Bibliotheek verwijderd", + "JOINED": "Lid geworden van bibliotheek", + "JOIN_REQUESTED": "Verzoek om lid te worden van deze bibliotheek is verzonden", + "JOIN_CANCELED": "Verzoek om lid te worden van de bibliotheek is geannuleerd", + "LEFT_LIBRARY": "U hebt de bibliotheek verlaten" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Details", "TABS": { "PROPERTIES": "Eigenschappen", + "LIBRARY_PROPERTIES": "Info", "VERSIONS": "Versies", "COMMENTS": "Opmerkingen" } @@ -261,8 +358,9 @@ "DIALOG": { "TITLE": "Rechten beheren", "CLOSE": "Sluiten", - "INHERIT_PERMISSIONS_BUTTON": "Machtiging overnemen", - "INHERITED_PERMISSIONS_BUTTON": "Machtiging overgenomen" + "INHERIT_PERMISSIONS_BUTTON": "Rechten overnemen", + "INHERITED_PERMISSIONS_BUTTON": "Overgenomen rechten", + "ADD_USER_OR_GROUP": "Gebruiker of groep toevoegen" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Bibliotheek maken", "CREATE": "Maken", + "UPDATE": "Bijwerken", + "EDIT": "Bewerken", "CANCEL": "Annuleren", "FORM": { "DESCRIPTION": "Beschrijving", "SITE_ID": "Bibliotheek-id", - "NAME": "Naam" + "NAME": "Naam", + "VISIBILITY": "Zichtbaarheid" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Openbaar", "MODERATED": "Onder toezicht" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Bibliotheeknaam wordt al gebruikt" + }, "ERRORS": { "GENERIC": "Er is een probleem opgetreden", - "EXISTENT_SITE": "Deze bibliotheek-id is niet beschikbaar. Probeer een andere bibliotheek.", + "EXISTENT_SITE": "Deze bibliotheek-id is niet beschikbaar. Probeer een andere bibliotheek-id.", "CONFLICT": "Deze bibliotheek wordt al gebruikt. Controleer de prullenbak.", "ID_TOO_LONG": "Gebruik 72 tekens of minder voor de URL-naam", "DESCRIPTION_TOO_LONG": "Gebruik 512 tekens of minder voor de beschrijving", "TITLE_TOO_LONG": "Gebruik 256 tekens of minder voor de titel", - "ILLEGAL_CHARACTERS": "Gebruik alleen cijfers en letters" + "ILLEGAL_CHARACTERS": "Gebruik alleen cijfers en letters", + "ONLY_SPACES": "Bibliotheeknaam mag niet alleen uit spaties bestaan", + "LIBRARY_UPDATE_ERROR": "Er is een fout opgetreden bij het bijwerken van de bibliotheekeigenschappen" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Bibliotheekeigenschappen bijgewerkt" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Zoeken", + "FILES": "Bestanden", + "FOLDERS": "Mappen", + "LIBRARIES": "Bibliotheken", + "HINT": "Invoer voor een zoekopdracht moet uit minstens 2 alfanumerieke tekens bestaan." + }, "SORT": { "RELEVANCE": "Relevantie", "FILENAME": "Bestandsnaam", diff --git a/src/assets/i18n/pt-BR.json b/src/assets/i18n/pt-BR.json index 04cc1eadca..e8b2cdbeed 100644 --- a/src/assets/i18n/pt-BR.json +++ b/src/assets/i18n/pt-BR.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Versão:", + "PLUGINS": { + "TITLE": "Extensões", + "ID": "ID", + "NAME": "Nome", + "VERSION": "Versão", + "VENDOR": "Fornecedor", + "LICENSE": "Licença", + "RUNTIME": "Tempo de execução", + "DESCRIPTION": "Descrição" + }, + "LICENSE": { + "TITLE": "Licença", + "PROPERTY": "Propriedade", + "VALUE": "Valor" + }, + "STATUS": { + "TITLE": "Status", + "PROPERTY": "Propriedade", + "VALUE": "Valor" + }, + "MODULES": { + "TITLE": "Módulos da plataforma Alfresco", + "ID": "ID", + "NAME": "Nome", + "VERSION": "Versão" + }, + "PACKAGES": { + "TITLE": "Pacotes", + "NAME": "Nome", + "VERSION": "Versão" + } + }, "LANGUAGE": "Idioma", "SIGN_IN": "Entrar", "SIGN_OUT": "Sair", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "Novo", - "TOOLTIP": "Adicionar novos arquivos ou pastas", + "TOOLTIP": "Adicionar novos arquivos e pastas ou criar uma Biblioteca de Arquivos", "MENU_ITEMS": { "CREATE_FOLDER": "Criar pasta", "UPLOAD_FILE": "Carregar arquivo", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Bibliotecas de arquivos", + "TITLE": "Minhas bibliotecas", + "DESCRIPTION": "Acessar bibliotecas de arquivos", "SIDENAV_LINK": { "LABEL": "Bibliotecas de arquivos", - "TOOLTIP": "Acessar bibliotecas de arquivos" + "TOOLTIP": "Bibliotecas de arquivos" }, "EMPTY_STATE": { - "TITLE": "Você ainda não é membro de nenhuma biblioteca de arquivos", - "TEXT": "Acesse as bibliotecas para carregar, visualizar e compartilhar arquivos." + "FILE_LIBRARIES": { + "TITLE": "Você ainda não é membro de nenhuma biblioteca de arquivos", + "TEXT": "Acesse as bibliotecas para carregar, visualizar e compartilhar arquivos." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Nenhuma biblioteca favorita", + "TEXT": "Adicione uma biblioteca como favorita para facilitar o acesso a ela." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Minhas bibliotecas", + "SIDENAV_LINK": { + "LABEL": "Minhas bibliotecas", + "TOOLTIP": "Acessar minhas bibliotecas" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Bibliotecas favoritas", + "SIDENAV_LINK": { + "LABEL": "Bibliotecas favoritas", + "TOOLTIP": "Acessar minhas bibliotecas favoritas" + } + } } }, "SHARED": { @@ -73,7 +130,7 @@ }, "EMPTY_STATE": { "TITLE": "Não há nenhum arquivo recente", - "TEXT": "Os itens que você carregar ou editar nos últimos 30 dias são exibidos aqui." + "TEXT": "Os itens que você carregou ou editou nos últimos 30 dias são exibidos aqui." } }, "FAVORITES": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Resultados da pesquisa", "FOUND_RESULTS": "{{ number }} resultados encontrados", + "FOUND_ONE_RESULT": "{{ number }} resultados encontrados", "CUSTOM_ROW": { "MODIFIED": "Modificado", "LOCATION": "Localização", "SIZE": "Tamanho" }, + "UNKNOWN_LOCATION": "Desconhecido", "NO_RESULTS": "Sua pesquisa retornou 0 resultados" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Bibliotecas encontradas...", + "FOUND_RESULTS": "{{ number }} resultados", + "FOUND_ONE_RESULT": "{{ number }} resultados" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Permissões", "RESTORE": "Restaurar", "FAVORITE": "Favoritos", + "ADD_FAVORITE": "Adicionar favorito", + "REMOVE_FAVORITE": "Remover favorito", "UNSHARE": "Descompartilhar", "DETAILS": "Exibir detalhes", "VERSIONS": "Gerenciar versões", @@ -133,7 +199,10 @@ "SHARE": "Compartilhar", "SHARE_EDIT": "Configurações de links compartilhados", "PRINT": "Imprimir", - "FULLSCREEN": "Ativar modo de tela cheia" + "FULLSCREEN": "Ativar modo de tela cheia", + "JOIN": "Ingressar", + "CANCEL_JOIN": "Cancelar solicitação de ingressar", + "LEAVE": "Sair da biblioteca" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Essa ação excluirá permanentemente os itens selecionados.", "YES_LABEL": "Excluir", "NO_LABEL": "Manter" + }, + "CONFIRM_LEAVE": { + "TITLE": "Sair desta biblioteca?", + "MESSAGE": "Sair removerá o seu acesso.", + "YES_LABEL": "OK", + "NO_LABEL": "Cancelar" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "Nome", "SIZE": "Tamanho", "MODIFIED_ON": "Modificado", "MODIFIED_BY": "Modificado por", - "STATUS": "Status", + "VISIBILITY": "Visibilidade", "TITLE": "Título", "LOCATION": "Localização", "SHARED_BY": "Compartilhado por", "DELETED_ON": "Excluído", - "DELETED_BY": "Excluído por" + "DELETED_BY": "Excluído por", + "ROLE": "Meu papel" }, "TOOLBAR": { "CARDVIEW": "Modo de visualização de cartão", @@ -166,9 +243,16 @@ "MODERATED": "Moderado", "PRIVATE": "Privado" }, + "SITES_ROLE": { + "MANAGER": "Gerente", + "COLLABORATOR": "Colaborador", + "CONTRIBUTOR": "Contribuidor", + "CONSUMER": "Consumidor" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Este arquivo ou pasta não existe, ou você não tem permissão de visualização.", + "CANNOT_NAVIGATE_LOCATION": "Não foi possível abrir este local", + "MISSING_CONTENT": "Este item não existe, ou você não tem permissão de visualização.", "GENERIC": "A ação não teve êxito. Tente novamente ou entre em contato com a Equipe de TI.", "CONFLICT": "Este nome já está em uso, tente outro nome.", "NODE_MOVE": "Falha ao mover, já existe um arquivo com o mesmo nome.", @@ -190,13 +274,21 @@ "GENERIC": "Houve um problema ao restaurar {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Não foi possível apagar a biblioteca" + "DELETE_LIBRARY_FAILED": "Não foi possível apagar a biblioteca", + "JOIN_REQUEST_FAILED": "Não foi possível ingressar na biblioteca", + "JOIN_CANCEL_FAILED": "Não foi possível cancelar a solicitação de ingressar", + "LEAVE_LIBRARY_FAILED": "Não foi possível sair desta biblioteca", + "INVALID_SENDER_EMAIL": "Você deve ter um endereço de e-mail válido para solicitar a entrada.", + "INVALID_RECEIVER_EMAIL": "O endereço de e-mail do(s) destinatário(s) não é válido, entre em contato com a TI." }, "UPLOAD": { "ERROR": { - "GENERIC": "Houve um problema", + "GENERIC": "Não foi possível carregar. Se o problema persistir, entre em contato com a TI", "CONFLICT": "Nova versão não carregada, já existe um arquivo com o mesmo nome", - "500": "Houve um problema ao carregar" + "500": "Erro interno no servidor. Tente novamente ou entre em contato com a TI [500]", + "504": "Tempo expirado no servidor, tente novamente ou entre em contato com o suporte de TI [504]", + "403": "Permissões insuficientes para carregar neste local [403]", + "404": "O local para carregar não existe mais [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "Não foi possível mover {{ failed }}." } }, - "LIBRARY_DELETED": "Biblioteca apagada" + "LIBRARY_DELETED": "Biblioteca apagada", + "JOINED": "Ingressou na biblioteca", + "JOIN_REQUESTED": "Solicitação enviada para ingressar nesta biblioteca", + "JOIN_CANCELED": "Cancelou a solicitação de ingressar na biblioteca", + "LEFT_LIBRARY": "Você saiu da biblioteca" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Detalhes", "TABS": { "PROPERTIES": "Propriedades", + "LIBRARY_PROPERTIES": "Sobre", "VERSIONS": "Versões", "COMMENTS": "Comentários" } @@ -259,10 +356,11 @@ }, "PERMISSIONS": { "DIALOG": { - "TITLE": "Gerenciar Permissões", + "TITLE": "Gerenciar permissões", "CLOSE": "Fechar", - "INHERIT_PERMISSIONS_BUTTON": "Herdar permissão", - "INHERITED_PERMISSIONS_BUTTON": "Permissão herdada" + "INHERIT_PERMISSIONS_BUTTON": "Herdar permissões", + "INHERITED_PERMISSIONS_BUTTON": "Permissões herdadas", + "ADD_USER_OR_GROUP": "Adicionar usuário ou grupo" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Criar Biblioteca", "CREATE": "Criar", + "UPDATE": "Atualizar", + "EDIT": "Editar", "CANCEL": "Cancelar", "FORM": { "DESCRIPTION": "Descrição", "SITE_ID": "ID de Biblioteca", - "NAME": "Nome" + "NAME": "Nome", + "VISIBILITY": "Visibilidade" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Público", "MODERATED": "Moderado" }, + "HINTS": { + "SITE_TITLE_EXISTS": "O nome da biblioteca já está em uso" + }, "ERRORS": { - "GENERIC": "Ocorreu um erro", - "EXISTENT_SITE": "Este ID de Biblioteca não está disponível. Tente uma Biblioteca diferente.", + "GENERIC": "Encontramos um problema", + "EXISTENT_SITE": "Este ID de biblioteca não está disponível. Tente um ID de biblioteca diferente.", "CONFLICT": "Este ID de Biblioteca já foi usado. Verifique a lixeira.", "ID_TOO_LONG": "Use 72 caracteres ou menos para o nome da URL", "DESCRIPTION_TOO_LONG": "Use 512 caracteres ou menos para a descrição", "TITLE_TOO_LONG": "Use 256 caracteres ou menos para o título", - "ILLEGAL_CHARACTERS": "Use somente números e letras" + "ILLEGAL_CHARACTERS": "Use somente números e letras", + "ONLY_SPACES": "O nome da biblioteca não pode conter somente espaços", + "LIBRARY_UPDATE_ERROR": "Houve um erro ao atualizar as propriedades da biblioteca" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Propriedades da biblioteca atualizadas" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Pesquisar", + "FILES": "Arquivos", + "FOLDERS": "Pastas", + "LIBRARIES": "Bibliotecas", + "HINT": "O termo da pesquisa deve ter no mínimo 2 caracteres alfanuméricos." + }, "SORT": { "RELEVANCE": "Relevância", "FILENAME": "Nome do arquivo", diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json index b1ed916f10..b0aa652d2a 100644 --- a/src/assets/i18n/ru.json +++ b/src/assets/i18n/ru.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "Версия:", + "PLUGINS": { + "TITLE": "Расширения", + "ID": "Идентификатор", + "NAME": "Имя", + "VERSION": "Версия", + "VENDOR": "Поставщик", + "LICENSE": "Лицензия", + "RUNTIME": "Среда выполнения", + "DESCRIPTION": "Описание" + }, + "LICENSE": { + "TITLE": "Лицензия", + "PROPERTY": "Свойство", + "VALUE": "Значение" + }, + "STATUS": { + "TITLE": "Статус", + "PROPERTY": "Свойство", + "VALUE": "Значение" + }, + "MODULES": { + "TITLE": "Модули платформы Alfresco", + "ID": "Идентификатор", + "NAME": "Имя", + "VERSION": "Версия" + }, + "PACKAGES": { + "TITLE": "Пакеты", + "NAME": "Имя", + "VERSION": "Версия" + } + }, "LANGUAGE": "Язык", "SIGN_IN": "Войти", "SIGN_OUT": "Выйти", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "Библиотеки файлов", + "TITLE": "Мои библиотеки", + "DESCRIPTION": "Библиотеки файлов доступа", "SIDENAV_LINK": { "LABEL": "Библиотеки файлов", - "TOOLTIP": "Получить доступ к библиотекам файлов" + "TOOLTIP": "Библиотеки файлов" }, "EMPTY_STATE": { - "TITLE": "Вы пока не являетесь участником ни одной файловой библиотеки", - "TEXT": "Станьте участником библиотеки, чтобы получить возможность загружать и просматривать и файлы, а также предоставлять к ним общий доступ." + "FILE_LIBRARIES": { + "TITLE": "Вы пока не являетесь участником ни одной файловой библиотеки", + "TEXT": "Станьте участником библиотеки, чтобы получить возможность загружать и просматривать файлы, а также предоставлять к ним общий доступ." + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Нет избранных библиотек", + "TEXT": "Добавляйте библиотеки в избранные, чтобы быстро находить их, когда они понадобятся снова." + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "Библиотеки файлов", + "SIDENAV_LINK": { + "LABEL": "Библиотеки файлов", + "TOOLTIP": "Доступ к моим библиотекам" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "Избранные библиотеки", + "SIDENAV_LINK": { + "LABEL": "Избранные библиотеки", + "TOOLTIP": "Доступ к моим избранным библиотекам" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "Результаты поиска", "FOUND_RESULTS": "Найдено результатов: {{ number }}", + "FOUND_ONE_RESULT": "Найден результат: {{ number }}", "CUSTOM_ROW": { "MODIFIED": "Изменено", "LOCATION": "Местоположение", "SIZE": "Размер" }, + "UNKNOWN_LOCATION": "Неизвестно", "NO_RESULTS": "По вашему запросу ничего не найдено" + }, + "SEARCH_LIBRARIES": { + "TITLE": "Найденные библиотеки...", + "FOUND_RESULTS": "Результатов: {{ number }}", + "FOUND_ONE_RESULT": "Результат: {{ number }}" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "Права", "RESTORE": "Восстановить", "FAVORITE": "Избранное", + "ADD_FAVORITE": "Добавить в избранное", + "REMOVE_FAVORITE": "Удалить из избранного", "UNSHARE": "Снять с публикации", "DETAILS": "Подробно", "VERSIONS": "Управление версиями", @@ -133,7 +199,10 @@ "SHARE": "Открыть доступ", "SHARE_EDIT": "Настройки ссылки общего доступа", "PRINT": "Печатать", - "FULLSCREEN": "Включить полноэкранный режим" + "FULLSCREEN": "Включить полноэкранный режим", + "JOIN": "Присоединиться", + "CANCEL_JOIN": "Отменить запрос на присоединение", + "LEAVE": "Покинуть библиотеку" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "Это безвозвратно удалит отмеченные объекты.", "YES_LABEL": "Удалить", "NO_LABEL": "Оставить" + }, + "CONFIRM_LEAVE": { + "TITLE": "Покинуть эту библиотеку?", + "MESSAGE": "Покинув библиотеку, вы потеряете к ней доступ.", + "YES_LABEL": "OK", + "NO_LABEL": "Отмена" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "Идентификатор", "NAME": "Имя", "SIZE": "Размер", "MODIFIED_ON": "Изменено", "MODIFIED_BY": "Изменено пользователем", - "STATUS": "Статус", + "VISIBILITY": "Видимость", "TITLE": "Название", "LOCATION": "Местоположение", "SHARED_BY": "Доступ открыт пользователем", "DELETED_ON": "Удаленные", - "DELETED_BY": "Удалено пользователем" + "DELETED_BY": "Удалено пользователем", + "ROLE": "Моя роль" }, "TOOLBAR": { "CARDVIEW": "Режим просмотра карты", @@ -166,9 +243,16 @@ "MODERATED": "Модерируемый", "PRIVATE": "Частный" }, + "SITES_ROLE": { + "MANAGER": "Менеджер", + "COLLABORATOR": "Редактор", + "CONTRIBUTOR": "Писатель", + "CONSUMER": "Читатель" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "Этот файл или папка больше не существует, или у вас нет разрешения на просмотр.", + "CANNOT_NAVIGATE_LOCATION": "Не удается открыть это местоположение", + "MISSING_CONTENT": "Этот элемент или больше не существует, или у вас нет разрешения на просмотр.", "GENERIC": "Действие не выполнено. Повторите попытку или обратитесь к IT-специалистам.", "CONFLICT": "Это имя уже используется, попробуйте другое.", "NODE_MOVE": "Перемещение не выполнено, файл с таким же именем уже существует.", @@ -190,13 +274,21 @@ "GENERIC": "Возникла проблема с восстановлением {{ name }}" } }, - "DELETE_LIBRARY_FAILED": "Не удается удалить библиотеку" + "DELETE_LIBRARY_FAILED": "Не удается удалить библиотеку", + "JOIN_REQUEST_FAILED": "Не удается присоединиться к библиотеке", + "JOIN_CANCEL_FAILED": "Не удается отменить запрос на присоединение", + "LEAVE_LIBRARY_FAILED": "Не удается покинуть эту библиотеку", + "INVALID_SENDER_EMAIL": "Перед запросом на присоединение убедитесь, что у вас есть действительный адрес электронной почты.", + "INVALID_RECEIVER_EMAIL": "Адрес электронной почты получателя(-ей) недействителен, обратитесь в службу поддержки." }, "UPLOAD": { "ERROR": { - "GENERIC": "Возникла проблема", + "GENERIC": "Не удалось отправить. Обратитесь в службу поддержки, если проблему не удастся устранить.", "CONFLICT": "Новая версия не загружена, существует другой файл с таким же именем", - "500": "Возникла проблема во время загрузки" + "500": "Внутренняя ошибка сервера. Повторите попытку или обратитесь в службу поддержки [500]", + "504": "Время сеанса сервера истекло. Повторите попытку или обратитесь в службу поддержки [504]", + "403": "Недостаточно разрешений для отправки в это местоположение [403]", + "404": "Местоположение, в которое выполняется отправка, больше не существует [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "Не удалось переместить: {{ failed }}." } }, - "LIBRARY_DELETED": "Библиотека удалена" + "LIBRARY_DELETED": "Библиотека удалена", + "JOINED": "Библиотека присоединена", + "JOIN_REQUESTED": "Отправлен запрос на присоединение к этой библиотеке", + "JOIN_CANCELED": "Запрос на присоединение к этой библиотеке отменен", + "LEFT_LIBRARY": "Вы уже покинули библиотеку" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "Сведения", "TABS": { "PROPERTIES": "Свойства", + "LIBRARY_PROPERTIES": "Информация", "VERSIONS": "Версии", "COMMENTS": "Комментарии" } @@ -261,8 +358,9 @@ "DIALOG": { "TITLE": "Управление разрешениями", "CLOSE": "Закрыть", - "INHERIT_PERMISSIONS_BUTTON": "Наследовать разрешение", - "INHERITED_PERMISSIONS_BUTTON": "Разрешение унаследовано" + "INHERIT_PERMISSIONS_BUTTON": "Наследовать разрешения", + "INHERITED_PERMISSIONS_BUTTON": "Разрешения унаследованы", + "ADD_USER_OR_GROUP": "Добавить пользователя или группу" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "Создание библиотеки", "CREATE": "Создать", + "UPDATE": "Обновить", + "EDIT": "Изменить", "CANCEL": "Отмена", "FORM": { "DESCRIPTION": "Описание", "SITE_ID": "Идентификатор библиотеки", - "NAME": "Имя" + "NAME": "Имя", + "VISIBILITY": "Видимость" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "Общедоступный", "MODERATED": "Модерируемый" }, + "HINTS": { + "SITE_TITLE_EXISTS": "Библиотека с таким именем уже существует" + }, "ERRORS": { "GENERIC": "Возникла проблема", - "EXISTENT_SITE": "Идентификатор библиотеки недоступен. Попробуйте использовать другую библиотеку.", + "EXISTENT_SITE": "Идентификатор библиотеки недоступен. Попробуйте использовать другой идентификатор библиотеки.", "CONFLICT": "Этот идентификатор библиотеки уже используется. Проверьте корзину.", "ID_TOO_LONG": "Имя URL должно содержать не более 72 символов", "DESCRIPTION_TOO_LONG": "Описание должно содержать не более 512 символов", "TITLE_TOO_LONG": "Название должно содержать не более 256 символов", - "ILLEGAL_CHARACTERS": "Используйте только буквы и цифры" + "ILLEGAL_CHARACTERS": "Используйте только буквы и цифры", + "ONLY_SPACES": "Имя библиотеки не может состоять только из пробелов", + "LIBRARY_UPDATE_ERROR": "Произошла ошибка при обновлении свойств библиотеки" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "Свойства библиотеки обновлены" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "Поиск", + "FILES": "Файлы", + "FOLDERS": "Папки", + "LIBRARIES": "Библиотеки", + "HINT": "Поисковый запрос должен содержать как минимум два буквенно-цифровых символа." + }, "SORT": { "RELEVANCE": "Релевантность", "FILENAME": "Имя файла", diff --git a/src/assets/i18n/zh-CN.json b/src/assets/i18n/zh-CN.json index 41d73dc261..77105732cb 100644 --- a/src/assets/i18n/zh-CN.json +++ b/src/assets/i18n/zh-CN.json @@ -1,5 +1,39 @@ { "APP": { + "ABOUT": { + "VERSION": "版本:", + "PLUGINS": { + "TITLE": "扩展", + "ID": "ID", + "NAME": "名称", + "VERSION": "版本", + "VENDOR": "供应商", + "LICENSE": "许可证", + "RUNTIME": "运行时", + "DESCRIPTION": "说明" + }, + "LICENSE": { + "TITLE": "许可证", + "PROPERTY": "属性", + "VALUE": "值" + }, + "STATUS": { + "TITLE": "状态", + "PROPERTY": "属性", + "VALUE": "值" + }, + "MODULES": { + "TITLE": "Alfresco 平台模块", + "ID": "ID", + "NAME": "名称", + "VERSION": "版本" + }, + "PACKAGES": { + "TITLE": "软件包", + "NAME": "名称", + "VERSION": "版本" + } + }, "LANGUAGE": "语言", "SIGN_IN": "登录", "SIGN_OUT": "退出", @@ -18,7 +52,7 @@ }, "NEW_MENU": { "LABEL": "新建", - "TOOLTIP": "添加新文件或文件夹", + "TOOLTIP": "添加新文件或文件夹,或者创建新文件库", "MENU_ITEMS": { "CREATE_FOLDER": "创建文件夹", "UPLOAD_FILE": "上传文件", @@ -44,14 +78,37 @@ } }, "LIBRARIES": { - "TITLE": "文件库", + "TITLE": "我的库", + "DESCRIPTION": "访问文件库", "SIDENAV_LINK": { "LABEL": "文件库", - "TOOLTIP": "访问文件库" + "TOOLTIP": "文件库" }, "EMPTY_STATE": { - "TITLE": "您还不是任何文件库的成员", - "TEXT": "加入文件库以上传、查看和共享文件。" + "FILE_LIBRARIES": { + "TITLE": "您还不是任何文件库的成员", + "TEXT": "加入文件库以上传、查看和共享文件。" + }, + "FAVORITE_LIBRARIES": { + "TITLE": "无收藏库", + "TEXT": "收藏您希望以后轻松查找的库" + } + }, + "MENU": { + "MY_LIBRARIES": { + "TITLE": "我的库", + "SIDENAV_LINK": { + "LABEL": "我的库", + "TOOLTIP": "访问我的库" + } + }, + "FAVORITE_LIBRARIES": { + "TITLE": "收藏库", + "SIDENAV_LINK": { + "LABEL": "收藏库", + "TOOLTIP": "访问我的收藏库" + } + } } }, "SHARED": { @@ -105,12 +162,19 @@ "SEARCH": { "TITLE": "搜索结果", "FOUND_RESULTS": "找到 {{ number }} 个结果", + "FOUND_ONE_RESULT": "找到 {{ number }} 个结果", "CUSTOM_ROW": { "MODIFIED": "已修改", "LOCATION": "位置", "SIZE": "字号(&S)" }, + "UNKNOWN_LOCATION": "未知", "NO_RESULTS": "您的搜索返回了 0 个结果" + }, + "SEARCH_LIBRARIES": { + "TITLE": "库找到...", + "FOUND_RESULTS": "{{ number }} 个结果", + "FOUND_ONE_RESULT": "{{ number }} 个结果" } }, "ACTIONS": { @@ -126,6 +190,8 @@ "PERMISSIONS": "权限", "RESTORE": "恢复", "FAVORITE": "收藏", + "ADD_FAVORITE": "添加收藏", + "REMOVE_FAVORITE": "删除收藏", "UNSHARE": "取消共享", "DETAILS": "查看详细信息", "VERSIONS": "管理版本", @@ -133,7 +199,10 @@ "SHARE": "共享", "SHARE_EDIT": "已共享链接的设置", "PRINT": "打印", - "FULLSCREEN": "激活全屏模式" + "FULLSCREEN": "激活全屏模式", + "JOIN": "加入", + "CANCEL_JOIN": "取消加入请求", + "LEAVE": "离开库" }, "DIALOGS": { "CONFIRM_PURGE": { @@ -141,20 +210,28 @@ "MESSAGE": "所选定的项将会被永久删除。", "YES_LABEL": "删除", "NO_LABEL": "保留" + }, + "CONFIRM_LEAVE": { + "TITLE": "要离开该库吗?", + "MESSAGE": "离开将会删除您的访问权。", + "YES_LABEL": "确定", + "NO_LABEL": "取消" } }, "DOCUMENT_LIST": { "COLUMNS": { + "ID": "ID", "NAME": "名称", "SIZE": "字号(&S)", "MODIFIED_ON": "已修改", "MODIFIED_BY": "修改人", - "STATUS": "状态", + "VISIBILITY": "可见性", "TITLE": "标题", "LOCATION": "位置", "SHARED_BY": "分享人", "DELETED_ON": "已删除", - "DELETED_BY": "删除人" + "DELETED_BY": "删除人", + "ROLE": "我的角色" }, "TOOLBAR": { "CARDVIEW": "卡查看模式", @@ -166,9 +243,16 @@ "MODERATED": "适中", "PRIVATE": "私有" }, + "SITES_ROLE": { + "MANAGER": "管理员", + "COLLABORATOR": "合作者", + "CONTRIBUTOR": "贡献者", + "CONSUMER": "使用者" + }, "MESSAGES": { "ERRORS": { - "MISSING_CONTENT": "此文件或文件夹不再存在,或者您无权对其进行查看。", + "CANNOT_NAVIGATE_LOCATION": "无法打开此位置", + "MISSING_CONTENT": "此项目不再存在,或者您无权对其进行查看。", "GENERIC": "此操作未成功,请重试或联系您的 IT 团队。", "CONFLICT": "此名称已使用,请尝试其他名称。", "NODE_MOVE": "未成功移动,存在有相同名称的文件。", @@ -190,13 +274,21 @@ "GENERIC": "恢复 {{ name }} 时出现问题" } }, - "DELETE_LIBRARY_FAILED": "无法删除库" + "DELETE_LIBRARY_FAILED": "无法删除库", + "JOIN_REQUEST_FAILED": "无法加入库", + "JOIN_CANCEL_FAILED": "无法取消加入请求", + "LEAVE_LIBRARY_FAILED": "无法离开该库", + "INVALID_SENDER_EMAIL": "在请求加入之前,您的电子邮件地址必须有效。", + "INVALID_RECEIVER_EMAIL": "收件人电子邮件地址无效,请联系 IT 人员。" }, "UPLOAD": { "ERROR": { - "GENERIC": "发生了问题", + "GENERIC": "上传不成功。如果此问题仍然存在,请联系 IT 人员", "CONFLICT": "未上传新版本,已存在另一个同名文件", - "500": "上传时发生了问题" + "500": "内部服务器错误,请重试或联系 IT 支持人员 [500]", + "504": "服务器超时,请重试或联系 IT 支持人员 [504]", + "403": "权限不足,无法在此位置中进行上传 [403]", + "404": "上传位置不再存在 [404]" } }, "INFO": { @@ -235,7 +327,11 @@ "FAIL": "{{ failed }} 无法移动。" } }, - "LIBRARY_DELETED": "库已删除" + "LIBRARY_DELETED": "库已删除", + "JOINED": "已加入库", + "JOIN_REQUESTED": "已发送加入该库的请求", + "JOIN_CANCELED": "已取消加入该库的请求", + "LEFT_LIBRARY": "您已离开该库" } }, "CONTENT_METADATA": { @@ -245,6 +341,7 @@ "TITLE": "详细信息", "TABS": { "PROPERTIES": "属性", + "LIBRARY_PROPERTIES": "关于", "VERSIONS": "版本", "COMMENTS": "注释" } @@ -262,7 +359,8 @@ "TITLE": "管理权限", "CLOSE": "关闭", "INHERIT_PERMISSIONS_BUTTON": "继承权限", - "INHERITED_PERMISSIONS_BUTTON": "已继承权限" + "INHERITED_PERMISSIONS_BUTTON": "已继承权限", + "ADD_USER_OR_GROUP": "添加用户或组" } }, "SHARED_LINK": { @@ -282,11 +380,14 @@ "DIALOG": { "CREATE_TITLE": "创建库", "CREATE": "创建", + "UPDATE": "更新", + "EDIT": "编辑", "CANCEL": "取消", "FORM": { "DESCRIPTION": "说明", "SITE_ID": "库 ID", - "NAME": "名称" + "NAME": "名称", + "VISIBILITY": "可见性" } }, "VISIBILITY": { @@ -294,17 +395,32 @@ "PUBLIC": "公共", "MODERATED": "适中" }, + "HINTS": { + "SITE_TITLE_EXISTS": "库名已在使用中" + }, "ERRORS": { - "GENERIC": "我们遇到了问题", - "EXISTENT_SITE": "此库 ID 不可用。请尝试使用其他库。", + "GENERIC": "我们遇到一个问题", + "EXISTENT_SITE": "此库 ID 不可用。请尝试使用其他库 ID。", "CONFLICT": "此库 ID 已被使用。请检查回收站。", "ID_TOO_LONG": "对 URL 名称使用 72 个字符或更少字符", "DESCRIPTION_TOO_LONG": "对描述使用 512 个字符或更少字符", "TITLE_TOO_LONG": "对标题使用 256 个字符或更少字符", - "ILLEGAL_CHARACTERS": "仅使用数字和字母" + "ILLEGAL_CHARACTERS": "仅使用数字和字母", + "ONLY_SPACES": "文档库名称不能只包含空格", + "LIBRARY_UPDATE_ERROR": "更新库属性时发生错误" + }, + "SUCCESS": { + "LIBRARY_UPDATED": "已更新库属性" } }, "SEARCH": { + "INPUT": { + "PLACEHOLDER": "搜索", + "FILES": "文件", + "FOLDERS": "文件夹", + "LIBRARIES": "库", + "HINT": "搜索输入必须至少具有 2 个字母数字字符。" + }, "SORT": { "RELEVANCE": "相关性", "FILENAME": "文件名", diff --git a/src/assets/images/adf-move-file-24px.svg b/src/assets/images/adf-move-file-24px.svg new file mode 100644 index 0000000000..bf39e51ccf --- /dev/null +++ b/src/assets/images/adf-move-file-24px.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/baseline-library_books-24px.svg b/src/assets/images/baseline-library_books-24px.svg new file mode 100644 index 0000000000..8898592b71 --- /dev/null +++ b/src/assets/images/baseline-library_books-24px.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/assets/images/join-library.svg b/src/assets/images/join-library.svg new file mode 100644 index 0000000000..24b798f83b --- /dev/null +++ b/src/assets/images/join-library.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/plugins/app.card-view.json b/src/assets/plugins/app.card-view.json new file mode 100644 index 0000000000..1bf2066071 --- /dev/null +++ b/src/assets/plugins/app.card-view.json @@ -0,0 +1,21 @@ +{ + "$schema": "../../../extension.schema.json", + "$id": "app.demo.cardView", + "$name": "app.demo.cardView", + "$version": "1.0.0", + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", + "$description": "Simple extension that provides Card View toolbar button", + + "features": { + "toolbar": [ + { + "id": "app.toolbar.cardView", + "order": 50, + "type": "custom", + "component": "app.toolbar.cardView" + } + ] + } +} diff --git a/src/assets/plugins/app.create.json b/src/assets/plugins/app.create.json index 28ebe97313..b5bd175b13 100644 --- a/src/assets/plugins/app.create.json +++ b/src/assets/plugins/app.create.json @@ -1,8 +1,12 @@ { "$schema": "../../../extension.schema.json", - "$name": "app.create", + "$id": "app.demo.create", + "$name": "app.demo.create", "$version": "1.0.0", - + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", + "$description": "Simple extension that provides custom entries for Create menu", "features": { "create": [ diff --git a/src/assets/plugins/plugin1.json b/src/assets/plugins/app.debug.json similarity index 94% rename from src/assets/plugins/plugin1.json rename to src/assets/plugins/app.debug.json index 10fcd30eec..4bbf94e55c 100644 --- a/src/assets/plugins/plugin1.json +++ b/src/assets/plugins/app.debug.json @@ -1,8 +1,12 @@ { "$schema": "../../../extension.schema.json", + "$id": "app.debug.plugin", "$version": "1.0.0", - "$name": "plugin1", - "$description": "demo plugin", + "$name": "app.debug.plugin", + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", + "$description": "Plugin for debugging and testing purposes", "actions": [ { diff --git a/src/assets/plugins/app.header.json b/src/assets/plugins/app.header.json index ff7420815d..d8ed5e753a 100644 --- a/src/assets/plugins/app.header.json +++ b/src/assets/plugins/app.header.json @@ -1,7 +1,12 @@ { "$schema": "../../../extension.schema.json", - "$name": "app", + "$id": "app.demo.header", + "$name": "app.demo.header", "$version": "1.0.0", + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", + "$description": "Sample app header extension", "actions": [ { diff --git a/src/assets/plugins/metadata-plugin.json b/src/assets/plugins/app.metadata.json similarity index 93% rename from src/assets/plugins/metadata-plugin.json rename to src/assets/plugins/app.metadata.json index 38fc96c437..4a86493f71 100644 --- a/src/assets/plugins/metadata-plugin.json +++ b/src/assets/plugins/app.metadata.json @@ -1,8 +1,13 @@ { "$schema": "../../../extension.schema.json", - "$version": "1.0.0", + "$id": "app.demo.metadata", "$name": "metadata-plugin", + "$version": "1.0.0", + "$vendor": "Alfresco Software, Ltd.", + "$license": "LGPL-3.0", + "$runtime": "1.5.0", "$description": "metadata card configuration plugin - testing purpose", + "features": { "content-metadata-presets": [ { diff --git a/src/environments/environment.e2e.ts b/src/environments/environment.e2e.ts new file mode 100644 index 0000000000..4f46d948b2 --- /dev/null +++ b/src/environments/environment.e2e.ts @@ -0,0 +1,34 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `.angular-cli.json`. + +export const environment = { + production: true, + e2e: true +}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 8740e89fdf..c4ec22f8c3 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -24,5 +24,6 @@ */ export const environment = { - production: true + production: true, + e2e: false }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 390941e8be..5762441b05 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -29,5 +29,6 @@ // The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { - production: false + production: false, + e2e: false }; diff --git a/src/polyfills.ts b/src/polyfills.ts index 6801cfe1a9..4e5e97f070 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -68,7 +68,7 @@ /** Evergreen browsers require these. **/ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; + /** @@ -102,39 +102,3 @@ import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ - - -// workaround for https://github.com/Microsoft/monaco-editor/issues/790 - -Promise.all = function(values: any): Promise { - let resolve: (v: any) => void; - let reject: (v: any) => void; - const promise = new this((res, rej) => { - resolve = res; - reject = rej; - }); - let count = 0; - let index = 0; - const resolvedValues: any[] = []; - for (let value of values) { - if (!(value && value.then)) { - value = this.resolve(value); - } - value.then( - (idx => (val: any) => { - resolvedValues[idx] = val; - count--; - if (!count) { - resolve(resolvedValues); - } - })(index), - reject - ); - count++; - index++; - } - if (!count) { - resolve(resolvedValues); - } - return promise; -}; diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json index ad18b9da38..9238c86ea2 100644 --- a/src/tsconfig.spec.json +++ b/src/tsconfig.spec.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/spec", - "target": "es2017", "types": ["jasmine", "node"] }, "files": ["test.ts", "polyfills.ts"], diff --git a/src/typings.d.ts b/src/typings.d.ts index 4f4100ba6c..5ff558bb6f 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -28,3 +28,5 @@ declare var module: NodeModule; interface NodeModule { id: string; } + +interface WebKitFileEntry {} diff --git a/src/versions.json b/src/versions.json new file mode 100644 index 0000000000..81e5264309 --- /dev/null +++ b/src/versions.json @@ -0,0 +1,5783 @@ +{ + "name": "alfresco-content-app", + "version": "1.5.0", + "problems": [ + "peer dep missing: @angular/cdk@^6.3.3, required by @mat-datetimepicker/core@2.0.1", + "peer dep missing: @angular/common@^6.0.0, required by @alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "peer dep missing: @angular/common@^6.0.0, required by @ngrx/router-store@6.1.2", + "peer dep missing: @angular/core@^6.0.0, required by @alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "peer dep missing: @angular/core@^6.0.7, required by @mat-datetimepicker/core@2.0.1", + "peer dep missing: @angular/core@^6.0.0, required by @ngrx/effects@6.1.2", + "peer dep missing: @angular/core@^6.0.0, required by @ngrx/router-store@6.1.2", + "peer dep missing: @angular/core@^6.0.0, required by @ngrx/store@6.1.2", + "peer dep missing: @angular/flex-layout@>=6.0.0-beta.18, required by @alfresco/adf-content-services@2.6.1", + "peer dep missing: @angular/flex-layout@>=6.0.0-beta.18, required by @alfresco/adf-core@2.6.1", + "peer dep missing: @angular/http@^6.1.4, required by @alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "peer dep missing: @angular/material@^6.3.3, required by @mat-datetimepicker/core@2.0.1", + "peer dep missing: @angular/material@^6.3.3, required by @mat-datetimepicker/moment@2.0.1", + "peer dep missing: @angular/material-moment-adapter@^6.3.3, required by @mat-datetimepicker/moment@2.0.1", + "peer dep missing: @angular/router@^6.0.0, required by @ngrx/router-store@6.1.2", + "peer dep missing: @mat-datetimepicker/core@2.0.0, required by @mat-datetimepicker/moment@2.0.1", + "peer dep missing: alfresco-js-api@2.7.0-2a84d662e8134ada8923742adad17351a4a2f777, required by @alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "peer dep missing: core-js@2.4.1, required by @alfresco/adf-core@2.6.1", + "peer dep missing: pdfjs-dist@2.0.303, required by @alfresco/adf-core@2.6.1" + ], + "dependencies": { + "@alfresco/adf-content-services": { + "version": "2.6.1", + "from": "@alfresco/adf-content-services@2.6.1", + "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-2.6.1.tgz" + }, + "@alfresco/adf-core": { + "version": "2.6.1", + "from": "@alfresco/adf-core@2.6.1", + "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-2.6.1.tgz" + }, + "@alfresco/adf-extensions": { + "version": "3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "from": "@alfresco/adf-extensions@alpha", + "resolved": "https://registry.npmjs.org/@alfresco/adf-extensions/-/adf-extensions-3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6.tgz" + }, + "@angular/animations": { + "version": "7.0.3", + "from": "@angular/animations@7.0.3", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.0.3.tgz" + }, + "@angular/cdk": { + "required": { + "_args": [ + [ + "@angular/cdk@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/cdk@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/cdk@7.0.3", + "_id": "@angular/cdk@7.0.3", + "_integrity": "sha512-QT7U2tOBVfwn8Q71Nyh0UjlyXfZNKdanq3+b8GJ/+IB/d8mVdMRTXBGQ4PqY7CP+wpkgm+wbbUt3urZF1AqdmQ==", + "_location": "/@angular/cdk", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/cdk@7.0.3", + "name": "@angular/cdk", + "escapedName": "@angular%2fcdk", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": null, + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "bugs": { + "url": "https://github.com/angular/material2/issues" + }, + "dependencies": { + "parse5": { + "name": "parse5", + "description": "HTML parser and serializer.", + "version": "5.1.0", + "author": { + "name": "Ivan Nikulin", + "email": "ifaaan@gmail.com", + "url": "https://github.com/inikulin" + }, + "contributors": "https://github.com/inikulin/parse5/graphs/contributors", + "homepage": "https://github.com/inikulin/parse5", + "keywords": [ + "html", + "parser", + "html5", + "WHATWG", + "specification", + "fast", + "html parser", + "html5 parser", + "htmlparser", + "parse5", + "serializer", + "html serializer", + "htmlserializer", + "parse", + "serialize" + ], + "license": "MIT", + "main": "./lib/index.js", + "repository": { + "type": "git", + "url": "git://github.com/inikulin/parse5.git" + }, + "files": [ + "lib" + ], + "_resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "_integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "_from": "parse5@5.1.0", + "readme": "

\n \n \"parse5\"\n \n

\n\n
\n

parse5

\nHTML parser and serializer.\n
\n
\n\n
\nnpm install --save parse5\n
\n
\n\n

\n 📖 Documentation 📖\n

\n\n---\n\n

\n List of parse5 toolset packages\n

\n\n

\n GitHub\n

\n\n

\n Online playground\n

\n\n

\n Version history\n

\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/inikulin/parse5/issues" + }, + "_id": "parse5@5.1.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "parse5@5.1.0", + "name": "parse5", + "escapedName": "parse5", + "rawSpec": "5.1.0", + "saveSpec": "[Circular]", + "fetchSpec": "5.1.0" + }, + "_spec": "5.1.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_optional": true, + "_args": [ + [ + "parse5@5.1.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/cdk/node_modules/parse5", + "error": "[Circular]", + "extraneous": false + }, + "tslib": { + "name": "tslib", + "author": { + "name": "Microsoft Corp." + }, + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": [ + "TypeScript", + "Microsoft", + "compiler", + "language", + "javascript", + "tslib", + "runtime" + ], + "bugs": { + "url": "https://github.com/Microsoft/TypeScript/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Microsoft/tslib.git" + }, + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "tslib@1.9.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular Material Component Development Kit", + "es2015": "./esm2015/cdk.js", + "homepage": "https://github.com/angular/material2#readme", + "keywords": [ + "angular", + "cdk", + "component", + "development", + "kit" + ], + "license": "MIT", + "main": "./bundles/cdk.umd.js", + "module": "./esm5/cdk.es5.js", + "name": "@angular/cdk", + "ng-update": { + "migrations": "./schematics/migration.json" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/core": ">=7.0.0", + "@angular/common": ">=7.0.0" + }, + "releaseGitBranch": "7.0.x", + "releaseGitCommitSha": "44db8860f62bc075e56ec65d784bfe8983a18690", + "releaseGitUser": "Jeremy Elbourn ", + "repository": { + "type": "git", + "url": "git+https://github.com/angular/material2.git" + }, + "schematics": "./schematics/collection.json", + "sideEffects": false, + "typings": "./cdk.d.ts", + "version": "7.0.3", + "readme": "Angular Material\n=======\n\nThe sources for this package are in the main [Angular Material](https://github.com/angular/material2) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT", + "readmeFilename": "README.md", + "devDependencies": {}, + "_dependencies": { + "parse5": "^5.0.0", + "tslib": "^1.7.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/cdk", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@mat-datetimepicker/core@2.0.1", + "requires": "@angular/cdk@^6.3.3" + } + ] + }, + "peerMissing": true + }, + "@angular/common": { + "required": { + "_args": [ + [ + "@angular/common@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/common@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/common@7.0.3", + "_id": "@angular/common@7.0.3", + "_integrity": "sha512-aiuQh6+5kWFp34SYEtpnkAJWU3Qn17S/9LjWSZbgfiaYG6MyszepxqLZPBSBPTElxx2u5VoCPh97+TpKoDqx+g==", + "_location": "/@angular/common", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/common@7.0.3", + "name": "@angular/common", + "escapedName": "@angular%2fcommon", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/common/-/common-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "author": { + "name": "angular" + }, + "bugs": { + "url": "https://github.com/angular/angular/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular - commonly needed directives and services", + "es2015": "./fesm2015/common.js", + "esm2015": "./esm2015/common.js", + "esm5": "./esm5/common.js", + "fesm2015": "./fesm2015/common.js", + "fesm5": "./fesm5/common.js", + "homepage": "https://github.com/angular/angular#readme", + "license": "MIT", + "locales": "locales", + "main": "./bundles/common.umd.js", + "module": "./fesm5/common.js", + "name": "@angular/common", + "ng-update": { + "packageGroup": [ + "@angular/core", + "@angular/bazel", + "@angular/common", + "@angular/compiler", + "@angular/compiler-cli", + "@angular/animations", + "@angular/elements", + "@angular/platform-browser", + "@angular/platform-browser-dynamic", + "@angular/forms", + "@angular/http", + "@angular/platform-server", + "@angular/platform-webworker", + "@angular/platform-webworker-dynamic", + "@angular/upgrade", + "@angular/router", + "@angular/language-service", + "@angular/service-worker" + ] + }, + "peerDependencies": { + "rxjs": "^6.0.0", + "@angular/core": "7.0.3" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/angular/angular.git" + }, + "sideEffects": false, + "typings": "./common.d.ts", + "version": "7.0.3", + "readme": "Angular\n=======\n\nThe sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT\n", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.9.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/common", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "requires": "@angular/common@^6.0.0" + }, + { + "requiredBy": "@ngrx/router-store@6.1.2", + "requires": "@angular/common@^6.0.0" + } + ] + }, + "peerMissing": true + }, + "@angular/compiler": { + "version": "7.0.3", + "from": "@angular/compiler@7.0.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.0.3.tgz" + }, + "@angular/core": { + "required": { + "_args": [ + [ + "@angular/core@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/core@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/core@7.0.3", + "_id": "@angular/core@7.0.3", + "_integrity": "sha512-x/OYYykVsi2vrKlYQJ37I8HYAI/s/CtL3Sd9bl87F6AnqLWnnKIxQaofT/ShfAfdP44LQoN5BNp5j+sjs8K4Kg==", + "_location": "/@angular/core", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/core@7.0.3", + "name": "@angular/core", + "escapedName": "@angular%2fcore", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/core/-/core-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "author": { + "name": "angular" + }, + "bugs": { + "url": "https://github.com/angular/angular/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular - the core framework", + "es2015": "./fesm2015/core.js", + "esm2015": "./esm2015/core.js", + "esm5": "./esm5/core.js", + "fesm2015": "./fesm2015/core.js", + "fesm5": "./fesm5/core.js", + "homepage": "https://github.com/angular/angular#readme", + "license": "MIT", + "main": "./bundles/core.umd.js", + "module": "./fesm5/core.js", + "name": "@angular/core", + "ng-update": { + "packageGroup": [ + "@angular/core", + "@angular/bazel", + "@angular/common", + "@angular/compiler", + "@angular/compiler-cli", + "@angular/animations", + "@angular/elements", + "@angular/platform-browser", + "@angular/platform-browser-dynamic", + "@angular/forms", + "@angular/http", + "@angular/platform-server", + "@angular/platform-webworker", + "@angular/platform-webworker-dynamic", + "@angular/upgrade", + "@angular/router", + "@angular/language-service", + "@angular/service-worker" + ] + }, + "peerDependencies": { + "rxjs": "^6.0.0", + "zone.js": "~0.8.26" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/angular/angular.git" + }, + "sideEffects": false, + "typings": "./core.d.ts", + "version": "7.0.3", + "readme": "Angular\n=======\n\nThe sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT\n", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.9.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/core", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "requires": "@angular/core@^6.0.0" + }, + { + "requiredBy": "@mat-datetimepicker/core@2.0.1", + "requires": "@angular/core@^6.0.7" + }, + { + "requiredBy": "@ngrx/effects@6.1.2", + "requires": "@angular/core@^6.0.0" + }, + { + "requiredBy": "@ngrx/router-store@6.1.2", + "requires": "@angular/core@^6.0.0" + }, + { + "requiredBy": "@ngrx/store@6.1.2", + "requires": "@angular/core@^6.0.0" + } + ] + }, + "peerMissing": true + }, + "@angular/flex-layout": { + "required": { + "name": "@angular/flex-layout", + "version": "7.0.0-beta.19", + "description": "Angular Flex-Layout", + "main": "./bundles/flex-layout.umd.js", + "module": "./esm5/flex-layout.es5.js", + "es2015": "./esm2015/flex-layout.js", + "typings": "./flex-layout.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/angular/flex-layout.git" + }, + "keywords": [ + "angular", + "flex-layout", + "flexbox css", + "media query", + "breakpoints" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/flex-layout/issues" + }, + "homepage": "https://github.com/angular/flex-layout#readme", + "peerDependencies": { + "@angular/cdk": "^7.0.0-rc.0", + "@angular/core": ">=7.0.0-rc.0", + "@angular/common": ">=7.0.0-rc.0", + "rxjs": "^6.0.0" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "sideEffects": false, + "_resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.19.tgz", + "_integrity": "sha512-MXq+zZ6/s5/+GsL9fZ42mKL0LjZ/+L0sVU5FaQuSAJ57soLl5QAGWvdxVmROtqcHd3Htp35R49nKSZBJ0nfAjg==", + "_from": "@angular/flex-layout@7.0.0-beta.19", + "readme": "Angular Flex-Layout\n=======\n\nThe sources for this package are in the main [Angular Flex-Layout](https://github.com/angular/flex-layout) repo. \nPlease file issues and pull requests against that repo.\n\nLicense: MIT\n", + "readmeFilename": "README.md", + "_id": "@angular/flex-layout@7.0.0-beta.19", + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/flex-layout@7.0.0-beta.19", + "name": "@angular/flex-layout", + "escapedName": "@angular%2fflex-layout", + "scope": "@angular", + "rawSpec": "7.0.0-beta.19", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.0-beta.19" + }, + "_spec": "7.0.0-beta.19", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "@angular/flex-layout@7.0.0-beta.19", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.7.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/flex-layout", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-content-services@2.6.1", + "requires": "@angular/flex-layout@>=6.0.0-beta.18" + }, + { + "requiredBy": "@alfresco/adf-core@2.6.1", + "requires": "@angular/flex-layout@>=6.0.0-beta.18" + } + ] + }, + "peerMissing": true + }, + "@angular/forms": { + "version": "7.0.3", + "from": "@angular/forms@7.0.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.0.3.tgz" + }, + "@angular/http": { + "required": { + "_args": [ + [ + "@angular/http@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/http@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/http@7.0.3", + "_id": "@angular/http@7.0.3", + "_integrity": "sha512-aL+z1/tbVY8oJw5v46rbMli5vBGDVyJvs95d1l2n3hWnwMTzS9AVetjcL3B3uruAYuXoh4QlSJ+ysBgdmV1+IA==", + "_location": "/@angular/http", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/http@7.0.3", + "name": "@angular/http", + "escapedName": "@angular%2fhttp", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/http/-/http-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "author": { + "name": "angular" + }, + "bugs": { + "url": "https://github.com/angular/angular/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular - the http service", + "es2015": "./fesm2015/http.js", + "esm2015": "./esm2015/http.js", + "esm5": "./esm5/http.js", + "fesm2015": "./fesm2015/http.js", + "fesm5": "./fesm5/http.js", + "homepage": "https://github.com/angular/angular#readme", + "license": "MIT", + "main": "./bundles/http.umd.js", + "module": "./fesm5/http.js", + "name": "@angular/http", + "ng-update": { + "packageGroup": [ + "@angular/core", + "@angular/bazel", + "@angular/common", + "@angular/compiler", + "@angular/compiler-cli", + "@angular/animations", + "@angular/elements", + "@angular/platform-browser", + "@angular/platform-browser-dynamic", + "@angular/forms", + "@angular/http", + "@angular/platform-server", + "@angular/platform-webworker", + "@angular/platform-webworker-dynamic", + "@angular/upgrade", + "@angular/router", + "@angular/language-service", + "@angular/service-worker" + ] + }, + "peerDependencies": { + "rxjs": "^6.0.0", + "@angular/core": "7.0.3", + "@angular/platform-browser": "7.0.3" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/angular/angular.git" + }, + "sideEffects": false, + "typings": "./http.d.ts", + "version": "7.0.3", + "readme": "Angular\n=======\n\nThe sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT\n", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.9.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/http", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "requires": "@angular/http@^6.1.4" + } + ] + }, + "peerMissing": true + }, + "@angular/material": { + "required": { + "_args": [ + [ + "@angular/material@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/material@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/material@7.0.3", + "_id": "@angular/material@7.0.3", + "_integrity": "sha512-acJ2zU44k/rsd4OeTdAMVP0R3te8aXwfubDQGc8YI1CdRVW1XqMSvAWkToYDVaGvnZV53zQt/iSi1XWaSXYf1Q==", + "_location": "/@angular/material", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/material@7.0.3", + "name": "@angular/material", + "escapedName": "@angular%2fmaterial", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/material/-/material-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "bugs": { + "url": "https://github.com/angular/material2/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular Material", + "es2015": "./esm2015/material.js", + "homepage": "https://github.com/angular/material2#readme", + "keywords": [ + "angular", + "material", + "material design", + "components" + ], + "license": "MIT", + "main": "./bundles/material.umd.js", + "module": "./esm5/material.es5.js", + "name": "@angular/material", + "ng-update": { + "migrations": "./schematics/migration.json", + "packageGroup": [ + "@angular/material", + "@angular/cdk", + "@angular/material-moment-adapter" + ] + }, + "peerDependencies": { + "@angular/animations": ">=7.0.0", + "@angular/cdk": "7.0.3", + "@angular/core": ">=7.0.0", + "@angular/common": ">=7.0.0" + }, + "releaseGitBranch": "7.0.x", + "releaseGitCommitSha": "44db8860f62bc075e56ec65d784bfe8983a18690", + "releaseGitUser": "Jeremy Elbourn ", + "repository": { + "type": "git", + "url": "git+https://github.com/angular/material2.git" + }, + "schematics": "./schematics/collection.json", + "sideEffects": false, + "typings": "./material.d.ts", + "version": "7.0.3", + "readme": "Angular Material\n=======\n\nThe sources for this package are in the main [Angular Material](https://github.com/angular/material2) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.7.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/material", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@mat-datetimepicker/core@2.0.1", + "requires": "@angular/material@^6.3.3" + }, + { + "requiredBy": "@mat-datetimepicker/moment@2.0.1", + "requires": "@angular/material@^6.3.3" + } + ] + }, + "peerMissing": true + }, + "@angular/material-moment-adapter": { + "required": { + "_args": [ + [ + "@angular/material-moment-adapter@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/material-moment-adapter@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/material-moment-adapter@7.0.3", + "_id": "@angular/material-moment-adapter@7.0.3", + "_integrity": "sha512-vbynHlQYWcgbZKRearq5LpflQp9VPDDNarHL+C4WD6aGLPYmIKotOWcBr083HfN1RnkCa7fh+GhOld/MHglHmg==", + "_location": "/@angular/material-moment-adapter", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/material-moment-adapter@7.0.3", + "name": "@angular/material-moment-adapter", + "escapedName": "@angular%2fmaterial-moment-adapter", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "bugs": { + "url": "https://github.com/angular/material2/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular Material Moment Adapter", + "es2015": "./esm2015/material-moment-adapter.js", + "homepage": "https://github.com/angular/material2#readme", + "license": "MIT", + "main": "./bundles/material-moment-adapter.umd.js", + "module": "./esm5/material-moment-adapter.es5.js", + "name": "@angular/material-moment-adapter", + "ng-update": { + "packageGroup": [ + "@angular/material", + "@angular/cdk", + "@angular/material-moment-adapter" + ] + }, + "peerDependencies": { + "@angular/material": "7.0.3", + "@angular/core": ">=7.0.0", + "moment": "^2.18.1" + }, + "releaseGitBranch": "7.0.x", + "releaseGitCommitSha": "44db8860f62bc075e56ec65d784bfe8983a18690", + "releaseGitUser": "Jeremy Elbourn ", + "repository": { + "type": "git", + "url": "git+https://github.com/angular/material2.git" + }, + "typings": "./material-moment-adapter.d.ts", + "version": "7.0.3", + "readme": "Angular Material\n=======\n\nThe sources for this package are in the main [Angular Material](https://github.com/angular/material2) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.7.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/material-moment-adapter", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@mat-datetimepicker/moment@2.0.1", + "requires": "@angular/material-moment-adapter@^6.3.3" + } + ] + }, + "peerMissing": true + }, + "@angular/platform-browser": { + "version": "7.0.3", + "from": "@angular/platform-browser@7.0.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.0.3.tgz" + }, + "@angular/platform-browser-dynamic": { + "version": "7.0.3", + "from": "@angular/platform-browser-dynamic@7.0.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.0.3.tgz" + }, + "@angular/router": { + "required": { + "_args": [ + [ + "@angular/router@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "@angular/router@7.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "@angular/router@7.0.3", + "_id": "@angular/router@7.0.3", + "_integrity": "sha512-885svORDpD9DkaMKjvGwn4g5bf0n3JR8os+gCNhzk0p4TPfpc+vmNo8SyY2jwdLMh2rQzrUQTDkn9SzzgiOfDQ==", + "_location": "/@angular/router", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@angular/router@7.0.3", + "name": "@angular/router", + "escapedName": "@angular%2frouter", + "scope": "@angular", + "rawSpec": "7.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@angular/router/-/router-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "author": { + "name": "angular" + }, + "bugs": { + "url": "https://github.com/angular/angular/issues" + }, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "description": "Angular - the routing library", + "es2015": "./fesm2015/router.js", + "esm2015": "./esm2015/router.js", + "esm5": "./esm5/router.js", + "fesm2015": "./fesm2015/router.js", + "fesm5": "./fesm5/router.js", + "homepage": "https://github.com/angular/angular/tree/master/packages/router", + "keywords": [ + "angular", + "router" + ], + "license": "MIT", + "main": "./bundles/router.umd.js", + "module": "./fesm5/router.js", + "name": "@angular/router", + "ng-update": { + "packageGroup": [ + "@angular/core", + "@angular/bazel", + "@angular/common", + "@angular/compiler", + "@angular/compiler-cli", + "@angular/animations", + "@angular/elements", + "@angular/platform-browser", + "@angular/platform-browser-dynamic", + "@angular/forms", + "@angular/http", + "@angular/platform-server", + "@angular/platform-webworker", + "@angular/platform-webworker-dynamic", + "@angular/upgrade", + "@angular/router", + "@angular/language-service", + "@angular/service-worker" + ] + }, + "peerDependencies": { + "@angular/core": "7.0.3", + "@angular/common": "7.0.3", + "@angular/platform-browser": "7.0.3", + "rxjs": "^6.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/angular/angular.git" + }, + "sideEffects": false, + "typings": "./router.d.ts", + "version": "7.0.3", + "readme": "Angular\n=======\n\nThe sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.\n\nLicense: MIT\n", + "readmeFilename": "README.md", + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.9.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@angular/router", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@ngrx/router-store@6.1.2", + "requires": "@angular/router@^6.0.0" + } + ] + }, + "peerMissing": true + }, + "@mat-datetimepicker/core": { + "required": { + "name": "@mat-datetimepicker/core", + "version": "2.0.1", + "repository": { + "type": "git", + "url": "git+https://github.com/kuhnroyal/mat-datetimepicker.git" + }, + "author": { + "name": "PL", + "email": "kuhnroyal@gmail.com" + }, + "license": "MIT", + "peerDependencies": { + "@angular/core": "^6.0.7", + "@angular/material": "^6.3.3", + "@angular/cdk": "^6.3.3" + }, + "main": "bundles/mat-datetimepicker-core.umd.js", + "module": "fesm5/mat-datetimepicker-core.js", + "es2015": "fesm2015/mat-datetimepicker-core.js", + "esm5": "esm5/mat-datetimepicker-core.js", + "esm2015": "esm2015/mat-datetimepicker-core.js", + "fesm5": "fesm5/mat-datetimepicker-core.js", + "fesm2015": "fesm2015/mat-datetimepicker-core.js", + "typings": "mat-datetimepicker-core.d.ts", + "metadata": "mat-datetimepicker-core.metadata.json", + "sideEffects": false, + "dependencies": { + "tslib": { + "name": "tslib", + "author": "[Circular]", + "homepage": "http://typescriptlang.org/", + "version": "1.9.3", + "license": "Apache-2.0", + "description": "Runtime library for TypeScript helper functions", + "keywords": "[Circular]", + "bugs": "[Circular]", + "repository": "[Circular]", + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "_resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "_integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "_from": "tslib@1.9.3", + "readme": "# tslib\r\n\r\nThis is a runtime library for [TypeScript](http://www.typescriptlang.org/) that contains all of the TypeScript helper functions.\r\n\r\nThis library is primarily used by the `--importHelpers` flag in TypeScript.\r\nWhen using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file:\r\n\r\n```ts\r\nvar __assign = (this && this.__assign) || Object.assign || function(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\r\n t[p] = s[p];\r\n }\r\n return t;\r\n};\r\nexports.x = {};\r\nexports.y = __assign({}, exports.x);\r\n\r\n```\r\n\r\nwill instead be emitted as something like the following:\r\n\r\n```ts\r\nvar tslib_1 = require(\"tslib\");\r\nexports.x = {};\r\nexports.y = tslib_1.__assign({}, exports.x);\r\n```\r\n\r\nBecause this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead.\r\nFor optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`.\r\n\r\n# Installing\r\n\r\nFor the latest stable version, run:\r\n\r\n## npm\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nnpm install --save tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nnpm install --save tslib@1.6.1\r\n```\r\n\r\n## bower\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\nbower install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\nbower install tslib@1.6.1\r\n```\r\n\r\n## JSPM\r\n\r\n```sh\r\n# TypeScript 2.3.3 or later\r\njspm install tslib\r\n\r\n# TypeScript 2.3.2 or earlier\r\njspm install tslib@1.6.1\r\n```\r\n\r\n# Usage\r\n\r\nSet the `importHelpers` compiler option on the command line:\r\n\r\n```\r\ntsc --importHelpers file.ts\r\n```\r\n\r\nor in your tsconfig.json:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"importHelpers\": true\r\n }\r\n}\r\n```\r\n\r\n#### For bower and JSPM users\r\n\r\nYou will need to add a `paths` mapping for `tslib`, e.g. For Bower users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"amd\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"bower_components/tslib/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\nFor JSPM users:\r\n\r\n```json\r\n{\r\n \"compilerOptions\": {\r\n \"module\": \"system\",\r\n \"importHelpers\": true,\r\n \"baseUrl\": \"./\",\r\n \"paths\": {\r\n \"tslib\" : [\"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\"]\r\n }\r\n }\r\n}\r\n```\r\n\r\n\r\n# Contribute\r\n\r\nThere are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript.\r\n\r\n* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in.\r\n* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls).\r\n* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).\r\n* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.\r\n* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).\r\n* Read the language specification ([docx](http://go.microsoft.com/fwlink/?LinkId=267121), [pdf](http://go.microsoft.com/fwlink/?LinkId=267238)).\r\n\r\n# Documentation\r\n\r\n* [Quick tutorial](http://www.typescriptlang.org/Tutorial)\r\n* [Programming handbook](http://www.typescriptlang.org/Handbook)\r\n* [Language specification](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)\r\n* [Homepage](http://www.typescriptlang.org/)\r\n", + "readmeFilename": "README.md", + "_id": "tslib@1.9.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "tslib@1.9.3", + "name": "tslib", + "escapedName": "tslib", + "rawSpec": "1.9.3", + "saveSpec": "[Circular]", + "fetchSpec": "1.9.3" + }, + "_spec": "1.9.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/tslib", + "error": "[Circular]", + "extraneous": false, + "_deduped": "tslib" + } + }, + "_resolved": "https://registry.npmjs.org/@mat-datetimepicker/core/-/core-2.0.1.tgz", + "_integrity": "sha1-4NsdtdTPe6Vrck7AQIF8totXdfI=", + "_from": "@mat-datetimepicker/core@2.0.1", + "readme": "# Material Datetimepicker for @angular/material 6.x\n\nThis is the main branch for @angular/material 6.x.\n\nYou can find the Angular 5 version on the `1.x` branch.\n\n### Description\n\nThe datetimepicker is taken from [Promact/md2](https://github.com/Promact/md2) and modified to use @angular/material as base and added theming support.\n\nLike the @angular/material datepicker it contains a native-datetime-adapter as well as a moment-datetime-adapter.\n\n[![Latest Stable Version](https://img.shields.io/npm/v/@mat-datetimepicker/core.svg)](https://www.npmjs.com/package/@mat-datetimepicker/core)\n[![License](https://img.shields.io/npm/l/@mat-datetimepicker/core.svg)](https://www.npmjs.com/package/@mat-datetimepicker/core)\n[![NPM Downloads](https://img.shields.io/npm/dm/@mat-datetimepicker/core.svg)](https://www.npmjs.com/package/@mat-datetimepicker/core)\n\n### Installation\nInstall:\n```\nyarn install @mat-datetimepicker/core\n```\nAnd for the moment adapter:\n```\nyarn install @angular/material-moment-adapter\nyarn install @mat-datetimepicker/moment\n``` \n\n### Performing a local build\n```\nyarn install\nyarn build\n``` \n\n### Using the local build in some project\n```\ncd my-project\n``` \nAdd the dependencies to your `package.json`:\n```\n\"dependencies\": {\n \"@mat-datetimepicker/core\": \"2.0.0\",\n \"@mat-datetimepicker/moment\": \"2.0.0\",\n}\n```\nLink the local built modules:\n```\nyarn link \"@mat-datetimepicker/core\"\nyarn link \"@mat-datetimepicker/moment\"\n``` \n\n### Import & configuration\nBasically the same way the @angular/material datepicker is configured and imported.\n\n```\nimports: [\n ...\n MatDatepickerModule,\n // use this if you want to use native javascript dates and INTL API if available\n // MatNativeDatetimeModule,\n MatMomentDatetimeModule,\n MatDatetimepickerModule\n]\n```\n\n@see [src/app/app.module.ts](src/app/app.module.ts)\n\n### Usage\n```\n
\n \n Start DateTime\n \n \n \n \n
\n```\n### Theming\n```\n@import '~@mat-datetimepicker/core/datetimepicker/datetimepicker-theme.scss';\n\n// Using the $theme variable from the pre-built theme you can call the theming function\n@include mat-datetimepicker-theme($theme);\n```\n@see [src/styles.scss](src/styles.scss)\n\n", + "readmeFilename": "README.md", + "description": "This is the main branch for @angular/material 6.x.", + "bugs": { + "url": "https://github.com/kuhnroyal/mat-datetimepicker/issues" + }, + "homepage": "https://github.com/kuhnroyal/mat-datetimepicker#readme", + "_id": "@mat-datetimepicker/core@2.0.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "@mat-datetimepicker/core@2.0.1", + "name": "@mat-datetimepicker/core", + "escapedName": "@mat-datetimepicker%2fcore", + "scope": "@mat-datetimepicker", + "rawSpec": "2.0.1", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.1" + }, + "_spec": "2.0.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "@mat-datetimepicker/core@2.0.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "tslib": "^1.9.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/@mat-datetimepicker/core", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@mat-datetimepicker/moment@2.0.1", + "requires": "@mat-datetimepicker/core@2.0.0" + } + ] + }, + "peerMissing": true + }, + "@mat-datetimepicker/moment": { + "version": "2.0.1", + "from": "@mat-datetimepicker/moment@2.0.1", + "resolved": "https://registry.npmjs.org/@mat-datetimepicker/moment/-/moment-2.0.1.tgz" + }, + "@ngrx/effects": { + "version": "6.1.2", + "from": "@ngrx/effects@6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-6.1.2.tgz" + }, + "@ngrx/router-store": { + "version": "6.1.2", + "from": "@ngrx/router-store@6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-6.1.2.tgz" + }, + "@ngrx/store": { + "version": "6.1.2", + "from": "@ngrx/store@6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-6.1.2.tgz" + }, + "@ngrx/store-devtools": { + "version": "6.1.2", + "from": "@ngrx/store-devtools@6.1.2", + "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-6.1.2.tgz" + }, + "@ngx-translate/core": { + "version": "10.0.2", + "from": "@ngx-translate/core@10.0.2", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-10.0.2.tgz" + }, + "alfresco-js-api": { + "required": { + "_args": [ + [ + "alfresco-js-api@2.6.1", + "/Users/dvuika/github/alfresco-content-app" + ], + [ + "alfresco-js-api@2.6.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_from": "alfresco-js-api@2.6.1", + "_id": "alfresco-js-api@2.6.1", + "_integrity": "sha512-E1maHlxlFS3DAmYWG9ueerMWgrcbJSVFO52Bfk2XGx3atEnH3iBFuG0ZczfCJCGIbtv22VliR5qQ90Cufuzhkw==", + "_location": "/alfresco-js-api", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "alfresco-js-api@2.6.1", + "name": "alfresco-js-api", + "escapedName": "alfresco-js-api", + "rawSpec": "2.6.1", + "saveSpec": "[Circular]", + "fetchSpec": "2.6.1" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.6.1.tgz", + "_spec": "2.6.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "author": { + "name": "Alfresco Software, Ltd." + }, + "bugs": { + "url": "https://github.com/Alfresco/alfresco-js-api/issues" + }, + "contributors": [ + { + "name": "Will Abson", + "email": "will.abson@alfresco.com" + }, + { + "name": "Eugenio Romano", + "email": "eugenio.romano@alfresco.com" + }, + { + "name": "Denys Vuika", + "email": "denys.vuika@gmail.com" + }, + { + "name": "Mario Romano", + "email": "mario.romano83@gmail.com" + } + ], + "dependencies": { + "event-emitter": { + "name": "event-emitter", + "version": "0.3.4", + "description": "Environment agnostic event emitter", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "event", + "events", + "trigger", + "observer", + "listener", + "emitter", + "pubsub" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/event-emitter.git" + }, + "dependencies": { + "d": { + "name": "d", + "version": "0.1.1", + "description": "Property descriptor factory", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "scripts": { + "test": "node node_modules/tad/bin/tad" + }, + "repository": { + "type": "git", + "url": "git://github.com/medikoo/d.git" + }, + "keywords": [ + "descriptor", + "es", + "ecmascript", + "ecma", + "property", + "descriptors", + "meta", + "properties" + ], + "dependencies": { + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "ecmascript", + "ecmascript5", + "ecmascript6", + "es5", + "es6", + "extensions", + "ext", + "addons", + "extras", + "harmony", + "javascript", + "polyfill", + "shim", + "util", + "utils", + "utilities" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/es5-ext.git" + }, + "dependencies": {}, + "devDependencies": { + "eslint": "^4.15", + "eslint-config-medikoo-es5": "^1.4.8", + "tad": "~0.2.7" + }, + "eslintConfig": { + "extends": "medikoo-es5", + "root": true, + "rules": { + "no-extend-native": "off" + } + }, + "scripts": { + "lint": "eslint --ignore-path=.gitignore .", + "test": "node ./node_modules/tad/bin/tad" + }, + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/es5-ext/issues" + }, + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "es5-ext@0.10.45", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es5-ext" + } + }, + "devDependencies": { + "tad": "~0.1.21" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", + "_integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", + "_from": "d@0.1.1", + "readme": "# D - Property descriptor factory\n\n_Originally derived from [es5-ext](https://github.com/medikoo/es5-ext) package._\n\nDefining properties with descriptors is very verbose:\n\n```javascript\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n whithdraw: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n balance: { get: function () {\n /* ... */\n }, configurable: true, enumerable: false }\n});\n```\n\nD cuts that to:\n\n```javascript\nvar d = require('d');\n\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: d(function () {\n /* ... */\n }),\n whithdraw: d(function () {\n /* ... */\n }),\n balance: d.gs(function () {\n /* ... */\n })\n});\n```\n\nBy default, created descriptor follow characteristics of native ES5 properties, and defines values as:\n\n```javascript\n{ configurable: true, enumerable: false, writable: true }\n```\n\nYou can overwrite it by preceding _value_ argument with instruction:\n```javascript\nd('c', value); // { configurable: true, enumerable: false, writable: false }\nd('ce', value); // { configurable: true, enumerable: true, writable: false }\nd('e', value); // { configurable: false, enumerable: true, writable: false }\n\n// Same way for get/set:\nd.gs('e', value); // { configurable: false, enumerable: true }\n```\n\n### Other utilities\n\n#### autoBind(obj, props) _(d/auto-bind)_\n\nDefine methods which will be automatically bound to its instances\n\n```javascript\nvar d = require('d');\nvar autoBind = require('d/auto-bind');\n\nvar Foo = function () { this._count = 0; };\nautoBind(Foo.prototype, {\n increment: d(function () { ++this._count; });\n});\n\nvar foo = new Foo();\n\n// Increment foo counter on each domEl click\ndomEl.addEventListener('click', foo.increment, false);\n```\n\n#### lazy(obj, props) _(d/lazy)_\n\nDefine lazy properties, which will be resolved on first access\n\n```javascript\nvar d = require('d');\nvar lazy = require('d/lazy');\n\nvar Foo = function () {};\nlazy(Foo.prototype, {\n items: d(function () { return []; })\n});\n\nvar foo = new Foo();\nfoo.items.push(1, 2); // foo.items array created\n```\n\n## Installation\n### NPM\n\nIn your project path:\n\n\t$ npm install d\n\n### Browser\n\nYou can easily bundle _D_ for browser with [modules-webmake](https://github.com/medikoo/modules-webmake)\n\n## Tests [![Build Status](https://travis-ci.org/medikoo/d.png)](https://travis-ci.org/medikoo/d)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/d/issues" + }, + "homepage": "https://github.com/medikoo/d#readme", + "_id": "d@0.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "d@0.1.1", + "name": "d", + "escapedName": "d", + "rawSpec": "0.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "0.1.1" + }, + "_spec": "0.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "d@0.1.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "es5-ext": "~0.10.2" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/d", + "error": "[Circular]", + "extraneous": false + }, + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": { + "es6-iterator": { + "name": "es6-iterator", + "version": "2.0.3", + "description": "Iterator abstraction based on ES6 specification", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "iterator", + "array", + "list", + "set", + "map", + "generator" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/es6-iterator.git" + }, + "dependencies": { + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "eslintConfig": "[Circular]", + "scripts": "[Circular]", + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es5-ext" + }, + "d": { + "name": "d", + "version": "1.0.0", + "description": "Property descriptor factory", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "descriptor", + "es", + "ecmascript", + "ecma", + "property", + "descriptors", + "meta", + "properties" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/d.git" + }, + "dependencies": { + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "eslintConfig": "[Circular]", + "scripts": "[Circular]", + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es5-ext" + } + }, + "devDependencies": { + "tad": "^0.2.4", + "xlint": "^0.2.2", + "xlint-jslint-medikoo": "^0.1.4" + }, + "scripts": { + "lint": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --no-cache --no-stream", + "lint-console": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --watch", + "test": "node node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "_integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "_from": "d@1.0.0", + "readme": "# D\n## Property descriptor factory\n\n_Originally derived from [es5-ext](https://github.com/medikoo/es5-ext) package._\n\nDefining properties with descriptors is very verbose:\n\n```javascript\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n withdraw: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n balance: { get: function () {\n /* ... */\n }, configurable: true, enumerable: false }\n});\n```\n\nD cuts that to:\n\n```javascript\nvar d = require('d');\n\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: d(function () {\n /* ... */\n }),\n withdraw: d(function () {\n /* ... */\n }),\n balance: d.gs(function () {\n /* ... */\n })\n});\n```\n\nBy default, created descriptor follow characteristics of native ES5 properties, and defines values as:\n\n```javascript\n{ configurable: true, enumerable: false, writable: true }\n```\n\nYou can overwrite it by preceding _value_ argument with instruction:\n```javascript\nd('c', value); // { configurable: true, enumerable: false, writable: false }\nd('ce', value); // { configurable: true, enumerable: true, writable: false }\nd('e', value); // { configurable: false, enumerable: true, writable: false }\n\n// Same way for get/set:\nd.gs('e', value); // { configurable: false, enumerable: true }\n```\n\n### Installation\n\n\t$ npm install d\n\t\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Other utilities\n\n#### autoBind(obj, props) _(d/auto-bind)_\n\nDefine methods which will be automatically bound to its instances\n\n```javascript\nvar d = require('d');\nvar autoBind = require('d/auto-bind');\n\nvar Foo = function () { this._count = 0; };\nObject.defineProperties(Foo.prototype, autoBind({\n increment: d(function () { ++this._count; });\n}));\n\nvar foo = new Foo();\n\n// Increment foo counter on each domEl click\ndomEl.addEventListener('click', foo.increment, false);\n```\n\n#### lazy(obj, props) _(d/lazy)_\n\nDefine lazy properties, which will be resolved on first access\n\n```javascript\nvar d = require('d');\nvar lazy = require('d/lazy');\n\nvar Foo = function () {};\nObject.defineProperties(Foo.prototype, lazy({\n items: d(function () { return []; })\n}));\n\nvar foo = new Foo();\nfoo.items.push(1, 2); // foo.items array created and defined directly on foo\n```\n\n## Tests [![Build Status](https://travis-ci.org/medikoo/d.png)](https://travis-ci.org/medikoo/d)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/d/issues" + }, + "homepage": "https://github.com/medikoo/d#readme", + "_id": "d@1.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "d@1.0.0", + "name": "d", + "escapedName": "d", + "rawSpec": "1.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.0" + }, + "_spec": "1.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "d@1.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "es5-ext": "^0.10.9" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es6-iterator/node_modules/d", + "error": "[Circular]", + "extraneous": false + }, + "es6-symbol": { + "name": "es6-symbol", + "version": "3.1.1", + "description": "ECMAScript 6 Symbol polyfill", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "symbol", + "private", + "property", + "es6", + "ecmascript", + "harmony", + "ponyfill", + "polyfill" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/es6-symbol.git" + }, + "dependencies": {}, + "devDependencies": { + "tad": "~0.2.7", + "xlint": "~0.2.2", + "xlint-jslint-medikoo": "~0.1.4" + }, + "scripts": { + "lint": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --no-cache --no-stream", + "lint-console": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --watch", + "test": "node ./node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "_integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "_from": "es6-symbol@3.1.1", + "readme": "# es6-symbol\n## ECMAScript 6 Symbol polyfill\n\nFor more information about symbols see following links\n- [Symbols in ECMAScript 6 by Axel Rauschmayer](http://www.2ality.com/2014/12/es6-symbols.html)\n- [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)\n- [Specification](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-symbol-constructor)\n\n### Limitations\n\nUnderneath it uses real string property names which can easily be retrieved, however accidental collision with other property names is unlikely.\n\n### Usage\n\nIf you'd like to use native version when it exists and fallback to [ponyfill](https://ponyfill.com) if it doesn't, use *es6-symbol* as following:\n\n```javascript\nvar Symbol = require('es6-symbol');\n```\n\nIf you want to make sure your environment implements `Symbol` globally, do:\n\n```javascript\nrequire('es6-symbol/implement');\n```\n\nIf you strictly want to use polyfill even if native `Symbol` exists (hard to find a good reason for that), do:\n\n```javascript\nvar Symbol = require('es6-symbol/polyfill');\n```\n\n#### API\n\nBest is to refer to [specification](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-symbol-objects). Still if you want quick look, follow examples:\n\n```javascript\nvar Symbol = require('es6-symbol');\n\nvar symbol = Symbol('My custom symbol');\nvar x = {};\n\nx[symbol] = 'foo';\nconsole.log(x[symbol]); 'foo'\n\n// Detect iterable:\nvar iterator, result;\nif (possiblyIterable[Symbol.iterator]) {\n iterator = possiblyIterable[Symbol.iterator]();\n result = iterator.next();\n while(!result.done) {\n console.log(result.value);\n result = iterator.next();\n }\n}\n```\n\n### Installation\n#### NPM\n\nIn your project path:\n\n\t$ npm install es6-symbol\n\n##### Browser\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n## Tests [![Build Status](https://travis-ci.org/medikoo/es6-symbol.png)](https://travis-ci.org/medikoo/es6-symbol)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/es6-symbol/issues" + }, + "homepage": "https://github.com/medikoo/es6-symbol#readme", + "_id": "es6-symbol@3.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "es6-symbol@3.1.1", + "name": "es6-symbol", + "escapedName": "es6-symbol", + "rawSpec": "3.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "3.1.1" + }, + "_spec": "3.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "es6-symbol@3.1.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es6-symbol", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es6-symbol" + } + }, + "devDependencies": { + "eslint": "^4.9", + "eslint-config-medikoo-es5": "^1.4.4", + "event-emitter": "^0.3.5", + "tad": "^0.2.7" + }, + "eslintConfig": { + "extends": "medikoo-es5", + "root": true, + "rules": { + "no-extend-native": "off" + } + }, + "scripts": { + "lint": "eslint --ignore-path=.gitignore .", + "test": "node ./node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "_integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "_from": "es6-iterator@2.0.3", + "readme": "# es6-iterator\n## ECMAScript 6 Iterator interface\n\n### Installation\n\n\t$ npm install es6-iterator\n\t\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n## API\n\n### Constructors\n\n#### Iterator(list) _(es6-iterator)_\n\nAbstract Iterator interface. Meant for extensions and not to be used on its own.\n\nAccepts any _list_ object (technically object with numeric _length_ property).\n\n_Mind it doesn't iterate strings properly, for that use dedicated [StringIterator](#string-iterator)_\n\n```javascript\nvar Iterator = require('es6-iterator')\nvar iterator = new Iterator([1, 2, 3]);\n\niterator.next(); // { value: 1, done: false }\niterator.next(); // { value: 2, done: false }\niterator.next(); // { value: 3, done: false }\niterator.next(); // { value: undefined, done: true }\n```\n\n\n#### ArrayIterator(arrayLike[, kind]) _(es6-iterator/array)_\n\nDedicated for arrays and array-likes. Supports three iteration kinds:\n* __value__ _(default)_ - Iterates values\n* __key__ - Iterates indexes\n* __key+value__ - Iterates keys and indexes, each iteration value is in _[key, value]_ form.\n\n\n```javascript\nvar ArrayIterator = require('es6-iterator/array')\nvar iterator = new ArrayIterator([1, 2, 3], 'key+value');\n\niterator.next(); // { value: [0, 1], done: false }\niterator.next(); // { value: [1, 2], done: false }\niterator.next(); // { value: [2, 3], done: false }\niterator.next(); // { value: undefined, done: true }\n```\n\nMay also be used for _arguments_ objects:\n\n```javascript\n(function () {\n var iterator = new ArrayIterator(arguments);\n\n iterator.next(); // { value: 1, done: false }\n iterator.next(); // { value: 2, done: false }\n iterator.next(); // { value: 3, done: false }\n iterator.next(); // { value: undefined, done: true }\n}(1, 2, 3));\n```\n\n#### StringIterator(str) _(es6-iterator/string)_\n\nAssures proper iteration over unicode symbols. \nSee: http://mathiasbynens.be/notes/javascript-unicode\n\n```javascript\nvar StringIterator = require('es6-iterator/string');\nvar iterator = new StringIterator('f🙈o🙉o🙊');\n\niterator.next(); // { value: 'f', done: false }\niterator.next(); // { value: '🙈', done: false }\niterator.next(); // { value: 'o', done: false }\niterator.next(); // { value: '🙉', done: false }\niterator.next(); // { value: 'o', done: false }\niterator.next(); // { value: '🙊', done: false }\niterator.next(); // { value: undefined, done: true }\n```\n\n### Function utilities\n\n#### forOf(iterable, callback[, thisArg]) _(es6-iterator/for-of)_\n\nPolyfill for ECMAScript 6 [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) statement.\n\n```\nvar forOf = require('es6-iterator/for-of');\nvar result = [];\n\nforOf('🙈🙉🙊', function (monkey) { result.push(monkey); });\nconsole.log(result); // ['🙈', '🙉', '🙊'];\n```\n\nOptionally you can break iteration at any point:\n\n```javascript\nvar result = [];\n\nforOf([1,2,3,4]', function (val, doBreak) {\n result.push(monkey);\n if (val >= 3) doBreak();\n});\nconsole.log(result); // [1, 2, 3];\n```\n\n#### get(obj) _(es6-iterator/get)_\n\nReturn iterator for any iterable object.\n\n```javascript\nvar getIterator = require('es6-iterator/get');\nvar iterator = get([1,2,3]);\n\niterator.next(); // { value: 1, done: false }\niterator.next(); // { value: 2, done: false }\niterator.next(); // { value: 3, done: false }\niterator.next(); // { value: undefined, done: true }\n```\n\n#### isIterable(obj) _(es6-iterator/is-iterable)_\n\nWhether _obj_ is iterable\n\n```javascript\nvar isIterable = require('es6-iterator/is-iterable');\n\nisIterable(null); // false\nisIterable(true); // false\nisIterable('str'); // true\nisIterable(['a', 'r', 'r']); // true\nisIterable(new ArrayIterator([])); // true\n```\n\n#### validIterable(obj) _(es6-iterator/valid-iterable)_\n\nIf _obj_ is an iterable it is returned. Otherwise _TypeError_ is thrown.\n\n### Method extensions\n\n#### iterator.chain(iterator1[, …iteratorn]) _(es6-iterator/#/chain)_\n\nChain multiple iterators into one.\n\n### Tests [![Build Status](https://travis-ci.org/medikoo/es6-iterator.png)](https://travis-ci.org/medikoo/es6-iterator)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/es6-iterator/issues" + }, + "homepage": "https://github.com/medikoo/es6-iterator#readme", + "_id": "es6-iterator@2.0.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "es6-iterator@2.0.3", + "name": "es6-iterator", + "escapedName": "es6-iterator", + "rawSpec": "2.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.3" + }, + "_spec": "2.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "es6-iterator@2.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es6-iterator", + "error": "[Circular]", + "extraneous": false + }, + "es6-symbol": { + "name": "es6-symbol", + "version": "3.1.1", + "description": "ECMAScript 6 Symbol polyfill", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": { + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "eslintConfig": "[Circular]", + "scripts": "[Circular]", + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es5-ext" + }, + "d": { + "name": "d", + "version": "1.0.0", + "description": "Property descriptor factory", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "descriptor", + "es", + "ecmascript", + "ecma", + "property", + "descriptors", + "meta", + "properties" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/d.git" + }, + "dependencies": { + "es5-ext": { + "name": "es5-ext", + "version": "0.10.45", + "description": "ECMAScript extensions and shims", + "author": "[Circular]", + "keywords": "[Circular]", + "repository": "[Circular]", + "dependencies": {}, + "devDependencies": "[Circular]", + "eslintConfig": "[Circular]", + "scripts": "[Circular]", + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false, + "_deduped": "es5-ext" + } + }, + "devDependencies": { + "tad": "^0.2.4", + "xlint": "^0.2.2", + "xlint-jslint-medikoo": "^0.1.4" + }, + "scripts": { + "lint": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --no-cache --no-stream", + "lint-console": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --watch", + "test": "node node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "_integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "_from": "d@1.0.0", + "readme": "# D\n## Property descriptor factory\n\n_Originally derived from [es5-ext](https://github.com/medikoo/es5-ext) package._\n\nDefining properties with descriptors is very verbose:\n\n```javascript\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n withdraw: { value: function () {\n /* ... */\n }, configurable: true, enumerable: false, writable: true },\n balance: { get: function () {\n /* ... */\n }, configurable: true, enumerable: false }\n});\n```\n\nD cuts that to:\n\n```javascript\nvar d = require('d');\n\nvar Account = function () {};\nObject.defineProperties(Account.prototype, {\n deposit: d(function () {\n /* ... */\n }),\n withdraw: d(function () {\n /* ... */\n }),\n balance: d.gs(function () {\n /* ... */\n })\n});\n```\n\nBy default, created descriptor follow characteristics of native ES5 properties, and defines values as:\n\n```javascript\n{ configurable: true, enumerable: false, writable: true }\n```\n\nYou can overwrite it by preceding _value_ argument with instruction:\n```javascript\nd('c', value); // { configurable: true, enumerable: false, writable: false }\nd('ce', value); // { configurable: true, enumerable: true, writable: false }\nd('e', value); // { configurable: false, enumerable: true, writable: false }\n\n// Same way for get/set:\nd.gs('e', value); // { configurable: false, enumerable: true }\n```\n\n### Installation\n\n\t$ npm install d\n\t\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Other utilities\n\n#### autoBind(obj, props) _(d/auto-bind)_\n\nDefine methods which will be automatically bound to its instances\n\n```javascript\nvar d = require('d');\nvar autoBind = require('d/auto-bind');\n\nvar Foo = function () { this._count = 0; };\nObject.defineProperties(Foo.prototype, autoBind({\n increment: d(function () { ++this._count; });\n}));\n\nvar foo = new Foo();\n\n// Increment foo counter on each domEl click\ndomEl.addEventListener('click', foo.increment, false);\n```\n\n#### lazy(obj, props) _(d/lazy)_\n\nDefine lazy properties, which will be resolved on first access\n\n```javascript\nvar d = require('d');\nvar lazy = require('d/lazy');\n\nvar Foo = function () {};\nObject.defineProperties(Foo.prototype, lazy({\n items: d(function () { return []; })\n}));\n\nvar foo = new Foo();\nfoo.items.push(1, 2); // foo.items array created and defined directly on foo\n```\n\n## Tests [![Build Status](https://travis-ci.org/medikoo/d.png)](https://travis-ci.org/medikoo/d)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/d/issues" + }, + "homepage": "https://github.com/medikoo/d#readme", + "_id": "d@1.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "d@1.0.0", + "name": "d", + "escapedName": "d", + "rawSpec": "1.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.0" + }, + "_spec": "1.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "d@1.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "es5-ext": "^0.10.9" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es6-symbol/node_modules/d", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": "[Circular]", + "scripts": "[Circular]", + "license": "MIT", + "_resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "_integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "_from": "es6-symbol@3.1.1", + "readme": "# es6-symbol\n## ECMAScript 6 Symbol polyfill\n\nFor more information about symbols see following links\n- [Symbols in ECMAScript 6 by Axel Rauschmayer](http://www.2ality.com/2014/12/es6-symbols.html)\n- [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)\n- [Specification](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-symbol-constructor)\n\n### Limitations\n\nUnderneath it uses real string property names which can easily be retrieved, however accidental collision with other property names is unlikely.\n\n### Usage\n\nIf you'd like to use native version when it exists and fallback to [ponyfill](https://ponyfill.com) if it doesn't, use *es6-symbol* as following:\n\n```javascript\nvar Symbol = require('es6-symbol');\n```\n\nIf you want to make sure your environment implements `Symbol` globally, do:\n\n```javascript\nrequire('es6-symbol/implement');\n```\n\nIf you strictly want to use polyfill even if native `Symbol` exists (hard to find a good reason for that), do:\n\n```javascript\nvar Symbol = require('es6-symbol/polyfill');\n```\n\n#### API\n\nBest is to refer to [specification](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-symbol-objects). Still if you want quick look, follow examples:\n\n```javascript\nvar Symbol = require('es6-symbol');\n\nvar symbol = Symbol('My custom symbol');\nvar x = {};\n\nx[symbol] = 'foo';\nconsole.log(x[symbol]); 'foo'\n\n// Detect iterable:\nvar iterator, result;\nif (possiblyIterable[Symbol.iterator]) {\n iterator = possiblyIterable[Symbol.iterator]();\n result = iterator.next();\n while(!result.done) {\n console.log(result.value);\n result = iterator.next();\n }\n}\n```\n\n### Installation\n#### NPM\n\nIn your project path:\n\n\t$ npm install es6-symbol\n\n##### Browser\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n## Tests [![Build Status](https://travis-ci.org/medikoo/es6-symbol.png)](https://travis-ci.org/medikoo/es6-symbol)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es6-symbol#readme", + "_id": "es6-symbol@3.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "es6-symbol@3.1.1", + "name": "es6-symbol", + "escapedName": "es6-symbol", + "rawSpec": "3.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "3.1.1" + }, + "_spec": "3.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es6-symbol", + "error": "[Circular]", + "extraneous": false + }, + "next-tick": { + "name": "next-tick", + "version": "1.0.0", + "description": "Environment agnostic nextTick polyfill", + "author": { + "name": "Mariusz Nowak", + "email": "medyk@medikoo.com", + "url": "http://www.medikoo.com/" + }, + "keywords": [ + "nexttick", + "setImmediate", + "setTimeout", + "async" + ], + "repository": { + "type": "git", + "url": "git://github.com/medikoo/next-tick.git" + }, + "devDependencies": { + "tad": "^0.2.4", + "xlint": "^0.2.2", + "xlint-jslint-medikoo": "^0.1.4" + }, + "scripts": { + "lint": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --no-cache --no-stream", + "lint-console": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --watch", + "test": "node node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "_integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "_from": "next-tick@1.0.0", + "readme": "# next-tick\n## Environment agnostic nextTick polyfill\n\nTo be used in environment agnostic modules that need nextTick functionality.\n\n- When run in Node.js `process.nextTick` is used\n- In modern browsers microtask resolution is guaranteed by `MutationObserver`\n- In other engines `setImmediate` or `setTimeout(fn, 0)` is used as fallback.\n- If none of the above is supported module resolves to `null`\n\n## Installation\n### NPM\n\nIn your project path:\n\n\t$ npm install next-tick\n\n#### Browser\n\nYou can easily bundle `next-tick` for browser with any CJS bundler, e.g. [modules-webmake](https://github.com/medikoo/modules-webmake)\n\n## Tests [![Build Status](https://api.travis-ci.org/medikoo/next-tick.png?branch=master)](https://travis-ci.org/medikoo/next-tick)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/next-tick/issues" + }, + "homepage": "https://github.com/medikoo/next-tick#readme", + "_id": "next-tick@1.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "next-tick@1.0.0", + "name": "next-tick", + "escapedName": "next-tick", + "rawSpec": "1.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.0" + }, + "_spec": "1.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "next-tick@1.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/next-tick", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": "[Circular]", + "eslintConfig": "[Circular]", + "scripts": "[Circular]", + "license": "ISC", + "_resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "_integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "_from": "es5-ext@0.10.45", + "readme": "[![Build status][nix-build-image]][nix-build-url]\n[![Windows status][win-build-image]][win-build-url]\n![Transpilation status][transpilation-image]\n[![npm version][npm-image]][npm-url]\n\n# es5-ext\n\n## ECMAScript 5 extensions\n\n### (with respect to ECMAScript 6 standard)\n\nShims for upcoming ES6 standard and other goodies implemented strictly with ECMAScript conventions in mind.\n\nIt's designed to be used in compliant ECMAScript 5 or ECMAScript 6 environments. Older environments are not supported, although most of the features should work with correct ECMAScript 5 shim on board.\n\nWhen used in ECMAScript 6 environment, native implementation (if valid) takes precedence over shims.\n\n### Installation\n\n $ npm install es5-ext\n\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n#### ECMAScript 6 features\n\nYou can force ES6 features to be implemented in your environment, e.g. following will assign `from` function to `Array` (only if it's not implemented already).\n\n```javascript\nrequire(\"es5-ext/array/from/implement\");\nArray.from(\"foo\"); // ['f', 'o', 'o']\n```\n\nYou can also access shims directly, without fixing native objects. Following will return native `Array.from` if it's available and fallback to shim if it's not.\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\nIf you want to use shim unconditionally (even if native implementation exists) do:\n\n```javascript\nvar aFrom = require(\"es5-ext/array/from/shim\");\naFrom(\"foo\"); // ['f', 'o', 'o']\n```\n\n##### List of ES6 shims\n\nIt's about properties introduced with ES6 and those that have been updated in new spec.\n\n* `Array.from` -> `require('es5-ext/array/from')`\n* `Array.of` -> `require('es5-ext/array/of')`\n* `Array.prototype.concat` -> `require('es5-ext/array/#/concat')`\n* `Array.prototype.copyWithin` -> `require('es5-ext/array/#/copy-within')`\n* `Array.prototype.entries` -> `require('es5-ext/array/#/entries')`\n* `Array.prototype.fill` -> `require('es5-ext/array/#/fill')`\n* `Array.prototype.filter` -> `require('es5-ext/array/#/filter')`\n* `Array.prototype.find` -> `require('es5-ext/array/#/find')`\n* `Array.prototype.findIndex` -> `require('es5-ext/array/#/find-index')`\n* `Array.prototype.keys` -> `require('es5-ext/array/#/keys')`\n* `Array.prototype.map` -> `require('es5-ext/array/#/map')`\n* `Array.prototype.slice` -> `require('es5-ext/array/#/slice')`\n* `Array.prototype.splice` -> `require('es5-ext/array/#/splice')`\n* `Array.prototype.values` -> `require('es5-ext/array/#/values')`\n* `Array.prototype[@@iterator]` -> `require('es5-ext/array/#/@@iterator')`\n* `Math.acosh` -> `require('es5-ext/math/acosh')`\n* `Math.asinh` -> `require('es5-ext/math/asinh')`\n* `Math.atanh` -> `require('es5-ext/math/atanh')`\n* `Math.cbrt` -> `require('es5-ext/math/cbrt')`\n* `Math.clz32` -> `require('es5-ext/math/clz32')`\n* `Math.cosh` -> `require('es5-ext/math/cosh')`\n* `Math.exmp1` -> `require('es5-ext/math/expm1')`\n* `Math.fround` -> `require('es5-ext/math/fround')`\n* `Math.hypot` -> `require('es5-ext/math/hypot')`\n* `Math.imul` -> `require('es5-ext/math/imul')`\n* `Math.log1p` -> `require('es5-ext/math/log1p')`\n* `Math.log2` -> `require('es5-ext/math/log2')`\n* `Math.log10` -> `require('es5-ext/math/log10')`\n* `Math.sign` -> `require('es5-ext/math/sign')`\n* `Math.signh` -> `require('es5-ext/math/signh')`\n* `Math.tanh` -> `require('es5-ext/math/tanh')`\n* `Math.trunc` -> `require('es5-ext/math/trunc')`\n* `Number.EPSILON` -> `require('es5-ext/number/epsilon')`\n* `Number.MAX_SAFE_INTEGER` -> `require('es5-ext/number/max-safe-integer')`\n* `Number.MIN_SAFE_INTEGER` -> `require('es5-ext/number/min-safe-integer')`\n* `Number.isFinite` -> `require('es5-ext/number/is-finite')`\n* `Number.isInteger` -> `require('es5-ext/number/is-integer')`\n* `Number.isNaN` -> `require('es5-ext/number/is-nan')`\n* `Number.isSafeInteger` -> `require('es5-ext/number/is-safe-integer')`\n* `Object.assign` -> `require('es5-ext/object/assign')`\n* `Object.keys` -> `require('es5-ext/object/keys')`\n* `Object.setPrototypeOf` -> `require('es5-ext/object/set-prototype-of')`\n* `RegExp.prototype.match` -> `require('es5-ext/reg-exp/#/match')`\n* `RegExp.prototype.replace` -> `require('es5-ext/reg-exp/#/replace')`\n* `RegExp.prototype.search` -> `require('es5-ext/reg-exp/#/search')`\n* `RegExp.prototype.split` -> `require('es5-ext/reg-exp/#/split')`\n* `RegExp.prototype.sticky` -> Implement with `require('es5-ext/reg-exp/#/sticky/implement')`, use as function with `require('es5-ext/reg-exp/#/is-sticky')`\n* `RegExp.prototype.unicode` -> Implement with `require('es5-ext/reg-exp/#/unicode/implement')`, use as function with `require('es5-ext/reg-exp/#/is-unicode')`\n* `String.fromCodePoint` -> `require('es5-ext/string/from-code-point')`\n* `String.raw` -> `require('es5-ext/string/raw')`\n* `String.prototype.codePointAt` -> `require('es5-ext/string/#/code-point-at')`\n* `String.prototype.contains` -> `require('es5-ext/string/#/contains')`\n* `String.prototype.endsWith` -> `require('es5-ext/string/#/ends-with')`\n* `String.prototype.normalize` -> `require('es5-ext/string/#/normalize')`\n* `String.prototype.repeat` -> `require('es5-ext/string/#/repeat')`\n* `String.prototype.startsWith` -> `require('es5-ext/string/#/starts-with')`\n* `String.prototype[@@iterator]` -> `require('es5-ext/string/#/@@iterator')`\n\n#### Non ECMAScript standard features\n\n**es5-ext** provides also other utils, and implements them as if they were proposed for a standard. It mostly offers methods (not functions) which can directly be assigned to native prototypes:\n\n```javascript\nObject.defineProperty(Function.prototype, \"partial\", {\n\tvalue: require(\"es5-ext/function/#/partial\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(Array.prototype, \"flatten\", {\n\tvalue: require(\"es5-ext/array/#/flatten\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\nObject.defineProperty(String.prototype, \"capitalize\", {\n\tvalue: require(\"es5-ext/string/#/capitalize\"),\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: true\n});\n```\n\nSee [es5-extend](https://github.com/wookieb/es5-extend#es5-extend), a great utility that automatically will extend natives for you.\n\n**Important:** Remember to **not** extend natives in scope of generic reusable packages (e.g. ones you intend to publish to npm). Extending natives is fine **only** if you're the _owner_ of the global scope, so e.g. in final project you lead development of.\n\nWhen you're in situation when native extensions are not good idea, then you should use methods indirectly:\n\n```javascript\nvar flatten = require(\"es5-ext/array/#/flatten\");\n\nflatten.call([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nfor better convenience you can turn methods into functions:\n\n```javascript\nvar call = Function.prototype.call;\nvar flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\n\nflatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nYou can configure custom toolkit (like [underscorejs](http://underscorejs.org/)), and use it throughout your application\n\n```javascript\nvar util = {};\nutil.partial = call.bind(require(\"es5-ext/function/#/partial\"));\nutil.flatten = call.bind(require(\"es5-ext/array/#/flatten\"));\nutil.startsWith = call.bind(require(\"es5-ext/string/#/starts-with\"));\n\nutil.flatten([1, [2, [3, 4]]]); // [1, 2, 3, 4]\n```\n\nAs with native ones most methods are generic and can be run on any type of object.\n\n## API\n\n### Global extensions\n\n#### global _(es5-ext/global)_\n\nObject that represents global scope\n\n### Array Constructor extensions\n\n#### from(arrayLike[, mapFn[, thisArg]]) _(es5-ext/array/from)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from). \nReturns array representation of _iterable_ or _arrayLike_. If _arrayLike_ is an instance of array, its copy is returned.\n\n#### generate([length[, …fill]]) _(es5-ext/array/generate)_\n\nGenerate an array of pre-given _length_ built of repeated arguments.\n\n#### isPlainArray(x) _(es5-ext/array/is-plain-array)_\n\nReturns true if object is plain array (not instance of one of the Array's extensions).\n\n#### of([…items]) _(es5-ext/array/of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.of). \nCreate an array from given arguments.\n\n#### toArray(obj) _(es5-ext/array/to-array)_\n\nReturns array representation of `obj`. If `obj` is already an array, `obj` is returned back.\n\n#### validArray(obj) _(es5-ext/array/valid-array)_\n\nReturns `obj` if it's an array, otherwise throws `TypeError`\n\n### Array Prototype extensions\n\n#### arr.binarySearch(compareFn) _(es5-ext/array/#/binary-search)_\n\nIn **sorted** list search for index of item for which _compareFn_ returns value closest to _0_. \nIt's variant of binary search algorithm\n\n#### arr.clear() _(es5-ext/array/#/clear)_\n\nClears the array\n\n#### arr.compact() _(es5-ext/array/#/compact)_\n\nReturns a copy of the context with all non-values (`null` or `undefined`) removed.\n\n#### arr.concat() _(es5-ext/array/#/concat)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat). \nES6's version of `concat`. Supports `isConcatSpreadable` symbol, and returns array of same type as the context.\n\n#### arr.contains(searchElement[, position]) _(es5-ext/array/#/contains)_\n\nWhether list contains the given value.\n\n#### arr.copyWithin(target, start[, end]) _(es5-ext/array/#/copy-within)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.copywithin).\n\n#### arr.diff(other) _(es5-ext/array/#/diff)_\n\nReturns the array of elements that are present in context list but not present in other list.\n\n#### arr.eIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-index-of)_\n\n_egal_ version of `indexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.eLastIndexOf(searchElement[, fromIndex]) _(es5-ext/array/#/e-last-index-of)_\n\n_egal_ version of `lastIndexOf` method. [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) logic is used for comparision\n\n#### arr.entries() _(es5-ext/array/#/entries)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.entries). \nReturns iterator object, which traverses the array. Each value is represented with an array, where first value is an index and second is corresponding to index value.\n\n#### arr.exclusion([…lists]]) _(es5-ext/array/#/exclusion)_\n\nReturns the array of elements that are found only in one of the lists (either context list or list provided in arguments).\n\n#### arr.fill(value[, start, end]) _(es5-ext/array/#/fill)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.fill).\n\n#### arr.filter(callback[, thisArg]) _(es5-ext/array/#/filter)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.filter). \nES6's version of `filter`, returns array of same type as the context.\n\n#### arr.find(predicate[, thisArg]) _(es5-ext/array/#/find)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.find). \nReturn first element for which given function returns true\n\n#### arr.findIndex(predicate[, thisArg]) _(es5-ext/array/#/find-index)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.findindex). \nReturn first index for which given function returns true\n\n#### arr.first() _(es5-ext/array/#/first)_\n\nReturns value for first defined index\n\n#### arr.firstIndex() _(es5-ext/array/#/first-index)_\n\nReturns first declared index of the array\n\n#### arr.flatten() _(es5-ext/array/#/flatten)_\n\nReturns flattened version of the array\n\n#### arr.forEachRight(cb[, thisArg]) _(es5-ext/array/#/for-each-right)_\n\n`forEach` starting from last element\n\n#### arr.group(cb[, thisArg]) _(es5-ext/array/#/group)_\n\nGroup list elements by value returned by _cb_ function\n\n#### arr.indexesOf(searchElement[, fromIndex]) _(es5-ext/array/#/indexes-of)_\n\nReturns array of all indexes of given value\n\n#### arr.intersection([…lists]) _(es5-ext/array/#/intersection)_\n\nComputes the array of values that are the intersection of all lists (context list and lists given in arguments)\n\n#### arr.isCopy(other) _(es5-ext/array/#/is-copy)_\n\nReturns true if both context and _other_ lists have same content\n\n#### arr.isUniq() _(es5-ext/array/#/is-uniq)_\n\nReturns true if all values in array are unique\n\n#### arr.keys() _(es5-ext/array/#/keys)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.keys). \nReturns iterator object, which traverses all array indexes.\n\n#### arr.last() _(es5-ext/array/#/last)_\n\nReturns value of last defined index\n\n#### arr.lastIndex() _(es5-ext/array/#/last)_\n\nReturns last defined index of the array\n\n#### arr.map(callback[, thisArg]) _(es5-ext/array/#/map)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.map). \nES6's version of `map`, returns array of same type as the context.\n\n#### arr.remove(value[, …valuen]) _(es5-ext/array/#/remove)_\n\nRemove values from the array\n\n#### arr.separate(sep) _(es5-ext/array/#/separate)_\n\nReturns array with items separated with `sep` value\n\n#### arr.slice(callback[, thisArg]) _(es5-ext/array/#/slice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.slice). \nES6's version of `slice`, returns array of same type as the context.\n\n#### arr.someRight(cb[, thisArg]) _(es5-ext/array/#/someRight)_\n\n`some` starting from last element\n\n#### arr.splice(callback[, thisArg]) _(es5-ext/array/#/splice)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.splice). \nES6's version of `splice`, returns array of same type as the context.\n\n#### arr.uniq() _(es5-ext/array/#/uniq)_\n\nReturns duplicate-free version of the array\n\n#### arr.values() _(es5-ext/array/#/values)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.values). \nReturns iterator object which traverses all array values.\n\n#### arr[@@iterator] _(es5-ext/array/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype-@@iterator). \nReturns iterator object which traverses all array values.\n\n### Boolean Constructor extensions\n\n#### isBoolean(x) _(es5-ext/boolean/is-boolean)_\n\nWhether value is boolean\n\n### Date Constructor extensions\n\n#### isDate(x) _(es5-ext/date/is-date)_\n\nWhether value is date instance\n\n#### validDate(x) _(es5-ext/date/valid-date)_\n\nIf given object is not date throw TypeError in other case return it.\n\n### Date Prototype extensions\n\n#### date.copy(date) _(es5-ext/date/#/copy)_\n\nReturns a copy of the date object\n\n#### date.daysInMonth() _(es5-ext/date/#/days-in-month)_\n\nReturns number of days of date's month\n\n#### date.floorDay() _(es5-ext/date/#/floor-day)_\n\nSets the date time to 00:00:00.000\n\n#### date.floorMonth() _(es5-ext/date/#/floor-month)_\n\nSets date day to 1 and date time to 00:00:00.000\n\n#### date.floorYear() _(es5-ext/date/#/floor-year)_\n\nSets date month to 0, day to 1 and date time to 00:00:00.000\n\n#### date.format(pattern) _(es5-ext/date/#/format)_\n\nFormats date up to given string. Supported patterns:\n\n* `%Y` - Year with century, 1999, 2003\n* `%y` - Year without century, 99, 03\n* `%m` - Month, 01..12\n* `%d` - Day of the month 01..31\n* `%H` - Hour (24-hour clock), 00..23\n* `%M` - Minute, 00..59\n* `%S` - Second, 00..59\n* `%L` - Milliseconds, 000..999\n\n### Error Constructor extensions\n\n#### custom(message/_, code, ext_/) _(es5-ext/error/custom)_\n\nCreates custom error object, optinally extended with `code` and other extension properties (provided with `ext` object)\n\n#### isError(x) _(es5-ext/error/is-error)_\n\nWhether value is an error (instance of `Error`).\n\n#### validError(x) _(es5-ext/error/valid-error)_\n\nIf given object is not error throw TypeError in other case return it.\n\n### Error Prototype extensions\n\n#### err.throw() _(es5-ext/error/#/throw)_\n\nThrows error\n\n### Function Constructor extensions\n\nSome of the functions were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### constant(x) _(es5-ext/function/constant)_\n\nReturns a constant function that returns pregiven argument\n\n_k(x)(y) =def x_\n\n#### identity(x) _(es5-ext/function/identity)_\n\nIdentity function. Returns first argument\n\n_i(x) =def x_\n\n#### invoke(name[, …args]) _(es5-ext/function/invoke)_\n\nReturns a function that takes an object as an argument, and applies object's\n_name_ method to arguments. \n_name_ can be name of the method or method itself.\n\n_invoke(name, …args)(object, …args2) =def object\\[name\\]\\(…args, …args2\\)_\n\n#### isArguments(x) _(es5-ext/function/is-arguments)_\n\nWhether value is arguments object\n\n#### isFunction(arg) _(es5-ext/function/is-function)_\n\nWhether value is instance of function\n\n#### noop() _(es5-ext/function/noop)_\n\nNo operation function\n\n#### pluck(name) _(es5-ext/function/pluck)_\n\nReturns a function that takes an object, and returns the value of its _name_\nproperty\n\n_pluck(name)(obj) =def obj[name]_\n\n#### validFunction(arg) _(es5-ext/function/valid-function)_\n\nIf given object is not function throw TypeError in other case return it.\n\n### Function Prototype extensions\n\nSome of the methods were inspired by [Functional JavaScript](http://osteele.com/sources/javascript/functional/) project by Olivier Steele\n\n#### fn.compose([…fns]) _(es5-ext/function/#/compose)_\n\nApplies the functions in reverse argument-list order.\n\n_f1.compose(f2, f3, f4)(…args) =def f1(f2(f3(f4(…arg))))_\n\n#### fn.copy() _(es5-ext/function/#/copy)_\n\nProduces copy of given function\n\n#### fn.curry([n]) _(es5-ext/function/#/curry)_\n\nInvoking the function returned by this function only _n_ arguments are passed to the underlying function. If the underlying function is not saturated, the result is a function that passes all its arguments to the underlying function. \nIf _n_ is not provided then it defaults to context function length\n\n_f.curry(4)(arg1, arg2)(arg3)(arg4) =def f(arg1, args2, arg3, arg4)_\n\n#### fn.lock([…args]) _(es5-ext/function/#/lock)_\n\nReturns a function that applies the underlying function to _args_, and ignores its own arguments.\n\n_f.lock(…args)(…args2) =def f(…args)_\n\n_Named after it's counterpart in Google Closure_\n\n#### fn.not() _(es5-ext/function/#/not)_\n\nReturns a function that returns boolean negation of value returned by underlying function.\n\n_f.not()(…args) =def !f(…args)_\n\n#### fn.partial([…args]) _(es5-ext/function/#/partial)_\n\nReturns a function that when called will behave like context function called with initially passed arguments. If more arguments are suplilied, they are appended to initial args.\n\n_f.partial(…args1)(…args2) =def f(…args1, …args2)_\n\n#### fn.spread() _(es5-ext/function/#/spread)_\n\nReturns a function that applies underlying function with first list argument\n\n_f.match()(args) =def f.apply(null, args)_\n\n#### fn.toStringTokens() _(es5-ext/function/#/to-string-tokens)_\n\nSerializes function into two (arguments and body) string tokens. Result is plain object with `args` and `body` properties.\n\n### Math extensions\n\n#### acosh(x) _(es5-ext/math/acosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.acosh).\n\n#### asinh(x) _(es5-ext/math/asinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.asinh).\n\n#### atanh(x) _(es5-ext/math/atanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.atanh).\n\n#### cbrt(x) _(es5-ext/math/cbrt)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cbrt).\n\n#### clz32(x) _(es5-ext/math/clz32)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.clz32).\n\n#### cosh(x) _(es5-ext/math/cosh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.cosh).\n\n#### expm1(x) _(es5-ext/math/expm1)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.expm1).\n\n#### fround(x) _(es5-ext/math/fround)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.fround).\n\n#### hypot([…values]) _(es5-ext/math/hypot)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.hypot).\n\n#### imul(x, y) _(es5-ext/math/imul)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.imul).\n\n#### log1p(x) _(es5-ext/math/log1p)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log1p).\n\n#### log2(x) _(es5-ext/math/log2)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log2).\n\n#### log10(x) _(es5-ext/math/log10)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.log10).\n\n#### sign(x) _(es5-ext/math/sign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sign).\n\n#### sinh(x) _(es5-ext/math/sinh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.sinh).\n\n#### tanh(x) _(es5-ext/math/tanh)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.tanh).\n\n#### trunc(x) _(es5-ext/math/trunc)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-math.trunc).\n\n### Number Constructor extensions\n\n#### EPSILON _(es5-ext/number/epsilon)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.epsilon).\n\nThe difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 x 10-16.\n\n#### isFinite(x) _(es5-ext/number/is-finite)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite). \nWhether value is finite. Differs from global isNaN that it doesn't do type coercion.\n\n#### isInteger(x) _(es5-ext/number/is-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger). \nWhether value is integer.\n\n#### isNaN(x) _(es5-ext/number/is-nan)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isnan). \nWhether value is NaN. Differs from global isNaN that it doesn't do type coercion.\n\n#### isNumber(x) _(es5-ext/number/is-number)_\n\nWhether given value is number\n\n#### isSafeInteger(x) _(es5-ext/number/is-safe-integer)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.issafeinteger).\n\n#### MAX*SAFE_INTEGER *(es5-ext/number/max-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.maxsafeinteger). \nThe value of Number.MAX_SAFE_INTEGER is 9007199254740991.\n\n#### MIN*SAFE_INTEGER *(es5-ext/number/min-safe-integer)\\_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.minsafeinteger). \nThe value of Number.MIN_SAFE_INTEGER is -9007199254740991 (253-1).\n\n#### toInteger(x) _(es5-ext/number/to-integer)_\n\nConverts value to integer\n\n#### toPosInteger(x) _(es5-ext/number/to-pos-integer)_\n\nConverts value to positive integer. If provided value is less than 0, then 0 is returned\n\n#### toUint32(x) _(es5-ext/number/to-uint32)_\n\nConverts value to unsigned 32 bit integer. This type is used for array lengths.\nSee: http://www.2ality.com/2012/02/js-integers.html\n\n### Number Prototype extensions\n\n#### num.pad(length[, precision]) _(es5-ext/number/#/pad)_\n\nPad given number with zeros. Returns string\n\n### Object Constructor extensions\n\n#### assign(target, source[, …sourcen]) _(es5-ext/object/assign)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). \nExtend _target_ by enumerable own properties of other objects. If properties are already set on target object, they will be overwritten.\n\n#### clear(obj) _(es5-ext/object/clear)_\n\nRemove all enumerable own properties of the object\n\n#### compact(obj) _(es5-ext/object/compact)_\n\nReturns copy of the object with all enumerable properties that have no falsy values\n\n#### compare(obj1, obj2) _(es5-ext/object/compare)_\n\nUniversal cross-type compare function. To be used for e.g. array sort.\n\n#### copy(obj) _(es5-ext/object/copy)_\n\nReturns copy of the object with all enumerable properties.\n\n#### copyDeep(obj) _(es5-ext/object/copy-deep)_\n\nReturns deep copy of the object with all enumerable properties.\n\n#### count(obj) _(es5-ext/object/count)_\n\nCounts number of enumerable own properties on object\n\n#### create(obj[, properties]) _(es5-ext/object/create)_\n\n`Object.create` alternative that provides workaround for [V8 issue](http://code.google.com/p/v8/issues/detail?id=2804).\n\nWhen `null` is provided as a prototype, it's substituted with specially prepared object that derives from Object.prototype but has all Object.prototype properties shadowed with undefined.\n\nIt's quirky solution that allows us to have plain objects with no truthy properties but with turnable prototype.\n\nUse only for objects that you plan to switch prototypes of and be aware of limitations of this workaround.\n\n#### eq(x, y) _(es5-ext/object/eq)_\n\nWhether two values are equal, using [_SameValueZero_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### every(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/every)_\n\nAnalogous to Array.prototype.every. Returns true if every key-value pair in this object satisfies the provided testing function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### filter(obj, cb[, thisArg]) _(es5-ext/object/filter)_\n\nAnalogous to Array.prototype.filter. Returns new object with properites for which _cb_ function returned truthy value.\n\n#### firstKey(obj) _(es5-ext/object/first-key)_\n\nReturns first enumerable key of the object, as keys are unordered by specification, it can be any key of an object.\n\n#### flatten(obj) _(es5-ext/object/flatten)_\n\nReturns new object, with flatten properties of input object\n\n_flatten({ a: { b: 1 }, c: { d: 1 } }) =def { b: 1, d: 1 }_\n\n#### forEach(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/for-each)_\n\nAnalogous to Array.prototype.forEach. Calls a function for each key-value pair found in object\nOptionally _compareFn_ can be provided which assures that properties are iterated in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### getPropertyNames() _(es5-ext/object/get-property-names)_\n\nGet all (not just own) property names of the object\n\n#### is(x, y) _(es5-ext/object/is)_\n\nWhether two values are equal, using [_SameValue_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) algorithm.\n\n#### isArrayLike(x) _(es5-ext/object/is-array-like)_\n\nWhether object is array-like object\n\n#### isCopy(x, y) _(es5-ext/object/is-copy)_\n\nTwo values are considered a copy of same value when all of their own enumerable properties have same values.\n\n#### isCopyDeep(x, y) _(es5-ext/object/is-copy-deep)_\n\nDeep comparision of objects\n\n#### isEmpty(obj) _(es5-ext/object/is-empty)_\n\nTrue if object doesn't have any own enumerable property\n\n#### isObject(arg) _(es5-ext/object/is-object)_\n\nWhether value is not primitive\n\n#### isPlainObject(arg) _(es5-ext/object/is-plain-object)_\n\nWhether object is plain object, its protototype should be Object.prototype and it cannot be host object.\n\n#### keyOf(obj, searchValue) _(es5-ext/object/key-of)_\n\nSearch object for value\n\n#### keys(obj) _(es5-ext/object/keys)_\n\n[_Updated with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys). \nES6's version of `keys`, doesn't throw on primitive input\n\n#### map(obj, cb[, thisArg]) _(es5-ext/object/map)_\n\nAnalogous to Array.prototype.map. Creates a new object with properties which values are results of calling a provided function on every key-value pair in this object.\n\n#### mapKeys(obj, cb[, thisArg]) _(es5-ext/object/map-keys)_\n\nCreate new object with same values, but remapped keys\n\n#### mixin(target, source) _(es5-ext/object/mixin)_\n\nExtend _target_ by all own properties of other objects. Properties found in both objects will be overwritten (unless they're not configurable and cannot be overwritten).\n_It was for a moment part of ECMAScript 6 draft._\n\n#### mixinPrototypes(target, …source]) _(es5-ext/object/mixin-prototypes)_\n\nExtends _target_, with all source and source's prototype properties.\nUseful as an alternative for `setPrototypeOf` in environments in which it cannot be shimmed (no `__proto__` support).\n\n#### normalizeOptions(options) _(es5-ext/object/normalize-options)_\n\nNormalizes options object into flat plain object.\n\nUseful for functions in which we either need to keep options object for future reference or need to modify it for internal use.\n\n* It never returns input `options` object back (always a copy is created)\n* `options` can be undefined in such case empty plain object is returned.\n* Copies all enumerable properties found down prototype chain.\n\n#### primitiveSet([…names]) _(es5-ext/object/primitive-set)_\n\nCreates `null` prototype based plain object, and sets on it all property names provided in arguments to true.\n\n#### safeTraverse(obj[, …names]) _(es5-ext/object/safe-traverse)_\n\nSafe navigation of object properties. See http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator\n\n#### serialize(value) _(es5-ext/object/serialize)_\n\nSerialize value into string. Differs from [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that it serializes also dates, functions and regular expresssions.\n\n#### setPrototypeOf(object, proto) _(es5-ext/object/set-prototype-of)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof). \nIf native version is not provided, it depends on existence of `__proto__` functionality, if it's missing, `null` instead of function is exposed.\n\n#### some(obj, cb[, thisArg[, compareFn]]) _(es5-ext/object/some)_\n\nAnalogous to Array.prototype.some Returns true if any key-value pair satisfies the provided\ntesting function. \nOptionally _compareFn_ can be provided which assures that keys are tested in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### toArray(obj[, cb[, thisArg[, compareFn]]]) _(es5-ext/object/to-array)_\n\nCreates an array of results of calling a provided function on every key-value pair in this object. \nOptionally _compareFn_ can be provided which assures that results are added in given order. If provided _compareFn_ is equal to `true`, then order is alphabetical (by key).\n\n#### unserialize(str) _(es5-ext/object/unserialize)_\n\nUserializes value previously serialized with [serialize](#serializevalue-es5-extobjectserialize)\n\n#### validCallable(x) _(es5-ext/object/valid-callable)_\n\nIf given object is not callable throw TypeError in other case return it.\n\n#### validObject(x) _(es5-ext/object/valid-object)_\n\nThrows error if given value is not an object, otherwise it is returned.\n\n#### validValue(x) _(es5-ext/object/valid-value)_\n\nThrows error if given value is `null` or `undefined`, otherwise returns value.\n\n### RegExp Constructor extensions\n\n#### escape(str) _(es5-ext/reg-exp/escape)_\n\nEscapes string to be used in regular expression\n\n#### isRegExp(x) _(es5-ext/reg-exp/is-reg-exp)_\n\nWhether object is regular expression\n\n#### validRegExp(x) _(es5-ext/reg-exp/valid-reg-exp)_\n\nIf object is regular expression it is returned, otherwise TypeError is thrown.\n\n### RegExp Prototype extensions\n\n#### re.isSticky(x) _(es5-ext/reg-exp/#/is-sticky)_\n\nWhether regular expression has `sticky` flag.\n\nIt's to be used as counterpart to [regExp.sticky](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky) if it's not implemented.\n\n#### re.isUnicode(x) _(es5-ext/reg-exp/#/is-unicode)_\n\nWhether regular expression has `unicode` flag.\n\nIt's to be used as counterpart to [regExp.unicode](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode) if it's not implemented.\n\n#### re.match(string) _(es5-ext/reg-exp/#/match)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.match).\n\n#### re.replace(string, replaceValue) _(es5-ext/reg-exp/#/replace)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.replace).\n\n#### re.search(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.search).\n\n#### re.split(string) _(es5-ext/reg-exp/#/search)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.split).\n\n#### re.sticky _(es5-ext/reg-exp/#/sticky/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.sticky). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n#### re.unicode _(es5-ext/reg-exp/#/unicode/implement)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-regexp.prototype.unicode). \nIt's a getter, so only `implement` and `is-implemented` modules are provided.\n\n### String Constructor extensions\n\n#### formatMethod(fMap) _(es5-ext/string/format-method)_\n\nCreates format method. It's used e.g. to create `Date.prototype.format` method\n\n#### fromCodePoint([…codePoints]) _(es5-ext/string/from-code-point)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.fromcodepoint)\n\n#### isString(x) _(es5-ext/string/is-string)_\n\nWhether object is string\n\n#### randomUniq() _(es5-ext/string/random-uniq)_\n\nReturns randomly generated id, with guarantee of local uniqueness (no same id will be returned twice)\n\n#### raw(callSite[, …substitutions]) _(es5-ext/string/raw)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.raw)\n\n### String Prototype extensions\n\n#### str.at(pos) _(es5-ext/string/#/at)_\n\n_Proposed for ECMAScript 6/7 standard, but not (yet) in a draft_\n\nReturns a string at given position in Unicode-safe manner.\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.at).\n\n#### str.camelToHyphen() _(es5-ext/string/#/camel-to-hyphen)_\n\nConvert camelCase string to hyphen separated, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from js property convention into filename convention.\n\n#### str.capitalize() _(es5-ext/string/#/capitalize)_\n\nCapitalize first character of a string\n\n#### str.caseInsensitiveCompare(str) _(es5-ext/string/#/case-insensitive-compare)_\n\nCase insensitive compare\n\n#### str.codePointAt(pos) _(es5-ext/string/#/code-point-at)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.codepointat)\n\nBased on [implementation by Mathias Bynens](https://github.com/mathiasbynens/String.prototype.codePointAt).\n\n#### str.contains(searchString[, position]) _(es5-ext/string/#/contains)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains)\n\nWhether string contains given string.\n\n#### str.endsWith(searchString[, endPosition]) _(es5-ext/string/#/ends-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith). \nWhether strings ends with given string\n\n#### str.hyphenToCamel() _(es5-ext/string/#/hyphen-to-camel)_\n\nConvert hyphen separated string to camelCase, e.g. one-two-three -> oneTwoThree.\nUseful when converting names from filename convention to js property name convention.\n\n#### str.indent(str[, count]) _(es5-ext/string/#/indent)_\n\nIndents each line with provided _str_ (if _count_ given then _str_ is repeated _count_ times).\n\n#### str.last() _(es5-ext/string/#/last)_\n\nReturn last character\n\n#### str.normalize([form]) _(es5-ext/string/#/normalize)_\n\n[_Introduced with ECMAScript 6_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). \nReturns the Unicode Normalization Form of a given string. \nBased on Matsuza's version. Code used for integrated shim can be found at [github.com/walling/unorm](https://github.com/walling/unorm/blob/master/lib/unorm.js)\n\n#### str.pad(fill[, length]) _(es5-ext/string/#/pad)_\n\nPad string with _fill_.\nIf _length_ si given than _fill_ is reapated _length_ times.\nIf _length_ is negative then pad is applied from right.\n\n#### str.repeat(n) _(es5-ext/string/#/repeat)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.repeat). \nRepeat given string _n_ times\n\n#### str.plainReplace(search, replace) _(es5-ext/string/#/plain-replace)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces just first occurrence of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.plainReplaceAll(search, replace) _(es5-ext/string/#/plain-replace-all)_\n\nSimple `replace` version. Doesn't support regular expressions. Replaces all occurrences of search string. Doesn't support insert patterns, therefore it is safe to replace text with text obtained programmatically (there's no need for additional _$_ characters escape in such case).\n\n#### str.startsWith(searchString[, position]) _(es5-ext/string/#/starts-with)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith). \nWhether strings starts with given string\n\n#### str[@@iterator] _(es5-ext/string/#/@@iterator)_\n\n[_Introduced with ECMAScript 6_](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype-@@iterator). \nReturns iterator object which traverses all string characters (with respect to unicode symbols)\n\n### Tests\n\n $ npm test\n\n[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/es5-ext/branches/master/shields_badge.svg\n[nix-build-url]: https://semaphoreci.com/medikoo-org/es5-ext\n[win-build-image]: https://ci.appveyor.com/api/projects/status/3jox67ksw3p8hkwh?svg=true\n[win-build-url]: https://ci.appveyor.com/project/medikoo/es5-ext\n[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg\n[npm-image]: https://img.shields.io/npm/v/es5-ext.svg\n[npm-url]: https://www.npmjs.com/package/es5-ext\n", + "readmeFilename": "README.md", + "bugs": "[Circular]", + "homepage": "https://github.com/medikoo/es5-ext#readme", + "_id": "es5-ext@0.10.45", + "_requested": { + "type": "version", + "registry": true, + "raw": "es5-ext@0.10.45", + "name": "es5-ext", + "escapedName": "es5-ext", + "rawSpec": "0.10.45", + "saveSpec": "[Circular]", + "fetchSpec": "0.10.45" + }, + "_spec": "0.10.45", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/es5-ext", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "tad": "~0.2.3", + "xlint": "~0.2.2", + "xlint-jslint-medikoo": "~0.1.4" + }, + "scripts": { + "lint": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --no-cache --no-stream", + "lint-console": "node node_modules/xlint/bin/xlint --linter=node_modules/xlint-jslint-medikoo/index.js --watch", + "test": "node ./node_modules/tad/bin/tad" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz", + "_integrity": "sha1-jWPd+0z+H647MsomXExyAiIIC7U=", + "_from": "event-emitter@0.3.4", + "readme": "# event-emitter\n## Environment agnostic event emitter\n\n### Installation\n\n\t$ npm install event-emitter\n\t\nTo port it to Browser or any other (non CJS) environment, use your favorite CJS bundler. No favorite yet? Try: [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake) or [Webpack](http://webpack.github.io/)\n\n### Usage\n\n```javascript\nvar ee = require('event-emitter');\n\nvar emitter = ee({}), listener;\n\nemitter.on('test', listener = function (args) {\n // …emitter logic\n});\n\nemitter.once('test', function (args) {\n // …invoked only once(!)\n});\n\nemitter.emit('test', arg1, arg2/*…args*/); // Two above listeners invoked\nemitter.emit('test', arg1, arg2/*…args*/); // Only first listener invoked\n\nemitter.off('test', listener); // Removed first listener\nemitter.emit('test', arg1, arg2/*…args*/); // No listeners invoked\n```\n### Additional utilities\n\n#### allOff(obj) _(event-emitter/all-off)_\n\nRemoves all listeners from given event emitter object\n\n#### hasListeners(obj[, name]) _(event-emitter/has-listeners)_\n\nWhether object has some listeners attached to the object.\nWhen `name` is provided, it checks listeners for specific event name\n\n```javascript\nvar emitter = ee();\nvar hasListeners = require('event-emitter/has-listeners');\nvar listener = function () {};\n\nhasListeners(emitter); // false\n\nemitter.on('foo', listener);\nhasListeners(emitter); // true\nhasListeners(emitter, 'foo'); // true\nhasListeners(emitter, 'bar'); // false\n\nemitter.off('foo', listener);\nhasListeners(emitter, 'foo'); // false\n```\n\n#### pipe(source, target[, emitMethodName]) _(event-emitter/pipe)_\n\nPipes all events from _source_ emitter onto _target_ emitter (all events from _source_ emitter will be emitted also on _target_ emitter, but not other way). \nReturns _pipe_ object which exposes `pipe.close` function. Invoke it to close configured _pipe_. \nIt works internally by redefinition of `emit` method, if in your interface this method is referenced differently, provide its name (or symbol) with third argument.\n\n#### unify(emitter1, emitter2) _(event-emitter/unify)_\n\nUnifies event handling for two objects. Events emitted on _emitter1_ would be also emitter on _emitter2_, and other way back. \nNon reversible.\n\n```javascript\nvar eeUnify = require('event-emitter/unify');\n\nvar emitter1 = ee(), listener1, listener3;\nvar emitter2 = ee(), listener2, listener4;\n\nemitter1.on('test', listener1 = function () { });\nemitter2.on('test', listener2 = function () { });\n\nemitter1.emit('test'); // Invoked listener1\nemitter2.emit('test'); // Invoked listener2\n\nvar unify = eeUnify(emitter1, emitter2);\n\nemitter1.emit('test'); // Invoked listener1 and listener2\nemitter2.emit('test'); // Invoked listener1 and listener2\n\nemitter1.on('test', listener3 = function () { });\nemitter2.on('test', listener4 = function () { });\n\nemitter1.emit('test'); // Invoked listener1, listener2, listener3 and listener4\nemitter2.emit('test'); // Invoked listener1, listener2, listener3 and listener4\n```\n\n### Tests [![Build Status](https://travis-ci.org/medikoo/event-emitter.png)](https://travis-ci.org/medikoo/event-emitter)\n\n\t$ npm test\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/medikoo/event-emitter/issues" + }, + "homepage": "https://github.com/medikoo/event-emitter#readme", + "_id": "event-emitter@0.3.4", + "_requested": { + "type": "version", + "registry": true, + "raw": "event-emitter@0.3.4", + "name": "event-emitter", + "escapedName": "event-emitter", + "rawSpec": "0.3.4", + "saveSpec": "[Circular]", + "fetchSpec": "0.3.4" + }, + "_spec": "0.3.4", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "event-emitter@0.3.4", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "es5-ext": "~0.10.7", + "d": "~0.1.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/event-emitter", + "error": "[Circular]", + "extraneous": false + }, + "superagent": { + "name": "superagent", + "version": "3.8.2", + "description": "elegant & feature rich browser / node HTTP with a fluent API", + "scripts": { + "prepare": "make all", + "test": "make test" + }, + "keywords": [ + "http", + "ajax", + "request", + "agent" + ], + "license": "MIT", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Kornel Lesiński", + "email": "kornel@geekhood.net" + }, + { + "name": "Peter Lyons", + "email": "pete@peterlyons.com" + }, + { + "name": "Hunter Loftis", + "email": "hunter@hunterloftis.com" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/superagent.git" + }, + "dependencies": { + "component-emitter": { + "name": "component-emitter", + "description": "Event emitter", + "version": "1.2.1", + "license": "MIT", + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "component": { + "scripts": { + "emitter/index.js": "index.js" + } + }, + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/component/emitter.git" + }, + "scripts": { + "test": "make test" + }, + "files": [ + "index.js", + "LICENSE" + ], + "_resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "_integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "_from": "component-emitter@1.2.1", + "readme": "# Emitter [![Build Status](https://travis-ci.org/component/emitter.png)](https://travis-ci.org/component/emitter)\r\n\r\n Event emitter component.\r\n\r\n## Installation\r\n\r\n```\r\n$ component install component/emitter\r\n```\r\n\r\n## API\r\n\r\n### Emitter(obj)\r\n\r\n The `Emitter` may also be used as a mixin. For example\r\n a \"plain\" object may become an emitter, or you may\r\n extend an existing prototype.\r\n\r\n As an `Emitter` instance:\r\n\r\n```js\r\nvar Emitter = require('emitter');\r\nvar emitter = new Emitter;\r\nemitter.emit('something');\r\n```\r\n\r\n As a mixin:\r\n\r\n```js\r\nvar Emitter = require('emitter');\r\nvar user = { name: 'tobi' };\r\nEmitter(user);\r\n\r\nuser.emit('im a user');\r\n```\r\n\r\n As a prototype mixin:\r\n\r\n```js\r\nvar Emitter = require('emitter');\r\nEmitter(User.prototype);\r\n```\r\n\r\n### Emitter#on(event, fn)\r\n\r\n Register an `event` handler `fn`.\r\n\r\n### Emitter#once(event, fn)\r\n\r\n Register a single-shot `event` handler `fn`,\r\n removed immediately after it is invoked the\r\n first time.\r\n\r\n### Emitter#off(event, fn)\r\n\r\n * Pass `event` and `fn` to remove a listener.\r\n * Pass `event` to remove all listeners on that event.\r\n * Pass nothing to remove all listeners on all events.\r\n\r\n### Emitter#emit(event, ...)\r\n\r\n Emit an `event` with variable option args.\r\n\r\n### Emitter#listeners(event)\r\n\r\n Return an array of callbacks, or an empty array.\r\n\r\n### Emitter#hasListeners(event)\r\n\r\n Check if this emitter has `event` handlers.\r\n\r\n## License\r\n\r\nMIT\r\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/component/emitter/issues" + }, + "homepage": "https://github.com/component/emitter#readme", + "_id": "component-emitter@1.2.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "component-emitter@1.2.1", + "name": "component-emitter", + "escapedName": "component-emitter", + "rawSpec": "1.2.1", + "saveSpec": "[Circular]", + "fetchSpec": "1.2.1" + }, + "_spec": "1.2.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "component-emitter@1.2.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/component-emitter", + "error": "[Circular]", + "extraneous": false + }, + "cookiejar": { + "name": "cookiejar", + "version": "2.1.2", + "author": { + "name": "bradleymeck" + }, + "main": "cookiejar.js", + "description": "simple persistent cookiejar system", + "files": [ + "cookiejar.js" + ], + "license": "MIT", + "jshintConfig": { + "node": true + }, + "scripts": { + "test": "node tests/test.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bmeck/node-cookiejar.git" + }, + "devDependencies": { + "jshint": "^2.9.4" + }, + "_resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "_integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "_from": "cookiejar@2.1.2", + "readme": "# CookieJar\n\n[![NPM version](http://img.shields.io/npm/v/cookiejar.svg)](https://www.npmjs.org/package/cookiejar)\n[![devDependency Status](https://david-dm.org/bmeck/node-cookiejar/dev-status.svg)](https://david-dm.org/bmeck/node-cookiejar?type=dev)\n\nSimple robust cookie library\n\n## Exports\n\n### CookieAccessInfo(domain,path,secure,script)\n\nclass to determine matching qualities of a cookie\n\n##### Properties\n\n* String domain - domain to match\n* String path - path to match\n* Boolean secure - access is secure (ssl generally)\n* Boolean script - access is from a script\n\n\n### Cookie(cookiestr_or_cookie, request_domain, request_path)\n\nIt turns input into a Cookie (singleton if given a Cookie),\nthe `request_domain` argument is used to default the domain if it is not explicit in the cookie string,\nthe `request_path` argument is used to set the path if it is not explicit in a cookie String.\n\nExplicit domains/paths will cascade, implied domains/paths must *exactly* match (see http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Pat).\n\n##### Properties\n\n* String name - name of the cookie\n* String value - string associated with the cookie\n* String domain - domain to match (on a cookie a '.' at the start means a wildcard matching anything ending in the rest)\n* Boolean explicit_domain - if the domain was explicitly set via the cookie string\n* String path - base path to match (matches any path starting with this '/' is root)\n* Boolean explicit_path - if the path was explicitly set via the cookie string\n* Boolean noscript - if it should be kept from scripts\n* Boolean secure - should it only be transmitted over secure means\n* Number expiration_date - number of millis since 1970 at which this should be removed\n\n##### Methods\n\n* `String toString()` - the __set-cookie:__ string for this cookie\n* `String toValueString()` - the __cookie:__ string for this cookie\n* `Cookie parse(cookiestr, request_domain, request_path)` - parses the string onto this cookie or a new one if called directly\n* `Boolean matches(access_info)` - returns true if the access_info allows retrieval of this cookie\n* `Boolean collidesWith(cookie)` - returns true if the cookies cannot exist in the same space (domain and path match)\n\n\n### CookieJar()\n\nclass to hold numerous cookies from multiple domains correctly\n\n##### Methods\n\n* `Cookie setCookie(cookie, request_domain, request_path)` - modify (or add if not already-existing) a cookie to the jar\n* `Cookie[] setCookies(cookiestr_or_list, request_domain, request_path)` - modify (or add if not already-existing) a large number of cookies to the jar\n* `Cookie getCookie(cookie_name,access_info)` - get a cookie with the name and access_info matching\n* `Cookie[] getCookies(access_info)` - grab all cookies matching this access_info\n", + "readmeFilename": "readme.md", + "bugs": { + "url": "https://github.com/bmeck/node-cookiejar/issues" + }, + "homepage": "https://github.com/bmeck/node-cookiejar#readme", + "_id": "cookiejar@2.1.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "cookiejar@2.1.2", + "name": "cookiejar", + "escapedName": "cookiejar", + "rawSpec": "2.1.2", + "saveSpec": "[Circular]", + "fetchSpec": "2.1.2" + }, + "_spec": "2.1.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "cookiejar@2.1.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/cookiejar", + "error": "[Circular]", + "extraneous": false + }, + "debug": { + "name": "debug", + "version": "3.1.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + }, + { + "name": "Andrew Rhyne", + "email": "rhyneandrew@gmail.com" + } + ], + "license": "MIT", + "dependencies": { + "ms": { + "name": "ms", + "version": "2.0.0", + "description": "Tiny milisecond conversion utility", + "repository": { + "type": "git", + "url": "git+https://github.com/zeit/ms.git" + }, + "main": "./index", + "files": [ + "index.js" + ], + "scripts": { + "precommit": "lint-staged", + "lint": "eslint lib/* bin/*", + "test": "mocha tests.js" + }, + "eslintConfig": { + "extends": "eslint:recommended", + "env": { + "node": true, + "es6": true + } + }, + "lint-staged": { + "*.js": [ + "npm run lint", + "prettier --single-quote --write", + "git add" + ] + }, + "license": "MIT", + "devDependencies": { + "eslint": "3.19.0", + "expect.js": "0.3.1", + "husky": "0.13.3", + "lint-staged": "3.4.1", + "mocha": "3.4.1" + }, + "_resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "_integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "_from": "ms@2.0.0", + "readme": "# ms\n\n[![Build Status](https://travis-ci.org/zeit/ms.svg?branch=master)](https://travis-ci.org/zeit/ms)\n[![Slack Channel](http://zeit-slackin.now.sh/badge.svg)](https://zeit.chat/)\n\nUse this package to easily convert various time formats to milliseconds.\n\n## Examples\n\n```js\nms('2 days') // 172800000\nms('1d') // 86400000\nms('10h') // 36000000\nms('2.5 hrs') // 9000000\nms('2h') // 7200000\nms('1m') // 60000\nms('5s') // 5000\nms('1y') // 31557600000\nms('100') // 100\n```\n\n### Convert from milliseconds\n\n```js\nms(60000) // \"1m\"\nms(2 * 60000) // \"2m\"\nms(ms('10 hours')) // \"10h\"\n```\n\n### Time format written-out\n\n```js\nms(60000, { long: true }) // \"1 minute\"\nms(2 * 60000, { long: true }) // \"2 minutes\"\nms(ms('10 hours'), { long: true }) // \"10 hours\"\n```\n\n## Features\n\n- Works both in [node](https://nodejs.org) and in the browser.\n- If a number is supplied to `ms`, a string with a unit is returned.\n- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`).\n- If you pass a string with a number and a valid unit, the number of equivalent ms is returned.\n\n## Caught a bug?\n\n1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device\n2. Link the package to the global module directory: `npm link`\n3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, node will now use your clone of ms!\n\nAs always, you can run the tests using: `npm test`\n", + "readmeFilename": "readme.md", + "bugs": { + "url": "https://github.com/zeit/ms/issues" + }, + "homepage": "https://github.com/zeit/ms#readme", + "_id": "ms@2.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "ms@2.0.0", + "name": "ms", + "escapedName": "ms", + "rawSpec": "2.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.0" + }, + "_spec": "2.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "ms@2.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/ms", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "browserify": "14.4.0", + "chai": "^3.5.0", + "concurrently": "^3.1.0", + "coveralls": "^2.11.15", + "eslint": "^3.12.1", + "istanbul": "^0.4.5", + "karma": "^1.3.0", + "karma-chai": "^0.1.0", + "karma-mocha": "^1.3.0", + "karma-phantomjs-launcher": "^1.0.2", + "karma-sinon": "^1.0.5", + "mocha": "^3.2.0", + "mocha-lcov-reporter": "^1.2.0", + "rimraf": "^2.5.4", + "sinon": "^1.17.6", + "sinon-chai": "^2.8.0" + }, + "main": "./src/index.js", + "browser": "./src/browser.js", + "_resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "_integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "_from": "debug@3.1.0", + "readme": "# debug\n[![Build Status](https://travis-ci.org/visionmedia/debug.svg?branch=master)](https://travis-ci.org/visionmedia/debug) [![Coverage Status](https://coveralls.io/repos/github/visionmedia/debug/badge.svg?branch=master)](https://coveralls.io/github/visionmedia/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)\n[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)\n\n\n\nA tiny JavaScript debugging utility modelled after Node.js core's debugging\ntechnique. Works in Node.js and web browsers.\n\n## Installation\n\n```bash\n$ npm install debug\n```\n\n## Usage\n\n`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.\n\nExample [_app.js_](./examples/node/app.js):\n\n```js\nvar debug = require('debug')('http')\n , http = require('http')\n , name = 'My App';\n\n// fake app\n\ndebug('booting %o', name);\n\nhttp.createServer(function(req, res){\n debug(req.method + ' ' + req.url);\n res.end('hello\\n');\n}).listen(3000, function(){\n debug('listening');\n});\n\n// fake worker of some kind\n\nrequire('./worker');\n```\n\nExample [_worker.js_](./examples/node/worker.js):\n\n```js\nvar a = require('debug')('worker:a')\n , b = require('debug')('worker:b');\n\nfunction work() {\n a('doing lots of uninteresting work');\n setTimeout(work, Math.random() * 1000);\n}\n\nwork();\n\nfunction workb() {\n b('doing some work');\n setTimeout(workb, Math.random() * 2000);\n}\n\nworkb();\n```\n\nThe `DEBUG` environment variable is then used to enable these based on space or\ncomma-delimited names.\n\nHere are some examples:\n\n\"screen\n\"screen\n\"screen\n\n#### Windows note\n\nOn Windows the environment variable is set using the `set` command.\n\n```cmd\nset DEBUG=*,-not_this\n```\n\nNote that PowerShell uses different syntax to set environment variables.\n\n```cmd\n$env:DEBUG = \"*,-not_this\"\n```\n\nThen, run the program to be debugged as usual.\n\n\n## Namespace Colors\n\nEvery debug instance has a color generated for it based on its namespace name.\nThis helps when visually parsing the debug output to identify which debug instance\na debug line belongs to.\n\n#### Node.js\n\nIn Node.js, colors are enabled when stderr is a TTY. You also _should_ install\nthe [`supports-color`](https://npmjs.org/supports-color) module alongside debug,\notherwise debug will only use a small handful of basic colors.\n\n\n\n#### Web Browser\n\nColors are also enabled on \"Web Inspectors\" that understand the `%c` formatting\noption. These are WebKit web inspectors, Firefox ([since version\n31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))\nand the Firebug plugin for Firefox (any version).\n\n\n\n\n## Millisecond diff\n\nWhen actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the \"+NNNms\" will show you how much time was spent between calls.\n\n\n\nWhen stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:\n\n\n\n\n## Conventions\n\nIf you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use \":\" to separate features. For example \"bodyParser\" from Connect would then be \"connect:bodyParser\". If you append a \"*\" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.\n\n## Wildcards\n\nThe `*` character may be used as a wildcard. Suppose for example your library has\ndebuggers named \"connect:bodyParser\", \"connect:compress\", \"connect:session\",\ninstead of listing all three with\n`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do\n`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.\n\nYou can also exclude specific debuggers by prefixing them with a \"-\" character.\nFor example, `DEBUG=*,-connect:*` would include all debuggers except those\nstarting with \"connect:\".\n\n## Environment Variables\n\nWhen running through Node.js, you can set a few environment variables that will\nchange the behavior of the debug logging:\n\n| Name | Purpose |\n|-----------|-------------------------------------------------|\n| `DEBUG` | Enables/disables specific debugging namespaces. |\n| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |\n| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |\n| `DEBUG_DEPTH` | Object inspection depth. |\n| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |\n\n\n__Note:__ The environment variables beginning with `DEBUG_` end up being\nconverted into an Options object that gets used with `%o`/`%O` formatters.\nSee the Node.js documentation for\n[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)\nfor the complete list.\n\n## Formatters\n\nDebug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.\nBelow are the officially supported formatters:\n\n| Formatter | Representation |\n|-----------|----------------|\n| `%O` | Pretty-print an Object on multiple lines. |\n| `%o` | Pretty-print an Object all on a single line. |\n| `%s` | String. |\n| `%d` | Number (both integer and float). |\n| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |\n| `%%` | Single percent sign ('%'). This does not consume an argument. |\n\n\n### Custom formatters\n\nYou can add custom formatters by extending the `debug.formatters` object.\nFor example, if you wanted to add support for rendering a Buffer as hex with\n`%h`, you could do something like:\n\n```js\nconst createDebug = require('debug')\ncreateDebug.formatters.h = (v) => {\n return v.toString('hex')\n}\n\n// …elsewhere\nconst debug = createDebug('foo')\ndebug('this is hex: %h', new Buffer('hello world'))\n// foo this is hex: 68656c6c6f20776f726c6421 +0ms\n```\n\n\n## Browser Support\n\nYou can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),\nor just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),\nif you don't want to build it yourself.\n\nDebug's enable state is currently persisted by `localStorage`.\nConsider the situation shown below where you have `worker:a` and `worker:b`,\nand wish to debug both. You can enable this using `localStorage.debug`:\n\n```js\nlocalStorage.debug = 'worker:*'\n```\n\nAnd then refresh the page.\n\n```js\na = debug('worker:a');\nb = debug('worker:b');\n\nsetInterval(function(){\n a('doing some work');\n}, 1000);\n\nsetInterval(function(){\n b('doing some work');\n}, 1200);\n```\n\n\n## Output streams\n\n By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:\n\nExample [_stdout.js_](./examples/node/stdout.js):\n\n```js\nvar debug = require('debug');\nvar error = debug('app:error');\n\n// by default stderr is used\nerror('goes to stderr!');\n\nvar log = debug('app:log');\n// set this namespace to log via console.log\nlog.log = console.log.bind(console); // don't forget to bind to console!\nlog('goes to stdout');\nerror('still goes to stderr!');\n\n// set all output to go via console.info\n// overrides all per-namespace log settings\ndebug.log = console.info.bind(console);\nerror('now goes to stdout via console.info');\nlog('still goes to stdout, but via console.info now');\n```\n\n## Checking whether a debug target is enabled\n\nAfter you've created a debug instance, you can determine whether or not it is\nenabled by checking the `enabled` property:\n\n```javascript\nconst debug = require('debug')('http');\n\nif (debug.enabled) {\n // do stuff...\n}\n```\n\nYou can also manually toggle this property to force the debug instance to be\nenabled or disabled.\n\n\n## Authors\n\n - TJ Holowaychuk\n - Nathan Rajlich\n - Andrew Rhyne\n\n## Backers\n\nSupport us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Sponsors\n\nBecome a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug#readme", + "_id": "debug@3.1.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "debug@3.1.0", + "name": "debug", + "escapedName": "debug", + "rawSpec": "3.1.0", + "saveSpec": "[Circular]", + "fetchSpec": "3.1.0" + }, + "_spec": "3.1.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "debug@3.1.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "ms": "2.0.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/debug", + "error": "[Circular]", + "extraneous": false + }, + "extend": { + "name": "extend", + "author": { + "name": "Stefan Thomas", + "email": "justmoon@members.fsf.org", + "url": "http://www.justmoon.net" + }, + "version": "3.0.2", + "description": "Port of jQuery.extend for node.js and the browser", + "main": "index", + "scripts": { + "pretest": "npm run lint", + "test": "npm run tests-only", + "posttest": "npm run coverage-quiet", + "tests-only": "node test", + "coverage": "covert test/index.js", + "coverage-quiet": "covert test/index.js --quiet", + "lint": "npm run jscs && npm run eslint", + "jscs": "jscs *.js */*.js", + "eslint": "eslint *.js */*.js" + }, + "contributors": [ + { + "name": "Jordan Harband", + "url": "https://github.com/ljharb" + } + ], + "keywords": [ + "extend", + "clone", + "merge" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/justmoon/node-extend.git" + }, + "dependencies": {}, + "devDependencies": { + "@ljharb/eslint-config": "^12.2.1", + "covert": "^1.1.0", + "eslint": "^4.19.1", + "jscs": "^3.0.7", + "tape": "^4.9.1" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "_integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "_from": "extend@3.0.2", + "readme": "[![Build Status][travis-svg]][travis-url]\n[![dependency status][deps-svg]][deps-url]\n[![dev dependency status][dev-deps-svg]][dev-deps-url]\n\n# extend() for Node.js [![Version Badge][npm-version-png]][npm-url]\n\n`node-extend` is a port of the classic extend() method from jQuery. It behaves as you expect. It is simple, tried and true.\n\nNotes:\n\n* Since Node.js >= 4,\n [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)\n now offers the same functionality natively (but without the \"deep copy\" option).\n See [ECMAScript 2015 (ES6) in Node.js](https://nodejs.org/en/docs/es6).\n* Some native implementations of `Object.assign` in both Node.js and many\n browsers (since NPM modules are for the browser too) may not be fully\n spec-compliant.\n Check [`object.assign`](https://www.npmjs.com/package/object.assign) module for\n a compliant candidate.\n\n## Installation\n\nThis package is available on [npm][npm-url] as: `extend`\n\n``` sh\nnpm install extend\n```\n\n## Usage\n\n**Syntax:** extend **(** [`deep`], `target`, `object1`, [`objectN`] **)**\n\n*Extend one object with one or more others, returning the modified object.*\n\n**Example:**\n\n``` js\nvar extend = require('extend');\nextend(targetObject, object1, object2);\n```\n\nKeep in mind that the target object will be modified, and will be returned from extend().\n\nIf a boolean true is specified as the first argument, extend performs a deep copy, recursively copying any objects it finds. Otherwise, the copy will share structure with the original object(s).\nUndefined properties are not copied. However, properties inherited from the object's prototype will be copied over.\nWarning: passing `false` as the first argument is not supported.\n\n### Arguments\n\n* `deep` *Boolean* (optional)\nIf set, the merge becomes recursive (i.e. deep copy).\n* `target`\t*Object*\nThe object to extend.\n* `object1`\t*Object*\nThe object that will be merged into the first.\n* `objectN` *Object* (Optional)\nMore objects to merge into the first.\n\n## License\n\n`node-extend` is licensed under the [MIT License][mit-license-url].\n\n## Acknowledgements\n\nAll credit to the jQuery authors for perfecting this amazing utility.\n\nPorted to Node.js by [Stefan Thomas][github-justmoon] with contributions by [Jonathan Buchanan][github-insin] and [Jordan Harband][github-ljharb].\n\n[travis-svg]: https://travis-ci.org/justmoon/node-extend.svg\n[travis-url]: https://travis-ci.org/justmoon/node-extend\n[npm-url]: https://npmjs.org/package/extend\n[mit-license-url]: http://opensource.org/licenses/MIT\n[github-justmoon]: https://github.com/justmoon\n[github-insin]: https://github.com/insin\n[github-ljharb]: https://github.com/ljharb\n[npm-version-png]: http://versionbadg.es/justmoon/node-extend.svg\n[deps-svg]: https://david-dm.org/justmoon/node-extend.svg\n[deps-url]: https://david-dm.org/justmoon/node-extend\n[dev-deps-svg]: https://david-dm.org/justmoon/node-extend/dev-status.svg\n[dev-deps-url]: https://david-dm.org/justmoon/node-extend#info=devDependencies\n\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/justmoon/node-extend/issues" + }, + "homepage": "https://github.com/justmoon/node-extend#readme", + "_id": "extend@3.0.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "extend@3.0.2", + "name": "extend", + "escapedName": "extend", + "rawSpec": "3.0.2", + "saveSpec": "[Circular]", + "fetchSpec": "3.0.2" + }, + "_spec": "3.0.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "extend@3.0.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/extend", + "error": "[Circular]", + "extraneous": false + }, + "form-data": { + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "name": "form-data", + "description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", + "version": "2.3.2", + "repository": { + "type": "git", + "url": "git://github.com/form-data/form-data.git" + }, + "main": "./lib/form_data", + "browser": "./lib/browser", + "scripts": { + "pretest": "rimraf coverage test/tmp", + "test": "istanbul cover test/run.js", + "posttest": "istanbul report lcov text", + "lint": "eslint lib/*.js test/*.js test/integration/*.js", + "report": "istanbul report lcov text", + "ci-lint": "is-node-modern 6 && npm run lint || is-node-not-modern 6", + "ci-test": "npm run test && npm run browser && npm run report", + "predebug": "rimraf coverage test/tmp", + "debug": "verbose=1 ./test/run.js", + "browser": "browserify -t browserify-istanbul test/run-browser.js | obake --coverage", + "check": "istanbul check-coverage coverage/coverage*.json", + "files": "pkgfiles --sort=name", + "get-version": "node -e \"console.log(require('./package.json').version)\"", + "update-readme": "sed -i.bak 's/\\/master\\.svg/\\/v'$(npm --silent run get-version)'.svg/g' README.md", + "restore-readme": "mv README.md.bak README.md", + "prepublish": "in-publish && npm run update-readme || not-in-publish", + "postpublish": "npm run restore-readme" + }, + "pre-commit": [ + "lint", + "ci-test", + "check" + ], + "engines": { + "node": ">= 0.12" + }, + "dependencies": { + "asynckit": { + "name": "asynckit", + "version": "0.4.0", + "description": "Minimal async jobs utility library, with streams support", + "main": "index.js", + "scripts": { + "clean": "rimraf coverage", + "lint": "eslint *.js lib/*.js test/*.js", + "test": "istanbul cover --reporter=json tape -- 'test/test-*.js' | tap-spec", + "win-test": "tape test/test-*.js", + "browser": "browserify -t browserify-istanbul test/lib/browserify_adjustment.js test/test-*.js | obake --coverage | tap-spec", + "report": "istanbul report", + "size": "browserify index.js | size-table asynckit", + "debug": "tape test/test-*.js" + }, + "pre-commit": [ + "clean", + "lint", + "test", + "browser", + "report", + "size" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/alexindigo/asynckit.git" + }, + "keywords": [ + "async", + "jobs", + "parallel", + "serial", + "iterator", + "array", + "object", + "stream", + "destroy", + "terminate", + "abort" + ], + "author": { + "name": "Alex Indigo", + "email": "iam@alexindigo.com" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/alexindigo/asynckit/issues" + }, + "homepage": "https://github.com/alexindigo/asynckit#readme", + "devDependencies": { + "browserify": "^13.0.0", + "browserify-istanbul": "^2.0.0", + "coveralls": "^2.11.9", + "eslint": "^2.9.0", + "istanbul": "^0.4.3", + "obake": "^0.1.2", + "phantomjs-prebuilt": "^2.1.7", + "pre-commit": "^1.1.3", + "reamde": "^1.1.0", + "rimraf": "^2.5.2", + "size-table": "^0.2.0", + "tap-spec": "^4.1.1", + "tape": "^4.5.1" + }, + "dependencies": {}, + "_resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "_integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "_from": "asynckit@0.4.0", + "readme": "# asynckit [![NPM Module](https://img.shields.io/npm/v/asynckit.svg?style=flat)](https://www.npmjs.com/package/asynckit)\n\nMinimal async jobs utility library, with streams support.\n\n[![PhantomJS Build](https://img.shields.io/travis/alexindigo/asynckit/v0.4.0.svg?label=browser&style=flat)](https://travis-ci.org/alexindigo/asynckit)\n[![Linux Build](https://img.shields.io/travis/alexindigo/asynckit/v0.4.0.svg?label=linux:0.12-6.x&style=flat)](https://travis-ci.org/alexindigo/asynckit)\n[![Windows Build](https://img.shields.io/appveyor/ci/alexindigo/asynckit/v0.4.0.svg?label=windows:0.12-6.x&style=flat)](https://ci.appveyor.com/project/alexindigo/asynckit)\n\n[![Coverage Status](https://img.shields.io/coveralls/alexindigo/asynckit/v0.4.0.svg?label=code+coverage&style=flat)](https://coveralls.io/github/alexindigo/asynckit?branch=master)\n[![Dependency Status](https://img.shields.io/david/alexindigo/asynckit/v0.4.0.svg?style=flat)](https://david-dm.org/alexindigo/asynckit)\n[![bitHound Overall Score](https://www.bithound.io/github/alexindigo/asynckit/badges/score.svg)](https://www.bithound.io/github/alexindigo/asynckit)\n\n\n\nAsyncKit provides harness for `parallel` and `serial` iterators over list of items represented by arrays or objects.\nOptionally it accepts abort function (should be synchronously return by iterator for each item), and terminates left over jobs upon an error event. For specific iteration order built-in (`ascending` and `descending`) and custom sort helpers also supported, via `asynckit.serialOrdered` method.\n\nIt ensures async operations to keep behavior more stable and prevent `Maximum call stack size exceeded` errors, from sync iterators.\n\n| compression | size |\n| :----------------- | -------: |\n| asynckit.js | 12.34 kB |\n| asynckit.min.js | 4.11 kB |\n| asynckit.min.js.gz | 1.47 kB |\n\n\n## Install\n\n```sh\n$ npm install --save asynckit\n```\n\n## Examples\n\n### Parallel Jobs\n\nRuns iterator over provided array in parallel. Stores output in the `result` array,\non the matching positions. In unlikely event of an error from one of the jobs,\nwill terminate rest of the active jobs (if abort function is provided)\nand return error along with salvaged data to the main callback function.\n\n#### Input Array\n\n```javascript\nvar parallel = require('asynckit').parallel\n , assert = require('assert')\n ;\n\nvar source = [ 1, 1, 4, 16, 64, 32, 8, 2 ]\n , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ]\n , expectedTarget = [ 1, 1, 2, 4, 8, 16, 32, 64 ]\n , target = []\n ;\n\nparallel(source, asyncJob, function(err, result)\n{\n assert.deepEqual(result, expectedResult);\n assert.deepEqual(target, expectedTarget);\n});\n\n// async job accepts one element from the array\n// and a callback function\nfunction asyncJob(item, cb)\n{\n // different delays (in ms) per item\n var delay = item * 25;\n\n // pretend different jobs take different time to finish\n // and not in consequential order\n var timeoutId = setTimeout(function() {\n target.push(item);\n cb(null, item * 2);\n }, delay);\n\n // allow to cancel \"leftover\" jobs upon error\n // return function, invoking of which will abort this job\n return clearTimeout.bind(null, timeoutId);\n}\n```\n\nMore examples could be found in [test/test-parallel-array.js](test/test-parallel-array.js).\n\n#### Input Object\n\nAlso it supports named jobs, listed via object.\n\n```javascript\nvar parallel = require('asynckit/parallel')\n , assert = require('assert')\n ;\n\nvar source = { first: 1, one: 1, four: 4, sixteen: 16, sixtyFour: 64, thirtyTwo: 32, eight: 8, two: 2 }\n , expectedResult = { first: 2, one: 2, four: 8, sixteen: 32, sixtyFour: 128, thirtyTwo: 64, eight: 16, two: 4 }\n , expectedTarget = [ 1, 1, 2, 4, 8, 16, 32, 64 ]\n , expectedKeys = [ 'first', 'one', 'two', 'four', 'eight', 'sixteen', 'thirtyTwo', 'sixtyFour' ]\n , target = []\n , keys = []\n ;\n\nparallel(source, asyncJob, function(err, result)\n{\n assert.deepEqual(result, expectedResult);\n assert.deepEqual(target, expectedTarget);\n assert.deepEqual(keys, expectedKeys);\n});\n\n// supports full value, key, callback (shortcut) interface\nfunction asyncJob(item, key, cb)\n{\n // different delays (in ms) per item\n var delay = item * 25;\n\n // pretend different jobs take different time to finish\n // and not in consequential order\n var timeoutId = setTimeout(function() {\n keys.push(key);\n target.push(item);\n cb(null, item * 2);\n }, delay);\n\n // allow to cancel \"leftover\" jobs upon error\n // return function, invoking of which will abort this job\n return clearTimeout.bind(null, timeoutId);\n}\n```\n\nMore examples could be found in [test/test-parallel-object.js](test/test-parallel-object.js).\n\n### Serial Jobs\n\nRuns iterator over provided array sequentially. Stores output in the `result` array,\non the matching positions. In unlikely event of an error from one of the jobs,\nwill not proceed to the rest of the items in the list\nand return error along with salvaged data to the main callback function.\n\n#### Input Array\n\n```javascript\nvar serial = require('asynckit/serial')\n , assert = require('assert')\n ;\n\nvar source = [ 1, 1, 4, 16, 64, 32, 8, 2 ]\n , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ]\n , expectedTarget = [ 0, 1, 2, 3, 4, 5, 6, 7 ]\n , target = []\n ;\n\nserial(source, asyncJob, function(err, result)\n{\n assert.deepEqual(result, expectedResult);\n assert.deepEqual(target, expectedTarget);\n});\n\n// extended interface (item, key, callback)\n// also supported for arrays\nfunction asyncJob(item, key, cb)\n{\n target.push(key);\n\n // it will be automatically made async\n // even it iterator \"returns\" in the same event loop\n cb(null, item * 2);\n}\n```\n\nMore examples could be found in [test/test-serial-array.js](test/test-serial-array.js).\n\n#### Input Object\n\nAlso it supports named jobs, listed via object.\n\n```javascript\nvar serial = require('asynckit').serial\n , assert = require('assert')\n ;\n\nvar source = [ 1, 1, 4, 16, 64, 32, 8, 2 ]\n , expectedResult = [ 2, 2, 8, 32, 128, 64, 16, 4 ]\n , expectedTarget = [ 0, 1, 2, 3, 4, 5, 6, 7 ]\n , target = []\n ;\n\nvar source = { first: 1, one: 1, four: 4, sixteen: 16, sixtyFour: 64, thirtyTwo: 32, eight: 8, two: 2 }\n , expectedResult = { first: 2, one: 2, four: 8, sixteen: 32, sixtyFour: 128, thirtyTwo: 64, eight: 16, two: 4 }\n , expectedTarget = [ 1, 1, 4, 16, 64, 32, 8, 2 ]\n , target = []\n ;\n\n\nserial(source, asyncJob, function(err, result)\n{\n assert.deepEqual(result, expectedResult);\n assert.deepEqual(target, expectedTarget);\n});\n\n// shortcut interface (item, callback)\n// works for object as well as for the arrays\nfunction asyncJob(item, cb)\n{\n target.push(item);\n\n // it will be automatically made async\n // even it iterator \"returns\" in the same event loop\n cb(null, item * 2);\n}\n```\n\nMore examples could be found in [test/test-serial-object.js](test/test-serial-object.js).\n\n_Note: Since _object_ is an _unordered_ collection of properties,\nit may produce unexpected results with sequential iterations.\nWhenever order of the jobs' execution is important please use `serialOrdered` method._\n\n### Ordered Serial Iterations\n\nTBD\n\nFor example [compare-property](compare-property) package.\n\n### Streaming interface\n\nTBD\n\n## Want to Know More?\n\nMore examples can be found in [test folder](test/).\n\nOr open an [issue](https://github.com/alexindigo/asynckit/issues) with questions and/or suggestions.\n\n## License\n\nAsyncKit is licensed under the MIT license.\n", + "readmeFilename": "README.md", + "_id": "asynckit@0.4.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "asynckit@0.4.0", + "name": "asynckit", + "escapedName": "asynckit", + "rawSpec": "0.4.0", + "saveSpec": "[Circular]", + "fetchSpec": "0.4.0" + }, + "_spec": "0.4.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "asynckit@0.4.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/asynckit", + "error": "[Circular]", + "extraneous": false + }, + "combined-stream": { + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "name": "combined-stream", + "description": "A stream that emits multiple other streams one after another.", + "version": "1.0.6", + "homepage": "https://github.com/felixge/node-combined-stream", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-combined-stream.git" + }, + "main": "./lib/combined_stream", + "scripts": { + "test": "node test/run.js" + }, + "engines": { + "node": ">= 0.8" + }, + "dependencies": { + "delayed-stream": { + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "contributors": [ + { + "name": "Mike Atkins", + "email": "apeherder@gmail.com" + } + ], + "name": "delayed-stream", + "description": "Buffers events from a stream until you are ready to handle them.", + "license": "MIT", + "version": "1.0.0", + "homepage": "https://github.com/felixge/node-delayed-stream", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-delayed-stream.git" + }, + "main": "./lib/delayed_stream", + "engines": { + "node": ">=0.4.0" + }, + "scripts": { + "test": "make test" + }, + "dependencies": {}, + "devDependencies": { + "fake": "0.2.0", + "far": "0.0.1" + }, + "_resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "_integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "_from": "delayed-stream@1.0.0", + "readme": "# delayed-stream\n\nBuffers events from a stream until you are ready to handle them.\n\n## Installation\n\n``` bash\nnpm install delayed-stream\n```\n\n## Usage\n\nThe following example shows how to write a http echo server that delays its\nresponse by 1000 ms.\n\n``` javascript\nvar DelayedStream = require('delayed-stream');\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n var delayed = DelayedStream.create(req);\n\n setTimeout(function() {\n res.writeHead(200);\n delayed.pipe(res);\n }, 1000);\n});\n```\n\nIf you are not using `Stream#pipe`, you can also manually release the buffered\nevents by calling `delayedStream.resume()`:\n\n``` javascript\nvar delayed = DelayedStream.create(req);\n\nsetTimeout(function() {\n // Emit all buffered events and resume underlaying source\n delayed.resume();\n}, 1000);\n```\n\n## Implementation\n\nIn order to use this meta stream properly, here are a few things you should\nknow about the implementation.\n\n### Event Buffering / Proxying\n\nAll events of the `source` stream are hijacked by overwriting the `source.emit`\nmethod. Until node implements a catch-all event listener, this is the only way.\n\nHowever, delayed-stream still continues to emit all events it captures on the\n`source`, regardless of whether you have released the delayed stream yet or\nnot.\n\nUpon creation, delayed-stream captures all `source` events and stores them in\nan internal event buffer. Once `delayedStream.release()` is called, all\nbuffered events are emitted on the `delayedStream`, and the event buffer is\ncleared. After that, delayed-stream merely acts as a proxy for the underlaying\nsource.\n\n### Error handling\n\nError events on `source` are buffered / proxied just like any other events.\nHowever, `delayedStream.create` attaches a no-op `'error'` listener to the\n`source`. This way you only have to handle errors on the `delayedStream`\nobject, rather than in two places.\n\n### Buffer limits\n\ndelayed-stream provides a `maxDataSize` property that can be used to limit\nthe amount of data being buffered. In order to protect you from bad `source`\nstreams that don't react to `source.pause()`, this feature is enabled by\ndefault.\n\n## API\n\n### DelayedStream.create(source, [options])\n\nReturns a new `delayedStream`. Available options are:\n\n* `pauseStream`\n* `maxDataSize`\n\nThe description for those properties can be found below.\n\n### delayedStream.source\n\nThe `source` stream managed by this object. This is useful if you are\npassing your `delayedStream` around, and you still want to access properties\non the `source` object.\n\n### delayedStream.pauseStream = true\n\nWhether to pause the underlaying `source` when calling\n`DelayedStream.create()`. Modifying this property afterwards has no effect.\n\n### delayedStream.maxDataSize = 1024 * 1024\n\nThe amount of data to buffer before emitting an `error`.\n\nIf the underlaying source is emitting `Buffer` objects, the `maxDataSize`\nrefers to bytes.\n\nIf the underlaying source is emitting JavaScript strings, the size refers to\ncharacters.\n\nIf you know what you are doing, you can set this property to `Infinity` to\ndisable this feature. You can also modify this property during runtime.\n\n### delayedStream.dataSize = 0\n\nThe amount of data buffered so far.\n\n### delayedStream.readable\n\nAn ECMA5 getter that returns the value of `source.readable`.\n\n### delayedStream.resume()\n\nIf the `delayedStream` has not been released so far, `delayedStream.release()`\nis called.\n\nIn either case, `source.resume()` is called.\n\n### delayedStream.pause()\n\nCalls `source.pause()`.\n\n### delayedStream.pipe(dest)\n\nCalls `delayedStream.resume()` and then proxies the arguments to `source.pipe`.\n\n### delayedStream.release()\n\nEmits and clears all events that have been buffered up so far. This does not\nresume the underlaying source, use `delayedStream.resume()` instead.\n\n## License\n\ndelayed-stream is licensed under the MIT license.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/felixge/node-delayed-stream/issues" + }, + "_id": "delayed-stream@1.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "delayed-stream@1.0.0", + "name": "delayed-stream", + "escapedName": "delayed-stream", + "rawSpec": "1.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.0" + }, + "_spec": "1.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "delayed-stream@1.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/delayed-stream", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "far": "~0.0.7" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "_integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "_from": "combined-stream@1.0.6", + "readme": "# combined-stream\n\nA stream that emits multiple other streams one after another.\n\n**NB** Currently `combined-stream` works with streams version 1 only. There is ongoing effort to switch this library to streams version 2. Any help is welcome. :) Meanwhile you can explore other libraries that provide streams2 support with more or less compatibility with `combined-stream`.\n\n- [combined-stream2](https://www.npmjs.com/package/combined-stream2): A drop-in streams2-compatible replacement for the combined-stream module.\n\n- [multistream](https://www.npmjs.com/package/multistream): A stream that emits multiple other streams one after another.\n\n## Installation\n\n``` bash\nnpm install combined-stream\n```\n\n## Usage\n\nHere is a simple example that shows how you can use combined-stream to combine\ntwo files into one:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create();\ncombinedStream.append(fs.createReadStream('file1.txt'));\ncombinedStream.append(fs.createReadStream('file2.txt'));\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\nWhile the example above works great, it will pause all source streams until\nthey are needed. If you don't want that to happen, you can set `pauseStreams`\nto `false`:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create({pauseStreams: false});\ncombinedStream.append(fs.createReadStream('file1.txt'));\ncombinedStream.append(fs.createReadStream('file2.txt'));\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\nHowever, what if you don't have all the source streams yet, or you don't want\nto allocate the resources (file descriptors, memory, etc.) for them right away?\nWell, in that case you can simply provide a callback that supplies the stream\nby calling a `next()` function:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create();\ncombinedStream.append(function(next) {\n next(fs.createReadStream('file1.txt'));\n});\ncombinedStream.append(function(next) {\n next(fs.createReadStream('file2.txt'));\n});\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\n## API\n\n### CombinedStream.create([options])\n\nReturns a new combined stream object. Available options are:\n\n* `maxDataSize`\n* `pauseStreams`\n\nThe effect of those options is described below.\n\n### combinedStream.pauseStreams = `true`\n\nWhether to apply back pressure to the underlaying streams. If set to `false`,\nthe underlaying streams will never be paused. If set to `true`, the\nunderlaying streams will be paused right after being appended, as well as when\n`delayedStream.pipe()` wants to throttle.\n\n### combinedStream.maxDataSize = `2 * 1024 * 1024`\n\nThe maximum amount of bytes (or characters) to buffer for all source streams.\nIf this value is exceeded, `combinedStream` emits an `'error'` event.\n\n### combinedStream.dataSize = `0`\n\nThe amount of bytes (or characters) currently buffered by `combinedStream`.\n\n### combinedStream.append(stream)\n\nAppends the given `stream` to the combinedStream object. If `pauseStreams` is\nset to `true, this stream will also be paused right away.\n\n`streams` can also be a function that takes one parameter called `next`. `next`\nis a function that must be invoked in order to provide the `next` stream, see\nexample above.\n\nRegardless of how the `stream` is appended, combined-stream always attaches an\n`'error'` listener to it, so you don't have to do that manually.\n\nSpecial case: `stream` can also be a String or Buffer.\n\n### combinedStream.write(data)\n\nYou should not call this, `combinedStream` takes care of piping the appended\nstreams into itself for you.\n\n### combinedStream.resume()\n\nCauses `combinedStream` to start drain the streams it manages. The function is\nidempotent, and also emits a `'resume'` event each time which usually goes to\nthe stream that is currently being drained.\n\n### combinedStream.pause();\n\nIf `combinedStream.pauseStreams` is set to `false`, this does nothing.\nOtherwise a `'pause'` event is emitted, this goes to the stream that is\ncurrently being drained, so you can use it to apply back pressure.\n\n### combinedStream.end();\n\nSets `combinedStream.writable` to false, emits an `'end'` event, and removes\nall streams from the queue.\n\n### combinedStream.destroy();\n\nSame as `combinedStream.end()`, except it emits a `'close'` event instead of\n`'end'`.\n\n## License\n\ncombined-stream is licensed under the MIT license.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/felixge/node-combined-stream/issues" + }, + "_id": "combined-stream@1.0.6", + "_requested": { + "type": "version", + "registry": true, + "raw": "combined-stream@1.0.6", + "name": "combined-stream", + "escapedName": "combined-stream", + "rawSpec": "1.0.6", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.6" + }, + "_spec": "1.0.6", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "combined-stream@1.0.6", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "delayed-stream": "~1.0.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/combined-stream", + "error": "[Circular]", + "extraneous": false + }, + "mime-types": { + "name": "mime-types", + "description": "The ultimate javascript content-type utility.", + "version": "2.1.19", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jeremiah Senkpiel", + "email": "fishrock123@rocketmail.com", + "url": "https://searchbeam.jit.su" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "types" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/mime-types.git" + }, + "dependencies": { + "mime-db": { + "name": "mime-db", + "description": "Media Type Database", + "version": "1.35.0", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + } + ], + "license": "MIT", + "keywords": [ + "mime", + "db", + "type", + "types", + "database", + "charset", + "charsets" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/mime-db.git" + }, + "devDependencies": { + "bluebird": "3.5.1", + "co": "4.6.0", + "cogent": "1.0.1", + "csv-parse": "2.5.0", + "eslint": "4.19.1", + "eslint-config-standard": "11.0.0", + "eslint-plugin-import": "2.13.0", + "eslint-plugin-node": "6.0.1", + "eslint-plugin-promise": "3.8.0", + "eslint-plugin-standard": "3.1.0", + "gnode": "0.1.2", + "mocha": "1.21.5", + "nyc": "11.8.0", + "raw-body": "2.3.3", + "stream-to-array": "2.3.0" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "README.md", + "db.json", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "node scripts/build", + "fetch": "node scripts/fetch-apache && gnode scripts/fetch-iana && node scripts/fetch-nginx", + "lint": "eslint .", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "nyc --reporter=html --reporter=text npm test", + "test-travis": "nyc --reporter=text npm test", + "update": "npm run fetch && npm run build" + }, + "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "_integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "_from": "mime-db@1.35.0", + "readme": "# mime-db\n\n[![NPM Version][npm-version-image]][npm-url]\n[![NPM Downloads][npm-downloads-image]][npm-url]\n[![Node.js Version][node-image]][node-url]\n[![Build Status][travis-image]][travis-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n\nThis is a database of all mime types.\nIt consists of a single, public JSON file and does not include any logic,\nallowing it to remain as un-opinionated as possible with an API.\nIt aggregates data from the following sources:\n\n- http://www.iana.org/assignments/media-types/media-types.xhtml\n- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\n- http://hg.nginx.org/nginx/raw-file/default/conf/mime.types\n\n## Installation\n\n```bash\nnpm install mime-db\n```\n\n### Database Download\n\nIf you're crazy enough to use this in the browser, you can just grab the\nJSON file using [RawGit](https://rawgit.com/). It is recommended to replace\n`master` with [a release tag](https://github.com/jshttp/mime-db/tags) as the\nJSON format may change in the future.\n\n```\nhttps://cdn.rawgit.com/jshttp/mime-db/master/db.json\n```\n\n## Usage\n\n```js\nvar db = require('mime-db');\n\n// grab data on .js files\nvar data = db['application/javascript'];\n```\n\n## Data Structure\n\nThe JSON file is a map lookup for lowercased mime types.\nEach mime type has the following properties:\n\n- `.source` - where the mime type is defined.\n If not set, it's probably a custom media type.\n - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types)\n - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml)\n - `nginx` - [nginx media types](http://hg.nginx.org/nginx/raw-file/default/conf/mime.types)\n- `.extensions[]` - known extensions associated with this mime type.\n- `.compressible` - whether a file of this type can be gzipped.\n- `.charset` - the default charset associated with this type, if any.\n\nIf unknown, every property could be `undefined`.\n\n## Contributing\n\nTo edit the database, only make PRs against `src/custom.json` or\n`src/custom-suffix.json`.\n\nThe `src/custom.json` file is a JSON object with the MIME type as the keys\nand the values being an object with the following keys:\n\n- `compressible` - leave out if you don't know, otherwise `true`/`false` to\n indicate whether the data represented by the type is typically compressible.\n- `extensions` - include an array of file extensions that are associated with\n the type.\n- `notes` - human-readable notes about the type, typically what the type is.\n- `sources` - include an array of URLs of where the MIME type and the associated\n extensions are sourced from. This needs to be a [primary source](https://en.wikipedia.org/wiki/Primary_source);\n links to type aggregating sites and Wikipedia are _not acceptable_.\n\nTo update the build, run `npm run build`.\n\n## Adding Custom Media Types\n\nThe best way to get new media types included in this library is to register\nthem with the IANA. The community registration procedure is outlined in\n[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types\nregistered with the IANA are automatically pulled into this library.\n\n[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg\n[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg\n[npm-url]: https://npmjs.org/package/mime-db\n[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg\n[travis-url]: https://travis-ci.org/jshttp/mime-db\n[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg\n[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master\n[node-image]: https://img.shields.io/node/v/mime-db.svg\n[node-url]: https://nodejs.org/en/download/\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/jshttp/mime-db/issues" + }, + "homepage": "https://github.com/jshttp/mime-db#readme", + "_id": "mime-db@1.35.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "mime-db@1.35.0", + "name": "mime-db", + "escapedName": "mime-db", + "rawSpec": "1.35.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.35.0" + }, + "_spec": "1.35.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "mime-db@1.35.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/mime-db", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "eslint": "4.19.1", + "eslint-config-standard": "11.0.0", + "eslint-plugin-import": "2.13.0", + "eslint-plugin-node": "6.0.1", + "eslint-plugin-promise": "3.8.0", + "eslint-plugin-standard": "3.1.0", + "istanbul": "0.4.5", + "mocha": "1.21.5" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "lint": "eslint .", + "test": "mocha --reporter spec test/test.js", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js" + }, + "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "_integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "_from": "mime-types@2.1.19", + "readme": "# mime-types\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n[![Node.js Version][node-version-image]][node-version-url]\n[![Build Status][travis-image]][travis-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n\nThe ultimate javascript content-type utility.\n\nSimilar to [the `mime@1.x` module](https://www.npmjs.com/package/mime), except:\n\n- __No fallbacks.__ Instead of naively returning the first available type,\n `mime-types` simply returns `false`, so do\n `var type = mime.lookup('unrecognized') || 'application/octet-stream'`.\n- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`.\n- No `.define()` functionality\n- Bug fixes for `.lookup(path)`\n\nOtherwise, the API is compatible with `mime` 1.x.\n\n## Install\n\nThis is a [Node.js](https://nodejs.org/en/) module available through the\n[npm registry](https://www.npmjs.com/). Installation is done using the\n[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):\n\n```sh\n$ npm install mime-types\n```\n\n## Adding Types\n\nAll mime types are based on [mime-db](https://www.npmjs.com/package/mime-db),\nso open a PR there if you'd like to add mime types.\n\n## API\n\n```js\nvar mime = require('mime-types')\n```\n\nAll functions return `false` if input is invalid or not found.\n\n### mime.lookup(path)\n\nLookup the content-type associated with a file.\n\n```js\nmime.lookup('json') // 'application/json'\nmime.lookup('.md') // 'text/markdown'\nmime.lookup('file.html') // 'text/html'\nmime.lookup('folder/file.js') // 'application/javascript'\nmime.lookup('folder/.htaccess') // false\n\nmime.lookup('cats') // false\n```\n\n### mime.contentType(type)\n\nCreate a full content-type header given a content-type or extension.\n\n```js\nmime.contentType('markdown') // 'text/x-markdown; charset=utf-8'\nmime.contentType('file.json') // 'application/json; charset=utf-8'\n\n// from a full path\nmime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8'\n```\n\n### mime.extension(type)\n\nGet the default extension for a content-type.\n\n```js\nmime.extension('application/octet-stream') // 'bin'\n```\n\n### mime.charset(type)\n\nLookup the implied default charset of a content-type.\n\n```js\nmime.charset('text/markdown') // 'UTF-8'\n```\n\n### var type = mime.types[extension]\n\nA map of content-types by extension.\n\n### [extensions...] = mime.extensions[type]\n\nA map of extensions by content-type.\n\n## License\n\n[MIT](LICENSE)\n\n[npm-image]: https://img.shields.io/npm/v/mime-types.svg\n[npm-url]: https://npmjs.org/package/mime-types\n[node-version-image]: https://img.shields.io/node/v/mime-types.svg\n[node-version-url]: https://nodejs.org/en/download/\n[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg\n[travis-url]: https://travis-ci.org/jshttp/mime-types\n[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg\n[coveralls-url]: https://coveralls.io/r/jshttp/mime-types\n[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg\n[downloads-url]: https://npmjs.org/package/mime-types\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/jshttp/mime-types/issues" + }, + "homepage": "https://github.com/jshttp/mime-types#readme", + "_id": "mime-types@2.1.19", + "_requested": { + "type": "version", + "registry": true, + "raw": "mime-types@2.1.19", + "name": "mime-types", + "escapedName": "mime-types", + "rawSpec": "2.1.19", + "saveSpec": "[Circular]", + "fetchSpec": "2.1.19" + }, + "_spec": "2.1.19", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "mime-types@2.1.19", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "mime-db": "~1.35.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/mime-types", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "browserify": "^13.1.1", + "browserify-istanbul": "^2.0.0", + "coveralls": "^2.11.14", + "cross-spawn": "^4.0.2", + "eslint": "^3.9.1", + "fake": "^0.2.2", + "far": "^0.0.7", + "formidable": "^1.0.17", + "in-publish": "^2.0.0", + "is-node-modern": "^1.0.0", + "istanbul": "^0.4.5", + "obake": "^0.1.2", + "phantomjs-prebuilt": "^2.1.13", + "pkgfiles": "^2.3.0", + "pre-commit": "^1.1.3", + "request": "2.76.0", + "rimraf": "^2.5.4", + "tape": "^4.6.2" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "_integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "_from": "form-data@2.3.2", + "readme": "# Form-Data [![NPM Module](https://img.shields.io/npm/v/form-data.svg)](https://www.npmjs.com/package/form-data) [![Join the chat at https://gitter.im/form-data/form-data](http://form-data.github.io/images/gitterbadge.svg)](https://gitter.im/form-data/form-data)\n\nA library to create readable ```\"multipart/form-data\"``` streams. Can be used to submit forms and file uploads to other web applications.\n\nThe API of this library is inspired by the [XMLHttpRequest-2 FormData Interface][xhr2-fd].\n\n[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface\n\n[![Linux Build](https://img.shields.io/travis/form-data/form-data/v2.3.2.svg?label=linux:4.x-9.x)](https://travis-ci.org/form-data/form-data)\n[![MacOS Build](https://img.shields.io/travis/form-data/form-data/v2.3.2.svg?label=macos:4.x-9.x)](https://travis-ci.org/form-data/form-data)\n[![Windows Build](https://img.shields.io/appveyor/ci/alexindigo/form-data/v2.3.2.svg?label=windows:4.x-9.x)](https://ci.appveyor.com/project/alexindigo/form-data)\n\n[![Coverage Status](https://img.shields.io/coveralls/form-data/form-data/v2.3.2.svg?label=code+coverage)](https://coveralls.io/github/form-data/form-data?branch=master)\n[![Dependency Status](https://img.shields.io/david/form-data/form-data.svg)](https://david-dm.org/form-data/form-data)\n[![bitHound Overall Score](https://www.bithound.io/github/form-data/form-data/badges/score.svg)](https://www.bithound.io/github/form-data/form-data)\n\n## Install\n\n```\nnpm install --save form-data\n```\n\n## Usage\n\nIn this example we are constructing a form with 3 fields that contain a string,\na buffer and a file stream.\n\n``` javascript\nvar FormData = require('form-data');\nvar fs = require('fs');\n\nvar form = new FormData();\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_file', fs.createReadStream('/foo/bar.jpg'));\n```\n\nAlso you can use http-response stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar http = require('http');\n\nvar form = new FormData();\n\nhttp.request('http://nodejs.org/images/logo.png', function(response) {\n form.append('my_field', 'my value');\n form.append('my_buffer', new Buffer(10));\n form.append('my_logo', response);\n});\n```\n\nOr @mikeal's [request](https://github.com/request/request) stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar request = require('request');\n\nvar form = new FormData();\n\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_logo', request('http://nodejs.org/images/logo.png'));\n```\n\nIn order to submit this form to a web application, call ```submit(url, [callback])``` method:\n\n``` javascript\nform.submit('http://example.org/', function(err, res) {\n // res – response object (http.IncomingMessage) //\n res.resume();\n});\n\n```\n\nFor more advanced request manipulations ```submit()``` method returns ```http.ClientRequest``` object, or you can choose from one of the alternative submission methods.\n\n### Custom options\n\nYou can provide custom options, such as `maxDataSize`:\n\n``` javascript\nvar FormData = require('form-data');\n\nvar form = new FormData({ maxDataSize: 20971520 });\nform.append('my_field', 'my value');\nform.append('my_buffer', /* something big */);\n```\n\nList of available options could be found in [combined-stream](https://github.com/felixge/node-combined-stream/blob/master/lib/combined_stream.js#L7-L15)\n\n### Alternative submission methods\n\nYou can use node's http client interface:\n\n``` javascript\nvar http = require('http');\n\nvar request = http.request({\n method: 'post',\n host: 'example.org',\n path: '/upload',\n headers: form.getHeaders()\n});\n\nform.pipe(request);\n\nrequest.on('response', function(res) {\n console.log(res.statusCode);\n});\n```\n\nOr if you would prefer the `'Content-Length'` header to be set for you:\n\n``` javascript\nform.submit('example.org/upload', function(err, res) {\n console.log(res.statusCode);\n});\n```\n\nTo use custom headers and pre-known length in parts:\n\n``` javascript\nvar CRLF = '\\r\\n';\nvar form = new FormData();\n\nvar options = {\n header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF,\n knownLength: 1\n};\n\nform.append('my_buffer', buffer, options);\n\nform.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n});\n```\n\nForm-Data can recognize and fetch all the required information from common types of streams (```fs.readStream```, ```http.response``` and ```mikeal's request```), for some other types of streams you'd need to provide \"file\"-related information manually:\n\n``` javascript\nsomeModule.stream(function(err, stdout, stderr) {\n if (err) throw err;\n\n var form = new FormData();\n\n form.append('file', stdout, {\n filename: 'unicycle.jpg', // ... or:\n filepath: 'photos/toys/unicycle.jpg',\n contentType: 'image/jpeg',\n knownLength: 19806\n });\n\n form.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n });\n});\n```\n\nThe `filepath` property overrides `filename` and may contain a relative path. This is typically used when uploading [multiple files from a directory](https://wicg.github.io/entries-api/#dom-htmlinputelement-webkitdirectory).\n\nFor edge cases, like POST request to URL with query string or to pass HTTP auth credentials, object can be passed to `form.submit()` as first parameter:\n\n``` javascript\nform.submit({\n host: 'example.com',\n path: '/probably.php?extra=params',\n auth: 'username:password'\n}, function(err, res) {\n console.log(res.statusCode);\n});\n```\n\nIn case you need to also send custom HTTP headers with the POST request, you can use the `headers` key in first parameter of `form.submit()`:\n\n``` javascript\nform.submit({\n host: 'example.com',\n path: '/surelynot.php',\n headers: {'x-test-header': 'test-header-value'}\n}, function(err, res) {\n console.log(res.statusCode);\n});\n```\n\n### Integration with other libraries\n\n#### Request\n\nForm submission using [request](https://github.com/request/request):\n\n```javascript\nvar formData = {\n my_field: 'my_value',\n my_file: fs.createReadStream(__dirname + '/unicycle.jpg'),\n};\n\nrequest.post({url:'http://service.com/upload', formData: formData}, function(err, httpResponse, body) {\n if (err) {\n return console.error('upload failed:', err);\n }\n console.log('Upload successful! Server responded with:', body);\n});\n```\n\nFor more details see [request readme](https://github.com/request/request#multipartform-data-multipart-form-uploads).\n\n#### node-fetch\n\nYou can also submit a form using [node-fetch](https://github.com/bitinn/node-fetch):\n\n```javascript\nvar form = new FormData();\n\nform.append('a', 1);\n\nfetch('http://example.com', { method: 'POST', body: form })\n .then(function(res) {\n return res.json();\n }).then(function(json) {\n console.log(json);\n });\n```\n\n## Notes\n\n- ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround.\n- Starting version `2.x` FormData has dropped support for `node@0.10.x`.\n\n## License\n\nForm-Data is released under the [MIT](License) license.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/form-data/form-data/issues" + }, + "homepage": "https://github.com/form-data/form-data#readme", + "_id": "form-data@2.3.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "form-data@2.3.2", + "name": "form-data", + "escapedName": "form-data", + "rawSpec": "2.3.2", + "saveSpec": "[Circular]", + "fetchSpec": "2.3.2" + }, + "_spec": "2.3.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "form-data@2.3.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/form-data", + "error": "[Circular]", + "extraneous": false + }, + "formidable": { + "name": "formidable", + "description": "A node.js module for parsing form data, especially file uploads.", + "homepage": "https://github.com/felixge/node-formidable", + "license": "MIT", + "version": "1.2.1", + "devDependencies": { + "gently": "^0.8.0", + "findit": "^0.1.2", + "hashish": "^0.0.4", + "urun": "^0.0.6", + "utest": "^0.0.8", + "request": "^2.11.4" + }, + "directories": { + "lib": "./lib" + }, + "main": "./lib/index", + "scripts": { + "test": "node test/run.js", + "clean": "rm test/tmp/*" + }, + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-formidable.git" + }, + "bugs": { + "url": "http://github.com/felixge/node-formidable/issues" + }, + "optionalDependencies": {}, + "_resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "_integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "_from": "formidable@1.2.1", + "readme": "# Formidable\n\n[![Build Status](https://travis-ci.org/felixge/node-formidable.svg?branch=master)](https://travis-ci.org/felixge/node-formidable)\n\n## Purpose\n\nA Node.js module for parsing form data, especially file uploads.\n\n## Current status\n\n**Maintainers Wanted:** Please see https://github.com/felixge/node-formidable/issues/412\n\nThis module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading\nand encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from\na large variety of clients and is considered production-ready.\n\n## Features\n\n* Fast (~500mb/sec), non-buffering multipart parser\n* Automatically writing file uploads to disk\n* Low memory footprint\n* Graceful error handling\n* Very high test coverage\n\n## Installation\n\n```sh\nnpm i -S formidable\n```\n\nThis is a low-level package, and if you're using a high-level framework it may already be included. However, [Express v4](http://expressjs.com) does not include any multipart handling, nor does [body-parser](https://github.com/expressjs/body-parser).\n\nNote: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.\n\n## Example\n\nParse an incoming file upload.\n```javascript\nvar formidable = require('formidable'),\n http = require('http'),\n util = require('util');\n\nhttp.createServer(function(req, res) {\n if (req.url == '/upload' && req.method.toLowerCase() == 'post') {\n // parse a file upload\n var form = new formidable.IncomingForm();\n\n form.parse(req, function(err, fields, files) {\n res.writeHead(200, {'content-type': 'text/plain'});\n res.write('received upload:\\n\\n');\n res.end(util.inspect({fields: fields, files: files}));\n });\n\n return;\n }\n\n // show a file upload form\n res.writeHead(200, {'content-type': 'text/html'});\n res.end(\n '
'+\n '
'+\n '
'+\n ''+\n '
'\n );\n}).listen(8080);\n```\n## API\n\n### Formidable.IncomingForm\n```javascript\nvar form = new formidable.IncomingForm()\n```\nCreates a new incoming form.\n\n```javascript\nform.encoding = 'utf-8';\n```\nSets encoding for incoming form fields.\n\n```javascript\nform.uploadDir = \"/my/dir\";\n```\nSets the directory for placing file uploads in. You can move them later on using\n`fs.rename()`. The default is `os.tmpdir()`.\n\n```javascript\nform.keepExtensions = false;\n```\nIf you want the files written to `form.uploadDir` to include the extensions of the original files, set this property to `true`.\n\n```javascript\nform.type\n```\nEither 'multipart' or 'urlencoded' depending on the incoming request.\n\n```javascript\nform.maxFieldsSize = 20 * 1024 * 1024;\n```\nLimits the amount of memory all fields together (except files) can allocate in bytes.\nIf this value is exceeded, an `'error'` event is emitted. The default\nsize is 20MB.\n\n```javascript\nform.maxFileSize = 200 * 1024 * 1024;\n```\nLimits the size of uploaded file.\nIf this value is exceeded, an `'error'` event is emitted. The default\nsize is 200MB.\n\n```javascript\nform.maxFields = 1000;\n```\nLimits the number of fields that the querystring parser will decode. Defaults\nto 1000 (0 for unlimited).\n\n```javascript\nform.hash = false;\n```\nIf you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.\n\n```javascript\nform.multiples = false;\n```\nIf this option is enabled, when you call `form.parse`, the `files` argument will contain arrays of files for inputs which submit multiple files using the HTML5 `multiple` attribute.\n\n```javascript\nform.bytesReceived\n```\nThe amount of bytes received for this form so far.\n\n```javascript\nform.bytesExpected\n```\nThe expected number of bytes in this form.\n\n```javascript\nform.parse(request, [cb]);\n```\nParses an incoming node.js `request` containing form data. If `cb` is provided, all fields and files are collected and passed to the callback:\n\n\n```javascript\nform.parse(req, function(err, fields, files) {\n // ...\n});\n\nform.onPart(part);\n```\nYou may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.\n\n```javascript\nform.onPart = function(part) {\n part.addListener('data', function() {\n // ...\n });\n}\n```\nIf you want to use formidable to only handle certain parts for you, you can do so:\n```javascript\nform.onPart = function(part) {\n if (!part.filename) {\n // let formidable handle all non-file parts\n form.handlePart(part);\n }\n}\n```\nCheck the code in this method for further inspiration.\n\n\n### Formidable.File\n```javascript\nfile.size = 0\n```\nThe size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.\n```javascript\nfile.path = null\n```\nThe path this file is being written to. You can modify this in the `'fileBegin'` event in\ncase you are unhappy with the way formidable generates a temporary path for your files.\n```javascript\nfile.name = null\n```\nThe name this file had according to the uploading client.\n```javascript\nfile.type = null\n```\nThe mime type of this file, according to the uploading client.\n```javascript\nfile.lastModifiedDate = null\n```\nA date object (or `null`) containing the time this file was last written to. Mostly\nhere for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).\n```javascript\nfile.hash = null\n```\nIf hash calculation was set, you can read the hex digest out of this var.\n\n#### Formidable.File#toJSON()\n\n This method returns a JSON-representation of the file, allowing you to\n `JSON.stringify()` the file which is useful for logging and responding\n to requests.\n\n### Events\n\n\n#### 'progress'\n\nEmitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.\n\n```javascript\nform.on('progress', function(bytesReceived, bytesExpected) {\n});\n```\n\n\n\n#### 'field'\n\nEmitted whenever a field / value pair has been received.\n\n```javascript\nform.on('field', function(name, value) {\n});\n```\n\n#### 'fileBegin'\n\nEmitted whenever a new file is detected in the upload stream. Use this event if\nyou want to stream the file to somewhere else while buffering the upload on\nthe file system.\n\n```javascript\nform.on('fileBegin', function(name, file) {\n});\n```\n\n#### 'file'\n\nEmitted whenever a field / file pair has been received. `file` is an instance of `File`.\n\n```javascript\nform.on('file', function(name, file) {\n});\n```\n\n#### 'error'\n\nEmitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.\n\n```javascript\nform.on('error', function(err) {\n});\n```\n\n#### 'aborted'\n\n\nEmitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an `error` event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).\n```javascript\nform.on('aborted', function() {\n});\n```\n\n##### 'end'\n```javascript\nform.on('end', function() {\n});\n```\nEmitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.\n\n\n\n## Changelog\n\n### v1.1.1 (2017-01-15)\n\n * Fix DeprecationWarning about os.tmpDir() (Christian)\n * Update `buffer.write` order of arguments for Node 7 (Kornel Lesiński)\n * JSON Parser emits error events to the IncomingForm (alessio.montagnani)\n * Improved Content-Disposition parsing (Sebastien)\n * Access WriteStream of fs during runtime instead of include time (Jonas Amundsen)\n * Use built-in toString to convert buffer to hex (Charmander)\n * Add hash to json if present (Nick Stamas)\n * Add license to package.json (Simen Bekkhus)\n\n### v1.0.14 (2013-05-03)\n\n* Add failing hash tests. (Ben Trask)\n* Enable hash calculation again (Eugene Girshov)\n* Test for immediate data events (Tim Smart)\n* Re-arrange IncomingForm#parse (Tim Smart)\n\n### v1.0.13\n\n* Only update hash if update method exists (Sven Lito)\n* According to travis v0.10 needs to go quoted (Sven Lito)\n* Bumping build node versions (Sven Lito)\n* Additional fix for empty requests (Eugene Girshov)\n* Change the default to 1000, to match the new Node behaviour. (OrangeDog)\n* Add ability to control maxKeys in the querystring parser. (OrangeDog)\n* Adjust test case to work with node 0.9.x (Eugene Girshov)\n* Update package.json (Sven Lito)\n* Path adjustment according to eb4468b (Markus Ast)\n\n### v1.0.12\n\n* Emit error on aborted connections (Eugene Girshov)\n* Add support for empty requests (Eugene Girshov)\n* Fix name/filename handling in Content-Disposition (jesperp)\n* Tolerate malformed closing boundary in multipart (Eugene Girshov)\n* Ignore preamble in multipart messages (Eugene Girshov)\n* Add support for application/json (Mike Frey, Carlos Rodriguez)\n* Add support for Base64 encoding (Elmer Bulthuis)\n* Add File#toJSON (TJ Holowaychuk)\n* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)\n* Documentation improvements (Sven Lito, Andre Azevedo)\n* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)\n* Use os.tmpdir() to get tmp directory (Andrew Kelley)\n* Improve package.json (Andrew Kelley, Sven Lito)\n* Fix benchmark script (Andrew Kelley)\n* Fix scope issue in incoming_forms (Sven Lito)\n* Fix file handle leak on error (OrangeDog)\n\n## License\n\nFormidable is licensed under the MIT license.\n\n## Ports\n\n* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable\n\n## Credits\n\n* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js\n", + "readmeFilename": "Readme.md", + "dependencies": {}, + "_id": "formidable@1.2.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "formidable@1.2.1", + "name": "formidable", + "escapedName": "formidable", + "rawSpec": "1.2.1", + "saveSpec": "[Circular]", + "fetchSpec": "1.2.1" + }, + "_spec": "1.2.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "formidable@1.2.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/formidable", + "error": "[Circular]", + "extraneous": false + }, + "methods": { + "name": "methods", + "description": "HTTP methods that node supports", + "version": "1.1.2", + "contributors": [ + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/jshttp/methods.git" + }, + "devDependencies": { + "istanbul": "0.4.1", + "mocha": "1.21.5" + }, + "files": [ + "index.js", + "HISTORY.md", + "LICENSE" + ], + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + }, + "browser": { + "http": false + }, + "keywords": [ + "http", + "methods" + ], + "_resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "_integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "_from": "methods@1.1.2", + "readme": "# Methods\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n[![Node.js Version][node-version-image]][node-version-url]\n[![Build Status][travis-image]][travis-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n\nHTTP verbs that Node.js core's HTTP parser supports.\n\nThis module provides an export that is just like `http.METHODS` from Node.js core,\nwith the following differences:\n\n * All method names are lower-cased.\n * Contains a fallback list of methods for Node.js versions that do not have a\n `http.METHODS` export (0.10 and lower).\n * Provides the fallback list when using tools like `browserify` without pulling\n in the `http` shim module.\n\n## Install\n\n```bash\n$ npm install methods\n```\n\n## API\n\n```js\nvar methods = require('methods')\n```\n\n### methods\n\nThis is an array of lower-cased method names that Node.js supports. If Node.js\nprovides the `http.METHODS` export, then this is the same array lower-cased,\notherwise it is a snapshot of the verbs from Node.js 0.10.\n\n## License\n\n[MIT](LICENSE)\n\n[npm-image]: https://img.shields.io/npm/v/methods.svg?style=flat\n[npm-url]: https://npmjs.org/package/methods\n[node-version-image]: https://img.shields.io/node/v/methods.svg?style=flat\n[node-version-url]: https://nodejs.org/en/download/\n[travis-image]: https://img.shields.io/travis/jshttp/methods.svg?style=flat\n[travis-url]: https://travis-ci.org/jshttp/methods\n[coveralls-image]: https://img.shields.io/coveralls/jshttp/methods.svg?style=flat\n[coveralls-url]: https://coveralls.io/r/jshttp/methods?branch=master\n[downloads-image]: https://img.shields.io/npm/dm/methods.svg?style=flat\n[downloads-url]: https://npmjs.org/package/methods\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/jshttp/methods/issues" + }, + "homepage": "https://github.com/jshttp/methods#readme", + "_id": "methods@1.1.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "methods@1.1.2", + "name": "methods", + "escapedName": "methods", + "rawSpec": "1.1.2", + "saveSpec": "[Circular]", + "fetchSpec": "1.1.2" + }, + "_spec": "1.1.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "methods@1.1.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/methods", + "error": "[Circular]", + "extraneous": false + }, + "mime": { + "author": { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + }, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "email": "benjamin@benjaminthomas.org", + "url": "http://github.com/bentomas" + } + ], + "description": "A comprehensive library for mime-type mapping", + "license": "MIT", + "dependencies": {}, + "devDependencies": { + "github-release-notes": "0.13.1", + "mime-db": "1.31.0", + "mime-score": "1.1.0" + }, + "scripts": { + "prepare": "node src/build.js", + "changelog": "gren changelog --tags=all --generate --override", + "test": "node src/test.js" + }, + "keywords": [ + "util", + "mime" + ], + "main": "mime.js", + "name": "mime", + "repository": { + "url": "git+https://github.com/broofa/node-mime.git", + "type": "git" + }, + "version": "1.6.0", + "_resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "_integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "_from": "mime@1.6.0", + "readme": "# mime\n\nComprehensive MIME type mapping API based on mime-db module.\n\n## Install\n\nInstall with [npm](http://github.com/isaacs/npm):\n\n npm install mime\n\n## Contributing / Testing\n\n npm run test\n\n## Command Line\n\n mime [path_string]\n\nE.g.\n\n > mime scripts/jquery.js\n application/javascript\n\n## API - Queries\n\n### mime.lookup(path)\nGet the mime type associated with a file, if no mime type is found `application/octet-stream` is returned. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g.\n\n```js\nvar mime = require('mime');\n\nmime.lookup('/path/to/file.txt'); // => 'text/plain'\nmime.lookup('file.txt'); // => 'text/plain'\nmime.lookup('.TXT'); // => 'text/plain'\nmime.lookup('htm'); // => 'text/html'\n```\n\n### mime.default_type\nSets the mime type returned when `mime.lookup` fails to find the extension searched for. (Default is `application/octet-stream`.)\n\n### mime.extension(type)\nGet the default extension for `type`\n\n```js\nmime.extension('text/html'); // => 'html'\nmime.extension('application/octet-stream'); // => 'bin'\n```\n\n### mime.charsets.lookup()\n\nMap mime-type to charset\n\n```js\nmime.charsets.lookup('text/plain'); // => 'UTF-8'\n```\n\n(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.)\n\n## API - Defining Custom Types\n\nCustom type mappings can be added on a per-project basis via the following APIs.\n\n### mime.define()\n\nAdd custom mime/extension mappings\n\n```js\nmime.define({\n 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],\n 'application/x-my-type': ['x-mt', 'x-mtt'],\n // etc ...\n});\n\nmime.lookup('x-sft'); // => 'text/x-some-format'\n```\n\nThe first entry in the extensions array is returned by `mime.extension()`. E.g.\n\n```js\nmime.extension('text/x-some-format'); // => 'x-sf'\n```\n\n### mime.load(filepath)\n\nLoad mappings from an Apache \".types\" format file\n\n```js\nmime.load('./my_project.types');\n```\nThe .types file format is simple - See the `types` dir for examples.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/broofa/node-mime/issues" + }, + "homepage": "https://github.com/broofa/node-mime#readme", + "_id": "mime@1.6.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "mime@1.6.0", + "name": "mime", + "escapedName": "mime", + "rawSpec": "1.6.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.6.0" + }, + "_spec": "1.6.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "mime@1.6.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/mime", + "error": "[Circular]", + "extraneous": false + }, + "qs": { + "name": "qs", + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "homepage": "https://github.com/ljharb/qs", + "version": "6.5.2", + "repository": { + "type": "git", + "url": "git+https://github.com/ljharb/qs.git" + }, + "main": "lib/index.js", + "contributors": [ + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + } + ], + "keywords": [ + "querystring", + "qs" + ], + "engines": { + "node": ">=0.6" + }, + "dependencies": {}, + "devDependencies": { + "@ljharb/eslint-config": "^12.2.1", + "browserify": "^16.2.0", + "covert": "^1.1.0", + "editorconfig-tools": "^0.1.1", + "eslint": "^4.19.1", + "evalmd": "^0.0.17", + "iconv-lite": "^0.4.21", + "mkdirp": "^0.5.1", + "qs-iconv": "^1.0.4", + "safe-publish-latest": "^1.1.1", + "safer-buffer": "^2.1.2", + "tape": "^4.9.0" + }, + "scripts": { + "prepublish": "safe-publish-latest && npm run dist", + "pretest": "npm run --silent readme && npm run --silent lint", + "test": "npm run --silent coverage", + "tests-only": "node test", + "readme": "evalmd README.md", + "prelint": "editorconfig-tools check * lib/* test/*", + "lint": "eslint lib/*.js test/*.js", + "coverage": "covert test", + "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js" + }, + "license": "BSD-3-Clause", + "_resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "_integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "_from": "qs@6.5.2", + "readme": "# qs [![Version Badge][2]][1]\n\n[![Build Status][3]][4]\n[![dependency status][5]][6]\n[![dev dependency status][7]][8]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][11]][1]\n\nA querystring parsing and stringifying library with some added security.\n\nLead Maintainer: [Jordan Harband](https://github.com/ljharb)\n\nThe **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).\n\n## Usage\n\n```javascript\nvar qs = require('qs');\nvar assert = require('assert');\n\nvar obj = qs.parse('a=c');\nassert.deepEqual(obj, { a: 'c' });\n\nvar str = qs.stringify(obj);\nassert.equal(str, 'a=c');\n```\n\n### Parsing Objects\n\n[](#preventEval)\n```javascript\nqs.parse(string, [options]);\n```\n\n**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.\nFor example, the string `'foo[bar]=baz'` converts to:\n\n```javascript\nassert.deepEqual(qs.parse('foo[bar]=baz'), {\n foo: {\n bar: 'baz'\n }\n});\n```\n\nWhen using the `plainObjects` option the parsed value is returned as a null object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:\n\n```javascript\nvar nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });\nassert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });\n```\n\nBy default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.\n\n```javascript\nvar protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });\nassert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });\n```\n\nURI encoded strings work too:\n\n```javascript\nassert.deepEqual(qs.parse('a%5Bb%5D=c'), {\n a: { b: 'c' }\n});\n```\n\nYou can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:\n\n```javascript\nassert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {\n foo: {\n bar: {\n baz: 'foobarbaz'\n }\n }\n});\n```\n\nBy default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like\n`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:\n\n```javascript\nvar expected = {\n a: {\n b: {\n c: {\n d: {\n e: {\n f: {\n '[g][h][i]': 'j'\n }\n }\n }\n }\n }\n }\n};\nvar string = 'a[b][c][d][e][f][g][h][i]=j';\nassert.deepEqual(qs.parse(string), expected);\n```\n\nThis depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`:\n\n```javascript\nvar deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });\nassert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } });\n```\n\nThe depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.\n\nFor similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:\n\n```javascript\nvar limited = qs.parse('a=b&c=d', { parameterLimit: 1 });\nassert.deepEqual(limited, { a: 'b' });\n```\n\nTo bypass the leading question mark, use `ignoreQueryPrefix`:\n\n```javascript\nvar prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });\nassert.deepEqual(prefixed, { a: 'b', c: 'd' });\n```\n\nAn optional delimiter can also be passed:\n\n```javascript\nvar delimited = qs.parse('a=b;c=d', { delimiter: ';' });\nassert.deepEqual(delimited, { a: 'b', c: 'd' });\n```\n\nDelimiters can be a regular expression too:\n\n```javascript\nvar regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });\nassert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });\n```\n\nOption `allowDots` can be used to enable dot notation:\n\n```javascript\nvar withDots = qs.parse('a.b=c', { allowDots: true });\nassert.deepEqual(withDots, { a: { b: 'c' } });\n```\n\n### Parsing Arrays\n\n**qs** can also parse arrays using a similar `[]` notation:\n\n```javascript\nvar withArray = qs.parse('a[]=b&a[]=c');\nassert.deepEqual(withArray, { a: ['b', 'c'] });\n```\n\nYou may specify an index as well:\n\n```javascript\nvar withIndexes = qs.parse('a[1]=c&a[0]=b');\nassert.deepEqual(withIndexes, { a: ['b', 'c'] });\n```\n\nNote that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number\nto create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving\ntheir order:\n\n```javascript\nvar noSparse = qs.parse('a[1]=b&a[15]=c');\nassert.deepEqual(noSparse, { a: ['b', 'c'] });\n```\n\nNote that an empty string is also a value, and will be preserved:\n\n```javascript\nvar withEmptyString = qs.parse('a[]=&a[]=b');\nassert.deepEqual(withEmptyString, { a: ['', 'b'] });\n\nvar withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');\nassert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });\n```\n\n**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will\ninstead be converted to an object with the index as the key:\n\n```javascript\nvar withMaxIndex = qs.parse('a[100]=b');\nassert.deepEqual(withMaxIndex, { a: { '100': 'b' } });\n```\n\nThis limit can be overridden by passing an `arrayLimit` option:\n\n```javascript\nvar withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });\nassert.deepEqual(withArrayLimit, { a: { '1': 'b' } });\n```\n\nTo disable array parsing entirely, set `parseArrays` to `false`.\n\n```javascript\nvar noParsingArrays = qs.parse('a[]=b', { parseArrays: false });\nassert.deepEqual(noParsingArrays, { a: { '0': 'b' } });\n```\n\nIf you mix notations, **qs** will merge the two items into an object:\n\n```javascript\nvar mixedNotation = qs.parse('a[0]=b&a[b]=c');\nassert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });\n```\n\nYou can also create arrays of objects:\n\n```javascript\nvar arraysOfObjects = qs.parse('a[][b]=c');\nassert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });\n```\n\n### Stringifying\n\n[](#preventEval)\n```javascript\nqs.stringify(object, [options]);\n```\n\nWhen stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:\n\n```javascript\nassert.equal(qs.stringify({ a: 'b' }), 'a=b');\nassert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');\n```\n\nThis encoding can be disabled by setting the `encode` option to `false`:\n\n```javascript\nvar unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });\nassert.equal(unencoded, 'a[b]=c');\n```\n\nEncoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`:\n```javascript\nvar encodedValues = qs.stringify(\n { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },\n { encodeValuesOnly: true }\n);\nassert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');\n```\n\nThis encoding can also be replaced by a custom encoding method set as `encoder` option:\n\n```javascript\nvar encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {\n // Passed in values `a`, `b`, `c`\n return // Return encoded string\n}})\n```\n\n_(Note: the `encoder` option does not apply if `encode` is `false`)_\n\nAnalogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:\n\n```javascript\nvar decoded = qs.parse('x=z', { decoder: function (str) {\n // Passed in values `x`, `z`\n return // Return decoded string\n}})\n```\n\nExamples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.\n\nWhen arrays are stringified, by default they are given explicit indices:\n\n```javascript\nqs.stringify({ a: ['b', 'c', 'd'] });\n// 'a[0]=b&a[1]=c&a[2]=d'\n```\n\nYou may override this by setting the `indices` option to `false`:\n\n```javascript\nqs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });\n// 'a=b&a=c&a=d'\n```\n\nYou may use the `arrayFormat` option to specify the format of the output array:\n\n```javascript\nqs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })\n// 'a[0]=b&a[1]=c'\nqs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })\n// 'a[]=b&a[]=c'\nqs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })\n// 'a=b&a=c'\n```\n\nWhen objects are stringified, by default they use bracket notation:\n\n```javascript\nqs.stringify({ a: { b: { c: 'd', e: 'f' } } });\n// 'a[b][c]=d&a[b][e]=f'\n```\n\nYou may override this to use dot notation by setting the `allowDots` option to `true`:\n\n```javascript\nqs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });\n// 'a.b.c=d&a.b.e=f'\n```\n\nEmpty strings and null values will omit the value, but the equals sign (=) remains in place:\n\n```javascript\nassert.equal(qs.stringify({ a: '' }), 'a=');\n```\n\nKey with no values (such as an empty object or array) will return nothing:\n\n```javascript\nassert.equal(qs.stringify({ a: [] }), '');\nassert.equal(qs.stringify({ a: {} }), '');\nassert.equal(qs.stringify({ a: [{}] }), '');\nassert.equal(qs.stringify({ a: { b: []} }), '');\nassert.equal(qs.stringify({ a: { b: {}} }), '');\n```\n\nProperties that are set to `undefined` will be omitted entirely:\n\n```javascript\nassert.equal(qs.stringify({ a: null, b: undefined }), 'a=');\n```\n\nThe query string may optionally be prepended with a question mark:\n\n```javascript\nassert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');\n```\n\nThe delimiter may be overridden with stringify as well:\n\n```javascript\nassert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');\n```\n\nIf you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option:\n\n```javascript\nvar date = new Date(7);\nassert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A'));\nassert.equal(\n qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }),\n 'a=7'\n);\n```\n\nYou may use the `sort` option to affect the order of parameter keys:\n\n```javascript\nfunction alphabeticalSort(a, b) {\n return a.localeCompare(b);\n}\nassert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');\n```\n\nFinally, you can use the `filter` option to restrict which keys will be included in the stringified output.\nIf you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you\npass an array, it will be used to select properties and array indices for stringification:\n\n```javascript\nfunction filterFunc(prefix, value) {\n if (prefix == 'b') {\n // Return an `undefined` value to omit a property.\n return;\n }\n if (prefix == 'e[f]') {\n return value.getTime();\n }\n if (prefix == 'e[g][0]') {\n return value * 2;\n }\n return value;\n}\nqs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc });\n// 'a=b&c=d&e[f]=123&e[g][0]=4'\nqs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] });\n// 'a=b&e=f'\nqs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] });\n// 'a[0]=b&a[2]=d'\n```\n\n### Handling of `null` values\n\nBy default, `null` values are treated like empty strings:\n\n```javascript\nvar withNull = qs.stringify({ a: null, b: '' });\nassert.equal(withNull, 'a=&b=');\n```\n\nParsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.\n\n```javascript\nvar equalsInsensitive = qs.parse('a&b=');\nassert.deepEqual(equalsInsensitive, { a: '', b: '' });\n```\n\nTo distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`\nvalues have no `=` sign:\n\n```javascript\nvar strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });\nassert.equal(strictNull, 'a&b=');\n```\n\nTo parse values without `=` back to `null` use the `strictNullHandling` flag:\n\n```javascript\nvar parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });\nassert.deepEqual(parsedStrictNull, { a: null, b: '' });\n```\n\nTo completely skip rendering keys with `null` values, use the `skipNulls` flag:\n\n```javascript\nvar nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });\nassert.equal(nullsSkipped, 'a=b');\n```\n\n### Dealing with special character sets\n\nBy default the encoding and decoding of characters is done in `utf-8`. If you\nwish to encode querystrings to a different character set (i.e.\n[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the\n[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:\n\n```javascript\nvar encoder = require('qs-iconv/encoder')('shift_jis');\nvar shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });\nassert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');\n```\n\nThis also works for decoding of query strings:\n\n```javascript\nvar decoder = require('qs-iconv/decoder')('shift_jis');\nvar obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });\nassert.deepEqual(obj, { a: 'こんにちは!' });\n```\n\n### RFC 3986 and RFC 1738 space encoding\n\nRFC3986 used as default option and encodes ' ' to *%20* which is backward compatible.\nIn the same time, output can be stringified as per RFC1738 with ' ' equal to '+'.\n\n```\nassert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');\nassert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');\nassert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');\n```\n\n[1]: https://npmjs.org/package/qs\n[2]: http://versionbadg.es/ljharb/qs.svg\n[3]: https://api.travis-ci.org/ljharb/qs.svg\n[4]: https://travis-ci.org/ljharb/qs\n[5]: https://david-dm.org/ljharb/qs.svg\n[6]: https://david-dm.org/ljharb/qs\n[7]: https://david-dm.org/ljharb/qs/dev-status.svg\n[8]: https://david-dm.org/ljharb/qs?type=dev\n[9]: https://ci.testling.com/ljharb/qs.png\n[10]: https://ci.testling.com/ljharb/qs\n[11]: https://nodei.co/npm/qs.png?downloads=true&stars=true\n[license-image]: http://img.shields.io/npm/l/qs.svg\n[license-url]: LICENSE\n[downloads-image]: http://img.shields.io/npm/dm/qs.svg\n[downloads-url]: http://npm-stat.com/charts.html?package=qs\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/ljharb/qs/issues" + }, + "_id": "qs@6.5.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "qs@6.5.2", + "name": "qs", + "escapedName": "qs", + "rawSpec": "6.5.2", + "saveSpec": "[Circular]", + "fetchSpec": "6.5.2" + }, + "_spec": "6.5.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "qs@6.5.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/qs", + "error": "[Circular]", + "extraneous": false + }, + "readable-stream": { + "name": "readable-stream", + "version": "2.3.6", + "description": "Streams3, a user-land copy of the stream library from Node.js", + "main": "readable.js", + "dependencies": { + "core-util-is": { + "name": "core-util-is", + "version": "1.0.2", + "description": "The `util.is*` functions introduced in Node v0.12.", + "main": "lib/util.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/core-util-is.git" + }, + "keywords": [ + "util", + "isBuffer", + "isArray", + "isNumber", + "isString", + "isRegExp", + "isThis", + "isThat", + "polyfill" + ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/isaacs/core-util-is/issues" + }, + "scripts": { + "test": "tap test.js" + }, + "devDependencies": { + "tap": "^2.3.0" + }, + "_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "_integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "_from": "core-util-is@1.0.2", + "readme": "# core-util-is\n\nThe `util.is*` functions introduced in Node v0.12.\n", + "readmeFilename": "README.md", + "homepage": "https://github.com/isaacs/core-util-is#readme", + "_id": "core-util-is@1.0.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "core-util-is@1.0.2", + "name": "core-util-is", + "escapedName": "core-util-is", + "rawSpec": "1.0.2", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.2" + }, + "_spec": "1.0.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "core-util-is@1.0.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/core-util-is", + "error": "[Circular]", + "extraneous": false + }, + "inherits": { + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.3", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, + "license": "ISC", + "scripts": { + "test": "node test" + }, + "devDependencies": { + "tap": "^7.1.0" + }, + "files": [ + "inherits.js", + "inherits_browser.js" + ], + "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "_integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "_from": "inherits@2.0.3", + "readme": "Browser-friendly inheritance fully compatible with standard node.js\n[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).\n\nThis package exports standard `inherits` from node.js `util` module in\nnode environment, but also provides alternative browser-friendly\nimplementation through [browser\nfield](https://gist.github.com/shtylman/4339901). Alternative\nimplementation is a literal copy of standard one located in standalone\nmodule to avoid requiring of `util`. It also has a shim for old\nbrowsers with no `Object.create` support.\n\nWhile keeping you sure you are using standard `inherits`\nimplementation in node.js environment, it allows bundlers such as\n[browserify](https://github.com/substack/node-browserify) to not\ninclude full `util` package to your client code if all you need is\njust `inherits` function. It worth, because browser shim for `util`\npackage is large and `inherits` is often the single function you need\nfrom it.\n\nIt's recommended to use this package instead of\n`require('util').inherits` for any code that has chances to be used\nnot only in node.js but in browser too.\n\n## usage\n\n```js\nvar inherits = require('inherits');\n// then use exactly as the standard one\n```\n\n## note on version ~1.0\n\nVersion ~1.0 had completely different motivation and is not compatible\nneither with 2.0 nor with standard node.js `inherits`.\n\nIf you are using version ~1.0 and planning to switch to ~2.0, be\ncareful:\n\n* new version uses `super_` instead of `super` for referencing\n superclass\n* new version overwrites current prototype while old one preserves any\n existing fields on it\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "homepage": "https://github.com/isaacs/inherits#readme", + "_id": "inherits@2.0.3", + "_requested": { + "type": "version", + "registry": true, + "raw": "inherits@2.0.3", + "name": "inherits", + "escapedName": "inherits", + "rawSpec": "2.0.3", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.3" + }, + "_spec": "2.0.3", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "inherits@2.0.3", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/inherits", + "error": "[Circular]", + "extraneous": false + }, + "isarray": { + "name": "isarray", + "description": "Array#isArray for older browsers", + "version": "1.0.0", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/isarray.git" + }, + "homepage": "https://github.com/juliangruber/isarray", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "tape": "~2.13.4" + }, + "keywords": [ + "browser", + "isarray", + "array" + ], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test.js", + "browsers": [ + "ie/8..latest", + "firefox/17..latest", + "firefox/nightly", + "chrome/22..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "scripts": { + "test": "tape test.js" + }, + "_resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "_integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "_from": "isarray@1.0.0", + "readme": "\n# isarray\n\n`Array#isArray` for older browsers.\n\n[![build status](https://secure.travis-ci.org/juliangruber/isarray.svg)](http://travis-ci.org/juliangruber/isarray)\n[![downloads](https://img.shields.io/npm/dm/isarray.svg)](https://www.npmjs.org/package/isarray)\n\n[![browser support](https://ci.testling.com/juliangruber/isarray.png)\n](https://ci.testling.com/juliangruber/isarray)\n\n## Usage\n\n```js\nvar isArray = require('isarray');\n\nconsole.log(isArray([])); // => true\nconsole.log(isArray({})); // => false\n```\n\n## Installation\n\nWith [npm](http://npmjs.org) do\n\n```bash\n$ npm install isarray\n```\n\nThen bundle for the browser with\n[browserify](https://github.com/substack/browserify).\n\nWith [component](http://component.io) do\n\n```bash\n$ component install juliangruber/isarray\n```\n\n## License\n\n(MIT)\n\nCopyright (c) 2013 Julian Gruber <julian@juliangruber.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/juliangruber/isarray/issues" + }, + "_id": "isarray@1.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "isarray@1.0.0", + "name": "isarray", + "escapedName": "isarray", + "rawSpec": "1.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.0" + }, + "_spec": "1.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "isarray@1.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/isarray", + "error": "[Circular]", + "extraneous": false + }, + "process-nextick-args": { + "name": "process-nextick-args", + "version": "2.0.0", + "description": "process.nextTick but always with args", + "main": "index.js", + "files": [ + "index.js" + ], + "scripts": { + "test": "node test.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/calvinmetcalf/process-nextick-args.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/calvinmetcalf/process-nextick-args/issues" + }, + "homepage": "https://github.com/calvinmetcalf/process-nextick-args", + "devDependencies": { + "tap": "~0.2.6" + }, + "_resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "_integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "_from": "process-nextick-args@2.0.0", + "readme": "process-nextick-args\n=====\n\n[![Build Status](https://travis-ci.org/calvinmetcalf/process-nextick-args.svg?branch=master)](https://travis-ci.org/calvinmetcalf/process-nextick-args)\n\n```bash\nnpm install --save process-nextick-args\n```\n\nAlways be able to pass arguments to process.nextTick, no matter the platform\n\n```js\nvar pna = require('process-nextick-args');\n\npna.nextTick(function (a, b, c) {\n console.log(a, b, c);\n}, 'step', 3, 'profit');\n```\n", + "readmeFilename": "readme.md", + "_id": "process-nextick-args@2.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "process-nextick-args@2.0.0", + "name": "process-nextick-args", + "escapedName": "process-nextick-args", + "rawSpec": "2.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.0" + }, + "_spec": "2.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "process-nextick-args@2.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/process-nextick-args", + "error": "[Circular]", + "extraneous": false + }, + "safe-buffer": { + "name": "safe-buffer", + "description": "Safer Node.js Buffer API", + "version": "5.1.2", + "author": { + "name": "Feross Aboukhadijeh", + "email": "feross@feross.org", + "url": "http://feross.org" + }, + "bugs": { + "url": "https://github.com/feross/safe-buffer/issues" + }, + "devDependencies": { + "standard": "*", + "tape": "^4.0.0" + }, + "homepage": "https://github.com/feross/safe-buffer", + "keywords": [ + "buffer", + "buffer allocate", + "node security", + "safe", + "safe-buffer", + "security", + "uninitialized" + ], + "license": "MIT", + "main": "index.js", + "types": "index.d.ts", + "repository": { + "type": "git", + "url": "git://github.com/feross/safe-buffer.git" + }, + "scripts": { + "test": "standard && tape test/*.js" + }, + "_resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "_integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "_from": "safe-buffer@5.1.2", + "readme": "# safe-buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]\n\n[travis-image]: https://img.shields.io/travis/feross/safe-buffer/master.svg\n[travis-url]: https://travis-ci.org/feross/safe-buffer\n[npm-image]: https://img.shields.io/npm/v/safe-buffer.svg\n[npm-url]: https://npmjs.org/package/safe-buffer\n[downloads-image]: https://img.shields.io/npm/dm/safe-buffer.svg\n[downloads-url]: https://npmjs.org/package/safe-buffer\n[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg\n[standard-url]: https://standardjs.com\n\n#### Safer Node.js Buffer API\n\n**Use the new Node.js Buffer APIs (`Buffer.from`, `Buffer.alloc`,\n`Buffer.allocUnsafe`, `Buffer.allocUnsafeSlow`) in all versions of Node.js.**\n\n**Uses the built-in implementation when available.**\n\n## install\n\n```\nnpm install safe-buffer\n```\n\n## usage\n\nThe goal of this package is to provide a safe replacement for the node.js `Buffer`.\n\nIt's a drop-in replacement for `Buffer`. You can use it by adding one `require` line to\nthe top of your node.js modules:\n\n```js\nvar Buffer = require('safe-buffer').Buffer\n\n// Existing buffer code will continue to work without issues:\n\nnew Buffer('hey', 'utf8')\nnew Buffer([1, 2, 3], 'utf8')\nnew Buffer(obj)\nnew Buffer(16) // create an uninitialized buffer (potentially unsafe)\n\n// But you can use these new explicit APIs to make clear what you want:\n\nBuffer.from('hey', 'utf8') // convert from many types to a Buffer\nBuffer.alloc(16) // create a zero-filled buffer (safe)\nBuffer.allocUnsafe(16) // create an uninitialized buffer (potentially unsafe)\n```\n\n## api\n\n### Class Method: Buffer.from(array)\n\n\n* `array` {Array}\n\nAllocates a new `Buffer` using an `array` of octets.\n\n```js\nconst buf = Buffer.from([0x62,0x75,0x66,0x66,0x65,0x72]);\n // creates a new Buffer containing ASCII bytes\n // ['b','u','f','f','e','r']\n```\n\nA `TypeError` will be thrown if `array` is not an `Array`.\n\n### Class Method: Buffer.from(arrayBuffer[, byteOffset[, length]])\n\n\n* `arrayBuffer` {ArrayBuffer} The `.buffer` property of a `TypedArray` or\n a `new ArrayBuffer()`\n* `byteOffset` {Number} Default: `0`\n* `length` {Number} Default: `arrayBuffer.length - byteOffset`\n\nWhen passed a reference to the `.buffer` property of a `TypedArray` instance,\nthe newly created `Buffer` will share the same allocated memory as the\nTypedArray.\n\n```js\nconst arr = new Uint16Array(2);\narr[0] = 5000;\narr[1] = 4000;\n\nconst buf = Buffer.from(arr.buffer); // shares the memory with arr;\n\nconsole.log(buf);\n // Prints: \n\n// changing the TypedArray changes the Buffer also\narr[1] = 6000;\n\nconsole.log(buf);\n // Prints: \n```\n\nThe optional `byteOffset` and `length` arguments specify a memory range within\nthe `arrayBuffer` that will be shared by the `Buffer`.\n\n```js\nconst ab = new ArrayBuffer(10);\nconst buf = Buffer.from(ab, 0, 2);\nconsole.log(buf.length);\n // Prints: 2\n```\n\nA `TypeError` will be thrown if `arrayBuffer` is not an `ArrayBuffer`.\n\n### Class Method: Buffer.from(buffer)\n\n\n* `buffer` {Buffer}\n\nCopies the passed `buffer` data onto a new `Buffer` instance.\n\n```js\nconst buf1 = Buffer.from('buffer');\nconst buf2 = Buffer.from(buf1);\n\nbuf1[0] = 0x61;\nconsole.log(buf1.toString());\n // 'auffer'\nconsole.log(buf2.toString());\n // 'buffer' (copy is not changed)\n```\n\nA `TypeError` will be thrown if `buffer` is not a `Buffer`.\n\n### Class Method: Buffer.from(str[, encoding])\n\n\n* `str` {String} String to encode.\n* `encoding` {String} Encoding to use, Default: `'utf8'`\n\nCreates a new `Buffer` containing the given JavaScript string `str`. If\nprovided, the `encoding` parameter identifies the character encoding.\nIf not provided, `encoding` defaults to `'utf8'`.\n\n```js\nconst buf1 = Buffer.from('this is a tést');\nconsole.log(buf1.toString());\n // prints: this is a tést\nconsole.log(buf1.toString('ascii'));\n // prints: this is a tC)st\n\nconst buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex');\nconsole.log(buf2.toString());\n // prints: this is a tést\n```\n\nA `TypeError` will be thrown if `str` is not a string.\n\n### Class Method: Buffer.alloc(size[, fill[, encoding]])\n\n\n* `size` {Number}\n* `fill` {Value} Default: `undefined`\n* `encoding` {String} Default: `utf8`\n\nAllocates a new `Buffer` of `size` bytes. If `fill` is `undefined`, the\n`Buffer` will be *zero-filled*.\n\n```js\nconst buf = Buffer.alloc(5);\nconsole.log(buf);\n // \n```\n\nThe `size` must be less than or equal to the value of\n`require('buffer').kMaxLength` (on 64-bit architectures, `kMaxLength` is\n`(2^31)-1`). Otherwise, a [`RangeError`][] is thrown. A zero-length Buffer will\nbe created if a `size` less than or equal to 0 is specified.\n\nIf `fill` is specified, the allocated `Buffer` will be initialized by calling\n`buf.fill(fill)`. See [`buf.fill()`][] for more information.\n\n```js\nconst buf = Buffer.alloc(5, 'a');\nconsole.log(buf);\n // \n```\n\nIf both `fill` and `encoding` are specified, the allocated `Buffer` will be\ninitialized by calling `buf.fill(fill, encoding)`. For example:\n\n```js\nconst buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');\nconsole.log(buf);\n // \n```\n\nCalling `Buffer.alloc(size)` can be significantly slower than the alternative\n`Buffer.allocUnsafe(size)` but ensures that the newly created `Buffer` instance\ncontents will *never contain sensitive data*.\n\nA `TypeError` will be thrown if `size` is not a number.\n\n### Class Method: Buffer.allocUnsafe(size)\n\n\n* `size` {Number}\n\nAllocates a new *non-zero-filled* `Buffer` of `size` bytes. The `size` must\nbe less than or equal to the value of `require('buffer').kMaxLength` (on 64-bit\narchitectures, `kMaxLength` is `(2^31)-1`). Otherwise, a [`RangeError`][] is\nthrown. A zero-length Buffer will be created if a `size` less than or equal to\n0 is specified.\n\nThe underlying memory for `Buffer` instances created in this way is *not\ninitialized*. The contents of the newly created `Buffer` are unknown and\n*may contain sensitive data*. Use [`buf.fill(0)`][] to initialize such\n`Buffer` instances to zeroes.\n\n```js\nconst buf = Buffer.allocUnsafe(5);\nconsole.log(buf);\n // \n // (octets will be different, every time)\nbuf.fill(0);\nconsole.log(buf);\n // \n```\n\nA `TypeError` will be thrown if `size` is not a number.\n\nNote that the `Buffer` module pre-allocates an internal `Buffer` instance of\nsize `Buffer.poolSize` that is used as a pool for the fast allocation of new\n`Buffer` instances created using `Buffer.allocUnsafe(size)` (and the deprecated\n`new Buffer(size)` constructor) only when `size` is less than or equal to\n`Buffer.poolSize >> 1` (floor of `Buffer.poolSize` divided by two). The default\nvalue of `Buffer.poolSize` is `8192` but can be modified.\n\nUse of this pre-allocated internal memory pool is a key difference between\ncalling `Buffer.alloc(size, fill)` vs. `Buffer.allocUnsafe(size).fill(fill)`.\nSpecifically, `Buffer.alloc(size, fill)` will *never* use the internal Buffer\npool, while `Buffer.allocUnsafe(size).fill(fill)` *will* use the internal\nBuffer pool if `size` is less than or equal to half `Buffer.poolSize`. The\ndifference is subtle but can be important when an application requires the\nadditional performance that `Buffer.allocUnsafe(size)` provides.\n\n### Class Method: Buffer.allocUnsafeSlow(size)\n\n\n* `size` {Number}\n\nAllocates a new *non-zero-filled* and non-pooled `Buffer` of `size` bytes. The\n`size` must be less than or equal to the value of\n`require('buffer').kMaxLength` (on 64-bit architectures, `kMaxLength` is\n`(2^31)-1`). Otherwise, a [`RangeError`][] is thrown. A zero-length Buffer will\nbe created if a `size` less than or equal to 0 is specified.\n\nThe underlying memory for `Buffer` instances created in this way is *not\ninitialized*. The contents of the newly created `Buffer` are unknown and\n*may contain sensitive data*. Use [`buf.fill(0)`][] to initialize such\n`Buffer` instances to zeroes.\n\nWhen using `Buffer.allocUnsafe()` to allocate new `Buffer` instances,\nallocations under 4KB are, by default, sliced from a single pre-allocated\n`Buffer`. This allows applications to avoid the garbage collection overhead of\ncreating many individually allocated Buffers. This approach improves both\nperformance and memory usage by eliminating the need to track and cleanup as\nmany `Persistent` objects.\n\nHowever, in the case where a developer may need to retain a small chunk of\nmemory from a pool for an indeterminate amount of time, it may be appropriate\nto create an un-pooled Buffer instance using `Buffer.allocUnsafeSlow()` then\ncopy out the relevant bits.\n\n```js\n// need to keep around a few small chunks of memory\nconst store = [];\n\nsocket.on('readable', () => {\n const data = socket.read();\n // allocate for retained data\n const sb = Buffer.allocUnsafeSlow(10);\n // copy the data into the new allocation\n data.copy(sb, 0, 0, 10);\n store.push(sb);\n});\n```\n\nUse of `Buffer.allocUnsafeSlow()` should be used only as a last resort *after*\na developer has observed undue memory retention in their applications.\n\nA `TypeError` will be thrown if `size` is not a number.\n\n### All the Rest\n\nThe rest of the `Buffer` API is exactly the same as in node.js.\n[See the docs](https://nodejs.org/api/buffer.html).\n\n\n## Related links\n\n- [Node.js issue: Buffer(number) is unsafe](https://github.com/nodejs/node/issues/4660)\n- [Node.js Enhancement Proposal: Buffer.from/Buffer.alloc/Buffer.zalloc/Buffer() soft-deprecate](https://github.com/nodejs/node-eps/pull/4)\n\n## Why is `Buffer` unsafe?\n\nToday, the node.js `Buffer` constructor is overloaded to handle many different argument\ntypes like `String`, `Array`, `Object`, `TypedArrayView` (`Uint8Array`, etc.),\n`ArrayBuffer`, and also `Number`.\n\nThe API is optimized for convenience: you can throw any type at it, and it will try to do\nwhat you want.\n\nBecause the Buffer constructor is so powerful, you often see code like this:\n\n```js\n// Convert UTF-8 strings to hex\nfunction toHex (str) {\n return new Buffer(str).toString('hex')\n}\n```\n\n***But what happens if `toHex` is called with a `Number` argument?***\n\n### Remote Memory Disclosure\n\nIf an attacker can make your program call the `Buffer` constructor with a `Number`\nargument, then they can make it allocate uninitialized memory from the node.js process.\nThis could potentially disclose TLS private keys, user data, or database passwords.\n\nWhen the `Buffer` constructor is passed a `Number` argument, it returns an\n**UNINITIALIZED** block of memory of the specified `size`. When you create a `Buffer` like\nthis, you **MUST** overwrite the contents before returning it to the user.\n\nFrom the [node.js docs](https://nodejs.org/api/buffer.html#buffer_new_buffer_size):\n\n> `new Buffer(size)`\n>\n> - `size` Number\n>\n> The underlying memory for `Buffer` instances created in this way is not initialized.\n> **The contents of a newly created `Buffer` are unknown and could contain sensitive\n> data.** Use `buf.fill(0)` to initialize a Buffer to zeroes.\n\n(Emphasis our own.)\n\nWhenever the programmer intended to create an uninitialized `Buffer` you often see code\nlike this:\n\n```js\nvar buf = new Buffer(16)\n\n// Immediately overwrite the uninitialized buffer with data from another buffer\nfor (var i = 0; i < buf.length; i++) {\n buf[i] = otherBuf[i]\n}\n```\n\n\n### Would this ever be a problem in real code?\n\nYes. It's surprisingly common to forget to check the type of your variables in a\ndynamically-typed language like JavaScript.\n\nUsually the consequences of assuming the wrong type is that your program crashes with an\nuncaught exception. But the failure mode for forgetting to check the type of arguments to\nthe `Buffer` constructor is more catastrophic.\n\nHere's an example of a vulnerable service that takes a JSON payload and converts it to\nhex:\n\n```js\n// Take a JSON payload {str: \"some string\"} and convert it to hex\nvar server = http.createServer(function (req, res) {\n var data = ''\n req.setEncoding('utf8')\n req.on('data', function (chunk) {\n data += chunk\n })\n req.on('end', function () {\n var body = JSON.parse(data)\n res.end(new Buffer(body.str).toString('hex'))\n })\n})\n\nserver.listen(8080)\n```\n\nIn this example, an http client just has to send:\n\n```json\n{\n \"str\": 1000\n}\n```\n\nand it will get back 1,000 bytes of uninitialized memory from the server.\n\nThis is a very serious bug. It's similar in severity to the\n[the Heartbleed bug](http://heartbleed.com/) that allowed disclosure of OpenSSL process\nmemory by remote attackers.\n\n\n### Which real-world packages were vulnerable?\n\n#### [`bittorrent-dht`](https://www.npmjs.com/package/bittorrent-dht)\n\n[Mathias Buus](https://github.com/mafintosh) and I\n([Feross Aboukhadijeh](http://feross.org/)) found this issue in one of our own packages,\n[`bittorrent-dht`](https://www.npmjs.com/package/bittorrent-dht). The bug would allow\nanyone on the internet to send a series of messages to a user of `bittorrent-dht` and get\nthem to reveal 20 bytes at a time of uninitialized memory from the node.js process.\n\nHere's\n[the commit](https://github.com/feross/bittorrent-dht/commit/6c7da04025d5633699800a99ec3fbadf70ad35b8)\nthat fixed it. We released a new fixed version, created a\n[Node Security Project disclosure](https://nodesecurity.io/advisories/68), and deprecated all\nvulnerable versions on npm so users will get a warning to upgrade to a newer version.\n\n#### [`ws`](https://www.npmjs.com/package/ws)\n\nThat got us wondering if there were other vulnerable packages. Sure enough, within a short\nperiod of time, we found the same issue in [`ws`](https://www.npmjs.com/package/ws), the\nmost popular WebSocket implementation in node.js.\n\nIf certain APIs were called with `Number` parameters instead of `String` or `Buffer` as\nexpected, then uninitialized server memory would be disclosed to the remote peer.\n\nThese were the vulnerable methods:\n\n```js\nsocket.send(number)\nsocket.ping(number)\nsocket.pong(number)\n```\n\nHere's a vulnerable socket server with some echo functionality:\n\n```js\nserver.on('connection', function (socket) {\n socket.on('message', function (message) {\n message = JSON.parse(message)\n if (message.type === 'echo') {\n socket.send(message.data) // send back the user's message\n }\n })\n})\n```\n\n`socket.send(number)` called on the server, will disclose server memory.\n\nHere's [the release](https://github.com/websockets/ws/releases/tag/1.0.1) where the issue\nwas fixed, with a more detailed explanation. Props to\n[Arnout Kazemier](https://github.com/3rd-Eden) for the quick fix. Here's the\n[Node Security Project disclosure](https://nodesecurity.io/advisories/67).\n\n\n### What's the solution?\n\nIt's important that node.js offers a fast way to get memory otherwise performance-critical\napplications would needlessly get a lot slower.\n\nBut we need a better way to *signal our intent* as programmers. **When we want\nuninitialized memory, we should request it explicitly.**\n\nSensitive functionality should not be packed into a developer-friendly API that loosely\naccepts many different types. This type of API encourages the lazy practice of passing\nvariables in without checking the type very carefully.\n\n#### A new API: `Buffer.allocUnsafe(number)`\n\nThe functionality of creating buffers with uninitialized memory should be part of another\nAPI. We propose `Buffer.allocUnsafe(number)`. This way, it's not part of an API that\nfrequently gets user input of all sorts of different types passed into it.\n\n```js\nvar buf = Buffer.allocUnsafe(16) // careful, uninitialized memory!\n\n// Immediately overwrite the uninitialized buffer with data from another buffer\nfor (var i = 0; i < buf.length; i++) {\n buf[i] = otherBuf[i]\n}\n```\n\n\n### How do we fix node.js core?\n\nWe sent [a PR to node.js core](https://github.com/nodejs/node/pull/4514) (merged as\n`semver-major`) which defends against one case:\n\n```js\nvar str = 16\nnew Buffer(str, 'utf8')\n```\n\nIn this situation, it's implied that the programmer intended the first argument to be a\nstring, since they passed an encoding as a second argument. Today, node.js will allocate\nuninitialized memory in the case of `new Buffer(number, encoding)`, which is probably not\nwhat the programmer intended.\n\nBut this is only a partial solution, since if the programmer does `new Buffer(variable)`\n(without an `encoding` parameter) there's no way to know what they intended. If `variable`\nis sometimes a number, then uninitialized memory will sometimes be returned.\n\n### What's the real long-term fix?\n\nWe could deprecate and remove `new Buffer(number)` and use `Buffer.allocUnsafe(number)` when\nwe need uninitialized memory. But that would break 1000s of packages.\n\n~~We believe the best solution is to:~~\n\n~~1. Change `new Buffer(number)` to return safe, zeroed-out memory~~\n\n~~2. Create a new API for creating uninitialized Buffers. We propose: `Buffer.allocUnsafe(number)`~~\n\n#### Update\n\nWe now support adding three new APIs:\n\n- `Buffer.from(value)` - convert from any type to a buffer\n- `Buffer.alloc(size)` - create a zero-filled buffer\n- `Buffer.allocUnsafe(size)` - create an uninitialized buffer with given size\n\nThis solves the core problem that affected `ws` and `bittorrent-dht` which is\n`Buffer(variable)` getting tricked into taking a number argument.\n\nThis way, existing code continues working and the impact on the npm ecosystem will be\nminimal. Over time, npm maintainers can migrate performance-critical code to use\n`Buffer.allocUnsafe(number)` instead of `new Buffer(number)`.\n\n\n### Conclusion\n\nWe think there's a serious design issue with the `Buffer` API as it exists today. It\npromotes insecure software by putting high-risk functionality into a convenient API\nwith friendly \"developer ergonomics\".\n\nThis wasn't merely a theoretical exercise because we found the issue in some of the\nmost popular npm packages.\n\nFortunately, there's an easy fix that can be applied today. Use `safe-buffer` in place of\n`buffer`.\n\n```js\nvar Buffer = require('safe-buffer').Buffer\n```\n\nEventually, we hope that node.js core can switch to this new, safer behavior. We believe\nthe impact on the ecosystem would be minimal since it's not a breaking change.\nWell-maintained, popular packages would be updated to use `Buffer.alloc` quickly, while\nolder, insecure packages would magically become safe from this attack vector.\n\n\n## links\n\n- [Node.js PR: buffer: throw if both length and enc are passed](https://github.com/nodejs/node/pull/4514)\n- [Node Security Project disclosure for `ws`](https://nodesecurity.io/advisories/67)\n- [Node Security Project disclosure for`bittorrent-dht`](https://nodesecurity.io/advisories/68)\n\n\n## credit\n\nThe original issues in `bittorrent-dht`\n([disclosure](https://nodesecurity.io/advisories/68)) and\n`ws` ([disclosure](https://nodesecurity.io/advisories/67)) were discovered by\n[Mathias Buus](https://github.com/mafintosh) and\n[Feross Aboukhadijeh](http://feross.org/).\n\nThanks to [Adam Baldwin](https://github.com/evilpacket) for helping disclose these issues\nand for his work running the [Node Security Project](https://nodesecurity.io/).\n\nThanks to [John Hiesey](https://github.com/jhiesey) for proofreading this README and\nauditing the code.\n\n\n## license\n\nMIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org)\n", + "readmeFilename": "README.md", + "_id": "safe-buffer@5.1.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "safe-buffer@5.1.2", + "name": "safe-buffer", + "escapedName": "safe-buffer", + "rawSpec": "5.1.2", + "saveSpec": "[Circular]", + "fetchSpec": "5.1.2" + }, + "_spec": "5.1.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "safe-buffer@5.1.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/safe-buffer", + "error": "[Circular]", + "extraneous": false + }, + "string_decoder": { + "name": "string_decoder", + "version": "1.1.1", + "description": "The string_decoder module from Node core", + "main": "lib/string_decoder.js", + "dependencies": { + "safe-buffer": { + "name": "safe-buffer", + "description": "Safer Node.js Buffer API", + "version": "5.1.2", + "author": "[Circular]", + "bugs": "[Circular]", + "devDependencies": "[Circular]", + "homepage": "https://github.com/feross/safe-buffer", + "keywords": "[Circular]", + "license": "MIT", + "main": "index.js", + "types": "index.d.ts", + "repository": "[Circular]", + "scripts": "[Circular]", + "_resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "_integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "_from": "safe-buffer@5.1.2", + "readme": "# safe-buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]\n\n[travis-image]: https://img.shields.io/travis/feross/safe-buffer/master.svg\n[travis-url]: https://travis-ci.org/feross/safe-buffer\n[npm-image]: https://img.shields.io/npm/v/safe-buffer.svg\n[npm-url]: https://npmjs.org/package/safe-buffer\n[downloads-image]: https://img.shields.io/npm/dm/safe-buffer.svg\n[downloads-url]: https://npmjs.org/package/safe-buffer\n[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg\n[standard-url]: https://standardjs.com\n\n#### Safer Node.js Buffer API\n\n**Use the new Node.js Buffer APIs (`Buffer.from`, `Buffer.alloc`,\n`Buffer.allocUnsafe`, `Buffer.allocUnsafeSlow`) in all versions of Node.js.**\n\n**Uses the built-in implementation when available.**\n\n## install\n\n```\nnpm install safe-buffer\n```\n\n## usage\n\nThe goal of this package is to provide a safe replacement for the node.js `Buffer`.\n\nIt's a drop-in replacement for `Buffer`. You can use it by adding one `require` line to\nthe top of your node.js modules:\n\n```js\nvar Buffer = require('safe-buffer').Buffer\n\n// Existing buffer code will continue to work without issues:\n\nnew Buffer('hey', 'utf8')\nnew Buffer([1, 2, 3], 'utf8')\nnew Buffer(obj)\nnew Buffer(16) // create an uninitialized buffer (potentially unsafe)\n\n// But you can use these new explicit APIs to make clear what you want:\n\nBuffer.from('hey', 'utf8') // convert from many types to a Buffer\nBuffer.alloc(16) // create a zero-filled buffer (safe)\nBuffer.allocUnsafe(16) // create an uninitialized buffer (potentially unsafe)\n```\n\n## api\n\n### Class Method: Buffer.from(array)\n\n\n* `array` {Array}\n\nAllocates a new `Buffer` using an `array` of octets.\n\n```js\nconst buf = Buffer.from([0x62,0x75,0x66,0x66,0x65,0x72]);\n // creates a new Buffer containing ASCII bytes\n // ['b','u','f','f','e','r']\n```\n\nA `TypeError` will be thrown if `array` is not an `Array`.\n\n### Class Method: Buffer.from(arrayBuffer[, byteOffset[, length]])\n\n\n* `arrayBuffer` {ArrayBuffer} The `.buffer` property of a `TypedArray` or\n a `new ArrayBuffer()`\n* `byteOffset` {Number} Default: `0`\n* `length` {Number} Default: `arrayBuffer.length - byteOffset`\n\nWhen passed a reference to the `.buffer` property of a `TypedArray` instance,\nthe newly created `Buffer` will share the same allocated memory as the\nTypedArray.\n\n```js\nconst arr = new Uint16Array(2);\narr[0] = 5000;\narr[1] = 4000;\n\nconst buf = Buffer.from(arr.buffer); // shares the memory with arr;\n\nconsole.log(buf);\n // Prints: \n\n// changing the TypedArray changes the Buffer also\narr[1] = 6000;\n\nconsole.log(buf);\n // Prints: \n```\n\nThe optional `byteOffset` and `length` arguments specify a memory range within\nthe `arrayBuffer` that will be shared by the `Buffer`.\n\n```js\nconst ab = new ArrayBuffer(10);\nconst buf = Buffer.from(ab, 0, 2);\nconsole.log(buf.length);\n // Prints: 2\n```\n\nA `TypeError` will be thrown if `arrayBuffer` is not an `ArrayBuffer`.\n\n### Class Method: Buffer.from(buffer)\n\n\n* `buffer` {Buffer}\n\nCopies the passed `buffer` data onto a new `Buffer` instance.\n\n```js\nconst buf1 = Buffer.from('buffer');\nconst buf2 = Buffer.from(buf1);\n\nbuf1[0] = 0x61;\nconsole.log(buf1.toString());\n // 'auffer'\nconsole.log(buf2.toString());\n // 'buffer' (copy is not changed)\n```\n\nA `TypeError` will be thrown if `buffer` is not a `Buffer`.\n\n### Class Method: Buffer.from(str[, encoding])\n\n\n* `str` {String} String to encode.\n* `encoding` {String} Encoding to use, Default: `'utf8'`\n\nCreates a new `Buffer` containing the given JavaScript string `str`. If\nprovided, the `encoding` parameter identifies the character encoding.\nIf not provided, `encoding` defaults to `'utf8'`.\n\n```js\nconst buf1 = Buffer.from('this is a tést');\nconsole.log(buf1.toString());\n // prints: this is a tést\nconsole.log(buf1.toString('ascii'));\n // prints: this is a tC)st\n\nconst buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex');\nconsole.log(buf2.toString());\n // prints: this is a tést\n```\n\nA `TypeError` will be thrown if `str` is not a string.\n\n### Class Method: Buffer.alloc(size[, fill[, encoding]])\n\n\n* `size` {Number}\n* `fill` {Value} Default: `undefined`\n* `encoding` {String} Default: `utf8`\n\nAllocates a new `Buffer` of `size` bytes. If `fill` is `undefined`, the\n`Buffer` will be *zero-filled*.\n\n```js\nconst buf = Buffer.alloc(5);\nconsole.log(buf);\n // \n```\n\nThe `size` must be less than or equal to the value of\n`require('buffer').kMaxLength` (on 64-bit architectures, `kMaxLength` is\n`(2^31)-1`). Otherwise, a [`RangeError`][] is thrown. A zero-length Buffer will\nbe created if a `size` less than or equal to 0 is specified.\n\nIf `fill` is specified, the allocated `Buffer` will be initialized by calling\n`buf.fill(fill)`. See [`buf.fill()`][] for more information.\n\n```js\nconst buf = Buffer.alloc(5, 'a');\nconsole.log(buf);\n // \n```\n\nIf both `fill` and `encoding` are specified, the allocated `Buffer` will be\ninitialized by calling `buf.fill(fill, encoding)`. For example:\n\n```js\nconst buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');\nconsole.log(buf);\n // \n```\n\nCalling `Buffer.alloc(size)` can be significantly slower than the alternative\n`Buffer.allocUnsafe(size)` but ensures that the newly created `Buffer` instance\ncontents will *never contain sensitive data*.\n\nA `TypeError` will be thrown if `size` is not a number.\n\n### Class Method: Buffer.allocUnsafe(size)\n\n\n* `size` {Number}\n\nAllocates a new *non-zero-filled* `Buffer` of `size` bytes. The `size` must\nbe less than or equal to the value of `require('buffer').kMaxLength` (on 64-bit\narchitectures, `kMaxLength` is `(2^31)-1`). Otherwise, a [`RangeError`][] is\nthrown. A zero-length Buffer will be created if a `size` less than or equal to\n0 is specified.\n\nThe underlying memory for `Buffer` instances created in this way is *not\ninitialized*. The contents of the newly created `Buffer` are unknown and\n*may contain sensitive data*. Use [`buf.fill(0)`][] to initialize such\n`Buffer` instances to zeroes.\n\n```js\nconst buf = Buffer.allocUnsafe(5);\nconsole.log(buf);\n // \n // (octets will be different, every time)\nbuf.fill(0);\nconsole.log(buf);\n // \n```\n\nA `TypeError` will be thrown if `size` is not a number.\n\nNote that the `Buffer` module pre-allocates an internal `Buffer` instance of\nsize `Buffer.poolSize` that is used as a pool for the fast allocation of new\n`Buffer` instances created using `Buffer.allocUnsafe(size)` (and the deprecated\n`new Buffer(size)` constructor) only when `size` is less than or equal to\n`Buffer.poolSize >> 1` (floor of `Buffer.poolSize` divided by two). The default\nvalue of `Buffer.poolSize` is `8192` but can be modified.\n\nUse of this pre-allocated internal memory pool is a key difference between\ncalling `Buffer.alloc(size, fill)` vs. `Buffer.allocUnsafe(size).fill(fill)`.\nSpecifically, `Buffer.alloc(size, fill)` will *never* use the internal Buffer\npool, while `Buffer.allocUnsafe(size).fill(fill)` *will* use the internal\nBuffer pool if `size` is less than or equal to half `Buffer.poolSize`. The\ndifference is subtle but can be important when an application requires the\nadditional performance that `Buffer.allocUnsafe(size)` provides.\n\n### Class Method: Buffer.allocUnsafeSlow(size)\n\n\n* `size` {Number}\n\nAllocates a new *non-zero-filled* and non-pooled `Buffer` of `size` bytes. The\n`size` must be less than or equal to the value of\n`require('buffer').kMaxLength` (on 64-bit architectures, `kMaxLength` is\n`(2^31)-1`). Otherwise, a [`RangeError`][] is thrown. A zero-length Buffer will\nbe created if a `size` less than or equal to 0 is specified.\n\nThe underlying memory for `Buffer` instances created in this way is *not\ninitialized*. The contents of the newly created `Buffer` are unknown and\n*may contain sensitive data*. Use [`buf.fill(0)`][] to initialize such\n`Buffer` instances to zeroes.\n\nWhen using `Buffer.allocUnsafe()` to allocate new `Buffer` instances,\nallocations under 4KB are, by default, sliced from a single pre-allocated\n`Buffer`. This allows applications to avoid the garbage collection overhead of\ncreating many individually allocated Buffers. This approach improves both\nperformance and memory usage by eliminating the need to track and cleanup as\nmany `Persistent` objects.\n\nHowever, in the case where a developer may need to retain a small chunk of\nmemory from a pool for an indeterminate amount of time, it may be appropriate\nto create an un-pooled Buffer instance using `Buffer.allocUnsafeSlow()` then\ncopy out the relevant bits.\n\n```js\n// need to keep around a few small chunks of memory\nconst store = [];\n\nsocket.on('readable', () => {\n const data = socket.read();\n // allocate for retained data\n const sb = Buffer.allocUnsafeSlow(10);\n // copy the data into the new allocation\n data.copy(sb, 0, 0, 10);\n store.push(sb);\n});\n```\n\nUse of `Buffer.allocUnsafeSlow()` should be used only as a last resort *after*\na developer has observed undue memory retention in their applications.\n\nA `TypeError` will be thrown if `size` is not a number.\n\n### All the Rest\n\nThe rest of the `Buffer` API is exactly the same as in node.js.\n[See the docs](https://nodejs.org/api/buffer.html).\n\n\n## Related links\n\n- [Node.js issue: Buffer(number) is unsafe](https://github.com/nodejs/node/issues/4660)\n- [Node.js Enhancement Proposal: Buffer.from/Buffer.alloc/Buffer.zalloc/Buffer() soft-deprecate](https://github.com/nodejs/node-eps/pull/4)\n\n## Why is `Buffer` unsafe?\n\nToday, the node.js `Buffer` constructor is overloaded to handle many different argument\ntypes like `String`, `Array`, `Object`, `TypedArrayView` (`Uint8Array`, etc.),\n`ArrayBuffer`, and also `Number`.\n\nThe API is optimized for convenience: you can throw any type at it, and it will try to do\nwhat you want.\n\nBecause the Buffer constructor is so powerful, you often see code like this:\n\n```js\n// Convert UTF-8 strings to hex\nfunction toHex (str) {\n return new Buffer(str).toString('hex')\n}\n```\n\n***But what happens if `toHex` is called with a `Number` argument?***\n\n### Remote Memory Disclosure\n\nIf an attacker can make your program call the `Buffer` constructor with a `Number`\nargument, then they can make it allocate uninitialized memory from the node.js process.\nThis could potentially disclose TLS private keys, user data, or database passwords.\n\nWhen the `Buffer` constructor is passed a `Number` argument, it returns an\n**UNINITIALIZED** block of memory of the specified `size`. When you create a `Buffer` like\nthis, you **MUST** overwrite the contents before returning it to the user.\n\nFrom the [node.js docs](https://nodejs.org/api/buffer.html#buffer_new_buffer_size):\n\n> `new Buffer(size)`\n>\n> - `size` Number\n>\n> The underlying memory for `Buffer` instances created in this way is not initialized.\n> **The contents of a newly created `Buffer` are unknown and could contain sensitive\n> data.** Use `buf.fill(0)` to initialize a Buffer to zeroes.\n\n(Emphasis our own.)\n\nWhenever the programmer intended to create an uninitialized `Buffer` you often see code\nlike this:\n\n```js\nvar buf = new Buffer(16)\n\n// Immediately overwrite the uninitialized buffer with data from another buffer\nfor (var i = 0; i < buf.length; i++) {\n buf[i] = otherBuf[i]\n}\n```\n\n\n### Would this ever be a problem in real code?\n\nYes. It's surprisingly common to forget to check the type of your variables in a\ndynamically-typed language like JavaScript.\n\nUsually the consequences of assuming the wrong type is that your program crashes with an\nuncaught exception. But the failure mode for forgetting to check the type of arguments to\nthe `Buffer` constructor is more catastrophic.\n\nHere's an example of a vulnerable service that takes a JSON payload and converts it to\nhex:\n\n```js\n// Take a JSON payload {str: \"some string\"} and convert it to hex\nvar server = http.createServer(function (req, res) {\n var data = ''\n req.setEncoding('utf8')\n req.on('data', function (chunk) {\n data += chunk\n })\n req.on('end', function () {\n var body = JSON.parse(data)\n res.end(new Buffer(body.str).toString('hex'))\n })\n})\n\nserver.listen(8080)\n```\n\nIn this example, an http client just has to send:\n\n```json\n{\n \"str\": 1000\n}\n```\n\nand it will get back 1,000 bytes of uninitialized memory from the server.\n\nThis is a very serious bug. It's similar in severity to the\n[the Heartbleed bug](http://heartbleed.com/) that allowed disclosure of OpenSSL process\nmemory by remote attackers.\n\n\n### Which real-world packages were vulnerable?\n\n#### [`bittorrent-dht`](https://www.npmjs.com/package/bittorrent-dht)\n\n[Mathias Buus](https://github.com/mafintosh) and I\n([Feross Aboukhadijeh](http://feross.org/)) found this issue in one of our own packages,\n[`bittorrent-dht`](https://www.npmjs.com/package/bittorrent-dht). The bug would allow\nanyone on the internet to send a series of messages to a user of `bittorrent-dht` and get\nthem to reveal 20 bytes at a time of uninitialized memory from the node.js process.\n\nHere's\n[the commit](https://github.com/feross/bittorrent-dht/commit/6c7da04025d5633699800a99ec3fbadf70ad35b8)\nthat fixed it. We released a new fixed version, created a\n[Node Security Project disclosure](https://nodesecurity.io/advisories/68), and deprecated all\nvulnerable versions on npm so users will get a warning to upgrade to a newer version.\n\n#### [`ws`](https://www.npmjs.com/package/ws)\n\nThat got us wondering if there were other vulnerable packages. Sure enough, within a short\nperiod of time, we found the same issue in [`ws`](https://www.npmjs.com/package/ws), the\nmost popular WebSocket implementation in node.js.\n\nIf certain APIs were called with `Number` parameters instead of `String` or `Buffer` as\nexpected, then uninitialized server memory would be disclosed to the remote peer.\n\nThese were the vulnerable methods:\n\n```js\nsocket.send(number)\nsocket.ping(number)\nsocket.pong(number)\n```\n\nHere's a vulnerable socket server with some echo functionality:\n\n```js\nserver.on('connection', function (socket) {\n socket.on('message', function (message) {\n message = JSON.parse(message)\n if (message.type === 'echo') {\n socket.send(message.data) // send back the user's message\n }\n })\n})\n```\n\n`socket.send(number)` called on the server, will disclose server memory.\n\nHere's [the release](https://github.com/websockets/ws/releases/tag/1.0.1) where the issue\nwas fixed, with a more detailed explanation. Props to\n[Arnout Kazemier](https://github.com/3rd-Eden) for the quick fix. Here's the\n[Node Security Project disclosure](https://nodesecurity.io/advisories/67).\n\n\n### What's the solution?\n\nIt's important that node.js offers a fast way to get memory otherwise performance-critical\napplications would needlessly get a lot slower.\n\nBut we need a better way to *signal our intent* as programmers. **When we want\nuninitialized memory, we should request it explicitly.**\n\nSensitive functionality should not be packed into a developer-friendly API that loosely\naccepts many different types. This type of API encourages the lazy practice of passing\nvariables in without checking the type very carefully.\n\n#### A new API: `Buffer.allocUnsafe(number)`\n\nThe functionality of creating buffers with uninitialized memory should be part of another\nAPI. We propose `Buffer.allocUnsafe(number)`. This way, it's not part of an API that\nfrequently gets user input of all sorts of different types passed into it.\n\n```js\nvar buf = Buffer.allocUnsafe(16) // careful, uninitialized memory!\n\n// Immediately overwrite the uninitialized buffer with data from another buffer\nfor (var i = 0; i < buf.length; i++) {\n buf[i] = otherBuf[i]\n}\n```\n\n\n### How do we fix node.js core?\n\nWe sent [a PR to node.js core](https://github.com/nodejs/node/pull/4514) (merged as\n`semver-major`) which defends against one case:\n\n```js\nvar str = 16\nnew Buffer(str, 'utf8')\n```\n\nIn this situation, it's implied that the programmer intended the first argument to be a\nstring, since they passed an encoding as a second argument. Today, node.js will allocate\nuninitialized memory in the case of `new Buffer(number, encoding)`, which is probably not\nwhat the programmer intended.\n\nBut this is only a partial solution, since if the programmer does `new Buffer(variable)`\n(without an `encoding` parameter) there's no way to know what they intended. If `variable`\nis sometimes a number, then uninitialized memory will sometimes be returned.\n\n### What's the real long-term fix?\n\nWe could deprecate and remove `new Buffer(number)` and use `Buffer.allocUnsafe(number)` when\nwe need uninitialized memory. But that would break 1000s of packages.\n\n~~We believe the best solution is to:~~\n\n~~1. Change `new Buffer(number)` to return safe, zeroed-out memory~~\n\n~~2. Create a new API for creating uninitialized Buffers. We propose: `Buffer.allocUnsafe(number)`~~\n\n#### Update\n\nWe now support adding three new APIs:\n\n- `Buffer.from(value)` - convert from any type to a buffer\n- `Buffer.alloc(size)` - create a zero-filled buffer\n- `Buffer.allocUnsafe(size)` - create an uninitialized buffer with given size\n\nThis solves the core problem that affected `ws` and `bittorrent-dht` which is\n`Buffer(variable)` getting tricked into taking a number argument.\n\nThis way, existing code continues working and the impact on the npm ecosystem will be\nminimal. Over time, npm maintainers can migrate performance-critical code to use\n`Buffer.allocUnsafe(number)` instead of `new Buffer(number)`.\n\n\n### Conclusion\n\nWe think there's a serious design issue with the `Buffer` API as it exists today. It\npromotes insecure software by putting high-risk functionality into a convenient API\nwith friendly \"developer ergonomics\".\n\nThis wasn't merely a theoretical exercise because we found the issue in some of the\nmost popular npm packages.\n\nFortunately, there's an easy fix that can be applied today. Use `safe-buffer` in place of\n`buffer`.\n\n```js\nvar Buffer = require('safe-buffer').Buffer\n```\n\nEventually, we hope that node.js core can switch to this new, safer behavior. We believe\nthe impact on the ecosystem would be minimal since it's not a breaking change.\nWell-maintained, popular packages would be updated to use `Buffer.alloc` quickly, while\nolder, insecure packages would magically become safe from this attack vector.\n\n\n## links\n\n- [Node.js PR: buffer: throw if both length and enc are passed](https://github.com/nodejs/node/pull/4514)\n- [Node Security Project disclosure for `ws`](https://nodesecurity.io/advisories/67)\n- [Node Security Project disclosure for`bittorrent-dht`](https://nodesecurity.io/advisories/68)\n\n\n## credit\n\nThe original issues in `bittorrent-dht`\n([disclosure](https://nodesecurity.io/advisories/68)) and\n`ws` ([disclosure](https://nodesecurity.io/advisories/67)) were discovered by\n[Mathias Buus](https://github.com/mafintosh) and\n[Feross Aboukhadijeh](http://feross.org/).\n\nThanks to [Adam Baldwin](https://github.com/evilpacket) for helping disclose these issues\nand for his work running the [Node Security Project](https://nodesecurity.io/).\n\nThanks to [John Hiesey](https://github.com/jhiesey) for proofreading this README and\nauditing the code.\n\n\n## license\n\nMIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org)\n", + "readmeFilename": "README.md", + "_id": "safe-buffer@5.1.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "safe-buffer@5.1.2", + "name": "safe-buffer", + "escapedName": "safe-buffer", + "rawSpec": "5.1.2", + "saveSpec": "[Circular]", + "fetchSpec": "5.1.2" + }, + "_spec": "5.1.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": "[Circular]", + "dependencies": {}, + "optionalDependencies": "[Circular]", + "_dependencies": "[Circular]", + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/safe-buffer", + "error": "[Circular]", + "extraneous": false, + "_deduped": "safe-buffer" + } + }, + "devDependencies": { + "babel-polyfill": "^6.23.0", + "core-util-is": "^1.0.2", + "inherits": "^2.0.3", + "tap": "~0.4.8" + }, + "scripts": { + "test": "tap test/parallel/*.js && node test/verify-dependencies", + "ci": "tap test/parallel/*.js test/ours/*.js --tap | tee test.tap && node test/verify-dependencies.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/nodejs/string_decoder.git" + }, + "homepage": "https://github.com/nodejs/string_decoder", + "keywords": [ + "string", + "decoder", + "browser", + "browserify" + ], + "license": "MIT", + "_resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "_integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "_from": "string_decoder@1.1.1", + "readme": "# string_decoder\n\n***Node-core v8.9.4 string_decoder for userland***\n\n\n[![NPM](https://nodei.co/npm/string_decoder.png?downloads=true&downloadRank=true)](https://nodei.co/npm/string_decoder/)\n[![NPM](https://nodei.co/npm-dl/string_decoder.png?&months=6&height=3)](https://nodei.co/npm/string_decoder/)\n\n\n```bash\nnpm install --save string_decoder\n```\n\n***Node-core string_decoder for userland***\n\nThis package is a mirror of the string_decoder implementation in Node-core.\n\nFull documentation may be found on the [Node.js website](https://nodejs.org/dist/v8.9.4/docs/api/).\n\nAs of version 1.0.0 **string_decoder** uses semantic versioning.\n\n## Previous versions\n\nPrevious version numbers match the versions found in Node core, e.g. 0.10.24 matches Node 0.10.24, likewise 0.11.10 matches Node 0.11.10.\n\n## Update\n\nThe *build/* directory contains a build script that will scrape the source from the [nodejs/node](https://github.com/nodejs/node) repo given a specific Node version.\n\n## Streams Working Group\n\n`string_decoder` is maintained by the Streams Working Group, which\noversees the development and maintenance of the Streams API within\nNode.js. The responsibilities of the Streams Working Group include:\n\n* Addressing stream issues on the Node.js issue tracker.\n* Authoring and editing stream documentation within the Node.js project.\n* Reviewing changes to stream subclasses within the Node.js project.\n* Redirecting changes to streams from the Node.js project to this\n project.\n* Assisting in the implementation of stream providers within Node.js.\n* Recommending versions of `readable-stream` to be included in Node.js.\n* Messaging about the future of streams to give the community advance\n notice of changes.\n\nSee [readable-stream](https://github.com/nodejs/readable-stream) for\nmore details.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/nodejs/string_decoder/issues" + }, + "_id": "string_decoder@1.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "string_decoder@1.1.1", + "name": "string_decoder", + "escapedName": "string_decoder", + "rawSpec": "1.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "1.1.1" + }, + "_spec": "1.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "string_decoder@1.1.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "safe-buffer": "~5.1.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/string_decoder", + "error": "[Circular]", + "extraneous": false + }, + "util-deprecate": { + "name": "util-deprecate", + "version": "1.0.2", + "description": "The Node.js `util.deprecate()` function with browser support", + "main": "node.js", + "browser": "browser.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/util-deprecate.git" + }, + "keywords": [ + "util", + "deprecate", + "browserify", + "browser", + "node" + ], + "author": { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/util-deprecate/issues" + }, + "homepage": "https://github.com/TooTallNate/util-deprecate", + "_resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "_integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "_from": "util-deprecate@1.0.2", + "readme": "util-deprecate\n==============\n### The Node.js `util.deprecate()` function with browser support\n\nIn Node.js, this module simply re-exports the `util.deprecate()` function.\n\nIn the web browser (i.e. via browserify), a browser-specific implementation\nof the `util.deprecate()` function is used.\n\n\n## API\n\nA `deprecate()` function is the only thing exposed by this module.\n\n``` javascript\n// setup:\nexports.foo = deprecate(foo, 'foo() is deprecated, use bar() instead');\n\n\n// users see:\nfoo();\n// foo() is deprecated, use bar() instead\nfoo();\nfoo();\n```\n\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2014 Nathan Rajlich \n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "_id": "util-deprecate@1.0.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "util-deprecate@1.0.2", + "name": "util-deprecate", + "escapedName": "util-deprecate", + "rawSpec": "1.0.2", + "saveSpec": "[Circular]", + "fetchSpec": "1.0.2" + }, + "_spec": "1.0.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "util-deprecate@1.0.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/util-deprecate", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "assert": "^1.4.0", + "babel-polyfill": "^6.9.1", + "buffer": "^4.9.0", + "lolex": "^2.3.2", + "nyc": "^6.4.0", + "tap": "^0.7.0", + "tape": "^4.8.0" + }, + "scripts": { + "test": "tap test/parallel/*.js test/ours/*.js && node test/verify-dependencies.js", + "ci": "tap test/parallel/*.js test/ours/*.js --tap | tee test.tap && node test/verify-dependencies.js", + "cover": "nyc npm test", + "report": "nyc report --reporter=lcov" + }, + "repository": { + "type": "git", + "url": "git://github.com/nodejs/readable-stream.git" + }, + "keywords": [ + "readable", + "stream", + "pipe" + ], + "browser": { + "util": false, + "./readable.js": "./readable-browser.js", + "./writable.js": "./writable-browser.js", + "./duplex.js": "./duplex-browser.js", + "./lib/internal/streams/stream.js": "./lib/internal/streams/stream-browser.js" + }, + "nyc": { + "include": [ + "lib/**.js" + ] + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "_integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "_from": "readable-stream@2.3.6", + "readme": "# readable-stream\n\n***Node-core v8.11.1 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream)\n\n\n[![NPM](https://nodei.co/npm/readable-stream.png?downloads=true&downloadRank=true)](https://nodei.co/npm/readable-stream/)\n[![NPM](https://nodei.co/npm-dl/readable-stream.png?&months=6&height=3)](https://nodei.co/npm/readable-stream/)\n\n\n[![Sauce Test Status](https://saucelabs.com/browser-matrix/readable-stream.svg)](https://saucelabs.com/u/readable-stream)\n\n```bash\nnpm install --save readable-stream\n```\n\n***Node-core streams for userland***\n\nThis package is a mirror of the Streams2 and Streams3 implementations in\nNode-core.\n\nFull documentation may be found on the [Node.js website](https://nodejs.org/dist/v8.11.1/docs/api/stream.html).\n\nIf you want to guarantee a stable streams base, regardless of what version of\nNode you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *\"stream\"* module in Node-core, for background see [this blogpost](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html).\n\nAs of version 2.0.0 **readable-stream** uses semantic versioning.\n\n# Streams Working Group\n\n`readable-stream` is maintained by the Streams Working Group, which\noversees the development and maintenance of the Streams API within\nNode.js. The responsibilities of the Streams Working Group include:\n\n* Addressing stream issues on the Node.js issue tracker.\n* Authoring and editing stream documentation within the Node.js project.\n* Reviewing changes to stream subclasses within the Node.js project.\n* Redirecting changes to streams from the Node.js project to this\n project.\n* Assisting in the implementation of stream providers within Node.js.\n* Recommending versions of `readable-stream` to be included in Node.js.\n* Messaging about the future of streams to give the community advance\n notice of changes.\n\n\n## Team Members\n\n* **Chris Dickinson** ([@chrisdickinson](https://github.com/chrisdickinson)) <christopher.s.dickinson@gmail.com>\n - Release GPG key: 9554F04D7259F04124DE6B476D5A82AC7E37093B\n* **Calvin Metcalf** ([@calvinmetcalf](https://github.com/calvinmetcalf)) <calvin.metcalf@gmail.com>\n - Release GPG key: F3EF5F62A87FC27A22E643F714CE4FF5015AA242\n* **Rod Vagg** ([@rvagg](https://github.com/rvagg)) <rod@vagg.org>\n - Release GPG key: DD8F2338BAE7501E3DD5AC78C273792F7D83545D\n* **Sam Newman** ([@sonewman](https://github.com/sonewman)) <newmansam@outlook.com>\n* **Mathias Buus** ([@mafintosh](https://github.com/mafintosh)) <mathiasbuus@gmail.com>\n* **Domenic Denicola** ([@domenic](https://github.com/domenic)) <d@domenic.me>\n* **Matteo Collina** ([@mcollina](https://github.com/mcollina)) <matteo.collina@gmail.com>\n - Release GPG key: 3ABC01543F22DD2239285CDD818674489FBC127E\n* **Irina Shestak** ([@lrlna](https://github.com/lrlna)) <shestak.irina@gmail.com>\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/nodejs/readable-stream/issues" + }, + "homepage": "https://github.com/nodejs/readable-stream#readme", + "_id": "readable-stream@2.3.6", + "_requested": { + "type": "version", + "registry": true, + "raw": "readable-stream@2.3.6", + "name": "readable-stream", + "escapedName": "readable-stream", + "rawSpec": "2.3.6", + "saveSpec": "[Circular]", + "fetchSpec": "2.3.6" + }, + "_spec": "2.3.6", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "readable-stream@2.3.6", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/readable-stream", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "Base64": "^1.0.1", + "basic-auth-connect": "^1.0.0", + "body-parser": "^1.18.2", + "browserify": "^14.1.0", + "cookie-parser": "^1.4.3", + "express": "^4.16.0", + "express-session": "^1.15.6", + "marked": "^0.3.6", + "mocha": "^3.5.3", + "multer": "^1.3.0", + "should": "^11.2.0", + "should-http": "^0.1.1", + "zuul": "^3.11.1" + }, + "browser": { + "./lib/node/index.js": "./lib/client.js", + "./test/support/server.js": "./test/support/blank.js" + }, + "component": { + "scripts": { + "superagent": "lib/client.js" + } + }, + "main": "./lib/node/index.js", + "engines": { + "node": ">= 4.0" + }, + "_resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", + "_integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "_from": "superagent@3.8.2", + "readme": "# SuperAgent [![Build Status](https://travis-ci.org/visionmedia/superagent.svg?branch=master)](https://travis-ci.org/visionmedia/superagent)\n\n[![Sauce Test Status](https://saucelabs.com/browser-matrix/shtylman-superagent.svg)](https://saucelabs.com/u/shtylman-superagent)\n\nSuperAgent is a small progressive __client-side__ HTTP request library, and __Node.js__ module with the same API, sporting many high-level HTTP client features. View the [docs](http://visionmedia.github.io/superagent/).\n\n![super agent](http://f.cl.ly/items/3d282n3A0h0Z0K2w0q2a/Screenshot.png)\n\n## Installation\n\nnode:\n\n```\n$ npm install superagent\n```\n\nWorks with [browserify](https://github.com/substack/node-browserify) and [webpack](https://github.com/visionmedia/superagent/wiki/SuperAgent-for-Webpack).\n\n```js\nrequest\n .post('/api/pet')\n .send({ name: 'Manny', species: 'cat' }) // sends a JSON post body\n .set('X-API-Key', 'foobar')\n .set('accept', 'json')\n .end((err, res) => {\n // Calling the end function will send the request\n });\n```\n\n## Supported browsers and Node versions\n\nTested browsers:\n\n- Latest Firefox, Chrome, Safari\n- Latest Android, iPhone\n- IE10 through latest. IE9 with polyfills. Even though IE9 is supported, a polyfill for `window.FormData` is required for `.field()`.\n\nNode 4 or later is required.\n\n## Plugins\n\nSuperAgent is easily extended via plugins.\n\n```js\nconst nocache = require('superagent-no-cache');\nconst request = require('superagent');\nconst prefix = require('superagent-prefix')('/static');\n\nrequest\n .get('/some-url')\n .query({ action: 'edit', city: 'London' }) // query string\n .use(prefix) // Prefixes *only* this request\n .use(nocache) // Prevents caching of *only* this request\n .end((err, res) => {\n // Do something\n });\n```\n\nExisting plugins:\n * [superagent-no-cache](https://github.com/johntron/superagent-no-cache) - prevents caching by including Cache-Control header\n * [superagent-prefix](https://github.com/johntron/superagent-prefix) - prefixes absolute URLs (useful in test environment)\n * [superagent-suffix](https://github.com/timneutkens1/superagent-suffix) - suffix URLs with a given path\n * [superagent-mock](https://github.com/M6Web/superagent-mock) - simulate HTTP calls by returning data fixtures based on the requested URL\n * [superagent-mocker](https://github.com/shuvalov-anton/superagent-mocker) — simulate REST API\n * [superagent-cache](https://github.com/jpodwys/superagent-cache) - A global SuperAgent patch with built-in, flexible caching\n * [superagent-cache-plugin](https://github.com/jpodwys/superagent-cache-plugin) - A SuperAgent plugin with built-in, flexible caching\n * [superagent-jsonapify](https://github.com/alex94puchades/superagent-jsonapify) - A lightweight [json-api](http://jsonapi.org/format/) client addon for superagent\n * [superagent-serializer](https://github.com/zzarcon/superagent-serializer) - Converts server payload into different cases\n * [superagent-use](https://github.com/koenpunt/superagent-use) - A client addon to apply plugins to all requests.\n * [superagent-httpbackend](https://www.npmjs.com/package/superagent-httpbackend) - stub out requests using AngularJS' $httpBackend syntax\n * [superagent-throttle](https://github.com/leviwheatcroft/superagent-throttle) - queues and intelligently throttles requests\n * [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's SuperAgent\n\nPlease prefix your plugin with `superagent-*` so that it can easily be found by others.\n\nFor SuperAgent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki).\n\n## Upgrading from previous versions:\n\nOur breaking changes are mostly in rarely used functionality and from stricter error handling.\n\n* [2.x to 3.x](https://github.com/visionmedia/superagent/releases/tag/v3.0.0):\n - Ensure you're running Node 4 or later. We dropped support for Node 0.x.\n - Test code that calls `.send()` multiple times. Invalid calls to `.send()` will now throw instead of sending garbage.\n* [1.x to 2.x](https://github.com/visionmedia/superagent/releases/tag/v2.0.0):\n - If you use `.parse()` in the *browser* version, rename it to `.serialize()`.\n - If you rely on `undefined` in query-string values being sent literally as the text \"undefined\", switch to checking for missing value instead. `?key=undefined` is now `?key` (without a value).\n - If you use `.then()` in Internet Explorer, ensure that you have a polyfill that adds a global `Promise` object.\n* 0.x to 1.x:\n - Use `.end(function(err, res){})`. 1-argument version is no longer supported.\n\n## Running node tests\n\nInstall dependencies:\n\n```shell\n$ npm install\n```\nRun em!\n\n```shell\n$ make test\n```\n\n## Running browser tests\n\nInstall dependencies:\n\n```shell\n$ npm install\n```\n\nStart the test runner:\n\n```shell\n$ make test-browser-local\n```\n\nVisit `http://localhost:4000/__zuul` in your browser.\n\nEdit tests and refresh your browser. You do not have to restart the test runner.\n\n\n## Packaging Notes for Developers\n\n**npm (for node)** is configured via the `package.json` file and the `.npmignore` file. Key metadata in the `package.json` file is the `version` field which should be changed according to semantic versioning and have a 1-1 correspondence with git tags. So for example, if you were to `git show v1.5.0:package.json | grep version`, you should see `\"version\": \"1.5.0\",` and this should hold true for every release. This can be handled via the `npm version` command. Be aware that when publishing, npm will presume the version being published should also be tagged in npm as `latest`, which is OK for normal incremental releases. For betas and minor/patch releases to older versions, be sure to include `--tag` appropriately to avoid an older release getting tagged as `latest`.\n\n**npm (for browser standalone)** When we publish versions to npm, we run `make superagent.js` which generates the standalone `superagent.js` file via `browserify`, and this file is included in the package published to npm (but this file is never checked into the git repository). If users want to install via npm but serve a single `.js` file directly to the browser, the `node_modules/superagent/superagent.js` is a standalone browserified file ready to go for that purpose. It is not minified.\n\n**npm (for browserify)** is handled via the `package.json` `browser` field which allows users to install SuperAgent via npm, reference it from their browser code with `require('superagent')`, and then build their own application bundle via `browserify`, which will use `lib/client.js` as the SuperAgent entrypoint.\n\n**bower** is configured via the `bower.json` file. Bower installs files directly from git/github without any transformation, so you *must* use Browserify or Webpack (or use npm).\n\n## License\n\nMIT\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/superagent/issues" + }, + "homepage": "https://github.com/visionmedia/superagent#readme", + "_id": "superagent@3.8.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "superagent@3.8.2", + "name": "superagent", + "escapedName": "superagent", + "rawSpec": "3.8.2", + "saveSpec": "[Circular]", + "fetchSpec": "3.8.2" + }, + "_spec": "3.8.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "superagent@3.8.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/superagent", + "error": "[Circular]", + "extraneous": false + } + }, + "description": "JavaScript client library for the Alfresco REST API", + "devDependencies": { + "babel-loader": "^7.0.0", + "babel-plugin-transform-proto-to-assign": "^6.9.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-es2015-loose": "^8.0.0", + "babel-preset-es2015-rollup": "^3.0.0", + "babelify": "^7.3.0", + "chai": "^3.5.0", + "chai-datetime": "^1.4.1", + "expect.js": "~0.3.1", + "grunt": "~0.4.0", + "grunt-cli": "^1.1.0", + "grunt-contrib-jshint": "^1.0.0", + "grunt-coveralls": "^1.0.0", + "grunt-istanbul": "^0.7.0", + "grunt-jscs": "^2.8.0", + "grunt-mocha-istanbul": "^3.0.1", + "grunt-mocha-test": "0.13.3", + "grunt-open": "^0.2.3", + "load-grunt-tasks": "^3.4.1", + "markdown-toc": "^0.12.14", + "mocha": "5.2.0", + "mocha-lcov-reporter": "^1.2.0", + "nock": "8.1.0", + "remove-comments-loader": "0.1.2", + "rimraf": "^2.5.2", + "sinon": "^1.17.3", + "sinon-chai": "^2.8.0", + "tsd-jsdoc": "^2.0.0-beta.3", + "tslint": "5.7.0", + "typescript": "^2.4.0", + "uglifyjs-webpack-plugin": "^1.2.3", + "watchify": "^3.7.0", + "webpack": "4.1.1", + "webpack-cli": "2.0.12" + }, + "homepage": "https://github.com/Alfresco/alfresco-js-api#readme", + "keywords": [ + "alfresco" + ], + "license": "Apache-2.0", + "main": "dist/alfresco-js-api.js", + "name": "alfresco-js-api", + "repository": { + "type": "git", + "url": "git+https://github.com/Alfresco/alfresco-js-api.git" + }, + "scripts": { + "build": "npm run clean-build && grunt && npm run tslint && npm run test && npm run webpack && npm run toc", + "clean": "rimraf node_modules && npm run clean-build", + "clean-build": "rimraf dist", + "coverage": "grunt coverage", + "generate": "mvn clean generate-sources", + "generate-api": "mvn install", + "generate-ts": "jsdoc -c config.json .", + "prepublish": "npm run webpack", + "test": "grunt test", + "toc": "markdown-toc -i README.md && markdown-toc -i test/mockObjects/README.md", + "tslint": "tslint --type-check -c tslint.json index.d.ts --project tsconfig.json ", + "watchify": "watchify -s AlfrescoApi main.js -o dist/alfresco-js-api.js", + "webpack": "webpack" + }, + "sideEffects": false, + "typings": "index.d.ts", + "version": "2.6.1", + "readme": "# Alfresco JavaScript API Client\n\n\n

\n \n Gitter chat\n \n \n travis Status\n \n \n Coverage Status\n \n \n license\n \n\n

\n\n

\n alfresco\n

\n\nThis project provides a JavaScript client API into the Alfresco REST API and Activiti REST API.\n\n\n\n\n\n- [Full documentation of all the methods of each API](#full-documentation-of-all-the-methods-of-each-api)\n- [Prerequisites](#prerequisites)\n- [Node](#node)\n- [Api Modules complete methods list](#api-modules-complete-methods-list)\n- [Install](#install)\n- [Use](#use)\n + [Import library for node projects](#import-library-for-node-projects)\n + [Import library for browser projects](#import-library-for-browser-projects)\n- [Authentication JS-API](#authentication-js-api)\n * [Login](#login)\n + [Login with Username and Password BPM and ECM](#login-with-username-and-password-bpm-and-ecm)\n - [Example](#example)\n + [Login with Username and Password ECM](#login-with-username-and-password-ecm)\n - [Example](#example-1)\n + [Login with ticket](#login-with-ticket)\n - [Login with ticket ECM](#login-with-ticket-ecm)\n - [Example](#example-2)\n - [Login with ticket ECM/BPM as parameter in the constructor](#login-with-ticket-ecmbpm-as-parameter-in-the-constructor)\n * [Example](#example-3)\n + [Login with Username and Password BPM](#login-with-username-and-password-bpm)\n + [Example](#example-4)\n + [Login with OAUTH2 Alfresco authorization server](#login-with-oauth2-alfresco-authorization-server)\n * [Implicit Flow](#implicit-flow)\n - [oauth2 properties](#oauth2-properties)\n - [Events](#events)\n + [Example](#example-5)\n + [Example skip login form](#example-skip-login-form)\n * [Password Flow](#password-flow)\n + [Example](#example-6)\n + [Example](#example-7)\n * [Logout](#logout)\n + [Example](#example-8)\n * [isLoggedIn](#isloggedin)\n + [Example](#example-9)\n * [Get tickets](#get-tickets)\n * [Events login/logout](#events-loginlogout)\n + [Example](#example-10)\n- [Custom Endpoint](#custom-endpoint)\n * [Example](#example-11)\n- [ECM](#ecm)\n * [Get Node content](#get-node--content)\n + [Example](#example-12)\n * [Get File or Folder Info](#get-file-or-folder-info)\n + [Example](#example-13)\n * [Get Folder Children Info](#get-folder-children-info)\n + [Example](#example-14)\n * [Create Folder](#create-folder)\n + [Example](#example-15)\n + [Example](#example-16)\n * [Upload File](#upload-file)\n + [Example](#example-17)\n * [Events Upload File](#events-upload-file)\n + [Example](#example-18)\n * [Delete File or Folder](#delete-file-or-folder)\n + [Example](#example-19)\n * [Delete File or Folder Permanent](#delete-file-or-folder-permanent)\n + [Example](#example-20)\n * [Get thumbnail Url](#get-thumbnail-url)\n + [Example](#example-21)\n * [Get preview Url](#get-preview-url)\n + [Example](#example-22)\n * [Get content Url](#get-content-url)\n + [Example](#example-23)\n * [Custom web scripts call](#custom-web-scripts-call)\n + [Parameters](#parameters)\n- [BPM](#bpm)\n * [Task Api](#task-api)\n + [List Task](#list-task)\n - [Parameters](#parameters-1)\n - [Example](#example-24)\n + [Get Task](#get-task)\n - [Parameters](#parameters-2)\n - [Example](#example-25)\n + [Filter Task](#filter-task)\n - [Parameters](#parameters-3)\n - [Example](#example-26)\n + [Complete Task](#complete-task)\n - [Parameters](#parameters-4)\n - [Example](#example-27)\n + [Get Task Form](#get-task-form)\n - [Parameters](#parameters-5)\n - [Example](#example-28)\n + [Complete Task Form](#complete-task-form)\n - [Parameters](#parameters-6)\n - [Example](#example-29)\n * [Process Api](#process-api)\n + [Get Process Instances](#get-process-instances)\n - [Parameters](#parameters-7)\n - [Example](#example-30)\n * [Models Api](#models-api)\n + [Get Model](#get-model)\n - [Parameters](#parameters-8)\n - [Example](#example-31)\n * [Report Api](#report-api)\n + [Create default Reports](#create-default-reports)\n - [Parameters](#parameters-9)\n - [Example](#example-32)\n + [Get Reports](#get-reports)\n - [Parameters](#parameters-10)\n - [Example](#example-33)\n + [Report Params](#report-params)\n - [Parameters](#parameters-11)\n - [Example](#example-34)\n * [Report Process Definitions](#report-process-definitions)\n - [Parameters](#parameters-12)\n - [Example](#example-35)\n * [Tasks of process definition](#tasks-of-process-definition)\n - [Parameters](#parameters-13)\n - [Example](#example-36)\n * [Generate reports](#generate-reports)\n - [Parameters](#parameters-14)\n - [Example](#example-37)\n * [Update report details](#update-report-details)\n - [Parameters](#parameters-15)\n - [Example](#example-38)\n * [Export to csv](#export-to-csv)\n - [Parameters](#parameters-16)\n - [Example](#example-39)\n * [Save Report](#save-report)\n - [Parameters](#parameters-17)\n - [Example](#example-40)\n * [Delete report](#delete-report)\n - [Parameters](#parameters-18)\n - [Example](#example-41)\n- [Error Events](#error-events)\n * [Example](#example-42)\n- [Development](#development)\n- [Release History](#release-history)\n\n\n\n\n\n# Full documentation of all the methods of each API\n\n- [Authentication Services](/src/alfresco-auth-rest-api)\n- [Content Services](/src/alfresco-core-rest-api)\n- [Process Services](/src/alfresco-activiti-rest-api)\n- [ContentCMN](/src/alfresco-private-rest-api)\n- [Content Search](/src/alfresco-search-rest-api)\n\n# Prerequisites\n\nTo correctly use the alfresco js api the minimal supported version are:\n\n- 5.2.a-EA Alfresco Platform Repository (version [5.2.a-EA](https://wiki.alfresco.com/wiki/Community_file_list_201606-EA) or newer)\n- 1.5 Activiti\n\n# Node\nTo correctly use the alfresco-js-api in node check that on your machine is running Node version 5.0.0 or higher.\n\n# Api Modules complete methods list\n\n- [Authentication API](/src/alfresco-auth-rest-api)\n- [Core API](/src/alfresco-core-rest-api)\n- [Governance core API](/src/alfresco-gs-core-rest-api)\n- [Governance classification API](/src/alfresco-gs-classification-rest-api)\n- [Discovery API](/src/alfresco-discovery-rest-api)\n- [Search API](/src/alfresco-search-rest-api)\n- [Activiti API](/src/alfresco-activiti-rest-api)\n- [Mock API](/test/mockObjects)\n\n# Install\n\n\nInstaller for browser versions:\n\n```sh\nnpm install --save alfresco-js-api\n```\n\nInstaller for node versions:\n\n```sh\nnpm install --save alfresco-js-api-node\n```\n\n\n# Use\n\n### Import library for node projects\n\n```javascript\nvar AlfrescoApi = require('alfresco-js-api-node');\n```\n\n### Import library for browser projects\n\n```html\n \n\n or for not minify version\n\n \n```\n\n\n# Authentication JS-API\n\n## Login\n\nAlfrescoApi({alfrescoHost, activitiHost, contextRoot, ticket});\n\nProperty | Description | default value|\n------------- | ------------- | -------------|\nhostEcm| (Optional value The Ip or Name of the host where your Alfresco instance is running )|http://127.0.0.1:8080 |\nhostBpm| (Optional value The Ip or Name of the host where your Activiti instance is running )|http://127.0.0.1:9999 |\nauthType| (Optional value can be 'BASIC' or 'OAUTH') | 'BASIC'|\noauth2| (Optional configuration for SSO) ||\ncontextRoot| (Optional value that define the context Root of the Alfresco ECM API default value is alfresco )|alfresco |\ncontextRootBpm| (Optional value that define the context Root of the Activiti API default value is activiti-app )|alfresco |\nprovider| (Optional value default value is ECM. This parameter can accept as value ECM BPM or ALL to use the API and Login in the ECM, Activiti BPM or Both )|alfresco |\nticket| (Optional only if you want login with the ticket see example below)| |\ndisableCsrf| To disable CSRF Token to be submitted. Only for Activiti call.| false |\n\n### Login with Username and Password BPM and ECM\n\n#### Example\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi({ provider:'ALL' });\n\nthis.alfrescoJsApi.login('admin', 'admin').then(function (data) {\n console.log('API called successfully Login in BPM and ECM performed ');\n}, function (error) {\n console.error(error);\n});\n```\n\n\n### Login with Username and Password ECM\n\n#### Example\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi();\n\nthis.alfrescoJsApi.login('admin', 'admin').then(function (data) {\n console.log('API called successfully Login ticket:' + data);\n}, function (error) {\n console.error(error);\n});\n\n//The output will be: API called successfully Login ticket: TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1\n\n```\n\n### Login with ticket\n\nIf you already know thw ticket when you invoke the constructor you can pass it as parameter in the constructor otherwise you can call the login with ticket that will validate the ticket against the server\n\n\n#### Login with ticket ECM\n\nThis authentication validate also the ticket against the server\n\n#### Example\n```javascript\nvar ticket = 'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1';\n\nthis.alfrescoJsApi.loginTicket(ticket).then(function (data) {\n console.log('valid ticket you are logged in');\n }, function (error) {\n console.error(error);\n });\n```\n\n#### Login with ticket ECM/BPM as parameter in the constructor\n\nWith this authentication the ticket is not validated against the server\n\n##### Example\n```javascript\n\n//Login ticket ECM\nthis.alfrescoJsApi = new AlfrescoApi({ ticketEcm:'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1', hostEcm:'http://127.0.0.1:8080'});\n\n//Login ticket BPM\nthis.alfrescoJsApi = new AlfrescoApi({ ticketBpm: 'Basic YWRtaW46YWRtaW4=', hostBpm:'http://127.0.0.1:9999'});\n\n//Login ticket ECM and BPM\nthis.alfrescoJsApi = new AlfrescoApi({ ticketEcm:'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1', ticketBpm: 'Basic YWRtaW46YWRtaW4=', hostEcm:'http://127.0.0.1:8080', hostBpm:'http://127.0.0.1:9999'});\n```\n\n### Login with Username and Password BPM\n\n### Example\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi({ provider:'BPM' });\n\nthis.alfrescoJsApi.login('admin', 'admin').then(function (data) {\n console.log('API called successfully Login in Activiti BPM performed ');\n}, function (error) {\n console.error(error);\n});\n\n```\n### Login with OAUTH2 Alfresco authorization server\n\n## Implicit Flow\n\nIf your want to be redirect to the authorization server and login there you can use the implicit flow to login\n\n#### oauth2 properties\n\nProperty | Description | default value|\n------------- | ------------- | -------------|\nhost| Your oauth2 server URL| null |\nclientId| Your clientId oauth2 | null |\nsecret| Your secret oauth2| null |\nscope| Your scope | null |\nimplicitFlow| true/false | false |\nredirectUri| url to be redirect after login| null|\nredirectLogout| url to be redirect after logout optional, if is nor present the redirectUri will be used| null|\nrefreshTokenTimeout| millisecond value, after how many millisecond youw ant refresh the token| 40000|\nredirectSilentIframeUri| url to be redirect after silent refresh login| /assets/silent-refresh.html |\nsilentLogin| direct execute the implicit login without the need od call this.alfrescoJsApi.implicitLogin() method| false|\n\n\nThe alfresco-js-api will automatically redirect you to the login page anf refresh the token if necessary\n\n#### Events\n\nProperty | Description | default value|\n------------- | ------------- | -------------|\nimplicit_redirect| triggered when the user is redirect to the auth server return url parameter of the redirect | |\ndiscovery| triggered when all the openId discovery url phase is terminated returnl an object with all the discovered url | |\ntoken_issued| triggered when a new token is issued| |\n\nThe alfresco-js-api will automatically redirect you to the login page anf refresh the token if necessary\n\n\n### Example\n\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi({\n oauth2: {\n host: 'HOST_OAUTH2_SERVER',\n clientId: 'YOUR_CLIENT_ID',\n secret: 'SECRET',\n scope: 'openid',\n implicitFlow: true,\n redirectUri: 'YOUR_HOME_APP_URL',\n silentRefreshTimeout: '600000' //Optional parameter 10 minutes default value\n },\n authType: 'OAUTH',\n provider: 'ALL'\n});\n\nthis.alfrescoJsApi.implicitLogin();\n\n```\n\n### Example skip login form\n\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi({\n oauth2: {\n host: 'HOST_OAUTH2_SERVER',\n clientId: 'YOUR_CLIENT_ID',\n secret: 'SECRET',\n scope: 'openid',\n implicitFlow: true,\n redirectUri: 'YOUR_HOME_APP_URL',\n silentRefreshTimeout: '600000' //Optional parameter 10 minutes default value,\n silentLogin: true\n },\n authType: 'OAUTH',\n provider: 'ALL'\n});\n\n```\n\n\n## Password Flow\n\nIf your auth endpoint is different from the standard one \"/oauth/token\" you can override it through the property authPath\n\n### Example\n```javascript\nthis.alfrescoJsApi = new AlfrescoApi({\n oauth2: {\n host: 'HOST_OAUTH2_SERVER',\n clientId: 'YOUR_CLIENT_ID',\n secret: 'SECRET',\n authPath:'my-custom-auth-endpoint/token'\n },\n authType: 'OAUTH',\n provider: 'ALL'\n });\n\nthis.alfrescoJsApi.login('admin', 'admin').then(function (data) {\n console.log('API called successfully Login in with authorization server performed ');\n}, function (error) {\n console.error(error);\n});\n\n```\n\nAfter the login if you want refresh your token you can use this call\n\n### Example\n\n```javascript\nthis.alfrescoJsApi.refreshToken()then(function (data) {\n console.log('Your token has been refreshed');\n }, function (error) {\n console.error(error);\n });\n```\n\n## Logout\n\nlogout()\n\n### Example\n\n```javascript\n\nthis.alfrescoJsApi.logout().then(function (data) {\n console.log('Successfully Logout');\n}, function (error) {\n console.error('Possible ticket already expired');\n});\n\n```\n\n## isLoggedIn\n\nisLoggedIn()\n\n> return true if you are logged in false if you are not.\n\n### Example\n\n```javascript\n\nvar isLoggedIn = this.alfrescoJsApi.isLoggedIn();\n\nif (isLoggedIn) {\n console.log('You are logged in');\n} else {\n console.log('You are not logged in');\n}\n\n```\n## Get tickets\n\ngetTicketEcm()\n\n>After the log in you can retrieve you ECM ticket\n\n```javascript\n var ecmTicket = this.alfrescoJsApi.getTicketEcm() ;\n console.log('This is your ECM ticket ' + ecmTicket);\n\n```\n\ngetTicketBpm()\n\n>After the log in you can retrieve you BPM ticket\n\n```javascript\n\n var bpmTicket = this.alfrescoJsApi.getTicketBpm();\n console.log('This is your BPM ticket ' + bpmTicket);\n```\n\n## Events login/logout\n\n> The login/logout are also an EventEmitter which you can register to listen to any of the following event types:\n* unauthorized (If this event is triggered a call to the Api was unauthorized)\n* success (If this event is triggered the login was success you can use this event instead the login promise)\n* logout (If this event is triggered the client is successfully logout)\n\n### Example\n\n```javascript\n\nthis.alfrescoJsApi.login('admin', 'admin').on('unauthorized', function(){\n console.log('You are unauthorized you can use this event to redirect to login');\n});\n\nthis.alfrescoJsApi.login('admin', 'admin').on('success', function(){\n console.log('Success Login');\n});\n\nthis.alfrescoJsApi.logout().on('logout', function(){\n console.log('Successfully Logout');\n});\n```\n\n# Custom Endpoint\n\nContent service and process service has two different clients:\n\n- this.alfrescoJsApi.bpmClient\n- this.alfrescoJsApi.ecmClient\n\nBoth client expose a method ***callApi**\n\n\n```javascript\n callApi(path: string, httpMethod: string, pathParams?: any, queryParams?: any, headerParams?: any, formParams?: any, bodyParam?: any, authNames?: string[], contentTypes?: string[], accepts?: string[], returnType?: any, contextRoot?: string, responseType?: string): Promise;\n```\n\nIf you want call your custom rest point in one of those two service use the corrispondin client. \n\n## Example\n\n```javascript\n\n this.alfrescoJsApi.bpmClient.callApi(\n '/api/enterprise/app-version', 'GET',\n {}, {}, {}, {}, {},\n [], ['application/json'], ['application/json'], {'String': 'String'}\n )\n\n ```\n \n\n# ECM\n\nA complete list of all the ECM methods is available here : [Core API](/src/alfresco-core-rest-api).\nBelow you can find some common examples.\n\n## Get Node content\n\ngetFileContent(nodeId, opts)\n\n>Returns the file content of the node with identifier **nodeId**.\n\n ### Example\n```javascript\n\nvar nodeId = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.core.nodesApi.getFileContent(nodeId).then(function(data) {\n fs.writeFile('./test/grass.jpg', data, function(error) {\n if (error) {\n console.error(error);\n return;\n }\n console.log('The file was saved!');\n });\n}, function(error) {\n console.error(error);\n});\n```\n\n## Get File or Folder Info\n\ngetNodeInfo(fileOrFolderId, opts)\n\n>Get information for the File/Folder with the identifier nodeId. The identifier of a node. You can also use one of these well-known aliases: -my- , -shared- or -root- as NodeId\n\n### Example\n\n```javascript\n\nvar fileOrFolderId = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.nodes.getNodeInfo(fileOrFolderId).then(function (data) {\n console.log('This is the name' + data.name );\n}, function (error) {\n console.log('This node does not exist');\n});\n\n```\n## Get Folder Children Info\n\ngetNodeChildren(fileOrFolderId, opts)\n\n>Minimal information for each child is returned by default.\nYou can use the include parameter to return additional information.\nreturns a promise with the Info about the children of the node if resolved and {error} if rejected.\nYou can also use one of these well-known aliases: -my- , -shared- or -root- as NodeId\n\n### Example\n\n```javascript\n\nvar folderNodeId = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.nodes.getNodeChildren(folderNodeId).then(function (data) {\n console.log('The number of children in this folder are ' + data.list.pagination.count );\n}, function (error) {\n console.log('This node does not exist');\n});\n\n```\n## Create Folder\n\ncreateFolder(name, relativePath, nodeIdParentFolder, opts)\n\n>createFolder return a promise that is resolved if the folder is created and {error} if rejected.\nYou can also use one of these well-known aliases: -my- , -shared- or -root- as nodeIdParentFolder\n\n### Example\n\n```javascript\n\nthis.alfrescoJsApi.nodes.createFolder('newFolderName').then(function (data) {\n console.log('The folder is created in root');\n}, function (error) {\n console.log('Error in creation of this folder or folder already exist' + error);\n});\n\n\nthis.alfrescoJsApi.nodes.createFolder('newFolderName', 'folderA/folderB').then(function (data) {\n console.log('The folder is created in folderA/folderB from root');\n}, function (error) {\n console.log('Error in creation of this folder or folder already exist' + error);\n});\n\n\nvar parentFolder = '80a94ac8-3ece-47ad-864e-5d939424c47c'\n\nthis.alfrescoJsApi.nodes.createFolder('newFolderName', 'folderA/folderB', parentFolder).then(function (data) {\n console.log('The folder is created in folderA/folderB from parentFolder:' + parentFolder);\n}, function (error) {\n console.log('Error in creation of this folder or folder already exist' + error);\n});\n\n```\n\n**CreateFolder With Auto Rename**\n\ncreateFolderAutoRename(name, relativePath, nodeIdParentFolder, opts)\n>is the same of createFolder(name, relativePath, nodeId, {autoRename: true}) is just syntactic sugar\n You can also use one of these well-known aliases: -my- , -shared- or -root- as nodeIdParentFolder\n\n### Example\n\n```javascript\n\nthis.alfrescoJsApi.nodes.createFolderAutoRename('newFolderName').then(function (data) {\n console.log('The folder is created in root');\n}, function (error) {\n console.log('Error in creation of this folder' + error);\n});\n```\n\n## Upload File\n\nuploadFile(fileDefinition, relativePath, nodeId, nodeBody, opts)\n>uploadFile return a promise that is resolved if the file is successful uploaded and {error} if rejected.\n\nThe fileDefinition provides information about files and allows JavaScript to access their content.\n\n*Web File Definition\nFile Definition are generally retrieved from a FileList object returned as a result of a user selecting files using the element\n\n*Node File Definition\nFile Definition are generally retrieved from a read Stram\n\n### Example\n\n```javascript\n\nvar fs = require('fs');\n\nvar fileToUpload = fs.createReadStream('./folderA/folderB/newFile.txt');\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload)\n .then(function () {\n console.log('File Uploaded in the root');\n }, function (error) {\n console.log('Error during the upload' + error);\n });\n\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload, null, null, null, {autoRename: true})\n .then(function () {\n console.log('File Uploaded in the root');\n }, function (error) {\n console.log('Error during the upload' + error);\n });\n\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload, 'folderX/folderY/folderZ')\n .then(function () {\n console.log('File Uploaded in the from root folderX/folderY/folderZ');\n }, function (error) {\n console.log('Error during the upload' + error);\n });\n\n\nvar parentFolder = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload, 'folderX/folderY/folderZ', parentFolder )\n .then(function () {\n console.log('File Uploaded in the from parentFolder ' + parentFolder + ' n folderX/folderY/folderZ');\n }, function (error) {\n console.log('Error during the upload' + error);\n });\n\n```\n\nThe default behaviour of the Upload API will not create any thumbnail.\nIn order to create a thumbnail you have to perform to pass the parameter ```javascript{renditions: 'doclib'}``` as in the example below.\nThis parameter will basically perform also a call to the Rendition API.\nFor more information about the Rendition API :\n* [Rendition API](/src/alfresco-core-rest-api/docs/Rendition.md)\n* [Rendition service Wiki](https://wiki.alfresco.com/wiki/Rendition_Service)\n\n```javascript\n\nvar fs = require('fs');\n\nvar fileToUpload = fs.createReadStream('./folderA/folderB/newFile.txt');\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload, null, null, null, {renditions: 'doclib'})\n .then(function () {\n console.log('File Uploaded in the root');\n }, function (error) {\n console.log('Error during the upload' + error);\n });\n\n```\n\n* To abort a file uploading\n\n\n```javascript\n\nvar fs = require('fs');\n\nvar fileToUpload = fs.createReadStream('./folderA/folderB/newFile.txt');\n\nvar promiseUpload = this.alfrescoJsApi.upload.uploadFile(fileToUpload)\n .once('abort', function () {\n console.log('File Uploaded aborted');\n });\n\npromiseUpload.abort();\n```\n\n\n## Events Upload File\n\n> The uploadFile is also an EventEmitter which you can register to listen to any of the following event types:\n* progress\n* success\n* abort\n* error\n* unauthorized\n\n### Example\n\n```javascript\nvar fs = require('fs');\n\nvar fileToUpload = fs.createReadStream('./folderA/folderB/newFile.txt');\n\nthis.alfrescoJsApi.upload.uploadFile(fileToUpload)\n .on('progress', (progress) => {\n console.log( 'Total :' + progress.total );\n console.log( 'Loaded :' + progress.loaded );\n console.log( 'Percent :' + progress.percent );\n })\n .on('success', () => {\n console.log( 'Your File is uploaded');\n });\n .on('abort', () => {\n console.log( 'Upload Aborted');\n })\n .on('error', () => {\n console.log( 'Error during the upload');\n })\n .on('unauthorized', () => {\n console.log('You are unauthorized');\n })\n```\n\n## Delete File or Folder\n\ndeleteNode(fileOrFolderId)\n\n>Delete File/Folder with the identifier nodeId, if the nodeId is a folder, then its children are also deleted\nDeleted nodes move to the trash bin is still possible to recover it\n\n### Example\n\n```javascript\n\nvar fileOrFolderId = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.nodes.deleteNode(fileOrFolderId).then(function (data) {\n console.log('The file/folder is deleted');\n}, function (error) {\n console.log('This node does not exist');\n});\n\n```\n\n## Delete File or Folder Permanent\n\ndeleteNodePermanent(fileOrFolderId)\n\n>Delete File/Folder with the identifier nodeId, if the nodeId is a folder, then its children are also deleted\nIf Deleted Permanent is used will not be possible recover the files\n\n### Example\n\n```javascript\n\nvar fileOrFolderId = '80a94ac8-3ece-47ad-864e-5d939424c47c';\n\nthis.alfrescoJsApi.nodes.deleteNodePermanent(fileOrFolderId).then(function (data) {\n console.log('The file/folder is deleted');\n}, function (error) {\n console.log('This node does not exist');\n});\n\n```\n\n## Get thumbnail Url\n\ngetDocumentThumbnailUrl(documentId)\n\n### Example\n\n```javascript\n\nvar thumbnailUrl = this.alfrescoJsApi.content.getDocumentThumbnailUrl('1a0b110f-1e09-4ca2-b367-fe25e4964a4');\n\n```\n\n## Get preview Url\n\ngetDocumentPreviewUrl(documentId)\n\n### Example\n\n```javascript\n\nvar previewUrl = this.alfrescoJsApi.content.getDocumentPreviewUrl('1a0b110f-1e09-4ca2-b367-fe25e4964a4');\n\n```\n\n## Get content Url\n\ngetContentUrl(documentId)\n\n### Example\n\n```javascript\n\nvar contentUrl = this.alfrescoJsApi.content.getContentUrl('1a0b110f-1e09-4ca2-b367-fe25e4964a4');\n\n```\n\n## Custom web scripts call\n\nFor mor information about web scripts read the [Wiki](https://wiki.alfresco.com/wiki/Web_Scripts) and the [Wiki with Web ScriptsExamples](https://wiki.alfresco.com/wiki/Web_Scripts_Examples)\n\nexecuteWebScript(httpMethod, scriptPath, scriptArgs, contextRoot, servicePath)\n\n> Anatomy of a Web Script URI **http(s)://(host):(port)/(contextPath)/(servicePath)/(scriptPath)?(scriptArgs)**\nA Web Script is simply a service bound to a URI which responds to HTTP methods such as GET, POST, PUT and DELETE. While using the same underlying code, there are broadly two kinds of Web Scripts.\n\n### Parameters\nName | Description\n------------- | -------------\n**httpMethod** | possible value GET, POST, PUT and DELETE\n**scriptPath** |path to Web Script (as defined by Web Script)\n**scriptArgs** |arguments to pass to Web Script\n**contextRoot** |path where application is deployed default value 'alfresco'\n**servicePath** |path where Web Script service is mapped default value 'service'\n**postBody** | post body\n\n```javascript\n\n//Call a GET on a Web Scripts available at the following URIs: http://127.0.01:8080/alfresco/service/mytasks\n\nthis.alfrescoJsApi.core.webscriptApi.executeWebScript('GET', 'mytasks').then(function (data) {\n console.log('Data received form http://127.0.01:8080/alfresco/service/mytasks' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n\n//Call a GET on a Web Scripts available at the following URIs: http://127.0.01:8080/share/service/mytasks\n\nthis.alfrescoJsApi.core.webscriptApi.executeWebScript('GET', 'mytasks', null, 'share').then(function (data) {\n console.log('Data received form http://127.0.01:8080/share/service/mytasks' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n\n//Call a GET on a Web Scripts available at the following URIs: http://127.0.01:8080/share/differentServiceSlug/mytasks\n\nthis.alfrescoJsApi.core.webscriptApi.executeWebScript('GET', 'mytasks', null, 'share', 'differentServiceSlug').then(function (data) {\n console.log('Data received form http://127.0.01:8080/share/differentServiceSlug/mytasks' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n\n```\n\n# BPM\n\nA complete list of all the BPM methods is available her[Activiti API](/src/alfresco-activiti-rest-api).\nBelow you can find some common examples.\n\n## Task Api\n\nBelow you can find some example relative to the Activiti process api for all the possible method go to [Task Api documentation](/src/alfresco-activiti-rest-api/docs/TaskApi.md)\n\n### List Task\n\nlistTasks(requestNode)\n\n>return a list of task based on the requestNode query\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **requestNode** | [**Representation**](/src/alfresco-activiti-rest-api/docs/TaskQueryRequestRepresentation.md)| requestNode\n\n#### Example\n\n```javascript\nvar requestTasks = new this.alfrescoJsApi.activiti.TaskQueryRequestRepresentation();\n\nthis.alfrescoJsApi.activiti.taskApi.listTasks(requestTasks).then(function (data) {\n console.log('listTasks ' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n### Get Task\n\ngetTask(taskId)\n\n>return the [**TaskRepresentation**](/src/alfresco-activiti-rest-api/docs/TaskRepresentation.md) of single task by id\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **taskId** | **String**| taskId\n\n#### Example\n\n```javascript\n\nvar taskId = '10'; // String | taskId\n\nthis.alfrescoJsApi.activiti.taskApi.getTask(taskId).then(function (data) {\n console.log('Task representation ' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n### Filter Task\n\nfilterTasks(requestTasks)\n\n>return the [**ResultListDataRepresentation**](/src/alfresco-activiti-rest-api/docs/ResultListDataRepresentation.md) that is a list of all the task filered\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **requestTasks** | [**TaskFilterRequestRepresentation**](/src/alfresco-activiti-rest-api/docs/TaskFilterRequestRepresentation.md)| requestTasks\n\n\n#### Example\n\n```javascript\n\nvar requestTasks = new this.alfrescoJsApi.activiti.TaskFilterRequestRepresentation();\nrequestTasks.appDefinitionId = 1;\n\nthis.alfrescoJsApi.activiti.taskApi.filterTasks(requestTasks).then(function (data) {\n console.log('Task filter list ' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n### Complete Task\n\ncompleteTask(taskId)\n\n>To complete a task (standalone or without a task form) :\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **taskId** | **String**| taskId\n\n#### Example\n\n```javascript\n\nvar taskId = '10'; // String | taskId\n\nthis.alfrescoJsApi.activiti.taskApi.completeTask(taskId).then(function () {\n console.log('Task completed');\n}, function (error) {\n console.log('Error' + error);\n});\n```\n### Get Task Form\n\ngetTaskForm(taskId)\n\n>Retrieve the Task Form representation. [**FormDefinitionRepresentation**](/src/alfresco-activiti-rest-api/docs/FormDefinitionRepresentation.md)\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **taskId** | **String**| taskId\n\n#### Example\n\n```javascript\n\nvar taskId = '10'; // String | taskId\n\nthis.alfrescoJsApi.activiti.taskApi.getTaskForm(taskId).then(function (data) {\n console.log('Task form representation' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n### Complete Task Form\n\ncompleteTaskForm(taskId, completeTaskFormRepresentation)\n\n>Complete a Task Form\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **taskId** | **String**| taskId\n **completeTaskFormRepresentation** | [**CompleteFormRepresentation**](/src/alfresco-activiti-rest-api/docs/CompleteFormRepresentation.md)| completeTaskFormRepresentation\n\n#### Example\n\n```javascript\n\nvar taskId = '10'; // String | taskId\n\nthis.alfrescoJsApi.activiti.taskApi.completeTaskForm(taskId, completeTaskFormRepresentation).then(function () {\n console.log('Task completed');\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n## Process Api\n\nBelow you can find some example relative to the Activiti process api for all the possible method go to [Process Api documentation](/src/alfresco-activiti-rest-api/docs/ProcessApi.md)\n\n\n### Get Process Instances\n\ngetProcessInstances(requestNode)\n\n>Retrieve a list of process instances [**ResultListDataRepresentation**](/src/alfresco-activiti-rest-api/docs/ResultListDataRepresentation.md)\n\n#### Parameters\n\nName | Type | Description\n------------- | ------------- | -------------\n **requestNode** | [**ProcessFilterRequestRepresentation**](/src/alfresco-activiti-rest-api/docs/ProcessFilterRequestRepresentation.md)| requestNode\n\n#### Example\n\n```javascript\nvar requestNode = new this.alfrescoJsApi.activiti.ProcessFilterRequestRepresentation();\n\nthis.alfrescoJsApi.activiti.processApi.getProcessInstances(requestNode).then(function (data) {\n console.log('All processes' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\nFiltered process:\n\n```javascript\n var requestNode = new this.alfrescoJsApi.activiti.ProcessFilterRequestRepresentation();\n\nrequestNode.page = 0;\nrequestNode.sort = 'created-desc';\nrequestNode.state = 'completed';\n\nthis.alfrescoJsApi.activiti.processApi.getProcessInstances(requestNode).then(function (data) {\n console.log('All processes completed' + data);\n}, function (error) {\n console.log('Error' + error);\n});\n```\n\n## Models Api\n\nBelow you can find some example relative to the Activiti process api for all the possible method go to [Task Api documentation](/src/alfresco-activiti-rest-api/docs/ModelsApi.md)\n\n### Get Model\n\ngetModel(modelId, opts)\n\n>To retrieve details about a particular model (process, form, decision rule or app) return a [**ModelRepresentation**](ModelRepresentation.md)\n\n#### Parameters\n\nName | Type | Description | Notes\n------------- | ------------- | ------------- | -------------\n **modelId** | **Integer**| modelId |\n **includePermissions** | **Boolean**| includePermissions | [optional]\n\n#### Example\n```javascript\n\nvar opts = {\n 'filter': 'myReusableForms',\n 'modelType': 2\n};\n\nthis.alfrescoJsApi.activiti.modelsApi.getModels(opts).then(function (data) {\n console.log('All your reusable forms' + data);\n }, function (error) {\n console.log('Error' + error);\n });\n```\n\n## Report Api\n\nBelow you can find some example relative to the Activiti report api for all the possible method go to [Report Api documentation](/src/alfresco-activiti-rest-api/docs/ReportApi.md)\n\n### Create default Reports\n\ncreateDefaultReports()\n\n>Create the default reports\n\n#### Parameters\n\nNo parameters required.\n\n#### Example\n\n```javascript\n\nthis.alfrescoJsApi.activiti.reportApi.createDefaultReports();\n```\n\n### Get Reports\n\ngetReportList()\n\n> Retrieve the available report list\n\n#### Parameters\n\nNo parameters required.\n\n#### Example\n\n```javascript\n\nthis.alfrescoJsApi.activiti.reportApi.getReportList();\n```\n\n### Report Params\n\ngetReportParams(reportId)\n\n> Retrieve the parameters referring to the reportId.\n\n#### Parameters\n\nName | Type | Description | Notes|\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n\n#### Example\n\n```javascript\n\nvar reportId = \"1\"; // String | reportId\n\nthis.alfrescoJsApi.activiti.reportApi.getReportParams(reportId);\n```\n\n## Report Process Definitions\n\ngetProcessDefinitions()\n\n> Retrieve the process definition list for all the apps.\n\n#### Parameters\n\nNo parameters required.\n\n#### Example\n\n```javascript\n\nthis.alfrescoJsApi.activiti.reportApi.getProcessDefinitions();\n```\n\n## Tasks of process definition\n\ngetTasksByProcessDefinitionId(reportId, processDefinitionId)\n\n> Retrieves all tasks that refer to the processDefinitionId\n\n#### Parameters\nName | Type | Description | Notes|\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n **processDefinitionId** | **String**| process definition id ||\n\n#### Example\n\n```javascript\n\nvar reportId = \"1\"; // String | reportId\nvar processDefinitionId = \"1\"; // String | processDefinitionId\n\nthis.alfrescoJsApi.activiti.reportApi.getTasksByProcessDefinitionId(reportId, processDefinitionId);\n```\n\n## Generate reports\n\ngetReportsByParams(reportId, paramsQuery)\n\n> Generate the reports based on the input parameters\n\n#### Parameters\nName | Type | Description | Notes|\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n **paramsQuery** | **Object**| Query parameters ||\n\n#### Example\n\n```javascript\n\nvar reportId = \"1\"; // String | reportId\nvar paramsQuery = {status: 'ALL'}; // Object | paramsQuery\n\nthis.alfrescoJsApi.activiti.reportApi.getReportsByParams(reportId, paramsQuery);\n```\n## Update report details\n\nupdateReport(reportId, name)\n\n> Update the report details\n\n#### Parameters\nName | Type | Description | Notes\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n **name** | **String**| The report name ||\n\n#### Example\n\n```javascript\n\nvar reportId = \"1\"; // String | reportId\nvar name = \"new Fake name\"; // String | reportId\n\nthis.alfrescoJsApi.activiti.reportApi.updateReport(reportId, name);\n```\n\n## Export to csv\nexportToCsv(reportId, queryParms)\n\n> Export a report as csv\n\n#### Parameters\nName | Type | Description | Notes\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n **queryParms** | **Object**| Query parameters ||\n\n#### Example\n```javascript\n\nvar reportId = \"1\"; // String | reportId\nvar queryParms = {\n 'processDefinitionId': 'TEST:99:999',\n 'dateRange': {\n 'startDate': '2017-01-01T00:00:00.000Z',\n 'endDate': '2017-01-24T23:59:59.999Z',\n 'rangeId': 'currentYear'\n },\n 'slowProcessInstanceInteger': 10,\n 'status': 'All',\n '__reportName': 'FAKE_REPORT_NAME'\n };\n\nthis.alfrescoJsApi.activiti.reportApi.exportToCsv(reportId, queryParms);\n```\n\n## Save Report\n\nsaveReport(reportId, queryParams)\n\n> Save a report\n\n#### Parameters\nName | Type | Description | Notes\n------------- | ------------- | ------------- | -------------|\n **reportId** | **String**| reportId ||\n **queryParms** | **Object**| Query parameters ||\n\n#### Example\n```javascript\n\nvar reportId = \"1\"; // String | reportId\nvar queryParms = {\n 'processDefinitionId': 'TEST:99:999',\n 'dateRange': {\n 'startDate': '2017-01-01T00:00:00.000Z',\n 'endDate': '2017-01-24T23:59:59.999Z',\n 'rangeId': 'currentYear'\n },\n 'slowProcessInstanceInteger': 10,\n 'status': 'All',\n '__reportName': 'FAKE_REPORT_NAME'\n };\n\nthis.alfrescoJsApi.activiti.reportApi.saveReport(reportId, queryParms);\n```\n\n## Delete report\ndeleteReport(reportId)\n\n> Delete a report\n\n#### Parameters\nName | Type | Description | Notes |\n------------- | ------------- | ------------- | ------------- |\n **reportId** | **String**| reportId | |\n\n#### Example\n```javascript\n\nvar reportId = \"1\"; // String | reportId\n\nthis.alfrescoJsApi.activiti.reportApi.deleteReport(reportId);\n```\n\n# Error Events\n\nThe alfresco-js-api has an error handler event where you can subscribe\n\n## Example\n```javascript\n this.alfrescoJsApi.on('error', (error) => {\n console.log(error)\n })\n```\n\n# Development\n\n* To run the build\n\n ```$ npm run build```\n\n* To run the build in watch mode\n\n ```$ npm run watchify```\n\n* To run the test\n\n ```$ npm run test```\n\n* To run the test coverage\n\n ```$ npm run coverage```\n\n\n# Release History\n\nRead the [Changelog] (./CHANGELOG.md)\n\n", + "readmeFilename": "README.md", + "optionalDependencies": {}, + "_dependencies": { + "event-emitter": "0.3.4", + "superagent": "3.8.2" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/alfresco-js-api", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-extensions@3.0.0-383b74151a47e188020249aea7ec0dfb586bd0b6", + "requires": "alfresco-js-api@2.7.0-2a84d662e8134ada8923742adad17351a4a2f777" + } + ] + }, + "peerMissing": true + }, + "core-js": { + "required": { + "name": "core-js", + "description": "Standard library", + "version": "2.5.7", + "repository": { + "type": "git", + "url": "git+https://github.com/zloirock/core-js.git" + }, + "main": "index.js", + "devDependencies": { + "LiveScript": "1.3.x", + "es-observable-tests": "0.2.x", + "eslint": "4.19.x", + "eslint-plugin-import": "2.12.x", + "grunt": "^1.0.2", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.1.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-uglify": "3.3.x", + "grunt-contrib-watch": "^1.0.0", + "grunt-karma": "^2.0.0", + "grunt-livescript": "0.6.x", + "karma": "^2.0.0", + "karma-qunit": "^2.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-firefox-launcher": "^1.0.1", + "karma-ie-launcher": "^1.0.0", + "karma-phantomjs-launcher": "1.0.x", + "phantomjs-prebuilt": "2.1.x", + "promises-aplus-tests": "^2.1.2", + "qunit": "2.6.x", + "temp": "^0.8.3", + "webpack": "^3.11.0" + }, + "scripts": { + "grunt": "grunt", + "lint": "eslint ./", + "promises-tests": "promises-aplus-tests tests/promises-aplus/adapter", + "observables-tests": "node tests/observables/adapter && node tests/observables/adapter-library", + "test": "npm run grunt clean copy && npm run lint && npm run grunt livescript client karma:default && npm run grunt library karma:library && npm run promises-tests && npm run observables-tests && lsc tests/commonjs" + }, + "license": "MIT", + "keywords": [ + "ES3", + "ES5", + "ES6", + "ES7", + "ES2015", + "ES2016", + "ES2017", + "ECMAScript 3", + "ECMAScript 5", + "ECMAScript 6", + "ECMAScript 7", + "ECMAScript 2015", + "ECMAScript 2016", + "ECMAScript 2017", + "Harmony", + "Strawman", + "Map", + "Set", + "WeakMap", + "WeakSet", + "Promise", + "Symbol", + "TypedArray", + "setImmediate", + "Dict", + "polyfill", + "shim" + ], + "_resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "_integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "_from": "core-js@2.5.7", + "readme": "# core-js\n\n[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/zloirock/core-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![version](https://img.shields.io/npm/v/core-js.svg)](https://www.npmjs.com/package/core-js) [![npm downloads](https://img.shields.io/npm/dm/core-js.svg)](http://npm-stat.com/charts.html?package=core-js&author=&from=2014-11-18) [![Build Status](https://travis-ci.org/zloirock/core-js.svg)](https://travis-ci.org/zloirock/core-js) [![devDependency status](https://david-dm.org/zloirock/core-js/dev-status.svg)](https://david-dm.org/zloirock/core-js?type=dev)\n#### As advertising: the author is looking for a good job :)\n\nModular standard library for JavaScript. Includes polyfills for [ECMAScript 5](#ecmascript-5), [ECMAScript 6](#ecmascript-6): [promises](#ecmascript-6-promise), [symbols](#ecmascript-6-symbol), [collections](#ecmascript-6-collections), iterators, [typed arrays](#ecmascript-6-typed-arrays), [ECMAScript 7+ proposals](#ecmascript-7-proposals), [setImmediate](#setimmediate), etc. Some additional features such as [dictionaries](#dict) or [extended partial application](#partial-application). You can require only needed features or use it without global namespace pollution.\n\n[*Example*](http://goo.gl/a2xexl):\n```js\nArray.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\n'*'.repeat(10); // => '**********'\nPromise.resolve(32).then(x => console.log(x)); // => 32\nsetImmediate(x => console.log(x), 42); // => 42\n```\n\n[*Without global namespace pollution*](http://goo.gl/paOHb0):\n```js\nvar core = require('core-js/library'); // With a modular system, otherwise use global `core`\ncore.Array.from(new core.Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\ncore.String.repeat('*', 10); // => '**********'\ncore.Promise.resolve(32).then(x => console.log(x)); // => 32\ncore.setImmediate(x => console.log(x), 42); // => 42\n```\n\n### Index\n- [Usage](#usage)\n - [Basic](#basic)\n - [CommonJS](#commonjs)\n - [Custom build](#custom-build-from-the-command-line)\n- [Supported engines](#supported-engines)\n- [Features](#features)\n - [ECMAScript 5](#ecmascript-5)\n - [ECMAScript 6](#ecmascript-6)\n - [ECMAScript 6: Object](#ecmascript-6-object)\n - [ECMAScript 6: Function](#ecmascript-6-function)\n - [ECMAScript 6: Array](#ecmascript-6-array)\n - [ECMAScript 6: String](#ecmascript-6-string)\n - [ECMAScript 6: RegExp](#ecmascript-6-regexp)\n - [ECMAScript 6: Number](#ecmascript-6-number)\n - [ECMAScript 6: Math](#ecmascript-6-math)\n - [ECMAScript 6: Date](#ecmascript-6-date)\n - [ECMAScript 6: Promise](#ecmascript-6-promise)\n - [ECMAScript 6: Symbol](#ecmascript-6-symbol)\n - [ECMAScript 6: Collections](#ecmascript-6-collections)\n - [ECMAScript 6: Typed Arrays](#ecmascript-6-typed-arrays)\n - [ECMAScript 6: Reflect](#ecmascript-6-reflect)\n - [ECMAScript 7+ proposals](#ecmascript-7-proposals)\n - [stage 4 proposals](#stage-4-proposals)\n - [stage 3 proposals](#stage-3-proposals)\n - [stage 2 proposals](#stage-2-proposals)\n - [stage 1 proposals](#stage-1-proposals)\n - [stage 0 proposals](#stage-0-proposals)\n - [pre-stage 0 proposals](#pre-stage-0-proposals)\n - [Web standards](#web-standards)\n - [setTimeout / setInterval](#settimeout--setinterval)\n - [setImmediate](#setimmediate)\n - [iterable DOM collections](#iterable-dom-collections)\n - [Non-standard](#non-standard)\n - [Object](#object)\n - [Dict](#dict)\n - [partial application](#partial-application)\n - [Number Iterator](#number-iterator)\n - [escaping strings](#escaping-strings)\n - [delay](#delay)\n - [helpers for iterators](#helpers-for-iterators)\n- [Missing polyfills](#missing-polyfills)\n- [Changelog](./CHANGELOG.md)\n\n## Usage\n### Basic\n```\nnpm i core-js\nbower install core.js\n```\n\n```js\n// Default\nrequire('core-js');\n// Without global namespace pollution\nvar core = require('core-js/library');\n// Shim only\nrequire('core-js/shim');\n```\nIf you need complete build for browser, use builds from `core-js/client` path: \n\n* [default](https://raw.githack.com/zloirock/core-js/v2.5.7/client/core.min.js): Includes all features, standard and non-standard.\n* [as a library](https://raw.githack.com/zloirock/core-js/v2.5.7/client/library.min.js): Like \"default\", but does not pollute the global namespace (see [2nd example at the top](#core-js)).\n* [shim only](https://raw.githack.com/zloirock/core-js/v2.5.7/client/shim.min.js): Only includes the standard methods.\n\nWarning: if you use `core-js` with the extension of native objects, require all needed `core-js` modules at the beginning of entry point of your application, otherwise, conflicts may occur.\n\n### CommonJS\nYou can require only needed modules.\n\n```js\nrequire('core-js/fn/set');\nrequire('core-js/fn/array/from');\nrequire('core-js/fn/array/find-index');\nArray.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\n[1, 2, NaN, 3, 4].findIndex(isNaN); // => 2\n\n// or, w/o global namespace pollution:\n\nvar Set = require('core-js/library/fn/set');\nvar from = require('core-js/library/fn/array/from');\nvar findIndex = require('core-js/library/fn/array/find-index');\nfrom(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\nfindIndex([1, 2, NaN, 3, 4], isNaN); // => 2\n```\nAvailable entry points for methods / constructors, as above examples, and namespaces: for example, `core-js/es6/array` (`core-js/library/es6/array`) contains all [ES6 `Array` features](#ecmascript-6-array), `core-js/es6` (`core-js/library/es6`) contains all ES6 features.\n\n##### Caveats when using CommonJS API:\n\n* `modules` path is internal API, does not inject all required dependencies and can be changed in minor or patch releases. Use it only for a custom build and / or if you know what are you doing.\n* `core-js` is extremely modular and uses a lot of very tiny modules, because of that for usage in browsers bundle up `core-js` instead of usage loader for each file, otherwise, you will have hundreds of requests.\n\n#### CommonJS and prototype methods without global namespace pollution\nIn the `library` version, we can't pollute prototypes of native constructors. Because of that, prototype methods transformed to static methods like in examples above. `babel` `runtime` transformer also can't transform them. But with transpilers we can use one more trick - [bind operator and virtual methods](https://github.com/zenparsing/es-function-bind). Special for that, available `/virtual/` entry points. Example:\n```js\nimport fill from 'core-js/library/fn/array/virtual/fill';\nimport findIndex from 'core-js/library/fn/array/virtual/find-index';\n\nArray(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4\n\n// or\n\nimport {fill, findIndex} from 'core-js/library/fn/array/virtual';\n\nArray(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4\n\n```\n\n### Custom build (from the command-line)\n```\nnpm i core-js && cd node_modules/core-js && npm i\nnpm run grunt build:core.dict,es6 -- --blacklist=es6.promise,es6.math --library=on --path=custom uglify\n```\nWhere `core.dict` and `es6` are modules (namespaces) names, which will be added to the build, `es6.promise` and `es6.math` are modules (namespaces) names, which will be excluded from the build, `--library=on` is flag for build without global namespace pollution and `custom` is target file name.\n\nAvailable namespaces: for example, `es6.array` contains [ES6 `Array` features](#ecmascript-6-array), `es6` contains all modules whose names start with `es6`.\n\n### Custom build (from external scripts)\n\n[`core-js-builder`](https://www.npmjs.com/package/core-js-builder) package exports a function that takes the same parameters as the `build` target from the previous section. This will conditionally include or exclude certain parts of `core-js`:\n\n```js\nrequire('core-js-builder')({\n modules: ['es6', 'core.dict'], // modules / namespaces\n blacklist: ['es6.reflect'], // blacklist of modules / namespaces, by default - empty list\n library: false, // flag for build without global namespace pollution, by default - false\n umd: true // use UMD wrapper for export `core` object, by default - true\n}).then(code => {\n // ...\n}).catch(error => {\n // ...\n});\n```\n## Supported engines\n**Tested in:**\n- Chrome 26+\n- Firefox 4+\n- Safari 5+\n- Opera 12+\n- Internet Explorer 6+ (sure, IE8- with ES3 limitations)\n- Edge\n- Android Browser 2.3+\n- iOS Safari 5.1+\n- PhantomJS 1.9 / 2.1\n- NodeJS 0.8+\n\n...and it doesn't mean `core-js` will not work in other engines, they just have not been tested.\n\n## Features:\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library) <- all features\ncore-js(/library)/shim <- only polyfills\n```\n### ECMAScript 5\nAll features moved to the [`es6` namespace](#ecmascript-6), here just a list of features:\n```js\nObject\n .create(proto | null, descriptors?) -> object\n .getPrototypeOf(object) -> proto | null\n .defineProperty(target, key, desc) -> target, cap for ie8-\n .defineProperties(target, descriptors) -> target, cap for ie8-\n .getOwnPropertyDescriptor(object, key) -> desc\n .getOwnPropertyNames(object) -> array\n .keys(object) -> array\n .seal(object) -> object, cap for ie8-\n .freeze(object) -> object, cap for ie8-\n .preventExtensions(object) -> object, cap for ie8-\n .isSealed(object) -> bool, cap for ie8-\n .isFrozen(object) -> bool, cap for ie8-\n .isExtensible(object) -> bool, cap for ie8-\nArray\n .isArray(var) -> bool\n #slice(start?, end?) -> array, fix for ie7-\n #join(string = ',') -> string, fix for ie7-\n #indexOf(var, from?) -> int\n #lastIndexOf(var, from?) -> int\n #every(fn(val, index, @), that) -> bool\n #some(fn(val, index, @), that) -> bool\n #forEach(fn(val, index, @), that) -> void\n #map(fn(val, index, @), that) -> array\n #filter(fn(val, index, @), that) -> array\n #reduce(fn(memo, val, index, @), memo?) -> var\n #reduceRight(fn(memo, val, index, @), memo?) -> var\n #sort(fn?) -> @, fixes for some engines\nFunction\n #bind(object, ...args) -> boundFn(...args)\nString\n #split(separator, limit) -> array\n #trim() -> str\nRegExp\n #toString() -> str\nNumber\n #toFixed(digits) -> string\n #toPrecision(precision) -> string\nparseInt(str, radix) -> int\nparseFloat(str) -> num\nDate\n .now() -> int\n #toISOString() -> string\n #toJSON() -> string\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es5\n```\n\n### ECMAScript 6\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6\n```\n#### ECMAScript 6: Object\nModules [`es6.object.assign`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.assign.js), [`es6.object.is`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.is.js), [`es6.object.set-prototype-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.set-prototype-of.js) and [`es6.object.to-string`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.to-string.js).\n\nIn ES6 most `Object` static methods should work with primitives. Modules [`es6.object.freeze`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.freeze.js), [`es6.object.seal`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.seal.js), [`es6.object.prevent-extensions`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.prevent-extensions.js), [`es6.object.is-frozen`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.is-frozen.js), [`es6.object.is-sealed`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.is-sealed.js), [`es6.object.is-extensible`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.is-extensible.js), [`es6.object.get-own-property-descriptor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.get-own-property-descriptor.js), [`es6.object.get-prototype-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.get-prototype-of.js), [`es6.object.keys`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.keys.js) and [`es6.object.get-own-property-names`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.get-own-property-names.js).\n\nJust ES5 features: [`es6.object.create`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.create.js), [`es6.object.define-property`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.define-property.js) and [`es6.object.define-properties`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.object.es6.object.define-properties.js).\n```js\nObject\n .assign(target, ...src) -> target\n .is(a, b) -> bool\n .setPrototypeOf(target, proto | null) -> target (required __proto__ - IE11+)\n .create(object | null, descriptors?) -> object\n .getPrototypeOf(var) -> object | null\n .defineProperty(object, key, desc) -> target\n .defineProperties(object, descriptors) -> target\n .getOwnPropertyDescriptor(var, key) -> desc | undefined\n .keys(var) -> array\n .getOwnPropertyNames(var) -> array\n .freeze(var) -> var\n .seal(var) -> var\n .preventExtensions(var) -> var\n .isFrozen(var) -> bool\n .isSealed(var) -> bool\n .isExtensible(var) -> bool\n #toString() -> string, ES6 fix: @@toStringTag support\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/object\ncore-js(/library)/fn/object/assign\ncore-js(/library)/fn/object/is\ncore-js(/library)/fn/object/set-prototype-of\ncore-js(/library)/fn/object/get-prototype-of\ncore-js(/library)/fn/object/create\ncore-js(/library)/fn/object/define-property\ncore-js(/library)/fn/object/define-properties\ncore-js(/library)/fn/object/get-own-property-descriptor\ncore-js(/library)/fn/object/keys\ncore-js(/library)/fn/object/get-own-property-names\ncore-js(/library)/fn/object/freeze\ncore-js(/library)/fn/object/seal\ncore-js(/library)/fn/object/prevent-extensions\ncore-js(/library)/fn/object/is-frozen\ncore-js(/library)/fn/object/is-sealed\ncore-js(/library)/fn/object/is-extensible\ncore-js/fn/object/to-string\n```\n[*Examples*](http://goo.gl/ywdwPz):\n```js\nvar foo = {q: 1, w: 2}\n , bar = {e: 3, r: 4}\n , baz = {t: 5, y: 6};\nObject.assign(foo, bar, baz); // => foo = {q: 1, w: 2, e: 3, r: 4, t: 5, y: 6}\n\nObject.is(NaN, NaN); // => true\nObject.is(0, -0); // => false\nObject.is(42, 42); // => true\nObject.is(42, '42'); // => false\n\nfunction Parent(){}\nfunction Child(){}\nObject.setPrototypeOf(Child.prototype, Parent.prototype);\nnew Child instanceof Child; // => true\nnew Child instanceof Parent; // => true\n\nvar O = {};\nO[Symbol.toStringTag] = 'Foo';\n'' + O; // => '[object Foo]'\n\nObject.keys('qwe'); // => ['0', '1', '2']\nObject.getPrototypeOf('qwe') === String.prototype; // => true\n```\n#### ECMAScript 6: Function\nModules [`es6.function.name`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.function.name.js), [`es6.function.has-instance`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.function.has-instance.js). Just ES5: [`es6.function.bind`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.function.bind.js).\n```js\nFunction\n #bind(object, ...args) -> boundFn(...args)\n #name -> string (IE9+)\n #@@hasInstance(var) -> bool\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js/es6/function\ncore-js/fn/function/name\ncore-js/fn/function/has-instance\ncore-js/fn/function/bind\ncore-js/fn/function/virtual/bind\n```\n[*Example*](http://goo.gl/zqu3Wp):\n```js\n(function foo(){}).name // => 'foo'\n\nconsole.log.bind(console, 42)(43); // => 42 43\n```\n#### ECMAScript 6: Array\nModules [`es6.array.from`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.from.js), [`es6.array.of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.of.js), [`es6.array.copy-within`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.copy-within.js), [`es6.array.fill`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.fill.js), [`es6.array.find`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.find.js), [`es6.array.find-index`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.find-index.js), [`es6.array.iterator`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.iterator.js). ES5 features with fixes: [`es6.array.is-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.is-array.js), [`es6.array.slice`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.slice.js), [`es6.array.join`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.join.js), [`es6.array.index-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.index-of.js), [`es6.array.last-index-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.last-index-of.js), [`es6.array.every`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.every.js), [`es6.array.some`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.some.js), [`es6.array.for-each`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.for-each.js), [`es6.array.map`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.map.js), [`es6.array.filter`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.filter.js), [`es6.array.reduce`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.reduce.js), [`es6.array.reduce-right`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.reduce-right.js), [`es6.array.sort`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.array.sort.js).\n```js\nArray\n .from(iterable | array-like, mapFn(val, index)?, that) -> array\n .of(...args) -> array\n .isArray(var) -> bool\n #copyWithin(target = 0, start = 0, end = @length) -> @\n #fill(val, start = 0, end = @length) -> @\n #find(fn(val, index, @), that) -> val\n #findIndex(fn(val, index, @), that) -> index | -1\n #values() -> iterator\n #keys() -> iterator\n #entries() -> iterator\n #join(string = ',') -> string, fix for ie7-\n #slice(start?, end?) -> array, fix for ie7-\n #indexOf(var, from?) -> index | -1\n #lastIndexOf(var, from?) -> index | -1\n #every(fn(val, index, @), that) -> bool\n #some(fn(val, index, @), that) -> bool\n #forEach(fn(val, index, @), that) -> void\n #map(fn(val, index, @), that) -> array\n #filter(fn(val, index, @), that) -> array\n #reduce(fn(memo, val, index, @), memo?) -> var\n #reduceRight(fn(memo, val, index, @), memo?) -> var\n #sort(fn?) -> @, invalid arguments fix\n #@@iterator() -> iterator (values)\n #@@unscopables -> object (cap)\nArguments\n #@@iterator() -> iterator (values, available only in core-js methods)\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/array\ncore-js(/library)/fn/array/from\ncore-js(/library)/fn/array/of\ncore-js(/library)/fn/array/is-array\ncore-js(/library)/fn/array/iterator\ncore-js(/library)/fn/array/copy-within\ncore-js(/library)/fn/array/fill\ncore-js(/library)/fn/array/find\ncore-js(/library)/fn/array/find-index\ncore-js(/library)/fn/array/values\ncore-js(/library)/fn/array/keys\ncore-js(/library)/fn/array/entries\ncore-js(/library)/fn/array/slice\ncore-js(/library)/fn/array/join\ncore-js(/library)/fn/array/index-of\ncore-js(/library)/fn/array/last-index-of\ncore-js(/library)/fn/array/every\ncore-js(/library)/fn/array/some\ncore-js(/library)/fn/array/for-each\ncore-js(/library)/fn/array/map\ncore-js(/library)/fn/array/filter\ncore-js(/library)/fn/array/reduce\ncore-js(/library)/fn/array/reduce-right\ncore-js(/library)/fn/array/sort\ncore-js(/library)/fn/array/virtual/iterator\ncore-js(/library)/fn/array/virtual/copy-within\ncore-js(/library)/fn/array/virtual/fill\ncore-js(/library)/fn/array/virtual/find\ncore-js(/library)/fn/array/virtual/find-index\ncore-js(/library)/fn/array/virtual/values\ncore-js(/library)/fn/array/virtual/keys\ncore-js(/library)/fn/array/virtual/entries\ncore-js(/library)/fn/array/virtual/slice\ncore-js(/library)/fn/array/virtual/join\ncore-js(/library)/fn/array/virtual/index-of\ncore-js(/library)/fn/array/virtual/last-index-of\ncore-js(/library)/fn/array/virtual/every\ncore-js(/library)/fn/array/virtual/some\ncore-js(/library)/fn/array/virtual/for-each\ncore-js(/library)/fn/array/virtual/map\ncore-js(/library)/fn/array/virtual/filter\ncore-js(/library)/fn/array/virtual/reduce\ncore-js(/library)/fn/array/virtual/reduce-right\ncore-js(/library)/fn/array/virtual/sort\n```\n[*Examples*](http://goo.gl/oaUFUf):\n```js\nArray.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\nArray.from({0: 1, 1: 2, 2: 3, length: 3}); // => [1, 2, 3]\nArray.from('123', Number); // => [1, 2, 3]\nArray.from('123', function(it){\n return it * it;\n}); // => [1, 4, 9]\n\nArray.of(1); // => [1]\nArray.of(1, 2, 3); // => [1, 2, 3]\n\nvar array = ['a', 'b', 'c'];\n\nfor(var val of array)console.log(val); // => 'a', 'b', 'c'\nfor(var val of array.values())console.log(val); // => 'a', 'b', 'c'\nfor(var key of array.keys())console.log(key); // => 0, 1, 2\nfor(var [key, val] of array.entries()){\n console.log(key); // => 0, 1, 2\n console.log(val); // => 'a', 'b', 'c'\n}\n\nfunction isOdd(val){\n return val % 2;\n}\n[4, 8, 15, 16, 23, 42].find(isOdd); // => 15\n[4, 8, 15, 16, 23, 42].findIndex(isOdd); // => 2\n[4, 8, 15, 16, 23, 42].find(isNaN); // => undefined\n[4, 8, 15, 16, 23, 42].findIndex(isNaN); // => -1\n\nArray(5).fill(42); // => [42, 42, 42, 42, 42]\n\n[1, 2, 3, 4, 5].copyWithin(0, 3); // => [4, 5, 3, 4, 5]\n```\n#### ECMAScript 6: String\nModules [`es6.string.from-code-point`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.from-code-point.js), [`es6.string.raw`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.raw.js), [`es6.string.iterator`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.iterator.js), [`es6.string.code-point-at`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.code-point-at.js), [`es6.string.ends-with`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.ends-with.js), [`es6.string.includes`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.includes.js), [`es6.string.repeat`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.repeat.js), [`es6.string.starts-with`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.starts-with.js) and [`es6.string.trim`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.trim.js).\n\nAnnex B HTML methods. Ugly, but it's also the part of the spec. Modules [`es6.string.anchor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.anchor.js), [`es6.string.big`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.big.js), [`es6.string.blink`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.blink.js), [`es6.string.bold`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.bold.js), [`es6.string.fixed`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.fixed.js), [`es6.string.fontcolor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.fontcolor.js), [`es6.string.fontsize`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.fontsize.js), [`es6.string.italics`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.italics.js), [`es6.string.link`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.link.js), [`es6.string.small`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.small.js), [`es6.string.strike`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.strike.js), [`es6.string.sub`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.sub.js) and [`es6.string.sup`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.string.sup.js).\n```js\nString\n .fromCodePoint(...codePoints) -> str\n .raw({raw}, ...substitutions) -> str\n #includes(str, from?) -> bool\n #startsWith(str, from?) -> bool\n #endsWith(str, from?) -> bool\n #repeat(num) -> str\n #codePointAt(pos) -> uint\n #trim() -> str, ES6 fix\n #anchor(name) -> str\n #big() -> str\n #blink() -> str\n #bold() -> str\n #fixed() -> str\n #fontcolor(color) -> str\n #fontsize(size) -> str\n #italics() -> str\n #link(url) -> str\n #small() -> str\n #strike() -> str\n #sub() -> str\n #sup() -> str\n #@@iterator() -> iterator (code points)\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/string\ncore-js(/library)/fn/string/from-code-point\ncore-js(/library)/fn/string/raw\ncore-js(/library)/fn/string/includes\ncore-js(/library)/fn/string/starts-with\ncore-js(/library)/fn/string/ends-with\ncore-js(/library)/fn/string/repeat\ncore-js(/library)/fn/string/code-point-at\ncore-js(/library)/fn/string/trim\ncore-js(/library)/fn/string/anchor\ncore-js(/library)/fn/string/big\ncore-js(/library)/fn/string/blink\ncore-js(/library)/fn/string/bold\ncore-js(/library)/fn/string/fixed\ncore-js(/library)/fn/string/fontcolor\ncore-js(/library)/fn/string/fontsize\ncore-js(/library)/fn/string/italics\ncore-js(/library)/fn/string/link\ncore-js(/library)/fn/string/small\ncore-js(/library)/fn/string/strike\ncore-js(/library)/fn/string/sub\ncore-js(/library)/fn/string/sup\ncore-js(/library)/fn/string/iterator\ncore-js(/library)/fn/string/virtual/includes\ncore-js(/library)/fn/string/virtual/starts-with\ncore-js(/library)/fn/string/virtual/ends-with\ncore-js(/library)/fn/string/virtual/repeat\ncore-js(/library)/fn/string/virtual/code-point-at\ncore-js(/library)/fn/string/virtual/trim\ncore-js(/library)/fn/string/virtual/anchor\ncore-js(/library)/fn/string/virtual/big\ncore-js(/library)/fn/string/virtual/blink\ncore-js(/library)/fn/string/virtual/bold\ncore-js(/library)/fn/string/virtual/fixed\ncore-js(/library)/fn/string/virtual/fontcolor\ncore-js(/library)/fn/string/virtual/fontsize\ncore-js(/library)/fn/string/virtual/italics\ncore-js(/library)/fn/string/virtual/link\ncore-js(/library)/fn/string/virtual/small\ncore-js(/library)/fn/string/virtual/strike\ncore-js(/library)/fn/string/virtual/sub\ncore-js(/library)/fn/string/virtual/sup\ncore-js(/library)/fn/string/virtual/iterator\n```\n[*Examples*](http://goo.gl/3UaQ93):\n```js\nfor(var val of 'a𠮷b'){\n console.log(val); // => 'a', '𠮷', 'b'\n}\n\n'foobarbaz'.includes('bar'); // => true\n'foobarbaz'.includes('bar', 4); // => false\n'foobarbaz'.startsWith('foo'); // => true\n'foobarbaz'.startsWith('bar', 3); // => true\n'foobarbaz'.endsWith('baz'); // => true\n'foobarbaz'.endsWith('bar', 6); // => true\n\n'string'.repeat(3); // => 'stringstringstring'\n\n'𠮷'.codePointAt(0); // => 134071\nString.fromCodePoint(97, 134071, 98); // => 'a𠮷b'\n\nvar name = 'Bob';\nString.raw`Hi\\n${name}!`; // => 'Hi\\\\nBob!' (ES6 template string syntax)\nString.raw({raw: 'test'}, 0, 1, 2); // => 't0e1s2t'\n\n'foo'.bold(); // => 'foo'\n'bar'.anchor('a\"b'); // => 'bar'\n'baz'.link('http://example.com'); // => 'baz'\n```\n#### ECMAScript 6: RegExp\nModules [`es6.regexp.constructor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.constructor.js) and [`es6.regexp.flags`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.flags.js).\n\nSupport well-known [symbols](#ecmascript-6-symbol) `@@match`, `@@replace`, `@@search` and `@@split`, modules [`es6.regexp.match`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.match.js), [`es6.regexp.replace`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.replace.js), [`es6.regexp.search`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.search.js) and [`es6.regexp.split`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.regexp.split.js).\n```\n[new] RegExp(pattern, flags?) -> regexp, ES6 fix: can alter flags (IE9+)\n #flags -> str (IE9+)\n #toString() -> str, ES6 fixes\n #@@match(str) -> array | null\n #@@replace(str, replacer) -> string\n #@@search(str) -> index\n #@@split(str, limit) -> array\nString\n #match(tpl) -> var, ES6 fix for support @@match\n #replace(tpl, replacer) -> var, ES6 fix for support @@replace\n #search(tpl) -> var, ES6 fix for support @@search\n #split(tpl, limit) -> var, ES6 fix for support @@split, some fixes for old engines\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js/es6/regexp\ncore-js/fn/regexp/constructor\ncore-js(/library)/fn/regexp/flags\ncore-js/fn/regexp/to-string\ncore-js/fn/regexp/match\ncore-js/fn/regexp/replace\ncore-js/fn/regexp/search\ncore-js/fn/regexp/split\n```\n[*Examples*](http://goo.gl/PiJxBD):\n```js\nRegExp(/./g, 'm'); // => /./m\n\n/foo/.flags; // => ''\n/foo/gim.flags; // => 'gim'\n\n'foo'.match({[Symbol.match]: _ => 1}); // => 1\n'foo'.replace({[Symbol.replace]: _ => 2}); // => 2\n'foo'.search({[Symbol.search]: _ => 3}); // => 3\n'foo'.split({[Symbol.split]: _ => 4}); // => 4\n\nRegExp.prototype.toString.call({source: 'foo', flags: 'bar'}); // => '/foo/bar'\n```\n#### ECMAScript 6: Number\nModule [`es6.number.constructor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.constructor.js). `Number` constructor support binary and octal literals, [*example*](http://goo.gl/jRd6b3):\n```js\nNumber('0b1010101'); // => 85\nNumber('0o7654321'); // => 2054353\n```\nModules [`es6.number.epsilon`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.epsilon.js), [`es6.number.is-finite`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.is-finite.js), [`es6.number.is-integer`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.is-integer.js), [`es6.number.is-nan`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.is-nan.js), [`es6.number.is-safe-integer`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.is-safe-integer.js), [`es6.number.max-safe-integer`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.max-safe-integer.js), [`es6.number.min-safe-integer`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.min-safe-integer.js), [`es6.number.parse-float`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.parse-float.js), [`es6.number.parse-int`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.parse-int.js), [`es6.number.to-fixed`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.to-fixed.js), [`es6.number.to-precision`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.number.to-precision.js), [`es6.parse-int`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.parse-int.js), [`es6.parse-float`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.parse-float.js).\n```js\n[new] Number(var) -> number | number object\n .isFinite(num) -> bool\n .isNaN(num) -> bool\n .isInteger(num) -> bool\n .isSafeInteger(num) -> bool\n .parseFloat(str) -> num\n .parseInt(str) -> int\n .EPSILON -> num\n .MAX_SAFE_INTEGER -> int\n .MIN_SAFE_INTEGER -> int\n #toFixed(digits) -> string, fixes\n #toPrecision(precision) -> string, fixes\nparseFloat(str) -> num, fixes\nparseInt(str) -> int, fixes\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/number\ncore-js/es6/number/constructor\ncore-js(/library)/fn/number/is-finite\ncore-js(/library)/fn/number/is-nan\ncore-js(/library)/fn/number/is-integer\ncore-js(/library)/fn/number/is-safe-integer\ncore-js(/library)/fn/number/parse-float\ncore-js(/library)/fn/number/parse-int\ncore-js(/library)/fn/number/epsilon\ncore-js(/library)/fn/number/max-safe-integer\ncore-js(/library)/fn/number/min-safe-integer\ncore-js(/library)/fn/number/to-fixed\ncore-js(/library)/fn/number/to-precision\ncore-js(/library)/fn/parse-float\ncore-js(/library)/fn/parse-int\n```\n#### ECMAScript 6: Math\nModules [`es6.math.acosh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.acosh.js), [`es6.math.asinh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.asinh.js), [`es6.math.atanh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.atanh.js), [`es6.math.cbrt`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.cbrt.js), [`es6.math.clz32`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.clz32.js), [`es6.math.cosh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.cosh.js), [`es6.math.expm1`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.expm1.js), [`es6.math.fround`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.fround.js), [`es6.math.hypot`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.hypot.js), [`es6.math.imul`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.imul.js), [`es6.math.log10`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.log10.js), [`es6.math.log1p`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.log1p.js), [`es6.math.log2`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.log2.js), [`es6.math.sign`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.sign.js), [`es6.math.sinh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.sinh.js), [`es6.math.tanh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.tanh.js), [`es6.math.trunc`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.math.trunc.js).\n```js\nMath\n .acosh(num) -> num\n .asinh(num) -> num\n .atanh(num) -> num\n .cbrt(num) -> num\n .clz32(num) -> uint\n .cosh(num) -> num\n .expm1(num) -> num\n .fround(num) -> num\n .hypot(...args) -> num\n .imul(num, num) -> int\n .log1p(num) -> num\n .log10(num) -> num\n .log2(num) -> num\n .sign(num) -> 1 | -1 | 0 | -0 | NaN\n .sinh(num) -> num\n .tanh(num) -> num\n .trunc(num) -> num\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/math\ncore-js(/library)/fn/math/acosh\ncore-js(/library)/fn/math/asinh\ncore-js(/library)/fn/math/atanh\ncore-js(/library)/fn/math/cbrt\ncore-js(/library)/fn/math/clz32\ncore-js(/library)/fn/math/cosh\ncore-js(/library)/fn/math/expm1\ncore-js(/library)/fn/math/fround\ncore-js(/library)/fn/math/hypot\ncore-js(/library)/fn/math/imul\ncore-js(/library)/fn/math/log1p\ncore-js(/library)/fn/math/log10\ncore-js(/library)/fn/math/log2\ncore-js(/library)/fn/math/sign\ncore-js(/library)/fn/math/sinh\ncore-js(/library)/fn/math/tanh\ncore-js(/library)/fn/math/trunc\n```\n#### ECMAScript 6: Date\nModules [`es6.date.to-string`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.date.to-string.js), ES5 features with fixes: [`es6.date.now`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.date.now.js), [`es6.date.to-iso-string`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.date.to-iso-string.js), [`es6.date.to-json`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.date.to-json.js) and [`es6.date.to-primitive`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.date.to-primitive.js).\n```js\nDate\n .now() -> int\n #toISOString() -> string\n #toJSON() -> string\n #toString() -> string\n #@@toPrimitive(hint) -> primitive\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js/es6/date\ncore-js/fn/date/to-string\ncore-js(/library)/fn/date/now\ncore-js(/library)/fn/date/to-iso-string\ncore-js(/library)/fn/date/to-json\ncore-js(/library)/fn/date/to-primitive\n```\n[*Example*](http://goo.gl/haeHLR):\n```js\nnew Date(NaN).toString(); // => 'Invalid Date'\n```\n\n#### ECMAScript 6: Promise\nModule [`es6.promise`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.promise.js).\n```js\nnew Promise(executor(resolve(var), reject(var))) -> promise\n #then(resolved(var), rejected(var)) -> promise\n #catch(rejected(var)) -> promise\n .resolve(promise | var) -> promise\n .reject(var) -> promise\n .all(iterable) -> promise\n .race(iterable) -> promise\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/promise\ncore-js(/library)/fn/promise\n```\nBasic [*example*](http://goo.gl/vGrtUC):\n```js\nfunction sleepRandom(time){\n return new Promise(function(resolve, reject){\n setTimeout(resolve, time * 1e3, 0 | Math.random() * 1e3);\n });\n}\n\nconsole.log('Run'); // => Run\nsleepRandom(5).then(function(result){\n console.log(result); // => 869, after 5 sec.\n return sleepRandom(10);\n}).then(function(result){\n console.log(result); // => 202, after 10 sec.\n}).then(function(){\n console.log('immediately after'); // => immediately after\n throw Error('Irror!');\n}).then(function(){\n console.log('will not be displayed');\n}).catch(x => console.log(x)); // => => Error: Irror!\n```\n`Promise.resolve` and `Promise.reject` [*example*](http://goo.gl/vr8TN3):\n```js\nPromise.resolve(42).then(x => console.log(x)); // => 42\nPromise.reject(42).catch(x => console.log(x)); // => 42\n\nPromise.resolve($.getJSON('/data.json')); // => ES6 promise\n```\n`Promise.all` [*example*](http://goo.gl/RdoDBZ):\n```js\nPromise.all([\n 'foo',\n sleepRandom(5),\n sleepRandom(15),\n sleepRandom(10) // after 15 sec:\n]).then(x => console.log(x)); // => ['foo', 956, 85, 382]\n```\n`Promise.race` [*example*](http://goo.gl/L8ovkJ):\n```js\nfunction timeLimit(promise, time){\n return Promise.race([promise, new Promise(function(resolve, reject){\n setTimeout(reject, time * 1e3, Error('Await > ' + time + ' sec'));\n })]);\n}\n\ntimeLimit(sleepRandom(5), 10).then(x => console.log(x)); // => 853, after 5 sec.\ntimeLimit(sleepRandom(15), 10).catch(x => console.log(x)); // Error: Await > 10 sec\n```\nECMAScript 7 [async functions](https://tc39.github.io/ecmascript-asyncawait) [example](http://goo.gl/wnQS4j):\n```js\nvar delay = time => new Promise(resolve => setTimeout(resolve, time))\n\nasync function sleepRandom(time){\n await delay(time * 1e3);\n return 0 | Math.random() * 1e3;\n};\nasync function sleepError(time, msg){\n await delay(time * 1e3);\n throw Error(msg);\n};\n\n(async () => {\n try {\n console.log('Run'); // => Run\n console.log(await sleepRandom(5)); // => 936, after 5 sec.\n var [a, b, c] = await Promise.all([\n sleepRandom(5),\n sleepRandom(15),\n sleepRandom(10)\n ]);\n console.log(a, b, c); // => 210 445 71, after 15 sec.\n await sleepError(5, 'Irror!');\n console.log('Will not be displayed');\n } catch(e){\n console.log(e); // => Error: 'Irror!', after 5 sec.\n }\n})();\n```\n\n##### Unhandled rejection tracking\n\nIn Node.js, like in native implementation, available events [`unhandledRejection`](https://nodejs.org/api/process.html#process_event_unhandledrejection) and [`rejectionHandled`](https://nodejs.org/api/process.html#process_event_rejectionhandled):\n```js\nprocess.on('unhandledRejection', (reason, promise) => console.log('unhandled', reason, promise));\nprocess.on('rejectionHandled', (promise) => console.log('handled', promise));\n\nvar p = Promise.reject(42);\n// unhandled 42 [object Promise]\n\nsetTimeout(() => p.catch(_ => _), 1e3);\n// handled [object Promise]\n```\nIn a browser on rejection, by default, you will see notify in the console, or you can add a custom handler and a handler on handling unhandled, [*example*](http://goo.gl/Wozskl):\n```js\nwindow.onunhandledrejection = e => console.log('unhandled', e.reason, e.promise);\nwindow.onrejectionhandled = e => console.log('handled', e.reason, e.promise);\n\nvar p = Promise.reject(42);\n// unhandled 42 [object Promise]\n\nsetTimeout(() => p.catch(_ => _), 1e3);\n// handled 42 [object Promise]\n```\n\n#### ECMAScript 6: Symbol\nModule [`es6.symbol`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.symbol.js).\n```js\nSymbol(description?) -> symbol\n .hasInstance -> @@hasInstance\n .isConcatSpreadable -> @@isConcatSpreadable\n .iterator -> @@iterator\n .match -> @@match\n .replace -> @@replace\n .search -> @@search\n .species -> @@species\n .split -> @@split\n .toPrimitive -> @@toPrimitive\n .toStringTag -> @@toStringTag\n .unscopables -> @@unscopables\n .for(key) -> symbol\n .keyFor(symbol) -> key\n .useSimple() -> void\n .useSetter() -> void\nObject\n .getOwnPropertySymbols(object) -> array\n```\nAlso wrapped some methods for correct work with `Symbol` polyfill.\n```js\nObject\n .create(proto | null, descriptors?) -> object\n .defineProperty(target, key, desc) -> target\n .defineProperties(target, descriptors) -> target\n .getOwnPropertyDescriptor(var, key) -> desc | undefined\n .getOwnPropertyNames(var) -> array\n #propertyIsEnumerable(key) -> bool\nJSON\n .stringify(target, replacer?, space?) -> string | undefined\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/symbol\ncore-js(/library)/fn/symbol\ncore-js(/library)/fn/symbol/has-instance\ncore-js(/library)/fn/symbol/is-concat-spreadable\ncore-js(/library)/fn/symbol/iterator\ncore-js(/library)/fn/symbol/match\ncore-js(/library)/fn/symbol/replace\ncore-js(/library)/fn/symbol/search\ncore-js(/library)/fn/symbol/species\ncore-js(/library)/fn/symbol/split\ncore-js(/library)/fn/symbol/to-primitive\ncore-js(/library)/fn/symbol/to-string-tag\ncore-js(/library)/fn/symbol/unscopables\ncore-js(/library)/fn/symbol/for\ncore-js(/library)/fn/symbol/key-for\n```\n[*Basic example*](http://goo.gl/BbvWFc):\n```js\nvar Person = (function(){\n var NAME = Symbol('name');\n function Person(name){\n this[NAME] = name;\n }\n Person.prototype.getName = function(){\n return this[NAME];\n };\n return Person;\n})();\n\nvar person = new Person('Vasya');\nconsole.log(person.getName()); // => 'Vasya'\nconsole.log(person['name']); // => undefined\nconsole.log(person[Symbol('name')]); // => undefined, symbols are uniq\nfor(var key in person)console.log(key); // => only 'getName', symbols are not enumerable\n```\n`Symbol.for` & `Symbol.keyFor` [*example*](http://goo.gl/0pdJjX):\n```js\nvar symbol = Symbol.for('key');\nsymbol === Symbol.for('key'); // true\nSymbol.keyFor(symbol); // 'key'\n```\n[*Example*](http://goo.gl/mKVOQJ) with methods for getting own object keys:\n```js\nvar O = {a: 1};\nObject.defineProperty(O, 'b', {value: 2});\nO[Symbol('c')] = 3;\nObject.keys(O); // => ['a']\nObject.getOwnPropertyNames(O); // => ['a', 'b']\nObject.getOwnPropertySymbols(O); // => [Symbol(c)]\nReflect.ownKeys(O); // => ['a', 'b', Symbol(c)]\n```\n##### Caveats when using `Symbol` polyfill:\n\n* We can't add new primitive type, `Symbol` returns object.\n* `Symbol.for` and `Symbol.keyFor` can't be shimmed cross-realm.\n* By default, to hide the keys, `Symbol` polyfill defines setter in `Object.prototype`. For this reason, uncontrolled creation of symbols can cause memory leak and the `in` operator is not working correctly with `Symbol` polyfill: `Symbol() in {} // => true`.\n\nYou can disable defining setters in `Object.prototype`. [Example](http://goo.gl/N5UD7J):\n```js\nSymbol.useSimple();\nvar s1 = Symbol('s1')\n , o1 = {};\no1[s1] = true;\nfor(var key in o1)console.log(key); // => 'Symbol(s1)_t.qamkg9f3q', w/o native Symbol\n\nSymbol.useSetter();\nvar s2 = Symbol('s2')\n , o2 = {};\no2[s2] = true;\nfor(var key in o2)console.log(key); // nothing\n```\n* Currently, `core-js` not adds setters to `Object.prototype` for well-known symbols for correct work something like `Symbol.iterator in foo`. It can cause problems with their enumerability.\n* Some problems possible with environment exotic objects (for example, IE `localStorage`).\n\n#### ECMAScript 6: Collections\n`core-js` uses native collections in most case, just fixes methods / constructor, if it's required, and in old environment uses fast polyfill (O(1) lookup).\n#### Map\nModule [`es6.map`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.map.js).\n```js\nnew Map(iterable (entries) ?) -> map\n #clear() -> void\n #delete(key) -> bool\n #forEach(fn(val, key, @), that) -> void\n #get(key) -> val\n #has(key) -> bool\n #set(key, val) -> @\n #size -> uint\n #values() -> iterator\n #keys() -> iterator\n #entries() -> iterator\n #@@iterator() -> iterator (entries)\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/map\ncore-js(/library)/fn/map\n```\n[*Examples*](http://goo.gl/GWR7NI):\n```js\nvar a = [1];\n\nvar map = new Map([['a', 1], [42, 2]]);\nmap.set(a, 3).set(true, 4);\n\nconsole.log(map.size); // => 4\nconsole.log(map.has(a)); // => true\nconsole.log(map.has([1])); // => false\nconsole.log(map.get(a)); // => 3\nmap.forEach(function(val, key){\n console.log(val); // => 1, 2, 3, 4\n console.log(key); // => 'a', 42, [1], true\n});\nmap.delete(a);\nconsole.log(map.size); // => 3\nconsole.log(map.get(a)); // => undefined\nconsole.log(Array.from(map)); // => [['a', 1], [42, 2], [true, 4]]\n\nvar map = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nfor(var [key, val] of map){\n console.log(key); // => 'a', 'b', 'c'\n console.log(val); // => 1, 2, 3\n}\nfor(var val of map.values())console.log(val); // => 1, 2, 3\nfor(var key of map.keys())console.log(key); // => 'a', 'b', 'c'\nfor(var [key, val] of map.entries()){\n console.log(key); // => 'a', 'b', 'c'\n console.log(val); // => 1, 2, 3\n}\n```\n#### Set\nModule [`es6.set`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.set.js).\n```js\nnew Set(iterable?) -> set\n #add(key) -> @\n #clear() -> void\n #delete(key) -> bool\n #forEach(fn(el, el, @), that) -> void\n #has(key) -> bool\n #size -> uint\n #values() -> iterator\n #keys() -> iterator\n #entries() -> iterator\n #@@iterator() -> iterator (values)\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/set\ncore-js(/library)/fn/set\n```\n[*Examples*](http://goo.gl/bmhLwg):\n```js\nvar set = new Set(['a', 'b', 'a', 'c']);\nset.add('d').add('b').add('e');\nconsole.log(set.size); // => 5\nconsole.log(set.has('b')); // => true\nset.forEach(function(it){\n console.log(it); // => 'a', 'b', 'c', 'd', 'e'\n});\nset.delete('b');\nconsole.log(set.size); // => 4\nconsole.log(set.has('b')); // => false\nconsole.log(Array.from(set)); // => ['a', 'c', 'd', 'e']\n\nvar set = new Set([1, 2, 3, 2, 1]);\n\nfor(var val of set)console.log(val); // => 1, 2, 3\nfor(var val of set.values())console.log(val); // => 1, 2, 3\nfor(var key of set.keys())console.log(key); // => 1, 2, 3\nfor(var [key, val] of set.entries()){\n console.log(key); // => 1, 2, 3\n console.log(val); // => 1, 2, 3\n}\n```\n#### WeakMap\nModule [`es6.weak-map`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.weak-map.js).\n```js\nnew WeakMap(iterable (entries) ?) -> weakmap\n #delete(key) -> bool\n #get(key) -> val\n #has(key) -> bool\n #set(key, val) -> @\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/weak-map\ncore-js(/library)/fn/weak-map\n```\n[*Examples*](http://goo.gl/SILXyw):\n```js\nvar a = [1]\n , b = [2]\n , c = [3];\n\nvar wmap = new WeakMap([[a, 1], [b, 2]]);\nwmap.set(c, 3).set(b, 4);\nconsole.log(wmap.has(a)); // => true\nconsole.log(wmap.has([1])); // => false\nconsole.log(wmap.get(a)); // => 1\nwmap.delete(a);\nconsole.log(wmap.get(a)); // => undefined\n\n// Private properties store:\nvar Person = (function(){\n var names = new WeakMap;\n function Person(name){\n names.set(this, name);\n }\n Person.prototype.getName = function(){\n return names.get(this);\n };\n return Person;\n})();\n\nvar person = new Person('Vasya');\nconsole.log(person.getName()); // => 'Vasya'\nfor(var key in person)console.log(key); // => only 'getName'\n```\n#### WeakSet\nModule [`es6.weak-set`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.weak-set.js).\n```js\nnew WeakSet(iterable?) -> weakset\n #add(key) -> @\n #delete(key) -> bool\n #has(key) -> bool\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/weak-set\ncore-js(/library)/fn/weak-set\n```\n[*Examples*](http://goo.gl/TdFbEx):\n```js\nvar a = [1]\n , b = [2]\n , c = [3];\n\nvar wset = new WeakSet([a, b, a]);\nwset.add(c).add(b).add(c);\nconsole.log(wset.has(b)); // => true\nconsole.log(wset.has([2])); // => false\nwset.delete(b);\nconsole.log(wset.has(b)); // => false\n```\n##### Caveats when using collections polyfill:\n\n* Weak-collections polyfill stores values as hidden properties of keys. It works correct and not leak in most cases. However, it is desirable to store a collection longer than its keys.\n\n#### ECMAScript 6: Typed Arrays\nImplementations and fixes `ArrayBuffer`, `DataView`, typed arrays constructors, static and prototype methods. Typed Arrays work only in environments with support descriptors (IE9+), `ArrayBuffer` and `DataView` should work anywhere.\n\nModules [`es6.typed.array-buffer`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.array-buffer.js), [`es6.typed.data-view`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.data-view.js), [`es6.typed.int8-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.int8-array.js), [`es6.typed.uint8-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.uint8-array.js), [`es6.typed.uint8-clamped-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.uint8-clamped-array.js), [`es6.typed.int16-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.int16-array.js), [`es6.typed.uint16-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.uint16-array.js), [`es6.typed.int32-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.int32-array.js), [`es6.typed.uint32-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.uint32-array.js), [`es6.typed.float32-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.float32-array.js) and [`es6.typed.float64-array`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.typed.float64-array.js).\n```js\nnew ArrayBuffer(length) -> buffer\n .isView(var) -> bool\n #slice(start = 0, end = @length) -> buffer\n #byteLength -> uint\n\nnew DataView(buffer, byteOffset = 0, byteLength = buffer.byteLength - byteOffset) -> view\n #getInt8(offset) -> int8\n #getUint8(offset) -> uint8\n #getInt16(offset, littleEndian = false) -> int16\n #getUint16(offset, littleEndian = false) -> uint16\n #getInt32(offset, littleEndian = false) -> int32\n #getUint32(offset, littleEndian = false) -> uint32\n #getFloat32(offset, littleEndian = false) -> float32\n #getFloat64(offset, littleEndian = false) -> float64\n #setInt8(offset, value) -> void\n #setUint8(offset, value) -> void\n #setInt16(offset, value, littleEndian = false) -> void\n #setUint16(offset, value, littleEndian = false) -> void\n #setInt32(offset, value, littleEndian = false) -> void\n #setUint32(offset, value, littleEndian = false) -> void\n #setFloat32(offset, value, littleEndian = false) -> void\n #setFloat64(offset, value, littleEndian = false) -> void\n #buffer -> buffer\n #byteLength -> uint\n #byteOffset -> uint\n\n{\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array\n}\n new %TypedArray%(length) -> typed\n new %TypedArray%(typed) -> typed\n new %TypedArray%(arrayLike) -> typed\n new %TypedArray%(iterable) -> typed\n new %TypedArray%(buffer, byteOffset = 0, length = (buffer.byteLength - byteOffset) / @BYTES_PER_ELEMENT) -> typed\n .BYTES_PER_ELEMENT -> uint\n .from(arrayLike | iterable, mapFn(val, index)?, that) -> typed\n .of(...args) -> typed\n #BYTES_PER_ELEMENT -> uint\n #copyWithin(target = 0, start = 0, end = @length) -> @\n #every(fn(val, index, @), that) -> bool\n #fill(val, start = 0, end = @length) -> @\n #filter(fn(val, index, @), that) -> typed\n #find(fn(val, index, @), that) -> val\n #findIndex(fn(val, index, @), that) -> index\n #forEach(fn(val, index, @), that) -> void\n #indexOf(var, from?) -> int\n #join(string = ',') -> string\n #lastIndexOf(var, from?) -> int\n #map(fn(val, index, @), that) -> typed\n #reduce(fn(memo, val, index, @), memo?) -> var\n #reduceRight(fn(memo, val, index, @), memo?) -> var\n #reverse() -> @\n #set(arrayLike, offset = 0) -> void\n #slice(start = 0, end = @length) -> typed\n #some(fn(val, index, @), that) -> bool\n #sort(fn(a, b)?) -> @\n #subarray(start = 0, end = @length) -> typed\n #toString() -> string\n #toLocaleString() -> string\n #values() -> iterator\n #keys() -> iterator\n #entries() -> iterator\n #@@iterator() -> iterator (values)\n #buffer -> buffer\n #byteLength -> uint\n #byteOffset -> uint\n #length -> uint\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/typed\ncore-js(/library)/fn/typed\ncore-js(/library)/fn/typed/array-buffer\ncore-js(/library)/fn/typed/data-view\ncore-js(/library)/fn/typed/int8-array\ncore-js(/library)/fn/typed/uint8-array\ncore-js(/library)/fn/typed/uint8-clamped-array\ncore-js(/library)/fn/typed/int16-array\ncore-js(/library)/fn/typed/uint16-array\ncore-js(/library)/fn/typed/int32-array\ncore-js(/library)/fn/typed/uint32-array\ncore-js(/library)/fn/typed/float32-array\ncore-js(/library)/fn/typed/float64-array\n```\n[*Examples*](http://goo.gl/yla75z):\n```js\nnew Int32Array(4); // => [0, 0, 0, 0]\nnew Uint8ClampedArray([1, 2, 3, 666]); // => [1, 2, 3, 255]\nnew Float32Array(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]\n\nvar buffer = new ArrayBuffer(8);\nvar view = new DataView(buffer);\nview.setFloat64(0, 123.456, true);\nnew Uint8Array(buffer.slice(4)); // => [47, 221, 94, 64]\n\nInt8Array.of(1, 1.5, 5.7, 745); // => [1, 1, 5, -23]\nUint8Array.from([1, 1.5, 5.7, 745]); // => [1, 1, 5, 233]\n\nvar typed = new Uint8Array([1, 2, 3]);\n\nvar a = typed.slice(1); // => [2, 3]\ntyped.buffer === a.buffer; // => false\nvar b = typed.subarray(1); // => [2, 3]\ntyped.buffer === b.buffer; // => true\n\ntyped.filter(it => it % 2); // => [1, 3]\ntyped.map(it => it * 1.5); // => [1, 3, 4]\n\nfor(var val of typed)console.log(val); // => 1, 2, 3\nfor(var val of typed.values())console.log(val); // => 1, 2, 3\nfor(var key of typed.keys())console.log(key); // => 0, 1, 2\nfor(var [key, val] of typed.entries()){\n console.log(key); // => 0, 1, 2\n console.log(val); // => 1, 2, 3\n}\n```\n##### Caveats when using typed arrays:\n\n* Typed Arrays polyfills works completely how should work by the spec, but because of internal use getter / setters on each instance, is slow and consumes significant memory. However, typed arrays polyfills required mainly for IE9 (and for `Uint8ClampedArray` in IE10 and early IE11), all modern engines have native typed arrays and requires only constructors fixes and methods.\n* The current version hasn't special entry points for methods, they can be added only with constructors. It can be added in the future.\n* In the `library` version we can't pollute native prototypes, so prototype methods available as constructors static.\n\n#### ECMAScript 6: Reflect\nModules [`es6.reflect.apply`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.apply.js), [`es6.reflect.construct`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.construct.js), [`es6.reflect.define-property`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.define-property.js), [`es6.reflect.delete-property`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.delete-property.js), [`es6.reflect.enumerate`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.enumerate.js), [`es6.reflect.get`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.get.js), [`es6.reflect.get-own-property-descriptor`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.get-own-property-descriptor.js), [`es6.reflect.get-prototype-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.get-prototype-of.js), [`es6.reflect.has`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.has.js), [`es6.reflect.is-extensible`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.is-extensible.js), [`es6.reflect.own-keys`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.own-keys.js), [`es6.reflect.prevent-extensions`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.prevent-extensions.js), [`es6.reflect.set`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.set.js), [`es6.reflect.set-prototype-of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es6.reflect.set-prototype-of.js).\n```js\nReflect\n .apply(target, thisArgument, argumentsList) -> var\n .construct(target, argumentsList, newTarget?) -> object\n .defineProperty(target, propertyKey, attributes) -> bool\n .deleteProperty(target, propertyKey) -> bool\n .enumerate(target) -> iterator (removed from the spec and will be removed from core-js@3)\n .get(target, propertyKey, receiver?) -> var\n .getOwnPropertyDescriptor(target, propertyKey) -> desc\n .getPrototypeOf(target) -> object | null\n .has(target, propertyKey) -> bool\n .isExtensible(target) -> bool\n .ownKeys(target) -> array\n .preventExtensions(target) -> bool\n .set(target, propertyKey, V, receiver?) -> bool\n .setPrototypeOf(target, proto) -> bool (required __proto__ - IE11+)\n```\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es6/reflect\ncore-js(/library)/fn/reflect\ncore-js(/library)/fn/reflect/apply\ncore-js(/library)/fn/reflect/construct\ncore-js(/library)/fn/reflect/define-property\ncore-js(/library)/fn/reflect/delete-property\ncore-js(/library)/fn/reflect/enumerate (deprecated and will be removed from the next major release)\ncore-js(/library)/fn/reflect/get\ncore-js(/library)/fn/reflect/get-own-property-descriptor\ncore-js(/library)/fn/reflect/get-prototype-of\ncore-js(/library)/fn/reflect/has\ncore-js(/library)/fn/reflect/is-extensible\ncore-js(/library)/fn/reflect/own-keys\ncore-js(/library)/fn/reflect/prevent-extensions\ncore-js(/library)/fn/reflect/set\ncore-js(/library)/fn/reflect/set-prototype-of\n```\n[*Examples*](http://goo.gl/gVT0cH):\n```js\nvar O = {a: 1};\nObject.defineProperty(O, 'b', {value: 2});\nO[Symbol('c')] = 3;\nReflect.ownKeys(O); // => ['a', 'b', Symbol(c)]\n\nfunction C(a, b){\n this.c = a + b;\n}\n\nvar instance = Reflect.construct(C, [20, 22]);\ninstance.c; // => 42\n```\n\n### ECMAScript 7+ proposals\n[The TC39 process.](https://tc39.github.io/process-document/)\n\n[*CommonJS entry points:*](#commonjs)\n```\ncore-js(/library)/es7\ncore-js(/library)/es7/array\ncore-js(/library)/es7/global\ncore-js(/library)/es7/string\ncore-js(/library)/es7/map\ncore-js(/library)/es7/set\ncore-js(/library)/es7/error\ncore-js(/library)/es7/math\ncore-js(/library)/es7/system\ncore-js(/library)/es7/symbol\ncore-js(/library)/es7/reflect\ncore-js(/library)/es7/observable\n```\n`core-js/stage/4` entry point contains only stage 4 proposals, `core-js/stage/3` - stage 3 and stage 4, etc.\n#### Stage 4 proposals\n\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/4\n```\n* `{Array, %TypedArray%}#includes` [proposal](https://github.com/tc39/Array.prototype.includes) - module [`es7.array.includes`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.array.includes.js), `%TypedArray%` version in modules from [this section](#ecmascript-6-typed-arrays).\n```js\nArray\n #includes(var, from?) -> bool\n{\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array\n}\n #includes(var, from?) -> bool\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/array/includes\n```\n[*Examples*](http://goo.gl/2Gq4ma):\n```js\n[1, 2, 3].includes(2); // => true\n[1, 2, 3].includes(4); // => false\n[1, 2, 3].includes(2, 2); // => false\n\n[NaN].indexOf(NaN); // => -1\n[NaN].includes(NaN); // => true\nArray(1).indexOf(undefined); // => -1\nArray(1).includes(undefined); // => true\n```\n* `Object.values`, `Object.entries` [proposal](https://github.com/tc39/proposal-object-values-entries) - modules [`es7.object.values`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.values.js), [`es7.object.entries`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.entries.js)\n```js\nObject\n .values(object) -> array\n .entries(object) -> array\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/object/values\ncore-js(/library)/fn/object/entries\n```\n[*Examples*](http://goo.gl/6kuGOn):\n```js\nObject.values({a: 1, b: 2, c: 3}); // => [1, 2, 3]\nObject.entries({a: 1, b: 2, c: 3}); // => [['a', 1], ['b', 2], ['c', 3]]\n\nfor(let [key, value] of Object.entries({a: 1, b: 2, c: 3})){\n console.log(key); // => 'a', 'b', 'c'\n console.log(value); // => 1, 2, 3\n}\n```\n* `Object.getOwnPropertyDescriptors` [proposal](https://github.com/tc39/proposal-object-getownpropertydescriptors) - module [`es7.object.get-own-property-descriptors`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.get-own-property-descriptors.js)\n```js\nObject\n .getOwnPropertyDescriptors(object) -> object\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/object/get-own-property-descriptors\n```\n*Examples*:\n```js\n// Shallow object cloning with prototype and descriptors:\nvar copy = Object.create(Object.getPrototypeOf(O), Object.getOwnPropertyDescriptors(O));\n// Mixin:\nObject.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n```\n* `String#padStart`, `String#padEnd` [proposal](https://github.com/tc39/proposal-string-pad-start-end) - modules [`es7.string.pad-start`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.pad-start.js), [`es7.string.pad-end`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.pad-end.js)\n```js\nString\n #padStart(length, fillStr = ' ') -> string\n #padEnd(length, fillStr = ' ') -> string\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/string/pad-start\ncore-js(/library)/fn/string/pad-end\ncore-js(/library)/fn/string/virtual/pad-start\ncore-js(/library)/fn/string/virtual/pad-end\n```\n[*Examples*](http://goo.gl/hK5ccv):\n```js\n'hello'.padStart(10); // => ' hello'\n'hello'.padStart(10, '1234'); // => '12341hello'\n'hello'.padEnd(10); // => 'hello '\n'hello'.padEnd(10, '1234'); // => 'hello12341'\n```\n* `Object#__(define|lookup)[GS]etter__`, [annex B ES2017](https://github.com/tc39/ecma262/pull/381), but we haven't special namespace for that - modules [`es7.object.define-setter`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.define-setter.js), [`es7.object.define-getter`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.define-getter.js), [`es7.object.lookup-setter`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.lookup-setter.js) and [`es7.object.lookup-getter`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.object.lookup-getter.js).\n```js\nObject\n #__defineSetter__(key, fn) -> void\n #__defineGetter__(key, fn) -> void\n #__lookupSetter__(key) -> fn | void\n #__lookupGetter__(key) -> fn | void\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/object/define-getter\ncore-js(/library)/fn/object/define-setter\ncore-js(/library)/fn/object/lookup-getter\ncore-js(/library)/fn/object/lookup-setter\n```\n\n#### Stage 3 proposals\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/3\n```\n* `global` [proposal](https://github.com/tc39/proposal-global) - modules [`es7.global`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.global.js) and [`es7.system.global`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.system.global.js) (obsolete)\n```js\nglobal -> object\nSystem\n .global -> object (obsolete)\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/global\ncore-js(/library)/fn/system/global (obsolete)\n```\n[*Examples*](http://goo.gl/gEqMl7):\n```js\nglobal.Array === Array; // => true\n```\n* `Promise#finally` [proposal](https://github.com/tc39/proposal-promise-finally) - module [`es7.promise.finally`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.promise.finally.js)\n```js\nPromise\n #finally(onFinally()) -> promise\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/promise/finally\n```\n[*Examples*](https://goo.gl/AhyBbJ):\n```js\nPromise.resolve(42).finally(() => console.log('You will see it anyway'));\n\nPromise.reject(42).finally(() => console.log('You will see it anyway'));\n\n#### Stage 2 proposals\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/2\n```\n* `String#trimLeft`, `String#trimRight` / `String#trimStart`, `String#trimEnd` [proposal](https://github.com/sebmarkbage/ecmascript-string-left-right-trim) - modules [`es7.string.trim-left`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.trim-right.js), [`es7.string.trim-right`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.trim-right.js)\n```js\nString\n #trimLeft() -> string\n #trimRight() -> string\n #trimStart() -> string\n #trimEnd() -> string\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/string/trim-start\ncore-js(/library)/fn/string/trim-end\ncore-js(/library)/fn/string/trim-left\ncore-js(/library)/fn/string/trim-right\ncore-js(/library)/fn/string/virtual/trim-start\ncore-js(/library)/fn/string/virtual/trim-end\ncore-js(/library)/fn/string/virtual/trim-left\ncore-js(/library)/fn/string/virtual/trim-right\n```\n[*Examples*](http://goo.gl/Er5lMJ):\n```js\n' hello '.trimLeft(); // => 'hello '\n' hello '.trimRight(); // => ' hello'\n```\n```\n* `Symbol.asyncIterator` for [async iteration proposal](https://github.com/tc39/proposal-async-iteration) - module [`es7.symbol.async-iterator`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.symbol.async-iterator.js)\n```js\nSymbol\n .asyncIterator -> @@asyncIterator\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/symbol/async-iterator\n```\n\n#### Stage 1 proposals\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/1\n```\n* `Promise.try` [proposal](https://github.com/tc39/proposal-promise-try) - module [`es7.promise.try`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.promise.try.js)\n```js\nPromise\n .try(function()) -> promise\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/promise/try\n```\n[*Examples*](https://goo.gl/k5GGRo):\n```js\nPromise.try(() => 42).then(it => console.log(`Promise, resolved as ${it}`));\n\nPromise.try(() => { throw 42; }).catch(it => console.log(`Promise, rejected as ${it}`));\n```\n* `Array#flatten` and `Array#flatMap` [proposal](https://tc39.github.io/proposal-flatMap) - modules [`es7.array.flatten`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.array.flatten.js) and [`es7.array.flat-map`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.array.flat-map.js)\n```js\nArray\n #flatten(depthArg = 1) -> array\n #flatMap(fn(val, key, @), that) -> array\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/array/flatten\ncore-js(/library)/fn/array/flat-map\ncore-js(/library)/fn/array/virtual/flatten\ncore-js(/library)/fn/array/virtual/flat-map\n```\n[*Examples*](https://goo.gl/jTXsZi):\n```js\n[1, [2, 3], [4, 5]].flatten(); // => [1, 2, 3, 4, 5]\n[1, [2, [3, [4]]], 5].flatten(); // => [1, 2, [3, [4]], 5]\n[1, [2, [3, [4]]], 5].flatten(3); // => [1, 2, 3, 4, 5]\n\n[{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}].flatMap(it => [it.a, it.b]); // => [1, 2, 3, 4, 5, 6]\n```\n* `.of` and `.from` methods on collection constructors [proposal](https://github.com/tc39/proposal-setmap-offrom) - modules [`es7.set.of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.set.of.js), [`es7.set.from`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.set.from.js), [`es7.map.of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.map.of.js), [`es7.map.from`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.map.from.js), [`es7.weak-set.of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.weak-set.of.js), [`es7.weak-set.from`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.weak-set.from.js), [`es7.weak-map.of`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.weak-map.of.js), [`es7.weak-map.from`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.weak-map.from.js)\n```js\nSet\n .of(...args) -> set\n .from(iterable, mapFn(val, index)?, that?) -> set\nMap\n .of(...args) -> map\n .from(iterable, mapFn(val, index)?, that?) -> map\nWeakSet\n .of(...args) -> weakset\n .from(iterable, mapFn(val, index)?, that?) -> weakset\nWeakMap\n .of(...args) -> weakmap\n .from(iterable, mapFn(val, index)?, that?) -> weakmap\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/set/of\ncore-js(/library)/fn/set/from\ncore-js(/library)/fn/map/of\ncore-js(/library)/fn/map/from\ncore-js(/library)/fn/weak-set/of\ncore-js(/library)/fn/weak-set/from\ncore-js(/library)/fn/weak-map/of\ncore-js(/library)/fn/weak-map/from\n```\n[*Examples*](https://goo.gl/mSC7eU):\n```js\nSet.of(1, 2, 3, 2, 1); // => Set {1, 2, 3}\n\nMap.from([[1, 2], [3, 4]], ([key, val]) => [key ** 2, val ** 2]); // => Map {1: 4, 9: 16}\n```\n* `String#matchAll` [proposal](https://github.com/tc39/String.prototype.matchAll) - module [`es7.string.match-all`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.match-all.js)\n```js\nString\n #matchAll(regexp) -> iterator\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/string/match-all\ncore-js(/library)/fn/string/virtual/match-all\n```\n[*Examples*](http://goo.gl/6kp9EB):\n```js\nfor(let [_, d, D] of '1111a2b3cccc'.matchAll(/(\\d)(\\D)/)){\n console.log(d, D); // => 1 a, 2 b, 3 c\n}\n```\n* `Observable` [proposal](https://github.com/zenparsing/es-observable) - modules [`es7.observable`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.observable.js) and [`es7.symbol.observable`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.symbol.observable.js)\n```js\nnew Observable(fn) -> observable\n #subscribe(observer) -> subscription\n #forEach(fn) -> promise\n #@@observable() -> @\n .of(...items) -> observable\n .from(observable | iterable) -> observable\n .@@species -> @\nSymbol\n .observable -> @@observable\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/observable\ncore-js(/library)/fn/symbol/observable\n```\n[*Examples*](http://goo.gl/1LDywi):\n```js\nnew Observable(observer => {\n observer.next('hello');\n observer.next('world');\n observer.complete();\n}).forEach(it => console.log(it))\n .then(_ => console.log('!'));\n```\n* `Math.{clamp, DEG_PER_RAD, degrees, fscale, rad-per-deg, radians, scale}` \n [proposal](https://github.com/rwaldron/proposal-math-extensions) - modules \n [`es7.math.clamp`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.clamp.js), \n [`es7.math.DEG_PER_RAD`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.DEG_PER_RAD.js), \n [`es7.math.degrees`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.degrees.js),\n [`es7.math.fscale`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.fscale.js), \n [`es7.math.RAD_PER_DEG`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.RAD_PER_DEG.js), \n [`es7.math.radians`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.radians.js) and\n [`es7.math.scale`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.scale.js)\n```js\nMath\n .DEG_PER_RAD -> number\n .RAD_PER_DEG -> number\n .clamp(x, lower, upper) -> number\n .degrees(radians) -> number\n .fscale(x, inLow, inHigh, outLow, outHigh) -> number\n .radians(degrees) -> number\n .scale(x, inLow, inHigh, outLow, outHigh) -> number\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/math/clamp\ncore-js(/library)/fn/math/deg-per-rad\ncore-js(/library)/fn/math/degrees\ncore-js(/library)/fn/math/fscale\ncore-js(/library)/fn/math/rad-per-deg\ncore-js(/library)/fn/math/radians\ncore-js(/library)/fn/math/scale\n```\n* `Math.signbit` [proposal](http://jfbastien.github.io/papers/Math.signbit.html) - module [`es7.math.signbit`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.signbit.js)\n```js\nMath\n .signbit(x) -> bool\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/math/signbit\n```\n[*Examples*](http://es6.zloirock.ru/):\n```js\nMath.signbit(NaN); // => NaN\nMath.signbit(1); // => true\nMath.signbit(-1); // => false\nMath.signbit(0); // => true\nMath.signbit(-0); // => false\n```\n\n#### Stage 0 proposals\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/0\n```\n* `String#at` [proposal](https://github.com/mathiasbynens/String.prototype.at) - module [`es7.string.at`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.string.at.js)\n```js\nString\n #at(index) -> string\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/string/at\ncore-js(/library)/fn/string/virtual/at\n```\n[*Examples*](http://goo.gl/XluXI8):\n```js\n'a𠮷b'.at(1); // => '𠮷'\n'a𠮷b'.at(1).length; // => 2\n```\n* `Map#toJSON`, `Set#toJSON` [proposal](https://github.com/DavidBruant/Map-Set.prototype.toJSON) - modules [`es7.map.to-json`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.map.to-json.js), [`es7.set.to-json`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.set.to-json.js) (rejected and will be removed from `core-js@3`)\n```js\nMap\n #toJSON() -> array (rejected and will be removed from core-js@3)\nSet\n #toJSON() -> array (rejected and will be removed from core-js@3)\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/map\ncore-js(/library)/fn/set\n```\n* `Error.isError` [proposal](https://github.com/ljharb/proposal-is-error) - module [`es7.error.is-error`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.error.is-error.js) (withdrawn and will be removed from `core-js@3`)\n```js\nError\n .isError(it) -> bool (withdrawn and will be removed from core-js@3)\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/error/is-error\n```\n* `Math.{iaddh, isubh, imulh, umulh}` [proposal](https://gist.github.com/BrendanEich/4294d5c212a6d2254703) - modules [`es7.math.iaddh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.iaddh.js), [`es7.math.isubh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.isubh.js), [`es7.math.imulh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.imulh.js) and [`es7.math.umulh`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.math.umulh.js)\n```js\nMath\n .iaddh(lo0, hi0, lo1, hi1) -> int32\n .isubh(lo0, hi0, lo1, hi1) -> int32\n .imulh(a, b) -> int32\n .umulh(a, b) -> uint32\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/math/iaddh\ncore-js(/library)/fn/math/isubh\ncore-js(/library)/fn/math/imulh\ncore-js(/library)/fn/math/umulh\n```\n* `global.asap`, [TC39 discussion](https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-09/sept-25.md#510-globalasap-for-enqueuing-a-microtask), module [`es7.asap`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.asap.js)\n```js\nasap(fn) -> void\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/asap\n```\n[*Examples*](http://goo.gl/tx3SRK):\n```js\nasap(() => console.log('called as microtask'));\n```\n\n#### Pre-stage 0 proposals\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/stage/pre\n```\n* `Reflect` metadata [proposal](https://github.com/jonathandturner/decorators/blob/master/specs/metadata.md) - modules [`es7.reflect.define-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.define-metadata.js), [`es7.reflect.delete-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.delete-metadata.js), [`es7.reflect.get-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.get-metadata.js), [`es7.reflect.get-metadata-keys`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.get-metadata-keys.js), [`es7.reflect.get-own-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.get-own-metadata.js), [`es7.reflect.get-own-metadata-keys`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.get-own-metadata-keys.js), [`es7.reflect.has-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.has-metadata.js), [`es7.reflect.has-own-metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.has-own-metadata.js) and [`es7.reflect.metadata`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/es7.reflect.metadata.js).\n```js\nReflect\n .defineMetadata(metadataKey, metadataValue, target, propertyKey?) -> void\n .getMetadata(metadataKey, target, propertyKey?) -> var\n .getOwnMetadata(metadataKey, target, propertyKey?) -> var\n .hasMetadata(metadataKey, target, propertyKey?) -> bool\n .hasOwnMetadata(metadataKey, target, propertyKey?) -> bool\n .deleteMetadata(metadataKey, target, propertyKey?) -> bool\n .getMetadataKeys(target, propertyKey?) -> array\n .getOwnMetadataKeys(target, propertyKey?) -> array\n .metadata(metadataKey, metadataValue) -> decorator(target, targetKey?) -> void\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/reflect/define-metadata\ncore-js(/library)/fn/reflect/delete-metadata\ncore-js(/library)/fn/reflect/get-metadata\ncore-js(/library)/fn/reflect/get-metadata-keys\ncore-js(/library)/fn/reflect/get-own-metadata\ncore-js(/library)/fn/reflect/get-own-metadata-keys\ncore-js(/library)/fn/reflect/has-metadata\ncore-js(/library)/fn/reflect/has-own-metadata\ncore-js(/library)/fn/reflect/metadata\n```\n[*Examples*](http://goo.gl/KCo3PS):\n```js\nvar O = {};\nReflect.defineMetadata('foo', 'bar', O);\nReflect.ownKeys(O); // => []\nReflect.getOwnMetadataKeys(O); // => ['foo']\nReflect.getOwnMetadata('foo', O); // => 'bar'\n```\n\n### Web standards\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/web\n```\n#### setTimeout / setInterval\nModule [`web.timers`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/web.timers.js). Additional arguments fix for IE9-.\n```js\nsetTimeout(fn(...args), time, ...args) -> id\nsetInterval(fn(...args), time, ...args) -> id\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/web/timers\ncore-js(/library)/fn/set-timeout\ncore-js(/library)/fn/set-interval\n```\n```js\n// Before:\nsetTimeout(log.bind(null, 42), 1000);\n// After:\nsetTimeout(log, 1000, 42);\n```\n#### setImmediate\nModule [`web.immediate`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/web.immediate.js). [`setImmediate` proposal](https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate) polyfill.\n```js\nsetImmediate(fn(...args), ...args) -> id\nclearImmediate(id) -> void\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/web/immediate\ncore-js(/library)/fn/set-immediate\ncore-js(/library)/fn/clear-immediate\n```\n[*Examples*](http://goo.gl/6nXGrx):\n```js\nsetImmediate(function(arg1, arg2){\n console.log(arg1, arg2); // => Message will be displayed with minimum delay\n}, 'Message will be displayed', 'with minimum delay');\n\nclearImmediate(setImmediate(function(){\n console.log('Message will not be displayed');\n}));\n```\n#### Iterable DOM collections\nSome DOM collections should have [iterable interface](https://heycam.github.io/webidl/#idl-iterable) or should be [inherited from `Array`](https://heycam.github.io/webidl/#LegacyArrayClass). That mean they should have `keys`, `values`, `entries` and `@@iterator` methods for iteration. So add them. Module [`web.dom.iterable`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/web.dom.iterable.js):\n```js\n{\n CSSRuleList,\n CSSStyleDeclaration,\n CSSValueList,\n ClientRectList,\n DOMRectList,\n DOMStringList,\n DOMTokenList,\n DataTransferItemList,\n FileList,\n HTMLAllCollection,\n HTMLCollection,\n HTMLFormElement,\n HTMLSelectElement,\n MediaList,\n MimeTypeArray,\n NamedNodeMap,\n NodeList,\n PaintRequestList,\n Plugin,\n PluginArray,\n SVGLengthList,\n SVGNumberList,\n SVGPathSegList,\n SVGPointList,\n SVGStringList,\n SVGTransformList,\n SourceBufferList,\n StyleSheetList,\n TextTrackCueList,\n TextTrackList,\n TouchList\n}\n #@@iterator() -> iterator (values)\n\n{\n DOMTokenList,\n NodeList\n}\n #values() -> iterator\n #keys() -> iterator\n #entries() -> iterator\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/web/dom-collections\ncore-js(/library)/fn/dom-collections/iterator\n```\n[*Examples*](http://goo.gl/lfXVFl):\n```js\nfor(var {id} of document.querySelectorAll('*')){\n if(id)console.log(id);\n}\n\nfor(var [index, {id}] of document.querySelectorAll('*').entries()){\n if(id)console.log(index, id);\n}\n```\n### Non-standard\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core\n```\n#### Object\nModules [`core.object.is-object`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.object.is-object.js), [`core.object.classof`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.object.classof.js), [`core.object.define`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.object.define.js), [`core.object.make`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.object.make.js).\n```js\nObject\n .isObject(var) -> bool\n .classof(var) -> string\n .define(target, mixin) -> target\n .make(proto | null, mixin?) -> object\n```\n\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core/object\ncore-js(/library)/fn/object/is-object\ncore-js(/library)/fn/object/define\ncore-js(/library)/fn/object/make\n```\nObject classify [*examples*](http://goo.gl/YZQmGo):\n```js\nObject.isObject({}); // => true\nObject.isObject(isNaN); // => true\nObject.isObject(null); // => false\n\nvar classof = Object.classof;\n\nclassof(null); // => 'Null'\nclassof(undefined); // => 'Undefined'\nclassof(1); // => 'Number'\nclassof(true); // => 'Boolean'\nclassof('string'); // => 'String'\nclassof(Symbol()); // => 'Symbol'\n\nclassof(new Number(1)); // => 'Number'\nclassof(new Boolean(true)); // => 'Boolean'\nclassof(new String('string')); // => 'String'\n\nvar fn = function(){}\n , list = (function(){return arguments})(1, 2, 3);\n\nclassof({}); // => 'Object'\nclassof(fn); // => 'Function'\nclassof([]); // => 'Array'\nclassof(list); // => 'Arguments'\nclassof(/./); // => 'RegExp'\nclassof(new TypeError); // => 'Error'\n\nclassof(new Set); // => 'Set'\nclassof(new Map); // => 'Map'\nclassof(new WeakSet); // => 'WeakSet'\nclassof(new WeakMap); // => 'WeakMap'\nclassof(new Promise(fn)); // => 'Promise'\n\nclassof([].values()); // => 'Array Iterator'\nclassof(new Set().values()); // => 'Set Iterator'\nclassof(new Map().values()); // => 'Map Iterator'\n\nclassof(Math); // => 'Math'\nclassof(JSON); // => 'JSON'\n\nfunction Example(){}\nExample.prototype[Symbol.toStringTag] = 'Example';\n\nclassof(new Example); // => 'Example'\n```\n`Object.define` and `Object.make` [*examples*](http://goo.gl/rtpD5Z):\n```js\n// Before:\nObject.defineProperty(target, 'c', {\n enumerable: true,\n configurable: true,\n get: function(){\n return this.a + this.b;\n }\n});\n\n// After:\nObject.define(target, {\n get c(){\n return this.a + this.b;\n }\n});\n\n// Shallow object cloning with prototype and descriptors:\nvar copy = Object.make(Object.getPrototypeOf(src), src);\n\n// Simple inheritance:\nfunction Vector2D(x, y){\n this.x = x;\n this.y = y;\n}\nObject.define(Vector2D.prototype, {\n get xy(){\n return Math.hypot(this.x, this.y);\n }\n});\nfunction Vector3D(x, y, z){\n Vector2D.apply(this, arguments);\n this.z = z;\n}\nVector3D.prototype = Object.make(Vector2D.prototype, {\n constructor: Vector3D,\n get xyz(){\n return Math.hypot(this.x, this.y, this.z);\n }\n});\n\nvar vector = new Vector3D(9, 12, 20);\nconsole.log(vector.xy); // => 15\nconsole.log(vector.xyz); // => 25\nvector.y++;\nconsole.log(vector.xy); // => 15.811388300841896\nconsole.log(vector.xyz); // => 25.495097567963924\n```\n#### Dict\nModule [`core.dict`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.dict.js). Based on [TC39 discuss](https://github.com/rwaldron/tc39-notes/blob/master/es6/2012-11/nov-29.md#collection-apis-review) / [strawman](http://wiki.ecmascript.org/doku.php?id=harmony:modules_standard#dictionaries).\n```js\n[new] Dict(iterable (entries) | object ?) -> dict\n .isDict(var) -> bool\n .values(object) -> iterator\n .keys(object) -> iterator\n .entries(object) -> iterator (entries)\n .has(object, key) -> bool\n .get(object, key) -> val\n .set(object, key, value) -> object\n .forEach(object, fn(val, key, @), that) -> void\n .map(object, fn(val, key, @), that) -> new @\n .mapPairs(object, fn(val, key, @), that) -> new @\n .filter(object, fn(val, key, @), that) -> new @\n .some(object, fn(val, key, @), that) -> bool\n .every(object, fn(val, key, @), that) -> bool\n .find(object, fn(val, key, @), that) -> val\n .findKey(object, fn(val, key, @), that) -> key\n .keyOf(object, var) -> key\n .includes(object, var) -> bool\n .reduce(object, fn(memo, val, key, @), memo?) -> var\n```\n\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core/dict\ncore-js(/library)/fn/dict\n```\n`Dict` create object without prototype from iterable or simple object.\n\n[*Examples*](http://goo.gl/pnp8Vr):\n```js\nvar map = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nDict(); // => {__proto__: null}\nDict({a: 1, b: 2, c: 3}); // => {__proto__: null, a: 1, b: 2, c: 3}\nDict(map); // => {__proto__: null, a: 1, b: 2, c: 3}\nDict([1, 2, 3].entries()); // => {__proto__: null, 0: 1, 1: 2, 2: 3}\n\nvar dict = Dict({a: 42});\ndict instanceof Object; // => false\ndict.a; // => 42\ndict.toString; // => undefined\n'a' in dict; // => true\n'hasOwnProperty' in dict; // => false\n\nDict.isDict({}); // => false\nDict.isDict(Dict()); // => true\n```\n`Dict.keys`, `Dict.values` and `Dict.entries` returns iterators for objects.\n\n[*Examples*](http://goo.gl/xAvECH):\n```js\nvar dict = {a: 1, b: 2, c: 3};\n\nfor(var key of Dict.keys(dict))console.log(key); // => 'a', 'b', 'c'\n\nfor(var val of Dict.values(dict))console.log(val); // => 1, 2, 3\n\nfor(var [key, val] of Dict.entries(dict)){\n console.log(key); // => 'a', 'b', 'c'\n console.log(val); // => 1, 2, 3\n}\n\nnew Map(Dict.entries(dict)); // => Map {a: 1, b: 2, c: 3}\n```\nBasic dict operations for objects with prototype [*examples*](http://goo.gl/B28UnG):\n```js\n'q' in {q: 1}; // => true\n'toString' in {}; // => true\n\nDict.has({q: 1}, 'q'); // => true\nDict.has({}, 'toString'); // => false\n\n({q: 1})['q']; // => 1\n({}).toString; // => function toString(){ [native code] }\n\nDict.get({q: 1}, 'q'); // => 1\nDict.get({}, 'toString'); // => undefined\n\nvar O = {};\nO['q'] = 1;\nO['q']; // => 1\nO['__proto__'] = {w: 2};\nO['__proto__']; // => {w: 2}\nO['w']; // => 2\n\nvar O = {};\nDict.set(O, 'q', 1);\nO['q']; // => 1\nDict.set(O, '__proto__', {w: 2});\nO['__proto__']; // => {w: 2}\nO['w']; // => undefined\n```\nOther methods of `Dict` module are static equivalents of `Array.prototype` methods for dictionaries.\n\n[*Examples*](http://goo.gl/xFi1RH):\n```js\nvar dict = {a: 1, b: 2, c: 3};\n\nDict.forEach(dict, console.log, console);\n// => 1, 'a', {a: 1, b: 2, c: 3}\n// => 2, 'b', {a: 1, b: 2, c: 3}\n// => 3, 'c', {a: 1, b: 2, c: 3}\n\nDict.map(dict, function(it){\n return it * it;\n}); // => {a: 1, b: 4, c: 9}\n\nDict.mapPairs(dict, function(val, key){\n if(key != 'b')return [key + key, val * val];\n}); // => {aa: 1, cc: 9}\n\nDict.filter(dict, function(it){\n return it % 2;\n}); // => {a: 1, c: 3}\n\nDict.some(dict, function(it){\n return it === 2;\n}); // => true\n\nDict.every(dict, function(it){\n return it === 2;\n}); // => false\n\nDict.find(dict, function(it){\n return it > 2;\n}); // => 3\nDict.find(dict, function(it){\n return it > 4;\n}); // => undefined\n\nDict.findKey(dict, function(it){\n return it > 2;\n}); // => 'c'\nDict.findKey(dict, function(it){\n return it > 4;\n}); // => undefined\n\nDict.keyOf(dict, 2); // => 'b'\nDict.keyOf(dict, 4); // => undefined\n\nDict.includes(dict, 2); // => true\nDict.includes(dict, 4); // => false\n\nDict.reduce(dict, function(memo, it){\n return memo + it;\n}); // => 6\nDict.reduce(dict, function(memo, it){\n return memo + it;\n}, ''); // => '123'\n```\n#### Partial application\nModule [`core.function.part`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.function.part.js).\n```js\nFunction\n #part(...args | _) -> fn(...args)\n```\n\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js/core/function\ncore-js(/library)/fn/function/part\ncore-js(/library)/fn/function/virtual/part\ncore-js(/library)/fn/_\n```\n`Function#part` partial apply function without `this` binding. Uses global variable `_` (`core._` for builds without global namespace pollution) as placeholder and not conflict with `Underscore` / `LoDash`.\n\n[*Examples*](http://goo.gl/p9ZJ8K):\n```js\nvar fn1 = log.part(1, 2);\nfn1(3, 4); // => 1, 2, 3, 4\n\nvar fn2 = log.part(_, 2, _, 4);\nfn2(1, 3); // => 1, 2, 3, 4\n\nvar fn3 = log.part(1, _, _, 4);\nfn3(2, 3); // => 1, 2, 3, 4\n\nfn2(1, 3, 5); // => 1, 2, 3, 4, 5\nfn2(1); // => 1, 2, undefined, 4\n```\n#### Number Iterator\nModule [`core.number.iterator`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.number.iterator.js).\n```js\nNumber\n #@@iterator() -> iterator\n```\n\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core/number\ncore-js(/library)/fn/number/iterator\ncore-js(/library)/fn/number/virtual/iterator\n```\n[*Examples*](http://goo.gl/o45pCN):\n```js\nfor(var i of 3)console.log(i); // => 0, 1, 2\n\n[...10]; // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n\nArray.from(10, Math.random); // => [0.9817775336559862, 0.02720663254149258, ...]\n\nArray.from(10, function(it){\n return this + it * it;\n}, .42); // => [0.42, 1.42, 4.42, 9.42, 16.42, 25.42, 36.42, 49.42, 64.42, 81.42]\n```\n#### Escaping strings\nModules [`core.regexp.escape`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.regexp.escape.js), [`core.string.escape-html`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.string.escape-html.js) and [`core.string.unescape-html`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.string.unescape-html.js).\n```js\nRegExp\n .escape(str) -> str\nString\n #escapeHTML() -> str\n #unescapeHTML() -> str\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core/regexp\ncore-js(/library)/core/string\ncore-js(/library)/fn/regexp/escape\ncore-js(/library)/fn/string/escape-html\ncore-js(/library)/fn/string/unescape-html\ncore-js(/library)/fn/string/virtual/escape-html\ncore-js(/library)/fn/string/virtual/unescape-html\n```\n[*Examples*](http://goo.gl/6bOvsQ):\n```js\nRegExp.escape('Hello, []{}()*+?.\\\\^$|!'); // => 'Hello, \\[\\]\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|!'\n\n''.escapeHTML(); // => '<script>doSomething();</script>'\n'<script>doSomething();</script>'.unescapeHTML(); // => ''\n```\n#### delay\nModule [`core.delay`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.delay.js). [Promise](#ecmascript-6-promise)-returning delay function, [esdiscuss](https://esdiscuss.org/topic/promise-returning-delay-function).\n```js\ndelay(ms) -> promise\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/core/delay\ncore-js(/library)/fn/delay\n```\n[*Examples*](http://goo.gl/lbucba):\n```js\ndelay(1e3).then(() => console.log('after 1 sec'));\n\n(async () => {\n await delay(3e3);\n console.log('after 3 sec');\n\n while(await delay(3e3))console.log('each 3 sec');\n})();\n```\n#### Helpers for iterators\nModules [`core.is-iterable`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.is-iterable.js), [`core.get-iterator`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.get-iterator.js), [`core.get-iterator-method`](https://github.com/zloirock/core-js/blob/v2.5.7/modules/core.get-iterator-method.js) - helpers for check iterability / get iterator in the `library` version or, for example, for `arguments` object:\n```js\ncore\n .isIterable(var) -> bool\n .getIterator(iterable) -> iterator\n .getIteratorMethod(var) -> function | undefined\n```\n[*CommonJS entry points:*](#commonjs)\n```js\ncore-js(/library)/fn/is-iterable\ncore-js(/library)/fn/get-iterator\ncore-js(/library)/fn/get-iterator-method\n```\n[*Examples*](http://goo.gl/SXsM6D):\n```js\nvar list = (function(){\n return arguments;\n})(1, 2, 3);\n\nconsole.log(core.isIterable(list)); // true;\n\nvar iter = core.getIterator(list);\nconsole.log(iter.next().value); // 1\nconsole.log(iter.next().value); // 2\nconsole.log(iter.next().value); // 3\nconsole.log(iter.next().value); // undefined\n\ncore.getIterator({}); // TypeError: [object Object] is not iterable!\n\nvar iterFn = core.getIteratorMethod(list);\nconsole.log(typeof iterFn); // 'function'\nvar iter = iterFn.call(list);\nconsole.log(iter.next().value); // 1\nconsole.log(iter.next().value); // 2\nconsole.log(iter.next().value); // 3\nconsole.log(iter.next().value); // undefined\n\nconsole.log(core.getIteratorMethod({})); // undefined\n```\n\n## Missing polyfills\n- ES5 `JSON` is missing now only in IE7- and never will it be added to `core-js`, if you need it in these old browsers, many implementations are available, for example, [json3](https://github.com/bestiejs/json3).\n- ES6 `String#normalize` is not a very useful feature, but this polyfill will be very large. If you need it, you can use [unorm](https://github.com/walling/unorm/).\n- ES6 `Proxy` can't be polyfilled, but for Node.js / Chromium with additional flags you can try [harmony-reflect](https://github.com/tvcutsem/harmony-reflect) for adapt old style `Proxy` API to final ES6 version.\n- ES6 logic for `@@isConcatSpreadable` and `@@species` (in most places) can be polyfilled without problems, but it will cause a serious slowdown in popular cases in some engines. It will be polyfilled when it will be implemented in modern engines.\n- ES7 `SIMD`. `core-js` doesn't add polyfill of this feature because of large size and some other reasons. You can use [this polyfill](https://github.com/tc39/ecmascript_simd/blob/master/src/ecmascript_simd.js).\n- `window.fetch` is not a cross-platform feature, in some environments it makes no sense. For this reason, I don't think it should be in `core-js`. Looking at a large number of requests it *may be* added in the future. Now you can use, for example, [this polyfill](https://github.com/github/fetch).\n- ECMA-402 `Intl` is missed because of size. You can use [this polyfill](https://github.com/andyearnshaw/Intl.js/).\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/zloirock/core-js/issues" + }, + "homepage": "https://github.com/zloirock/core-js#readme", + "_id": "core-js@2.5.7", + "_requested": { + "type": "version", + "registry": true, + "raw": "core-js@2.5.7", + "name": "core-js", + "escapedName": "core-js", + "rawSpec": "2.5.7", + "saveSpec": "[Circular]", + "fetchSpec": "2.5.7" + }, + "_spec": "2.5.7", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "core-js@2.5.7", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/core-js", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-core@2.6.1", + "requires": "core-js@2.4.1" + } + ] + }, + "peerMissing": true + }, + "hammerjs": { + "version": "2.0.8", + "from": "hammerjs@2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz" + }, + "minimatch-browser": { + "version": "1.0.0", + "from": "minimatch-browser@1.0.0", + "resolved": "https://registry.npmjs.org/minimatch-browser/-/minimatch-browser-1.0.0.tgz" + }, + "moment": { + "version": "2.22.2", + "from": "moment@2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz" + }, + "moment-es6": { + "version": "1.0.0", + "from": "moment-es6@1.0.0", + "resolved": "https://registry.npmjs.org/moment-es6/-/moment-es6-1.0.0.tgz" + }, + "pdfjs-dist": { + "required": { + "name": "pdfjs-dist", + "version": "2.0.489", + "main": "build/pdf.js", + "description": "Generic build of Mozilla's PDF.js library.", + "keywords": [ + "Mozilla", + "pdf", + "pdf.js" + ], + "homepage": "http://mozilla.github.io/pdf.js/", + "bugs": { + "url": "https://github.com/mozilla/pdf.js/issues" + }, + "license": "Apache-2.0", + "dependencies": { + "node-ensure": { + "name": "node-ensure", + "version": "0.0.0", + "description": "Async module-loading library and protocol for bundlers/loaders targeting isomorphic apps and Node.js.", + "main": "index.js", + "browser": { + "./index.js": "./browser.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "require", + "ensure", + "dynamic", + "module", + "loader", + "bundler", + "async" + ], + "author": { + "name": "Carl A. Bauer" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bauerca/node-ensure.git" + }, + "bugs": { + "url": "https://github.com/bauerca/node-ensure/issues" + }, + "homepage": "https://github.com/bauerca/node-ensure", + "license": "MIT", + "_resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "_integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=", + "_from": "node-ensure@0.0.0", + "readme": "# node-ensure\n\nA simple library that shims asynchronous module loading into Node.js to help\nwith building module bundlers and client-side loaders for isomorphic apps.\nThis library is super slim (read the source) and mainly represents an agreement\nbetween developers and users of a particular bundler/loader.\n\nNOTE: This module is *not* compatible with Browserify. It is for developers that\nwant to split their bundles for the client. For example, see\n[dynapack](https://github.com/bauerca/dynapack).\n\n*Syntax is inspired by the CommonJS\n[Modules/Async/A](http://wiki.commonjs.org/wiki/Modules/Async/A) proposal.*\n\n\n## Installation\n\n```\nnpm install node-ensure\n```\n\n## Example\n\n```js\nvar ensure = require('node-ensure');\n\nensure(['superagent', 'react'], function(err) {\n var request = require('superagent');\n var React = require('react');\n\n // Do the coolest of things.\n});\n```\n\nIf your bundler needs `require.ensure`, do this instead:\n\n```js\nrequire.ensure = require('node-ensure');\n\nrequire.ensure(['superagent', 'react'], function(err) {\n var request = require('superagent');\n var React = require('react');\n\n // Do the coolest of things.\n});\n```\n\n## Usage\n\nThe returned function takes an array of strings and a callback, in that\norder (see the example above). The callback takes a single error argument, which\nusually indicates a network problem or other client-side loader-specific runtime\nerror (it should never receive an error when used in Node.js).\n\nWithin the ensure callback, load modules with standard require calls.\n\n## Bundlers/Loaders\n\nThis library primarily constitutes an agreement between users and developers of\nmodule bundlers and (client-side) loaders. The users agree to the usage instructions\nsupplied above.\n\nBundlers and/or loaders must adhere to the following:\n\n- The bundler/loader uses the package.json `\"browser\"` property for replacing\n server-only modules with browser-ready counterparts (a la Browserify).\n- The `require` function passed to a module must have a `require.ensure`\n function.\n- Each `require.ensure` must accept the same arguments as described in [Usage](#usage).\n- Each `require.ensure` must not access variables via closure unless those variables\n are shared by **all** `require.ensure` functions.\n- Each `require.ensure` may access properties\n on `this`. However, this assumes users have attached node-ensure to `require` via\n `require.ensure = require('node-ensure')`.\n\nHappy loading!\n\n# License\n\nMIT\n", + "readmeFilename": "README.md", + "_id": "node-ensure@0.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "node-ensure@0.0.0", + "name": "node-ensure", + "escapedName": "node-ensure", + "rawSpec": "0.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "0.0.0" + }, + "_spec": "0.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "node-ensure@0.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/node-ensure", + "error": "[Circular]", + "extraneous": false + }, + "worker-loader": { + "name": "worker-loader", + "version": "1.1.1", + "author": { + "name": "Tobias Koppers @sokra" + }, + "description": "worker loader module for webpack", + "main": "dist/cjs.js", + "files": [ + "dist" + ], + "engines": { + "node": ">= 4.8 < 5.0.0 || >= 5.10" + }, + "scripts": { + "start": "npm run build -- -w", + "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/**/*.test.js' --copy-files", + "clean": "del-cli dist", + "lint": "eslint --cache src test", + "lint-staged": "lint-staged", + "prebuild": "npm run clean", + "prepare": "npm run build", + "release": "standard-version", + "security": "nsp check", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage", + "travis:lint": "npm run lint && npm run security", + "travis:test": "npm run test -- --runInBand", + "travis:coverage": "npm run test:coverage -- --runInBand", + "appveyor:test": "npm run test", + "defaults": "webpack-defaults" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + }, + "dependencies": { + "loader-utils": { + "name": "loader-utils", + "version": "1.1.0", + "author": { + "name": "Tobias Koppers @sokra" + }, + "description": "utils for webpack loaders", + "dependencies": { + "big.js": { + "name": "big.js", + "description": "A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic", + "version": "3.2.0", + "keywords": [ + "arbitrary", + "precision", + "arithmetic", + "big", + "number", + "decimal", + "float", + "biginteger", + "bigdecimal", + "bignumber", + "bigint", + "bignum" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/MikeMcl/big.js.git" + }, + "main": "big.js", + "author": { + "name": "Michael Mclaughlin", + "email": "M8ch88l@gmail.com" + }, + "bugs": { + "url": "https://github.com/MikeMcl/big.js/issues" + }, + "engines": { + "node": "*" + }, + "license": "MIT", + "scripts": { + "test": "node ./test/every-test.js", + "build": "uglifyjs big.js --source-map doc/big.js.map -c -m -o big.min.js --preamble \"/* big.js v3.2.0 https://github.com/MikeMcl/big.js/LICENCE */\"" + }, + "files": [ + "big.js", + "big.min.js" + ], + "_resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "_integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "_from": "big.js@3.2.0", + "readme": "\r\n# big.js #\r\n\r\nA small, fast JavaScript library for arbitrary-precision decimal arithmetic.\r\n\r\nThe little sister to [bignumber.js](https://github.com/MikeMcl/bignumber.js/).\r\nSee also [decimal.js](https://github.com/MikeMcl/decimal.js/), and [here](https://github.com/MikeMcl/big.js/wiki) for the difference between them.\r\n\r\n## Features\r\n\r\n - Faster, smaller and easier-to-use than JavaScript versions of Java's BigDecimal\r\n - Only 2.7 KB minified and gzipped\r\n - Simple API\r\n - Replicates the `toExponential`, `toFixed` and `toPrecision` methods of JavaScript's Number type\r\n - Includes a `sqrt` method\r\n - Stores values in an accessible decimal floating point format\r\n - No dependencies\r\n - Comprehensive [documentation](http://mikemcl.github.io/big.js/) and test set\r\n\r\n## Load\r\n\r\nThe library is the single JavaScript file *big.js* (or *big.min.js*, which is *big.js* minified).\r\n\r\nIt can be loaded via a script tag in an HTML document for the browser\r\n\r\n \r\n\r\nor as a CommonJS, [Node.js](http://nodejs.org) or AMD module using `require`.\r\n\r\n var Big = require('big.js');\r\n\r\nFor Node.js, the library is available from the npm registry:\r\n\r\n $ npm install big.js\r\n\r\n\r\n\r\n## Use\r\n\r\n*In all examples below, `var`, semicolons and `toString` calls are not shown.\r\nIf a commented-out value is in quotes it means `toString` has been called on the preceding expression.*\r\n\r\nThe library exports a single function: Big, the constructor of Big number instances.\r\nIt accepts a value of type Number, String or Big number Object.\r\n\r\n x = new Big(123.4567)\r\n y = Big('123456.7e-3') // 'new' is optional\r\n z = new Big(x)\r\n x.eq(y) && x.eq(z) && y.eq(z) // true\r\n\r\nA Big number is immutable in the sense that it is not changed by its methods.\r\n\r\n 0.3 - 0.1 // 0.19999999999999998\r\n x = new Big(0.3)\r\n x.minus(0.1) // \"0.2\"\r\n x // \"0.3\"\r\n\r\nThe methods that return a Big number can be chained.\r\n\r\n x.div(y).plus(z).times(9).minus('1.234567801234567e+8').plus(976.54321).div('2598.11772')\r\n x.sqrt().div(y).pow(3).gt(y.mod(z)) // true\r\n\r\nLike JavaScript's Number type, there are `toExponential`, `toFixed` and `toPrecision` methods.\r\n\r\n x = new Big(255.5)\r\n x.toExponential(5) // \"2.55500e+2\"\r\n x.toFixed(5) // \"255.50000\"\r\n x.toPrecision(5) // \"255.50\"\r\n\r\nThe maximum number of decimal places and the rounding mode used to round the results of the `div`, `sqrt` and `pow`\r\n(with negative exponent) methods is determined by the value of the `DP` and `RM` properties of the `Big` number constructor. \r\n\r\nThe other methods always give the exact result. \r\n\r\n(From *v3.0.0*, multiple Big number constructors can be created, see Change Log below.)\r\n\r\n Big.DP = 10\r\n Big.RM = 1\r\n\r\n x = new Big(2);\r\n y = new Big(3);\r\n z = x.div(y) // \"0.6666666667\"\r\n z.sqrt() // \"0.8164965809\"\r\n z.pow(-3) // \"3.3749999995\"\r\n z.times(z) // \"0.44444444448888888889\"\r\n z.times(z).round(10) // \"0.4444444445\"\r\n\r\n\r\nThe value of a Big number is stored in a decimal floating point format in terms of a coefficient, exponent and sign.\r\n\r\n x = new Big(-123.456);\r\n x.c // [1,2,3,4,5,6] coefficient (i.e. significand)\r\n x.e // 2 exponent\r\n x.s // -1 sign\r\n\r\nFor further information see the [API](http://mikemcl.github.io/big.js/) reference from the *doc* folder.\r\n\r\n## Test\r\n\r\nThe *test* directory contains the test scripts for each Big number method.\r\n\r\nThe tests can be run with Node or a browser.\r\n\r\nTo test a single method, from a command-line shell at the *test* directory, use e.g.\r\n\r\n $ node toFixed\r\n\r\nTo test all the methods\r\n\r\n $ node every-test\r\n\r\nFor the browser, see *single-test.html* and *every-test.html* in the *test/browser* directory.\r\n\r\n*big-vs-number.html* enables some of the methods of big.js to be compared with those of JavaScript's Number type.\r\n\r\n## Performance\r\n\r\nThe *perf* directory contains two applications and a *lib* directory containing the BigDecimal libraries used by both.\r\n\r\n*big-vs-bigdecimal.html* tests the performance of big.js against the JavaScript translations of two versions of BigDecimal, its use should be more or less self-explanatory.\r\n(The GWT version doesn't work in IE 6.)\r\n\r\n* GWT: java.math.BigDecimal\r\n\r\n* ICU4J: com.ibm.icu.math.BigDecimal\r\n\r\n\r\nThe BigDecimal in Node's npm registry is the GWT version. Despite its seeming popularity I have found it to have some serious bugs, see the Node script *perf/lib/bigdecimal_GWT/bugs.js* for examples of flaws in its *remainder*, *divide* and *compareTo* methods.\r\n\r\n*bigtime.js* is a Node command-line application which tests the performance of big.js against the GWT version of\r\nBigDecimal from the npm registry.\r\n\r\nFor example, to compare the time taken by the big.js `plus` method and the BigDecimal `add` method:\r\n\r\n $ node bigtime plus 10000 40\r\n\r\nThis will time 10000 calls to each, using operands of up to 40 random digits and will check that the results match.\r\n\r\nFor help:\r\n\r\n $ node bigtime -h\r\n\r\n## Build\r\n\r\nI.e. minify.\r\n\r\nFor Node, if uglify-js is installed globally ( `npm install uglify-js -g` ) then\r\n\r\n uglifyjs -o ./big.min.js ./big.js\r\n\r\nwill create *big.min.js*.\r\n\r\nThe *big.min.js* already present was created with *Microsoft Ajax Minifier 5.11*.\r\n\r\n## TypeScript\r\n\r\nThe [DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped) project has a TypeScript [definitions file](https://github.com/borisyankov/DefinitelyTyped/blob/master/big.js/big.js.d.ts) for big.js.\r\n\r\nThe definitions file can be added to your project via the [big.js.TypeScript.DefinitelyTyped](https://www.nuget.org/packages/big.js.TypeScript.DefinitelyTyped/0.0.1) NuGet package or via [tsd](http://definitelytyped.org/tsd/).\r\n\r\n tsd query big.js --action install\r\n\r\nAny questions about the TypeScript definitions file should be addressed to the DefinitelyTyped project.\r\n\r\n## Feedback\r\n\r\nFeedback is welcome.\r\n\r\nBugs/comments/questions?\r\nOpen an issue, or email\r\n\r\nMichael\r\nM8ch88l@gmail.com\r\n\r\nBitcoin donation to:\r\n**1DppGRQSjVSMgGxuygDEHQuWEdTiVEzJYG**\r\nThank you\r\n\r\n## Licence\r\n\r\nSee LICENCE.\r\n\r\n## Change Log\r\n\r\n####3.2.0\r\n\r\n* 14/09/17 Aid ES6 import.\r\n\r\n####3.1.3\r\n\r\n* Minor documentation updates.\r\n\r\n####3.1.2\r\n\r\n* README typo.\r\n\r\n####3.1.1\r\n\r\n* API documentation update, including FAQ additions.\r\n\r\n####3.1.0\r\n\r\n* Renamed and exposed `TO_EXP_NEG` and `TO_EXP_POS` as `Big.E_NEG` and\r\n `Big.E_POS`.\r\n\r\n####3.0.2\r\n\r\n* Remove *.npmignore*, use `files` field in *package.json* instead.\r\n\r\n####3.0.1\r\n\r\n* Added `sub`, `add` and `mul` aliases.\r\n* Clean-up after lint.\r\n\r\n####3.0.0\r\n\r\n* 10/12/14 Added [multiple constructor functionality](http://mikemcl.github.io/big.js/#faq).\r\n* No breaking changes or other additions, but a major code reorganisation,\r\n so *v3* seemed appropriate.\r\n\r\n####2.5.2\r\n\r\n* 1/11/14 Added bower.json.\r\n\r\n####2.5.1\r\n\r\n* 8/06/14 Amend README requires.\r\n\r\n####2.5.0\r\n\r\n* 26/01/14 Added `toJSON` method so serialization uses `toString`.\r\n\r\n####2.4.1\r\n\r\n* 17/10/13 Conform signed zero to IEEEE 754 (2008).\r\n\r\n####2.4.0\r\n\r\n* 19/09/13 Throw instances of `Error`.\r\n\r\n####2.3.0\r\n\r\n* 16/09/13 Added `cmp` method.\r\n\r\n####2.2.0\r\n\r\n* 11/07/13 Added 'round up' mode.\r\n\r\n####2.1.0\r\n\r\n* 26/06/13 Allow e.g. `.1` and `2.`.\r\n\r\n####2.0.0\r\n\r\n* 12/05/13 Added `abs` method and replaced `cmp` with `eq`, `gt`, `gte`, `lt`, and `lte` methods.\r\n\r\n####1.0.1\r\n\r\n* Changed default value of MAX_DP to 1E6\r\n\r\n####1.0.0\r\n\r\n* 7/11/2012 Initial release\r\n", + "readmeFilename": "README.md", + "homepage": "https://github.com/MikeMcl/big.js#readme", + "_id": "big.js@3.2.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "big.js@3.2.0", + "name": "big.js", + "escapedName": "big.js", + "rawSpec": "3.2.0", + "saveSpec": "[Circular]", + "fetchSpec": "3.2.0" + }, + "_spec": "3.2.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "big.js@3.2.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/big.js", + "error": "[Circular]", + "extraneous": false + }, + "emojis-list": { + "name": "emojis-list", + "description": "Complete list of standard emojis.", + "homepage": "https://github.com/Kikobeats/emojis-list", + "version": "2.1.0", + "main": "./index.js", + "author": { + "name": "Kiko Beats", + "email": "josefrancisco.verdu@gmail.com", + "url": "https://github.com/Kikobeats" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kikobeats/emojis-list.git" + }, + "bugs": { + "url": "https://github.com/Kikobeats/emojis-list/issues" + }, + "keywords": [ + "archive", + "complete", + "emoji", + "list", + "standard" + ], + "devDependencies": { + "acho": "latest", + "browserify": "latest", + "cheerio": "latest", + "got": ">=5 <6", + "gulp": "latest", + "gulp-header": "latest", + "gulp-uglify": "latest", + "gulp-util": "latest", + "standard": "latest", + "vinyl-buffer": "latest", + "vinyl-source-stream": "latest" + }, + "engines": { + "node": ">= 0.10" + }, + "files": [ + "index.js" + ], + "scripts": { + "pretest": "standard update.js", + "test": "echo 'YOLO'", + "update": "node update" + }, + "license": "MIT", + "_resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "_integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "_from": "emojis-list@2.1.0", + "readme": "# emojis-list\n\n[![Dependency status](http://img.shields.io/david/Kikobeats/emojis-list.svg?style=flat-square)](https://david-dm.org/Kikobeats/emojis-list)\n[![Dev Dependencies Status](http://img.shields.io/david/dev/Kikobeats/emojis-list.svg?style=flat-square)](https://david-dm.org/Kikobeats/emojis-list#info=devDependencies)\n[![NPM Status](http://img.shields.io/npm/dm/emojis-list.svg?style=flat-square)](https://www.npmjs.org/package/emojis-list)\n[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/kikobeats)\n\n> Complete list of standard Unicode Hex Character Code that represent emojis.\n\n**NOTE**: The lists is related with the Unicode Hex Character Code. The representation of the emoji depend of the system. Will be possible that the system don't have all the representations.\n\n## Install\n\n```bash\nnpm install emojis-list --save\n```\n\nIf you want to use in the browser (powered by [Browserify](http://browserify.org/)):\n\n```bash\nbower install emojis-list --save\n```\n\nand later link in your HTML:\n\n```html\n\n```\n\n## Usage\n\n```\nvar emojis = require('emojis-list');\nconsole.log(emojis[0]);\n// => 🀄\n```\n\n## Related\n\n* [emojis-unicode](https://github.com/Kikobeats/emojis-unicode) – Complete list of standard Unicode codes that represent emojis.\n* [emojis-keywords](https://github.com/Kikobeats/emojis-keywords) – Complete list of am emoji shortcuts.\n* [is-emoji-keyword](https://github.com/Kikobeats/is-emoji-keyword) – Check if a word is a emoji shortcut.\n* [is-standard-emoji](https://github.com/kikobeats/is-standard-emoji) – Simply way to check if a emoji is a standard emoji.\n* [trim-emoji](https://github.com/Kikobeats/trim-emoji) – Deletes ':' from the begin and the end of an emoji shortcut.\n\n## License\n\nMIT © [Kiko Beats](http://www.kikobeats.com)\n", + "readmeFilename": "README.md", + "_id": "emojis-list@2.1.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "emojis-list@2.1.0", + "name": "emojis-list", + "escapedName": "emojis-list", + "rawSpec": "2.1.0", + "saveSpec": "[Circular]", + "fetchSpec": "2.1.0" + }, + "_spec": "2.1.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "emojis-list@2.1.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/emojis-list", + "error": "[Circular]", + "extraneous": false + }, + "json5": { + "name": "json5", + "version": "0.5.1", + "description": "JSON for the ES5 era.", + "keywords": [ + "json", + "es5" + ], + "author": { + "name": "Aseem Kishore", + "email": "aseem.kishore@gmail.com" + }, + "contributors": [ + { + "name": "Max Nanasy", + "email": "max.nanasy@gmail.com" + }, + { + "name": "Andrew Eisenberg", + "email": "andrew@eisenberg.as" + }, + { + "name": "Jordan Tucker", + "email": "jordanbtucker@gmail.com" + } + ], + "main": "lib/json5.js", + "bin": { + "json5": "lib/cli.js" + }, + "files": [ + "lib/" + ], + "dependencies": {}, + "devDependencies": { + "gulp": "^3.9.1", + "gulp-jshint": "^2.0.1", + "jshint": "^2.9.3", + "jshint-stylish": "^2.2.1", + "mocha": "^3.1.0" + }, + "scripts": { + "build": "node ./lib/cli.js -c package.json5", + "test": "mocha --ui exports --reporter spec" + }, + "homepage": "http://json5.org/", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/aseemk/json5.git" + }, + "_resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "_integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "_from": "json5@0.5.1", + "readme": "# JSON5 – Modern JSON\n\n[![Build Status](https://travis-ci.org/json5/json5.svg)](https://travis-ci.org/json5/json5)\n\nJSON is an excellent data format, but we think it can be better.\n\n**JSON5 is a proposed extension to JSON** that aims to make it easier for\n*humans to write and maintain* by hand. It does this by adding some minimal\nsyntax features directly from ECMAScript 5.\n\nJSON5 remains a **strict subset of JavaScript**, adds **no new data types**,\nand **works with all existing JSON content**.\n\nJSON5 is *not* an official successor to JSON, and JSON5 content may *not*\nwork with existing JSON parsers. For this reason, JSON5 files use a new .json5\nextension. *(TODO: new MIME type needed too.)*\n\nThe code here is a **reference JavaScript implementation** for both Node.js\nand all browsers. It’s based directly off of Douglas Crockford’s own [JSON\nimplementation][json_parse.js], and it’s both robust and secure.\n\n\n## Why\n\nJSON isn’t the friendliest to *write*. Keys need to be quoted, objects and\narrays can’t have trailing commas, and comments aren’t allowed — even though\nnone of these are the case with regular JavaScript today.\n\nThat was fine when JSON’s goal was to be a great data format, but JSON’s usage\nhas expanded beyond *machines*. JSON is now used for writing [configs][ex1],\n[manifests][ex2], even [tests][ex3] — all by *humans*.\n\n[ex1]: http://plovr.com/docs.html\n[ex2]: https://www.npmjs.org/doc/files/package.json.html\n[ex3]: http://code.google.com/p/fuzztester/wiki/JSONFileFormat\n\nThere are other formats that are human-friendlier, like YAML, but changing\nfrom JSON to a completely different format is undesirable in many cases.\nJSON5’s aim is to remain close to JSON and JavaScript.\n\n\n## Features\n\nThe following is the exact list of additions to JSON’s syntax introduced by\nJSON5. **All of these are optional**, and **all of these come from ES5**.\n\n### Objects\n\n- Object keys can be unquoted if they’re valid [identifiers][mdn_variables].\n Yes, even reserved keywords (like `default`) are valid unquoted keys in ES5\n [[§11.1.5](http://es5.github.com/#x11.1.5), [§7.6](http://es5.github.com/#x7.6)].\n ([More info](https://mathiasbynens.be/notes/javascript-identifiers))\n\n *(TODO: Unicode characters and escape sequences aren’t yet supported in this\n implementation.)*\n \n- Object keys can also be single-quoted.\n\n- Objects can have trailing commas.\n\n[mdn_variables]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables\n\n### Arrays\n\n- Arrays can have trailing commas.\n\n### Strings\n\n- Strings can be single-quoted.\n\n- Strings can be split across multiple lines; just prefix each newline with a\n backslash. [ES5 [§7.8.4](http://es5.github.com/#x7.8.4)]\n\n### Numbers\n\n- Numbers can be hexadecimal (base 16).\n\n- Numbers can begin or end with a (leading or trailing) decimal point.\n\n- Numbers can include `Infinity`, `-Infinity`, `NaN`, and `-NaN`.\n\n- Numbers can begin with an explicit plus sign.\n\n### Comments\n\n- Both inline (single-line) and block (multi-line) comments are allowed.\n\n\n## Example\n\nThe following is a contrived example, but it illustrates most of the features:\n\n```js\n{\n foo: 'bar',\n while: true,\n\n this: 'is a \\\nmulti-line string',\n\n // this is an inline comment\n here: 'is another', // inline comment\n\n /* this is a block comment\n that continues on another line */\n\n hex: 0xDEADbeef,\n half: .5,\n delta: +10,\n to: Infinity, // and beyond!\n\n finally: 'a trailing comma',\n oh: [\n \"we shouldn't forget\",\n 'arrays can have',\n 'trailing commas too',\n ],\n}\n```\n\nThis implementation’s own [package.json5](package.json5) is more realistic:\n\n```js\n// This file is written in JSON5 syntax, naturally, but npm needs a regular\n// JSON file, so compile via `npm run build`. Be sure to keep both in sync!\n\n{\n name: 'json5',\n version: '0.5.0',\n description: 'JSON for the ES5 era.',\n keywords: ['json', 'es5'],\n author: 'Aseem Kishore ',\n contributors: [\n // TODO: Should we remove this section in favor of GitHub's list?\n // https://github.com/aseemk/json5/contributors\n 'Max Nanasy ',\n 'Andrew Eisenberg ',\n 'Jordan Tucker ',\n ],\n main: 'lib/json5.js',\n bin: 'lib/cli.js',\n files: [\"lib/\"],\n dependencies: {},\n devDependencies: {\n gulp: \"^3.9.1\",\n 'gulp-jshint': \"^2.0.0\",\n jshint: \"^2.9.1\",\n 'jshint-stylish': \"^2.1.0\",\n mocha: \"^2.4.5\"\n },\n scripts: {\n build: 'node ./lib/cli.js -c package.json5',\n test: 'mocha --ui exports --reporter spec',\n // TODO: Would it be better to define these in a mocha.opts file?\n },\n homepage: 'http://json5.org/',\n license: 'MIT',\n repository: {\n type: 'git',\n url: 'https://github.com/aseemk/json5.git',\n },\n}\n```\n\n\n## Community\n\nJoin the [Google Group](http://groups.google.com/group/json5) if you’re\ninterested in JSON5 news, updates, and general discussion.\nDon’t worry, it’s very low-traffic.\n\nThe [GitHub wiki](https://github.com/aseemk/json5/wiki) is a good place to track\nJSON5 support and usage. Contribute freely there!\n\n[GitHub Issues](https://github.com/aseemk/json5/issues) is the place to\nformally propose feature requests and report bugs. Questions and general\nfeedback are better directed at the Google Group.\n\n\n## Usage\n\nThis JavaScript implementation of JSON5 simply provides a `JSON5` object just\nlike the native ES5 `JSON` object.\n\nTo use from Node:\n\n```sh\nnpm install json5\n```\n\n```js\nvar JSON5 = require('json5');\n```\n\nTo use in the browser (adds the `JSON5` object to the global namespace):\n\n```html\n\n```\n\nThen in both cases, you can simply replace native `JSON` calls with `JSON5`:\n\n```js\nvar obj = JSON5.parse('{unquoted:\"key\",trailing:\"comma\",}');\nvar str = JSON5.stringify(obj);\n```\n\n`JSON5.parse` supports all of the JSON5 features listed above (*TODO: except\nUnicode*), as well as the native [`reviver` argument][json-parse].\n\n[json-parse]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse\n\n`JSON5.stringify` mainly avoids quoting keys where possible, but we hope to\nkeep expanding it in the future (e.g. to also output trailing commas).\nIt supports the native [`replacer` and `space` arguments][json-stringify],\nas well. *(TODO: Any implemented `toJSON` methods aren’t used today.)*\n\n[json-stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\n\n\n### Extras\n\nIf you’re running this on Node, you can also register a JSON5 `require()` hook\nto let you `require()` `.json5` files just like you can `.json` files:\n\n```js\nrequire('json5/lib/require');\nrequire('./path/to/foo'); // tries foo.json5 after foo.js, foo.json, etc.\nrequire('./path/to/bar.json5');\n```\n\nThis module also provides a `json5` executable (requires Node) for converting\nJSON5 files to JSON:\n\n```sh\njson5 -c path/to/foo.json5 # generates path/to/foo.json\n```\n\n\n## Development\n\n```sh\ngit clone git://github.com/aseemk/json5.git\ncd json5\nnpm install\nnpm test\n```\n\nAs the `package.json5` file states, be sure to run `npm run build` on changes\nto `package.json5`, since npm requires `package.json`.\n\nFeel free to [file issues](https://github.com/aseemk/json5/issues) and submit\n[pull requests](https://github.com/aseemk/json5/pulls) — contributions are\nwelcome. If you do submit a pull request, please be sure to add or update the\ntests, and ensure that `npm test` continues to pass.\n\n\n## License\n\nMIT. See [LICENSE.md](./LICENSE.md) for details.\n\n\n## Credits\n\n[Michael Bolin](http://bolinfest.com/) independently arrived at and published\nsome of these same ideas with awesome explanations and detail.\nRecommended reading:\n[Suggested Improvements to JSON](http://bolinfest.com/essays/json.html)\n\n[Douglas Crockford](http://www.crockford.com/) of course designed and built\nJSON, but his state machine diagrams on the [JSON website](http://json.org/),\nas cheesy as it may sound, gave me motivation and confidence that building a\nnew parser to implement these ideas this was within my reach!\nThis code is also modeled directly off of Doug’s open-source [json_parse.js][]\nparser. I’m super grateful for that clean and well-documented code.\n\n[json_parse.js]: https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js\n\n[Max Nanasy](https://github.com/MaxNanasy) has been an early and prolific\nsupporter, contributing multiple patches and ideas. Thanks Max!\n\n[Andrew Eisenberg](https://github.com/aeisenberg) has contributed the\n`stringify` method.\n\n[Jordan Tucker](https://github.com/jordanbtucker) has aligned JSON5 more closely\nwith ES5 and is actively maintaining this project.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/aseemk/json5/issues" + }, + "_id": "json5@0.5.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "json5@0.5.1", + "name": "json5", + "escapedName": "json5", + "rawSpec": "0.5.1", + "saveSpec": "[Circular]", + "fetchSpec": "0.5.1" + }, + "_spec": "0.5.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "json5@0.5.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/json5", + "error": "[Circular]", + "extraneous": false + } + }, + "scripts": { + "test": "mocha", + "posttest": "npm run lint", + "lint": "eslint lib test", + "travis": "npm run cover -- --report lcovonly", + "cover": "istanbul cover -x *.runtime.js node_modules/mocha/bin/_mocha", + "release": "npm test && standard-version" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/webpack/loader-utils.git" + }, + "engines": { + "node": ">=4.0.0" + }, + "devDependencies": { + "coveralls": "^2.11.2", + "eslint": "^3.15.0", + "eslint-plugin-node": "^4.0.1", + "istanbul": "^0.3.14", + "mocha": "^1.21.4", + "standard-version": "^4.0.0" + }, + "main": "lib/index.js", + "files": [ + "lib" + ], + "_resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "_integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "_from": "loader-utils@1.1.0", + "readme": "# loader-utils\n\n## Methods\n\n### `getOptions`\n\nRecommended way to retrieve the options of a loader invocation:\n\n```javascript\n// inside your loader\nconst options = loaderUtils.getOptions(this);\n```\n\n1. If `this.query` is a string:\n\t- Tries to parse the query string and returns a new object\n\t- Throws if it's not a valid query string\n2. If `this.query` is object-like, it just returns `this.query`\n3. In any other case, it just returns `null`\n\n**Please note:** The returned `options` object is *read-only*. It may be re-used across multiple invocations.\nIf you pass it on to another library, make sure to make a *deep copy* of it:\n\n```javascript\nconst options = Object.assign(\n\t{},\n\tloaderUtils.getOptions(this), // it is safe to pass null to Object.assign()\n\tdefaultOptions\n);\n// don't forget nested objects or arrays\noptions.obj = Object.assign({}, options.obj); \noptions.arr = options.arr.slice();\nsomeLibrary(options);\n```\n\n[clone-deep](https://www.npmjs.com/package/clone-deep) is a good library to make a deep copy of the options.\n\n#### Options as query strings\n\nIf the loader options have been passed as loader query string (`loader?some¶ms`), the string is parsed by using [`parseQuery`](#parsequery).\n\n### `parseQuery`\n\nParses a passed string (e.g. `loaderContext.resourceQuery`) as a query string, and returns an object.\n\n``` javascript\nconst params = loaderUtils.parseQuery(this.resourceQuery); // resource: `file?param1=foo`\nif (params.param1 === \"foo\") {\n\t// do something\n}\n```\n\nThe string is parsed like this:\n\n``` text\n -> Error\n? -> {}\n?flag -> { flag: true }\n?+flag -> { flag: true }\n?-flag -> { flag: false }\n?xyz=test -> { xyz: \"test\" }\n?xyz=1 -> { xyz: \"1\" } // numbers are NOT parsed\n?xyz[]=a -> { xyz: [\"a\"] }\n?flag1&flag2 -> { flag1: true, flag2: true }\n?+flag1,-flag2 -> { flag1: true, flag2: false }\n?xyz[]=a,xyz[]=b -> { xyz: [\"a\", \"b\"] }\n?a%2C%26b=c%2C%26d -> { \"a,&b\": \"c,&d\" }\n?{data:{a:1},isJSON5:true} -> { data: { a: 1 }, isJSON5: true }\n```\n\n### `stringifyRequest`\n\nTurns a request into a string that can be used inside `require()` or `import` while avoiding absolute paths.\nUse it instead of `JSON.stringify(...)` if you're generating code inside a loader.\n\n**Why is this necessary?** Since webpack calculates the hash before module paths are translated into module ids, we must avoid absolute paths to ensure\nconsistent hashes across different compilations.\n\nThis function:\n\n- resolves absolute requests into relative requests if the request and the module are on the same hard drive\n- replaces `\\` with `/` if the request and the module are on the same hard drive\n- won't change the path at all if the request and the module are on different hard drives\n- applies `JSON.stringify` to the result\n\n```javascript\nloaderUtils.stringifyRequest(this, \"./test.js\");\n// \"\\\"./test.js\\\"\"\n\nloaderUtils.stringifyRequest(this, \".\\\\test.js\");\n// \"\\\"./test.js\\\"\"\n\nloaderUtils.stringifyRequest(this, \"test\");\n// \"\\\"test\\\"\"\n\nloaderUtils.stringifyRequest(this, \"test/lib/index.js\");\n// \"\\\"test/lib/index.js\\\"\"\n\nloaderUtils.stringifyRequest(this, \"otherLoader?andConfig!test?someConfig\");\n// \"\\\"otherLoader?andConfig!test?someConfig\\\"\"\n\nloaderUtils.stringifyRequest(this, require.resolve(\"test\"));\n// \"\\\"../node_modules/some-loader/lib/test.js\\\"\"\n\nloaderUtils.stringifyRequest(this, \"C:\\\\module\\\\test.js\");\n// \"\\\"../../test.js\\\"\" (on Windows, in case the module and the request are on the same drive)\n\nloaderUtils.stringifyRequest(this, \"C:\\\\module\\\\test.js\");\n// \"\\\"C:\\\\module\\\\test.js\\\"\" (on Windows, in case the module and the request are on different drives)\n\nloaderUtils.stringifyRequest(this, \"\\\\\\\\network-drive\\\\test.js\");\n// \"\\\"\\\\\\\\network-drive\\\\\\\\test.js\\\"\" (on Windows, in case the module and the request are on different drives)\n```\n\n### `urlToRequest`\n\nConverts some resource URL to a webpack module request.\n\n```javascript\nconst url = \"path/to/module.js\";\nconst request = loaderUtils.urlToRequest(url); // \"./path/to/module.js\"\n```\n\n#### Module URLs\n\nAny URL containing a `~` will be interpreted as a module request. Anything after the `~` will be considered the request path.\n\n```javascript\nconst url = \"~path/to/module.js\";\nconst request = loaderUtils.urlToRequest(url); // \"path/to/module.js\"\n```\n\n#### Root-relative URLs\n\nURLs that are root-relative (start with `/`) can be resolved relative to some arbitrary path by using the `root` parameter:\n\n```javascript\nconst url = \"/path/to/module.js\";\nconst root = \"./root\";\nconst request = loaderUtils.urlToRequest(url, root); // \"./root/path/to/module.js\"\n```\n\nTo convert a root-relative URL into a module URL, specify a `root` value that starts with `~`:\n\n```javascript\nconst url = \"/path/to/module.js\";\nconst root = \"~\";\nconst request = loaderUtils.urlToRequest(url, root); // \"path/to/module.js\"\n```\n\n### `interpolateName`\n\nInterpolates a filename template using multiple placeholders and/or a regular expression.\nThe template and regular expression are set as query params called `name` and `regExp` on the current loader's context.\n\n```javascript\nconst interpolatedName = loaderUtils.interpolateName(loaderContext, name, options);\n```\n\nThe following tokens are replaced in the `name` parameter:\n\n* `[ext]` the extension of the resource\n* `[name]` the basename of the resource\n* `[path]` the path of the resource relative to the `context` query parameter or option.\n* `[folder]` the folder of the resource is in.\n* `[emoji]` a random emoji representation of `options.content`\n* `[emoji:]` same as above, but with a customizable number of emojis\n* `[hash]` the hash of `options.content` (Buffer) (by default it's the hex digest of the md5 hash)\n* `[:hash::]` optionally one can configure\n * other `hashType`s, i. e. `sha1`, `md5`, `sha256`, `sha512`\n * other `digestType`s, i. e. `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`\n * and `length` the length in chars\n* `[N]` the N-th match obtained from matching the current file name against `options.regExp`\n\nExamples\n\n``` javascript\n// loaderContext.resourcePath = \"/app/js/javascript.js\"\nloaderUtils.interpolateName(loaderContext, \"js/[hash].script.[ext]\", { content: ... });\n// => js/9473fdd0d880a43c21b7778d34872157.script.js\n\n// loaderContext.resourcePath = \"/app/page.html\"\nloaderUtils.interpolateName(loaderContext, \"html-[hash:6].html\", { content: ... });\n// => html-9473fd.html\n\n// loaderContext.resourcePath = \"/app/flash.txt\"\nloaderUtils.interpolateName(loaderContext, \"[hash]\", { content: ... });\n// => c31e9820c001c9c4a86bce33ce43b679\n\n// loaderContext.resourcePath = \"/app/img/image.gif\"\nloaderUtils.interpolateName(loaderContext, \"[emoji]\", { content: ... });\n// => 👍\n\n// loaderContext.resourcePath = \"/app/img/image.gif\"\nloaderUtils.interpolateName(loaderContext, \"[emoji:4]\", { content: ... });\n// => 🙍🏢📤🐝\n\n// loaderContext.resourcePath = \"/app/img/image.png\"\nloaderUtils.interpolateName(loaderContext, \"[sha512:hash:base64:7].[ext]\", { content: ... });\n// => 2BKDTjl.png\n// use sha512 hash instead of md5 and with only 7 chars of base64\n\n// loaderContext.resourcePath = \"/app/img/myself.png\"\n// loaderContext.query.name =\nloaderUtils.interpolateName(loaderContext, \"picture.png\");\n// => picture.png\n\n// loaderContext.resourcePath = \"/app/dir/file.png\"\nloaderUtils.interpolateName(loaderContext, \"[path][name].[ext]?[hash]\", { content: ... });\n// => /app/dir/file.png?9473fdd0d880a43c21b7778d34872157\n\n// loaderContext.resourcePath = \"/app/js/page-home.js\"\nloaderUtils.interpolateName(loaderContext, \"script-[1].[ext]\", { regExp: \"page-(.*)\\\\.js\", content: ... });\n// => script-home.js\n```\n\n### `getHashDigest`\n\n``` javascript\nconst digestString = loaderUtils.getHashDigest(buffer, hashType, digestType, maxLength);\n```\n\n* `buffer` the content that should be hashed\n* `hashType` one of `sha1`, `md5`, `sha256`, `sha512` or any other node.js supported hash type\n* `digestType` one of `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`\n* `maxLength` the maximum length in chars\n\n## License\n\nMIT (http://www.opensource.org/licenses/mit-license.php)\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/webpack/loader-utils/issues" + }, + "homepage": "https://github.com/webpack/loader-utils#readme", + "_id": "loader-utils@1.1.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "loader-utils@1.1.0", + "name": "loader-utils", + "escapedName": "loader-utils", + "rawSpec": "1.1.0", + "saveSpec": "[Circular]", + "fetchSpec": "1.1.0" + }, + "_spec": "1.1.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "loader-utils@1.1.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/loader-utils", + "error": "[Circular]", + "extraneous": false + }, + "schema-utils": { + "name": "schema-utils", + "version": "0.4.5", + "author": { + "name": "Webpack Contrib", + "url": "https://github.com/webpack-contrib" + }, + "description": "Webpack Schema Validation Utilities", + "license": "MIT", + "main": "dist/cjs.js", + "files": [ + "dist" + ], + "scripts": { + "start": "npm run build -- -w", + "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/**/*.test.js' --copy-files", + "clean": "del-cli dist", + "commitlint": "commitlint", + "commitmsg": "commitlint -e $GIT_PARAMS", + "lint": "eslint --cache src test", + "ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}", + "lint-staged": "lint-staged", + "prebuild": "npm run clean", + "prepare": "npm run build", + "release": "standard-version", + "release:ci": "conventional-github-releaser -p angular", + "release:validate": "commitlint --from=$(git describe --tags --abbrev=0) --to=$(git rev-parse HEAD)", + "security": "nsp check", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage", + "ci:lint": "npm run lint && npm run security", + "ci:test": "npm run test -- --runInBand", + "ci:coverage": "npm run test:coverage -- --runInBand", + "defaults": "webpack-defaults" + }, + "dependencies": { + "ajv": { + "name": "ajv", + "version": "6.5.2", + "description": "Another JSON Schema Validator", + "main": "lib/ajv.js", + "typings": "lib/ajv.d.ts", + "files": [ + "lib/", + "dist/", + "scripts/", + "LICENSE", + ".tonic_example.js" + ], + "scripts": { + "eslint": "eslint lib/*.js lib/compile/*.js spec/*.js scripts", + "jshint": "jshint lib/*.js lib/**/*.js --exclude lib/dotjs/**/*", + "test-spec": "mocha spec/*.spec.js -R spec", + "test-fast": "AJV_FAST_TEST=true npm run test-spec", + "test-debug": "mocha spec/*.spec.js --debug-brk -R spec", + "test-cov": "nyc npm run test-spec", + "test-ts": "tsc --target ES5 --noImplicitAny lib/ajv.d.ts", + "bundle": "del-cli dist && node ./scripts/bundle.js . Ajv pure_getters", + "bundle-beautify": "node ./scripts/bundle.js js-beautify", + "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", + "test-karma": "karma start --single-run --browsers PhantomJS", + "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", + "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov && if-node-version 8 npm run test-browser", + "prepublish": "npm run build && npm run bundle", + "watch": "watch 'npm run build' ./lib/dot" + }, + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/ajv.git" + }, + "keywords": [ + "JSON", + "schema", + "validator", + "validation", + "jsonschema", + "json-schema", + "json-schema-validator", + "json-schema-validation" + ], + "author": { + "name": "Evgeny Poberezkin" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/epoberezkin/ajv/issues" + }, + "homepage": "https://github.com/epoberezkin/ajv", + "tonicExampleFilename": ".tonic_example.js", + "dependencies": { + "fast-deep-equal": { + "name": "fast-deep-equal", + "version": "2.0.1", + "description": "Fast deep equal", + "main": "index.js", + "scripts": { + "eslint": "eslint *.js benchmark spec", + "test-spec": "mocha spec/*.spec.js -R spec", + "test-cov": "nyc npm run test-spec", + "test-ts": "tsc --target ES5 --noImplicitAny index.d.ts", + "test": "npm run eslint && npm run test-ts && npm run test-cov" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/fast-deep-equal.git" + }, + "keywords": [ + "fast", + "equal", + "deep-equal" + ], + "author": { + "name": "Evgeny Poberezkin" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/epoberezkin/fast-deep-equal/issues" + }, + "homepage": "https://github.com/epoberezkin/fast-deep-equal#readme", + "devDependencies": { + "benchmark": "^2.1.4", + "coveralls": "^2.13.1", + "deep-eql": "latest", + "deep-equal": "latest", + "eslint": "^4.0.0", + "lodash": "latest", + "mocha": "^3.4.2", + "nano-equal": "latest", + "nyc": "^11.0.2", + "pre-commit": "^1.2.2", + "ramda": "latest", + "shallow-equal-fuzzy": "latest", + "typescript": "^2.6.1", + "underscore": "latest" + }, + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "files": [ + "index.js", + "index.d.ts" + ], + "types": "index.d.ts", + "_resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "_integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "_from": "fast-deep-equal@2.0.1", + "readme": "# fast-deep-equal\nThe fastest deep equal\n\n[![Build Status](https://travis-ci.org/epoberezkin/fast-deep-equal.svg?branch=master)](https://travis-ci.org/epoberezkin/fast-deep-equal)\n[![npm version](https://badge.fury.io/js/fast-deep-equal.svg)](http://badge.fury.io/js/fast-deep-equal)\n[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/fast-deep-equal/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/fast-deep-equal?branch=master)\n\n\n## Install\n\n```bash\nnpm install fast-deep-equal\n```\n\n\n## Features\n\n- ES5 compatible\n- works in node.js (0.10+) and browsers (IE9+)\n- checks equality of Date and RegExp objects by value.\n\n\n## Usage\n\n```javascript\nvar equal = require('fast-deep-equal');\nconsole.log(equal({foo: 'bar'}, {foo: 'bar'})); // true\n```\n\n\n## Performance benchmark\n\nNode.js v9.11.1:\n\n```\nfast-deep-equal x 226,960 ops/sec ±1.55% (86 runs sampled)\nnano-equal x 218,210 ops/sec ±0.79% (89 runs sampled)\nshallow-equal-fuzzy x 206,762 ops/sec ±0.84% (88 runs sampled)\nunderscore.isEqual x 128,668 ops/sec ±0.75% (91 runs sampled)\nlodash.isEqual x 44,895 ops/sec ±0.67% (85 runs sampled)\ndeep-equal x 51,616 ops/sec ±0.96% (90 runs sampled)\ndeep-eql x 28,218 ops/sec ±0.42% (85 runs sampled)\nassert.deepStrictEqual x 1,777 ops/sec ±1.05% (86 runs sampled)\nramda.equals x 13,466 ops/sec ±0.82% (86 runs sampled)\nThe fastest is fast-deep-equal\n```\n\nTo run benchmark (requires node.js 6+):\n\n```bash\nnpm install\nnode benchmark\n```\n\n\n## License\n\n[MIT](https://github.com/epoberezkin/fast-deep-equal/blob/master/LICENSE)\n", + "readmeFilename": "README.md", + "_id": "fast-deep-equal@2.0.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "fast-deep-equal@2.0.1", + "name": "fast-deep-equal", + "escapedName": "fast-deep-equal", + "rawSpec": "2.0.1", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.1" + }, + "_spec": "2.0.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "fast-deep-equal@2.0.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/fast-deep-equal", + "error": "[Circular]", + "extraneous": false + }, + "fast-json-stable-stringify": { + "name": "fast-json-stable-stringify", + "version": "2.0.0", + "description": "deterministic `JSON.stringify()` - a faster version of substack's json-stable-strigify without jsonify", + "main": "index.js", + "devDependencies": { + "benchmark": "^2.1.4", + "coveralls": "^3.0.0", + "eslint": "^4.9.0", + "fast-stable-stringify": "latest", + "faster-stable-stringify": "latest", + "json-stable-stringify": "latest", + "nyc": "^11.2.1", + "pre-commit": "^1.2.2", + "tape": "~1.0.4" + }, + "scripts": { + "eslint": "eslint index.js test", + "test-spec": "tape test/*.js", + "test": "npm run eslint && nyc npm run test-spec" + }, + "repository": { + "type": "git", + "url": "git://github.com/epoberezkin/fast-json-stable-stringify.git" + }, + "homepage": "https://github.com/epoberezkin/fast-json-stable-stringify", + "keywords": [ + "json", + "stringify", + "deterministic", + "hash", + "stable" + ], + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "license": "MIT", + "nyc": { + "exclude": [ + "test", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "_resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "_integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "_from": "fast-json-stable-stringify@2.0.0", + "readme": "# fast-json-stable-stringify\n\nDeterministic `JSON.stringify()` - a faster version of [@substack](https://github.com/substack)'s json-stable-strigify without [jsonify](https://github.com/substack/jsonify).\n\nYou can also pass in a custom comparison function.\n\n[![Build Status](https://travis-ci.org/epoberezkin/fast-json-stable-stringify.svg?branch=master)](https://travis-ci.org/epoberezkin/fast-json-stable-stringify)\n[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/fast-json-stable-stringify/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/fast-json-stable-stringify?branch=master)\n\n# example\n\n``` js\nvar stringify = require('fast-json-stable-stringify');\nvar obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };\nconsole.log(stringify(obj));\n```\n\noutput:\n\n```\n{\"a\":3,\"b\":[{\"x\":4,\"y\":5,\"z\":6},7],\"c\":8}\n```\n\n\n# methods\n\n``` js\nvar stringify = require('fast-json-stable-stringify')\n```\n\n## var str = stringify(obj, opts)\n\nReturn a deterministic stringified string `str` from the object `obj`.\n\n\n## options\n\n### cmp\n\nIf `opts` is given, you can supply an `opts.cmp` to have a custom comparison\nfunction for object keys. Your function `opts.cmp` is called with these\nparameters:\n\n``` js\nopts.cmp({ key: akey, value: avalue }, { key: bkey, value: bvalue })\n```\n\nFor example, to sort on the object key names in reverse order you could write:\n\n``` js\nvar stringify = require('fast-json-stable-stringify');\n\nvar obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };\nvar s = stringify(obj, function (a, b) {\n return a.key < b.key ? 1 : -1;\n});\nconsole.log(s);\n```\n\nwhich results in the output string:\n\n```\n{\"c\":8,\"b\":[{\"z\":6,\"y\":5,\"x\":4},7],\"a\":3}\n```\n\nOr if you wanted to sort on the object values in reverse order, you could write:\n\n```\nvar stringify = require('fast-json-stable-stringify');\n\nvar obj = { d: 6, c: 5, b: [{z:3,y:2,x:1},9], a: 10 };\nvar s = stringify(obj, function (a, b) {\n return a.value < b.value ? 1 : -1;\n});\nconsole.log(s);\n```\n\nwhich outputs:\n\n```\n{\"d\":6,\"c\":5,\"b\":[{\"z\":3,\"y\":2,\"x\":1},9],\"a\":10}\n```\n\n### cycles\n\nPass `true` in `opts.cycles` to stringify circular property as `__cycle__` - the result will not be a valid JSON string in this case.\n\nTypeError will be thrown in case of circular object without this option.\n\n\n# install\n\nWith [npm](https://npmjs.org) do:\n\n```\nnpm install fast-json-stable-stringify\n```\n\n\n# benchmark\n\nTo run benchmark (requires Node.js 6+):\n```\nnode benchmark\n```\n\nResults:\n```\nfast-json-stable-stringify x 17,189 ops/sec ±1.43% (83 runs sampled)\njson-stable-stringify x 13,634 ops/sec ±1.39% (85 runs sampled)\nfast-stable-stringify x 20,212 ops/sec ±1.20% (84 runs sampled)\nfaster-stable-stringify x 15,549 ops/sec ±1.12% (84 runs sampled)\nThe fastest is fast-stable-stringify\n```\n\n\n# license\n\n[MIT](https://github.com/epoberezkin/fast-json-stable-stringify/blob/master/LICENSE)\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/epoberezkin/fast-json-stable-stringify/issues" + }, + "_id": "fast-json-stable-stringify@2.0.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "fast-json-stable-stringify@2.0.0", + "name": "fast-json-stable-stringify", + "escapedName": "fast-json-stable-stringify", + "rawSpec": "2.0.0", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.0" + }, + "_spec": "2.0.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "fast-json-stable-stringify@2.0.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/fast-json-stable-stringify", + "error": "[Circular]", + "extraneous": false + }, + "json-schema-traverse": { + "name": "json-schema-traverse", + "version": "0.4.1", + "description": "Traverse JSON Schema passing each schema object to callback", + "main": "index.js", + "scripts": { + "eslint": "eslint index.js spec", + "test-spec": "mocha spec -R spec", + "test": "npm run eslint && nyc npm run test-spec" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/json-schema-traverse.git" + }, + "keywords": [ + "JSON-Schema", + "traverse", + "iterate" + ], + "author": { + "name": "Evgeny Poberezkin" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/epoberezkin/json-schema-traverse/issues" + }, + "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme", + "devDependencies": { + "coveralls": "^2.13.1", + "eslint": "^3.19.0", + "mocha": "^3.4.2", + "nyc": "^11.0.2", + "pre-commit": "^1.2.2" + }, + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "_resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "_integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "_from": "json-schema-traverse@0.4.1", + "readme": "# json-schema-traverse\nTraverse JSON Schema passing each schema object to callback\n\n[![Build Status](https://travis-ci.org/epoberezkin/json-schema-traverse.svg?branch=master)](https://travis-ci.org/epoberezkin/json-schema-traverse)\n[![npm version](https://badge.fury.io/js/json-schema-traverse.svg)](https://www.npmjs.com/package/json-schema-traverse)\n[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/json-schema-traverse/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/json-schema-traverse?branch=master)\n\n\n## Install\n\n```\nnpm install json-schema-traverse\n```\n\n\n## Usage\n\n```javascript\nconst traverse = require('json-schema-traverse');\nconst schema = {\n properties: {\n foo: {type: 'string'},\n bar: {type: 'integer'}\n }\n};\n\ntraverse(schema, {cb});\n// cb is called 3 times with:\n// 1. root schema\n// 2. {type: 'string'}\n// 3. {type: 'integer'}\n\n// Or:\n\ntraverse(schema, {cb: {pre, post}});\n// pre is called 3 times with:\n// 1. root schema\n// 2. {type: 'string'}\n// 3. {type: 'integer'}\n//\n// post is called 3 times with:\n// 1. {type: 'string'}\n// 2. {type: 'integer'}\n// 3. root schema\n\n```\n\nCallback function `cb` is called for each schema object (not including draft-06 boolean schemas), including the root schema, in pre-order traversal. Schema references ($ref) are not resolved, they are passed as is. Alternatively, you can pass a `{pre, post}` object as `cb`, and then `pre` will be called before traversing child elements, and `post` will be called after all child elements have been traversed.\n\nCallback is passed these parameters:\n\n- _schema_: the current schema object\n- _JSON pointer_: from the root schema to the current schema object\n- _root schema_: the schema passed to `traverse` object\n- _parent JSON pointer_: from the root schema to the parent schema object (see below)\n- _parent keyword_: the keyword inside which this schema appears (e.g. `properties`, `anyOf`, etc.)\n- _parent schema_: not necessarily parent object/array; in the example above the parent schema for `{type: 'string'}` is the root schema\n- _index/property_: index or property name in the array/object containing multiple schemas; in the example above for `{type: 'string'}` the property name is `'foo'`\n\n\n## Traverse objects in all unknown keywords\n\n```javascript\nconst traverse = require('json-schema-traverse');\nconst schema = {\n mySchema: {\n minimum: 1,\n maximum: 2\n }\n};\n\ntraverse(schema, {allKeys: true, cb});\n// cb is called 2 times with:\n// 1. root schema\n// 2. mySchema\n```\n\nWithout option `allKeys: true` callback will be called only with root schema.\n\n\n## License\n\n[MIT](https://github.com/epoberezkin/json-schema-traverse/blob/master/LICENSE)\n", + "readmeFilename": "README.md", + "_id": "json-schema-traverse@0.4.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "json-schema-traverse@0.4.1", + "name": "json-schema-traverse", + "escapedName": "json-schema-traverse", + "rawSpec": "0.4.1", + "saveSpec": "[Circular]", + "fetchSpec": "0.4.1" + }, + "_spec": "0.4.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "json-schema-traverse@0.4.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/json-schema-traverse", + "error": "[Circular]", + "extraneous": false + }, + "uri-js": { + "name": "uri-js", + "version": "4.2.2", + "description": "An RFC 3986/3987 compliant, scheme extendable URI/IRI parsing/validating/resolving library for JavaScript.", + "main": "dist/es5/uri.all.js", + "types": "dist/es5/uri.all.d.ts", + "directories": { + "test": "tests" + }, + "scripts": { + "build:esnext": "tsc", + "build:es5": "rollup -c && cp dist/esnext/uri.d.ts dist/es5/uri.all.d.ts && npm run build:es5:fix-sourcemap", + "build:es5:fix-sourcemap": "sorcery -i dist/es5/uri.all.js", + "build:es5:min": "uglifyjs dist/es5/uri.all.js --support-ie8 --output dist/es5/uri.all.min.js --in-source-map dist/es5/uri.all.js.map --source-map uri.all.min.js.map --comments --compress --mangle --pure-funcs merge subexp && mv uri.all.min.js.map dist/es5/ && cp dist/es5/uri.all.d.ts dist/es5/uri.all.min.d.ts", + "build": "npm run build:esnext && npm run build:es5 && npm run build:es5:min", + "test": "mocha -u mocha-qunit-ui dist/es5/uri.all.js tests/tests.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/garycourt/uri-js.git" + }, + "keywords": [ + "URI", + "IRI", + "IDN", + "URN", + "UUID", + "HTTP", + "HTTPS", + "MAILTO", + "RFC3986", + "RFC3987", + "RFC5891", + "RFC2616", + "RFC2818", + "RFC2141", + "RFC4122", + "RFC4291", + "RFC5952", + "RFC6068", + "RFC6874" + ], + "author": { + "name": "Gary Court", + "email": "gary.court@gmail.com" + }, + "license": "BSD-2-Clause", + "bugs": { + "url": "https://github.com/garycourt/uri-js/issues" + }, + "homepage": "https://github.com/garycourt/uri-js", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-plugin-external-helpers": "^6.22.0", + "babel-preset-latest": "^6.24.1", + "mocha": "^3.2.0", + "mocha-qunit-ui": "^0.1.3", + "rollup": "^0.41.6", + "rollup-plugin-babel": "^2.7.1", + "rollup-plugin-node-resolve": "^2.0.0", + "sorcery": "^0.10.0", + "typescript": "^2.8.1", + "uglify-js": "^2.8.14" + }, + "dependencies": { + "punycode": { + "name": "punycode", + "version": "2.1.1", + "description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.", + "homepage": "https://mths.be/punycode", + "main": "punycode.js", + "jsnext:main": "punycode.es6.js", + "module": "punycode.es6.js", + "engines": { + "node": ">=6" + }, + "keywords": [ + "punycode", + "unicode", + "idn", + "idna", + "dns", + "url", + "domain" + ], + "license": "MIT", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "contributors": [ + { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/bestiejs/punycode.js.git" + }, + "bugs": { + "url": "https://github.com/bestiejs/punycode.js/issues" + }, + "files": [ + "LICENSE-MIT.txt", + "punycode.js", + "punycode.es6.js" + ], + "scripts": { + "test": "mocha tests", + "prepublish": "node scripts/prepublish.js" + }, + "devDependencies": { + "codecov": "^1.0.1", + "istanbul": "^0.4.1", + "mocha": "^2.5.3" + }, + "jspm": { + "map": { + "./punycode.js": { + "node": "@node/punycode" + } + } + }, + "_resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "_integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "_from": "punycode@2.1.1", + "readme": "# Punycode.js [![Build status](https://travis-ci.org/bestiejs/punycode.js.svg?branch=master)](https://travis-ci.org/bestiejs/punycode.js) [![Code coverage status](http://img.shields.io/codecov/c/github/bestiejs/punycode.js.svg)](https://codecov.io/gh/bestiejs/punycode.js) [![Dependency status](https://gemnasium.com/bestiejs/punycode.js.svg)](https://gemnasium.com/bestiejs/punycode.js)\n\nPunycode.js is a robust Punycode converter that fully complies to [RFC 3492](https://tools.ietf.org/html/rfc3492) and [RFC 5891](https://tools.ietf.org/html/rfc5891).\n\nThis JavaScript library is the result of comparing, optimizing and documenting different open-source implementations of the Punycode algorithm:\n\n* [The C example code from RFC 3492](https://tools.ietf.org/html/rfc3492#appendix-C)\n* [`punycode.c` by _Markus W. Scherer_ (IBM)](http://opensource.apple.com/source/ICU/ICU-400.42/icuSources/common/punycode.c)\n* [`punycode.c` by _Ben Noordhuis_](https://github.com/bnoordhuis/punycode/blob/master/punycode.c)\n* [JavaScript implementation by _some_](http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion/301287#301287)\n* [`punycode.js` by _Ben Noordhuis_](https://github.com/joyent/node/blob/426298c8c1c0d5b5224ac3658c41e7c2a3fe9377/lib/punycode.js) (note: [not fully compliant](https://github.com/joyent/node/issues/2072))\n\nThis project was [bundled](https://github.com/joyent/node/blob/master/lib/punycode.js) with Node.js from [v0.6.2+](https://github.com/joyent/node/compare/975f1930b1...61e796decc) until [v7](https://github.com/nodejs/node/pull/7941) (soft-deprecated).\n\nThe current version supports recent versions of Node.js only. It provides a CommonJS module and an ES6 module. For the old version that offers the same functionality with broader support, including Rhino, Ringo, Narwhal, and web browsers, see [v1.4.1](https://github.com/bestiejs/punycode.js/releases/tag/v1.4.1).\n\n## Installation\n\nVia [npm](https://www.npmjs.com/):\n\n```bash\nnpm install punycode --save\n```\n\nIn [Node.js](https://nodejs.org/):\n\n```js\nconst punycode = require('punycode');\n```\n\n## API\n\n### `punycode.decode(string)`\n\nConverts a Punycode string of ASCII symbols to a string of Unicode symbols.\n\n```js\n// decode domain name parts\npunycode.decode('maana-pta'); // 'mañana'\npunycode.decode('--dqo34k'); // '☃-⌘'\n```\n\n### `punycode.encode(string)`\n\nConverts a string of Unicode symbols to a Punycode string of ASCII symbols.\n\n```js\n// encode domain name parts\npunycode.encode('mañana'); // 'maana-pta'\npunycode.encode('☃-⌘'); // '--dqo34k'\n```\n\n### `punycode.toUnicode(input)`\n\nConverts a Punycode string representing a domain name or an email address to Unicode. Only the Punycoded parts of the input will be converted, i.e. it doesn’t matter if you call it on a string that has already been converted to Unicode.\n\n```js\n// decode domain names\npunycode.toUnicode('xn--maana-pta.com');\n// → 'mañana.com'\npunycode.toUnicode('xn----dqo34k.com');\n// → '☃-⌘.com'\n\n// decode email addresses\npunycode.toUnicode('джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq');\n// → 'джумла@джpумлатест.bрфa'\n```\n\n### `punycode.toASCII(input)`\n\nConverts a lowercased Unicode string representing a domain name or an email address to Punycode. Only the non-ASCII parts of the input will be converted, i.e. it doesn’t matter if you call it with a domain that’s already in ASCII.\n\n```js\n// encode domain names\npunycode.toASCII('mañana.com');\n// → 'xn--maana-pta.com'\npunycode.toASCII('☃-⌘.com');\n// → 'xn----dqo34k.com'\n\n// encode email addresses\npunycode.toASCII('джумла@джpумлатест.bрфa');\n// → 'джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq'\n```\n\n### `punycode.ucs2`\n\n#### `punycode.ucs2.decode(string)`\n\nCreates an array containing the numeric code point values of each Unicode symbol in the string. While [JavaScript uses UCS-2 internally](https://mathiasbynens.be/notes/javascript-encoding), this function will convert a pair of surrogate halves (each of which UCS-2 exposes as separate characters) into a single code point, matching UTF-16.\n\n```js\npunycode.ucs2.decode('abc');\n// → [0x61, 0x62, 0x63]\n// surrogate pair for U+1D306 TETRAGRAM FOR CENTRE:\npunycode.ucs2.decode('\\uD834\\uDF06');\n// → [0x1D306]\n```\n\n#### `punycode.ucs2.encode(codePoints)`\n\nCreates a string based on an array of numeric code point values.\n\n```js\npunycode.ucs2.encode([0x61, 0x62, 0x63]);\n// → 'abc'\npunycode.ucs2.encode([0x1D306]);\n// → '\\uD834\\uDF06'\n```\n\n### `punycode.version`\n\nA string representing the current Punycode.js version number.\n\n## Author\n\n| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias \"Follow @mathias on Twitter\") |\n|---|\n| [Mathias Bynens](https://mathiasbynens.be/) |\n\n## License\n\nPunycode.js is available under the [MIT](https://mths.be/mit) license.\n", + "readmeFilename": "README.md", + "_id": "punycode@2.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "punycode@2.1.1", + "name": "punycode", + "escapedName": "punycode", + "rawSpec": "2.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "2.1.1" + }, + "_spec": "2.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "punycode@2.1.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/punycode", + "error": "[Circular]", + "extraneous": false + } + }, + "_resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "_integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "_from": "uri-js@4.2.2", + "readme": "# URI.js\n\nURI.js is an [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt) compliant, scheme extendable URI parsing/validating/resolving library for all JavaScript environments (browsers, Node.js, etc).\nIt is also compliant with the IRI ([RFC 3987](http://www.ietf.org/rfc/rfc3987.txt)), IDNA ([RFC 5890](http://www.ietf.org/rfc/rfc5890.txt)), IPv6 Address ([RFC 5952](http://www.ietf.org/rfc/rfc5952.txt)), IPv6 Zone Identifier ([RFC 6874](http://www.ietf.org/rfc/rfc6874.txt)) specifications.\n\nURI.js has an extensive test suite, and works in all (Node.js, web) environments. It weighs in at 6.2kb (gzipped, 16kb deflated).\n\n## API\n\n### Parsing\n\n\tURI.parse(\"uri://user:pass@example.com:123/one/two.three?q1=a1&q2=a2#body\");\n\t//returns:\n\t//{\n\t// scheme : \"uri\",\n\t// userinfo : \"user:pass\",\n\t// host : \"example.com\",\n\t// port : 123,\n\t// path : \"/one/two.three\",\n\t// query : \"q1=a1&q2=a2\",\n\t// fragment : \"body\"\n\t//}\n\n### Serializing\n\n\tURI.serialize({scheme : \"http\", host : \"example.com\", fragment : \"footer\"}) === \"http://example.com/#footer\"\n\n### Resolving\n\n\tURI.resolve(\"uri://a/b/c/d?q\", \"../../g\") === \"uri://a/g\"\n\n### Normalizing\n\n\tURI.normalize(\"HTTP://ABC.com:80/%7Esmith/home.html\") === \"http://abc.com/~smith/home.html\"\n\n### Comparison\n\n\tURI.equal(\"example://a/b/c/%7Bfoo%7D\", \"eXAMPLE://a/./b/../b/%63/%7bfoo%7d\") === true\n\n### IP Support\n\n\t//IPv4 normalization\n\tURI.normalize(\"//192.068.001.000\") === \"//192.68.1.0\"\n\n\t//IPv6 normalization\n\tURI.normalize(\"//[2001:0:0DB8::0:0001]\") === \"//[2001:0:db8::1]\"\n\n\t//IPv6 zone identifier support\n\tURI.parse(\"//[2001:db8::7%25en1]\");\n\t//returns:\n\t//{\n\t// host : \"2001:db8::7%en1\"\n\t//}\n\n### IRI Support\n\n\t//convert IRI to URI\n\tURI.serialize(URI.parse(\"http://examplé.org/rosé\")) === \"http://xn--exampl-gva.org/ros%C3%A9\"\n\t//convert URI to IRI\n\tURI.serialize(URI.parse(\"http://xn--exampl-gva.org/ros%C3%A9\"), {iri:true}) === \"http://examplé.org/rosé\"\n\n### Options\n\nAll of the above functions can accept an additional options argument that is an object that can contain one or more of the following properties:\n\n*\t`scheme` (string)\n\n\tIndicates the scheme that the URI should be treated as, overriding the URI's normal scheme parsing behavior.\n\n*\t`reference` (string)\n\n\tIf set to `\"suffix\"`, it indicates that the URI is in the suffix format, and the validator will use the option's `scheme` property to determine the URI's scheme.\n\n*\t`tolerant` (boolean, false)\n\n\tIf set to `true`, the parser will relax URI resolving rules.\n\n*\t`absolutePath` (boolean, false)\n\n\tIf set to `true`, the serializer will not resolve a relative `path` component.\n\n*\t`iri` (boolean, false)\n\n\tIf set to `true`, the serializer will unescape non-ASCII characters as per [RFC 3987](http://www.ietf.org/rfc/rfc3987.txt).\n\n*\t`unicodeSupport` (boolean, false)\n\n\tIf set to `true`, the parser will unescape non-ASCII characters in the parsed output as per [RFC 3987](http://www.ietf.org/rfc/rfc3987.txt).\n\n*\t`domainHost` (boolean, false)\n\n\tIf set to `true`, the library will treat the `host` component as a domain name, and convert IDNs (International Domain Names) as per [RFC 5891](http://www.ietf.org/rfc/rfc5891.txt).\n\n## Scheme Extendable\n\nURI.js supports inserting custom [scheme](http://en.wikipedia.org/wiki/URI_scheme) dependent processing rules. Currently, URI.js has built in support for the following schemes:\n\n*\thttp \\[[RFC 2616](http://www.ietf.org/rfc/rfc2616.txt)\\]\n*\thttps \\[[RFC 2818](http://www.ietf.org/rfc/rfc2818.txt)\\]\n*\tmailto \\[[RFC 6068](http://www.ietf.org/rfc/rfc6068.txt)\\]\n*\turn \\[[RFC 2141](http://www.ietf.org/rfc/rfc2141.txt)\\]\n*\turn:uuid \\[[RFC 4122](http://www.ietf.org/rfc/rfc4122.txt)\\]\n\n### HTTP Support\n\n\tURI.equal(\"HTTP://ABC.COM:80\", \"http://abc.com/\") === true\n\n### Mailto Support\n\n\tURI.parse(\"mailto:alpha@example.com,bravo@example.com?subject=SUBSCRIBE&body=Sign%20me%20up!\");\n\t//returns:\n\t//{\n\t//\tscheme : \"mailto\",\n\t//\tto : [\"alpha@example.com\", \"bravo@example.com\"],\n\t//\tsubject : \"SUBSCRIBE\",\n\t//\tbody : \"Sign me up!\"\n\t//}\n\n\tURI.serialize({\n\t\tscheme : \"mailto\",\n\t\tto : [\"alpha@example.com\"],\n\t\tsubject : \"REMOVE\",\n\t\tbody : \"Please remove me\",\n\t\theaders : {\n\t\t\tcc : \"charlie@example.com\"\n\t\t}\n\t}) === \"mailto:alpha@example.com?cc=charlie@example.com&subject=REMOVE&body=Please%20remove%20me\"\n\n### URN Support\n\n\tURI.parse(\"urn:example:foo\");\n\t//returns:\n\t//{\n\t//\tscheme : \"urn\",\n\t//\tnid : \"example\",\n\t//\tnss : \"foo\",\n\t//}\n\n#### URN UUID Support\n\n\tURI.parse(\"urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6\");\n\t//returns:\n\t//{\n\t//\tscheme : \"urn\",\n\t//\tnid : \"example\",\n\t//\tuuid : \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\",\n\t//}\n\n## Usage\n\nTo load in a browser, use the following tag:\n\n\t\n\nTo load in a CommonJS (Node.js) environment, first install with npm by running on the command line:\n\n\tnpm install uri-js\n\nThen, in your code, load it using:\n\n\tconst URI = require(\"uri-js\");\n\nIf you are writing your code in ES6+ (ESNEXT) or TypeScript, you would load it using:\n\n\timport * as URI from \"uri-js\";\n\nOr you can load just what you need using named exports:\n\n\timport { parse, serialize, resolve, resolveComponents, normalize, equal, removeDotSegments, pctEncChar, pctDecChars, escapeComponent, unescapeComponent } from \"uri-js\";\n\n## Breaking changes\n\n### Breaking changes from 3.x\n\nURN parsing has been completely changed to better align with the specification. Scheme is now always `urn`, but has two new properties: `nid` which contains the Namspace Identifier, and `nss` which contains the Namespace Specific String. The `nss` property will be removed by higher order scheme handlers, such as the UUID URN scheme handler.\n\nThe UUID of a URN can now be found in the `uuid` property.\n\n### Breaking changes from 2.x\n\nURI validation has been removed as it was slow, exposed a vulnerabilty, and was generally not useful.\n\n### Breaking changes from 1.x\n\nThe `errors` array on parsed components is now an `error` string.\n\n## License ([Simplified BSD](http://en.wikipedia.org/wiki/BSD_licenses#2-clause))\n\nCopyright 2011 Gary Court. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1.\tRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2.\tRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY GARY COURT \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nThe views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Gary Court.\n", + "readmeFilename": "README.md", + "_id": "uri-js@4.2.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "uri-js@4.2.2", + "name": "uri-js", + "escapedName": "uri-js", + "rawSpec": "4.2.2", + "saveSpec": "[Circular]", + "fetchSpec": "4.2.2" + }, + "_spec": "4.2.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "uri-js@4.2.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "punycode": "^2.1.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/uri-js", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "ajv-async": "^1.0.0", + "bluebird": "^3.1.5", + "brfs": "^2.0.0", + "browserify": "^16.2.0", + "chai": "^4.0.1", + "coveralls": "^3.0.1", + "del-cli": "^1.1.0", + "dot": "^1.0.3", + "eslint": "^5.0.0", + "gh-pages-generator": "^0.2.3", + "glob": "^7.0.0", + "if-node-version": "^1.0.0", + "js-beautify": "^1.7.3", + "jshint": "^2.9.4", + "json-schema-test": "^2.0.0", + "karma": "^2.0.2", + "karma-chrome-launcher": "^2.0.0", + "karma-mocha": "^1.1.1", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.1.0", + "mocha": "^5.1.1", + "nyc": "^12.0.1", + "phantomjs-prebuilt": "^2.1.4", + "pre-commit": "^1.1.1", + "require-globify": "^1.3.0", + "typescript": "^2.8.3", + "uglify-js": "^3.3.24", + "watch": "^1.0.0" + }, + "_resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "_integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "_from": "ajv@6.5.2", + "readme": "\"Ajv\n\n# Ajv: Another JSON Schema Validator\n\nThe fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07.\n\n\n[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv)\n[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv)\n[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv)\n[![Coverage Status](https://coveralls.io/repos/epoberezkin/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/epoberezkin/ajv?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/)\n[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)\n\n\n## Using version 6\n\n[JSON Schema draft-07](http://json-schema.org/latest/json-schema-validation.html) is published.\n\n[Ajv version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes).\n\n__Please note__: To use Ajv with draft-06 schemas you need to explicitly add the meta-schema to the validator instance:\n\n```javascript\najv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));\n```\n\nTo use Ajv with draft-04 schemas in addition to explicitly adding meta-schema you also need to use option schemaId:\n\n```javascript\nvar ajv = new Ajv({schemaId: 'id'});\n// If you want to use both draft-04 and draft-06/07 schemas:\n// var ajv = new Ajv({schemaId: 'auto'});\najv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));\n```\n\n\n## Contents\n\n- [Performance](#performance)\n- [Features](#features)\n- [Getting started](#getting-started)\n- [Frequently Asked Questions](https://github.com/epoberezkin/ajv/blob/master/FAQ.md)\n- [Using in browser](#using-in-browser)\n- [Command line interface](#command-line-interface)\n- Validation\n - [Keywords](#validation-keywords)\n - [Annotation keywords](#annotation-keywords)\n - [Formats](#formats)\n - [Combining schemas with $ref](#ref)\n - [$data reference](#data-reference)\n - NEW: [$merge and $patch keywords](#merge-and-patch-keywords)\n - [Defining custom keywords](#defining-custom-keywords)\n - [Asynchronous schema compilation](#asynchronous-schema-compilation)\n - [Asynchronous validation](#asynchronous-validation)\n- Modifying data during validation\n - [Filtering data](#filtering-data)\n - [Assigning defaults](#assigning-defaults)\n - [Coercing data types](#coercing-data-types)\n- API\n - [Methods](#api)\n - [Options](#options)\n - [Validation errors](#validation-errors)\n- [Plugins](#plugins)\n- [Related packages](#related-packages)\n- [Packages using Ajv](#some-packages-using-ajv)\n- [Tests, Contributing, History, License](#tests)\n\n\n## Performance\n\nAjv generates code using [doT templates](https://github.com/olado/doT) to turn JSON Schemas into super-fast validation functions that are efficient for v8 optimization.\n\nCurrently Ajv is the fastest and the most standard compliant validator according to these benchmarks:\n\n- [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) - 50% faster than the second place\n- [jsck benchmark](https://github.com/pandastrike/jsck#benchmarks) - 20-190% faster\n- [z-schema benchmark](https://rawgit.com/zaggino/z-schema/master/benchmark/results.html)\n- [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html)\n\n\nPerformance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark):\n\n[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:|djv|ajv|json-schema-validator-generator|jsen|is-my-json-valid|themis|z-schema|jsck|skeemas|json-schema-library|tv4&chd=t:100,98,72.1,66.8,50.1,15.1,6.1,3.8,1.2,0.7,0.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance)\n\n\n## Features\n\n- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) and draft-04 standards:\n - all validation keywords (see [JSON Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md))\n - full support of remote refs (remote schemas have to be added with `addSchema` or compiled to be available)\n - support of circular references between schemas\n - correct string lengths for strings with unicode pairs (can be turned off)\n - [formats](#formats) defined by JSON Schema draft-07 standard and custom formats (can be turned off)\n - [validates schemas against meta-schema](#api-validateschema)\n- supports [browsers](#using-in-browser) and Node.js 0.10-8.x\n- [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation\n- \"All errors\" validation mode with [option allErrors](#options)\n- [error messages with parameters](#validation-errors) describing error reasons to allow creating custom error messages\n- i18n error messages support with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) package\n- [filtering data](#filtering-data) from additional properties\n- [assigning defaults](#assigning-defaults) to missing properties and items\n- [coercing data](#coercing-data-types) to the types specified in `type` keywords\n- [custom keywords](#defining-custom-keywords)\n- draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else`\n- draft-06 boolean schemas (`true`/`false` as a schema to always pass/fail).\n- keywords `switch`, `patternRequired`, `formatMaximum` / `formatMinimum` and `formatExclusiveMaximum` / `formatExclusiveMinimum` from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) with [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package\n- [$data reference](#data-reference) to use values from the validated data as values for the schema keywords\n- [asynchronous validation](#asynchronous-validation) of custom formats and keywords\n\nCurrently Ajv is the only validator that passes all the tests from [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), apart from the test that requires that `1.0` is not an integer that is impossible to satisfy in JavaScript).\n\n\n## Install\n\n```\nnpm install ajv\n```\n\n\n## Getting started\n\nTry it in the Node.js REPL: https://tonicdev.com/npm/ajv\n\n\nThe fastest validation call:\n\n```javascript\nvar Ajv = require('ajv');\nvar ajv = new Ajv(); // options can be passed, e.g. {allErrors: true}\nvar validate = ajv.compile(schema);\nvar valid = validate(data);\nif (!valid) console.log(validate.errors);\n```\n\nor with less code\n\n```javascript\n// ...\nvar valid = ajv.validate(schema, data);\nif (!valid) console.log(ajv.errors);\n// ...\n```\n\nor\n\n```javascript\n// ...\nvar valid = ajv.addSchema(schema, 'mySchema')\n .validate('mySchema', data);\nif (!valid) console.log(ajv.errorsText());\n// ...\n```\n\nSee [API](#api) and [Options](#options) for more details.\n\nAjv compiles schemas to functions and caches them in all cases (using schema serialized with [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again.\n\nThe best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods (there is no additional function call).\n\n__Please note__: every time a validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](#validation-errors)\n\n\n## Using in browser\n\nYou can require Ajv directly from the code you browserify - in this case Ajv will be a part of your bundle.\n\nIf you need to use Ajv in several bundles you can create a separate UMD bundle using `npm run bundle` script (thanks to [siddo420](https://github.com/siddo420)).\n\nThen you need to load Ajv in the browser:\n```html\n\n```\n\nThis bundle can be used with different module systems; it creates global `Ajv` if no module system is found.\n\nThe browser bundle is available on [cdnjs](https://cdnjs.com/libraries/ajv).\n\nAjv is tested with these browsers:\n\n[![Sauce Test Status](https://saucelabs.com/browser-matrix/epoberezkin.svg)](https://saucelabs.com/u/epoberezkin)\n\n__Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/epoberezkin/ajv/issues/234)).\n\n\n## Command line interface\n\nCLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ajv-cli). It supports:\n\n- compiling JSON Schemas to test their validity\n- BETA: generating standalone module exporting a validation function to be used without Ajv (using [ajv-pack](https://github.com/epoberezkin/ajv-pack))\n- migrate schemas to draft-07 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate))\n- validating data file(s) against JSON Schema\n- testing expected validity of data against JSON Schema\n- referenced schemas\n- custom meta-schemas\n- files in JSON and JavaScript format\n- all Ajv options\n- reporting changes in data after validation in [JSON-patch](https://tools.ietf.org/html/rfc6902) format\n\n\n## Validation keywords\n\nAjv supports all validation keywords from draft-07 of JSON Schema standard:\n\n- [type](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#type)\n- [for numbers](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-numbers) - maximum, minimum, exclusiveMaximum, exclusiveMinimum, multipleOf\n- [for strings](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-strings) - maxLength, minLength, pattern, format\n- [for arrays](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-arrays) - maxItems, minItems, uniqueItems, items, additionalItems, [contains](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#contains)\n- [for objects](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-objects) - maxProperties, minProperties, required, properties, patternProperties, additionalProperties, dependencies, [propertyNames](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#propertynames)\n- [for all types](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-all-types) - enum, [const](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#const)\n- [compound keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf, [if/then/else](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#ifthenelse)\n\nWith [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package Ajv also supports validation keywords from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) for JSON Schema standard:\n\n- [patternRequired](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#patternrequired-proposed) - like `required` but with patterns that some property should match.\n- [formatMaximum, formatMinimum, formatExclusiveMaximum, formatExclusiveMinimum](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#formatmaximum--formatminimum-and-exclusiveformatmaximum--exclusiveformatminimum-proposed) - setting limits for date, time, etc.\n\nSee [JSON Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) for more details.\n\n\n## Annotation keywords\n\nJSON Schema specification defines several annotation keywords that describe schema itself but do not perform any validation.\n\n- `title` and `description`: information about the data represented by that schema\n- `$comment` (NEW in draft-07): information for developers. With option `$comment` Ajv logs or passes the comment string to the user-supplied function. See [Options](#options).\n- `default`: a default value of the data instance, see [Assigning defaults](#assigning-defaults).\n- `examples` (NEW in draft-07): an array of data instances. Ajv does not check the validity of these instances against the schema.\n- `readOnly` and `writeOnly` (NEW in draft-07): marks data-instance as read-only or write-only in relation to the source of the data (database, api, etc.).\n- `contentEncoding`: [RFC 2045](https://tools.ietf.org/html/rfc2045#section-6.1 ), e.g., \"base64\".\n- `contentMediaType`: [RFC 2046](https://tools.ietf.org/html/rfc2046), e.g., \"image/png\".\n\n__Please note__: Ajv does not implement validation of the keywords `examples`, `contentEncoding` and `contentMediaType` but it reserves them. If you want to create a plugin that implements some of them, it should remove these keywords from the instance.\n\n\n## Formats\n\nThe following formats are supported for string validation with \"format\" keyword:\n\n- _date_: full-date according to [RFC3339](http://tools.ietf.org/html/rfc3339#section-5.6).\n- _time_: time with optional time-zone.\n- _date-time_: date-time from the same source (time-zone is mandatory). `date`, `time` and `date-time` validate ranges in `full` mode and only regexp in `fast` mode (see [options](#options)).\n- _uri_: full URI.\n- _uri-reference_: URI reference, including full and relative URIs.\n- _uri-template_: URI template according to [RFC6570](https://tools.ietf.org/html/rfc6570)\n- _url_: [URL record](https://url.spec.whatwg.org/#concept-url).\n- _email_: email address.\n- _hostname_: host name according to [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5).\n- _ipv4_: IP address v4.\n- _ipv6_: IP address v6.\n- _regex_: tests whether a string is a valid regular expression by passing it to RegExp constructor.\n- _uuid_: Universally Unique IDentifier according to [RFC4122](http://tools.ietf.org/html/rfc4122).\n- _json-pointer_: JSON-pointer according to [RFC6901](https://tools.ietf.org/html/rfc6901).\n- _relative-json-pointer_: relative JSON-pointer according to [this draft](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00).\n\n__Please note__: JSON Schema draft-07 also defines formats `iri`, `iri-reference`, `idn-hostname` and `idn-email` for URLs, hostnames and emails with international characters. Ajv does not implement these formats. If you create Ajv plugin that implements them please make a PR to mention this plugin here.\n\nThere are two modes of format validation: `fast` and `full`. This mode affects formats `date`, `time`, `date-time`, `uri`, `uri-reference`, `email`, and `hostname`. See [Options](#options) for details.\n\nYou can add additional formats and replace any of the formats above using [addFormat](#api-addformat) method.\n\nThe option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details.\n\nYou can find patterns used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js).\n\n\n## Combining schemas with $ref\n\nYou can structure your validation logic across multiple schema files and have schemas reference each other using `$ref` keyword.\n\nExample:\n\n```javascript\nvar schema = {\n \"$id\": \"http://example.com/schemas/schema.json\",\n \"type\": \"object\",\n \"properties\": {\n \"foo\": { \"$ref\": \"defs.json#/definitions/int\" },\n \"bar\": { \"$ref\": \"defs.json#/definitions/str\" }\n }\n};\n\nvar defsSchema = {\n \"$id\": \"http://example.com/schemas/defs.json\",\n \"definitions\": {\n \"int\": { \"type\": \"integer\" },\n \"str\": { \"type\": \"string\" }\n }\n};\n```\n\nNow to compile your schema you can either pass all schemas to Ajv instance:\n\n```javascript\nvar ajv = new Ajv({schemas: [schema, defsSchema]});\nvar validate = ajv.getSchema('http://example.com/schemas/schema.json');\n```\n\nor use `addSchema` method:\n\n```javascript\nvar ajv = new Ajv;\nvar validate = ajv.addSchema(defsSchema)\n .compile(schema);\n```\n\nSee [Options](#options) and [addSchema](#api) method.\n\n__Please note__:\n- `$ref` is resolved as the uri-reference using schema $id as the base URI (see the example).\n- References can be recursive (and mutually recursive) to implement the schemas for different data structures (such as linked lists, trees, graphs, etc.).\n- You don't have to host your schema files at the URIs that you use as schema $id. These URIs are only used to identify the schemas, and according to JSON Schema specification validators should not expect to be able to download the schemas from these URIs.\n- The actual location of the schema file in the file system is not used.\n- You can pass the identifier of the schema as the second parameter of `addSchema` method or as a property name in `schemas` option. This identifier can be used instead of (or in addition to) schema $id.\n- You cannot have the same $id (or the schema identifier) used for more than one schema - the exception will be thrown.\n- You can implement dynamic resolution of the referenced schemas using `compileAsync` method. In this way you can store schemas in any system (files, web, database, etc.) and reference them without explicitly adding to Ajv instance. See [Asynchronous schema compilation](#asynchronous-schema-compilation).\n\n\n## $data reference\n\nWith `$data` option you can use values from the validated data as the values for the schema keywords. See [proposal](https://github.com/json-schema/json-schema/wiki/$data-(v5-proposal)) for more information about how it works.\n\n`$data` reference is supported in the keywords: const, enum, format, maximum/minimum, exclusiveMaximum / exclusiveMinimum, maxLength / minLength, maxItems / minItems, maxProperties / minProperties, formatMaximum / formatMinimum, formatExclusiveMaximum / formatExclusiveMinimum, multipleOf, pattern, required, uniqueItems.\n\nThe value of \"$data\" should be a [JSON-pointer](https://tools.ietf.org/html/rfc6901) to the data (the root is always the top level data object, even if the $data reference is inside a referenced subschema) or a [relative JSON-pointer](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) (it is relative to the current point in data; if the $data reference is inside a referenced subschema it cannot point to the data outside of the root level for this subschema).\n\nExamples.\n\nThis schema requires that the value in property `smaller` is less or equal than the value in the property larger:\n\n```javascript\nvar ajv = new Ajv({$data: true});\n\nvar schema = {\n \"properties\": {\n \"smaller\": {\n \"type\": \"number\",\n \"maximum\": { \"$data\": \"1/larger\" }\n },\n \"larger\": { \"type\": \"number\" }\n }\n};\n\nvar validData = {\n smaller: 5,\n larger: 7\n};\n\najv.validate(schema, validData); // true\n```\n\nThis schema requires that the properties have the same format as their field names:\n\n```javascript\nvar schema = {\n \"additionalProperties\": {\n \"type\": \"string\",\n \"format\": { \"$data\": \"0#\" }\n }\n};\n\nvar validData = {\n 'date-time': '1963-06-19T08:30:06.283185Z',\n email: 'joe.bloggs@example.com'\n}\n```\n\n`$data` reference is resolved safely - it won't throw even if some property is undefined. If `$data` resolves to `undefined` the validation succeeds (with the exclusion of `const` keyword). If `$data` resolves to incorrect type (e.g. not \"number\" for maximum keyword) the validation fails.\n\n\n## $merge and $patch keywords\n\nWith the package [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) and [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902).\n\nTo add keywords `$merge` and `$patch` to Ajv instance use this code:\n\n```javascript\nrequire('ajv-merge-patch')(ajv);\n```\n\nExamples.\n\nUsing `$merge`:\n\n```json\n{\n \"$merge\": {\n \"source\": {\n \"type\": \"object\",\n \"properties\": { \"p\": { \"type\": \"string\" } },\n \"additionalProperties\": false\n },\n \"with\": {\n \"properties\": { \"q\": { \"type\": \"number\" } }\n }\n }\n}\n```\n\nUsing `$patch`:\n\n```json\n{\n \"$patch\": {\n \"source\": {\n \"type\": \"object\",\n \"properties\": { \"p\": { \"type\": \"string\" } },\n \"additionalProperties\": false\n },\n \"with\": [\n { \"op\": \"add\", \"path\": \"/properties/q\", \"value\": { \"type\": \"number\" } }\n ]\n }\n}\n```\n\nThe schemas above are equivalent to this schema:\n\n```json\n{\n \"type\": \"object\",\n \"properties\": {\n \"p\": { \"type\": \"string\" },\n \"q\": { \"type\": \"number\" }\n },\n \"additionalProperties\": false\n}\n```\n\nThe properties `source` and `with` in the keywords `$merge` and `$patch` can use absolute or relative `$ref` to point to other schemas previously added to the Ajv instance or to the fragments of the current schema.\n\nSee the package [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) for more information.\n\n\n## Defining custom keywords\n\nThe advantages of using custom keywords are:\n\n- allow creating validation scenarios that cannot be expressed using JSON Schema\n- simplify your schemas\n- help bringing a bigger part of the validation logic to your schemas\n- make your schemas more expressive, less verbose and closer to your application domain\n- implement custom data processors that modify your data (`modifying` option MUST be used in keyword definition) and/or create side effects while the data is being validated\n\nIf a keyword is used only for side-effects and its validation result is pre-defined, use option `valid: true/false` in keyword definition to simplify both generated code (no error handling in case of `valid: true`) and your keyword functions (no need to return any validation result).\n\nThe concerns you have to be aware of when extending JSON Schema standard with custom keywords are the portability and understanding of your schemas. You will have to support these custom keywords on other platforms and to properly document these keywords so that everybody can understand them in your schemas.\n\nYou can define custom keywords with [addKeyword](#api-addkeyword) method. Keywords are defined on the `ajv` instance level - new instances will not have previously defined keywords.\n\nAjv allows defining keywords with:\n- validation function\n- compilation function\n- macro function\n- inline compilation function that should return code (as string) that will be inlined in the currently compiled schema.\n\nExample. `range` and `exclusiveRange` keywords using compiled schema:\n\n```javascript\najv.addKeyword('range', {\n type: 'number',\n compile: function (sch, parentSchema) {\n var min = sch[0];\n var max = sch[1];\n\n return parentSchema.exclusiveRange === true\n ? function (data) { return data > min && data < max; }\n : function (data) { return data >= min && data <= max; }\n }\n});\n\nvar schema = { \"range\": [2, 4], \"exclusiveRange\": true };\nvar validate = ajv.compile(schema);\nconsole.log(validate(2.01)); // true\nconsole.log(validate(3.99)); // true\nconsole.log(validate(2)); // false\nconsole.log(validate(4)); // false\n```\n\nSeveral custom keywords (typeof, instanceof, range and propertyNames) are defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package - they can be used for your schemas and as a starting point for your own custom keywords.\n\nSee [Defining custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md) for more details.\n\n\n## Asynchronous schema compilation\n\nDuring asynchronous compilation remote references are loaded using supplied function. See `compileAsync` [method](#api-compileAsync) and `loadSchema` [option](#options).\n\nExample:\n\n```javascript\nvar ajv = new Ajv({ loadSchema: loadSchema });\n\najv.compileAsync(schema).then(function (validate) {\n var valid = validate(data);\n // ...\n});\n\nfunction loadSchema(uri) {\n return request.json(uri).then(function (res) {\n if (res.statusCode >= 400)\n throw new Error('Loading error: ' + res.statusCode);\n return res.body;\n });\n}\n```\n\n__Please note__: [Option](#options) `missingRefs` should NOT be set to `\"ignore\"` or `\"fail\"` for asynchronous compilation to work.\n\n\n## Asynchronous validation\n\nExample in Node.js REPL: https://tonicdev.com/esp/ajv-asynchronous-validation\n\nYou can define custom formats and keywords that perform validation asynchronously by accessing database or some other service. You should add `async: true` in the keyword or format definition (see [addFormat](#api-addformat), [addKeyword](#api-addkeyword) and [Defining custom keywords](#defining-custom-keywords)).\n\nIf your schema uses asynchronous formats/keywords or refers to some schema that contains them it should have `\"$async\": true` keyword so that Ajv can compile it correctly. If asynchronous format/keyword or reference to asynchronous schema is used in the schema without `$async` keyword Ajv will throw an exception during schema compilation.\n\n__Please note__: all asynchronous subschemas that are referenced from the current or other schemas should have `\"$async\": true` keyword as well, otherwise the schema compilation will fail.\n\nValidation function for an asynchronous custom format/keyword should return a promise that resolves with `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function).\n\nAjv compiles asynchronous schemas to [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent). Async functions are supported in Node.js 7+ and all modern browsers. You can also supply any other transpiler as a function via `processCode` option. See [Options](#options).\n\nThe compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both synchronous and asynchronous schemas.\n\nValidation result will be a promise that resolves with validated data or rejects with an exception `Ajv.ValidationError` that contains the array of validation errors in `errors` property.\n\n\nExample:\n\n```javascript\nvar ajv = new Ajv;\n// require('ajv-async')(ajv);\n\najv.addKeyword('idExists', {\n async: true,\n type: 'number',\n validate: checkIdExists\n});\n\n\nfunction checkIdExists(schema, data) {\n return knex(schema.table)\n .select('id')\n .where('id', data)\n .then(function (rows) {\n return !!rows.length; // true if record is found\n });\n}\n\nvar schema = {\n \"$async\": true,\n \"properties\": {\n \"userId\": {\n \"type\": \"integer\",\n \"idExists\": { \"table\": \"users\" }\n },\n \"postId\": {\n \"type\": \"integer\",\n \"idExists\": { \"table\": \"posts\" }\n }\n }\n};\n\nvar validate = ajv.compile(schema);\n\nvalidate({ userId: 1, postId: 19 })\n.then(function (data) {\n console.log('Data is valid', data); // { userId: 1, postId: 19 }\n})\n.catch(function (err) {\n if (!(err instanceof Ajv.ValidationError)) throw err;\n // data is invalid\n console.log('Validation errors:', err.errors);\n});\n```\n\n### Using transpilers with asynchronous validation functions.\n\n[ajv-async](https://github.com/epoberezkin/ajv-async) uses [nodent](https://github.com/MatAtBread/nodent) to transpile async functions. To use another transpiler you should separately install it (or load its bundle in the browser).\n\n\n#### Using nodent\n\n```javascript\nvar ajv = new Ajv;\nrequire('ajv-async')(ajv);\n// in the browser if you want to load ajv-async bundle separately you can:\n// window.ajvAsync(ajv);\nvar validate = ajv.compile(schema); // transpiled es7 async function\nvalidate(data).then(successFunc).catch(errorFunc);\n```\n\n\n#### Using other transpilers\n\n```javascript\nvar ajv = new Ajv({ processCode: transpileFunc });\nvar validate = ajv.compile(schema); // transpiled es7 async function\nvalidate(data).then(successFunc).catch(errorFunc);\n```\n\nSee [Options](#options).\n\n\n## Filtering data\n\nWith [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation.\n\nThis option modifies original data.\n\nExample:\n\n```javascript\nvar ajv = new Ajv({ removeAdditional: true });\nvar schema = {\n \"additionalProperties\": false,\n \"properties\": {\n \"foo\": { \"type\": \"number\" },\n \"bar\": {\n \"additionalProperties\": { \"type\": \"number\" },\n \"properties\": {\n \"baz\": { \"type\": \"string\" }\n }\n }\n }\n}\n\nvar data = {\n \"foo\": 0,\n \"additional1\": 1, // will be removed; `additionalProperties` == false\n \"bar\": {\n \"baz\": \"abc\",\n \"additional2\": 2 // will NOT be removed; `additionalProperties` != false\n },\n}\n\nvar validate = ajv.compile(schema);\n\nconsole.log(validate(data)); // true\nconsole.log(data); // { \"foo\": 0, \"bar\": { \"baz\": \"abc\", \"additional2\": 2 }\n```\n\nIf `removeAdditional` option in the example above were `\"all\"` then both `additional1` and `additional2` properties would have been removed.\n\nIf the option were `\"failing\"` then property `additional1` would have been removed regardless of its value and property `additional2` would have been removed only if its value were failing the schema in the inner `additionalProperties` (so in the example above it would have stayed because it passes the schema, but any non-number would have been removed).\n\n__Please note__: If you use `removeAdditional` option with `additionalProperties` keyword inside `anyOf`/`oneOf` keywords your validation can fail with this schema, for example:\n\n```json\n{\n \"type\": \"object\",\n \"oneOf\": [\n {\n \"properties\": {\n \"foo\": { \"type\": \"string\" }\n },\n \"required\": [ \"foo\" ],\n \"additionalProperties\": false\n },\n {\n \"properties\": {\n \"bar\": { \"type\": \"integer\" }\n },\n \"required\": [ \"bar\" ],\n \"additionalProperties\": false\n }\n ]\n}\n```\n\nThe intention of the schema above is to allow objects with either the string property \"foo\" or the integer property \"bar\", but not with both and not with any other properties.\n\nWith the option `removeAdditional: true` the validation will pass for the object `{ \"foo\": \"abc\"}` but will fail for the object `{\"bar\": 1}`. It happens because while the first subschema in `oneOf` is validated, the property `bar` is removed because it is an additional property according to the standard (because it is not included in `properties` keyword in the same schema).\n\nWhile this behaviour is unexpected (issues [#129](https://github.com/epoberezkin/ajv/issues/129), [#134](https://github.com/epoberezkin/ajv/issues/134)), it is correct. To have the expected behaviour (both objects are allowed and additional properties are removed) the schema has to be refactored in this way:\n\n```json\n{\n \"type\": \"object\",\n \"properties\": {\n \"foo\": { \"type\": \"string\" },\n \"bar\": { \"type\": \"integer\" }\n },\n \"additionalProperties\": false,\n \"oneOf\": [\n { \"required\": [ \"foo\" ] },\n { \"required\": [ \"bar\" ] }\n ]\n}\n```\n\nThe schema above is also more efficient - it will compile into a faster function.\n\n\n## Assigning defaults\n\nWith [option `useDefaults`](#options) Ajv will assign values from `default` keyword in the schemas of `properties` and `items` (when it is the array of schemas) to the missing properties and items.\n\nThis option modifies original data.\n\n__Please note__: by default the default value is inserted in the generated validation code as a literal (starting from v4.0), so the value inserted in the data will be the deep clone of the default in the schema.\n\nIf you need to insert the default value in the data by reference pass the option `useDefaults: \"shared\"`.\n\nInserting defaults by reference can be faster (in case you have an object in `default`) and it allows to have dynamic values in defaults, e.g. timestamp, without recompiling the schema. The side effect is that modifying the default value in any validated data instance will change the default in the schema and in other validated data instances. See example 3 below.\n\n\nExample 1 (`default` in `properties`):\n\n```javascript\nvar ajv = new Ajv({ useDefaults: true });\nvar schema = {\n \"type\": \"object\",\n \"properties\": {\n \"foo\": { \"type\": \"number\" },\n \"bar\": { \"type\": \"string\", \"default\": \"baz\" }\n },\n \"required\": [ \"foo\", \"bar\" ]\n};\n\nvar data = { \"foo\": 1 };\n\nvar validate = ajv.compile(schema);\n\nconsole.log(validate(data)); // true\nconsole.log(data); // { \"foo\": 1, \"bar\": \"baz\" }\n```\n\nExample 2 (`default` in `items`):\n\n```javascript\nvar schema = {\n \"type\": \"array\",\n \"items\": [\n { \"type\": \"number\" },\n { \"type\": \"string\", \"default\": \"foo\" }\n ]\n}\n\nvar data = [ 1 ];\n\nvar validate = ajv.compile(schema);\n\nconsole.log(validate(data)); // true\nconsole.log(data); // [ 1, \"foo\" ]\n```\n\nExample 3 (inserting \"defaults\" by reference):\n\n```javascript\nvar ajv = new Ajv({ useDefaults: 'shared' });\n\nvar schema = {\n properties: {\n foo: {\n default: { bar: 1 }\n }\n }\n}\n\nvar validate = ajv.compile(schema);\n\nvar data = {};\nconsole.log(validate(data)); // true\nconsole.log(data); // { foo: { bar: 1 } }\n\ndata.foo.bar = 2;\n\nvar data2 = {};\nconsole.log(validate(data2)); // true\nconsole.log(data2); // { foo: { bar: 2 } }\n```\n\n`default` keywords in other cases are ignored:\n\n- not in `properties` or `items` subschemas\n- in schemas inside `anyOf`, `oneOf` and `not` (see [#42](https://github.com/epoberezkin/ajv/issues/42))\n- in `if` subschema of `switch` keyword\n- in schemas generated by custom macro keywords\n\n\n## Coercing data types\n\nWhen you are validating user inputs all your data properties are usually strings. The option `coerceTypes` allows you to have your data types coerced to the types specified in your schema `type` keywords, both to pass the validation and to use the correctly typed data afterwards.\n\nThis option modifies original data.\n\n__Please note__: if you pass a scalar value to the validating function its type will be coerced and it will pass the validation, but the value of the variable you pass won't be updated because scalars are passed by value.\n\n\nExample 1:\n\n```javascript\nvar ajv = new Ajv({ coerceTypes: true });\nvar schema = {\n \"type\": \"object\",\n \"properties\": {\n \"foo\": { \"type\": \"number\" },\n \"bar\": { \"type\": \"boolean\" }\n },\n \"required\": [ \"foo\", \"bar\" ]\n};\n\nvar data = { \"foo\": \"1\", \"bar\": \"false\" };\n\nvar validate = ajv.compile(schema);\n\nconsole.log(validate(data)); // true\nconsole.log(data); // { \"foo\": 1, \"bar\": false }\n```\n\nExample 2 (array coercions):\n\n```javascript\nvar ajv = new Ajv({ coerceTypes: 'array' });\nvar schema = {\n \"properties\": {\n \"foo\": { \"type\": \"array\", \"items\": { \"type\": \"number\" } },\n \"bar\": { \"type\": \"boolean\" }\n }\n};\n\nvar data = { \"foo\": \"1\", \"bar\": [\"false\"] };\n\nvar validate = ajv.compile(schema);\n\nconsole.log(validate(data)); // true\nconsole.log(data); // { \"foo\": [1], \"bar\": false }\n```\n\nThe coercion rules, as you can see from the example, are different from JavaScript both to validate user input as expected and to have the coercion reversible (to correctly validate cases where different types are defined in subschemas of \"anyOf\" and other compound keywords).\n\nSee [Coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md) for details.\n\n\n## API\n\n##### new Ajv(Object options) -> Object\n\nCreate Ajv instance.\n\n\n##### .compile(Object schema) -> Function<Object data>\n\nGenerate validating function and cache the compiled schema for future use.\n\nValidating function returns a boolean value. This function has properties `errors` and `schema`. Errors encountered during the last validation are assigned to `errors` property (it is assigned `null` if there was no errors). `schema` property contains the reference to the original schema.\n\nThe schema passed to this method will be validated against meta-schema unless `validateSchema` option is false. If schema is invalid, an error will be thrown. See [options](#options).\n\n\n##### .compileAsync(Object schema [, Boolean meta] [, Function callback]) -> Promise\n\nAsynchronous version of `compile` method that loads missing remote schemas using asynchronous function in `options.loadSchema`. This function returns a Promise that resolves to a validation function. An optional callback passed to `compileAsync` will be called with 2 parameters: error (or null) and validating function. The returned promise will reject (and the callback will be called with an error) when:\n\n- missing schema can't be loaded (`loadSchema` returns a Promise that rejects).\n- a schema containing a missing reference is loaded, but the reference cannot be resolved.\n- schema (or some loaded/referenced schema) is invalid.\n\nThe function compiles schema and loads the first missing schema (or meta-schema) until all missing schemas are loaded.\n\nYou can asynchronously compile meta-schema by passing `true` as the second parameter.\n\nSee example in [Asynchronous compilation](#asynchronous-schema-compilation).\n\n\n##### .validate(Object schema|String key|String ref, data) -> Boolean\n\nValidate data using passed schema (it will be compiled and cached).\n\nInstead of the schema you can use the key that was previously passed to `addSchema`, the schema id if it was present in the schema or any previously resolved reference.\n\nValidation errors will be available in the `errors` property of Ajv instance (`null` if there were no errors).\n\n__Please note__: every time this method is called the errors are overwritten so you need to copy them to another variable if you want to use them later.\n\nIf the schema is asynchronous (has `$async` keyword on the top level) this method returns a Promise. See [Asynchronous validation](#asynchronous-validation).\n\n\n##### .addSchema(Array<Object>|Object schema [, String key]) -> Ajv\n\nAdd schema(s) to validator instance. This method does not compile schemas (but it still validates them). Because of that dependencies can be added in any order and circular dependencies are supported. It also prevents unnecessary compilation of schemas that are containers for other schemas but not used as a whole.\n\nArray of schemas can be passed (schemas should have ids), the second parameter will be ignored.\n\nKey can be passed that can be used to reference the schema and will be used as the schema id if there is no id inside the schema. If the key is not passed, the schema id will be used as the key.\n\n\nOnce the schema is added, it (and all the references inside it) can be referenced in other schemas and used to validate data.\n\nAlthough `addSchema` does not compile schemas, explicit compilation is not required - the schema will be compiled when it is used first time.\n\nBy default the schema is validated against meta-schema before it is added, and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option.\n\n__Please note__: Ajv uses the [method chaining syntax](https://en.wikipedia.org/wiki/Method_chaining) for all methods with the prefix `add*` and `remove*`.\nThis allows you to do nice things like the following.\n\n```javascript\nvar validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri);\n```\n\n##### .addMetaSchema(Array<Object>|Object schema [, String key]) -> Ajv\n\nAdds meta schema(s) that can be used to validate other schemas. That function should be used instead of `addSchema` because there may be instance options that would compile a meta schema incorrectly (at the moment it is `removeAdditional` option).\n\nThere is no need to explicitly add draft-07 meta schema (http://json-schema.org/draft-07/schema) - it is added by default, unless option `meta` is set to `false`. You only need to use it if you have a changed meta-schema that you want to use to validate your schemas. See `validateSchema`.\n\n\n##### .validateSchema(Object schema) -> Boolean\n\nValidates schema. This method should be used to validate schemas rather than `validate` due to the inconsistency of `uri` format in JSON Schema standard.\n\nBy default this method is called automatically when the schema is added, so you rarely need to use it directly.\n\nIf schema doesn't have `$schema` property, it is validated against draft 6 meta-schema (option `meta` should not be false).\n\nIf schema has `$schema` property, then the schema with this id (that should be previously added) is used to validate passed schema.\n\nErrors will be available at `ajv.errors`.\n\n\n##### .getSchema(String key) -> Function<Object data>\n\nRetrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). The returned validating function has `schema` property with the reference to the original schema.\n\n\n##### .removeSchema([Object schema|String key|String ref|RegExp pattern]) -> Ajv\n\nRemove added/cached schema. Even if schema is referenced by other schemas it can be safely removed as dependent schemas have local references.\n\nSchema can be removed using:\n- key passed to `addSchema`\n- it's full reference (id)\n- RegExp that should match schema id or key (meta-schemas won't be removed)\n- actual schema object that will be stable-stringified to remove schema from cache\n\nIf no parameter is passed all schemas but meta-schemas will be removed and the cache will be cleared.\n\n\n##### .addFormat(String name, String|RegExp|Function|Object format) -> Ajv\n\nAdd custom format to validate strings or numbers. It can also be used to replace pre-defined formats for Ajv instance.\n\nStrings are converted to RegExp.\n\nFunction should return validation result as `true` or `false`.\n\nIf object is passed it should have properties `validate`, `compare` and `async`:\n\n- _validate_: a string, RegExp or a function as described above.\n- _compare_: an optional comparison function that accepts two strings and compares them according to the format meaning. This function is used with keywords `formatMaximum`/`formatMinimum` (defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package). It should return `1` if the first value is bigger than the second value, `-1` if it is smaller and `0` if it is equal.\n- _async_: an optional `true` value if `validate` is an asynchronous function; in this case it should return a promise that resolves with a value `true` or `false`.\n- _type_: an optional type of data that the format applies to. It can be `\"string\"` (default) or `\"number\"` (see https://github.com/epoberezkin/ajv/issues/291#issuecomment-259923858). If the type of data is different, the validation will pass.\n\nCustom formats can be also added via `formats` option.\n\n\n##### .addKeyword(String keyword, Object definition) -> Ajv\n\nAdd custom validation keyword to Ajv instance.\n\nKeyword should be different from all standard JSON Schema keywords and different from previously defined keywords. There is no way to redefine keywords or to remove keyword definition from the instance.\n\nKeyword must start with a letter, `_` or `$`, and may continue with letters, numbers, `_`, `$`, or `-`.\nIt is recommended to use an application-specific prefix for keywords to avoid current and future name collisions.\n\nExample Keywords:\n- `\"xyz-example\"`: valid, and uses prefix for the xyz project to avoid name collisions.\n- `\"example\"`: valid, but not recommended as it could collide with future versions of JSON Schema etc.\n- `\"3-example\"`: invalid as numbers are not allowed to be the first character in a keyword\n\nKeyword definition is an object with the following properties:\n\n- _type_: optional string or array of strings with data type(s) that the keyword applies to. If not present, the keyword will apply to all types.\n- _validate_: validating function\n- _compile_: compiling function\n- _macro_: macro function\n- _inline_: compiling function that returns code (as string)\n- _schema_: an optional `false` value used with \"validate\" keyword to not pass schema\n- _metaSchema_: an optional meta-schema for keyword schema\n- _modifying_: `true` MUST be passed if keyword modifies data\n- _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords.\n- _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function).\n- _async_: an optional `true` value if the validation function is asynchronous (whether it is compiled or passed in _validate_ property); in this case it should return a promise that resolves with a value `true` or `false`. This option is ignored in case of \"macro\" and \"inline\" keywords.\n- _errors_: an optional boolean indicating whether keyword returns errors. If this property is not set Ajv will determine if the errors were set in case of failed validation.\n\n_compile_, _macro_ and _inline_ are mutually exclusive, only one should be used at a time. _validate_ can be used separately or in addition to them to support $data reference.\n\n__Please note__: If the keyword is validating data type that is different from the type(s) in its definition, the validation function will not be called (and expanded macro will not be used), so there is no need to check for data type inside validation function or inside schema returned by macro function (unless you want to enforce a specific type and for some reason do not want to use a separate `type` keyword for that). In the same way as standard keywords work, if the keyword does not apply to the data type being validated, the validation of this keyword will succeed.\n\nSee [Defining custom keywords](#defining-custom-keywords) for more details.\n\n\n##### .getKeyword(String keyword) -> Object|Boolean\n\nReturns custom keyword definition, `true` for pre-defined keywords and `false` if the keyword is unknown.\n\n\n##### .removeKeyword(String keyword) -> Ajv\n\nRemoves custom or pre-defined keyword so you can redefine them.\n\nWhile this method can be used to extend pre-defined keywords, it can also be used to completely change their meaning - it may lead to unexpected results.\n\n__Please note__: schemas compiled before the keyword is removed will continue to work without changes. To recompile schemas use `removeSchema` method and compile them again.\n\n\n##### .errorsText([Array<Object> errors [, Object options]]) -> String\n\nReturns the text with all errors in a String.\n\nOptions can have properties `separator` (string used to separate errors, \", \" by default) and `dataVar` (the variable name that dataPaths are prefixed with, \"data\" by default).\n\n\n## Options\n\nDefaults:\n\n```javascript\n{\n // validation and reporting options:\n $data: false,\n allErrors: false,\n verbose: false,\n $comment: false, // NEW in Ajv version 6.0\n jsonPointers: false,\n uniqueItems: true,\n unicode: true,\n format: 'fast',\n formats: {},\n unknownFormats: true,\n schemas: {},\n logger: undefined,\n // referenced schema options:\n schemaId: '$id',\n missingRefs: true,\n extendRefs: 'ignore', // recommended 'fail'\n loadSchema: undefined, // function(uri: string): Promise {}\n // options to modify validated data:\n removeAdditional: false,\n useDefaults: false,\n coerceTypes: false,\n // asynchronous validation options:\n transpile: undefined, // requires ajv-async package\n // advanced options:\n meta: true,\n validateSchema: true,\n addUsedSchema: true,\n inlineRefs: true,\n passContext: false,\n loopRequired: Infinity,\n ownProperties: false,\n multipleOfPrecision: false,\n errorDataPath: 'object', // deprecated\n messages: true,\n sourceCode: false,\n processCode: undefined, // function (str: string): string {}\n cache: new Cache,\n serialize: undefined\n}\n```\n\n##### Validation and reporting options\n\n- _$data_: support [$data references](#data-reference). Draft 6 meta-schema that is added by default will be extended to allow them. If you want to use another meta-schema you need to use $dataMetaSchema method to add support for $data reference. See [API](#api).\n- _allErrors_: check all rules collecting all errors. Default is to return after the first error.\n- _verbose_: include the reference to the part of the schema (`schema` and `parentSchema`) and validated data in errors (false by default).\n- _$comment_ (NEW in Ajv version 6.0): log or pass the value of `$comment` keyword to a function. Option values:\n - `false` (default): ignore $comment keyword.\n - `true`: log the keyword value to console.\n - function: pass the keyword value, its schema path and root schema to the specified function\n- _jsonPointers_: set `dataPath` property of errors using [JSON Pointers](https://tools.ietf.org/html/rfc6901) instead of JavaScript property access notation.\n- _uniqueItems_: validate `uniqueItems` keyword (true by default).\n- _unicode_: calculate correct length of strings with unicode pairs (true by default). Pass `false` to use `.length` of strings that is faster, but gives \"incorrect\" lengths of strings with unicode pairs - each unicode pair is counted as two characters.\n- _format_: formats validation mode ('fast' by default). Pass 'full' for more correct and slow validation or `false` not to validate formats at all. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode.\n- _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method.\n- _unknownFormats_: handling of unknown formats. Option values:\n - `true` (default) - if an unknown format is encountered the exception is thrown during schema compilation. If `format` keyword value is [$data reference](#data-reference) and it is unknown the validation will fail.\n - `[String]` - an array of unknown format names that will be ignored. This option can be used to allow usage of third party schemas with format(s) for which you don't have definitions, but still fail if another unknown format is used. If `format` keyword value is [$data reference](#data-reference) and it is not in this array the validation will fail.\n - `\"ignore\"` - to log warning during schema compilation and always pass validation (the default behaviour in versions before 5.0.0). This option is not recommended, as it allows to mistype format name and it won't be validated without any error message. This behaviour is required by JSON Schema specification.\n- _schemas_: an array or object of schemas that will be added to the instance. In case you pass the array the schemas must have IDs in them. When the object is passed the method `addSchema(value, key)` will be called for each schema in this object.\n- _logger_: sets the logging method. Default is the global `console` object that should have methods `log`, `warn` and `error`. Option values:\n - custom logger - it should have methods `log`, `warn` and `error`. If any of these methods is missing an exception will be thrown.\n - `false` - logging is disabled.\n\n\n##### Referenced schema options\n\n- _schemaId_: this option defines which keywords are used as schema URI. Option value:\n - `\"$id\"` (default) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged).\n - `\"id\"` - only use `id` keyword as schema URI (as specified in JSON Schema draft-04), ignore `$id` keyword (if it is present a warning will be logged).\n - `\"auto\"` - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation.\n- _missingRefs_: handling of missing referenced schemas. Option values:\n - `true` (default) - if the reference cannot be resolved during compilation the exception is thrown. The thrown error has properties `missingRef` (with hash fragment) and `missingSchema` (without it). Both properties are resolved relative to the current base id (usually schema id, unless it was substituted).\n - `\"ignore\"` - to log error during compilation and always pass validation.\n - `\"fail\"` - to log error and successfully compile schema but fail validation if this rule is checked.\n- _extendRefs_: validation of other keywords when `$ref` is present in the schema. Option values:\n - `\"ignore\"` (default) - when `$ref` is used other keywords are ignored (as per [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3) standard). A warning will be logged during the schema compilation.\n - `\"fail\"` (recommended) - if other validation keywords are used together with `$ref` the exception will be thrown when the schema is compiled. This option is recommended to make sure schema has no keywords that are ignored, which can be confusing.\n - `true` - validate all keywords in the schemas with `$ref` (the default behaviour in versions before 5.0.0).\n- _loadSchema_: asynchronous function that will be used to load remote schemas when `compileAsync` [method](#api-compileAsync) is used and some reference is missing (option `missingRefs` should NOT be 'fail' or 'ignore'). This function should accept remote schema uri as a parameter and return a Promise that resolves to a schema. See example in [Asynchronous compilation](#asynchronous-schema-compilation).\n\n\n##### Options to modify validated data\n\n- _removeAdditional_: remove additional properties - see example in [Filtering data](#filtering-data). This option is not used if schema is added with `addMetaSchema` method. Option values:\n - `false` (default) - not to remove additional properties\n - `\"all\"` - all additional properties are removed, regardless of `additionalProperties` keyword in schema (and no validation is made for them).\n - `true` - only additional properties with `additionalProperties` keyword equal to `false` are removed.\n - `\"failing\"` - additional properties that fail schema validation will be removed (where `additionalProperties` keyword is `false` or schema).\n- _useDefaults_: replace missing properties and items with the values from corresponding `default` keywords. Default behaviour is to ignore `default` keywords. This option is not used if schema is added with `addMetaSchema` method. See examples in [Assigning defaults](#assigning-defaults). Option values:\n - `false` (default) - do not use defaults\n - `true` - insert defaults by value (safer and slower, object literal is used).\n - `\"shared\"` - insert defaults by reference (faster). If the default is an object, it will be shared by all instances of validated data. If you modify the inserted default in the validated data, it will be modified in the schema as well.\n- _coerceTypes_: change data type of data to match `type` keyword. See the example in [Coercing data types](#coercing-data-types) and [coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). Option values:\n - `false` (default) - no type coercion.\n - `true` - coerce scalar data types.\n - `\"array\"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).\n\n\n##### Asynchronous validation options\n\n- _transpile_: Requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. It determines whether Ajv transpiles compiled asynchronous validation function. Option values:\n - `undefined` (default) - transpile with [nodent](https://github.com/MatAtBread/nodent) if async functions are not supported.\n - `true` - always transpile with nodent.\n - `false` - do not transpile; if async functions are not supported an exception will be thrown.\n\n\n##### Advanced options\n\n- _meta_: add [meta-schema](http://json-schema.org/documentation.html) so it can be used by other schemas (true by default). If an object is passed, it will be used as the default meta-schema for schemas that have no `$schema` keyword. This default meta-schema MUST have `$schema` keyword.\n- _validateSchema_: validate added/compiled schemas against meta-schema (true by default). `$schema` property in the schema can be http://json-schema.org/draft-07/schema or absent (draft-07 meta-schema will be used) or can be a reference to the schema previously added with `addMetaSchema` method. Option values:\n - `true` (default) - if the validation fails, throw the exception.\n - `\"log\"` - if the validation fails, log error.\n - `false` - skip schema validation.\n- _addUsedSchema_: by default methods `compile` and `validate` add schemas to the instance if they have `$id` (or `id`) property that doesn't start with \"#\". If `$id` is present and it is not unique the exception will be thrown. Set this option to `false` to skip adding schemas to the instance and the `$id` uniqueness check when these methods are used. This option does not affect `addSchema` method.\n- _inlineRefs_: Affects compilation of referenced schemas. Option values:\n - `true` (default) - the referenced schemas that don't have refs in them are inlined, regardless of their size - that substantially improves performance at the cost of the bigger size of compiled schema functions.\n - `false` - to not inline referenced schemas (they will be compiled as separate functions).\n - integer number - to limit the maximum number of keywords of the schema that will be inlined.\n- _passContext_: pass validation context to custom keyword functions. If this option is `true` and you pass some context to the compiled validation function with `validate.call(context, data)`, the `context` will be available as `this` in your custom keywords. By default `this` is Ajv instance.\n- _loopRequired_: by default `required` keyword is compiled into a single expression (or a sequence of statements in `allErrors` mode). In case of a very large number of properties in this keyword it may result in a very big validation function. Pass integer to set the number of properties above which `required` keyword will be validated in a loop - smaller validation function size but also worse performance.\n- _ownProperties_: by default Ajv iterates over all enumerable object properties; when this option is `true` only own enumerable object properties (i.e. found directly on the object rather than on its prototype) are iterated. Contributed by @mbroadst.\n- _multipleOfPrecision_: by default `multipleOf` keyword is validated by comparing the result of division with parseInt() of that result. It works for dividers that are bigger than 1. For small dividers such as 0.01 the result of the division is usually not integer (even when it should be integer, see issue [#84](https://github.com/epoberezkin/ajv/issues/84)). If you need to use fractional dividers set this option to some positive integer N to have `multipleOf` validated using this formula: `Math.abs(Math.round(division) - division) < 1e-N` (it is slower but allows for float arithmetics deviations).\n- _errorDataPath_ (deprecated): set `dataPath` to point to 'object' (default) or to 'property' when validating keywords `required`, `additionalProperties` and `dependencies`.\n- _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)).\n- _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call).\n- _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options:\n - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass `require('js-beautify').js_beautify`.\n - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information.\n- _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`.\n- _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used.\n\n\n## Validation errors\n\nIn case of validation failure, Ajv assigns the array of errors to `errors` property of validation function (or to `errors` property of Ajv instance when `validate` or `validateSchema` methods were called). In case of [asynchronous validation](#asynchronous-validation), the returned promise is rejected with exception `Ajv.ValidationError` that has `errors` property.\n\n\n### Error objects\n\nEach error is an object with the following properties:\n\n- _keyword_: validation keyword.\n- _dataPath_: the path to the part of the data that was validated. By default `dataPath` uses JavaScript property access notation (e.g., `\".prop[1].subProp\"`). When the option `jsonPointers` is true (see [Options](#options)) `dataPath` will be set using JSON pointer standard (e.g., `\"/prop/1/subProp\"`).\n- _schemaPath_: the path (JSON-pointer as a URI fragment) to the schema of the keyword that failed validation.\n- _params_: the object with the additional information about error that can be used to create custom error messages (e.g., using [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) package). See below for parameters set by all keywords.\n- _message_: the standard error message (can be excluded with option `messages` set to false).\n- _schema_: the schema of the keyword (added with `verbose` option).\n- _parentSchema_: the schema containing the keyword (added with `verbose` option)\n- _data_: the data validated by the keyword (added with `verbose` option).\n\n__Please note__: `propertyNames` keyword schema validation errors have an additional property `propertyName`, `dataPath` points to the object. After schema validation for each property name, if it is invalid an additional error is added with the property `keyword` equal to `\"propertyNames\"`.\n\n\n### Error parameters\n\nProperties of `params` object in errors depend on the keyword that failed validation.\n\n- `maxItems`, `minItems`, `maxLength`, `minLength`, `maxProperties`, `minProperties` - property `limit` (number, the schema of the keyword).\n- `additionalItems` - property `limit` (the maximum number of allowed items in case when `items` keyword is an array of schemas and `additionalItems` is false).\n- `additionalProperties` - property `additionalProperty` (the property not used in `properties` and `patternProperties` keywords).\n- `dependencies` - properties:\n - `property` (dependent property),\n - `missingProperty` (required missing dependency - only the first one is reported currently)\n - `deps` (required dependencies, comma separated list as a string),\n - `depsCount` (the number of required dependencies).\n- `format` - property `format` (the schema of the keyword).\n- `maximum`, `minimum` - properties:\n - `limit` (number, the schema of the keyword),\n - `exclusive` (boolean, the schema of `exclusiveMaximum` or `exclusiveMinimum`),\n - `comparison` (string, comparison operation to compare the data to the limit, with the data on the left and the limit on the right; can be \"<\", \"<=\", \">\", \">=\")\n- `multipleOf` - property `multipleOf` (the schema of the keyword)\n- `pattern` - property `pattern` (the schema of the keyword)\n- `required` - property `missingProperty` (required property that is missing).\n- `propertyNames` - property `propertyName` (an invalid property name).\n- `patternRequired` (in ajv-keywords) - property `missingPattern` (required pattern that did not match any property).\n- `type` - property `type` (required type(s), a string, can be a comma-separated list)\n- `uniqueItems` - properties `i` and `j` (indices of duplicate items).\n- `const` - property `allowedValue` pointing to the value (the schema of the keyword).\n- `enum` - property `allowedValues` pointing to the array of values (the schema of the keyword).\n- `$ref` - property `ref` with the referenced schema URI.\n- `oneOf` - property `passingSchemas` (array of indices of passing schemas, null if no schema passes).\n- custom keywords (in case keyword definition doesn't create errors) - property `keyword` (the keyword name).\n\n\n## Plugins\n\nAjv can be extended with plugins that add custom keywords, formats or functions to process generated code. When such plugin is published as npm package it is recommended that it follows these conventions:\n\n- it exports a function\n- this function accepts ajv instance as the first parameter and returns the same instance to allow chaining\n- this function can accept an optional configuration as the second parameter\n\nIf you have published a useful plugin please submit a PR to add it to the next section.\n\n\n## Related packages\n\n- [ajv-async](https://github.com/epoberezkin/ajv-async) - plugin to configure async validation mode\n- [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface\n- [ajv-errors](https://github.com/epoberezkin/ajv-errors) - plugin for custom error messages\n- [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) - internationalised error messages\n- [ajv-istanbul](https://github.com/epoberezkin/ajv-istanbul) - plugin to instrument generated validation code to measure test coverage of your schemas\n- [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) - plugin with custom validation keywords (if/then/else, select, typeof, etc.)\n- [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) - plugin with keywords $merge and $patch\n- [ajv-pack](https://github.com/epoberezkin/ajv-pack) - produces a compact module exporting validation functions\n\n\n## Some packages using Ajv\n\n- [webpack](https://github.com/webpack/webpack) - a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser\n- [jsonscript-js](https://github.com/JSONScript/jsonscript-js) - the interpreter for [JSONScript](http://www.jsonscript.org) - scripted processing of existing endpoints and services\n- [osprey-method-handler](https://github.com/mulesoft-labs/osprey-method-handler) - Express middleware for validating requests and responses based on a RAML method object, used in [osprey](https://github.com/mulesoft/osprey) - validating API proxy generated from a RAML definition\n- [har-validator](https://github.com/ahmadnassri/har-validator) - HTTP Archive (HAR) validator\n- [jsoneditor](https://github.com/josdejong/jsoneditor) - a web-based tool to view, edit, format, and validate JSON http://jsoneditoronline.org\n- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON Schema http://jsonschemalint.com\n- [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for Node.js\n- [table](https://github.com/gajus/table) - formats data into a string table\n- [ripple-lib](https://github.com/ripple/ripple-lib) - a JavaScript API for interacting with [Ripple](https://ripple.com) in Node.js and the browser\n- [restbase](https://github.com/wikimedia/restbase) - distributed storage with REST API & dispatcher for backend services built to provide a low-latency & high-throughput API for Wikipedia / Wikimedia content\n- [hippie-swagger](https://github.com/CacheControl/hippie-swagger) - [Hippie](https://github.com/vesln/hippie) wrapper that provides end to end API testing with swagger validation\n- [react-form-controlled](https://github.com/seeden/react-form-controlled) - React controlled form components with validation\n- [rabbitmq-schema](https://github.com/tjmehta/rabbitmq-schema) - a schema definition module for RabbitMQ graphs and messages\n- [@query/schema](https://www.npmjs.com/package/@query/schema) - stream filtering with a URI-safe query syntax parsing to JSON Schema\n- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON Schema with expect in mocha tests\n- [grunt-jsonschema-ajv](https://github.com/SignpostMarv/grunt-jsonschema-ajv) - Grunt plugin for validating files against JSON Schema\n- [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) - extract text from bundle into a file\n- [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app\n- [addons-linter](https://github.com/mozilla/addons-linter) - Mozilla Add-ons Linter\n- [gh-pages-generator](https://github.com/epoberezkin/gh-pages-generator) - multi-page site generator converting markdown files to GitHub pages\n- [ESLint](https://github.com/eslint/eslint) - the pluggable linting utility for JavaScript and JSX\n\n\n## Tests\n\n```\nnpm install\ngit submodule update --init\nnpm test\n```\n\n## Contributing\n\nAll validation functions are generated using doT templates in [dot](https://github.com/epoberezkin/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency.\n\n`npm run build` - compiles templates to [dotjs](https://github.com/epoberezkin/ajv/tree/master/lib/dotjs) folder.\n\n`npm run watch` - automatically compiles templates when files in dot folder change\n\nPlease see [Contributing guidelines](https://github.com/epoberezkin/ajv/blob/master/CONTRIBUTING.md)\n\n\n## Changes history\n\nSee https://github.com/epoberezkin/ajv/releases\n\n__Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0).\n\n[Version 5.0.0](https://github.com/epoberezkin/ajv/releases/tag/5.0.0).\n\n[Version 4.0.0](https://github.com/epoberezkin/ajv/releases/tag/4.0.0).\n\n[Version 3.0.0](https://github.com/epoberezkin/ajv/releases/tag/3.0.0).\n\n[Version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0).\n\n\n## License\n\n[MIT](https://github.com/epoberezkin/ajv/blob/master/LICENSE)\n", + "readmeFilename": "README.md", + "_id": "ajv@6.5.2", + "_requested": { + "type": "version", + "registry": true, + "raw": "ajv@6.5.2", + "name": "ajv", + "escapedName": "ajv", + "rawSpec": "6.5.2", + "saveSpec": "[Circular]", + "fetchSpec": "6.5.2" + }, + "_spec": "6.5.2", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "ajv@6.5.2", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/ajv", + "error": "[Circular]", + "extraneous": false + }, + "ajv-keywords": { + "name": "ajv-keywords", + "version": "3.2.0", + "description": "Custom JSON-Schema keywords for Ajv validator", + "main": "index.js", + "scripts": { + "build": "node node_modules/ajv/scripts/compile-dots.js node_modules/ajv/lib keywords", + "prepublish": "npm run build", + "test": "npm run build && npm run eslint && npm run test-cov", + "eslint": "eslint index.js keywords/*.js", + "test-spec": "mocha spec/*.spec.js -R spec", + "test-cov": "istanbul cover -x 'spec/**' node_modules/mocha/bin/_mocha -- spec/*.spec.js -R spec" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/ajv-keywords.git" + }, + "keywords": [ + "JSON-Schema", + "ajv", + "keywords" + ], + "files": [ + "index.js", + "keywords" + ], + "author": { + "name": "Evgeny Poberezkin" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/epoberezkin/ajv-keywords/issues" + }, + "homepage": "https://github.com/epoberezkin/ajv-keywords#readme", + "peerDependencies": { + "ajv": "^6.0.0" + }, + "devDependencies": { + "ajv": "^6.0.0", + "ajv-pack": "^0.3.0", + "chai": "^4.0.2", + "coveralls": "^3.0.0", + "dot": "^1.1.1", + "eslint": "^4.9.0", + "glob": "^7.1.1", + "istanbul": "^0.4.3", + "js-beautify": "^1.7.4", + "json-schema-test": "^2.0.0", + "mocha": "^5.0.0", + "pre-commit": "^1.1.3", + "uuid": "^3.0.1" + }, + "_resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "_integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "_from": "ajv-keywords@3.2.0", + "readme": "# ajv-keywords\n\nCustom JSON-Schema keywords for [Ajv](https://github.com/epoberezkin/ajv) validator\n\n[![Build Status](https://travis-ci.org/epoberezkin/ajv-keywords.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv-keywords)\n[![npm](https://img.shields.io/npm/v/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)\n[![npm downloads](https://img.shields.io/npm/dm/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)\n[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/ajv-keywords/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/ajv-keywords?branch=master)\n[![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv-keywords.svg)](https://greenkeeper.io/)\n[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)\n\n\n## Contents\n\n- [Install](#install)\n- [Usage](#usage)\n- [Keywords](#keywords)\n - [typeof](#typeof)\n - [instanceof](#instanceof)\n - [range and exclusiveRange](#range-and-exclusiverange)\n - [switch](#switch)\n - [select/selectCases/selectDefault](#selectselectcasesselectdefault) (BETA)\n - [patternRequired](#patternrequired)\n - [prohibited](#prohibited)\n - [deepProperties](#deepproperties)\n - [deepRequired](#deeprequired)\n - [uniqueItemProperties](#uniqueitemproperties)\n - [regexp](#regexp)\n - [formatMaximum / formatMinimum and formatExclusiveMaximum / formatExclusiveMinimum](#formatmaximum--formatminimum-and-formatexclusivemaximum--formatexclusiveminimum)\n - [dynamicDefaults](#dynamicdefaults)\n - [transform](#transform)\n- [License](#license)\n\n\n## Install\n\n```\nnpm install ajv-keywords\n```\n\n\n## Usage\n\nTo add all available keywords:\n\n```javascript\nvar Ajv = require('ajv');\nvar ajv = new Ajv;\nrequire('ajv-keywords')(ajv);\n\najv.validate({ instanceof: 'RegExp' }, /.*/); // true\najv.validate({ instanceof: 'RegExp' }, '.*'); // false\n```\n\nTo add a single keyword:\n\n```javascript\nrequire('ajv-keywords')(ajv, 'instanceof');\n```\n\nTo add multiple keywords:\n\n```javascript\nrequire('ajv-keywords')(ajv, ['typeof', 'instanceof']);\n```\n\nTo add a single keyword in browser (to avoid adding unused code):\n\n```javascript\nrequire('ajv-keywords/keywords/instanceof')(ajv);\n```\n\n\n## Keywords\n\n### `typeof`\n\nBased on JavaScript `typeof` operation.\n\nThe value of the keyword should be a string (`\"undefined\"`, `\"string\"`, `\"number\"`, `\"object\"`, `\"function\"`, `\"boolean\"` or `\"symbol\"`) or array of strings.\n\nTo pass validation the result of `typeof` operation on the value should be equal to the string (or one of the strings in the array).\n\n```\najv.validate({ typeof: 'undefined' }, undefined); // true\najv.validate({ typeof: 'undefined' }, null); // false\najv.validate({ typeof: ['undefined', 'object'] }, null); // true\n```\n\n\n### `instanceof`\n\nBased on JavaScript `instanceof` operation.\n\nThe value of the keyword should be a string (`\"Object\"`, `\"Array\"`, `\"Function\"`, `\"Number\"`, `\"String\"`, `\"Date\"`, `\"RegExp\"`, `\"Promise\"` or `\"Buffer\"`) or array of strings.\n\nTo pass validation the result of `data instanceof ...` operation on the value should be true:\n\n```\najv.validate({ instanceof: 'Array' }, []); // true\najv.validate({ instanceof: 'Array' }, {}); // false\najv.validate({ instanceof: ['Array', 'Function'] }, function(){}); // true\n```\n\nYou can add your own constructor function to be recognised by this keyword:\n\n```javascript\nfunction MyClass() {}\nvar instanceofDefinition = require('ajv-keywords').get('instanceof').definition;\n// or require('ajv-keywords/keywords/instanceof').definition;\ninstanceofDefinition.CONSTRUCTORS.MyClass = MyClass;\n\najv.validate({ instanceof: 'MyClass' }, new MyClass); // true\n```\n\n\n### `range` and `exclusiveRange`\n\nSyntax sugar for the combination of minimum and maximum keywords, also fails schema compilation if there are no numbers in the range.\n\nThe value of this keyword must be the array consisting of two numbers, the second must be greater or equal than the first one.\n\nIf the validated value is not a number the validation passes, otherwise to pass validation the value should be greater (or equal) than the first number and smaller (or equal) than the second number in the array. If `exclusiveRange` keyword is present in the same schema and its value is true, the validated value must not be equal to the range boundaries.\n\n```javascript\nvar schema = { range: [1, 3] };\najv.validate(schema, 1); // true\najv.validate(schema, 2); // true\najv.validate(schema, 3); // true\najv.validate(schema, 0.99); // false\najv.validate(schema, 3.01); // false\n\nvar schema = { range: [1, 3], exclusiveRange: true };\najv.validate(schema, 1.01); // true\najv.validate(schema, 2); // true\najv.validate(schema, 2.99); // true\najv.validate(schema, 1); // false\najv.validate(schema, 3); // false\n```\n\n\n### `switch`\n\nThis keyword allows to perform advanced conditional validation.\n\nThe value of the keyword is the array of if/then clauses. Each clause is the object with the following properties:\n\n- `if` (optional) - the value is JSON-schema\n- `then` (required) - the value is JSON-schema or boolean\n- `continue` (optional) - the value is boolean\n\nThe validation process is dynamic; all clauses are executed sequentially in the following way:\n\n1. `if`:\n 1. `if` property is JSON-schema according to which the data is:\n 1. valid => go to step 2.\n 2. invalid => go to the NEXT clause, if this was the last clause the validation of `switch` SUCCEEDS.\n 2. `if` property is absent => go to step 2.\n2. `then`:\n 1. `then` property is `true` or it is JSON-schema according to which the data is valid => go to step 3.\n 2. `then` property is `false` or it is JSON-schema according to which the data is invalid => the validation of `switch` FAILS.\n3. `continue`:\n 1. `continue` property is `true` => go to the NEXT clause, if this was the last clause the validation of `switch` SUCCEEDS.\n 2. `continue` property is `false` or absent => validation of `switch` SUCCEEDS.\n\n```javascript\nrequire('ajv-keywords')(ajv, 'switch');\n\nvar schema = {\n type: 'array',\n items: {\n type: 'integer',\n 'switch': [\n { if: { not: { minimum: 1 } }, then: false },\n { if: { maximum: 10 }, then: true },\n { if: { maximum: 100 }, then: { multipleOf: 10 } },\n { if: { maximum: 1000 }, then: { multipleOf: 100 } },\n { then: false }\n ]\n }\n};\n\nvar validItems = [1, 5, 10, 20, 50, 100, 200, 500, 1000];\n\nvar invalidItems = [1, 0, 2000, 11, 57, 123, 'foo'];\n```\n\n__Please note__: this keyword is moved here from Ajv, mainly to preserve backward compatibility. It is unlikely to become a standard. It's preferable to use `if`/`then`/`else` keywords if possible, as they are likely to be added to the standard. The above schema is equivalent to (for example):\n\n```javascript\n{\n type: 'array',\n items: {\n type: 'integer',\n if: { minimum: 1, maximum: 10 },\n then: true,\n else: {\n if: { maximum: 100 },\n then: { multipleOf: 10 },\n else: {\n if: { maximum: 1000 },\n then: { multipleOf: 100 },\n else: false\n }\n }\n }\n}\n```\n\n\n### `select`/`selectCases`/`selectDefault`\n\nThese keywords allow to choose the schema to validate the data based on the value of some property in the validated data.\n\nThese keywords must be present in the same schema object (`selectDefault` is optional).\n\nThe value of `select` keyword should be a [$data reference](https://github.com/epoberezkin/ajv/tree/5.0.2-beta.0#data-reference) that points to any primitive JSON type (string, number, boolean or null) in the data that is validated. You can also use a constant of primitive type as the value of this keyword (e.g., for debugging purposes).\n\nThe value of `selectCases` keyword must be an object where each property name is a possible string representation of the value of `select` keyword and each property value is a corresponding schema (from draft-06 it can be boolean) that must be used to validate the data.\n\nThe value of `selectDefault` keyword is a schema (from draft-06 it can be boolean) that must be used to validate the data in case `selectCases` has no key equal to the stringified value of `select` keyword.\n\nThe validation succeeds in one of the following cases:\n- the validation of data using selected schema succeeds,\n- none of the schemas is selected for validation,\n- the value of select is undefined (no property in the data that the data reference points to).\n\nIf `select` value (in data) is not a primitive type the validation fails.\n\n__Please note__: these keywords require Ajv `$data` option to support [$data reference](https://github.com/epoberezkin/ajv/tree/5.0.2-beta.0#data-reference).\n\n\n```javascript\nrequire('ajv-keywords')(ajv, 'select');\n\nvar schema = {\n type: object,\n required: ['kind'],\n properties: {\n kind: { type: 'string' }\n },\n select: { $data: '0/kind' },\n selectCases: {\n foo: {\n required: ['foo'],\n properties: {\n kind: {},\n foo: { type: 'string' }\n },\n additionalProperties: false\n },\n bar: {\n required: ['bar'],\n properties: {\n kind: {},\n bar: { type: 'number' }\n },\n additionalProperties: false\n }\n },\n selectDefault: {\n propertyNames: {\n not: { enum: ['foo', 'bar'] }\n }\n }\n};\n\nvar validDataList = [\n { kind: 'foo', foo: 'any' },\n { kind: 'bar', bar: 1 },\n { kind: 'anything_else', not_bar_or_foo: 'any value' }\n];\n\nvar invalidDataList = [\n { kind: 'foo' }, // no propery foo\n { kind: 'bar' }, // no propery bar\n { kind: 'foo', foo: 'any', another: 'any value' }, // additional property\n { kind: 'bar', bar: 1, another: 'any value' }, // additional property\n { kind: 'anything_else', foo: 'any' } // property foo not allowed\n { kind: 'anything_else', bar: 1 } // property bar not allowed\n];\n```\n\n__Please note__: the current implementation is BETA. It does not allow using relative URIs in $ref keywords in schemas in `selectCases` and `selectDefault` that point outside of these schemas. The workaround is to use absolute URIs (that can point to any (sub-)schema added to Ajv, including those inside the current root schema where `select` is used). See [tests](https://github.com/epoberezkin/ajv-keywords/blob/v2.0.0/spec/tests/select.json#L314).\n\n\n### `patternRequired`\n\nThis keyword allows to require the presence of properties that match some pattern(s).\n\nThis keyword applies only to objects. If the data is not an object, the validation succeeds.\n\nThe value of this keyword should be an array of strings, each string being a regular expression. For data object to be valid each regular expression in this array should match at least one property name in the data object.\n\nIf the array contains multiple regular expressions, more than one expression can match the same property name.\n\n```javascript\nvar schema = { patternRequired: [ 'f.*o', 'b.*r' ] };\n\nvar validData = { foo: 1, bar: 2 };\nvar alsoValidData = { foobar: 3 };\n\nvar invalidDataList = [ {}, { foo: 1 }, { bar: 2 } ];\n```\n\n\n### `prohibited`\n\nThis keyword allows to prohibit that any of the properties in the list is present in the object.\n\nThis keyword applies only to objects. If the data is not an object, the validation succeeds.\n\nThe value of this keyword should be an array of strings, each string being a property name. For data object to be valid none of the properties in this array should be present in the object.\n\n```\nvar schema = { prohibited: ['foo', 'bar']};\n\nvar validData = { baz: 1 };\nvar alsoValidData = {};\n\nvar invalidDataList = [\n { foo: 1 },\n { bar: 2 },\n { foo: 1, bar: 2}\n];\n```\n\n\n### `deepProperties`\n\nThis keyword allows to validate deep properties (identified by JSON pointers).\n\nThis keyword applies only to objects. If the data is not an object, the validation succeeds.\n\nThe value should be an object, where keys are JSON pointers to the data, starting from the current position in data, and the values are JSON schemas. For data object to be valid the value of each JSON pointer should be valid according to the corresponding schema.\n\n```javascript\nvar schema = {\n type: 'object',\n deepProperties: {\n \"/users/1/role\": { \"enum\": [\"admin\"] }\n }\n};\n\nvar validData = {\n users: [\n {},\n {\n id: 123,\n role: 'admin'\n }\n ]\n};\n\nvar alsoValidData = {\n users: {\n \"1\": {\n id: 123,\n role: 'admin'\n }\n }\n};\n\nvar invalidData = {\n users: [\n {},\n {\n id: 123,\n role: 'user'\n }\n ]\n};\n\nvar alsoInvalidData = {\n users: {\n \"1\": {\n id: 123,\n role: 'user'\n }\n }\n};\n```\n\n\n### `deepRequired`\n\nThis keyword allows to check that some deep properties (identified by JSON pointers) are available.\n\nThis keyword applies only to objects. If the data is not an object, the validation succeeds.\n\nThe value should be an array of JSON pointers to the data, starting from the current position in data. For data object to be valid each JSON pointer should be some existing part of the data.\n\n```javascript\nvar schema = {\n type: 'object',\n deepRequired: [\"/users/1/role\"]\n};\n\nvar validData = {\n users: [\n {},\n {\n id: 123,\n role: 'admin'\n }\n ]\n};\n\nvar invalidData = {\n users: [\n {},\n {\n id: 123\n }\n ]\n};\n```\n\nSee [json-schema-org/json-schema-spec#203](https://github.com/json-schema-org/json-schema-spec/issues/203#issue-197211916) for an example of the equivalent schema without `deepRequired` keyword.\n\n\n### `uniqueItemProperties`\n\nThe keyword allows to check that some properties in array items are unique.\n\nThis keyword applies only to arrays. If the data is not an array, the validation succeeds.\n\nThe value of this keyword must be an array of strings - property names that should have unique values across all items.\n\n```javascript\nvar schema = { uniqueItemProperties: [ \"id\", \"name\" ] };\n\nvar validData = [\n { id: 1 },\n { id: 2 },\n { id: 3 }\n];\n\nvar invalidData1 = [\n { id: 1 },\n { id: 1 },\n { id: 3 }\n];\n\nvar invalidData2 = [\n { id: 1, name: \"taco\" },\n { id: 2, name: \"taco\" }, // duplicate \"name\"\n { id: 3, name: \"salsa\" }\n];\n```\n\nThis keyword is contributed by [@blainesch](https://github.com/blainesch).\n\n\n### `regexp`\n\nThis keyword allows to use regular expressions with flags in schemas (the standard `pattern` keyword does not support flags).\n\nThis keyword applies only to strings. If the data is not a string, the validation succeeds.\n\nThe value of this keyword can be either a string (the result of `regexp.toString()`) or an object with the properties `pattern` and `flags` (the same strings that should be passed to RegExp constructor).\n\n```javascript\nvar schema = {\n type: 'object',\n properties: {\n foo: { regexp: '/foo/i' },\n bar: { regexp: { pattern: 'bar', flags: 'i' } }\n }\n};\n\nvar validData = {\n foo: 'Food',\n bar: 'Barmen'\n};\n\nvar invalidData = {\n foo: 'fog',\n bar: 'bad'\n};\n```\n\n\n### `formatMaximum` / `formatMinimum` and `formatExclusiveMaximum` / `formatExclusiveMinimum`\n\nThese keywords allow to define minimum/maximum constraints when the format keyword defines ordering.\n\nThese keywords apply only to strings. If the data is not a string, the validation succeeds.\n\nThe value of keyword `formatMaximum` (`formatMinimum`) should be a string. This value is the maximum (minimum) allowed value for the data to be valid as determined by `format` keyword.\n\nWhen this keyword is added, it defines comparison rules for formats `\"date\"`, `\"time\"` and `\"date-time\". Custom formats also can have comparison rules. See [addFormat](https://github.com/epoberezkin/ajv#api-addformat) method.\n\nThe value of keyword `formatExclusiveMaximum` (`formatExclusiveMinimum`) should be a boolean value. These keyword cannot be used without `formatMaximum` (`formatMinimum`). If this keyword value is equal to `true`, the data to be valid should not be equal to the value in `formatMaximum` (`formatMinimum`) keyword.\n\n```javascript\nrequire('ajv-keywords')(ajv, ['formatMinimum', 'formatMaximum']);\n\nvar schema = {\n format: 'date',\n formatMinimum: '2016-02-06',\n formatMaximum: '2016-12-27',\n formatExclusiveMaximum: true\n}\n\nvar validDataList = ['2016-02-06', '2016-12-26', 1];\n\nvar invalidDataList = ['2016-02-05', '2016-12-27', 'abc'];\n```\n\n\n### `dynamicDefaults`\n\nThis keyword allows to assign dynamic defaults to properties, such as timestamps, unique IDs etc.\n\nThis keyword only works if `useDefaults` options is used and not inside `anyOf` keywrods etc., in the same way as [default keyword treated by Ajv](https://github.com/epoberezkin/ajv#assigning-defaults).\n\nThe keyword should be added on the object level. Its value should be an object with each property corresponding to a property name, in the same way as in standard `properties` keyword. The value of each property can be:\n\n- an identifier of default function (a string)\n- an object with properties `func` (an identifier) and `args` (an object with parameters that will be passed to this function during schema compilation - see examples).\n\nThe properties used in `dynamicDefaults` should not be added to `required` keyword (or validation will fail), because unlike `default` this keyword is processed after validation.\n\nThere are several predefined dynamic default functions:\n\n- `\"timestamp\"` - current timestamp in milliseconds\n- `\"datetime\"` - current date and time as string (ISO, valid according to `date-time` format)\n- `\"date\"` - current date as string (ISO, valid according to `date` format)\n- `\"time\"` - current time as string (ISO, valid according to `time` format)\n- `\"random\"` - pseudo-random number in [0, 1) interval\n- `\"randomint\"` - pseudo-random integer number. If string is used as a property value, the function will randomly return 0 or 1. If object `{func: 'randomint', max: N}` is used then the default will be an integer number in [0, N) interval.\n- `\"seq\"` - sequential integer number starting from 0. If string is used as a property value, the default sequence will be used. If object `{func: 'seq', name: 'foo'}` is used then the sequence with name `\"foo\"` will be used. Sequences are global, even if different ajv instances are used.\n\n```javascript\nvar schema = {\n type: 'object',\n dynamicDefaults: {\n ts: 'datetime',\n r: { func: 'randomint', max: 100 },\n id: { func: 'seq', name: 'id' }\n },\n properties: {\n ts: {\n type: 'string',\n format: 'datetime'\n },\n r: {\n type: 'integer',\n minimum: 0,\n maximum: 100,\n exclusiveMaximum: true\n },\n id: {\n type: 'integer',\n minimum: 0\n }\n }\n};\n\nvar data = {};\najv.validate(data); // true\ndata; // { ts: '2016-12-01T22:07:28.829Z', r: 25, id: 0 }\n\nvar data1 = {};\najv.validate(data1); // true\ndata1; // { ts: '2016-12-01T22:07:29.832Z', r: 68, id: 1 }\n\najv.validate(data1); // true\ndata1; // didn't change, as all properties were defined\n```\n\nYou can add your own dynamic default function to be recognised by this keyword:\n\n```javascript\nvar uuid = require('uuid');\n\nfunction uuidV4() { return uuid.v4(); }\n\nvar definition = require('ajv-keywords').get('dynamicDefaults').definition;\n// or require('ajv-keywords/keywords/dynamicDefaults').definition;\ndefinition.DEFAULTS.uuid = uuidV4;\n\nvar schema = {\n dynamicDefaults: { id: 'uuid' },\n properties: { id: { type: 'string', format: 'uuid' } }\n};\n\nvar data = {};\najv.validate(schema, data); // true\ndata; // { id: 'a1183fbe-697b-4030-9bcc-cfeb282a9150' };\n\nvar data1 = {};\najv.validate(schema, data1); // true\ndata1; // { id: '5b008de7-1669-467a-a5c6-70fa244d7209' }\n```\n\nYou also can define dynamic default that accepts parameters, e.g. version of uuid:\n\n```javascript\nvar uuid = require('uuid');\n\nfunction getUuid(args) {\n var version = 'v' + (arvs && args.v || 4);\n return function() {\n return uuid[version]();\n };\n}\n\nvar definition = require('ajv-keywords').get('dynamicDefaults').definition;\ndefinition.DEFAULTS.uuid = getUuid;\n\nvar schema = {\n dynamicDefaults: {\n id1: 'uuid', // v4\n id2: { func: 'uuid', v: 4 }, // v4\n id3: { func: 'uuid', v: 1 } // v1\n }\n};\n```\n\n### `transform`\n\nThis keyword allows a string to be modified before validation. \n\nThese keywords apply only to strings. If the data is not a string, the transform is skipped.\n\nThere are limitation due to how ajv is written:\n- a stand alone string cannot be transformed. ie `data = 'a'; ajv.validate(schema, data);`\n- currently cannot work with `ajv-pack`\n\n**Supported options:**\n- `trim`: remove whitespace from start and end\n- `trimLeft`: remove whitespace from start\n- `trimRight`: remove whitespace from end\n- `toLowerCase`: case string to all lower case\n- `toUpperCase`: case string to all upper case\n- `toEnumCase`: case string to match case in schema\n\nOptions are applied in the order they are listed.\n\nNote: `toEnumCase` requires that all allowed values are unique when case insensitive.\n\n**Example: multiple options**\n```javascript\nrequire('ajv-keywords')(ajv, ['transform']);\n\nvar schema = {\n type: 'array',\n items: {\n type:'string',\n transform:['trim','lowercase']\n }\n};\n\nvar data = [' MixCase '];\navj.validate(schema, data);\nconsole.log(data); // ['mixcase']\n\n```\n\n**Example: `enumcase`**\n```javascript\nrequire('ajv-keywords')(ajv, ['transform']);\n\nvar schema = {\n type: 'array',\n items: {\n type:'string',\n transform:['trim','enumcase'],\n enum:['pH']\n }\n};\n\nvar data = ['ph',' Ph','PH','pH '];\navj.validate(schema, data);\nconsole.log(data); // ['pH','pH','pH','pH']\n```\n\n\n## License\n\n[MIT](https://github.com/epoberezkin/ajv-keywords/blob/master/LICENSE)\n", + "readmeFilename": "README.md", + "_id": "ajv-keywords@3.2.0", + "_requested": { + "type": "version", + "registry": true, + "raw": "ajv-keywords@3.2.0", + "name": "ajv-keywords", + "escapedName": "ajv-keywords", + "rawSpec": "3.2.0", + "saveSpec": "[Circular]", + "fetchSpec": "3.2.0" + }, + "_spec": "3.2.0", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "ajv-keywords@3.2.0", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "dependencies": {}, + "optionalDependencies": {}, + "_dependencies": {}, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/ajv-keywords", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "@commitlint/cli": "^5.2.8", + "@commitlint/config-angular": "^5.1.1", + "@webpack-contrib/eslint-config-webpack": "^2.0.2", + "babel-cli": "^6.26.0", + "babel-jest": "^22.2.2", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.6.1", + "conventional-github-releaser": "^2.0.0", + "cross-env": "^5.1.3", + "del": "^3.0.0", + "del-cli": "^1.1.0", + "eslint": "^4.17.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-prettier": "^2.6.0", + "husky": "^0.14.3", + "jest": "^22.2.2", + "lint-staged": "^6.1.0", + "memory-fs": "^0.4.1", + "nsp": "^3.1.0", + "pre-commit": "^1.2.2", + "prettier": "^1.10.2", + "standard-version": "^4.3.0", + "webpack": "^3.11.0", + "webpack-defaults": "^2.0.0-rc.4" + }, + "engines": { + "node": ">= 4.8.0 || >= 6.9.0 || >= 8.9.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + }, + "homepage": "https://github.com/webpack-contrib/schema-utils", + "repository": { + "type": "git", + "url": "git+https://github.com/webpack-contrib/schema-utils.git" + }, + "bugs": { + "url": "https://github.com/webpack-contrib/schema-utils/issues" + }, + "pre-commit": "lint-staged", + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "_resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "_integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "_from": "schema-utils@0.4.5", + "readme": "[![npm][npm]][npm-url]\n[![node][node]][node-url]\n[![deps][deps]][deps-url]\n[![test][test]][test-url]\n[![coverage][cover]][cover-url]\n[![chat][chat]][chat-url]\n\n
\n \n \n \n \n \n \n

Schema Utils

\n
\n\n

Install

\n\n```bash\nnpm i schema-utils\n```\n\n

Usage

\n\n### `validateOptions`\n\n**schema.json**\n```js\n{\n \"type\": \"object\",\n \"properties\": {\n // Options...\n },\n \"additionalProperties\": false\n}\n```\n\n```js\nimport schema from 'path/to/schema.json'\nimport validateOptions from 'schema-utils'\n\nvalidateOptions(schema, options, 'Loader/Plugin Name')\n```\n\n

Examples

\n\n**schema.json**\n```json\n{\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"test\": {\n \"anyOf\": [\n { \"type\": \"array\" },\n { \"type\": \"string\" },\n { \"instanceof\": \"RegExp\" }\n ]\n },\n \"transform\": {\n \"instanceof\": \"Function\"\n },\n \"sourceMap\": {\n \"type\": \"boolean\"\n }\n },\n \"additionalProperties\": false\n}\n```\n\n### `Loader`\n\n```js\nimport { getOptions } from 'loader-utils'\nimport validateOptions from 'schema-utils'\n\nimport schema from 'path/to/schema.json'\n\nfunction loader (src, map) {\n const options = getOptions(this) || {}\n\n validateOptions(schema, options, 'Loader Name')\n\n // Code...\n}\n```\n\n### `Plugin`\n\n```js\nimport validateOptions from 'schema-utils'\n\nimport schema from 'path/to/schema.json'\n\nclass Plugin {\n constructor (options) {\n validateOptions(schema, options, 'Plugin Name')\n\n this.options = options\n }\n\n apply (compiler) {\n // Code...\n }\n}\n```\n\n

Maintainers

\n\n\n \n \n \n \n \n \n \n
\n \n
\n Juho Vepsäläinen\n
\n \n
\n Joshua Wiens\n
\n \n
\n Michael Ciniawsky\n
\n\n\n[npm]: https://img.shields.io/npm/v/schema-utils.svg\n[npm-url]: https://npmjs.com/package/schema-utils\n\n[node]: https://img.shields.io/node/v/schema-utils.svg\n[node-url]: https://nodejs.org\n\n[deps]: https://david-dm.org/webpack-contrib/schema-utils.svg\n[deps-url]: https://david-dm.org/webpack-contrib/schema-utils\n\n[test]: http://img.shields.io/travis/webpack-contrib/schema-utils.svg\n[test-url]: https://travis-ci.org/webpack-contrib/schema-utils\n\n[cover]: https://codecov.io/gh/webpack-contrib/schema-utils/branch/master/graph/badge.svg\n[cover-url]: https://codecov.io/gh/webpack-contrib/schema-utils\n\n[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg\n[chat-url]: https://gitter.im/webpack/webpack\n", + "readmeFilename": "README.md", + "_id": "schema-utils@0.4.5", + "_requested": { + "type": "version", + "registry": true, + "raw": "schema-utils@0.4.5", + "name": "schema-utils", + "escapedName": "schema-utils", + "rawSpec": "0.4.5", + "saveSpec": "[Circular]", + "fetchSpec": "0.4.5" + }, + "_spec": "0.4.5", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "schema-utils@0.4.5", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/schema-utils", + "error": "[Circular]", + "extraneous": false + } + }, + "devDependencies": { + "babel-cli": "^6.0.0", + "babel-jest": "^21.0.0", + "babel-plugin-transform-object-rest-spread": "^6.0.0", + "babel-polyfill": "^6.0.0", + "babel-preset-env": "^1.0.0", + "cross-env": "^5.0.0", + "del": "^3.0.0", + "del-cli": "^1.0.0", + "eslint": "^4.0.0", + "eslint-config-webpack": "^1.0.0", + "eslint-plugin-import": "^2.2.0", + "jest": "^21.0.0", + "lint-staged": "^4.0.0", + "nsp": "^2.6.0", + "pre-commit": "^1.0.0", + "standard-version": "^4.0.0", + "webpack": "^3.0.0", + "webpack-defaults": "^1.6.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/webpack-contrib/worker-loader.git" + }, + "bugs": { + "url": "https://github.com/webpack-contrib/worker-loader/issues" + }, + "homepage": "https://github.com/webpack-contrib/html-loader", + "license": "MIT", + "pre-commit": "lint-staged", + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "_resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-1.1.1.tgz", + "_integrity": "sha512-qJZLVS/jMCBITDzPo/RuweYSIG8VJP5P67mP/71alGyTZRe1LYJFdwLjLalY3T5ifx0bMDRD3OB6P2p1escvlg==", + "_from": "worker-loader@1.1.1", + "readme": "[![npm][npm]][npm-url]\r\n[![node][node]][node-url]\r\n[![deps][deps]][deps-url]\r\n[![test][test]][test-url]\r\n[![coverage][cover]][cover-url]\r\n[![chat][chat]][chat-url]\r\n\r\n
\r\n \r\n \r\n \r\n

Worker Loader

\r\n

This loader registers the script as Web Worker

\r\n

\r\n\r\n\r\n

Install

\r\n\r\n```bash\r\nnpm i -D worker-loader\r\n```\r\n\r\n

Usage

\r\n\r\n### `Inlined`\r\n\r\n**App.js**\r\n```js\r\nimport Worker from 'worker-loader!./Worker.js';\r\n```\r\n\r\n### `Config`\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n module: {\r\n rules: [\r\n {\r\n test: /\\.worker\\.js$/,\r\n use: { loader: 'worker-loader' }\r\n }\r\n ]\r\n }\r\n}\r\n```\r\n\r\n**App.js**\r\n```js\r\nimport Worker from './file.worker.js';\r\n\r\nconst worker = new Worker();\r\n\r\nworker.postMessage({ a: 1 });\r\nworker.onmessage = function (event) {};\r\n\r\nworker.addEventListener(\"message\", function (event) {});\r\n```\r\n\r\n

Options

\r\n\r\n|Name|Type|Default|Description|\r\n|:--:|:--:|:-----:|:----------|\r\n|[**`name`**](#name)|`{String}`|`[hash].worker.js`|Set a custom name for the output script|\r\n|[**`inline`**](#inline)|`{Boolean}`|`false`|Inline the worker as a BLOB|\r\n|[**`fallback`**](#fallback)|`{Boolean}`|`false`|Require a fallback for non-worker supporting environments|\r\n|[**`publicPath`**](#publicPath)|`{String}`|`null`|Override the path from which worker scripts are downloaded|\r\n\r\n### `name`\r\n\r\nTo set a custom name for the output script, use the `name` parameter. The name may contain the string `[hash]`, which will be replaced with a content dependent hash for caching purposes\r\n\r\n*webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader',\r\n options: { name: 'WorkerName.[hash].js' }\r\n}\r\n```\r\n\r\n### `inline`\r\n\r\nYou can also inline the worker as a BLOB with the `inline` parameter\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader',\r\n options: { inline: true }\r\n}\r\n```\r\n\r\n> ℹ️ Inline mode will also create chunks for browsers without support for inline workers, to disable this behavior just set `fallback` parameter as `false`\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader'\r\n options: { inline: true, fallback: false }\r\n}\r\n```\r\n\r\n### `fallback`\r\n\r\nRequire a fallback for non-worker supporting environments\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader'\r\n options: { fallback: false }\r\n}\r\n```\r\n\r\n### `publicPath`\r\n\r\nOverrides the path from which worker scripts are downloaded. If not specified, the same public path used for other\r\nwebpack assets is used\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader'\r\n options: { publicPath: '/scripts/workers/' }\r\n}\r\n```\r\n\r\n

Examples

\r\n\r\nThe worker file can import dependencies just like any other file\r\n\r\n**Worker.js**\r\n```js\r\nconst _ = require('lodash')\r\n\r\nconst obj = { foo: 'foo' }\r\n\r\n_.has(obj, 'foo')\r\n\r\n// Post data to parent thread\r\nself.postMessage({ foo: 'foo' })\r\n\r\n// Respond to message from parent thread\r\nself.addEventListener('message', (event) => console.log(event)) \r\n```\r\n\r\n### `Integrating with ES2015 Modules`\r\n\r\n> ℹ️ You can even use ES2015 Modules if you have the [`babel-loader`](https://github.com/babel/babel-loader) configured.\r\n\r\n**Worker.js**\r\n```js\r\nimport _ from 'lodash'\r\n\r\nconst obj = { foo: 'foo' }\r\n\r\n_.has(obj, 'foo')\r\n\r\n// Post data to parent thread\r\nself.postMessage({ foo: 'foo' })\r\n\r\n// Respond to message from parent thread\r\nself.addEventListener('message', (event) => console.log(event))\r\n```\r\n\r\n### `Integrating with TypeScript`\r\n\r\nTo integrate with TypeScript, you will need to define a custom module for the exports of your worker\r\n\r\n**typings/custom.d.ts**\r\n```typescript\r\ndeclare module \"worker-loader!*\" {\r\n class WebpackWorker extends Worker {\r\n constructor();\r\n }\r\n\r\n export = WebpackWorker;\r\n}\r\n```\r\n\r\n**Worker.ts**\r\n```typescript\r\nconst ctx: Worker = self as any;\r\n\r\n// Post data to parent thread\r\nctx.postMessage({ foo: \"foo\" });\r\n\r\n// Respond to message from parent thread\r\nctx.addEventListener(\"message\", (event) => console.log(event));\r\n```\r\n\r\n**App.ts**\r\n```typescript\r\nimport Worker = require(\"worker-loader!./Worker\");\r\n\r\nconst worker = new Worker();\r\n\r\nworker.postMessage({ a: 1 });\r\nworker.onmessage = (event) => {};\r\n\r\nworker.addEventListener(\"message\", (event) => {});\r\n```\r\n\r\n### `Cross-Origin Policy`\r\n\r\n[`WebWorkers`](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) are restricted by a [same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy), so if your `webpack` assets are not being served from the same origin as your application, their download may be blocked by your browser. This scenario can commonly occur if you are hosting your assets under a CDN domain. Even downloads from the `webpack-dev-server` could be blocked. There are two workarounds\r\n\r\nFirstly, you can inline the worker as a blob instead of downloading it as an external script via the [`inline`](#inline) parameter\r\n\r\n**App.js**\r\n```js\r\nimport Worker from './file.worker.js';\r\n```\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader'\r\n options: { inline: true }\r\n}\r\n```\r\n\r\nSecondly, you may override the base download URL for your worker script via the [`publicPath`](#publicpath) option\r\n\r\n**App.js**\r\n```js\r\n// This will cause the worker to be downloaded from `/workers/file.worker.js`\r\nimport Worker from './file.worker.js';\r\n```\r\n\r\n**webpack.config.js**\r\n```js\r\n{\r\n loader: 'worker-loader'\r\n options: { publicPath: '/workers/' }\r\n}\r\n```\r\n\r\n

Maintainers

\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n
\r\n Bogdan Chadkin\r\n
\r\n \r\n \r\n
\r\n Juho Vepsäläinen\r\n
\r\n
\r\n \r\n \r\n
\r\n Joshua Wiens\r\n
\r\n
\r\n \r\n \r\n
\r\n Michael Ciniawsky\r\n
\r\n
\r\n \r\n \r\n
\r\n Alexander Krasnoyarov\r\n
\r\n
\r\n\r\n\r\n[npm]: https://img.shields.io/npm/v/worker-loader.svg\r\n[npm-url]: https://npmjs.com/package/worker-loader\r\n\r\n[node]: https://img.shields.io/node/v/cache-loader.svg\r\n[node-url]: https://nodejs.org\r\n\r\n[deps]: https://david-dm.org/webpack-contrib/worker-loader.svg\r\n[deps-url]: https://david-dm.org/webpack-contrib/worker-loader\r\n\r\n[test]: http://img.shields.io/travis/webpack-contrib/worker-loader.svg\r\n[test-url]: https://travis-ci.org/webpack-contrib/worker-loader\r\n\r\n[cover]: https://codecov.io/gh/webpack-contrib/cache-loader/branch/master/graph/badge.svg\r\n[cover-url]: https://codecov.io/gh/webpack-contrib/cache-loader\r\n\r\n[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg\r\n[chat-url]: https://gitter.im/webpack/webpack\r\n", + "readmeFilename": "README.md", + "_id": "worker-loader@1.1.1", + "_requested": { + "type": "version", + "registry": true, + "raw": "worker-loader@1.1.1", + "name": "worker-loader", + "escapedName": "worker-loader", + "rawSpec": "1.1.1", + "saveSpec": "[Circular]", + "fetchSpec": "1.1.1" + }, + "_spec": "1.1.1", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "worker-loader@1.1.1", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "optionalDependencies": {}, + "_dependencies": { + "loader-utils": "^1.0.0", + "schema-utils": "^0.4.0" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/worker-loader", + "error": "[Circular]", + "extraneous": false + } + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0" + }, + "browser": { + "fs": false, + "http": false, + "https": false, + "node-ensure": false + }, + "format": "amd", + "repository": { + "type": "git", + "url": "git+https://github.com/mozilla/pdfjs-dist.git" + }, + "_resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.0.489.tgz", + "_integrity": "sha1-Y+VLKSqGeQpFRpfrRNQ0e4+/rSc=", + "_from": "pdfjs-dist@2.0.489", + "readme": "# PDF.js\n\nPDF.js is a Portable Document Format (PDF) library that is built with HTML5.\nOur goal is to create a general-purpose, web standards-based platform for\nparsing and rendering PDFs.\n\nThis is a pre-built version of the PDF.js source code. It is automatically\ngenerated by the build scripts.\n\nSee https://github.com/mozilla/pdf.js for learning and contributing.\n", + "readmeFilename": "README.md", + "_id": "pdfjs-dist@2.0.489", + "_requested": { + "type": "version", + "registry": true, + "raw": "pdfjs-dist@2.0.489", + "name": "pdfjs-dist", + "escapedName": "pdfjs-dist", + "rawSpec": "2.0.489", + "saveSpec": "[Circular]", + "fetchSpec": "2.0.489" + }, + "_spec": "2.0.489", + "_where": "/Users/dvuika/github/alfresco-content-app", + "_args": [ + [ + "pdfjs-dist@2.0.489", + "/Users/dvuika/github/alfresco-content-app" + ] + ], + "devDependencies": {}, + "optionalDependencies": {}, + "_dependencies": { + "node-ensure": "^0.0.0", + "worker-loader": "^1.1.1" + }, + "path": "/Users/dvuika/github/alfresco-content-app/node_modules/pdfjs-dist", + "error": "[Circular]", + "extraneous": false, + "peerMissing": [ + { + "requiredBy": "@alfresco/adf-core@2.6.1", + "requires": "pdfjs-dist@2.0.303" + } + ] + }, + "peerMissing": true + }, + "rxjs": { + "version": "6.3.3", + "from": "rxjs@6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz" + }, + "zone.js": { + "version": "0.8.26", + "from": "zone.js@0.8.26", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.26.tgz" + } + } +} diff --git a/tsconfig.json b/tsconfig.json index ebc2864342..f06fa76b9b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compileOnSave": false, "compilerOptions": { + "importHelpers": true, "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false, @@ -10,10 +11,11 @@ "noUnusedLocals": true, "target": "es5", "typeRoots": ["node_modules/@types"], - "lib": ["es2017", "dom"], + "lib": ["es2018", "dom"], "module": "es2015", "baseUrl": "./", - "paths": {} + "paths": {}, + "resolveJsonModule": true }, "exclude": ["node_modules"], "angularCompilerOptions": { diff --git a/tslint.json b/tslint.json index 33cde3b23a..82c1fb1635 100644 --- a/tslint.json +++ b/tslint.json @@ -55,7 +55,12 @@ "check-type" ], "directive-selector": [true, "attribute", "aca", "camelCase"], - "component-selector": [true, "element", ["app", "aca"], "kebab-case"], + "component-selector": [ + true, + "element", + ["app", "aca", "adf"], + "kebab-case" + ], "use-input-property-decorator": true, "use-output-property-decorator": true, "use-host-property-decorator": false,