diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 58a5e25486..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,325 +0,0 @@ -# Zanata change log summary - -## zanata-3.0.0 - * File upload - * Move raw document storage to file system - * Implement virus scanning using ClamAV (clamdscan) - * Add descriptions on project type selectors - * Allow adapter parameters to be set on source document upload - * Editor improvements - * Add attention key shortcut: Alt+X - * Add attention shortcut to copy from source: Alt+X,G - * TMX import/export - * Allow users to export translations to TMX (from Project/Version pages) - * Allow admins to export **all** project translations to TMX (from Projects page) - * Allow admins to import and export TMX translation memories (from Admin pages) - * Imported TMX shown in translation memory search results - * Translation review/approval - * Coordinators can assign reviewers for their languages - * Project maintainers can require review for translations in their projects - * Reviewers can approve or reject translations - * Translators and reviewers can add comments to translations - * New visual style for Zanata - * Add Zanata dashboard - * Recent translation/review activity - * List of maintained projects - * Upgrade platform to JBoss EAP 6.1 - -## zanata-2.3.1 - * Bug fixes: - * Prevent incorrect validation warnings with concurrent edits - * Search result back to editor causes multiple code mirror focus - * Support message bookmark - - -## TODO fill in releases between 2.0.3 and 2.3.1 - -## zanata-2.0.3 - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?j_top=OR&f1=cf_fixed_in&o1=substring&classification=Community&o2=substring&query_format=advanced&f2=cf_fixed_in&bug_status=VERIFIED&bug_status=RELEASE_PENDING&bug_status=POST&bug_status=CLOSED&v1=2.0.3&product=Zanata - * Allow admin to add extra locales by typing in the BCP-47 locale code. - * TM Merge reports what it did - * Allow choice of editor page size - * Support txt, dtd and open document format (REST & web interface) - * Editor option to disable CodeMirror (to enable browser spell-check) - * Detect loss of connection to server - * Fix for problem creating users with Kerberos - * Allow Project Maintainers to Delete a Source Document - -## zanata-2.0.2 - * Bug fixes for document search/navigation - -## zanata-2.0.1 - * Update jboss-el to avoid bad artifact in repository - -## zanata-2.0.0 - * UI redesign - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?j_top=OR&f1=cf_fixed_in&o1=substring&classification=Community&o2=substring&query_format=advanced&f2=cf_fixed_in&bug_status=CLOSED&v1=1.8.0&v2=2.0.0&product=Zanata - * Performance: async push service to avoid timeouts when pushing source/target - * Performance: improve performance when loading large documents - * Allow user to save work when concurrent edit occurs - * Include last translator information in TM info box - * Web analytics (Piwik integration) - * Navigation breadcrumbs - * Bugzilla link in UI - * Get Stats about Translation Documents via REST - * Remove blinking notification in editor - * Configurable page size - * Advanced glossary features - * Open ID Authentication - * Admin role assignment configuration for authentication types - * Highlight tags in editor fields (CodeMirror for editor) - * Translation editor rewrite - * Project-level default Copy Trans options - * Red bars for translations with validation warnings should stay in red when moving to the next row - * Option to show word or message based statistics - * Visible whitespace in editor - * View history of translations for a text flow - -## zanata-1.7.3 - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?f1=cf_fixed_in&o1=substring&classification=Community&query_format=advanced&bug_status=CLOSED&v1=1.7.3&product=Zanata - -## zanata-1.7.2 - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.7.2&query_format=advanced&bug_status=CLOSED&product=Zanata - -## zanata-1.7.1 - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.7.1&query_format=advanced&bug_status=CLOSED&product=Zanata - -## zanata-1.7.0 - * UI Improvements - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6.2&target_release=1.7&query_format=advanced&bug_status=CLOSED&product=Zanata - * Don't enforce locales for source documents - * On-Demand copy trans - * Email log appender - * Centralise management of key shortcuts in Zanata - * Improvements to reindexing (processing in small batches, index classes separately) - * Editor validation for XML entities - * Undo button for saved translations - * Translation Memory merge in editor - * Add support for positional strings in printf validator - * Translation Memory now uses word-based indexing - - -## zanata-1.6.1 - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6.1&query_format=advanced&bug_status=CLOSED&product=Zanata - * Allow Zanata to add locales for which plural form is not known - -## zanata-1.6.0 - * UI Improvements - * Bug fixes: https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6&target_release=1.6-alpha-1&target_release=1.6-beta-1&query_format=advanced&bug_status=CLOSED&product=Zanata - * Allow Project Maintainers to edit all language files - * Glossary suggestions - * Add the ability to specify custom locales that are not enabled by default - * Upgrade Liquibase to version 2.0 - * Project grouping - * Support plural forms - * Offline translation feature via web UI - * Allow translators to push translations using Maven client - * Indicators for simultaneous edits - * Java style variable validations in translation editor - * "Create Project" for non-administrator users - * Display page context in window title - * Ability to monitor Zanata server statistics (JavaMelody) - * Overview for available keyboard shortcuts in web editor - -## zanata-1.5.0 - * Allow bookmarking of selected document, document list filter and current view: https://bugzilla.redhat.com/show_bug.cgi?id=757621 - * Add workspace query string parameters for generating a custom doclist with a custom title: https://bugzilla.redhat.com/show_bug.cgi?id=758587 - * e.g. &title=Custom%20title&doc=full/path/of/first/doc&doc=full/path/of/second/doc - * Redesign of color scheme translation editor workspace layout - * Project/project iteration status changes: ACTIVE, READONLY, and OBSOLETE - * Allow readonly access to retired project/project iteration: https://bugzilla.redhat.com/show_bug.cgi?id=755759 - * Implement filter messages in the editor by translation status: https://bugzilla.redhat.com/show_bug.cgi?id=773459 - * Implement validation in editor: - * Newline validation on leading and trailing string: https://bugzilla.redhat.com/show_bug.cgi?id=768802 - * Variables to be checked for consistency: https://bugzilla.redhat.com/show_bug.cgi?id=769471 - * XML and HTML tags to be checked for completeness: https://bugzilla.redhat.com/show_bug.cgi?id=756235 - * Project list filtering based on status - * Overall statistics page for Admin - * Add file download page with the option to download a single PO file, or a zip with a project iteration's files for one locale - * Translation memory results now have highlighted differences - * Activate entity caching - * Maven client option to create 'skeleton' PO files when no translations are present - * Maven client option to log detailed client-server message information - * Generate Zanata Rest API documentation - * Add automated compatibility tests with previous versions of the Zanata java client classes - * Redirect to previous page after sign in - * Several UI updates and changes - * Bug fixes: - * Rapid key navigation causes backlog of TM queries: https://bugzilla.redhat.com/show_bug.cgi?id=785034 - * Long strings slow down the operation: https://bugzilla.redhat.com/show_bug.cgi?id=750956 - * "Participants" information is incorrect.: https://bugzilla.redhat.com/show_bug.cgi?id=756292 - * Translation editor: Long word in source cell invades the editor cell: https://bugzilla.redhat.com/show_bug.cgi?id=759337 - * On push operations, copyTrans runs too slowly: https://bugzilla.redhat.com/show_bug.cgi?id=746899 - * Edit profile: "duplicate email" is shown even if user press save without changing email: https://bugzilla.redhat.com/show_bug.cgi?id=719176 - * Translation editor table shows changes which failed to save: https://bugzilla.redhat.com/show_bug.cgi?id=690669 - -## zanata-1.4.5.2 - * Fix handling of fuzzy entries when saving Properties files - -## zanata-1.4.5.1 - * Fix regression with Unicode encoding for ordinary (Latin-1) .properties files: https://bugzilla.redhat.com/show_bug.cgi?id=795597 - -## zanata-1.4.5 - * Add support for Maven modules: https://bugzilla.redhat.com/show_bug.cgi?id=742872 - * Fix bug: Moving to a new page does not refresh the translation textboxes (ghost translations): https://bugzilla.redhat.com/show_bug.cgi?id=760431 - - -## zanata-1.4.4 - * Ensure final reindex batch is properly flushed: https://bugzilla.redhat.com/show_bug.cgi?id=747836 - * Support UTF-8 Properties files, handle empty properties: https://bugzilla.redhat.com/show_bug.cgi?id=760390 - * Fix bug: Editor table stops working after 'Source and Target' search returns no results: https://bugzilla.redhat.com/show_bug.cgi?id=759994 - * Add dryRun option for Maven goals 'push' and 'pull' - -## zanata-1.4.3 - * Show message context in editor info panel: https://bugzilla.redhat.com/show_bug.cgi?id=750690 - * Update gwteventservice to 1.2.0-RC1 - * Modify email templates to include server URL - * Fix problems with editor table when searching or switching pages: https://bugzilla.redhat.com/show_bug.cgi?id=751264 - * Add failsafe editor in case of Seam Text problems: https://bugzilla.redhat.com/show_bug.cgi?id=727716 - * Change string similarity algorithm so that only identical strings (not substrings) can get 100%: https://bugzilla.redhat.com/show_bug.cgi?id=730189 - * Bugfix: 'J' and 'K' navigation keys trigger when entering text in the TM search box: https://bugzilla.redhat.com/show_bug.cgi?id=754637 - * Bugfix: Not able to work in parallel on the same workbench: https://bugzilla.redhat.com/show_bug.cgi?id=756293 - * Show progress during re-index operations; avoid timeout for large databases: https://bugzilla.redhat.com/show_bug.cgi?id=747836 - -## zanata-1.4.2 - * Language team coordinator: https://bugzilla.redhat.com/show_bug.cgi?id=742083 - * Users now have to ask before joining a language team - * Coordinator can add and remove team members - * Contact coordinators - * Contact server admins: https://bugzilla.redhat.com/show_bug.cgi?id=742854 - * First/last entry button: https://bugzilla.redhat.com/show_bug.cgi?id=743783 - * Load project pages faster: https://bugzilla.redhat.com/show_bug.cgi?id=744114 - * Option for Enter to save translation: https://bugzilla.redhat.com/show_bug.cgi?id=744671 - * Sort projects by name, not ID: https://bugzilla.redhat.com/show_bug.cgi?id=746859 - * Make newlines visible to reduce newline mismatch errors in translations: https://bugzilla.redhat.com/show_bug.cgi?id=740122 - * Improve shortcut keys: https://bugzilla.redhat.com/show_bug.cgi?id=740191 - * Fix tab order: editor cell -> Save as Approved -> Save as Fuzzy -> Cancel - * Save as Fuzzy now leaves the cell editor open: https://bugzilla.redhat.com/show_bug.cgi?id=746870 - * Modal navigation: next fuzzy, untranslated, fuzzy or untranslated: https://bugzilla.redhat.com/show_bug.cgi?id=743134 - * Rearrange various UI elements to be more logical (profile page, document stats, project search field) - -## zanata-1.4.1 - * Fixed: % completed should be calculated with words, not messages: https://bugzilla.redhat.com/show_bug.cgi?id=741523 - * Fixed: Selecting Administration submenu items does not always highlight the parent menu: https://bugzilla.redhat.com/show_bug.cgi?id=724867 - * Fixed: Change of tile to list view on Language page, make project list sortable: https://bugzilla.redhat.com/show_bug.cgi?id=742111 - * Performance fix for projects with 1000+ documents: https://bugzilla.redhat.com/show_bug.cgi?id=743179 - -## zanata-1.4 - * add project-type to zanata.xml for generic push/pull commands - * redirect to login from translation editor when required - * if domain is left blank by admin, don't populate email address for new users - * UI bug fixes - -## zanata-1.4-alpha-1 - * create generic push/pull commands, with include/exclude filters - * add support for Java Properties and XLIFF projects - * bug fix: mark existing translations of modified XLIFF/Properties strings as fuzzy - * modify keyboard shortcuts in editor - * add new Zanata logo/favicon - * various UI improvements - * auto-size for translation text area - * add icons to buttons and remove text - * add option to hide editor buttons - * remove Clone and Save button; move Copy button to middle - * autosave when leaving a cell - * remove Fuzzy checkbox; add Save as Fuzzy - * better statistics graphs - * display resource IDs for translation units - * add ability to hide translation unit details - * show translation states with coloured side bars, and italics for Fuzzy - * recalculate missing word counts - * bug fixes - -## zanata-1.3.1 (never released) - * add liquibase script - * bug fix for search re-indexing by admin - * copy translations of identical strings when importing new documents - * bug fixes and improvements for UI - * bug fix for word counts (thread safety) - * remove email address from Language Team pages - * enable stats for anonymous users - * no need to enforce locales for source documents - * bug fix for push/merge when PO files are missing some msgids - -## zanata-1.3 - * bug fixes for authentication and for source comments - -## zanata-1.3-alpha-3 - * finalise rebrand from flies->zanata: XML namespaces, media types, etc - * more logging for authentication errors - * bug fix for Kerberos authentication - -## zanata-1.3-alpha-2 - * switch source control to git on github - * rebrand from flies->zanata (maven artifacts, java packages, mailing lists) - * Fedora authentication rhbz#692011 - * generate zanata.xml config file (http://code.google.com/p/flies/issues/detail?id=282) - * merge translations on import (http://code.google.com/p/flies/issues/detail?id=28) - * preserve and generate PO header comments for translator credits (http://code.google.com/p/flies/issues/detail?id=269) - * bug fixes - -## zanata-1.3-alpha-1 - * rebrand from flies->zanata (except URIs, maven artifacts and java packages) - * specify locales per project/version (http://code.google.com/p/flies/issues/detail?id=261) - * added tab for home page, removed project list, contents editable by admin (http://code.google.com/p/flies/issues/detail?id=279) - * added help page/tab, contents editable by admin (http://code.google.com/p/flies/issues/detail?id=280) - * removed name and description from project version (http://code.google.com/p/flies/issues/detail?id=281) - * stats for all languages (http://code.google.com/p/flies/issues/detail?id=275) - * workaround for form/login issue on Firefox 4.0 rhbz#691963 - * bug fixes - -## flies-1.2 - * disabled bad key bindings (http://code.google.com/p/flies/issues/detail?id=262) - * fixed python client issue with PotEntryHeader.extractedComment (http://code.google.com/p/flies/issues/detail?id=256) - * web template redesign (new logo, CSS) (http://code.google.com/p/flies/issues/detail?id=238) - * fixed Seam integration tests (http://code.google.com/p/flies/issues/detail?id=231) - -## flies-1.2-alpha-3 - * improve notifications in editor (http://code.google.com/p/flies/issues/detail?id=191) - * highlight search terms in editor (http://code.google.com/p/flies/issues/detail?id=227) - -## flies-1.2-alpha-2 - * better messages - * bug fixes - -## flies-1.2-alpha-1 - * development change: re-arranged Maven modules into common, client and server - -## flies-1.1.1 - * use word counts in translation statistics (http://code.google.com/p/flies/issues/detail?id=203) - * bug fixes - -## flies-1.1 - * Kerberos/JAAS fixes - * require name & email address on first login for JAAS/Kerberos - * validate changes to email address - * use correct BCP-47 language tags (zh-CN-Hans is now zh-Hans-CN) - -## flies-1.1-alpha-1 - * JAAS authentication - * Kerberos authentication - * remove communities tab and my communities UI (http://code.google.com/p/flies/issues/detail?id=197) - * remove "Language Missing" button (http://code.google.com/p/flies/issues/detail?id=185) - * show member number for the language groups (http://code.google.com/p/flies/issues/detail?id=186) - * allow overriding POT directory in Maven client (http://code.google.com/p/flies/issues/detail?id=200) - * support `[servers]` in flies.ini for Maven client (http://code.google.com/p/flies/issues/detail?id=193) - * better info/error messages in Maven client - -## flies-1.0.3 - * fix TM caching issue (http://code.google.com/p/flies/issues/detail?id=190) - * add 'translator' role and security rules - * configurable URLs - -## flies-1.0.2 - * minor UI fixes (http://code.google.com/p/flies/issues/detail?id=173, http://code.google.com/p/flies/issues/detail?id=176) - * ergonomics for Maven client - * UI for assigning project maintainers (http://code.google.com/p/flies/issues/detail?id=180) - * better error checking in REST API (http://code.google.com/p/flies/issues/detail?id=175) - * security rule fix (http://code.google.com/p/flies/issues/detail?id=182) - -## flies-1.0.1 - * database schema fixes - * fixes for deployment issues - -## flies-1.0 - * initial release diff --git a/docs/configuration/authentication.md b/docs/configuration/authentication.md index 2919352d22..85b0c41b40 100644 --- a/docs/configuration/authentication.md +++ b/docs/configuration/authentication.md @@ -206,7 +206,6 @@ standalone.xml: ### Single Provider -_( As of version 3.5.1 )_ It's possible to configure Zanata to use a single pre-defined Open Id authentication provider. To do this, just add an extra `module-option` to the `login-module` element, like this: ```xml diff --git a/docs/configuration/infinispan.md b/docs/configuration/infinispan.md index 4973ddc7a3..d8eedaa0ed 100644 --- a/docs/configuration/infinispan.md +++ b/docs/configuration/infinispan.md @@ -1,11 +1,6 @@ -# Infinispan +# Infinispan for caching -_This section is still under review and is about features that have not been released yet_ - -Zanata uses Infinispan to manage its internal data caches and search indexes. Configuration for these caches happens in JBoss' `standalone/configuration/standalone.xml`. There are two different caches that need to be configured for Zanata: - -1. Hibernate search Indexes -1. Other internal data caches +Zanata uses Infinispan to manage some of its internal data caches. Configuration for these caches happens in JBoss' `standalone/configuration/standalone.xml`. The Infinispan configuration will be located inside the following module in `standalone.xml`: @@ -17,40 +12,14 @@ The Infinispan configuration will be located inside the following module in `sta Keep in mind that the module version may vary depending on your JBoss version. -### Hibernate Cache - -The following is the recommended configuration for the Hibernate cache: +### Configuration for Internal data caches ```xml ... - - - - - - - - - - - - - - - - -... -``` - -Depending on your JBoss installation, the hibernate cache might already be present in the configuration, in which case there is no need to create another one, but just modify it. - -### Other internal data caches - -```xml -... - + @@ -59,3 +28,5 @@ Depending on your JBoss installation, the hibernate cache might already be prese ... ``` + +*Please see the JBoss EAP or Wildfly documentation for more options on cache configuration.* diff --git a/docs/configuration/installation.md b/docs/configuration/installation.md new file mode 100644 index 0000000000..44d9378681 --- /dev/null +++ b/docs/configuration/installation.md @@ -0,0 +1,109 @@ +Zanata can be installed by downloading a web archive (war) file, and configuring Jboss EAP or Wildfly according to this guide, or by downloading a handy installer. + +## What you need + +- JBoss Enterprise Application Platform 6.3 (EAP). This is the recommended container for Zanata, and it can be [downloaded here](http://www.jboss.org/jbossas/downloads/). +- ... OR Wildfly (recommended version is 8.1.x) which can be [downloaded here](http://wildfly.org/downloads/) +- A suitable MySQL database. This is NOT included in the Zanata archive. You can [download MySQL here](http://dev.mysql.com/downloads/mysql/). +- An email (SMTP) server to perform certain notifications. +- JDK version 7 or later (7 is recommended for EAP as it is not yet certified to run against Java 8). [OpenJDK](http://openjdk.java.net/install/) is recommended, but you can also download [Oracle's JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) + +The following packages are optional, but recommended: + +- clamav for virus protection. + +## Setting up the Zanata Database + + 1. Download and install MySQL 5 from the [MySQL download page](http://dev.mysql.com/downloads/mysql/). + Zanata has been thoroughly tested against MySQL 5 and the Zanata team therefore recommends that you install and use this version with Zanata. + + 1. Start MySQL service and create a database schema for Zanata. + `CREATE DATABASE zanata /**!40100 DEFAULT CHARACTER SET utf8 **/;` + +## Installing Zanata + +You can run Zanata on JBoss EAP 6 or Wildfly. Just download one of the installer archives below for your platform, and then extract it on top of your JBoss or Wildfly installation. + +- [Zanata for JBoss EAP](http://sourceforge.net/projects/zanata/files/installer/zanata-3.6.0-eap-6.zip/download) +- [Zanata for Wildfly](http://sourceforge.net/projects/zanata/files/installer/zanata-3.6.0-wildfly-8.1.zip/download) + +## Run the installer + +Zanata comes bundled with an installer that helps with some of the initial setup. Simply run the following commands on a shell terminal: + +```sh +$ cd /bin/zanata-installer +$ ./install.sh +``` + +(there's also a .bat file if you are on Windows) The installation script will start asking some configuration questions. It will also download the Zanata web application and place it in the JBoss installation. + +## Some advanced configuration + +Zanata does not create an admin user by default. You need to register specific users to have administrative privileges. + + 1. Open the `/standalone/configuration/standalone.xml` file. + + 1. Locate the following line, and replace `admin` with a comma-separated list of users that require administrator privileges on the system. + +```xml + +``` + + 1. Register a user under the name "admin", and it will automatically have administrator privileges. Any number of users may be added to this list in a comma-separated format. + + 1. In the same file, configure other properties to your particular setup by adding more lines if necessary. The following properties must be configured in order for Zanata to run properly: +```xml + +``` + + This is the default email address that will appear as the sender on Zanata emails. + + 1. The following properties relate to the SMTP email server that Zanata uses to send emails. It defaults to a locally installed server using port 25. Add values to suit your configuration. If a particular property does not apply to the email server being used, you can comment it out or remove it completely. + +```xml + + + + + + +``` + +## Installing virus scanner (optional) + +To prevent virus infected document being uploaded, Zanata is capable of working with clamav. +If clamav is not installed, a warning will be logged when files are uploaded. +If clamav is installed but `clamd` is not running, +Zanata may reject all uploaded files (depending on file type). To install and run clamav: +``` +# Assuming the function install_missing() is still available +if [ -e /usr/bin/systemctl ];then + install_missing clamav-server clamav-scanner-systemd + sudo systemctl enable clamd@scan + sudo systemctl start clamd@scan +else + install_missing clamd + sudo chkconfig clamd on + if ! service clamd status ;then + sudo service clamd start + fi +fi +``` + +You should probably also ensure that freshclam is set to run at least once per day, +to keep virus definitions up to date. +The clamav package will probably do this for you, but you can check by looking for `/etc/cron.daily/freshclam`. +To override the default behaviour above, you can set the system property `virusScanner` when running the server. +`DISABLED` means no virus scanning will be performed; all files will be assumed safe. +Any other value will be treated as the name of a virus scanner command: the command will be called with the name of a file to scan. + +## Running Zanata + +Go to the `/bin` directory and run the `standalone.sh` (Linux, Mac) or `standalone.bat` (Windows) file. + +## Using Zanata + +To start using your Zanata server, open a browser and navigate to `http://localhost:8080/zanata` + +You can now upload some source strings and start translating. To get started, see [Adding Source Strings](user-guide/projects/upload-strings). \ No newline at end of file diff --git a/docs/images/project-view-versions.png b/docs/images/project-view-versions.png new file mode 100644 index 0000000000..b73fad68e1 Binary files /dev/null and b/docs/images/project-view-versions.png differ diff --git a/docs/images/version-merge-trans-cancel.png b/docs/images/version-merge-trans-cancel.png new file mode 100644 index 0000000000..8eddac8038 Binary files /dev/null and b/docs/images/version-merge-trans-cancel.png differ diff --git a/docs/images/version-merge-trans-dialog.png b/docs/images/version-merge-trans-dialog.png new file mode 100644 index 0000000000..b03ed7d790 Binary files /dev/null and b/docs/images/version-merge-trans-dialog.png differ diff --git a/docs/images/version-merge-trans-progress.png b/docs/images/version-merge-trans-progress.png new file mode 100644 index 0000000000..d2df13775e Binary files /dev/null and b/docs/images/version-merge-trans-progress.png differ diff --git a/docs/images/version-more-action-menu.png b/docs/images/version-more-action-menu.png new file mode 100644 index 0000000000..538ebd23c5 Binary files /dev/null and b/docs/images/version-more-action-menu.png differ diff --git a/docs/images/version-view-languages.png b/docs/images/version-view-languages.png new file mode 100644 index 0000000000..e54b3fd3cd Binary files /dev/null and b/docs/images/version-view-languages.png differ diff --git a/docs/index.md b/docs/index.md index f5258d6982..003e2669fc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,31 +1,22 @@ Zanata Server ============= - -TBD - -Features --------- - -- TBD +Please visit [About|Zanata](http://zanata.org/about/) Installation ------------ - +[Installation section](configuration/installation.md) Contribute ---------- -- Issue Tracker: http://bugzilla.redhat.com -- Source Code: github.com/zanata/zanata-server +- [Issue Tracker](http://bugzilla.redhat.com/buglist.cgi?product=Zanata) +- [Source Code on GitHub](http://github.com/zanata) Support ------- - -If you are having issues, please let us know. -We have a mailing list located at: zanata-users@redhat.com +For help and support options, see [Help|Zanata](http://zanata.org/help/) License ------- - - \ No newline at end of file +Zanata is Free software, licensed under the [LGPL](http://www.gnu.org/licenses/lgpl-2.1.html). \ No newline at end of file diff --git a/docs/release-notes.md b/docs/release-notes.md index 4da242c100..25be520f4d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,3 +1,47 @@ +## 3.7 + +
Infrastructure Changes
+ +* Zanata now uses Infinispan as its cache provider, and the cache needs to be configured in Jboss' `standalone.xml` file. Please see the [Infinispan](configuration/infinispan) section for more information. + +* This release adds a one-time migration of some data, which can cause a timeout during server startup. This applies to +all plain text and libreoffice formats, so is only a concern for servers that are upgrading from an earlier version and +already have several hundred such documents. To avoid the timeout, add or change the following property in +`standalone.xml`. A value of 1000 seconds is sufficient in our tests. Since the migration is performed only once, the +property can safely be reverted or removed before subsequent startups. + + + ... + + + + +
Bug fixes
+* [1194543](https://bugzilla.redhat.com/show_bug.cgi?id=1194543) - Manual document re-upload makes previous translations fuzzy +* [1029734](https://bugzilla.redhat.com/show_bug.cgi?id=1029734) - po header contains invalid entry will cause upload/push failure + +----------------------- + +
New Features
+* [1133989](https://bugzilla.redhat.com/show_bug.cgi?id=1133989) - Copy translations from existing version. +* + +## 3.6.1 + +
Bug fixes
+* [1194543](https://bugzilla.redhat.com/show_bug.cgi?id=1194543) - Manual document re-upload makes previous translations fuzzy +* [1197902](https://bugzilla.redhat.com/show_bug.cgi?id=1197902) - Large translated document push times are inconsistent +* [1183412](https://bugzilla.redhat.com/show_bug.cgi?id=1183412) - Emails to administrators are sent in the current interface language +* [1202670](https://bugzilla.redhat.com/show_bug.cgi?id=1202670) - There should be visual clues to indicate active, readonly, and archived versions. +* [875965](https://bugzilla.redhat.com/show_bug.cgi?id=875965) - Enable visible white space in source +* [1205465](https://bugzilla.redhat.com/show_bug.cgi?id=1205465) - User emails are visible to non admin users in Language page +* [1205468](https://bugzilla.redhat.com/show_bug.cgi?id=1205468) - Sorting mechanism broken on Languages page +* [1205046](https://bugzilla.redhat.com/show_bug.cgi?id=1205046) - Key shortcuts are not all visible on a small window +* [1000273](https://bugzilla.redhat.com/show_bug.cgi?id=1000273) - Font in TM and font in Editor Not matching +* [1013928](https://bugzilla.redhat.com/show_bug.cgi?id=1013928) - Editor options panel cannot scroll on small screens + +---- + ## 3.6
New Editor (Alpha)
@@ -22,7 +66,7 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up
New Features
* [1044261](https://bugzilla.redhat.com/show_bug.cgi?id=1044261) - Drupal integration with Zanata -* [1066780](https://bugzilla.redhat.com/show_bug.cgi?id=1066780) - RFE: Improve the project list page [proto] + * [1127066](https://bugzilla.redhat.com/show_bug.cgi?id=1127066) - Copy Version button on project version listing * [1162383](https://bugzilla.redhat.com/show_bug.cgi?id=1162383) - Updated pages in Administration section * [1120457](https://bugzilla.redhat.com/show_bug.cgi?id=1120457) - Email notify the user when the language team permissions change @@ -31,9 +75,10 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up * [1127056](https://bugzilla.redhat.com/show_bug.cgi?id=1127056) - Migration Guide for community users * [1122776](https://bugzilla.redhat.com/show_bug.cgi?id=1122776) - WebHooks callback API * [1186951](https://bugzilla.redhat.com/show_bug.cgi?id=1186951) - Zanata Overlay module +* [1183994](https://bugzilla.redhat.com/show_bug.cgi?id=1183994) - RFE: Gather and display metrics detailing the number of words translated by a specific translator, for a specific project -
Bugfixes
+
Bug fixes
* [1132271](https://bugzilla.redhat.com/show_bug.cgi?id=1132271) - Access contact admin url without logging in will trigger an exception * [1082448](https://bugzilla.redhat.com/show_bug.cgi?id=1082448) - Dashboard shows incorrect number of maintained projects * [1069951](https://bugzilla.redhat.com/show_bug.cgi?id=1069951) - Empty string in adding a language causes a broken language to be added @@ -47,7 +92,7 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up * [1185134](https://bugzilla.redhat.com/show_bug.cgi?id=1185134) - Placeholder text in server config ToU field valid, but rejected * [1185170](https://bugzilla.redhat.com/show_bug.cgi?id=1185170) - Create version in a project is always created as read only * [1186084](https://bugzilla.redhat.com/show_bug.cgi?id=1186084) - WebUI is very slow if users cannot access Google -* [1186997](https://bugzilla.redhat.com/show_bug.cgi?id=1186997) - Introduction of hornetq-ra breaks the overlay installer +* [1186997](https://bugzilla.redhat.com/show_bug.cgi?id=1186997) - Introduction of hornetq-ra breaks the overlay installer * [1192271](https://bugzilla.redhat.com/show_bug.cgi?id=1192271) - For gettext plural project, project-version statistics was inconsistent between language and document, sometime more than 100% * [1193699](https://bugzilla.redhat.com/show_bug.cgi?id=1193699) - Bookmarked url (selected language or selected doc) in version page, bookmarked url selected language, selected project in version-group page not working @@ -107,7 +152,7 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up * [988202](https://bugzilla.redhat.com/show_bug.cgi?id=988202) - RFE: REST API rate limiting * [1002378](https://bugzilla.redhat.com/show_bug.cgi?id=1002378) - RFE: Introduce a modular translation structure, and gwt generate the *Messages.properties files * [1066701](https://bugzilla.redhat.com/show_bug.cgi?id=1066701) - RFE: As a Zanata user, I would like to be able to bookmark language and project selections in the groups page - * Now is possible to bookmark a project version, language, or setting item for communication or later reference. + * Now is possible to bookmark a project version, language, or setting item for communication or later reference. * [1066756](https://bugzilla.redhat.com/show_bug.cgi?id=1066756) - RFE: Merge user settings pages into dashboard * [1066796](https://bugzilla.redhat.com/show_bug.cgi?id=1066796) - RFE: Implement new project page * [1077439](https://bugzilla.redhat.com/show_bug.cgi?id=1077439) - RFE: Use lucene indexes to do Copy Trans. @@ -126,7 +171,7 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up * [1086036](https://bugzilla.redhat.com/show_bug.cgi?id=1086036) - Project / version language listing and inheritance issue * [1088651](https://bugzilla.redhat.com/show_bug.cgi?id=1088651) - New About tab does not handle existing project Seam text * [1088737](https://bugzilla.redhat.com/show_bug.cgi?id=1088737) - Project type for a version is null after creation if the project type setting is not touched -* [1094071](https://bugzilla.redhat.com/show_bug.cgi?id=1094071) - Copy Translations information not correct +* [1094071](https://bugzilla.redhat.com/show_bug.cgi?id=1094071) - Copy Translations information not correct * [1094090](https://bugzilla.redhat.com/show_bug.cgi?id=1094090) - TMX import/export blocked by api not providing user key * [1096564](https://bugzilla.redhat.com/show_bug.cgi?id=1096564) - Entering garbage at the end of a projects url breaks navigation * [1097940](https://bugzilla.redhat.com/show_bug.cgi?id=1097940) - New password field should have show/hide toggle @@ -157,7 +202,7 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up
New Features
* [978072](https://bugzilla.redhat.com/show_bug.cgi?id=978072) - RFE: support roff as an input/output format - * This feature is implemented on the client side only with [1038449 - command hook](https://bugzilla.redhat.com/show_bug.cgi?id=1038449). Users who wish to push .roff file can use a command hook to invoke external tool (po4a) before push to convert .roff into .pot. Invoke po4a again after pull to convert translated .po into .roff. + * This feature is implemented on the client side only with [1038449 - command hook](https://bugzilla.redhat.com/show_bug.cgi?id=1038449). Users who wish to push .roff file can use a command hook to invoke external tool (po4a) before push to convert .roff into .pot. Invoke po4a again after pull to convert translated .po into .roff. * [1036435](https://bugzilla.redhat.com/show_bug.cgi?id=1036435) - RFE: Upgrade to Liquibase 3.x * [980670](https://bugzilla.redhat.com/show_bug.cgi?id=980670) - [RFE] Add HTML as an input method to be translated @@ -210,4 +255,549 @@ Zanata now requires JMS to be configured in standalone.xml in order to queue up * [1060621](https://bugzilla.redhat.com/show_bug.cgi?id=1060621) - [Regression] Validation warnings panel not displayed * [1044768](https://bugzilla.redhat.com/show_bug.cgi?id=1044768) - Zanata does not pull the latest changes in translation due to unchanged ETags * [1063112](https://bugzilla.redhat.com/show_bug.cgi?id=1063112) - Client push in dryRun mode should not invoke CopyTrans -* [1069428](https://bugzilla.redhat.com/show_bug.cgi?id=1069428) - Various concurrency problems due to unsafe Seam injections \ No newline at end of file +* [1069428](https://bugzilla.redhat.com/show_bug.cgi?id=1069428) - Various concurrency problems due to unsafe Seam injections + +----------------------- + +## 3.0.0 +
New Features
+ +* [980659](https://bugzilla.redhat.com/show_bug.cgi?id=980659) - TMX import/export + * Imported TMX shown in translation memory search results + * Allow users to export translations to TMX (from Project/Version pages) + * Allow admins to export **all** project translations to TMX (from Projects page) + * Allow admins to import and export TMX translation memories (from Admin pages) + +* [953734](https://bugzilla.redhat.com/show_bug.cgi?id=953734) - Translation review/approval + * Coordinators can assign reviewers for their languages + * Project maintainers can require review for translations in their projects + +* [979285](https://bugzilla.redhat.com/show_bug.cgi?id=979285) - Implement virus scanning using ClamAV (clamdscan) +* [978666](https://bugzilla.redhat.com/show_bug.cgi?id=978666) - Translators and reviewers can add comments to translations +* [844819](https://bugzilla.redhat.com/show_bug.cgi?id=844819) - New visual style for Zanata +* [1066756](https://bugzilla.redhat.com/show_bug.cgi?id=1066756) - Add user dashboard +* [981064](https://bugzilla.redhat.com/show_bug.cgi?id=981064) - Recent translation/review activity +* Upgrade platform to JBoss EAP 6.1 +* Add descriptions on project type selectors +* Allow adapter parameters to be set on source document upload +* Editor improvements +* Add attention key shortcut: Alt+X +* Add attention shortcut to copy from source: Alt+X,G +* File upload +* Move raw document storage to file system + +----------------------- + +## 2.3.2 + +* [958350](https://bugzilla.redhat.com/show_bug.cgi?id=958350) - Concurrent save on different row causes incorrect validation warnings in current row +* [959060](https://bugzilla.redhat.com/show_bug.cgi?id=959060) - Generated Zanata config file contains invalid project type +* [961163](https://bugzilla.redhat.com/show_bug.cgi?id=961163) - shift-w and g erroneously triggers Copy from Source +* [959115](https://bugzilla.redhat.com/show_bug.cgi?id=959115) - Database connection pool leaks under load + +----------------------- + +## 2.3.1 +
Bug fixes
+ +* [953195](https://bugzilla.redhat.com/show_bug.cgi?id=953195) - HQL query exception while trying to filter strings +* Prevent incorrect validation warnings with concurrent edits +* Search result back to editor causes multiple code mirror focus +* Support message bookmark + +----------------------- + +## 2.3.0 + +* [908548](https://bugzilla.redhat.com/show_bug.cgi?id=908548) - Long document names cause layout issues in Doc page +* [786630](https://bugzilla.redhat.com/show_bug.cgi?id=786630) - Shortcut Alt+G causes editor to lose focus +* [870876](https://bugzilla.redhat.com/show_bug.cgi?id=870876) - PO download for non-PO projects cannot be uploaded +* [846314](https://bugzilla.redhat.com/show_bug.cgi?id=846314) - Show validation state in doc list and link to error-filter view in editor +* [844553](https://bugzilla.redhat.com/show_bug.cgi?id=844553) - Notification of an entry should have a link that go to the entry +* [727826](https://bugzilla.redhat.com/show_bug.cgi?id=727826) - Order Projects alphabetically +* [917911](https://bugzilla.redhat.com/show_bug.cgi?id=917911) - Keep "Validation Warnings: n" displayed even when moving focus to different pages +* [910637](https://bugzilla.redhat.com/show_bug.cgi?id=910637) - Keep "Validation Warnings: n" displayed even when moving focus to different entry +* [767055](https://bugzilla.redhat.com/show_bug.cgi?id=767055) - Error when pull as XLIFF file format: Underlying stream encoding 'ASCII' and input parameter for writeStartDocument() method 'utf-8' do not match +* [953361](https://bugzilla.redhat.com/show_bug.cgi?id=953361) - Source document name search triggers delete confirmation +* [874335](https://bugzilla.redhat.com/show_bug.cgi?id=874335) - Allow admins to see the email addresses of project maintainers +* [950806](https://bugzilla.redhat.com/show_bug.cgi?id=950806) - Notification links disappear from list when detail is viewed +* [947832](https://bugzilla.redhat.com/show_bug.cgi?id=947832) - Empty translation page when pushing next +* [923461](https://bugzilla.redhat.com/show_bug.cgi?id=923461) - Update document list view and link to the violated entries after project wide validation +* [910183](https://bugzilla.redhat.com/show_bug.cgi?id=910183) - Search in Document List does not show when on page 2+ of Document List +* [854087](https://bugzilla.redhat.com/show_bug.cgi?id=854087) - report which locales have recent changes + +----------------------- + +## 2.2.2 + +* [917895](https://bugzilla.redhat.com/show_bug.cgi?id=917895) - Validation rules should be enabled by default +* [917897](https://bugzilla.redhat.com/show_bug.cgi?id=917897) - AlreadyClosedException when new document uploaded and translated +* [807100](https://bugzilla.redhat.com/show_bug.cgi?id=807100) - Removing admin role doesn't take effect for Kerberos authentication + +----------------------- + +## 2.2.1 + +* [915130](https://bugzilla.redhat.com/show_bug.cgi?id=915130) - Unexpected error when clicking "resend activation email" or "update email address" +* [916812](https://bugzilla.redhat.com/show_bug.cgi?id=916812) - Activation Key should update after user click "Resend activation email" and "Change email" + +----------------------- + +## 2.2.0 + +* [895280](https://bugzilla.redhat.com/show_bug.cgi?id=895280) - Persist project type on server +* [893811](https://bugzilla.redhat.com/show_bug.cgi?id=893811) - Old registration activation link should expire after a given period +* [750104](https://bugzilla.redhat.com/show_bug.cgi?id=750104) - Old email validation links for email change should expire after a given period +* [913373](https://bugzilla.redhat.com/show_bug.cgi?id=913373) - Ctrl-Enter not moving to next trans unit if there are no changes +* [908563](https://bugzilla.redhat.com/show_bug.cgi?id=908563) - Html Xml tag validation will produce exception in certain case +* [912583](https://bugzilla.redhat.com/show_bug.cgi?id=912583) - Change project type 'raw' to be 'file' +* [910216](https://bugzilla.redhat.com/show_bug.cgi?id=910216) - Statistics API returns word level statistics when only message level statistics are requested +* [910212](https://bugzilla.redhat.com/show_bug.cgi?id=910212) - Ability to resume push/pull from a specified document +* [903470](https://bugzilla.redhat.com/show_bug.cgi?id=903470) - Allow java clients to send and receive source control URLs for projects +* [896356](https://bugzilla.redhat.com/show_bug.cgi?id=896356) - Need to specify the size of the stream when sending a file (or part thereof) +* [896299](https://bugzilla.redhat.com/show_bug.cgi?id=896299) - store and display source control URL +* [895295](https://bugzilla.redhat.com/show_bug.cgi?id=895295) - Validator to warn of inconsistent number of lines +* [913745](https://bugzilla.redhat.com/show_bug.cgi?id=913745) - Zip File download does not work +* [913331](https://bugzilla.redhat.com/show_bug.cgi?id=913331) - "Contact Team Coordinator" return unexpected error +* [913310](https://bugzilla.redhat.com/show_bug.cgi?id=913310) - Value in zanata.properties does not shows up in server configuration page +* [912590](https://bugzilla.redhat.com/show_bug.cgi?id=912590) - Project maintainer should be able to "edit page code" +* [909032](https://bugzilla.redhat.com/show_bug.cgi?id=909032) - Project version's project type should default to that of the project +* [909026](https://bugzilla.redhat.com/show_bug.cgi?id=909026) - Unexpected error when trying to download config file when project-type not set on version +* [903926](https://bugzilla.redhat.com/show_bug.cgi?id=903926) - Project maintainer should be able to define and save validations rules per project/document +* [903477](https://bugzilla.redhat.com/show_bug.cgi?id=903477) - Workspace document list view should have same features as JSF document list view +* [903026](https://bugzilla.redhat.com/show_bug.cgi?id=903026) - Display Last Translator and Last Modified column in the document list + +----------------------- + +## 2.1.3 + +* [896332](https://bugzilla.redhat.com/show_bug.cgi?id=896332) - CopyTrans should use the most recent matching translation + +----------------------- + +## 2.1.1 + +* [894909](https://bugzilla.redhat.com/show_bug.cgi?id=894909) - Kerberos user unable to log in properly +* [888090](https://bugzilla.redhat.com/show_bug.cgi?id=888090) - Implement REST ETag mechanism for certain GET operations + +----------------------- + +## 2.1.0 + +* [844550](https://bugzilla.redhat.com/show_bug.cgi?id=844550) - Provide sort by option on branch stats page +* [874367](https://bugzilla.redhat.com/show_bug.cgi?id=874367) - Editor should warn before saving a Fuzzy translation as Approved from a keyboard shortcut +* [877223](https://bugzilla.redhat.com/show_bug.cgi?id=877223) - Add "clear" button to search field in workspace +* [878275](https://bugzilla.redhat.com/show_bug.cgi?id=878275) - Breadcrumb navigation in workspace should separate project version and locale +* [880436](https://bugzilla.redhat.com/show_bug.cgi?id=880436) - Plain text area editor doesn't get autosize correctly with long string +* [882739](https://bugzilla.redhat.com/show_bug.cgi?id=882739) - Tooltips on paging buttons (editor) shows shortcut keys which doesn't apply +* [892816](https://bugzilla.redhat.com/show_bug.cgi?id=892816) - Recently removed project maintainer retains access to project maintainer actions +* [874374](https://bugzilla.redhat.com/show_bug.cgi?id=874374) - Make translation editor options persistent +* [880894](https://bugzilla.redhat.com/show_bug.cgi?id=880894) - Externalize Email Server configuration +* [881549](https://bugzilla.redhat.com/show_bug.cgi?id=881549) - Allow admins to change account user names +* [884335](https://bugzilla.redhat.com/show_bug.cgi?id=884335) - Add Translation Memory Cache for filter query +* [891485](https://bugzilla.redhat.com/show_bug.cgi?id=891485) - Removing a locale member causes a RecordNotFound error +* [864280](https://bugzilla.redhat.com/show_bug.cgi?id=864280) - upload/download raw file types with the Maven plugin +* [876012](https://bugzilla.redhat.com/show_bug.cgi?id=876012) - The Content-Type of Download as po link is application/octet-stream, but should be text/plain +* [881962](https://bugzilla.redhat.com/show_bug.cgi?id=881962) - Project-wide Search and replace starts by Enter key before ready +* [887052](https://bugzilla.redhat.com/show_bug.cgi?id=887052) - Source and Target search in editor fails when the search term includes an apostrophe (') +* [888150](https://bugzilla.redhat.com/show_bug.cgi?id=888150) - Case sensitive search should return case sensitive results +* [877228](https://bugzilla.redhat.com/show_bug.cgi?id=877228) - Clearing the search field in workspace should keep position at last selected message +* [880444](https://bugzilla.redhat.com/show_bug.cgi?id=880444) - enable spell check in code mirror editor for Firefox +* [880879](https://bugzilla.redhat.com/show_bug.cgi?id=880879) - Undo button causing repeated save failures and other weirdness +* [884402](https://bugzilla.redhat.com/show_bug.cgi?id=884402) - Entry should NOT move unless it is absolutely needed +* [884502](https://bugzilla.redhat.com/show_bug.cgi?id=884502) - navigation breaks in filter mode after saved status not included in filter view +* [887717](https://bugzilla.redhat.com/show_bug.cgi?id=887717) - enable 'Enter' key saves immediately will make pager input dysfunctional +* [887718](https://bugzilla.redhat.com/show_bug.cgi?id=887718) - Too slow to load the last pages of a big document with Firefox +* [888096](https://bugzilla.redhat.com/show_bug.cgi?id=888096) - project become read only with editor options panel open will still allow user to change editor options +* [888592](https://bugzilla.redhat.com/show_bug.cgi?id=888592) - Options to customize translation editor display +* [889411](https://bugzilla.redhat.com/show_bug.cgi?id=889411) - Red border indicating failed validation shows on strings without validation warning/error +* [891458](https://bugzilla.redhat.com/show_bug.cgi?id=891458) - Document List search returning incorrect results +* [885934](https://bugzilla.redhat.com/show_bug.cgi?id=885934) - option to avoid encoding tab as \t +* [803923](https://bugzilla.redhat.com/show_bug.cgi?id=803923) - email should be able to corrected during register validation +* [829565](https://bugzilla.redhat.com/show_bug.cgi?id=829565) - Kerberos activation link in email gets 404 page not found +* [872039](https://bugzilla.redhat.com/show_bug.cgi?id=872039) - Escaping with single-quote (a.k.a. Apostrophes ') character in MessageFormat strings can cause confusing validation warnings +* [886711](https://bugzilla.redhat.com/show_bug.cgi?id=886711) - Error when using pull for project type raw when the document name does not include a type extension +* [831056](https://bugzilla.redhat.com/show_bug.cgi?id=831056) - Option for highlight only the search terms +* [785046](https://bugzilla.redhat.com/show_bug.cgi?id=785046) - Limit source string length in properties file +* [846643](https://bugzilla.redhat.com/show_bug.cgi?id=846643) - Shorten the navigation sequence to open a document in the Editor +* [884386](https://bugzilla.redhat.com/show_bug.cgi?id=884386) - Email validation link should be invalid after user validate the email, or user request another validation + +----------------------- + +## 2.0.3 +
New Features
+ +* Allow admin to add extra locales by typing in the BCP-47 locale code. +* TM Merge reports what it did +* Allow choice of editor page size +* Support txt, dtd and open document format (REST & web interface) +* Editor option to disable CodeMirror (to enable browser spell-check) +* Detect loss of connection to server +* Fix for problem creating users with Kerberos +* Allow Project Maintainers to Delete a Source Document + +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?j_top=OR&f1=cf_fixed_in&o1=substring&classification=Community&o2=substring&query_format=advanced&f2=cf_fixed_in&bug_status=VERIFIED&bug_status=RELEASE_PENDING&bug_status=POST&bug_status=CLOSED&v1=2.0.3&product=Zanata) + +----------------------- + +## 2.0.2 +* Bug fixes for document search/navigation + + +----------------------- + +## 2.0.1 +* Update jboss-el to avoid bad artifact in repository + +----------------------- + +## 2.0.0 +
New Features
+ +* UI redesign +* Performance: async push service to avoid timeouts when pushing source/target +* Performance: improve performance when loading large documents +* Allow user to save work when concurrent edit occurs +* Include last translator information in TM info box +* Web analytics (Piwik integration) +* Navigation breadcrumbs +* Bugzilla link in UI +* Get Stats about Translation Documents via REST +* Remove blinking notification in editor +* Configurable page size +* Advanced glossary features +* Open ID Authentication +* Admin role assignment configuration for authentication types +* Highlight tags in editor fields (CodeMirror for editor) +* Translation editor rewrite +* Project-level default Copy Trans options +* Red bars for translations with validation warnings should stay in red when moving to the next row +* Option to show word or message based statistics +* Visible whitespace in editor +* View history of translations for a text flow + +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?j_top=OR&f1=cf_fixed_in&o1=substring&classification=Community&o2=substring&query_format=advanced&f2=cf_fixed_in&bug_status=CLOSED&v1=1.8.0&v2=2.0.0&product=Zanata) + +----------------------- + +## 1.7.3 +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?f1=cf_fixed_in&o1=substring&classification=Community&query_format=advanced&bug_status=CLOSED&v1=1.7.3&product=Zanata) + +----------------------- + +## 1.7.2 +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.7.2&query_format=advanced&bug_status=CLOSED&product=Zanata) + +----------------------- + +## 1.7.1 +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.7.1&query_format=advanced&bug_status=CLOSED&product=Zanata) + +----------------------- + +## 1.7.0 +
New Features
+ +* UI Improvements +* Don't enforce locales for source documents +* On-Demand copy trans +* Email log appender +* Centralise management of key shortcuts in Zanata +* Improvements to reindexing (processing in small batches, index classes separately) +* Editor validation for XML entities +* Undo button for saved translations +* Translation Memory merge in editor +* Add support for positional strings in printf validator +* Translation Memory now uses word-based indexing + +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6.2&target_release=1.7&query_format=advanced&bug_status=CLOSED&product=Zanata) + +----------------------- + +## 1.6.1 +* Allow Zanata to add locales for which plural form is not known + +[
Bug fixes | Bugzilla
](https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6.1&query_format=advanced&bug_status=CLOSED&product=Zanata) + +----------------------- + +## 1.6.0 +
New Features
+ +* UI Improvements +* Allow Project Maintainers to edit all language files +* Glossary suggestions +* Add the ability to specify custom locales that are not enabled by default +* Upgrade Liquibase to version 2.0 +* Project grouping +* Support plural forms +* Offline translation feature via web UI +* Allow translators to push translations using Maven client +* Indicators for simultaneous edits +* Java style variable validations in translation editor +* "Create Project" for non-administrator users +* Display page context in window title +* Ability to monitor Zanata server statistics (JavaMelody) +* Overview for available keyboard shortcuts in web editor + +[
Bug fixes
](https://bugzilla.redhat.com/buglist.cgi?classification=Community&target_release=1.6&target_release=1.6-alpha-1&target_release=1.6-beta-1&query_format=advanced&bug_status=CLOSED&product=Zanata) + +----------------------- + +## 1.5.0 +
New Features
+ +* [757621](https://bugzilla.redhat.com/show_bug.cgi?id=757621) - Allow bookmarking of selected document, document list filter and current view +* [758587](https://bugzilla.redhat.com/show_bug.cgi?id=758587) - Add workspace query string parameters for generating a custom doclist with a custom title. + * e.g. &title=Custom%20title&doc=full/path/of/first/doc&doc=full/path/of/second/doc + +* [755759](https://bugzilla.redhat.com/show_bug.cgi?id=755759) - Allow readonly access to retired project/project iteration +* [773459](https://bugzilla.redhat.com/show_bug.cgi?id=773459) - Implement filter messages in the editor by translation status +* [768802](https://bugzilla.redhat.com/show_bug.cgi?id=768802) - Newline validation on leading and trailing string +* [769471](https://bugzilla.redhat.com/show_bug.cgi?id=769471) - Variables to be checked for consistency +* [756235](https://bugzilla.redhat.com/show_bug.cgi?id=756235) - XML and HTML tags to be checked for completeness +* Redesign of color scheme translation editor workspace layout +* Project/project iteration status changes: ACTIVE, READONLY, and OBSOLETE +* Project list filtering based on status +* Overall statistics page for Admin +* Add file download page with the option to download a single PO file, or a zip with a project iteration's files for one locale +* Translation memory results now have highlighted differences +* Activate entity caching +* Maven client option to create 'skeleton' PO files when no translations are present +* Maven client option to log detailed client-server message information +* Generate Zanata Rest API documentation +* Add automated compatibility tests with previous versions of the Zanata java client classes +* Redirect to previous page after sign in +* Several UI updates and changes + +
Bug fixes
+ +* [785034](https://bugzilla.redhat.com/show_bug.cgi?id=785034) - Rapid key navigation causes backlog of TM queries +* [750956](https://bugzilla.redhat.com/show_bug.cgi?id=750956) - Long strings slow down the operation +* [756292](https://bugzilla.redhat.com/show_bug.cgi?id=756292) - "Participants" information is incorrect +* [759337](https://bugzilla.redhat.com/show_bug.cgi?id=759337) - Translation editor: Long word in source cell invades the editor cell +* [746899](https://bugzilla.redhat.com/show_bug.cgi?id=746899) - On push operations, copyTrans runs too slowly +* [719176](https://bugzilla.redhat.com/show_bug.cgi?id=719176) - Edit profile: "duplicate email" is shown even if user press save without changing email +* [690669](https://bugzilla.redhat.com/show_bug.cgi?id=690669) - Translation editor table shows changes which failed to save + +----------------------- + +## 1.4.5.2 + * Fix handling of fuzzy entries when saving Properties files + +----------------------- + +## 1.4.5.1 + * [795597](https://bugzilla.redhat.com/show_bug.cgi?id=795597) - Fix regression with Unicode encoding for ordinary (Latin-1) .properties files + +----------------------- + +## 1.4.5 + * [742872](https://bugzilla.redhat.com/show_bug.cgi?id=742872) - Add support for Maven modules: + * [760431](https://bugzilla.redhat.com/show_bug.cgi?id=760431) - Fix bug: Moving to a new page does not refresh the translation textboxes (ghost translations) + +----------------------- + +## 1.4.4 +* [747836](https://bugzilla.redhat.com/show_bug.cgi?id=747836) - Ensure final reindex batch is properly flushed +* [760390](https://bugzilla.redhat.com/show_bug.cgi?id=760390) - Support UTF-8 Properties files, handle empty properties +* [759994](https://bugzilla.redhat.com/show_bug.cgi?id=759994) - Fix bug: Editor table stops working after 'Source and Target' search returns no results +* Add dryRun option for Maven goals 'push' and 'pull' + +----------------------- + +## 1.4.3 +
New Features
+ +* [750690](https://bugzilla.redhat.com/show_bug.cgi?id=750690) - Show message context in editor info panel +* [727716](https://bugzilla.redhat.com/show_bug.cgi?id=727716) - Add failsafe editor in case of Seam Text problems +* [730189](https://bugzilla.redhat.com/show_bug.cgi?id=730189) - Change string similarity algorithm so that only identical strings (not substrings) can get 100% +* [747836](https://bugzilla.redhat.com/show_bug.cgi?id=747836) - Show progress during re-index operations; avoid timeout for large databases +* Update gwteventservice to 1.2.0-RC1 +* Modify email templates to include server URL + +
Bug fixes
+ +* [754637](https://bugzilla.redhat.com/show_bug.cgi?id=754637) - 'J' and 'K' navigation keys trigger when entering text in the TM search box +* [756293](https://bugzilla.redhat.com/show_bug.cgi?id=756293) - Not able to work in parallel on the same workbench +* [751264](https://bugzilla.redhat.com/show_bug.cgi?id=751264) - Fix problems with editor table when searching or switching pages + +----------------------- + +## 1.4.2 +* [742083](https://bugzilla.redhat.com/show_bug.cgi?id=742083) - Language team coordinator +* [742854](https://bugzilla.redhat.com/show_bug.cgi?id=742854) - Contact server admins +* [743783](https://bugzilla.redhat.com/show_bug.cgi?id=743783) - First/last entry button +* [744114](https://bugzilla.redhat.com/show_bug.cgi?id=744114) - Load project pages faster +* [744671](https://bugzilla.redhat.com/show_bug.cgi?id=744671) - Option for Enter to save translation +* [746859](https://bugzilla.redhat.com/show_bug.cgi?id=746859) - Sort projects by name, not ID +* [740122](https://bugzilla.redhat.com/show_bug.cgi?id=740122) - Make newlines visible to reduce newline mismatch errors in translations +* [740191](https://bugzilla.redhat.com/show_bug.cgi?id=740191) - Improve shortcut keys +* [746870](https://bugzilla.redhat.com/show_bug.cgi?id=746870) - Save as Fuzzy now leaves the cell editor open +* [743134](https://bugzilla.redhat.com/show_bug.cgi?id=743134) - Modal navigation: next fuzzy, untranslated, fuzzy or untranslated +* Rearrange various UI elements to be more logical (profile page, document stats, project search field) +* Users now have to ask before joining a language team +* Coordinator can add and remove team members +* Contact coordinators +* Fix tab order: editor cell -> Save as Approved -> Save as Fuzzy -> Cancel + +----------------------- + +## 1.4.1 +* [741523](https://bugzilla.redhat.com/show_bug.cgi?id=741523) - Fixed: % completed should be calculated with words, not messages +* [724867](https://bugzilla.redhat.com/show_bug.cgi?id=724867) - Fixed: Selecting Administration submenu items does not always highlight the parent menu +* [742111](https://bugzilla.redhat.com/show_bug.cgi?id=742111) - Fixed: Change of tile to list view on Language page, make project list sortable +* [743179](https://bugzilla.redhat.com/show_bug.cgi?id=743179) - Performance fix for projects with 1000+ documents + +----------------------- + +## 1.4 +* add project-type to zanata.xml for generic push/pull commands +* redirect to login from translation editor when required +* if domain is left blank by admin, don't populate email address for new users +* UI bug fixes + +----------------------- + +## 1.4-alpha-1 +* create generic push/pull commands, with include/exclude filters +* add support for Java Properties and XLIFF projects +* bug fix: mark existing translations of modified XLIFF/Properties strings as fuzzy +* modify keyboard shortcuts in editor +* add new Zanata logo/favicon +* various UI improvements +* auto-size for translation text area +* add icons to buttons and remove text +* add option to hide editor buttons +* remove Clone and Save button; move Copy button to middle +* autosave when leaving a cell +* remove Fuzzy checkbox; add Save as Fuzzy +* better statistics graphs +* display resource IDs for translation units +* add ability to hide translation unit details +* show translation states with coloured side bars, and italics for Fuzzy +* recalculate missing word counts +* add liquibase script +* bug fix for search re-indexing by admin +* copy translations of identical strings when importing new documents +* bug fixes and improvements for UI +* bug fix for word counts (thread safety) +* remove email address from Language Team pages +* enable stats for anonymous users +* no need to enforce locales for source documents +* bug fix for push/merge when PO files are missing some msgids +* bug fixes + +----------------------- + +## 1.3 +* bug fixes for authentication and for source comments + +----------------------- + +## 1.3-alpha-3 +* finalise rebrand from flies->zanata: XML namespaces, media types, etc +* more logging for authentication errors +* bug fix for Kerberos authentication + +----------------------- + +## 1.3-alpha-2 +* switch source control to git on github +* rebrand from flies->zanata (maven artifacts, java packages, mailing lists) +* Fedora authentication rhbz#692011 +* generate zanata.xml config file (http://code.google.com/p/flies/issues/detail?id=282) +* merge translations on import (http://code.google.com/p/flies/issues/detail?id=28) +* preserve and generate PO header comments for translator credits (http://code.google.com/p/flies/issues/detail?id=269) +* bug fixes + +----------------------- + +## 1.3-alpha-1 +* rebrand from flies->zanata (except URIs, maven artifacts and java packages) +* specify locales per project/version (http://code.google.com/p/flies/issues/detail?id=261) +* added tab for home page, removed project list, contents editable by admin (http://code.google.com/p/flies/issues/detail?id=279) +* added help page/tab, contents editable by admin (http://code.google.com/p/flies/issues/detail?id=280) +* removed name and description from project version (http://code.google.com/p/flies/issues/detail?id=281) +* stats for all languages (http://code.google.com/p/flies/issues/detail?id=275) +* workaround for form/login issue on Firefox 4.0 rhbz#691963 +* bug fixes + +----------------------- + +## flies-1.2 +* disabled bad key bindings (http://code.google.com/p/flies/issues/detail?id=262) +* fixed python client issue with PotEntryHeader.extractedComment (http://code.google.com/p/flies/issues/detail?id=256) +* web template redesign (new logo, CSS) (http://code.google.com/p/flies/issues/detail?id=238) +* fixed Seam integration tests (http://code.google.com/p/flies/issues/detail?id=231) + +----------------------- + +## flies-1.2-alpha-3 +* improve notifications in editor (http://code.google.com/p/flies/issues/detail?id=191) +* highlight search terms in editor (http://code.google.com/p/flies/issues/detail?id=227) + +----------------------- + +## flies-1.2-alpha-2 +* better messages +* bug fixes + +----------------------- + +## flies-1.2-alpha-1 +* development change: re-arranged Maven modules into common, client and server + +----------------------- + +## flies-1.1.1 +* use word counts in translation statistics (http://code.google.com/p/flies/issues/detail?id=203) +* bug fixes + +----------------------- + +## flies-1.1 +* Kerberos/JAAS fixes +* require name & email address on first login for JAAS/Kerberos +* validate changes to email address +* use correct BCP-47 language tags (zh-CN-Hans is now zh-Hans-CN) + +----------------------- + +## flies-1.1-alpha-1 +* JAAS authentication +* Kerberos authentication +* remove communities tab and my communities UI (http://code.google.com/p/flies/issues/detail?id=197) +* remove "Language Missing" button (http://code.google.com/p/flies/issues/detail?id=185) +* show member number for the language groups (http://code.google.com/p/flies/issues/detail?id=186) +* allow overriding POT directory in Maven client (http://code.google.com/p/flies/issues/detail?id=200) +* support `[servers]` in flies.ini for Maven client (http://code.google.com/p/flies/issues/detail?id=193) +* better info/error messages in Maven client + +----------------------- + +## flies-1.0.3 +* fix TM caching issue (http://code.google.com/p/flies/issues/detail?id=190) +* add 'translator' role and security rules +* configurable URLs + +----------------------- + +## flies-1.0.2 +* minor UI fixes (http://code.google.com/p/flies/issues/detail?id=173, http://code.google.com/p/flies/issues/detail?id=176) +* ergonomics for Maven client +* UI for assigning project maintainers (http://code.google.com/p/flies/issues/detail?id=180) +* better error checking in REST API (http://code.google.com/p/flies/issues/detail?id=175) +* security rule fix (http://code.google.com/p/flies/issues/detail?id=182) + +----------------------- + +## flies-1.0.1 +* database schema fixes +* fixes for deployment issues + +----------------------- + +## flies-1.0 +* initial release + diff --git a/docs/user-guide/client-command-hook.md b/docs/user-guide/client-command-hook.md new file mode 100644 index 0000000000..eaaea4650c --- /dev/null +++ b/docs/user-guide/client-command-hook.md @@ -0,0 +1,38 @@ +Custom commands can be specified in zanata.xml. Custom commands can be almost any command that can run on the command line, and can be configured to run before or after a Zanata client command. This feature was added in version 3.3.0 of the CLI client and the Maven Plugin, and cannot be used in earlier versions. + +To specify one or more custom commands: + + 1. Add a `` element in zanata.xml within the `` element. + 1. For each Zanata command that will have custom commands attached, add a `` element that specifies the command as an attribute. + 1. For each custom command to run before a Zanata command, add a `` element with the command as its body. + 1. For each custom command to run after a Zanata command, add an `` element with the command as its body. + +For example, the following elements within the top-level element will generate a .pot file from a man page before push, clean up the generated file after push, and will generate a translated German man page after pull then remove all downloaded .po files. + +```xml + + + +... + + + + po4a-gettextize -f man -m manpage.1 -p manpage.pot + rm -f manpage.pot + + + po4a-translate -f man -m manpage.1 -p trans/de/manpage.po -l manpage.de.1 --keep 1 + rm -rf trans + + + +... + + +``` + +Multiple commands of the same type (i.e. "before" or "after") within a hook will be run in the order that they are specified in zanata.xml. e.g. when running pull with the above config, po4a will always run before rm. If any of these commands fails, the whole operation is aborted. e.g. when running push, if po4a fails then push and rm will not be run, and if push fails then rm will not be run. + +Note that some commands (such as 'cd') do not work as custom commands. The ranges of commands that work and that do not work as custom commands have not yet been thoroughly investigated. + +The return value of each custom command is displayed after the command is run. A return value of 0 usually indicates success, and any other number usually indicates an error. Console output from custom commands is not yet displayed or logged. \ No newline at end of file diff --git a/docs/user-guide/client-configuration.md b/docs/user-guide/client-configuration.md index a004f67084..a29a5ad95d 100644 --- a/docs/user-guide/client-configuration.md +++ b/docs/user-guide/client-configuration.md @@ -1,5 +1,3 @@ -# Common configuration files for Flies clients - # Introduction In general, Zanata clients should get their configuration from user config, project config and command line args (or similar). @@ -84,6 +82,4 @@ URLs in project configuration should be matched against the [servers] defined in ## Command line or other mechanism (eg Maven properties) -The client should also provide the ability to override user/project configuration values, perhaps with command line arguments or GUI options, etc. - -See ZanataMavenIntegration for the Maven configuration properties and example pom.xml. \ No newline at end of file +The client should also provide the ability to override user/project configuration values, perhaps with command line arguments or GUI options, etc. \ No newline at end of file diff --git a/docs/user-guide/merge-translations.md b/docs/user-guide/merge-translations.md new file mode 100644 index 0000000000..727b19a0dc --- /dev/null +++ b/docs/user-guide/merge-translations.md @@ -0,0 +1,59 @@ +# Merge translations from another project version + +Merge translations allows project maintainers to copy translations across different project version based on matching context. (See below for context matching rules) + +## Restrictions + +- This feature is only available to project maintainers. +- Only translations that are in translated/approved state will be used. +- Merge translation can only be run if there are no other copy operations in progress for the selected version, such as copy-trans or copy version. + +## Rule of which translations will be copied over + + + + + + + + + + + + + + + + +
**From****To****Copy?**
fuzzy/untranslatedanyNo
different source text/document id/
resId/msgctxt/locale
anyNo
translated/approveduntranslated/fuzzyYes
translated/approvedtranslated/approvedcopy if `From` is newer and
`Keep existing translated/approved translations` is unchecked
+ +## Run Merge translations + +1. Login to Zanata. +1. Select a project version you wish to copy translations to. +1. Expand `More Action` menu on top right corner and click on `Merge Translations`. This action is only available if you are a maintainer of the project. +
+ More action menu in project version page +
+1. In displayed dialog, select the project-version you wish to copy translations from. +
+ Merge translation dialog +
+1. If you do not want to overwrite translated/approved translations, ensure that `Keep existing translated/approved translations` is checked. If this option is not checked, translated/approved translations will be replaced with newer translated/approved translations if they are available. +1. Click `Merge Translations` button to start the process. +1. The progress of the merge process is shown by a progress bar on the version page. +
+ Merge translation in progress +
+ +## Cancel Merge translation +**_Note: This will only stop additional translations being merged. Any translations that have already been merged will remain merged._** + +1. Go to the progress bar section in project version page. +1. Click on the `Cancel` button on top right panel. +
+ Cancel merge translation in progress +
+ + + diff --git a/docs/user-guide/project-types.md b/docs/user-guide/project-types.md index 783497a837..33e6468aad 100644 --- a/docs/user-guide/project-types.md +++ b/docs/user-guide/project-types.md @@ -4,10 +4,12 @@ Project type can be specified on the command line, in `zanata.xml`, in `pom.xml` ## Which Project Type? File types are _(selectively)_ supported via the Okapi Framework. + ### Supported Types -#### No Selection -No Selection allows the upload of all file types other than `.properties` and `.xml` via the Website GUI, but will prevent the use of Zanata CLI clients. +#### Unspecified +This project type selection is only shown for old projects that were created before project types were shown on the server. If your project or project-version has this type, you should change it to the appropriate type. +When project type is not specified, you will not be able to upload source files through the web interface, and you will have to manually add a project type to the client configuration file. #### File `.txt` `.odt` `.ods` `.odg` `.odp` `.idml` `.dtd` `.htm` `.html` Previously _Raw_, File is an **experimental** project type that provides limited support for plain-text, LibreOffice, HTML, inDesign and DTD files. Source files must be under a separate directory to translation files. The behaviour of this project type is subject to change without notice while it is in experimental state.
@@ -15,16 +17,20 @@ The parser recognises newlines for `.txt`, and paragraphs for `.html`. #### Gettext `.pot` Uses the gettext format with a single template (.pot) file. Translation files (.po) are named with the locale identifier. + #### Podir `.pot` Uses gettext format with multiple template (.pot) files. Translation files use the same name as template files, but are placed in a directory named with the locale identifier. Use this type for publican/docbook projects. + #### Properties `.properties` Handles normal java properties files using ISO-8859-1 encoding (Latin-1). Java properties files require non Latin-1 characters to be escaped with unicode escape characters (e.g. \uFEDC).
**_Note: Currently, properties files can only be uploaded using the [Zanata CLI Client](http://zanata.org/help/cli/cli-push/)_** + #### Utf8Properties `.properties` Handles non-standard java properties files that use UTF-8 encoding, and do not use unicode escape characters.
**_Note: Currently, properties files can only be uploaded using the [Zanata CLI Client](http://zanata.org/help/cli/cli-push/)_** ### Partial / Limited Support + #### Xliff Not actively supported. _(Details on file schema hopefully forthcoming...)_ #### Xml diff --git a/docs/user-guide/projects/project-view.md b/docs/user-guide/projects/project-view.md new file mode 100644 index 0000000000..605479e1a0 --- /dev/null +++ b/docs/user-guide/projects/project-view.md @@ -0,0 +1,19 @@ +When a project is selected, you will be taken to an overall view of the project. This will show a list of the available versions as well as an indication of their translation progress. + +
+Project Languages Settings tab +
Project View
+
+
+ +1. Project Information +2. Switches between the different views available for the project +3. Different options to sort project versions. + +The different views for a project offer more information about it. + +- **Maintainers** shows the project Maintainers. +- **About** shows more information about the project. +- **Settings** _(This is only available to Project Maintainers)_ allows the user to change project settings. + +To [view the information about a specific version](user-guide/versions/version-view), you can click on the version's name from the list. \ No newline at end of file diff --git a/docs/user-guide/user-profile.md b/docs/user-guide/user-profile.md new file mode 100644 index 0000000000..95a3ae03fd --- /dev/null +++ b/docs/user-guide/user-profile.md @@ -0,0 +1,6 @@ +# User Profile Page + +You can view a user's profile page by typing the username in the top search field and click on the search result. + +On the user profile page, you can view this user's recent contribution statistics according to the selected date range option. +By default it will show this week's contribution categorized by language and project. diff --git a/docs/user-guide/versions/version-view.md b/docs/user-guide/versions/version-view.md new file mode 100644 index 0000000000..ccb118208a --- /dev/null +++ b/docs/user-guide/versions/version-view.md @@ -0,0 +1,18 @@ +When a project version is selected, you will be shown specific details about the selected version. + +
+Version View Languages Tab +
Version View
+
+
+ +1. Project and version information +2. Version's overall translation progress information. +3. A list of languages that are available for translation on the version. +4. Once a language is selected, a list of documents for the version will be shown, with progress information for each document and the selected language. + +The different tabs for a version offer more information about it. + +- **Documents** Similar to the languages tab, this view lets you select a single document to view detailed progress information for each language. +- **Groups** Shows if there are any groups that the version belongs to. +- **Settings** _(This is only available to Project Maintainers)_ allows the user to change version settings. \ No newline at end of file diff --git a/etc/scripts/cargowait.sh b/etc/scripts/cargowait.sh index 79ee8226fc..efc8b60bd0 100755 --- a/etc/scripts/cargowait.sh +++ b/etc/scripts/cargowait.sh @@ -35,7 +35,7 @@ echo ">> Now working in $(pwd)" echo # this will build zanata war and prepare war overlay for functional test -mvn ${clean} package -Dchromefirefox -DskipTests -Dfunctional-test -pl zanata-model,zanata-war,zanata-test-war; +mvn ${clean} package -Dchromefirefox -DskipTests -Dappserver=wildfly8 -pl zanata-model,zanata-war,zanata-test-war; # this will start cargo container and deploy above generated overlay war and then pause -mvn ${clean} process-test-resources package cargo:run -Dfunctional-test -Dmysql.port=13306 -pl functional-test ${extraArgs}; +mvn ${clean} package cargo:run -DskipTests -Dappserver=wildfly8 -Dmysql.port=13306 -pl functional-test ${extraArgs}; diff --git a/functional-test/pom.xml b/functional-test/pom.xml index 3044f6eb96..4a046aea1e 100644 --- a/functional-test/pom.xml +++ b/functional-test/pom.xml @@ -427,12 +427,9 @@ - - - + + + @@ -809,7 +806,7 @@ ===== Properties that can be set for functional test ===== -DskipFuncTests : to skip running functional tests, and/or: -DskipArqTests : to skip Arquillian integration tests (if building zanata-war) - + Unless skipping tests, you must choose an appserver: -Dappserver=jbosseap6 -Dcargo.installation=http://example.com/jbosseap632.zip -Dcargo.basename=jbosseap632 or -Dappserver=wildfly8 @@ -818,9 +815,9 @@ appserver.dir.name is top-level dir inside zip. For jbosseap6, default is jboss-eap-6.3. Override for later versions. -DallFuncTests to enable all functional tests (defaults to smoke tests) - + -Dcargo.debug.jvm.args : If not set by default will listen to port 8787. Need to set to empty on jenkins - + -Dzanata.target.version=version of zanata to deploy. Default is: ${project.parent.version} -Dzanata.instance.url=http://${cargo.host}:${cargo.servlet.port}/${context.path} -Dzanata.apikey=b6d7044e9ee3b2447c28fb7c50d86d98 @@ -845,10 +842,8 @@ run - - + + diff --git a/functional-test/src/main/java/org/zanata/page/administration/ServerConfigurationPage.java b/functional-test/src/main/java/org/zanata/page/administration/ServerConfigurationPage.java index 7b23d43fab..537aa75cb8 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/ServerConfigurationPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/ServerConfigurationPage.java @@ -20,11 +20,13 @@ */ package org.zanata.page.administration; +import com.google.common.base.Function; import lombok.extern.slf4j.Slf4j; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.Select; import org.zanata.page.BasePage; /** @@ -34,9 +36,18 @@ @Slf4j public class ServerConfigurationPage extends BasePage { - private By urlField = By.id("serverConfigForm:urlField"); + private By urlField = By.id("serverConfigForm:urlField:url"); + public static By registerUrlField = By.id("serverConfigForm:registerField:registerUrl"); + private By emailDomainField = By.id("serverConfigForm:emailDomainField:emailDomain"); + private By adminEmailField = By.id("serverConfigForm:adminEmailField:adminEml"); + public static By fromEmailField = By.id("serverConfigForm:fromEmailField:fromEml"); + private By enableLogCheck = By.id("serverConfigForm:enableLogCheck"); + private By logLevelSelect = By.id("serverConfigForm:logEmailLvl"); + private By emailDestinationField = By.id("serverConfigForm:logDestEmailField:logDestEml"); private By helpUrlField = By.id("serverConfigForm:helpUrlField"); private By termsUrlField = By.id("serverConfigForm:termsOfUseUrlField"); + private By piwikUrl = By.id("serverConfigForm:piwikUrlField:piwikUrlEml"); + private By piwikId = By.id("serverConfigForm:piwikIdSiteEml"); private By maxConcurrentField = By.id("serverConfigForm:maxConcurrentPerApiKeyField:maxConcurrentPerApiKeyEml"); private By maxActiveField = By.id("serverConfigForm:maxActiveRequestsPerApiKeyField:maxActiveRequestsPerApiKeyEml"); private By saveButton = By.id("serverConfigForm:save"); @@ -54,6 +65,42 @@ private void enterTextConfigField(By by, String text) { .sendKeys(text).perform(); } + public ServerConfigurationPage inputServerURL(String url) { + log.info("Enter Server URL {}", url); + enterTextConfigField(urlField, url); + return new ServerConfigurationPage(getDriver()); + } + + public ServerConfigurationPage inputRegisterURL(String url) { + log.info("Enter Register URL {}", url); + enterTextConfigField(registerUrlField, url); + return new ServerConfigurationPage(getDriver()); + } + + public boolean expectFieldValue(final By by, final String expectedValue) { + log.info("Wait for field {} value", by.toString(), expectedValue); + return waitForAMoment().until(new Function() { + @Override + public Boolean apply(WebDriver input) { + String value = waitForWebElement(by).getAttribute("value"); + log.info("Found {}", value); + return expectedValue.equals(value); + } + }); + } + + public ServerConfigurationPage inputAdminEmail(String email) { + log.info("Enter admin email address {}", email); + enterTextConfigField(adminEmailField, email); + return new ServerConfigurationPage(getDriver()); + } + + public ServerConfigurationPage inputAdminFromEmail(String email) { + log.info("Enter admin From email address {}", email); + enterTextConfigField(fromEmailField, email); + return new ServerConfigurationPage(getDriver()); + } + public ServerConfigurationPage inputHelpURL(String url) { log.info("Enter Help URL {}", url); enterTextConfigField(helpUrlField, url); @@ -66,10 +113,61 @@ public ServerConfigurationPage inputTermsOfUseURL(String url) { return new ServerConfigurationPage(getDriver()); } + public ServerConfigurationPage clickLoggingEnabledCheckbox() { + log.info("Click enable logging checkbox"); + waitForWebElement(enableLogCheck).click(); + return new ServerConfigurationPage(getDriver()); + } + + public ServerConfigurationPage selectLoggingLevel(String logLevel) { + log.info("Select logging level {}", logLevel); + new Select(waitForWebElement(logLevelSelect)).selectByVisibleText( + logLevel); + return new ServerConfigurationPage(getDriver()); + } + + public String selectedLoggingLevel() { + log.info("Query selected logging level"); + return new Select(waitForWebElement(logLevelSelect)) + .getFirstSelectedOption().getText(); + } + + public ServerConfigurationPage inputLogEmailTarget(String email) { + log.info("Enter log email target {}", email); + enterTextConfigField(emailDestinationField, email); + return new ServerConfigurationPage(getDriver()); + } + + public String getLogEmailTarget() { + log.info("Query log email target"); + return waitForWebElement(emailDestinationField).getAttribute("value"); + } + + public ServerConfigurationPage inputPiwikUrl(String url) { + log.info("Enter Piwik URL", url); + enterTextConfigField(piwikUrl, url); + return new ServerConfigurationPage(getDriver()); + } + + public String getPiwikUrl() { + log.info("Query Piwik URL"); + return waitForWebElement(piwikUrl).getAttribute("value"); + } + + public ServerConfigurationPage inputPiwikID(String id) { + log.info("Enter Piwik ID", id); + enterTextConfigField(piwikId, id); + return new ServerConfigurationPage(getDriver()); + } + + public String getPiwikID() { + log.info("Query Piwik ID"); + return waitForWebElement(piwikId).getAttribute("value"); + } + public ServerConfigurationPage inputMaxConcurrent(int max) { log.info("Enter maximum concurrent API requests {}", max); - waitForWebElement(maxConcurrentField).clear(); - waitForWebElement(maxConcurrentField).sendKeys(max + ""); + enterTextConfigField(maxConcurrentField, Integer.toString(max)); return new ServerConfigurationPage(getDriver()); } @@ -80,8 +178,7 @@ public String getMaxConcurrentRequestsPerApiKey() { public ServerConfigurationPage inputMaxActive(int max) { log.info("Enter maximum active API requests {}", max); - waitForWebElement(maxActiveField).clear(); - waitForWebElement(maxActiveField).sendKeys(max + ""); + enterTextConfigField(maxActiveField, Integer.toString(max)); return new ServerConfigurationPage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectLanguagesTab.java b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectLanguagesTab.java index 0e05412a34..584d701e2b 100644 --- a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectLanguagesTab.java +++ b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectLanguagesTab.java @@ -180,6 +180,11 @@ public ProjectLanguagesTab clickAddAlias(String locale) { return new ProjectLanguagesTab(getDriver()); } + public ProjectLanguagesTab clickEditAlias(String locale) { + LanguageList.clickEditAlias(waitForWebElement(activeLocales), locale); + return new ProjectLanguagesTab(getDriver()); + } + public ProjectLanguagesTab enterAliasForLocale(String locale, String alias) { LanguageList.enterAlias(waitForWebElement(activeLocales), locale, alias); @@ -191,6 +196,11 @@ public ProjectLanguagesTab saveLocaleAlias(String locale) { return new ProjectLanguagesTab(getDriver()); } + public ProjectLanguagesTab deleteAlias(String locale) { + LanguageList.unsetAlias(waitForWebElement(activeLocales), locale); + return new ProjectLanguagesTab(getDriver()); + } + public String getAlias(String locale) { return LanguageList.getAliasForLocale(waitForWebElement(activeLocales), locale); diff --git a/functional-test/src/main/java/org/zanata/util/LanguageList.java b/functional-test/src/main/java/org/zanata/util/LanguageList.java index aa28abbe0e..76e6bb14ff 100644 --- a/functional-test/src/main/java/org/zanata/util/LanguageList.java +++ b/functional-test/src/main/java/org/zanata/util/LanguageList.java @@ -43,6 +43,8 @@ private LanguageList(){} private static By aliasInput = By.className("form--inline__input"); private static By setAlias = By.className("form--inline__addon"); private static By addAliasLink = By.linkText("Add alias"); + private static By deleteAliasLink = By.className("i--trash"); + private static By editAliasLink = By.linkText("Edit alias"); public static List getListedLocales(WebElement localeList) { return Lists.transform(getListElements(localeList), @@ -86,21 +88,40 @@ public static void clickAddAlias(WebElement list, String locale) { getLocaleEntry(list, locale).findElement(addAliasLink).click(); } + public static void clickEditAlias(WebElement list, String locale) { + getLocaleEntry(list, locale).findElement(editAliasLink).click(); + } + public static void enterAlias(WebElement localeList, String locale, String alias) { - getLocaleEntry(localeList, locale).findElement(aliasInput) - .sendKeys(alias); + WebElement field = getLocaleEntry(localeList, locale) + .findElement(aliasInput); + field.clear(); + field.sendKeys(alias); } public static void setAlias(WebElement localeList, String locale) { getLocaleEntry(localeList, locale).findElement(setAlias).click(); } + public static void unsetAlias(WebElement localeList, String locale) { + getLocaleEntry(localeList, locale).findElement(deleteAliasLink).click(); + } + public static String getAliasForLocale(WebElement list, String locale) { + if (!hasAlias(list, locale)) { + return ""; + } + return getLocaleEntry(list, locale).findElement(localeAlias) .getText(); } + private static boolean hasAlias(WebElement localeList, String locale) { + return getLocaleEntry(localeList, locale).findElements(localeAlias) + .size() > 0; + } + private static WebElement getLocaleEntry(WebElement list, String locale) { List listElements = LanguageList .getListElements(list); diff --git a/functional-test/src/test/java/org/zanata/feature/administration/ServerSettingsTest.java b/functional-test/src/test/java/org/zanata/feature/administration/ServerSettingsTest.java index 6ba500f8f0..11042da5ac 100644 --- a/functional-test/src/test/java/org/zanata/feature/administration/ServerSettingsTest.java +++ b/functional-test/src/test/java/org/zanata/feature/administration/ServerSettingsTest.java @@ -1,14 +1,38 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ package org.zanata.feature.administration; +import lombok.extern.slf4j.Slf4j; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.testharness.TestPlan; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.page.account.RegisterPage; +import org.zanata.page.administration.ServerConfigurationPage; import org.zanata.page.utility.HomePage; import org.zanata.util.AddUsersRule; +import org.zanata.util.HasEmailRule; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.RegisterWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -16,12 +40,94 @@ * @author Damian Jansen * djansen@redhat.com */ +@Slf4j @Category(TestPlan.DetailedTest.class) public class ServerSettingsTest extends ZanataTestCase { + @Rule + public HasEmailRule hasEmailRule = new HasEmailRule(); + @Rule public AddUsersRule addUsersRule = new AddUsersRule(); + @Test + public void setServerURLTest() { + new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .inputServerURL("http://myserver.com/zanata") + .save() + .goToHomePage() + .clickContactAdmin() + .inputSubject("I AM THE ADMIN") + .inputMessage("Test pattern") + .send(HomePage.class); + String emailContent = HasEmailRule + .getEmailContent(hasEmailRule.getMessages().get(0)); + + assertThat(emailContent).contains("http://myserver.com/zanata") + .as("The email indicates the expected server url"); + } + + @Test + public void setRegisterURLTest() { + String url = "http://myserver.com/register"; + ServerConfigurationPage serverConfigurationPage = new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .inputRegisterURL(url) + .save() + .goToServerConfigPage(); + + assertThat(serverConfigurationPage + .expectFieldValue(ServerConfigurationPage.registerUrlField, url)) + .as("The expected url was displayed"); + } + + @Test + public void setAdministratorEmailTest() { + new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .inputAdminEmail("lara@example.com") + .save() + .goToHomePage() + .clickContactAdmin() + .inputSubject("Test email recipient") + .inputMessage("Test pattern") + .send(HomePage.class); + + assertThat(hasEmailRule.getMessages().get(0).getEnvelopeReceiver()) + .contains("lara@example.com") + .as("The recipient admin was set"); + } + + @Test + public void setAdministratorEmailFromTest() { + String email = "lara@example.com"; + ServerConfigurationPage serverConfigurationPage = new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .inputAdminFromEmail(email) + .save() + .goToServerConfigPage(); + + assertThat(serverConfigurationPage.expectFieldValue( + ServerConfigurationPage.fromEmailField, email)); + + serverConfigurationPage.goToHomePage().logout(); + new RegisterWorkFlow().registerInternal("test1", "test1", "test123", + "test1@test.com"); + + assertThat(hasEmailRule.getMessages().get(0).getEnvelopeSender()) + .contains("lara@example.com") + .as("The server email sender was set"); + } + @Test public void setHelpURLTest() { HomePage homePage = new LoginWorkFlow() @@ -70,4 +176,43 @@ public void setTermsOfUseURLTest() { .isEqualTo("http://www.test.com/") .as("The Terms of Use URL was set correctly"); } + + @Test + public void setEmailLoggingTest() { + ServerConfigurationPage serverConfigurationPage = new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .clickLoggingEnabledCheckbox() + .selectLoggingLevel("Error") + .inputLogEmailTarget("lara@example.com") + .save() + .goToServerConfigPage(); + + assertThat(serverConfigurationPage.selectedLoggingLevel()) + .isEqualTo("Error") + .as("Level is correct"); + assertThat(serverConfigurationPage.getLogEmailTarget()) + .isEqualTo("lara@example.com") + .as("Recipient is correct"); + } + + @Test + public void setPiwikTest() { + ServerConfigurationPage serverConfigurationPage = new LoginWorkFlow() + .signIn("admin", "admin") + .goToAdministration() + .goToServerConfigPage() + .inputPiwikUrl("http://example.com/piwik") + .inputPiwikID("12345") + .save() + .goToServerConfigPage(); + + assertThat(serverConfigurationPage.getPiwikUrl()) + .isEqualTo("http://example.com/piwik") + .as("Piwik url is correct is correct"); + assertThat(serverConfigurationPage.getPiwikID()) + .isEqualTo("12345") + .as("Piwik ID is correct"); + } } diff --git a/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java b/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java index fc4f2be293..654558995f 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java @@ -28,6 +28,7 @@ import org.assertj.core.api.Assertions; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -129,4 +130,60 @@ public void setLanguageAliasTest() { .isEqualTo("pl-PL") .as("The alias was set"); } + + @Feature(summary = "The administrator can remove an alias for a project " + + "language", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) + @Test + public void removeLanguageAliasTest() { + ProjectLanguagesTab projectLanguagesTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") + .gotoSettingsTab() + .gotoSettingsLanguagesTab() + .clickLanguageActionsDropdown("pl") + .clickAddAlias("pl") + .enterAliasForLocale("pl", "pl-PL") + .saveLocaleAlias("pl"); + + assertThat(projectLanguagesTab.getAlias("pl")) + .isEqualTo("pl-PL") + .as("The alias was set"); + + projectLanguagesTab = projectLanguagesTab + .clickLanguageActionsDropdown("pl") + .deleteAlias("pl"); + + assertThat(projectLanguagesTab.getAlias("pl")) + .isEmpty(); + } + + @Feature(summary = "The administrator can edit an alias for a project " + + "language", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) + @Test + public void editLanguageAliasTest() { + String locale = "pl"; + ProjectLanguagesTab projectLanguagesTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") + .gotoSettingsTab() + .gotoSettingsLanguagesTab() + .clickLanguageActionsDropdown(locale) + .clickAddAlias(locale) + .enterAliasForLocale(locale, "pl-PL") + .saveLocaleAlias(locale); + + assertThat(projectLanguagesTab.getAlias(locale)) + .isEqualTo("pl-PL") + .as("The alias was set"); + + projectLanguagesTab = projectLanguagesTab + .clickLanguageActionsDropdown(locale) + .clickEditAlias(locale) + .enterAliasForLocale(locale, "pl-POL") + .saveLocaleAlias(locale); + + assertThat(projectLanguagesTab.getAlias(locale)) + .as("The alias was changed") + .isEqualTo("pl-POL"); + } } diff --git a/functional-test/src/test/resources/conf/standalone.xml b/functional-test/src/test/resources/conf/standalone.xml index 75d4462c58..7dcc3fd579 100644 --- a/functional-test/src/test/resources/conf/standalone.xml +++ b/functional-test/src/test/resources/conf/standalone.xml @@ -28,6 +28,7 @@ + @@ -85,6 +86,18 @@ + + + + + + + + + + + + @@ -182,6 +195,7 @@
@@ -198,6 +212,18 @@ + + + + + + + + diff --git a/functional-test/src/test/resources/conf/standalone_wildfly.xml b/functional-test/src/test/resources/conf/standalone_wildfly.xml index 54a5be7ebf..b1e1665108 100644 --- a/functional-test/src/test/resources/conf/standalone_wildfly.xml +++ b/functional-test/src/test/resources/conf/standalone_wildfly.xml @@ -28,6 +28,7 @@ + @@ -104,6 +105,18 @@ + + + + + + + + + + + + @@ -233,7 +246,8 @@ - + @@ -249,6 +263,19 @@ + + + + + + + + diff --git a/mkdocs.yml b/mkdocs.yml index 78e05aafd1..eb6e6537ac 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,12 +9,12 @@ pages: - ['index.md', 'Home'] # Configuration Guide +- ['configuration/installation.md', 'Configuration', 'Installation'] - ['configuration/authentication.md', 'Configuration', 'Authentication'] - ['configuration/document-storage-directory.md', 'Configuration', 'Document Storage Directory'] # Once pull request https://github.com/zanata/zanata-server/pull/633 is integrated, this can be uncommented # - ['configuration/email.md', 'Configuration', 'Email'] -# Once pull request https://github.com/zanata/zanata-server/pull/577 is integrated, this can be uncommented -# - ['configuration/infinispan.md', 'Configuration', 'Caches'] +- ['configuration/infinispan.md', 'Configuration', 'Caches'] - ['configuration/jms-messaging.md', 'Configuration', 'JMS Messaging'] # Release notes @@ -27,10 +27,16 @@ pages: - ['user-guide/keyboard-shortcuts.md', 'User Guide', 'Keyboard Shortcuts'] - ['user-guide/project-types.md', 'User Guide', 'Project Types'] - ['user-guide/client-configuration.md', 'User Guide', 'Client Configuration'] +- ['user-guide/client-command-hook.md', 'User Guide', 'Client Command Hook'] +- ['user-guide/user-profile.md', 'User Guide', 'User Profile Page'] +- ['user-guide/merge-translations.md', 'User Guide', 'Merge translations'] ## Project - ['user-guide/projects/create-project.md', 'User Guide', 'Creating a Project'] - ['user-guide/projects/project-settings.md', 'User Guide', 'Project settings'] - ['user-guide/projects/create-version.md', 'User Guide', 'Creating a Version'] - ['user-guide/projects/upload-strings.md', 'User Guide', 'Upload Strings'] +- ['user-guide/projects/project-view.md', 'User Guide', 'The Project View'] +## Version +- ['user-guide/versions/version-view.md', 'User Guide', 'The Version View'] copyright: Copyright © 2015, Red Hat. diff --git a/pom.xml b/pom.xml index b7bbb21c09..cb36349d5a 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ scm:git:git://github.com/zanata/zanata-server.git scm:git:git@github.com:zanata/zanata-server.git https://github.com/zanata/zanata-server + HEAD @@ -44,7 +45,7 @@ 3.3.1 3.3.1 - 3.4.0 + 3.6.0 4.5.0.Final @@ -340,6 +341,20 @@ 3.2.3.Final + + org.jboss.marshalling + jboss-marshalling + 1.4.6.Final + test + + + + org.jboss.marshalling + jboss-marshalling-river + 1.4.6.Final + test + + org.opensymphony.quartz quartz @@ -686,6 +701,20 @@ ${hibernate.search.scope} + + org.hibernate + hibernate-search-infinispan + ${hibernate.search.version} + ${hibernate.search.scope} + + + + org.infinispan + infinispan-core + + + + org.hibernate.common hibernate-commons-annotations @@ -708,12 +737,6 @@ jboss-logging - - - - org.hibernate - hibernate-ehcache - ${hibernate.version} @@ -741,6 +764,12 @@ + + org.hibernate + hibernate-ehcache + ${hibernate.version} + + org.hibernate hibernate-testing @@ -941,7 +970,7 @@ org.fedorahosted.tennera jgettext - 0.13 + 0.14 org.functionaljava @@ -1413,20 +1442,10 @@ - - - - + + + + @@ -1501,24 +1520,12 @@ 1.2.1.Final test - - org.jboss.marshalling - jboss-marshalling - 1.4.6.Final - test - org.jboss.logmanager jboss-logmanager 1.5.2.Final test - - org.jboss.marshalling - jboss-marshalling-river - 1.4.6.Final - test - org.jboss.modules jboss-modules diff --git a/zanata-model/pom.xml b/zanata-model/pom.xml index 85e8ddc087..2234cfbea9 100644 --- a/zanata-model/pom.xml +++ b/zanata-model/pom.xml @@ -15,6 +15,7 @@ scm:git:git://github.com/zanata/zanata.git scm:git:git@github.com:zanata/zanata.git https://github.com/zanata/zanata + HEAD diff --git a/zanata-model/src/main/java/org/zanata/model/HTextFlowTarget.java b/zanata-model/src/main/java/org/zanata/model/HTextFlowTarget.java index 3ead98305d..6654c3f3f8 100644 --- a/zanata-model/src/main/java/org/zanata/model/HTextFlowTarget.java +++ b/zanata-model/src/main/java/org/zanata/model/HTextFlowTarget.java @@ -46,6 +46,7 @@ import javax.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -117,10 +118,25 @@ public class HTextFlowTarget extends ModelEntityBase implements HasContents, private List reviewComments; + @Getter + private String revisionComment; + + private boolean revisionCommentSet = false; + // Only for internal use (persistence transient) @Setter(AccessLevel.PRIVATE) private Integer oldVersionNum; + public void setRevisionComment(String revisionComment) { + this.revisionComment = revisionComment; + revisionCommentSet = true; + } + + @Transient + boolean isRevisionCommentSet() { + return revisionCommentSet; + } + // Only for internal use (persistence transient) @Setter(AccessLevel.PRIVATE) private HTextFlowTargetHistory initialState; @@ -133,8 +149,8 @@ public HTextFlowTarget(HTextFlow textFlow, @Nonnull HLocale locale) { // TODO PERF @NaturalId(mutable=false) for better criteria caching @NaturalId - @ManyToOne - @JoinColumn(name = "locale", nullable = false) + @ManyToOne(optional = false) + @JoinColumn(name = "locale", nullable = false, updatable = false) @Field(analyze = Analyze.NO) @FieldBridge(impl = LocaleIdBridge.class) public @Nonnull @@ -411,6 +427,9 @@ private void preUpdate(HTextFlowTarget tft) { // insert history if this has changed from its initial state if (tft.initialState != null && tft.initialState.hasChanged(tft)) { tft.getHistory().put(tft.oldVersionNum, tft.initialState); + if (!tft.isRevisionCommentSet()) { + tft.setRevisionComment(null); + } } } diff --git a/zanata-model/src/main/java/org/zanata/model/HTextFlowTargetHistory.java b/zanata-model/src/main/java/org/zanata/model/HTextFlowTargetHistory.java index 8b1029537c..35e8c28ffb 100644 --- a/zanata-model/src/main/java/org/zanata/model/HTextFlowTargetHistory.java +++ b/zanata-model/src/main/java/org/zanata/model/HTextFlowTargetHistory.java @@ -46,6 +46,8 @@ import org.zanata.common.ContentState; import com.google.common.base.Objects; +import lombok.Getter; +import lombok.Setter; @Entity @Immutable @@ -105,6 +107,10 @@ public static String getQueryNameMatchingHistory(int size) { private HPerson reviewer; + @Getter + @Setter + private String revisionComment; + public HTextFlowTargetHistory() { } @@ -115,9 +121,11 @@ public HTextFlowTargetHistory(HTextFlowTarget target) { this.textFlowRevision = target.getTextFlowRevision(); this.textFlowTarget = target; this.versionNum = target.getVersionNum(); - translator = target.getTranslator(); - reviewer = target.getReviewer(); + this.translator = target.getTranslator(); + this.reviewer = target.getReviewer(); this.setContents(target.getContents()); + this.revisionComment = target.getRevisionComment(); + } @Id @@ -246,12 +254,13 @@ public boolean hasChanged(HTextFlowTarget current) { this.lastModifiedBy) || !Objects.equal(current.getTranslator(), this.translator) || !Objects.equal(current.getReviewer(), this.reviewer) - || !Objects.equal(current.getState(), this.state) + || !Objects.equal(current.getState(), this.state) || !Objects.equal(current.getTextFlowRevision(), this.textFlowRevision) || !Objects.equal(current.getLastChanged(), this.lastChanged) || !Objects.equal(current.getTextFlow().getId(), this.textFlowTarget.getId()) - || !Objects.equal(current.getVersionNum(), this.versionNum); + || !Objects.equal(current.getVersionNum(), this.versionNum) + || !Objects.equal(current.getRevisionComment(), this.revisionComment); } } diff --git a/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml b/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml index ee7c3d676c..94ffcde332 100644 --- a/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml +++ b/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml @@ -31,6 +31,7 @@ + @@ -112,9 +113,18 @@ + + + + + + + + + @@ -234,6 +244,19 @@ + + + + + + + + diff --git a/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml b/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml index a4edfd67c3..a6b0e56d01 100644 --- a/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml +++ b/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml @@ -32,6 +32,7 @@ + @@ -110,6 +111,18 @@ + + + + + + + + + + + + @@ -255,7 +268,8 @@ - + @@ -271,6 +285,19 @@ + + + + + + + + diff --git a/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java b/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java index 64d9852827..50b430c8a1 100644 --- a/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java +++ b/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java @@ -85,21 +85,7 @@ public void deleteExceptEssentialData() { .createQuery("from HApplicationConfiguration", HApplicationConfiguration.class).getResultList(); - Iterable cleanableConfig = - Iterables.filter(configurations, - new Predicate() { - @Override - public boolean - apply(HApplicationConfiguration input) { - String key = input.getKey(); - // TODO if we ever need to test this settings, we need to reset values for following to default - return !key - .equals(HApplicationConfiguration.KEY_EMAIL_FROM_ADDRESS) - && !key.equals(HApplicationConfiguration.KEY_EMAIL_LOG_EVENTS) - && !key.equals(HApplicationConfiguration.KEY_EMAIL_LOG_LEVEL); - } - }); - for (HApplicationConfiguration configuration : cleanableConfig) { + for (HApplicationConfiguration configuration : configurations) { entityManager.remove(configuration); } entityManager.flush(); diff --git a/zanata-war/pom.xml b/zanata-war/pom.xml index 2514a0c72d..c8e685b826 100644 --- a/zanata-war/pom.xml +++ b/zanata-war/pom.xml @@ -15,6 +15,7 @@ scm:git:git://github.com/zanata/zanata.git scm:git:git@github.com:zanata/zanata.git https://github.com/zanata/zanata + HEAD @@ -132,7 +133,6 @@ org.hibernate.common:hibernate-commons-annotations - org.hibernate:hibernate-ehcache org.hibernate:hibernate-search-analyzers org.hibernate:hibernate-testing @@ -991,10 +991,8 @@ run - - + + @@ -1054,9 +1052,7 @@ - + @@ -1186,10 +1182,7 @@ - + @@ -1499,11 +1492,6 @@ ${hibernate.scope} - - org.hibernate - hibernate-ehcache - - org.hibernate hibernate-validator @@ -1527,6 +1515,11 @@ + + org.hibernate + hibernate-ehcache + + org.hibernate hibernate-testing @@ -1539,6 +1532,12 @@ + + net.sf.ehcache + ehcache-core + 2.5.1 + + io.undertow undertow-core @@ -1582,12 +1581,6 @@ 3.3 - - net.sf.ehcache - ehcache-core - 2.5.1 - - @@ -1767,6 +1760,31 @@ org.hibernate hibernate-search-engine + + org.hibernate + hibernate-search-infinispan + + + + org.infinispan + infinispan-core + + 5.2.4.Final + provided + + + + + org.jboss.marshalling + jboss-marshalling + provided + + + org.jboss.marshalling + jboss-marshalling-river + provided + org.jboss.spec.javax.el @@ -2117,7 +2135,7 @@ se.jiderhamn classloader-leak-prevention - 1.11.0 + 1.12.0 javax.servlet diff --git a/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java b/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java index b5c59ca21d..26f6337c7e 100644 --- a/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java +++ b/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java @@ -123,7 +123,7 @@ public class ApplicationConfiguration implements Serializable { private Optional openIdProvider; // Cache the OpenId provider - private String serverPath; + private String defaultServerPath; @Create public void load() { @@ -243,20 +243,26 @@ public String getRegisterPath() { public String getServerPath() { String configuredValue = databaseBackedConfig.getServerHost(); if (configuredValue != null) { - serverPath = configuredValue; + return configuredValue; + } else if (defaultServerPath != null) { + return defaultServerPath; + } else { + createDefaultServerPath(); + return defaultServerPath; } - // Try to determine a server path if one is not configured - if (serverPath == null) { - HttpServletRequest request = - ServletContexts.instance().getRequest(); - if (request != null) { - serverPath = - request.getScheme() + "://" + request.getServerName() - + ":" + request.getServerPort() - + request.getContextPath(); - } + } + + + //@see comment at org.zanata.security.AuthenticationManager.onLoginCompleted() + public void createDefaultServerPath() { + HttpServletRequest request = + ServletContexts.instance().getRequest(); + if (request != null) { + defaultServerPath = + request.getScheme() + "://" + request.getServerName() + + ":" + request.getServerPort() + + request.getContextPath(); } - return serverPath; } public String getDocumentFileStorageLocation() { diff --git a/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java b/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java index 2b97959264..da133d01cf 100644 --- a/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java +++ b/zanata-war/src/main/java/org/zanata/action/CopyTransAction.java @@ -30,6 +30,8 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.security.Restrict; +import org.jboss.seam.faces.FacesMessages; +import org.jboss.seam.international.StatusMessage; import org.zanata.async.handle.CopyTransTaskHandle; import org.zanata.dao.ProjectIterationDAO; import org.zanata.i18n.Messages; @@ -37,7 +39,7 @@ import org.zanata.model.HProjectIteration; import org.zanata.seam.scope.ConversationScopeMessages; import org.zanata.service.impl.CopyTransOptionFactory; -import org.zanata.ui.ProgressBar; +import org.zanata.ui.CopyAction; import org.zanata.util.DateUtil; import com.google.common.base.Optional; @@ -52,7 +54,7 @@ */ @Name("copyTransAction") @Scope(ScopeType.CONVERSATION) -public class CopyTransAction implements Serializable, ProgressBar { +public class CopyTransAction extends CopyAction implements Serializable { private static final long serialVersionUID = 1L; @In @@ -86,35 +88,40 @@ public void onCreate() { .getExplicitOptions()); } - @Override public boolean isInProgress() { return copyTransManager.isCopyTransRunning(getProjectIteration()); } + @Override + public String getProgressMessage() { + StringBuilder message = new StringBuilder(); + message.append( + msgs.format("jsf.iteration.CopyTrans.processedItems", + getCurrentProgress(), getMaxProgress())) + .append(", ") + .append(msgs.format( + "jsf.iteration.CopyTrans.estimatedTimeRemaining", + getCopyTransEstimatedTimeLeft())); + + return message.toString(); + } + + @Override + public void onComplete() { + FacesMessages.instance().add(StatusMessage.Severity.INFO, + msgs.format("jsf.iteration.CopyTrans.Completed", + getProjectSlug(), getIterationSlug())); + } + @Begin(join = true) public void updateCopyTrans(String action, String value) { copyTransOptionsModel.update(action, value); } @Override - public String getCompletedPercentage() { - CopyTransTaskHandle handle = - copyTransManager - .getCopyTransProcessHandle(getProjectIteration()); - if (handle != null) { - double completedPercent = - (double) handle.getCurrentProgress() / (double) handle - .getMaxProgress() * 100; - if (Double.compare(completedPercent, 100) == 0) { - conversationScopeMessages - .setMessage( - FacesMessage.SEVERITY_INFO, - msgs.get("jsf.iteration.CopyTrans.Completed")); - } - return PERCENT_FORMAT.format(completedPercent); - } else { - return "0"; - } + protected CopyTransTaskHandle getHandle() { + return copyTransManager + .getCopyTransProcessHandle(getProjectIteration()); } public HProjectIteration getProjectIteration() { diff --git a/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java b/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java index 6769251552..d8ca47b6e7 100644 --- a/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java +++ b/zanata-war/src/main/java/org/zanata/action/CopyVersionManager.java @@ -18,8 +18,9 @@ import org.zanata.service.CopyVersionService; /** + * Manages copy version tasks. + * * @author Alex Eng aeng@redhat.com - * @author Carlos Munoz aeng@redhat.com */ @AutoCreate @Name("copyVersionManager") @@ -35,6 +36,16 @@ public class CopyVersionManager implements Serializable { @In private Identity identity; + /** + * Copy existing version to new version + * + * @param projectSlug + * - existing project identifier + * @param versionSlug + * - existing version identifier + * @param newVersionSlug + * - new version identifier + */ public void startCopyVersion(String projectSlug, String versionSlug, String newVersionSlug) { CopyVersionKey key = CopyVersionKey.getKey(projectSlug, newVersionSlug); @@ -44,6 +55,14 @@ public void startCopyVersion(String projectSlug, String versionSlug, newVersionSlug, handle); } + /** + * Cancel running copy version task + * + * @param projectSlug + * - target project identifier + * @param versionSlug + * - target version identifier + */ public void cancelCopyVersion(String projectSlug, String versionSlug) { if (isCopyVersionRunning(projectSlug, versionSlug)) { CopyVersionTaskHandle handle = @@ -68,11 +87,21 @@ public boolean isCopyVersionRunning(String projectSlug, String versionSlug) { return handle != null && !handle.isDone(); } + /** + * Key used for copy version task + * + * @param projectSlug + * - target project identifier + * @param versionSlug + * - target version identifier + */ @EqualsAndHashCode @Getter @AllArgsConstructor public static final class CopyVersionKey implements Serializable { + // target project identifier private final String projectSlug; + // target version identifier private final String versionSlug; public static CopyVersionKey getKey(String projectSlug, diff --git a/zanata-war/src/main/java/org/zanata/action/DashboardAction.java b/zanata-war/src/main/java/org/zanata/action/DashboardAction.java index 959e3349c1..4131138011 100644 --- a/zanata-war/src/main/java/org/zanata/action/DashboardAction.java +++ b/zanata-war/src/main/java/org/zanata/action/DashboardAction.java @@ -62,7 +62,6 @@ @Restrict("#{identity.loggedIn}") public class DashboardAction implements Serializable { private static final long serialVersionUID = 1L; - private static final int USER_IMAGE_SIZE = 115; @In private GravatarService gravatarServiceImpl; @@ -100,7 +99,8 @@ public class DashboardAction implements Serializable { fetchUserMaintainedProjects(); public String getUserImageUrl() { - return gravatarServiceImpl.getUserImageUrl(USER_IMAGE_SIZE); + return gravatarServiceImpl.getUserImageUrl( + GravatarService.USER_IMAGE_SIZE); } public String getUsername() { diff --git a/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java b/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java index b8ed5442dc..a1575dbb7b 100644 --- a/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java +++ b/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java @@ -24,12 +24,14 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import org.apache.commons.lang.StringUtils; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.zanata.common.LocaleId; import org.zanata.model.HLocale; import org.zanata.security.ZanataIdentity; import org.zanata.service.LanguageTeamService; @@ -37,6 +39,7 @@ import org.zanata.ui.InMemoryListFilter; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import lombok.Getter; @Name("languagesAction") @@ -55,6 +58,8 @@ public class LanguagesAction extends InMemoryListFilter implements private List allLanguages; + private Map membersSize = Maps.newHashMap(); + @Getter private SortingType LanguageSortingList = new SortingType( Lists.newArrayList(SortingType.SortOption.ALPHABETICAL, @@ -81,6 +86,11 @@ public void sortLanguageList() { this.reset(); } + public int getMemberSize(LocaleId localeId) { + Integer size = membersSize.get(localeId); + return size == null ? 0 : size; + } + @Override protected List fetchAll() { if (allLanguages == null) { @@ -90,6 +100,10 @@ protected List fetchAll() { } else { allLanguages = localeServiceImpl.getSupportedLocales(); } + for (HLocale locale : allLanguages) { + membersSize.put(locale.getLocaleId(), locale.getMembers() + .size()); + } } return allLanguages; } @@ -127,9 +141,9 @@ public int compare(HLocale o1, HLocale o2) { } else if (selectedSortOption .equals(SortingType.SortOption.LOCALE_ID)) { return o1.getLocaleId().getId().compareTo( - o2.getLocaleId().getId()); + o2.getLocaleId().getId()); } else { - return o1.getMembers().size() - o2.getMembers().size(); + return getMemberSize(o1.getLocaleId()) - getMemberSize(o2.getLocaleId()); } } } diff --git a/zanata-war/src/main/java/org/zanata/action/MergeTransAction.java b/zanata-war/src/main/java/org/zanata/action/MergeTransAction.java new file mode 100644 index 0000000000..f9734e9564 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/MergeTransAction.java @@ -0,0 +1,238 @@ +package org.zanata.action; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import javax.faces.application.FacesMessage; + +import com.google.common.collect.Lists; +import lombok.Getter; +import lombok.Setter; + +import org.apache.commons.lang.StringUtils; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.faces.FacesMessages; +import org.jboss.seam.international.StatusMessage; +import org.jboss.seam.security.management.JpaIdentityStore; +import org.zanata.async.handle.MergeTranslationsTaskHandle; +import org.zanata.common.EntityStatus; +import org.zanata.dao.ProjectDAO; +import org.zanata.dao.ProjectIterationDAO; +import org.zanata.i18n.Messages; +import org.zanata.model.HAccount; +import org.zanata.model.HProject; +import org.zanata.model.HProjectIteration; +import org.zanata.ui.CopyAction; + +/** + * Handles user interaction from merge_trans_modal.xhtml. + * - start merge translation process. + * - cancel merge translation process. + * - gives progress data of merge translation. + * - provides projects and versions for user selection. + * + * see merge_trans_modal.xhtml for all actions. + * + * @author Alex Eng aeng@redhat.com + */ +@Name("mergeTransAction") +@Scope(ScopeType.PAGE) +public class MergeTransAction extends CopyAction implements Serializable { + + @Getter + private String targetProjectSlug; + + @Getter + @Setter + private String targetVersionSlug; + + @Getter + private String sourceProjectSlug; + + @Getter + @Setter + private String sourceVersionSlug; + + @Getter + @Setter + private boolean keepExistingTranslation; + + @In + private ProjectDAO projectDAO; + + @In + private ProjectIterationDAO projectIterationDAO; + + @In + private MergeTranslationsManager mergeTranslationsManager; + + @In + private CopyTransManager copyTransManager; + + @In + private CopyVersionManager copyVersionManager; + + @In + private Messages msgs; + + @In(required = false, value = JpaIdentityStore.AUTHENTICATED_USER) + private HAccount authenticatedAccount; + + private HProjectIteration targetVersion; + + private HProject sourceProject; + + public void setTargetProjectSlug(String targetProjectSlug) { + this.targetProjectSlug = targetProjectSlug; + + /** + * This should only true during first instantiation to make sure + * sourceProject from selection are the same as targetProject on load. + * + * See pages.xml for setter of mergeTransAction.targetProjectSlug. + */ + if(sourceProjectSlug == null) { + setSourceProjectSlug(targetProjectSlug); + } + } + + public void setSourceProjectSlug(String sourceProjectSlug) { + if(!StringUtils.equals(this.sourceProjectSlug, sourceProjectSlug)) { + this.sourceProjectSlug = sourceProjectSlug; + refreshSourceProject(); + this.sourceVersionSlug = null; + if (!getSourceProject().getProjectIterations().isEmpty()) { + this.sourceVersionSlug = + getSourceProject().getProjectIterations().get(0).getSlug(); + } + } + } + + private void refreshSourceProject() { + sourceProject = null; + } + + public HProjectIteration getTargetVersion() { + if (targetVersion == null && StringUtils.isNotEmpty(targetProjectSlug) + && StringUtils.isNotEmpty(targetVersionSlug)) { + targetVersion = + projectIterationDAO.getBySlug(targetProjectSlug, + targetVersionSlug); + } + return targetVersion; + } + + public HProject getSourceProject() { + if(sourceProject == null && StringUtils.isNotEmpty(sourceProjectSlug)) { + sourceProject = projectDAO.getBySlug(sourceProjectSlug); + } + return sourceProject; + } + + public List getSourceVersions() { + List versions = + getSourceProject().getProjectIterations(); + + if(versions.isEmpty()) { + return Collections.emptyList(); + } + + List results = Lists.newArrayList(); + //remove obsolete version and target version if both are the same project + for (HProjectIteration version : versions) { + if (version.getStatus().equals(EntityStatus.OBSOLETE)) { + continue; + } + if(StringUtils.equals(sourceProjectSlug, targetProjectSlug) && + version.getSlug().equals(targetVersionSlug)) { + continue; + } + results.add(version); + } + return results; + } + + /** + * Only display user maintained project to merge translation from in this + * UI. TODO: implement filterable drop down and allow users to select any + * available project. + * + */ + public List getProjects() { + return projectDAO.getProjectsForMaintainer( + authenticatedAccount.getPerson(), null, 0, Integer.MAX_VALUE); + } + + public void startMergeTranslations() { + if (StringUtils.isEmpty(sourceProjectSlug) + || StringUtils.isEmpty(sourceVersionSlug) + || StringUtils.isEmpty(targetProjectSlug) + || StringUtils.isEmpty(targetVersionSlug)) { + FacesMessages.instance().add(StatusMessage.Severity.ERROR, + msgs.get("jsf.iteration.mergeTrans.noSourceAndTarget")); + return; + } + if (isCopyActionsRunning()) { + FacesMessages.instance().add(StatusMessage.Severity.WARN, + msgs.get("jsf.iteration.mergeTrans.hasCopyActionRunning")); + return; + } + mergeTranslationsManager.start(sourceProjectSlug, + sourceVersionSlug, targetProjectSlug, targetVersionSlug, + !keepExistingTranslation); + } + + // Check if copy-trans, copy version or merge-trans is running for the + // target version + public boolean isCopyActionsRunning() { + return mergeTranslationsManager.isRunning( + targetProjectSlug, targetVersionSlug) + || copyVersionManager.isCopyVersionRunning(targetProjectSlug, + targetVersionSlug) || + copyTransManager.isCopyTransRunning(getTargetVersion()); + } + + @Override + public boolean isInProgress() { + return mergeTranslationsManager.isRunning( + targetProjectSlug, targetVersionSlug); + } + + @Override + public String getProgressMessage() { + MergeTranslationsTaskHandle handle = getHandle(); + if(handle != null) { + return msgs.format("jsf.iteration.mergeTrans.progress.message", + handle.getCurrentProgress(), handle.getTotalTranslations()); + } + return ""; + } + + @Override + public void onComplete() { + FacesMessages.instance().add(StatusMessage.Severity.INFO, + msgs.format("jsf.iteration.mergeTrans.completed.message", + sourceProjectSlug, sourceVersionSlug, + targetProjectSlug, targetVersionSlug)); + } + + public void cancel() { + mergeTranslationsManager.cancel(targetProjectSlug, + targetVersionSlug); + FacesMessages.instance().add( + FacesMessage.SEVERITY_INFO, + msgs.format("jsf.iteration.mergeTrans.cancel.message", + sourceProjectSlug, sourceVersionSlug, + targetProjectSlug, targetVersionSlug)); + } + + @Override + protected MergeTranslationsTaskHandle getHandle() { + return mergeTranslationsManager.getProcessHandle( + targetProjectSlug, targetVersionSlug); + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java b/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java new file mode 100644 index 0000000000..6e2e516be6 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java @@ -0,0 +1,114 @@ +package org.zanata.action; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.security.Identity; +import org.zanata.async.AsyncTaskHandleManager; +import org.zanata.async.handle.MergeTranslationsTaskHandle; +import org.zanata.service.MergeTranslationsService; + +/** + * Manages tasks to copy translations from one existing version to another. + * + * @author Alex Eng aeng@redhat.com + */ +@AutoCreate +@Name("mergeTranslationsManager") +@Scope(ScopeType.STATELESS) +@Slf4j +public class MergeTranslationsManager implements Serializable { + @In + private AsyncTaskHandleManager asyncTaskHandleManager; + + @In + private MergeTranslationsService mergeTranslationsServiceImpl; + + @In + private Identity identity; + + /** + * Merge translations from an existing version to another. + * + * @param sourceProjectSlug - source project identifier + * @param sourceVersionSlug - source version identifier + * @param targetProjectSlug - target project identifier + * @param targetVersionSlug - target version identifier + * @param useNewerTranslation - to override translated/approved string + * in target with newer entry in source + */ + public void start(String sourceProjectSlug, String sourceVersionSlug, + String targetProjectSlug, String targetVersionSlug, + boolean useNewerTranslation) { + + Key key = + Key.getKey(targetProjectSlug, + targetVersionSlug); + + MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle(); + asyncTaskHandleManager.registerTaskHandle(handle, key); + mergeTranslationsServiceImpl.startMergeTranslations(sourceProjectSlug, + sourceVersionSlug, targetProjectSlug, targetVersionSlug, + useNewerTranslation, handle); + } + + /** + * Cancel running merge translations task + * + * @param projectSlug - target project identifier + * @param versionSlug - target version identifier + */ + public void cancel(String projectSlug, String versionSlug) { + if (isRunning(projectSlug, versionSlug)) { + MergeTranslationsTaskHandle handle = + getProcessHandle(projectSlug, versionSlug); + handle.cancel(true); + handle.setCancelledTime(System.currentTimeMillis()); + handle.setCancelledBy(identity.getCredentials().getUsername()); + + log.info("Merge translations cancelled- {}:{}", projectSlug, + versionSlug); + } + } + + public MergeTranslationsTaskHandle getProcessHandle( + String projectSlug, String versionSlug) { + return (MergeTranslationsTaskHandle) asyncTaskHandleManager + .getHandleByKey( + Key.getKey(projectSlug, versionSlug)); + } + + public boolean isRunning(String projectSlug, String versionSlug) { + MergeTranslationsTaskHandle handle = + getProcessHandle(projectSlug, versionSlug); + return handle != null && !handle.isDone(); + } + + /** + * Key used for copy version task + * + */ + @EqualsAndHashCode + @Getter + @AllArgsConstructor + public static final class Key implements Serializable { + // target project identifier + private final String projectSlug; + // target version identifier + private final String versionSlug; + + public static Key getKey(String projectSlug, + String versionSlug) { + return new Key(projectSlug, versionSlug); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/ProfileHome.java b/zanata-war/src/main/java/org/zanata/action/ProfileHome.java new file mode 100644 index 0000000000..4a291a3d45 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/ProfileHome.java @@ -0,0 +1,147 @@ +/* + * Copyright 2010, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.action; + +import java.io.Serializable; +import java.util.Set; + +import javax.annotation.Nullable; + +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang.StringUtils; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.faces.FacesMessages; +import org.jboss.seam.international.StatusMessage; +import org.jboss.seam.security.management.JpaIdentityStore; +import org.zanata.dao.AccountDAO; +import org.zanata.dao.PersonDAO; +import org.zanata.i18n.Messages; +import org.zanata.model.HAccount; +import org.zanata.model.HLocale; +import org.zanata.model.HPerson; +import org.zanata.security.ZanataIdentity; +import org.zanata.service.GravatarService; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.collect.Collections2; + +import static org.apache.commons.lang.StringUtils.abbreviate; + +/** + * User profile page backing bean. + * + * @see ProfileAction for edit user profile form page + * @see NewProfileAction for new user profile form page + * + */ +@Name("profileHome") +@Scope(ScopeType.PAGE) +@Slf4j +public class ProfileHome implements Serializable { + private static final long serialVersionUID = 1L; + private String username; + + @Getter + private String name; + + @Getter + private String userImageUrl; + + @Getter + private String userLanguageTeams; + + @In + ZanataIdentity identity; + @In(required = false, value = JpaIdentityStore.AUTHENTICATED_USER) + HAccount authenticatedAccount; + @In + PersonDAO personDAO; + @In + AccountDAO accountDAO; + @In + private GravatarService gravatarServiceImpl; + @In + Messages msgs; + + private void init() { + HAccount account; + FacesMessages facesMessages = FacesMessages.instance(); + account = accountDAO.getByUsername(username); + if (account == null) { + facesMessages.clear(); + facesMessages.add(StatusMessage.Severity.ERROR, + msgs.format("jsf.UsernameNotAvailable", abbreviate(username, + 24))); + account = useAuthenticatedAccount(); + if (account == null) { + // user not logged in and username not found + return; + } + } + HPerson person = + personDAO.findById(account.getPerson().getId()); + Set languageMemberships = person.getLanguageMemberships(); + name = person.getName(); + userImageUrl = gravatarServiceImpl + .getUserImageUrl(GravatarService.USER_IMAGE_SIZE, + person.getEmail()); + userLanguageTeams = Joiner.on(", ").skipNulls().join( + Collections2.transform(languageMemberships, + new Function() { + @Nullable + @Override + public Object apply(@NonNull HLocale locale) { + return locale.retrieveDisplayName(); + } + })); + } + + private HAccount useAuthenticatedAccount() { + if (identity.isLoggedIn()) { + HAccount account; + username = authenticatedAccount.getUsername(); + account = authenticatedAccount; + return account; + } + return null; + } + + public String getUsername() { + if (Strings.isNullOrEmpty(username) && identity.isLoggedIn()) { + username = authenticatedAccount.getUsername(); + init(); + } + return username; + } + + public void setUsername(String username) { + this.username = username; + init(); + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectSearch.java b/zanata-war/src/main/java/org/zanata/action/ProjectSearch.java index 56abea8067..346ffc6497 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectSearch.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectSearch.java @@ -17,7 +17,9 @@ import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.zanata.dao.AccountDAO; import org.zanata.dao.ProjectDAO; +import org.zanata.model.HAccount; import org.zanata.model.HProject; import org.zanata.security.ZanataIdentity; @@ -25,6 +27,9 @@ import org.zanata.ui.AbstractAutocomplete; import org.zanata.util.ServiceLocator; +/** + * This will search both projects and people. + */ @Name("projectSearch") @Scope(ScopeType.CONVERSATION) @AutoCreate @@ -58,9 +63,15 @@ public class SearchResult { @Getter private HProject project; + @Getter + private HAccount account; + public boolean isProjectNull() { return project == null; } + public boolean isUserNull() { + return account == null; + } } private class ProjectAutocomplete extends @@ -69,6 +80,9 @@ private class ProjectAutocomplete extends private ProjectDAO projectDAO = ServiceLocator.instance().getInstance(ProjectDAO.class); + private AccountDAO accountDAO = ServiceLocator.instance().getInstance( + AccountDAO.class); + /** * Return results on search */ @@ -79,22 +93,28 @@ public List suggest() { return result; } try { + String searchQuery = getQuery().trim(); List searchResult = projectDAO.searchProjects( - getQuery().trim(), + searchQuery, INITIAL_RESULT_COUNT, 0, ZanataIdentity.instance().hasPermission( "HProject", "view-obsolete")); for (HProject project : searchResult) { - result.add(new SearchResult(project)); + result.add(new SearchResult(project, null)); + } + List hAccounts = accountDAO.searchQuery(searchQuery); + for (HAccount hAccount : hAccounts) { + result.add(new SearchResult(null, hAccount)); } result.add(new SearchResult()); return result; } catch (ParseException pe) { return result; } + } /** diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index b6b5b66d8f..a8488eeb15 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -42,6 +42,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.security.Restrict; +import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.util.Hex; import org.zanata.async.handle.CopyVersionTaskHandle; import org.zanata.common.DocumentType; @@ -77,8 +78,8 @@ import org.zanata.service.VersionStateCache; import org.zanata.ui.AbstractListFilter; import org.zanata.ui.AbstractSortAction; +import org.zanata.ui.CopyAction; import org.zanata.ui.InMemoryListFilter; -import org.zanata.ui.ProgressBar; import org.zanata.ui.model.statistic.WordStatistic; import org.zanata.util.DateUtil; import org.zanata.util.ServiceLocator; @@ -107,6 +108,12 @@ public class VersionHomeAction extends AbstractSortAction implements @In private CopyVersionManager copyVersionManager; + @In + private MergeTranslationsManager mergeTranslationsManager; + + @In + private CopyTransManager copyTransManager; + @In private ProjectIterationDAO projectIterationDAO; @@ -297,11 +304,6 @@ public void setProjectSlug(String projectSlug) { copyVersionHandler.setProjectSlug(projectSlug); } - public void onCopyVersionComplete() { - conversationScopeMessages.setMessage(FacesMessage.SEVERITY_INFO, - msgs.format("jsf.copyVersion.Completed", versionSlug)); - } - public void cancelCopyVersion() { copyVersionManager.cancelCopyVersion(projectSlug, versionSlug); conversationScopeMessages.setMessage(FacesMessage.SEVERITY_INFO, @@ -309,7 +311,7 @@ public void cancelCopyVersion() { } @NoArgsConstructor - public static class CopyVersionHandler implements ProgressBar { + public static class CopyVersionHandler extends CopyAction { @Setter private String projectSlug; @@ -324,16 +326,15 @@ public boolean isInProgress() { } @Override - public String getCompletedPercentage() { - CopyVersionTaskHandle handle = getHandle(); - if (handle != null) { - double completedPercent = - (double) handle.getCurrentProgress() / (double) handle - .getMaxProgress() * 100; - return PERCENT_FORMAT.format(completedPercent); - } else { - return "0"; - } + public String getProgressMessage() { + return getMessages().format("jsf.copyVersion.processedDocuments", + getProcessedDocuments(), getTotalDocuments()); + } + + @Override + public void onComplete() { + FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, + getMessages().format("jsf.copyVersion.Completed", versionSlug)); } public int getProcessedDocuments() { @@ -352,12 +353,16 @@ public int getTotalDocuments() { return 0; } + private Messages getMessages() { + return ServiceLocator.instance().getInstance(Messages.class); + } + private CopyVersionManager getCopyVersionManager() { return ServiceLocator.instance().getInstance( CopyVersionManager.class); } - private CopyVersionTaskHandle getHandle() { + protected CopyVersionTaskHandle getHandle() { CopyVersionManager copyVersionManager = ServiceLocator .instance().getInstance(CopyVersionManager.class); @@ -910,6 +915,16 @@ public String decodeDocId(String docId) { return UrlUtil.decodeString(docId); } + // Check if copy-trans, copy version or merge-trans is running for given + // version + public boolean isCopyActionsRunning() { + return mergeTranslationsManager.isRunning( + projectSlug, versionSlug) + || copyVersionManager.isCopyVersionRunning(projectSlug, + versionSlug) || + copyTransManager.isCopyTransRunning(getVersion()); + } + public void uploadTranslationFile(HLocale hLocale) { identity.checkPermission("modify-translation", hLocale, getVersion() .getProject()); diff --git a/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java index 1d014af54e..07b6a932b8 100644 --- a/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java +++ b/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java @@ -70,7 +70,13 @@ public class OkapiFilterAdapter implements FileFormatAdapter { * Determines how TextFlow ids are assigned for Okapi TextUnits */ public enum IdSource { - /** use ID of TextUnit as is (only unique if no sub-documents) */ + /** + * Use ID of TextUnit as is (only unique if no sub-documents). + * + * Note: if the underlying filter gives inconsistent ids (e.g. + * positional identifiers when paragraphs are inserted), + * Zanata's merge algorithm is too weak to handle this well. + */ textUnitId, /** * use 'name' attribute of TextUnit, if any. Not guaranteed to be @@ -83,8 +89,12 @@ public enum IdSource { */ contentHash, /** - * concatenate name of sub-document and ID of TextUnit. Should be unique + * Concatenate name of sub-document and ID of TextUnit. Should be unique * (assuming sub-document names are). + * + * Note: if the underlying filter gives inconsistent ids (e.g. + * positional identifiers when paragraphs are inserted), + * Zanata's merge algorithm is too weak to handle this well. */ subDocNameAndTextUnitId }; @@ -249,7 +259,7 @@ private boolean shouldAdd(String id, HasContents hc, if (addedResources.containsKey(id)) { if (!hc.getContents().equals(addedResources.get(id).getContents())) { throw new FileFormatAdapterException( - "Same id but different contents for text text flow, " + "Same id but different contents for text flow, " + "not suitable for eliding."); } return false; diff --git a/zanata-war/src/main/java/org/zanata/adapter/OpenOfficeAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/OpenOfficeAdapter.java index 1f2102d4db..cace06da11 100644 --- a/zanata-war/src/main/java/org/zanata/adapter/OpenOfficeAdapter.java +++ b/zanata-war/src/main/java/org/zanata/adapter/OpenOfficeAdapter.java @@ -31,7 +31,7 @@ */ public class OpenOfficeAdapter extends OkapiFilterAdapter { public OpenOfficeAdapter() { - super(prepareFilter(), IdSource.subDocNameAndTextUnitId, true); + super(prepareFilter(), IdSource.contentHash, true); } private static OpenOfficeFilter prepareFilter() { diff --git a/zanata-war/src/main/java/org/zanata/adapter/PlainTextAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/PlainTextAdapter.java index 56c01ef58d..a6efc98b96 100644 --- a/zanata-war/src/main/java/org/zanata/adapter/PlainTextAdapter.java +++ b/zanata-war/src/main/java/org/zanata/adapter/PlainTextAdapter.java @@ -30,6 +30,6 @@ */ public class PlainTextAdapter extends OkapiFilterAdapter { public PlainTextAdapter() { - super(new PlainTextFilter(), IdSource.textUnitId); + super(new PlainTextFilter(), IdSource.contentHash); } } diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java index bd243c0eca..813e621ffb 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java @@ -101,7 +101,7 @@ public void execute() { taskFuture.set(returnValue); } catch (Throwable t) { taskFuture.setException(t); - log.warn( + log.error( "Exception when executing an asynchronous task.", t); } } diff --git a/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java b/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java index 5b1b00cb13..16eb3c97b1 100644 --- a/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java +++ b/zanata-war/src/main/java/org/zanata/async/handle/CopyVersionTaskHandle.java @@ -41,12 +41,6 @@ public class CopyVersionTaskHandle extends AsyncTaskHandle { private long cancelledTime; @Getter @Setter private String triggeredBy; - @Getter - private boolean prepared; - - public void setPrepared() { - this.prepared = true; - } /** * Increments the processed document count by 1 diff --git a/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java b/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java new file mode 100644 index 0000000000..9c09d75d8c --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.async.handle; + +import lombok.Getter; +import lombok.Setter; + +import org.zanata.async.AsyncTaskHandle; + +/** + * Asynchronous task handle for the merge translations process. + * + * @author Alex Eng aeng@redhat.com + */ +public class MergeTranslationsTaskHandle extends AsyncTaskHandle { + @Getter @Setter + private int totalTranslations; + @Getter @Setter + private String cancelledBy; + @Getter @Setter + private long cancelledTime; + @Getter @Setter + private String triggeredBy; +} diff --git a/zanata-war/src/main/java/org/zanata/cache/CacheContainerProducer.java b/zanata-war/src/main/java/org/zanata/cache/CacheContainerProducer.java new file mode 100644 index 0000000000..a7b8028e7c --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/cache/CacheContainerProducer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.cache; + +import lombok.extern.slf4j.Slf4j; +import org.infinispan.manager.CacheContainer; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.Startup; +import org.jboss.seam.annotations.Unwrap; +import org.zanata.util.ServiceLocator; + +import javax.naming.NamingException; + +/** + * Produces a cache container for injection. + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("cacheContainer") +@Scope(ScopeType.APPLICATION) +@AutoCreate +@Startup +@Slf4j +public class CacheContainerProducer { + + private static final String CACHE_CONTAINER_NAME = + "java:jboss/infinispan/container/zanata"; + + private CacheContainer container; + + @Create + public void initialize() { + try { + container = + ServiceLocator.instance().getJndiComponent( + CACHE_CONTAINER_NAME, + CacheContainer.class); + } catch (NamingException e) { + String msg = "A cache container with name " + + "'" + CACHE_CONTAINER_NAME + "' " + + "has not been configured."; + log.warn(msg); + throw new RuntimeException(msg, e); + } + } + + @Unwrap + public CacheContainer getCacheContainer() { + return container; + } +} diff --git a/zanata-war/src/main/java/org/zanata/cache/EhcacheLoader.java b/zanata-war/src/main/java/org/zanata/cache/EhcacheLoader.java deleted file mode 100644 index 3721516653..0000000000 --- a/zanata-war/src/main/java/org/zanata/cache/EhcacheLoader.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This 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 2.1 of - * the License, or (at your option) any later version. - * - * This software 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 this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.zanata.cache; - -import java.util.Collection; -import java.util.Map; - -import net.sf.ehcache.CacheException; -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.Status; - -import com.google.common.cache.CacheLoader; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - */ - -@SuppressWarnings("rawtypes") -public class EhcacheLoader implements net.sf.ehcache.loader.CacheLoader { - - private CacheLoader loader; - - public EhcacheLoader(CacheLoader loader) { - this.loader = loader; - } - - @Override - public V load(Object key) throws CacheException { - try { - return loader.load((K) key); - } catch (Exception e) { - throw new CacheException(e); - } - } - - @Override - public Map loadAll(Collection keys) { - try { - return loader.loadAll(keys); - } catch (Exception e) { - throw new CacheException(e); - } - } - - @Override - public Object load(Object key, Object argument) { - return load(key); - } - - @Override - public Map loadAll(Collection keys, Object argument) { - return loadAll(keys); - } - - @Override - public String getName() { - return getClass().getName() + ":" + loader.getClass().getName(); - } - - @Override - public net.sf.ehcache.loader.CacheLoader clone(Ehcache cache) - throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - @Override - public void init() { - } - - @Override - public void dispose() throws CacheException { - } - - @Override - public Status getStatus() { - return Status.STATUS_ALIVE; - } - -} diff --git a/zanata-war/src/main/java/org/zanata/cache/EhcacheWrapper.java b/zanata-war/src/main/java/org/zanata/cache/EhcacheWrapper.java deleted file mode 100644 index 2b455ff7e5..0000000000 --- a/zanata-war/src/main/java/org/zanata/cache/EhcacheWrapper.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.zanata.cache; - -import com.google.common.cache.CacheLoader; - -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.Element; - -public class EhcacheWrapper implements CacheWrapper { - private final String cacheName; - private final CacheManager cacheManager; - - public EhcacheWrapper(final String cacheName, - final CacheManager cacheManager) { - this.cacheName = cacheName; - this.cacheManager = cacheManager; - } - - @Override - public void put(final K key, final V value) { - getCache().put(new Element(key, value)); - } - - @Override - public V get(final K key) { - Element element = getCache().get(key); - if (element != null) { - return (V) element.getValue(); - } - return null; - } - - @Override - public V getWithLoader(final K key) { - Element element = getCache().getWithLoader(key, null, null); - if (element != null) { - return (V) element.getValue(); - } - return null; - } - - public Ehcache getCache() { - return cacheManager.getEhcache(cacheName); - } - - public static EhcacheWrapper create(final String cacheName, - final CacheManager cacheManager) { - cacheManager.addCacheIfAbsent(cacheName); - return new EhcacheWrapper(cacheName, cacheManager); - } - - public static EhcacheWrapper create(final String cacheName, - final CacheManager cacheManager, CacheLoader loader) { - EhcacheWrapper cacheWrapper = create(cacheName, cacheManager); - cacheWrapper.getCache().registerCacheLoader( - new EhcacheLoader(loader)); - return cacheWrapper; - } - - @Override - public boolean remove(K key) { - return getCache().remove(key); - } - -} diff --git a/zanata-war/src/main/java/org/zanata/cache/InfinispanCacheWrapper.java b/zanata-war/src/main/java/org/zanata/cache/InfinispanCacheWrapper.java new file mode 100644 index 0000000000..8e78950980 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/cache/InfinispanCacheWrapper.java @@ -0,0 +1,97 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.cache; + +import com.google.common.cache.CacheLoader; +import org.infinispan.Cache; +import org.infinispan.manager.CacheContainer; + +/** + * @author Carlos Munoz camunoz@redhat.com + */ +public class InfinispanCacheWrapper implements CacheWrapper { + + private final String cacheName; + private final CacheContainer cacheContainer; + private CacheLoader cacheLoader; + + public InfinispanCacheWrapper(String cacheName, + CacheContainer cacheContainer) { + this.cacheName = cacheName; + this.cacheContainer = cacheContainer; + } + + public InfinispanCacheWrapper(String cacheName, + CacheContainer cacheContainer, + CacheLoader cacheLoader) { + this(cacheName, cacheContainer); + this.cacheLoader = cacheLoader; + } + + @Override + public void put(K key, V value) { + getCache().put(key, value); + } + + @Override + public V get(K key) { + return getCache().get(key); + } + + @Override + public synchronized V getWithLoader(K key) { + // NB: Need to manually implement the cache loader feature + V cachedValue = getCache().get(key); + if(cachedValue == null && cacheLoader != null) { + try { + cachedValue = cacheLoader.load(key); + } catch (Exception e) { + throw new RuntimeException( + "Unable to load entry with cache loader ", e); + } + getCache().put(key, cachedValue); + } + return cachedValue; + } + + @Override + public boolean remove(K key) { + return getCache().remove(key) != null; + } + + public Cache getCache() { + return cacheContainer.getCache(cacheName); + } + + public static InfinispanCacheWrapper create( + final String cacheName, + final CacheContainer cacheManager) { + cacheManager.getCache(cacheName); + return new InfinispanCacheWrapper(cacheName, cacheManager); + } + + public static InfinispanCacheWrapper create( + final String cacheName, + final CacheContainer cacheManager, CacheLoader loader) { + cacheManager.getCache(cacheName); + return new InfinispanCacheWrapper(cacheName, cacheManager, loader); + } +} diff --git a/zanata-war/src/main/java/org/zanata/dao/AccountDAO.java b/zanata-war/src/main/java/org/zanata/dao/AccountDAO.java index 5f261a90f2..e928c4e02c 100644 --- a/zanata-war/src/main/java/org/zanata/dao/AccountDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/AccountDAO.java @@ -125,10 +125,10 @@ public HAccount create(String username, String password, boolean enabled) { // TODO: use hibernate search public List searchQuery(String searchQuery) { - String userName = searchQuery + "%"; + String userName = "%" + searchQuery + "%"; Query query = getSession().createQuery( - "from HAccount as a where a.username like :username"); + "from HAccount as a where lower(a.username) like lower(:username)"); query.setParameter("username", userName); query.setComment("AccountDAO.searchQuery/username"); return query.list(); diff --git a/zanata-war/src/main/java/org/zanata/dao/NativeQuery.java b/zanata-war/src/main/java/org/zanata/dao/NativeQuery.java index 2b4f692a64..b733617d80 100644 --- a/zanata-war/src/main/java/org/zanata/dao/NativeQuery.java +++ b/zanata-war/src/main/java/org/zanata/dao/NativeQuery.java @@ -11,4 +11,13 @@ @Target({ElementType.METHOD, ElementType.LOCAL_VARIABLE, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.SOURCE) public @interface NativeQuery { + /** + * Reason why it has to be native query. + */ + String value() default ""; + + /** + * If the query is specific to certain database due to built-in function etc. + */ + String specificTo() default ""; } diff --git a/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java b/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java index 40c5968313..c207be1284 100644 --- a/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java @@ -31,6 +31,8 @@ import org.zanata.model.HProject; import org.zanata.model.HProjectIteration; +import com.google.common.collect.Lists; + @Name("projectDAO") @AutoCreate @Scope(ScopeType.STATELESS) @@ -71,11 +73,11 @@ public List getProjectMaintainerBySlug(String slug) { @SuppressWarnings("unchecked") public List getOffsetListOrderByName(int offset, int count, - boolean filterActive, boolean filterReadOnly, - boolean filterObsolete) { + boolean filterOutActive, boolean filterOutReadOnly, + boolean filterOutObsolete) { String condition = - constructFilterCondition(filterActive, filterReadOnly, - filterObsolete); + constructFilterCondition(filterOutActive, filterOutReadOnly, + filterOutObsolete); Query q = getSession().createQuery( "from HProject p " + condition @@ -85,12 +87,12 @@ public List getProjectMaintainerBySlug(String slug) { return q.list(); } - public int getFilterProjectSize(boolean filterActive, - boolean filterReadOnly, boolean filterObsolete) { + public int getFilterProjectSize(boolean filterOutActive, + boolean filterOutReadOnly, boolean filterOutObsolete) { String query = "select count(*) from HProject p " - + constructFilterCondition(filterActive, - filterReadOnly, filterObsolete); + + constructFilterCondition(filterOutActive, + filterOutReadOnly, filterOutObsolete); Query q = getSession().createQuery(query.toString()); q.setCacheable(true).setComment("ProjectDAO.getFilterProjectSize"); Long totalCount = (Long) q.uniqueResult(); @@ -100,21 +102,21 @@ public int getFilterProjectSize(boolean filterActive, return totalCount.intValue(); } - private String constructFilterCondition(boolean filterActive, - boolean filterReadOnly, boolean filterObsolete) { + private String constructFilterCondition(boolean filterOutActive, + boolean filterOutReadOnly, boolean filterOutObsolete) { StringBuilder condition = new StringBuilder(); - if (filterActive || filterReadOnly || filterObsolete) { + if (filterOutActive || filterOutReadOnly || filterOutObsolete) { condition.append("where "); } - if (filterActive) { + if (filterOutActive) { // TODO bind this as a parameter condition.append("p.status <> '" + EntityStatus.ACTIVE.getInitial() + "' "); } - if (filterReadOnly) { - if (filterActive) { + if (filterOutReadOnly) { + if (filterOutActive) { condition.append("and "); } @@ -123,8 +125,8 @@ private String constructFilterCondition(boolean filterActive, + EntityStatus.READONLY.getInitial() + "' "); } - if (filterObsolete) { - if (filterActive || filterReadOnly) { + if (filterOutObsolete) { + if (filterOutActive || filterOutReadOnly) { condition.append("and "); } diff --git a/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java b/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java index 16e3a1bba7..5a1228073b 100644 --- a/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java @@ -21,6 +21,7 @@ package org.zanata.dao; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -33,6 +34,7 @@ import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.zanata.common.ContentState; import org.zanata.common.LocaleId; import org.zanata.model.HDocument; import org.zanata.model.HLocale; @@ -300,4 +302,81 @@ public Map getByDocumentAndResIds(HDocument document, return builder.build(); } + /** + * Return text flows that have matching document id and content, resId + * between the given source and target version + * + * @param sourceVersionId + * @param targetVersionId + * @param offset + * @param maxResults + */ + public List getSourceByMatchedContext(Long sourceVersionId, + Long targetVersionId, int offset, int maxResults) { + String queryString = generateSourceByMatchedContext(false); + + Query query = getSession() + .createQuery(queryString) + .setParameter("sourceVersionId", sourceVersionId) + .setParameter("targetVersionId" , targetVersionId) + .setMaxResults(maxResults) + .setFirstResult(offset) + .setCacheable(true) + .setComment("TextFlowDAO.getTranslationsByMatchedContext"); + + List results = Lists.newArrayList(); + for(Object result: query.list()) { + Object[] castResults = (Object[]) result; + results.add(new HTextFlow[] { (HTextFlow) castResults[0], + (HTextFlow) castResults[1] }); + } + return results; + } + + public int getSourceByMatchedContextCount(Long sourceVersionId, + Long targetVersionId) { + String queryString = generateSourceByMatchedContext(true); + Query query = getSession() + .createQuery(queryString) + .setParameter("sourceVersionId", sourceVersionId) + .setParameter("targetVersionId" , targetVersionId) + .setCacheable(true) + .setComment("TextFlowDAO.getTranslationsByMatchedContextCount"); + Long count = (Long) query.uniqueResult(); + return count == null ? 0 : count.intValue(); + } + + /** + * Generate query string for text flows that have matching document id and + * content between the given source and target version + * + * @param getRecordCount - if true, generate select count(*) hql. + * + */ + private String generateSourceByMatchedContext(boolean getRecordCount) { + StringBuilder queryBuilder = new StringBuilder(); + + if(getRecordCount) { + queryBuilder.append( + "select count(fromTF.id) from HTextFlow fromTF, HTextFlow toTF "); + } else { + queryBuilder.append( + "select fromTF, toTF from HTextFlow fromTF, HTextFlow toTF "); + } + queryBuilder + .append("where fromTF.document.projectIteration.id = :sourceVersionId ") + .append("and toTF.document.projectIteration.id = :targetVersionId ") + .append("and fromTF.obsolete = false ") + .append("and fromTF.document.obsolete = false ") + .append("and toTF.obsolete = false ") + .append("and toTF.document.obsolete = false ") + .append("and fromTF <> toTF ") + .append("and fromTF.contentHash = toTF.contentHash ") + .append("and fromTF.resId = toTF.resId ") + .append("and fromTF.document.docId = toTF.document.docId"); + + return queryBuilder.toString(); + + } + } diff --git a/zanata-war/src/main/java/org/zanata/dao/TextFlowTargetHistoryDAO.java b/zanata-war/src/main/java/org/zanata/dao/TextFlowTargetHistoryDAO.java index 723b23563c..ed390c487e 100644 --- a/zanata-war/src/main/java/org/zanata/dao/TextFlowTargetHistoryDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/TextFlowTargetHistoryDAO.java @@ -20,18 +20,28 @@ */ package org.zanata.dao; +import java.math.BigInteger; import java.util.Date; import java.util.List; +import java.util.concurrent.TimeUnit; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.transform.ResultTransformer; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.zanata.common.ContentState; +import org.zanata.model.HPerson; import org.zanata.model.HTextFlowTarget; import org.zanata.model.HTextFlowTargetHistory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; + @Name("textFlowTargetHistoryDAO") @AutoCreate @Scope(ScopeType.STATELESS) @@ -159,4 +169,113 @@ public boolean findConflictInHistory(HTextFlowTarget target, return count != 0; } + /** + * Query to get total wordCount of a person(translated_by_id or + * reviewed_by_id) from HTextFlowTarget union HTextFlowTargetHistory tables + * within given date range group by lastChangeDate (date portion only), + * project version, locale and state. + * + * HTextFlowTargetHistory: gets all records translated from user in any + * version, any locale and dateRange. + * + * HTextFlowTarget: gets all records translated from user in any version, + * any locale and dateRange. + * + * @param user + * a HPerson person + * @param fromDate + * date from + * @param toDate + * date to + * + * @param userZoneOpt + * optional DateTimeZone of the user. Only present if it's + * different from system time zone + * @param systemZone + * current system time zone + * @param resultTransformer + * result transformer to transform query results + * @return a list of transformed object + */ + @NativeQuery(value = "need to use union", specificTo = "mysql due to usage of date() and convert_tz() functions.") + public List getUserTranslationMatrix( + HPerson user, DateTime fromDate, DateTime toDate, + Optional userZoneOpt, DateTimeZone systemZone, + ResultTransformer resultTransformer) { + // @formatter:off + String queryHistory = "select history.id, iter.id as iteration, tft.locale as locale, tf.wordCount as wordCount, history.state as state, history.lastChanged as lastChanged " + + " from HTextFlowTargetHistory history " + + " join HTextFlowTarget tft on tft.id = history.target_id " + + " join HTextFlow tf on tf.id = tft.tf_id " + + " join HDocument doc on doc.id = tf.document_id " + + " join HProjectIteration iter on iter.id = doc.project_iteration_id " + + " where history.lastChanged >= :fromDate and history.lastChanged <= :toDate " + + " and history.last_modified_by_id = :user and (history.translated_by_id is not null or history.reviewed_by_id is not null)" + + " and history.state <> :untranslated and history.state <> :rejected"; + + String queryTarget = "select tft.id, iter.id as iteration, tft.locale as locale, tf.wordCount as wordCount, tft.state as state, tft.lastChanged as lastChanged " + + " from HTextFlowTarget tft " + + " join HTextFlow tf on tf.id = tft.tf_id " + + " join HDocument doc on doc.id = tf.document_id " + + " join HProjectIteration iter on iter.id = doc.project_iteration_id " + + " where tft.lastChanged >= :fromDate and tft.lastChanged <= :toDate " + + " and tft.last_modified_by_id = :user and (tft.translated_by_id is not null or tft.reviewed_by_id is not null)" + + " and tft.state <> :untranslated and tft.state <> :rejected"; + + String convertedLastChanged = convertTimeZoneFunction("lastChanged", + userZoneOpt, systemZone); + // @formatter:on + String dateOfLastChanged = stripTimeFromDateTimeFunction(convertedLastChanged); + String queryString = + "select " + dateOfLastChanged + ", iteration, locale, state, sum(wordCount)" + + " from (" + + " (" + queryHistory + ") union (" + queryTarget + ")" + + " ) as all_translation" + + " group by " + dateOfLastChanged + ", iteration, locale, state " + + " order by lastChanged, iteration, locale, state"; + Query query = getSession().createSQLQuery(queryString) + .setParameter("user", user.getId()) + .setInteger("untranslated", ContentState.New.ordinal()) + .setInteger("rejected", ContentState.Rejected.ordinal()) + .setTimestamp("fromDate", fromDate.toDate()) + .setTimestamp("toDate", toDate.toDate()) + .setResultTransformer(resultTransformer); + return query.list(); + } + + @VisibleForTesting + protected String convertTimeZoneFunction(String columnName, + Optional userZoneOpt, DateTimeZone systemZone) { + if (userZoneOpt.isPresent()) { + String userOffset = getOffsetAsString(userZoneOpt.get()); + String systemOffset = getOffsetAsString(systemZone); + return String.format("CONVERT_TZ(%s, '%s', '%s')", columnName, systemOffset, userOffset); + } + // no need to convert timezone + return columnName; + } + + // This is so we can override it in test and be able to test it against h2 + @VisibleForTesting + protected String stripTimeFromDateTimeFunction(String columnName) { + return "date(" + columnName + ")"; + } + + @SuppressWarnings("unchecked") + private T loadById(Object object, Class entityClass) { + return (T) getSession().byId(entityClass).load( + ((BigInteger) object).longValue()); + } + + private static String getOffsetAsString(DateTimeZone zone) { + int standardOffset = zone.getStandardOffset(0); + String prefix = ""; + if (standardOffset < 0) { + prefix = "-"; + standardOffset = -standardOffset; + } + return String.format("%s%02d:00", prefix, + TimeUnit.MILLISECONDS.toHours(standardOffset)); + } + } diff --git a/zanata-war/src/main/java/org/zanata/email/EmailBuilder.java b/zanata-war/src/main/java/org/zanata/email/EmailBuilder.java index af4ce37eb2..c9d65f26e1 100644 --- a/zanata-war/src/main/java/org/zanata/email/EmailBuilder.java +++ b/zanata-war/src/main/java/org/zanata/email/EmailBuilder.java @@ -37,6 +37,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; +import org.zanata.i18n.MessagesFactory; import static com.googlecode.totallylazy.collections.PersistentMap.constructors.map; import static org.jboss.seam.ScopeType.EVENT; @@ -66,7 +67,7 @@ public EmailBuilder() { @In private Context emailContext; @In - private Messages msgs; + private MessagesFactory messagesFactory; private static VelocityEngine makeVelocityEngine() { VelocityEngine ve = new VelocityEngine(); @@ -149,9 +150,14 @@ MimeMessage buildMessage(MimeMessage msg, EmailStrategy strategy, InternetAddress[] toAddresses, List receivedReasons) throws MessagingException { + // TODO remember users' locales, and customise for each recipient + // msgs = messagesFactory.getMessages(account.getLocale()); + Messages msgs = messagesFactory.getDefaultLocaleMessages(); + Optional from = strategy.getFromAddress(); + String fromName = msgs.get("jsf.Zanata"); msg.setFrom(from.or(Addresses.getAddress( - emailContext.getFromAddress(), emailContext.getFromName()))); + emailContext.getFromAddress(), fromName))); Optional replyTo = strategy.getReplyToAddress(); if (replyTo.isPresent()) { msg.setReplyTo(replyTo.get()); @@ -202,17 +208,12 @@ MimeMessage buildMessage(MimeMessage msg, EmailStrategy strategy, public static class Context { @In private ApplicationConfiguration applicationConfiguration; - @In - private Messages msgs; String getServerPath() { return applicationConfiguration.getServerPath(); } String getFromAddress() { return applicationConfiguration.getFromEmailAddr(); } - String getFromName() { - return msgs.get("jsf.Zanata"); - } } } diff --git a/zanata-war/src/main/java/org/zanata/exception/InvalidDateParamException.java b/zanata-war/src/main/java/org/zanata/exception/InvalidDateParamException.java index 4c26afa84d..420687b8d8 100644 --- a/zanata-war/src/main/java/org/zanata/exception/InvalidDateParamException.java +++ b/zanata-war/src/main/java/org/zanata/exception/InvalidDateParamException.java @@ -6,6 +6,7 @@ * @author Alex Eng aeng@redhat.com */ public class InvalidDateParamException extends BadRequestException { + private static final long serialVersionUID = -3758176883692074605L; public InvalidDateParamException(String message) { super(message); diff --git a/zanata-war/src/main/java/org/zanata/i18n/Messages.java b/zanata-war/src/main/java/org/zanata/i18n/Messages.java index cecfdb7b13..1a199bfc53 100644 --- a/zanata-war/src/main/java/org/zanata/i18n/Messages.java +++ b/zanata-war/src/main/java/org/zanata/i18n/Messages.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * Copyright 2010-2015, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * @@ -58,12 +58,24 @@ @Scope(EVENT) public class Messages extends AbstractMap { + /** + * Gets the 'messages' ResourceBundle for the locale of the current + * request, if any, otherwise server's default locale. + * @see org.jboss.seam.web.Locale + */ private static ResourceBundle getResourceBundle() { + return getResourceBundle(org.jboss.seam.core.Locale.instance()); + } + + /** + * Gets the 'messages' ResourceBundle for the specified locale. + */ + private static ResourceBundle getResourceBundle(java.util.Locale locale) { // Generic ResourceBundle without built-in interpolation: ResourceBundle resourceBundle = null; try { resourceBundle = ResourceBundle.getBundle( - "messages", org.jboss.seam.core.Locale.instance()); + "messages", locale); } catch (MissingResourceException e) { resourceBundle = new ResourceBundle() { @Override @@ -83,8 +95,23 @@ public Enumeration getKeys() { private final ResourceBundle resourceBundle; + /** + * Create an instance for the locale of the current request, if any, + * otherwise the server's default locale. + */ public Messages() { - this.resourceBundle = getResourceBundle(); + this(getResourceBundle()); + } + + /** + * Create an instance for the specified locale. + */ + public Messages(java.util.Locale locale) { + this(getResourceBundle(locale)); + } + + private Messages(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; } @Observer("org.jboss.seam.localeSelected") diff --git a/zanata-war/src/main/java/org/zanata/i18n/MessagesFactory.java b/zanata-war/src/main/java/org/zanata/i18n/MessagesFactory.java new file mode 100644 index 0000000000..c4f8f65915 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/i18n/MessagesFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.i18n; + +import static org.jboss.seam.ScopeType.APPLICATION; + +import lombok.Getter; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; + +import java.util.Locale; + +/** + * Factory bean to return an instance of Messages based on a parameter. + * + * @author Sean Flanigan sflaniga@redhat.com + */ +@AutoCreate +@Name("messagesFactory") +@Scope(APPLICATION) +public class MessagesFactory { + + /** + * Returns an instance of Messages for the server's default locale. + */ + @Getter + private final Messages defaultLocaleMessages = getMessages(Locale.getDefault()); + + /** + * Returns an instance of Messages for the specified locale. + */ + public Messages getMessages(Locale locale) { + return new Messages(locale); + } +} diff --git a/zanata-war/src/main/java/org/zanata/liquibase/custom/ChangePositionalResIdToContentHash.java b/zanata-war/src/main/java/org/zanata/liquibase/custom/ChangePositionalResIdToContentHash.java new file mode 100644 index 0000000000..e50fc69581 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/liquibase/custom/ChangePositionalResIdToContentHash.java @@ -0,0 +1,215 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.liquibase.custom; + +import com.google.common.collect.ImmutableSet; +import liquibase.change.custom.CustomTaskChange; +import liquibase.database.Database; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.CustomChangeException; +import liquibase.exception.DatabaseException; +import liquibase.exception.SetupException; +import liquibase.exception.ValidationErrors; +import liquibase.logging.LogFactory; +import liquibase.logging.Logger; +import liquibase.resource.ResourceAccessor; +import org.zanata.common.DocumentType; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Set; + + +/** + * Change the resource ID (resId) for text flows in some document formats to be + * a hash of the content, rather than a position-based value. + * + * Text flows in the following document types {@link org.zanata.common.DocumentType} are + * modified: + * + * - txt {@link org.zanata.common.DocumentType#PLAIN_TEXT} + * - all libreoffice types + * + * If multiple text flows in the same document have the same content, only the first + * will be kept. + */ +public class ChangePositionalResIdToContentHash implements CustomTaskChange { + + private static final Set documentTypes = ImmutableSet.of(DocumentType.PLAIN_TEXT, + DocumentType.OPEN_DOCUMENT_DATABASE, DocumentType.OPEN_DOCUMENT_FORMULA, + DocumentType.OPEN_DOCUMENT_GRAPHICS, DocumentType.OPEN_DOCUMENT_PRESENTATION, + DocumentType.OPEN_DOCUMENT_SPREADSHEET, DocumentType.OPEN_DOCUMENT_TEXT); + + private static final String countDocumentsOfTypeSql = + "select count(*) from HDocument_RawDocument " + + "left join HRawDocument on HDocument_RawDocument.rawDocumentId=HRawDocument.id " + + "where HRawDocument.type = ?"; + + private static final String selectDocumentsOfTypeSql = + "select documentId from HDocument_RawDocument " + + "left join HRawDocument on HDocument_RawDocument.rawDocumentId=HRawDocument.id " + + "where HRawDocument.type = ?"; + + // id must be present as primary key to allow updates in the ResultSet + private static final String selectTextFlowsSql = + "select id, contentHash, pos, resId, obsolete from HTextFlow where document_id = ? " + + "order by pos"; + private static final int ID_COLUMN = 1; + private static final int CONTENT_HASH_COLUMN = 2; + private static final int POS_COLUMN = 3; + private static final int RES_ID_COLUMN = 4; + private static final int OBSOLETE_COLUMN = 5; + + private Logger log = LogFactory.getLogger(); + + private PreparedStatement countDocumentsOfType; + private PreparedStatement selectDocumentsOfType; + private PreparedStatement selectTextFlows; + + private int totalDocs = 0; + private int totalTextFlows = 0; + + @Override + public void execute(Database database) throws CustomChangeException { + + try { + final JdbcConnection conn = (JdbcConnection) database.getConnection(); + try (PreparedStatement cdot = conn.prepareStatement(countDocumentsOfTypeSql, + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + PreparedStatement sdot = selectDocumentsOfType = conn.prepareStatement(selectDocumentsOfTypeSql, + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + PreparedStatement stf = conn.prepareStatement(selectTextFlowsSql, + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { + + // binding to fields to allow cleaner parameter lists. + countDocumentsOfType = cdot; + selectDocumentsOfType = sdot; + selectTextFlows = stf; + + log.info("Changing resource id from positional to content hash for text flows in some document types."); + + for (DocumentType type : documentTypes) { + migrateResId(type); + } + } + } catch (DatabaseException | SQLException e) { + throw new CustomChangeException(e); + } + // connection is not closed in a finally clause since doing so causes liquibase to throw exceptions. + } + + /** + * Update resId for all text flows in documents of the given type. + * + * @param type type to migrate + * @throws SQLException + */ + private void migrateResId(DocumentType type) throws SQLException { + long docsOfType; + long docsProcessed = 0; + countDocumentsOfType.setString(1, type.name()); + try (ResultSet countOfType = countDocumentsOfType.executeQuery()) { + if (countOfType.next()) { + docsOfType = countOfType.getLong(1); + log.info("preparing to process " + docsOfType + " documents of type " + type.name()); + } else { + // unexpected error, but does not have to destroy everything. + throw new SQLException("Count query returned no rows."); + } + } + + selectDocumentsOfType.setString(1, type.name()); + try (ResultSet documentIds = selectDocumentsOfType.executeQuery()) { + while (documentIds.next()) { + Long documentId = documentIds.getLong(1); + int textFlowsInDocument = migrateResIdForDocument(documentId); + docsProcessed++; + log.info(" processed " + textFlowsInDocument + " text flows in document " + docsProcessed + " of " + docsOfType); + totalDocs++; + } + log.info(" finished processing " + docsProcessed + " documents of type " + type.name()); + } + } + + /** + * Update resId for all text flows in a specified document. + * + * @param docId database id for the document + */ + private int migrateResIdForDocument(Long docId) throws SQLException { + selectTextFlows.setLong(1, docId); + try (ResultSet textFlows = selectTextFlows.executeQuery()) { + + Set usedIds = new HashSet<>(); + int newPos = 0; + int processed = 0; + + while (textFlows.next()) { + long id = textFlows.getLong(ID_COLUMN); + boolean isObsolete = textFlows.getBoolean(OBSOLETE_COLUMN); + + if (!isObsolete) { + String contentHash = textFlows.getString(CONTENT_HASH_COLUMN); + + if (usedIds.contains(contentHash)) { + textFlows.updateBoolean(OBSOLETE_COLUMN, true); + textFlows.updateRow(); + } else { + textFlows.updateLong(POS_COLUMN, newPos); + textFlows.updateString(RES_ID_COLUMN, contentHash); + // Must set 'bit' fields explicitly when doing an update + // due to a bug in the mysql connector. See + // https://bugs.mysql.com/bug.php?id=75475 + textFlows.updateBoolean(OBSOLETE_COLUMN, isObsolete); + textFlows.updateRow(); + + usedIds.add(contentHash); + newPos++; + } + } + processed++; + totalTextFlows++; + } + return processed; + } + } + + @Override + public String getConfirmationMessage() { + return "Finished ChangePositionalResIdToContentHash. Updated resource ID for " + totalTextFlows + + " text flows in " + totalDocs + " documents."; + } + + @Override + public void setUp() throws SetupException { + } + + @Override + public void setFileOpener(ResourceAccessor resourceAccessor) { + } + + @Override + public ValidationErrors validate(Database database) { + return null; + } +} diff --git a/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java b/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java index f8b473b8fe..1aed08bdcc 100644 --- a/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java +++ b/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java @@ -85,7 +85,7 @@ public void handle(Serializable data) { changedEvent.getLanguage()); String contactTeamCoordinatorLink = applicationConfiguration.getServerPath() + - "/language/contact/" + changedEvent.getLanguage(); + "/language/view/" + changedEvent.getLanguage(); LanguageTeamPermissionChangeEmailStrategy emailStrategy = new LanguageTeamPermissionChangeEmailStrategy( changedEvent, msgs, contactTeamCoordinatorLink); diff --git a/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java b/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java index c12f6bd52a..e71a526d52 100644 --- a/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java +++ b/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java @@ -31,6 +31,7 @@ import lombok.extern.slf4j.Slf4j; import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Observer; @@ -60,6 +61,19 @@ public class NotificationManager implements Serializable { @In private QueueSession queueSession; + + @Create + public void onCreate() { + try { + mailQueueSender.getQueue(); + } catch (JMSException e) { + // it will never reach this block. As long as you call getQueue() + // and if the queue is not defined, seam will terminate: + // org.jboss.seam.jms.ManagedQueueSender.getQueue(ManagedQueueSender.java:45 + Throwables.propagate(e); + } + } + @Observer(LanguageTeamPermissionChangedEvent.EVENT_NAME) public void onLanguageTeamPermissionChanged( diff --git a/zanata-war/src/main/java/org/zanata/rest/dto/TranslationMatrix.java b/zanata-war/src/main/java/org/zanata/rest/dto/TranslationMatrix.java new file mode 100644 index 0000000000..d9d487a46e --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/rest/dto/TranslationMatrix.java @@ -0,0 +1,24 @@ +package org.zanata.rest.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import org.zanata.common.ContentState; +import org.zanata.common.LocaleId; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +@Data +@AllArgsConstructor +public class TranslationMatrix { + private String savedDate; + private String projectSlug; + private String projectName; + private String versionSlug; + private LocaleId localeId; + private String localeDisplayName; + private ContentState savedState; + private long wordCount; +} diff --git a/zanata-war/src/main/java/org/zanata/rest/service/DateRange.java b/zanata-war/src/main/java/org/zanata/rest/service/DateRange.java new file mode 100644 index 0000000000..93bd5dc8cb --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/rest/service/DateRange.java @@ -0,0 +1,74 @@ +package org.zanata.rest.service; + +import java.util.Date; +import java.util.TimeZone; + +import org.jboss.resteasy.spi.BadRequestException; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.Days; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.zanata.exception.InvalidDateParamException; +import org.zanata.util.DateUtil; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** +* @author Patrick Huang +* pahuang@redhat.com +*/ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +class DateRange { + private static final int MAX_STATS_DAYS = 365; + @Getter + private final DateTime fromDate; + @Getter + private final DateTime toDate; + @Getter + private final DateTimeZone timeZone; + + static DateRange from(String dateRangeParam) { + return from(dateRangeParam, null); + } + + static DateRange from(String dateRangeParam, String fromTimezoneId) { + String[] dateRange = dateRangeParam.split("\\.\\."); + if (dateRange.length != 2) { + throw new InvalidDateParamException(dateRangeParam); + } + DateTimeZone zone; + if (fromTimezoneId == null) { + zone = DateTimeZone.getDefault(); + } else { + try { + zone = DateTimeZone.forID(fromTimezoneId); + } catch (IllegalArgumentException e) { + throw new BadRequestException("Invalid timezone ID:" + fromTimezoneId); + } + } + + DateTime fromDate; + DateTime toDate; + + try { + DateTimeFormatter formatter = + DateTimeFormat.forPattern(StatisticsResource.DATE_FORMAT) + .withZone(zone); + fromDate = formatter.parseDateTime(dateRange[0]); + toDate = formatter.parseDateTime(dateRange[1]); + + fromDate = fromDate.withTimeAtStartOfDay(); // start of day + toDate = toDate.plusDays(1).minusMillis(1); // end of day + + if (fromDate.isAfter(toDate) || Days.daysBetween(fromDate, + toDate).getDays() > MAX_STATS_DAYS) { + throw new InvalidDateParamException(dateRangeParam); + } + } catch (IllegalArgumentException e) { + throw new InvalidDateParamException(dateRangeParam); + } + return new DateRange(fromDate, toDate, zone); + } +} diff --git a/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java b/zanata-war/src/main/java/org/zanata/rest/service/StatisticsServiceImpl.java similarity index 72% rename from zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java rename to zanata-war/src/main/java/org/zanata/rest/service/StatisticsServiceImpl.java index 1a9b8cf06b..ceacfcb2b7 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/StatisticsServiceImpl.java @@ -18,23 +18,36 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ -package org.zanata.service.impl; +package org.zanata.rest.service; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.util.Date; import java.util.List; import java.util.Map; +import javax.persistence.EntityManager; +import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.hibernate.transform.ResultTransformer; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; import org.zanata.common.ContentState; import org.zanata.common.EntityStatus; import org.zanata.common.LocaleId; @@ -44,7 +57,6 @@ import org.zanata.dao.PersonDAO; import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowTargetHistoryDAO; -import org.zanata.exception.InvalidDateParamException; import org.zanata.model.HDocument; import org.zanata.model.HLocale; import org.zanata.model.HPerson; @@ -52,18 +64,21 @@ import org.zanata.model.HTextFlowTarget; import org.zanata.rest.NoSuchEntityException; import org.zanata.rest.dto.Link; +import org.zanata.rest.dto.TranslationMatrix; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; import org.zanata.rest.dto.stats.TranslationStatistics; import org.zanata.rest.dto.stats.TranslationStatistics.StatUnit; import org.zanata.rest.dto.stats.contribution.BaseContributionStatistic; import org.zanata.rest.dto.stats.contribution.ContributionStatistics; import org.zanata.rest.dto.stats.contribution.LocaleStatistics; -import org.zanata.rest.service.StatisticsResource; -import org.zanata.rest.service.ZPathService; import org.zanata.service.TranslationStateCache; +import org.zanata.service.impl.LocaleServiceImpl; import org.zanata.util.DateUtil; import org.zanata.util.StatisticsUtil; import org.zanata.webtrans.shared.model.DocumentStatus; +import com.google.common.base.Optional; + +import static org.apache.commons.lang.StringUtils.abbreviate; /** * Default implementation for the @@ -97,9 +112,10 @@ public class StatisticsServiceImpl implements StatisticsResource { private PersonDAO personDAO; @In - private TranslationStateCache translationStateCacheImpl; + private EntityManager entityManager; - private static final int MAX_STATS_DAYS = 365; + @In + private TranslationStateCache translationStateCacheImpl; // TODO Need to refactor this method to get Message statistic by default. // This is to be consistance with UI which uses message stats, and for @@ -114,7 +130,7 @@ public ContainerTranslationStatistics getStatistics(String projectSlug, if (locales.length == 0) { List iterationLocales = localeServiceImpl.getSupportedLanguageByProjectIteration( - projectSlug, iterationSlug); + projectSlug, iterationSlug); localeIds = new LocaleId[iterationLocales.size()]; for (int i = 0, iterationLocalesSize = iterationLocales.size(); i < iterationLocalesSize; i++) { HLocale loc = iterationLocales.get(i); @@ -222,7 +238,7 @@ public ContainerTranslationStatistics getStatistics(String projectSlug, if (locales.length == 0) { List iterationLocales = localeServiceImpl.getSupportedLanguageByProjectIteration( - projectSlug, iterationSlug); + projectSlug, iterationSlug); localeIds = new LocaleId[iterationLocales.size()]; for (int i = 0, iterationLocalesSize = iterationLocales.size(); i < iterationLocalesSize; i++) { HLocale loc = iterationLocales.get(i); @@ -291,12 +307,10 @@ public ContainerTranslationStatistics getStatistics(String projectSlug, * Get contribution statistic (translations) from project-version within * given date range. * - * Throws NoSuchEntityException if: - * - project/version not found or is obsolete, - * - user not found + * Throws NoSuchEntityException if: - project/version not found or is + * obsolete, - user not found * - * Throws InvalidDateParamException if: - * - dateRangeParam is in wrong format, + * Throws InvalidDateParamException if: - dateRangeParam is in wrong format, * - date range is over MAX_STATS_DAYS * * @param projectSlug @@ -306,8 +320,8 @@ public ContainerTranslationStatistics getStatistics(String projectSlug, * @param username * username of contributor * @param dateRangeParam - * from..to (yyyy-mm-dd..yyyy-mm-dd), - * date range maximum: 365 days + * from..to (yyyy-mm-dd..yyyy-mm-dd), date range maximum: 365 + * days */ @Override public ContributionStatistics getContributionStatistics(String projectSlug, @@ -320,38 +334,17 @@ public ContributionStatistics getContributionStatistics(String projectSlug, throw new NoSuchEntityException(projectSlug + "/" + versionSlug); } - HPerson person = personDAO.findByUsername(username); - if (person == null) { - throw new NoSuchEntityException(username); - } - - String[] dateRange = dateRangeParam.split("\\.\\."); - if (dateRange.length != 2) { - throw new InvalidDateParamException(dateRangeParam); - } - - Date fromDate, toDate; - - try { - fromDate = DateUtil.getDate(dateRange[0], DATE_FORMAT); - toDate = DateUtil.getDate(dateRange[1], DATE_FORMAT); + HPerson person = findPersonOrExceptionOnNotFound(username); - fromDate = DateUtil.getStartOfDay(fromDate); - toDate = DateUtil.getEndOfTheDay(toDate); - - if (fromDate.after(toDate) || !DateUtil.isDatesInRange(fromDate, - toDate, MAX_STATS_DAYS)) { - throw new InvalidDateParamException(dateRangeParam); - } - } catch (IllegalArgumentException e) { - throw new InvalidDateParamException(dateRangeParam); - } + DateRange dateRange = DateRange.from(dateRangeParam); LocaleStatistics localeStatistics = new LocaleStatistics(); List data = textFlowTargetHistoryDAO.getUserContributionStatisticInVersion( - version.getId(), person.getId(), fromDate, toDate); + version.getId(), person.getId(), + dateRange.getFromDate().toDate(), + dateRange.getToDate().toDate()); for (Object[] entry : data) { int count = ((BigDecimal) entry[0]).intValue(); @@ -374,6 +367,14 @@ public ContributionStatistics getContributionStatistics(String projectSlug, return result; } + private HPerson findPersonOrExceptionOnNotFound(String username) { + HPerson person = personDAO.findByUsername(username); + if (person == null) { + throw new NoSuchEntityException(abbreviate(username, 23)); + } + return person; + } + private TranslationStatistics getWordsStats(TransUnitWords wordCount, LocaleId locale, Date lastChanged, String lastModifiedBy) { TranslationStatistics stats = @@ -427,4 +428,106 @@ public ContainerTranslationStatistics getDocStatistics(Long documentId, return result; } + + /** + * Get translation work statistics for a user in given date range. + * + * Throws NoSuchEntityException if: - user not found + * + * Throws InvalidDateParamException if: - dateRangeParam is in wrong format, + * - date range is over MAX_STATS_DAYS + * + * @param username + * username of contributor + * @param dateRangeParam + * from..to (yyyy-mm-dd..yyyy-mm-dd), date range maximum: 365 + * days + * @param userTimeZoneID + * optional user time zone ID. Will use system default in absence + * or GMT zone if provided time zone ID can not be understood. + */ + @Path("user/{username}/{dateRangeParam}") + @GET + @Produces({"application/json"}) + public List getUserWorkMatrix( + @PathParam("username") final String username, + @PathParam("dateRangeParam") String dateRangeParam, + @QueryParam("userTimeZone") String userTimeZoneID) { + HPerson person = + findPersonOrExceptionOnNotFound(username); + DateRange dateRange = DateRange.from(dateRangeParam, userTimeZoneID); + DateTime fromDate = dateRange.getFromDate(); + DateTime toDate = dateRange.getToDate(); + + DateTimeZone userZone = dateRange.getTimeZone(); + DateTimeFormatter dateFormatter = + DateTimeFormat.forPattern(DATE_FORMAT) + .withZone(userZone); + + // TODO system time zone should be persisted in database + DateTimeZone systemZone = DateTimeZone.getDefault(); + + Optional userZoneOpt; + if (userZone.getStandardOffset(0) != systemZone.getStandardOffset(0)) { + userZoneOpt = Optional.of(userZone); + } else { + userZoneOpt = Optional.absent(); + } + + List translationMatrixList = + textFlowTargetHistoryDAO.getUserTranslationMatrix(person, + fromDate, toDate, userZoneOpt, systemZone, + new UserMatrixResultTransformer(entityManager, dateFormatter)); + + return translationMatrixList; + } + + @RequiredArgsConstructor + public static class UserMatrixResultTransformer implements + ResultTransformer { + private static final long serialVersionUID = 1L; + private final EntityManager entityManager; + private final DateTimeFormatter dateFormater; + + @Override + public Object transformTuple(Object[] tuple, String[] aliases) { + String savedDate = dateFormater.print( + new DateTime(tuple[0]).toDate().getTime()); + HProjectIteration iteration = + entityManager.find(HProjectIteration.class, + ((BigInteger) tuple[1]).longValue()); + String projectSlug = iteration.getProject().getSlug(); + String projectName = iteration.getProject().getName(); + String versionSlug = iteration.getSlug(); + + HLocale locale = + entityManager.find(HLocale.class, + ((BigInteger) tuple[2]).longValue()); + String localeDisplayName = locale.retrieveDisplayName(); + LocaleId localeId = locale.getLocaleId(); + + ContentState savedState = ContentState.values()[(int) tuple[3]]; + long wordCount = + ((BigDecimal) tuple[4]).toBigInteger().longValue(); + + return new TranslationMatrix(savedDate, projectSlug, projectName, + versionSlug, localeId, localeDisplayName, + savedState, wordCount); + } + + @Override + public List transformList(List collection) { + return collection; + } + } + + @Getter + @RequiredArgsConstructor + public static class UserTranslationMatrix { + private final Date savedDate; + private final HProjectIteration projectIteration; + private final HLocale locale; + private final ContentState savedState; + private final long wordCount; + } } diff --git a/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java b/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java index 33fbdd5378..6ac9ee1735 100644 --- a/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java +++ b/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java @@ -309,7 +309,7 @@ public void onLoginCompleted(@Observes LoginCompleted payload) { // is available. In cases where it's not in database and // there is no servlet request, the value will not be null. // e.g. EmailBuilder triggered by JMS message - applicationConfiguration.getServerPath(); + applicationConfiguration.createDefaultServerPath(); } public boolean isAccountWaitingForActivation(String username) { diff --git a/zanata-war/src/main/java/org/zanata/security/ZanataIdentity.java b/zanata-war/src/main/java/org/zanata/security/ZanataIdentity.java index 5f5ee59563..0e5e737c86 100644 --- a/zanata-war/src/main/java/org/zanata/security/ZanataIdentity.java +++ b/zanata-war/src/main/java/org/zanata/security/ZanataIdentity.java @@ -122,8 +122,8 @@ public boolean hasPermission(Object target, String action) { target, action, getAccountUsername()); } } else { - if (log.isWarnEnabled()) { - log.warn("DENIED hasPermission({}, {}) for user {}", + if (log.isDebugEnabled()) { + log.debug("DENIED hasPermission({}, {}) for user {}", target, action, getAccountUsername()); } } @@ -144,8 +144,8 @@ public boolean hasPermission(String name, String action, Object... arg) { name, action, arg, getAccountUsername()); } } else { - if (log.isWarnEnabled()) { - log.warn("DENIED hasPermission({}, {}, {}) for user {}", + if (log.isDebugEnabled()) { + log.debug("DENIED hasPermission({}, {}, {}) for user {}", name, action, arg, getAccountUsername()); } } diff --git a/zanata-war/src/main/java/org/zanata/service/GravatarService.java b/zanata-war/src/main/java/org/zanata/service/GravatarService.java index 8cac6f58cf..195a1bd117 100644 --- a/zanata-war/src/main/java/org/zanata/service/GravatarService.java +++ b/zanata-war/src/main/java/org/zanata/service/GravatarService.java @@ -1,6 +1,8 @@ package org.zanata.service; public interface GravatarService { + int USER_IMAGE_SIZE = 115; + String getUserImageUrl(int size); String getUserImageUrl(int size, String email); diff --git a/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java b/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java new file mode 100644 index 0000000000..a2366286fa --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java @@ -0,0 +1,54 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.service; + +import java.util.concurrent.Future; + +import org.zanata.async.handle.MergeTranslationsTaskHandle; +import org.zanata.model.HProjectIteration; + +public interface MergeTranslationsService { + //@formatter:off + /** + * Starts a background merge translations of a version to another. + * + * @param sourceProjectSlug - source project identifier + * @param sourceVersionSlug - source version identifier + * @param targetProjectSlug - target project identifier + * @param targetVersionSlug - target version identifier + * @param useNewerTranslation - to override translated/approved string in target with newer entry in source + * @param handle - task handler for merge translation + */ + //@formatter:on + Future startMergeTranslations(String sourceProjectSlug, + String sourceVersionSlug, String targetProjectSlug, + String targetVersionSlug, boolean useNewerTranslation, + MergeTranslationsTaskHandle handle); + + /** + * Return total count of translations to be processed. + * + * @param sourceVersionId - source HProjectIteration id + * @param targetVersionId - target HProjectIteration id + */ + int getTotalProgressCount(HProjectIteration sourceVersion, + HProjectIteration targetVersion); +} diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransWork.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransWork.java index 754d33749e..c88e80fd46 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransWork.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransWork.java @@ -16,6 +16,7 @@ import org.zanata.service.TranslationFinder; import org.zanata.service.ValidationService; import org.zanata.service.VersionStateCache; +import org.zanata.util.MessageGenerator; import org.zanata.webtrans.shared.model.ValidationAction; import com.google.common.base.Optional; import com.google.common.base.Stopwatch; @@ -179,23 +180,6 @@ protected Integer work() throws Exception { return numCopied; } - private String createComment(HTextFlowTarget target) { - String author; - HDocument document = target.getTextFlow().getDocument(); - String projectname = - document.getProjectIteration().getProject().getName(); - String version = document.getProjectIteration().getSlug(); - String documentid = document.getDocId(); - if (target.getLastModifiedBy() != null) { - author = ", author " + target.getLastModifiedBy().getName(); - } else { - author = ""; - } - - return "translation auto-copied from project " + projectname - + ", version " + version + ", document " + documentid + author; - } - private void saveCopyTransMatch(final HTextFlowTarget matchingTarget, final HTextFlow originalTf, final HCopyTransOptions options, final boolean requireTranslationReview) { @@ -264,12 +248,18 @@ public Boolean get() { } hTarget.setContents(matchingTarget.getContents()); hTarget.setState(copyState); - HSimpleComment hcomment = hTarget.getComment(); - if (hcomment == null) { - hcomment = new HSimpleComment(); - hTarget.setComment(hcomment); + if(matchingTarget.getComment() == null) { + hTarget.setComment(null); + } else { + HSimpleComment hComment = hTarget.getComment(); + if (hComment == null) { + hComment = new HSimpleComment(); + hTarget.setComment(hComment); + } + hComment.setComment(matchingTarget.getComment().getComment()); } - hcomment.setComment(createComment(matchingTarget)); + hTarget.setRevisionComment(MessageGenerator + .getCopyTransMessage(matchingTarget)); // TODO Maybe we should think about registering a Hibernate // integrator for these updates diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java index 7327c836a2..380a7ed764 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java @@ -22,6 +22,7 @@ import org.zanata.model.HLocale; import org.zanata.model.HProjectIteration; import org.zanata.model.HRawDocument; +import org.zanata.model.HSimpleComment; import org.zanata.model.HTextFlow; import org.zanata.model.HTextFlowTarget; import org.zanata.model.HTextFlowTargetHistory; @@ -33,6 +34,7 @@ import org.zanata.service.CopyVersionService; import org.zanata.service.VersionStateCache; import org.zanata.util.JPACopier; +import org.zanata.util.MessageGenerator; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -350,6 +352,10 @@ JPACopier. copyBean(tft, "textFlow", "reviewComments", "history"); copy.setTextFlow(newTf); copy.setTextFlowRevision(newTf.getRevision()); + if(tft.getComment() != null) { + copy.setComment(new HSimpleComment(tft.getComment().getComment())); + } + copy.setRevisionComment(MessageGenerator.getCopyVersionMessage(tft)); // copy review comment copy.setReviewComments(Lists diff --git a/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java new file mode 100644 index 0000000000..ba8929bd17 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java @@ -0,0 +1,278 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.service.impl; + +import java.util.List; +import java.util.concurrent.Future; + +import javax.annotation.Nonnull; + +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.zanata.async.Async; +import org.zanata.async.AsyncTaskResult; +import org.zanata.async.ContainsAsyncMethods; +import org.zanata.async.handle.MergeTranslationsTaskHandle; +import org.zanata.dao.ProjectIterationDAO; +import org.zanata.dao.TextFlowDAO; +import org.zanata.model.HLocale; +import org.zanata.model.HProjectIteration; +import org.zanata.model.HTextFlowTarget; +import org.zanata.security.ZanataIdentity; +import org.zanata.service.LocaleService; +import org.zanata.service.MergeTranslationsService; + +import com.google.common.base.Optional; +import com.google.common.base.Stopwatch; +import org.zanata.service.TranslationStateCache; +import org.zanata.service.VersionStateCache; + +/** + * Service provider for merge translations task. + * + * @see org.zanata.action.MergeTranslationsManager + * + * @author Alex Eng aeng@redhat.com + */ +@Name("mergeTranslationsServiceImpl") +@Scope(ScopeType.STATELESS) +@Slf4j +@ContainsAsyncMethods +public class MergeTranslationsServiceImpl implements MergeTranslationsService { + + @In + private ProjectIterationDAO projectIterationDAO; + + @In + private TextFlowDAO textFlowDAO; + + @In + private ZanataIdentity identity; + + @In + private VersionStateCache versionStateCacheImpl; + + @In + private TranslationStateCache translationStateCacheImpl; + + @In + private LocaleService localeServiceImpl; + + private final static int TRANSLATION_BATCH_SIZE = 10; + + @Override + @Async + public Future startMergeTranslations(String sourceProjectSlug, + String sourceVersionSlug, String targetProjectSlug, + String targetVersionSlug, boolean useNewerTranslation, + MergeTranslationsTaskHandle handle) { + + HProjectIteration sourceVersion = + projectIterationDAO.getBySlug(sourceProjectSlug, + sourceVersionSlug); + + if (sourceVersion == null) { + log.error("Cannot find source version of {}:{}", sourceProjectSlug, + sourceVersionSlug); + return AsyncTaskResult.taskResult(); + } + + HProjectIteration targetVersion = + projectIterationDAO.getBySlug(targetProjectSlug, + targetVersionSlug); + + if (targetVersion == null) { + log.error("Cannot find target version of {}:{}", targetProjectSlug, + targetVersionSlug); + return AsyncTaskResult.taskResult(); + } + + if (isVersionsEmpty(sourceVersion, targetVersion)) { + return AsyncTaskResult.taskResult(); + } + + if (getSupportedLocales(targetProjectSlug, targetVersionSlug).isEmpty()) { + log.error("No locales enabled in target version of {} [{}]", + targetProjectSlug, targetVersionSlug); + return AsyncTaskResult.taskResult(); + } + + Optional taskHandleOpt = + Optional.fromNullable(handle); + + if (taskHandleOpt.isPresent()) { + prepareMergeTranslationsHandle(sourceVersion, targetVersion, + taskHandleOpt.get()); + } + + Stopwatch overallStopwatch = Stopwatch.createStarted(); + log.info("merge translations start: from {} to {}", sourceProjectSlug + + ":" + sourceVersionSlug, targetProjectSlug + ":" + + targetVersionSlug); + + int startCount = 0; + int totalCount = getTotalMatchCount(sourceVersion.getId(), + targetVersion.getId()); + + List supportedLocales = getSupportedLocales(targetVersion + .getProject().getSlug(), targetVersion.getSlug()); + + while (startCount < totalCount) { + int processedCount = + mergeTranslationBatch(sourceVersion, targetVersion, + supportedLocales, useNewerTranslation, startCount, + TRANSLATION_BATCH_SIZE); + if (taskHandleOpt.isPresent()) { + taskHandleOpt.get().increaseProgress(processedCount); + } + + startCount += TRANSLATION_BATCH_SIZE; + textFlowDAO.clear(); + } + versionStateCacheImpl.clearVersionStatsCache(targetVersion.getId()); + log.info("merge translation end: from {} to {}, {}", sourceProjectSlug + + ":" + sourceVersionSlug, targetProjectSlug + ":" + + targetVersionSlug, overallStopwatch); + + return AsyncTaskResult.taskResult(); + } + + protected int mergeTranslationBatch(HProjectIteration sourceVersion, + HProjectIteration targetVersion, List supportedLocales, + boolean useNewerTranslation, int offset, int batchSize) { + try { + return new MergeTranslationsWork(sourceVersion.getId(), + targetVersion.getId(), offset, batchSize, + useNewerTranslation, supportedLocales, + textFlowDAO, translationStateCacheImpl) + .workInTransaction(); + } catch (Exception e) { + log.warn("exception during copy text flow target", e); + return 0; + } + } + + /** + * Check if sourceVersion or targetVersion has source document. + * + * @param sourceVersion + * @param targetVersion + */ + private boolean isVersionsEmpty(HProjectIteration sourceVersion, + HProjectIteration targetVersion) { + if (sourceVersion.getDocuments().isEmpty()) { + log.error("No documents in source version {}:{}", sourceVersion + .getProject().getSlug(), sourceVersion.getSlug()); + return true; + } + if (targetVersion.getDocuments().isEmpty()) { + log.error("No documents in target version {}:{}", targetVersion + .getProject().getSlug(), targetVersion.getSlug()); + return true; + } + return false; + } + + private void prepareMergeTranslationsHandle( + @Nonnull HProjectIteration sourceVersion, + @Nonnull HProjectIteration targetVersion, + @Nonnull MergeTranslationsTaskHandle handle) { + handle.setTriggeredBy(identity.getAccountUsername()); + + int total = getTotalProgressCount(sourceVersion, targetVersion); + handle.setMaxProgress(total); + handle.setTotalTranslations(total); + } + + @Override + public int getTotalProgressCount(HProjectIteration sourceVersion, + HProjectIteration targetVersion) { + int matchCount = getTotalMatchCount(sourceVersion.getId(), + targetVersion.getId()); + + List locales = + getSupportedLocales(targetVersion.getProject().getSlug(), + targetVersion.getSlug()); + + return matchCount * locales.size(); + } + + private int getTotalMatchCount(Long sourceVersionId, Long targetVersionId) { + return textFlowDAO.getSourceByMatchedContextCount( + sourceVersionId, targetVersionId); + } + + public List getSupportedLocales(String projectSlug, + String versionSlug) { + return localeServiceImpl.getSupportedLanguageByProjectIteration( + projectSlug, versionSlug); + } + + // @formatter:off + /** + * Rule of which translation should merge + * | from | to | copy? | + * |-----------------------|------------------|-----------| + * |fuzzy/untranslated | any | no | + * |-----------------------|------------------|-----------| + * |different source text/ | | | + * |docId/locale/resId | any | no | + * |-----------------------|------------------|-----------| + * |translated/approved | untranslated | yes | + * |-----------------------|------------------|-----------| + * |translated/approved | fuzzy | yes | + * |-----------------------|------------------|-----------| + * |translated/approved | same as from | copy if from is newer + * and option says to copy + * + * @param sourceTft - matched documentId, source text, + * translated/approved HTextFlowTarget. + * @see org.zanata.dao.TextFlowDAO#getSourceByMatchedContext + * @param targetTft - HTextFlowTarget from target version + */ + // @formatter:on + public static boolean shouldMerge(HTextFlowTarget sourceTft, + HTextFlowTarget targetTft, boolean useNewerTranslation) { + // should NOT merge is source tft is not translated/approved + if (!sourceTft.getState().isTranslated()) { + return false; + } + + // should merge if target is not in translated/approved state + if (!targetTft.getState().isTranslated()) { + return true; + } + + // should NOT merge if both state and contents are the same + if (sourceTft.getState().equals(targetTft.getState()) + && sourceTft.getContents().equals(targetTft.getContents())) { + return false; + } + + // if both in translated state return latest if enabled + return useNewerTranslation && sourceTft.getLastChanged().after( + targetTft.getLastChanged()); + } +} diff --git a/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsWork.java b/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsWork.java new file mode 100644 index 0000000000..b51939a255 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsWork.java @@ -0,0 +1,160 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.service.impl; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.core.Events; +import org.jboss.seam.util.Work; +import org.zanata.common.ContentState; +import org.zanata.dao.TextFlowDAO; +import org.zanata.events.TextFlowTargetStateEvent; +import org.zanata.model.HLocale; +import org.zanata.model.HSimpleComment; +import org.zanata.model.HTextFlow; +import org.zanata.model.HTextFlowTarget; +import org.zanata.service.TranslationStateCache; +import org.zanata.util.MessageGenerator; + +import com.google.common.base.Stopwatch; + +/** + * Merge translation and persist in transaction. + * + * Merge HTextFlowTargets from HProjectIteration(id=sourceVersionId) to + * HProjectIteration(id=targetVersionId) in batches(batchStart, batchLength) + * + * @see org.zanata.service.impl.MergeTranslationsServiceImpl#startMergeTranslations + * @see org.zanata.service.impl.MergeTranslationsServiceImpl#shouldMerge + * + * returns count of processed translations. + * + * @author Alex Eng aeng@redhat.com + */ +@Slf4j +@AllArgsConstructor +public class MergeTranslationsWork extends Work { + private final Long sourceVersionId; + private final Long targetVersionId; + private final int batchStart; + private final int batchLength; + private final boolean useNewerTranslation; + private final List supportedLocales; + + private final TextFlowDAO textFlowDAO; + + private final TranslationStateCache translationStateCacheImpl; + + private final Stopwatch stopwatch = Stopwatch.createUnstarted(); + + @Override + protected Integer work() throws Exception { + stopwatch.start(); + + List matches = + textFlowDAO.getSourceByMatchedContext( + sourceVersionId, targetVersionId, batchStart, + batchLength); + + for (HTextFlow[] results : matches) { + HTextFlow sourceTf = results[0]; + HTextFlow targetTf = results[1]; + boolean foundChange = false; + + for (HLocale hLocale : supportedLocales) { + HTextFlowTarget sourceTft = + sourceTf.getTargets().get(hLocale.getId()); + // only process translated state + if (sourceTft == null || !sourceTft.getState().isTranslated()) { + continue; + } + + HTextFlowTarget targetTft = + targetTf.getTargets().get(hLocale.getId()); + if (targetTft == null) { + targetTft = new HTextFlowTarget(targetTf, hLocale); + targetTft.setVersionNum(0); + targetTf.getTargets().put(hLocale.getId(), targetTft); + } + + if (MergeTranslationsServiceImpl.shouldMerge(sourceTft, + targetTft, useNewerTranslation)) { + foundChange = true; + mergeTextFlowTarget(sourceTft, targetTft); + } + } + if (foundChange) { + translationStateCacheImpl.clearDocumentStatistics(targetTf + .getDocument().getId()); + textFlowDAO.makePersistent(targetTf); + textFlowDAO.flush(); + } + } + stopwatch.stop(); + log.info("Complete merge translations of {} in {}", matches.size() + * supportedLocales.size(), stopwatch); + return matches.size() * supportedLocales.size(); + } + + private void mergeTextFlowTarget(HTextFlowTarget sourceTft, + HTextFlowTarget targetTft) { + ContentState oldState = targetTft.getState(); + + targetTft.setContents(sourceTft.getContents()); + targetTft.setState(sourceTft.getState()); + targetTft.setLastChanged(sourceTft.getLastChanged()); + targetTft.setLastModifiedBy(sourceTft.getLastModifiedBy()); + targetTft.setTranslator(sourceTft.getTranslator()); + + if (sourceTft.getComment() == null) { + targetTft.setComment(null); + } else { + HSimpleComment hComment = targetTft.getComment(); + if (hComment == null) { + hComment = new HSimpleComment(); + targetTft.setComment(hComment); + } + hComment.setComment(sourceTft.getComment().getComment()); + } + targetTft.setRevisionComment(MessageGenerator + .getMergeTranslationMessage(sourceTft)); + + raiseSuccessEvent(targetTft, oldState); + } + + private void raiseSuccessEvent(HTextFlowTarget targetTft, + ContentState oldState) { + if (Events.exists()) { + TextFlowTargetStateEvent event = + new TextFlowTargetStateEvent(null, targetVersionId, + targetTft.getTextFlow().getDocument().getId(), + targetTft.getTextFlow().getId(), + targetTft.getLocale().getLocaleId(), + targetTft.getId(), targetTft.getState(), oldState); + + Events.instance().raiseTransactionSuccessEvent( + TextFlowTargetStateEvent.EVENT_NAME, event); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java index 7f254e486b..923974b7d8 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java @@ -112,9 +112,6 @@ public class TranslationServiceImpl implements TranslationService { @In private DocumentDAO documentDAO; - @In - private PersonDAO personDAO; - @In private TextFlowDAO textFlowDAO; @@ -634,6 +631,7 @@ protected Boolean work() throws Exception { } }.workInTransaction(); } catch (Exception e) { + log.error("exception in transferFromTranslationsResourceExtensions: {}", e.getMessage()); throw new ZanataServiceException("Error during translation.", 500, e); } @@ -672,6 +670,7 @@ protected Boolean work() throws Exception { work.setAssignCreditToUploader(assignCreditToUploader); changed |= work.workInTransaction(); } catch (Exception e) { + log.error("exception in SaveBatchWork:{}", e.getMessage()); throw new ZanataServiceException("Error during translation.", 500, e); } @@ -703,6 +702,7 @@ protected Void work() throws Exception { document.getId(), false, hLocale.getLocaleId())); } catch (Exception e) { + log.error("exception in removeTargets: {}", e.getMessage()); throw new ZanataServiceException("Error during translation.", 500, e); } @@ -739,6 +739,10 @@ private final class SaveBatchWork extends Work { @Override protected Boolean work() throws Exception { + // we need to call clear at the beginning because text flow target + // history rely on after commit callback. + textFlowTargetDAO.clear(); + document = entityManager.find(HDocument.class, document.getId()); boolean changed = false; // we need a fresh object in this session, @@ -832,7 +836,7 @@ public String apply(TextFlowTarget input) { changed = true; Long actorId; - if(assignCreditToUploader){ + if (assignCreditToUploader){ HPerson hPerson = authenticatedAccount.getPerson(); hTarget.setTranslator(hPerson); hTarget.setLastModifiedBy(hPerson); @@ -851,8 +855,6 @@ public String apply(TextFlowTarget input) { handleOp.get().increaseProgress(1); } } - // every batch will start with a new hibernate session therefore no - // need to call clear textFlowTargetDAO.flush(); return changed; diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java index 3b23aa821d..54dbf8e311 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java @@ -26,11 +26,12 @@ import java.util.List; import java.util.Map; +import com.google.common.annotations.VisibleForTesting; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import net.sf.ehcache.CacheManager; +import org.infinispan.manager.CacheContainer; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.Create; @@ -39,7 +40,7 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.zanata.cache.CacheWrapper; -import org.zanata.cache.EhcacheWrapper; +import org.zanata.cache.InfinispanCacheWrapper; import org.zanata.common.LocaleId; import org.zanata.dao.DocumentDAO; import org.zanata.dao.LocaleDAO; @@ -83,8 +84,6 @@ public class TranslationStateCacheImpl implements TranslationStateCache { private static final String TFT_VALIDATION_CACHE_NAME = BASE + ".targetValidationCache"; - private CacheManager cacheManager; - private CacheWrapper documentStatisticCache; private CacheLoader documentStatisticLoader; @@ -95,7 +94,19 @@ public class TranslationStateCacheImpl implements TranslationStateCache { private CacheLoader> targetValidationLoader; @In - private ServiceLocator serviceLocator; + private CacheContainer cacheContainer; + + @In + private TextFlowDAO textFlowDAO; + + @In + private TextFlowTargetDAO textFlowTargetDAO; + + @In + private DocumentDAO documentDAO; + + @In + private LocaleDAO localeDAO; // constructor for Seam public TranslationStateCacheImpl() { @@ -104,6 +115,7 @@ public TranslationStateCacheImpl() { } // Constructor for testing + @VisibleForTesting public TranslationStateCacheImpl( CacheLoader documentStatisticLoader, CacheLoader docStatsLoader, @@ -115,24 +127,20 @@ public TranslationStateCacheImpl( @Create public void create() { - cacheManager = CacheManager.create(); documentStatisticCache = - EhcacheWrapper.create(DOC_STATISTIC_CACHE_NAME, - cacheManager, documentStatisticLoader); + InfinispanCacheWrapper.create(DOC_STATISTIC_CACHE_NAME, + cacheContainer, documentStatisticLoader); docStatusCache = - EhcacheWrapper.create(DOC_STATUS_CACHE_NAME, cacheManager, + InfinispanCacheWrapper.create(DOC_STATUS_CACHE_NAME, + cacheContainer, docStatusLoader); targetValidationCache = - EhcacheWrapper.create(TFT_VALIDATION_CACHE_NAME, cacheManager, + InfinispanCacheWrapper.create(TFT_VALIDATION_CACHE_NAME, + cacheContainer, targetValidationLoader); } - @Destroy - public void destroy() { - cacheManager.shutdown(); - } - @Override public WordStatistic getDocumentStatistics(Long documentId, LocaleId localeId) { @@ -142,7 +150,6 @@ public WordStatistic getDocumentStatistics(Long documentId, @Override public void clearDocumentStatistics(Long documentId) { - LocaleDAO localeDAO = serviceLocator.getInstance(LocaleDAO.class); for (HLocale locale : localeDAO.findAll()) { DocumentLocaleKey key = new DocumentLocaleKey(documentId, locale.getLocaleId()); @@ -156,6 +163,7 @@ public void clearDocumentStatistics(Long documentId, LocaleId localeId) { localeId)); } + public DocumentStatus getDocumentStatus(Long documentId, LocaleId localeId) { return docStatusCache.getWithLoader(new DocumentLocaleKey( documentId, localeId)); @@ -197,8 +205,8 @@ private void updateDocStatusCache(DocumentLocaleKey key, DocumentStatus documentStatus = docStatusCache.get(key); if (documentStatus != null) { HTextFlowTarget target = - getTextFlowTargetDAO().findById(updatedTargetId, false); - updateDocumentStatus(getDocumentDAO(), documentStatus, + textFlowTargetDAO.findById(updatedTargetId, false); + updateDocumentStatus(documentDAO, documentStatus, key.getDocumentId(), target); } } @@ -206,7 +214,7 @@ private void updateDocStatusCache(DocumentLocaleKey key, private Boolean loadTargetValidation(Long textFlowTargetId, ValidationId validationId) { HTextFlowTarget tft = - getTextFlowTargetDAO().findById(textFlowTargetId, false); + textFlowTargetDAO.findById(textFlowTargetId, false); if (tft != null) { ValidationAction action = ValidationFactoryProvider.getFactoryInstance() @@ -219,18 +227,6 @@ private Boolean loadTargetValidation(Long textFlowTargetId, return null; } - DocumentDAO getDocumentDAO() { - return serviceLocator.getInstance(DocumentDAO.class); - } - - TextFlowTargetDAO getTextFlowTargetDAO() { - return serviceLocator.getInstance(TextFlowTargetDAO.class); - } - - TextFlowDAO getTextFlowDAO() { - return serviceLocator.getInstance(TextFlowDAO.class); - } - private static class DocumentStatisticLoader extends CacheLoader { diff --git a/zanata-war/src/main/java/org/zanata/service/impl/VersionStateCacheImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/VersionStateCacheImpl.java index ec088c8fc2..390b51d71d 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/VersionStateCacheImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/VersionStateCacheImpl.java @@ -22,8 +22,8 @@ package org.zanata.service.impl; -import net.sf.ehcache.CacheManager; - +import com.google.common.annotations.VisibleForTesting; +import org.infinispan.manager.CacheContainer; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.Destroy; @@ -32,7 +32,7 @@ import org.jboss.seam.annotations.Observer; import org.jboss.seam.annotations.Scope; import org.zanata.cache.CacheWrapper; -import org.zanata.cache.EhcacheWrapper; +import org.zanata.cache.InfinispanCacheWrapper; import org.zanata.common.LocaleId; import org.zanata.dao.LocaleDAO; import org.zanata.dao.ProjectIterationDAO; @@ -61,11 +61,12 @@ public class VersionStateCacheImpl implements VersionStateCache { private static final String VERSION_STATISTIC_CACHE_NAME = BASE + ".versionStatisticCache"; - private CacheManager cacheManager; - private CacheWrapper versionStatisticCache; private CacheLoader versionStatisticLoader; + @In + private CacheContainer cacheContainer; + @In private ServiceLocator serviceLocator; @@ -82,15 +83,9 @@ public VersionStateCacheImpl( @Create public void create() { - cacheManager = CacheManager.create(); versionStatisticCache = - EhcacheWrapper.create(VERSION_STATISTIC_CACHE_NAME, - cacheManager, versionStatisticLoader); - } - - @Destroy - public void destroy() { - cacheManager.shutdown(); + InfinispanCacheWrapper.create(VERSION_STATISTIC_CACHE_NAME, + cacheContainer, versionStatisticLoader); } @Observer(TextFlowTargetStateEvent.EVENT_NAME) @@ -129,6 +124,11 @@ public void clearVersionStatsCache(Long versionId) { } } + @VisibleForTesting + public void setCacheContainer(CacheContainer cacheContainer) { + this.cacheContainer = cacheContainer; + } + private static class VersionStatisticLoader extends CacheLoader { diff --git a/zanata-war/src/main/java/org/zanata/ui/ActivityEntry.java b/zanata-war/src/main/java/org/zanata/ui/ActivityEntry.java index 3c7d83ad34..b6694e314d 100644 --- a/zanata-war/src/main/java/org/zanata/ui/ActivityEntry.java +++ b/zanata-war/src/main/java/org/zanata/ui/ActivityEntry.java @@ -40,6 +40,10 @@ import org.zanata.util.UrlUtil; /** + * Provides data and operations needed to display an activity entry. + * + * This is used by template activity-entry.xhtml + * * @author Alex Eng aeng@redhat.com */ @Name("activityEntry") @@ -203,16 +207,23 @@ public String getProjectName(Activity activity) { } public String getVersionName(Activity activity) { + HProjectIteration version = getVersion(activity); + if (version == null) { + return ""; + } else { + return version.getSlug(); + } + } + + public HProjectIteration getVersion(Activity activity) { Object context = getEntity(activity.getContextType(), activity.getContextId()); - if (isTranslationUpdateActivity(activity.getActivityType()) || activity.getActivityType() == ActivityType.UPLOAD_SOURCE_DOCUMENT || activity.getActivityType() == ActivityType.UPLOAD_TRANSLATION_DOCUMENT) { - HProjectIteration version = (HProjectIteration) context; - return version.getSlug(); + return (HProjectIteration) context; } - return ""; + return null; } public String getDocumentName(Activity activity) { diff --git a/zanata-war/src/main/java/org/zanata/ui/CopyAction.java b/zanata-war/src/main/java/org/zanata/ui/CopyAction.java new file mode 100644 index 0000000000..c6feda9499 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/ui/CopyAction.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ + +package org.zanata.ui; + +import org.zanata.async.AsyncTaskHandle; + +/** + * + * Interface for Seam component - version-copy-action-loader.xhtml + * + * @author Alex Eng aeng@redhat.com + */ +public abstract class CopyAction implements ProgressBar { + + public abstract String getProgressMessage(); + + public abstract void onComplete(); + + protected abstract AsyncTaskHandle getHandle(); + + @Override + public String getCompletedPercentage() { + AsyncTaskHandle handle = getHandle(); + + if (handle != null) { + double completedPercent = + (double) handle.getCurrentProgress() / (double) handle + .getMaxProgress() * 100; + if (Double.compare(completedPercent, 100) == 0) { + onComplete(); + } + return PERCENT_FORMAT.format(completedPercent); + } else { + return "0"; + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/ui/ProgressBar.java b/zanata-war/src/main/java/org/zanata/ui/ProgressBar.java index 402258c39e..a5f7d4877f 100644 --- a/zanata-war/src/main/java/org/zanata/ui/ProgressBar.java +++ b/zanata-war/src/main/java/org/zanata/ui/ProgressBar.java @@ -1,23 +1,22 @@ /* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. * - * * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the - * * @author tags. See the copyright.txt file in the distribution for a full - * * listing of individual contributors. - * * - * * This 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 2.1 of the License, or (at your option) - * * any later version. - * * - * * This software 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 this software; if not, write to the Free Software Foundation, - * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * * site: http://www.fsf.org. + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. */ package org.zanata.ui; @@ -25,6 +24,9 @@ import java.text.DecimalFormat; /** + * + * Interface for Seam component - progressBar.xhtml + * * @author Alex Eng aeng@redhat.com */ public interface ProgressBar { diff --git a/zanata-war/src/main/java/org/zanata/util/MessageGenerator.java b/zanata-war/src/main/java/org/zanata/util/MessageGenerator.java new file mode 100644 index 0000000000..85b6d2b075 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/util/MessageGenerator.java @@ -0,0 +1,117 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ + +package org.zanata.util; + +import org.apache.commons.lang.StringUtils; +import org.zanata.model.HDocument; +import org.zanata.model.HTextFlowTarget; + +/** + * Generate messages/comments of Zanata business actions + * + * @author Alex Eng aeng@redhat.com + */ +public class MessageGenerator { + + /** + * Prefix of action type for generated message + */ + public static final String PREFIX_MERGE_TRANS = "Merge translations"; + public static final String PREFIX_COPY_TRANS = "Copy translation"; + public static final String PREFIX_COPY_VERSION = "Copy version"; + + /** + * Create revision comment for translation that is copied by merge + * translation + * + * @see org.zanata.service.MergeTranslationsService + * + * @param tft - HTextFlowTarget to copy from + */ + public static final String getMergeTranslationMessage( + HTextFlowTarget tft) { + HDocument document = tft.getTextFlow().getDocument(); + String author = ""; + if (tft.getLastModifiedBy() != null) { + author = tft.getLastModifiedBy().getName(); + } + + return generateAutoCopiedMessage(PREFIX_MERGE_TRANS, + document.getProjectIteration().getProject().getName(), + document.getProjectIteration() + .getSlug(), document.getDocId(), author); + } + + /** + * Create revision comment for translation that is copied by copy trans + * @see org.zanata.service.CopyTransService + * + * @param tft - HTextFlowTarget to copy from + */ + public static final String getCopyTransMessage(HTextFlowTarget tft) { + HDocument document = tft.getTextFlow().getDocument(); + String author = ""; + if (tft.getLastModifiedBy() != null) { + author = tft.getLastModifiedBy().getName(); + } + return generateAutoCopiedMessage(PREFIX_COPY_TRANS, + document.getProjectIteration() + .getProject().getName(), document.getProjectIteration() + .getSlug(), document.getDocId(), author); + } + + /** + * Create revision comment for translation that is copied by copy version + * @see org.zanata.service.CopyVersionService + * + * @param tft - HTextFlowTarget to copy from + */ + public static final String getCopyVersionMessage(HTextFlowTarget tft) { + HDocument document = tft.getTextFlow().getDocument(); + String author = ""; + if (tft.getLastModifiedBy() != null) { + author = tft.getLastModifiedBy().getName(); + } + return generateAutoCopiedMessage(PREFIX_COPY_VERSION, + document.getProjectIteration().getProject().getName(), + document.getProjectIteration() + .getSlug(), document.getDocId(), author); + } + + private static final String generateAutoCopiedMessage(String prefix, + String projectName, String versionSlug, String docId, String author) { + StringBuilder comment = new StringBuilder(); + + comment.append(prefix + ": translation auto-copied from project ") + .append(projectName) + .append(", version ") + .append(versionSlug) + .append(", document ") + .append(docId); + + if (!StringUtils.isEmpty(author)) { + comment.append(", author ") + .append(author); + } + return comment.toString(); + } +} diff --git a/zanata-war/src/main/java/org/zanata/util/ServiceLocator.java b/zanata-war/src/main/java/org/zanata/util/ServiceLocator.java index acc1c975e2..7845392293 100644 --- a/zanata-war/src/main/java/org/zanata/util/ServiceLocator.java +++ b/zanata-war/src/main/java/org/zanata/util/ServiceLocator.java @@ -21,6 +21,10 @@ package org.zanata.util; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; @@ -72,4 +76,10 @@ public EntityManagerFactory getEntityManagerFactory() { return (EntityManagerFactory) Component.getInstance("entityManagerFactory"); } + public T getJndiComponent(String jndiName, Class clazz) + throws NamingException { + Context ctx = new InitialContext(); + return (T) ctx.lookup(jndiName); + } + } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/AppPresenter.java b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/AppPresenter.java index 342ce5ea54..4e96efb5e1 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/AppPresenter.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/AppPresenter.java @@ -193,7 +193,12 @@ protected void onBind() { userWorkspaceContext.getWorkspaceContext().getLocaleName())); } - display.setReadOnlyVisible(userWorkspaceContext.hasReadOnlyAccess()); + boolean showObsolete = userWorkspaceContext.getWorkspaceRestrictions().isProjectObsolete(); + boolean showReadOnly = !showObsolete && userWorkspaceContext.hasReadOnlyAccess(); + + display.setReadOnlyVisible(showReadOnly); + display.setObsoleteVisible(showObsolete); + } private void registerKeyShortcuts() { diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java index a39d4d570a..e114dd192b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java @@ -381,8 +381,11 @@ String undoUnsuccessful(@PluralCount int unsuccessfulCount, @DefaultMessage("Page size") String pageSize(); - @DefaultMessage("Read only") - String readOnly(); + @DefaultMessage("This project-version is readonly. It cannot be edited.") + String readOnlyTooltip(); + + @DefaultMessage("This project-version is archived. It cannot be edited.") + String obsoleteTooltip(); @DefaultMessage("Advanced user configuration") String otherConfiguration(); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppDisplay.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppDisplay.java index b8bcef684a..c0a8b8330c 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppDisplay.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppDisplay.java @@ -24,6 +24,8 @@ void setStats(ContainerTranslationStatistics transStats, void setReadOnlyVisible(boolean visible); + void setObsoleteVisible(boolean showObsolete); + void showSideMenu(boolean isShowing); void setProjectLinkLabel(String text); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java index e0ba1e5c3b..05d1d86835 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java @@ -92,6 +92,9 @@ interface Styles extends CssResource { @UiField InlineLabel readOnlyLabel; + @UiField + InlineLabel obsoleteLabel; + @UiField(provided = true) Breadcrumb selectedDocumentSpan; @@ -173,7 +176,8 @@ public AppView(WebTransMessages messages, initWidget(uiBinder.createAndBindUi(this)); - readOnlyLabel.setTitle(messages.readOnly()); + readOnlyLabel.setTitle(messages.readOnlyTooltip()); + obsoleteLabel.setTitle(messages.obsoleteTooltip()); keyShortcuts.setTitle(messages.availableKeyShortcutsTitle()); @@ -279,6 +283,11 @@ public void setReadOnlyVisible(boolean visible) { readOnlyLabel.setVisible(visible); } + @Override + public void setObsoleteVisible(boolean visible) { + obsoleteLabel.setVisible(visible); + } + private final static double MIN_MENU_WIDTH = 2; private final static double EXPENDED_MENU_RIGHT = 23; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.ui.xml b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.ui.xml index 9368796ba3..ef3a9cde2b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.ui.xml +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.ui.xml @@ -75,7 +75,9 @@
  • + styleName="i i--lock txt--danger txt--hero l--push-right-quarter" /> + diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationUpdateListener.java b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationUpdateListener.java index 6435053220..f1abd28ab4 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationUpdateListener.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationUpdateListener.java @@ -98,7 +98,8 @@ public void onPostUpdate(final PostUpdateEvent event) { if (!(entity instanceof HTextFlowTarget)) { return; } - + final HTextFlowTarget target = + HTextFlowTarget.class.cast(event.getEntity()); try { new Work() { @Override @@ -108,8 +109,7 @@ protected Void work() throws Exception { Lists.newArrayList(event.getOldState()), Predicates.instanceOf(ContentState.class)); - HTextFlowTarget target = - HTextFlowTarget.class.cast(event.getEntity()); + prepareTransUnitUpdatedEvent(target.getVersionNum() - 1, oldContentState, target); return null; @@ -196,13 +196,14 @@ public void onPostInsert(final PostInsertEvent event) { if (!(entity instanceof HTextFlowTarget)) { return; } + final HTextFlowTarget target = + HTextFlowTarget.class.cast(event.getEntity()); try { new Work() { @Override protected Void work() throws Exception { - HTextFlowTarget target = - HTextFlowTarget.class.cast(event.getEntity()); + prepareTransUnitUpdatedEvent(0, ContentState.New, target); return null; } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ActivateWorkspaceHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ActivateWorkspaceHandler.java index 1f34c08639..3dd6e11b6c 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ActivateWorkspaceHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ActivateWorkspaceHandler.java @@ -129,13 +129,14 @@ public ActivateWorkspaceResult execute(ActivateWorkspaceAction action, boolean isProjectActive = isProjectIterationActive(project.getStatus(), projectIteration.getStatus()); + boolean isProjectObsolete = isProjectIterationObsolete(project.getStatus(), projectIteration.getStatus()); boolean hasWriteAccess = hasWritePermission(project, locale); boolean hasGlossaryUpdateAccess = hasGlossaryUpdatePermission(); boolean requireReview = projectIteration.getRequireTranslationReview(); boolean hasReviewAccess = hasReviewerPermission(locale, project); WorkspaceRestrictions workspaceRestrictions = - new WorkspaceRestrictions(isProjectActive, hasWriteAccess, + new WorkspaceRestrictions(isProjectActive, isProjectObsolete, hasWriteAccess, hasGlossaryUpdateAccess, hasReviewAccess, requireReview); log.debug("workspace restrictions: {}", workspaceRestrictions); @@ -183,6 +184,12 @@ private boolean isProjectIterationActive(EntityStatus projectStatus, .equals(EntityStatus.ACTIVE)); } + private boolean isProjectIterationObsolete(EntityStatus projectStatus, + EntityStatus iterStatus) { + return (projectStatus.equals(EntityStatus.OBSOLETE) || iterStatus + .equals(EntityStatus.OBSOLETE)); + } + protected Person retrievePerson() { HAccount authenticatedAccount = (HAccount) Contexts.getSessionContext().get( diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentStatsHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentStatsHandler.java index 56e1791627..50400e37d1 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentStatsHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentStatsHandler.java @@ -9,7 +9,7 @@ import org.jboss.seam.annotations.Scope; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; import org.zanata.service.TranslationStateCache; -import org.zanata.service.impl.StatisticsServiceImpl; +import org.zanata.rest.service.StatisticsServiceImpl; import org.zanata.webtrans.server.ActionHandlerFor; import org.zanata.webtrans.shared.model.AuditInfo; import org.zanata.webtrans.shared.model.DocumentId; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/shared/model/WorkspaceRestrictions.java b/zanata-war/src/main/java/org/zanata/webtrans/shared/model/WorkspaceRestrictions.java index e2e7e16b8e..4f923e3c5a 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/shared/model/WorkspaceRestrictions.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/shared/model/WorkspaceRestrictions.java @@ -9,6 +9,7 @@ */ public class WorkspaceRestrictions implements IsSerializable { private boolean isProjectActive; + private boolean isProjectObsolete; private boolean hasEditTranslationAccess; private boolean hasReviewAccess; private boolean hasGlossaryUpdateAccess; @@ -18,10 +19,11 @@ public class WorkspaceRestrictions implements IsSerializable { private WorkspaceRestrictions() { } - public WorkspaceRestrictions(boolean projectActive, + public WorkspaceRestrictions(boolean projectActive, boolean projectObsolete, boolean hasEditTranslationAccess, boolean hasGlossaryUpdateAccess, boolean hasReviewAccess, boolean projectRequireReview) { this.isProjectActive = projectActive; + this.isProjectObsolete = projectObsolete; this.hasEditTranslationAccess = hasEditTranslationAccess; this.hasGlossaryUpdateAccess = hasGlossaryUpdateAccess; this.hasReviewAccess = hasReviewAccess; @@ -32,6 +34,10 @@ public boolean isProjectActive() { return isProjectActive; } + public boolean isProjectObsolete() { + return isProjectObsolete; + } + public boolean isHasEditTranslationAccess() { return hasEditTranslationAccess; } @@ -49,20 +55,26 @@ public boolean isProjectRequireReview() { } public WorkspaceRestrictions changeProjectActivity(boolean projectActive) { - return new WorkspaceRestrictions(projectActive, + return new WorkspaceRestrictions(projectActive, isProjectObsolete, + hasEditTranslationAccess, hasGlossaryUpdateAccess, + hasReviewAccess, projectRequireReview); + } + + public WorkspaceRestrictions changeProjectObsolescence(boolean projectObsolete) { + return new WorkspaceRestrictions(isProjectActive, projectObsolete, hasEditTranslationAccess, hasGlossaryUpdateAccess, hasReviewAccess, projectRequireReview); } public WorkspaceRestrictions changeEditTranslationAccess( boolean hasEditTranslationAccess) { - return new WorkspaceRestrictions(isProjectActive, + return new WorkspaceRestrictions(isProjectActive, isProjectObsolete, hasEditTranslationAccess, hasGlossaryUpdateAccess, hasReviewAccess, projectRequireReview); } public WorkspaceRestrictions changeReviewAccess(boolean hasReviewAccess) { - return new WorkspaceRestrictions(isProjectActive, + return new WorkspaceRestrictions(isProjectActive, isProjectObsolete, hasEditTranslationAccess, hasGlossaryUpdateAccess, hasReviewAccess, projectRequireReview); } diff --git a/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml b/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml index 9be10c01cc..6b92660a56 100644 --- a/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml +++ b/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml @@ -27,4 +27,50 @@ + + + Modify existing id for plaintext and libreoffice documents to use content hash instead of old positional id. + + + + + add indexes to HTextFlowTargetHistory and HTextFlowTarget table + + + + + + + + + + + + + + + + add indexes to HTextFlowTargetHistory and HTextFlowTarget table + + + + + + + + + + Add revisionComment to HTextFlowTarget, HTextFlowTargetHistory + + + + + + + + + + + + diff --git a/zanata-war/src/main/resources/messages.properties b/zanata-war/src/main/resources/messages.properties index 29545a2420..cf72d51fb5 100644 --- a/zanata-war/src/main/resources/messages.properties +++ b/zanata-war/src/main/resources/messages.properties @@ -91,6 +91,7 @@ jsf.auth.KerberosNotice.label=It looks like you don't have a valid Kerberos tick #--- Project search (all pages) --- jsf.SearchProjects=Search Projects +jsf.SearchProjectsAndPeople=Search Projects and People jsf.project.search.IncludeObsoleteTooltip=Include Obsolete Projects in Search #--- Footer (all pages) --- @@ -210,7 +211,7 @@ jsf.validation.target=Target jsf.validation.updated=Updated validation {0} to {1}. # as a verb jsf.CopyTrans.run=Copy Translations -jsf.CopyTrans.cancel=Cancel Translation Copy +jsf.CopyTrans.cancel=Stop copying translations # as a noun (the CopyTrans feature) jsf.CopyTrans=Copy Translations jsf.Copytrans.message="Copy Translations" attempts to reuse translations that have been entered in Zanata by matching them with untranslated strings in your project/version. Consequently, "Copy Translations" is best used before translation and review work is initiated on a project. @@ -387,6 +388,7 @@ jsf.group.RemoveVersion.sr.label=Remove jsf.group.RemoveVersion.title=Remove Version jsf.group.RemoveMaintainer.sr.label=Remove jsf.group.RemoveMaintainer.title=Remove Maintainer +jsf.group.ArchivedVersionNotIncluded=This project-version is archived and will not be included. #------ [home] > Projects > [project-id] > Copy Trans Options ------ @@ -442,10 +444,11 @@ jsf.iteration.CopyTrans.Help.translated=Translation will be reused and marked as jsf.Start=Start jsf.iteration.CopyTrans.NoDocuments=There are no documents in this project version. jsf.iteration.CopyTrans.Started="Copy Translations" started. -jsf.iteration.CopyTrans.Completed="Copy Translations" completed. +jsf.iteration.CopyTrans.Completed="Copy Translations" for {0} [{1}] is completed. jsf.iteration.CopyTrans.Cancelled="Copy Translations" cancelled. jsf.iteration.CopyTransOpts.tooltip=Help: Set this version's "Copy Translations" settings. jsf.iteration.tooltip.readonly=This version is currently read only +jsf.iteration.tooltip.obsolete=This version is currently archived #------ [home] > Projects > [project-id] > Settings > Languages @@ -623,7 +626,7 @@ jsf.generatezip.ProgressLabel=Processing {0} of {1} jsf.iteration.files.WhyCantITranslate=Why can't I translate? jsf.iteration.files.translateDenied.NotLoggedIn=You are not logged In. jsf.iteration.files.translateDenied.VersionIsReadOnly=This project version is Read-Only. -jsf.iteration.files.translateDenied.VersionIsObsolete=This project version is Obsolete. +jsf.iteration.files.translateDenied.VersionIsObsolete=This project version is Archived. ! {0} is a language name jsf.iteration.files.translateDenied.UserNotTranslatorInLanguageTeam=You are a not translator of the {0} language team. ! {0} is a list of user roles @@ -635,9 +638,23 @@ jsf.iteration.NoDocumentInVersion=No documents in this version jsf.iteration.NoLanguagesInVersion=No language in this version jsf.iteration.TranslateOnline=Translate Online jsf.iteration.ViewOnline=View Online - - - +jsf.iteration.mergeTrans.header=Merge translations from an existing version +jsf.iteration.mergeTrans=Merge Translations +jsf.iteration.mergeTrans.description1=All matching translated/approved translations from the source version will be copied to the target version. +jsf.iteration.mergeTrans.description2=If there is an existing translated/approved translation, the newer translation will be used. +jsf.iteration.mergeTrans.toVersion.label=To (Target) +jsf.iteration.mergeTrans.fromVersion.label=From (Source) +jsf.iteration.mergeTrans.keepExistingTranslated=Keep existing translated/approved translations +jsf.iteration.mergeTrans.keepExistingTranslated.Description=Do not replace existing translated/approved translations with newer translations from this source. +jsf.iteration.mergeTrans.completed.message=Merge translations from {0} [{1}] to {2} [{3}] is completed. +jsf.iteration.mergeTrans.progress.message=Processed translation {0} of {1} +jsf.iteration.mergeTrans.cancel.message=Cancelled merge translations from {0} [{1}] to {2} [{3}]. +jsf.iteration.mergeTrans.hasCopyActionRunning=Another copy process is in progress for this version. Please try again after it is completed. +jsf.iteration.mergeTrans.chooseAnotherProject=Choose another project +jsf.iteration.mergeTrans.noSourceAndTarget=No source version or target version. +jsf.iteration.mergeTrans.cancelMerge=Cancel merge translations +jsf.iteration.mergeTrans.noVersionToMerge.heading=No versions available in this project to merge from. +jsf.iteration.mergeTrans.noVersionToMerge.description=Please choose another project. #------ [home] > Projects > [project-id] > [version-id] > Settings ------ jsf.upload.UploadNewDocuments=Upload new documents @@ -891,6 +908,9 @@ jsf.AccountDetails=Account Details jsf.Profile=Profile jsf.NewUser.Label=New User +#-------[home] > My Profile > User Profile +jsf.Profile.User=User Profile + #------ [home] > My Profile > Identities ------ #------ [home] > My Profile > Identities > Add New Identity ------ @@ -1265,7 +1285,7 @@ jsf.email.group.maintainer.ReceivedReason=You are maintainer in group "{0}" jsf.email.languageteam.permission.Subject=Your permissions in language team "{0}" have changed jsf.email.languageteam.permission.ReceivedReason=You are a team member in the "{0}" language team jsf.email.languageteam.permission.DearName=Dear {0}, -jsf.email.languageteam.permission.howToReply=Please do not reply to this system generated email. For any questions, please contact your team coordinator. +jsf.email.languageteam.permission.howToReply=Please do not reply to this system generated email. For any questions, please log in and contact your team coordinator. jsf.email.languageteam.permission.Changed={0} has changed your permissions in language team "{1}". jsf.email.languageteam.permission.old.prefix=You were: jsf.email.languageteam.permission.old.notInTeam=not a team member. diff --git a/zanata-war/src/main/resources/messages_br.properties b/zanata-war/src/main/resources/messages_br.properties index 1419866638..da938b04fb 100644 --- a/zanata-war/src/main/resources/messages_br.properties +++ b/zanata-war/src/main/resources/messages_br.properties @@ -321,7 +321,6 @@ jsf.iteration.files.Path=Treug jsf.iteration.files.Filter.title=Sila\u00F1 dre anv an teulio\u00F9 jsf.Upload.Label=Kas an droidigezh jsf.iteration.files.Merge=Touezia\u00F1 -jsf.iteration.files.MergeCheckbox.Title=Enrollet e vo an troidigezhio\u00F9 hizivaet ma'z eo gweredekaet. A re all a chomo evel m'emaint. jsf.iteration.files.Download=Pellgarga\u00F1 jsf.iteration.files.dotpot=.pot jsf.iteration.files.dotofflinepot=.pot ezlinenn @@ -332,7 +331,6 @@ jsf.iteration.files.dotofflinepo=.po ezlinenn jsf.iteration.files.dotofflinepo.description=Mentrezh po arbennik hag a arver msgctx evit kadavi\u00F1 an naoudi Zanata. jsf.iteration.files.dotofflinepo.purpose=Azgoulennet eo ar mentrezh-ma\u00F1 gant Zanata pa vez karget restro\u00F9 po evit un teul ne oa ket e mentrezh po da genta\u00F1. jsf.iteration.files.ConfirmDocDeletion=Ha sur oc'h e fell deoc'h dilemel tarzh ar restr-ma\u00F1 ? -jsf.iteration.files.ProcessDlgTitle=O keweria\u00F1 restro\u00F9 ar raktres... jsf.iteration.files.UpdateDocument=Hizivaat an teul-ma\u00F1 jsf.iteration.files.DeleteDocument=Dilemel an teul-ma\u00F1 jsf.iteration.files.DownloadDocument=Pellgarga\u00F1 an teul-ma\u00F1 [{0}] @@ -350,7 +348,6 @@ jsf.iteration.files.DownloadTranslated=Pellgarga\u00F1 [{0}] bet troet jsf.iteration.files.DownloadAll=Pellgarga\u00F1 pep tra (zip) jsf.iteration.files.DownloadAllOfflinePo=Pellgarga\u00F1 pep tra evit un droidigezh ezlinenn jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=Rizh ar raktres n'eo ket bet arventennet evit an arredoadur-ma\u00F1. Kit e darempred gant ardead ar raktres. -jsf.iteration.files.ConfirmDownloadAllFiles=Ema\u00F1 ho pellgargadur o vont da veza\u00F1 prientet hag an dra-se a c'hellfe padout un tammig amzer. Mat eo ? jsf.iteration.files.WhyCantITranslate=Perak n'hallan ket trei\u00F1 ? jsf.iteration.files.translateDenied.NotLoggedIn=N'oc'h ket kennasket ken. jsf.iteration.files.translateDenied.VersionIsReadOnly=Ar raktres-ma\u00F1 zo e lenn hepken. @@ -448,7 +445,6 @@ jsf.register.LogIn.label=En em gennaska\u00F1 jsf.ForgotYourPassword=Ankouaet hoc'h eus ho ker-tremen ? jsf.ResetPassword=Adderaouekaat ar ger-tremen jsf.SubmitRequest=Kinnig ur goulenn -jsf.ResetYourPassword=Adderaouekaat ho ker-tremen jsf.NewPassword=Ger-tremen nevez jsf.OldPassword=Ger-tremen kozh jsf.ChangePassword=Kemma\u00F1 ar ger-tremen @@ -457,12 +453,7 @@ jsf.login.WithZanata.label=En em gennaska\u00F1 gant hoc'h anv arveriad jsf.login.DontHaveAnAccount.label=Ur gont hoc'h eus ? jsf.ActivateAccount=Gweredekaat ar gont jsf.ValidateEmail=Talvoudekaat ar postel -jsf.InactiveAccount=Kont dizoberiant -jsf.inactiveaccount.PleaseSelectOne=N'eo ket bet gweredekaet ho kont evit poent. Mar plij, dibabit unan eus an dibarzhio\u00F9 ama\u00F1 dindan \: -jsf.ResendActivationEmail=Kas ar postel gweredekaat en-dro jsf.or=PE -jsf.inactiveaccount.UpdateAndResend=Hizivaat ar chomlec'h postel ha kas ar postel gweredekaat en-dro \: -jsf.UpdateEmail=Hizivaat ho chomlec'h postel jsf.InvalidActivationKey=Alc'hwez gweredekaat didalvoudek jsf.Error=Fazi jsf.ErrorTitle=Digarezit @@ -528,10 +519,6 @@ jsf.CreateRole=Kroui\u00F1 ur roll jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=Ha sur oc'h e fell deoc'h dilemel ar roll-ma\u00F1 ? N'hall ket beza\u00F1 disc'hraet. jsf.Role=Roll jsf.RoleDetails=Munudo\u00F9 ar roll -jsf.EnabledByDefault=Gweredekaet dre ziouer -jsf.AreYouSureYouWishToEnableThisLanguage=Ha sur oc'h e fell deoc'h gweredekaat ar yezh-ma\u00F1 ? -jsf.AreYouSureYouWishToDisableThisLanguage=Ha fellout a ra deoc'h diweredekaat ar yezh-ma\u00F1 ? -jsf.TeamMembers=Izili ar skipailh jsf.language.validation.ReplaceUnderscores=Amsavi\u00F1 i jsf.language.validation.Underscores="_" a zlefe beza\u00F1 amsavet gant "/" jsf.CountryCode=Boneg ar vro diff --git a/zanata-war/src/main/resources/messages_cs.properties b/zanata-war/src/main/resources/messages_cs.properties index 724c51f928..ff51ee4dcc 100644 --- a/zanata-war/src/main/resources/messages_cs.properties +++ b/zanata-war/src/main/resources/messages_cs.properties @@ -424,7 +424,6 @@ jsf.iteration.files.Filter.title=Filtrovat podle n\u00E1zvu dokumentu jsf.Upload.Label=Nahr\u00E1t p\u0159eklad jsf.iteration.files.Merge=Slou\u010Dit jsf.iteration.files.Merge.title=Je-li zatr\u017Eeno, sou\u010Dasn\u00E9 \u00FAdaje budou slou\u010Deny s nahran\u00FDm dokumentem. V opa\u010Dn\u00E9m p\u0159\u00EDpad\u011B budou \u00FAdaje nahran\u00FDm dokumentem potla\u010Deny. -jsf.iteration.files.MergeCheckbox.Title=Je-li zatr\u017Eeno, aktualizovan\u00E9 p\u0159eklady budou zaps\u00E1ny, v\u0161e ostatn\u00ED z\u016Fstane nezm\u011Bn\u011Bno. jsf.iteration.files.Download=St\u00E1hnout jsf.iteration.files.dotpot=.pot jsf.iteration.files.dotofflinepot=offline .pot @@ -436,13 +435,11 @@ jsf.iteration.files.dotofflinepo.description=Zvl\u00E1\u0161tn\u00ED form\u00E1t jsf.iteration.files.dotofflinepo.purpose=Tento form\u00E1t vy\u017Eaduje Zanata p\u0159i nahr\u00E1v\u00E1n\u00ED p\u0159eklad\u016F po pro dokument, kter\u00FD byl p\u016Fvodn\u011B ve form\u00E1tu neodpov\u00EDdaj\u00EDc\u00EDm form\u00E1tu po. jsf.iteration.files.ConfirmDocDeletion=Opravdu chcete odstranit tento zdrojov\u00FD soubor? jsf.iteration.files.DocumentDeleted=Dokument \u00FAsp\u011B\u0161n\u011B smaz\u00E1n. -jsf.iteration.files.ProcessDlgTitle=Zpracov\u00E1n\u00ED soubor\u016F projektu... jsf.iteration.files.UpdateDocument=Aktualizovat tento dokument jsf.iteration.files.DeleteDocument=Smazat tento dokument jsf.iteration.files.DownloadDocument=St\u00E1hnout dokument [{0}] jsf.iteration.files.UploadNewSourceDocument=Nahr\u00E1t nov\u00FD zdrojov\u00FD dokument jsf.iteration.files.FilenameWithSemicolonNotSupported=Zanata nepodporuje n\u00E1zvy soubor\u016F obsahuj\u00EDc\u00ED st\u0159edn\u00EDk. -jsf.SupportedUploadFormats=Podporovan\u00E9 typy\: .pot .dtd .txt .html .htm .odt .odp .ods .odg .idml .srt .vtt. .sub .sbt jsf.SourceLanguage=Jazyk zdroje jsf.iteration.files.DocumentPath=Cesta k dokumentu jsf.iteration.files.CustomParams=Vlastn\u00ED parametry parsov\u00E1n\u00ED @@ -456,8 +453,6 @@ jsf.iteration.files.DownloadAll=St\u00E1hnout v\u0161e (zip) jsf.iteration.files.DownloadAllOfflinePo=St\u00E1hnout v\u0161e pro p\u0159eklad bez p\u0159ipojen\u00ED jsf.iteration.files.DownloadAllFiles.ProjectTypeNotAllowed=Typ projektu mus\u00ED b\u00FDt nastaven na "Gettext" nebo "Podir". Kontaktujte vedouc\u00EDho projektu. jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=Typ projektu nebyl pro toto opakov\u00E1n\u00ED zad\u00E1n. Kontaktujte vedouc\u00EDho projektu. -jsf.iteration.files.ConfirmDownloadAllFiles=Sta\u017Een\u00ED bude pro v\u00E1s p\u0159ipraveno, jeho dokon\u010Den\u00ED m\u016F\u017Ee trvat n\u011Bkolik m\u00E1lo minut. Je to v po\u0159\u00E1dku? -jsf.generatezip.ProgressLabel={0} z {1} jsf.iteration.files.WhyCantITranslate=Pro\u010D nelze p\u0159ekl\u00E1dat? jsf.iteration.files.translateDenied.NotLoggedIn=Nejste p\u0159ihl\u00E1\u0161en(a). jsf.iteration.files.translateDenied.VersionIsReadOnly=Tato verze projektu je pouze pro \u010Dten\u00ED. @@ -611,7 +606,6 @@ jsf.register.LogIn.label=P\u0159ihl\u00E1sit se jsf.ForgotYourPassword=Zapomn\u011Bli jste sv\u00E9 heslo? jsf.ResetPassword=Znovu nastavit heslo jsf.SubmitRequest=Odeslat \u017E\u00E1dost -jsf.ResetYourPassword=Nastavte znovu sv\u00E9 heslo jsf.NewPassword=Nov\u00E9 heslo jsf.OldPassword=Star\u00E9 heslo jsf.ChangePassword=Zm\u011Bnit heslo @@ -622,12 +616,7 @@ jsf.login.OrLoginUsing.label=nebo se p\u0159ihlaste pou\u017Eit\u00EDm jsf.UsernameNotAvailable=U\u017Eivatel "{0}" nen\u00ED k dispozici jsf.ActivateAccount=Aktivovat \u00FA\u010Det jsf.ValidateEmail=Ov\u011B\u0159it e-mail -jsf.InactiveAccount=Neaktivn\u00ED \u00FA\u010Det -jsf.inactiveaccount.PleaseSelectOne=V\u00E1\u0161 \u00FA\u010Det nebyl dosud aktivov\u00E1n. Pros\u00EDme, vyberte jednu z n\u00E1sleduj\u00EDc\u00EDch mo\u017Enost\u00ED\: -jsf.ResendActivationEmail=Op\u011Bt zaslat aktiva\u010Dn\u00ED e-mail jsf.or=NEBO -jsf.inactiveaccount.UpdateAndResend=Zaktualizovat e-mailovou adresu a op\u011Bt zaslat aktiva\u010Dn\u00ED e-mail\: -jsf.UpdateEmail=Aktualizovat e-mailovou adresu jsf.InvalidActivationKey=Neplatn\u00FD aktiva\u010Dn\u00ED kl\u00ED\u010D jsf.ActivationLinkExpired=\u010Casov\u00E1 platnost aktiva\u010Dn\u00EDho odkazu vypr\u0161ela. Pros\u00EDme, p\u0159ihlaste se a klepn\u011Bte na "Znovu zaslat aktiva\u010Dn\u00ED kl\u00ED\u010D". jsf.Error=Chyba @@ -707,10 +696,6 @@ jsf.CreateRole=Vytvo\u0159it roli jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=Opravdu si p\u0159ejete smazat tuto roli? Tuto akci nelze vz\u00EDt zp\u011Bt. jsf.Role=Role jsf.RoleDetails=Podrobnosti o roli -jsf.EnabledByDefault=Standardn\u011B povoleno -jsf.AreYouSureYouWishToEnableThisLanguage=Opravdu si p\u0159ejete povolit tento jazyk? -jsf.AreYouSureYouWishToDisableThisLanguage=Opravdu si p\u0159ejete zak\u00E1zat tento projekt? -jsf.TeamMembers=\u010Clenov\u00E9 t\u00FDmu jsf.language.validation.ReplaceUnderscores=Nahradit v\u0161echny. jsf.language.validation.Underscores=Podr\u017E\u00EDtka by m\u011Bla b\u00FDt nahrazena poml\u010Dkami. jsf.CountryCode=K\u00F3d zem\u011B @@ -748,8 +733,6 @@ jsf.manageSearch.ErrorMessage=N\u011Bkter\u00E9 objekty nelze p\u0159eindexovat jsf.manageSearch.PleaseReindex=Pros\u00EDm, p\u0159eindexovat znovu, aby se zajistilo, \u017Ee index vyhled\u00E1v\u00E1n\u00ED je aktu\u00E1ln\u00ED. jsf.manageSearch.ProgressMessage=dokon\u010Deno {0} z {1} operac\u00ED jsf.manageSearch.CurrentTable=Zpracov\u00E1n\u00ED tabulky\: {0} -jsf.ManageSearch.ElapsedTime=\u010Cas trv\u00E1n\u00ED\: {0} -jsf.ManageSearch.RemainingTime=Zb\u00FDv\u00E1 (asi)\: {0} jsf.ManageSearch.Abort=Zru\u0161it jsf.TotalTranslators=P\u0159ekladatel\u00E9 jsf.TotalReviewers=Kontrolo\u0159i diff --git a/zanata-war/src/main/resources/messages_en_GB.properties b/zanata-war/src/main/resources/messages_en_GB.properties index 7167725ac0..1e89b29572 100644 --- a/zanata-war/src/main/resources/messages_en_GB.properties +++ b/zanata-war/src/main/resources/messages_en_GB.properties @@ -396,8 +396,6 @@ jsf.iteration.files.Filter.title=Filter by document name # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.Merge=Merge # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.iteration.files.MergeCheckbox.Title=When checked, updated translations will be written, leaving all others unchanged. -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.Download=Download # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.dotpot=.pot @@ -417,12 +415,8 @@ jsf.iteration.files.dotofflinepo.description=Special po format that uses msgctxt jsf.iteration.files.dotofflinepo.purpose=This format is required by Zanata when uploading po translations for a document that was originally in a non-po format. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.ConfirmDocDeletion=Are you sure you want to remove this source file? -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.iteration.files.ProcessDlgTitle=Processing project files... jsf.iteration.files.FilenameWithSemicolonNotSupported=Zanata does not support filenames that contain a semicolon. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.SupportedUploadFormats=Supported types\: .pot .dtd .txt .html .htm .odt .odp .ods .odg .idml .srt .vtt .sub .sbt -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.SourceLanguage=Source Language # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.DocumentPath=Document Path @@ -436,10 +430,6 @@ jsf.ConfigFileDisabledProjectNotSet=Disabled because maintainer has not set proj # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=The project type for this iteration has not been set. Contact the project maintainer. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.iteration.files.ConfirmDownloadAllFiles=Your download will be prepared and may take a few minutes to complete. Is this ok? -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.generatezip.ProgressLabel={0} of {1} -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.WhyCantITranslate=Why can't I translate? # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.iteration.files.translateDenied.NotLoggedIn=You are not logged In. @@ -548,8 +538,6 @@ jsf.ResetPassword=Reset Password # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.SubmitRequest=Submit Request # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.ResetYourPassword=Reset Your Password -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.NewPassword=New Password # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.OldPassword=Old Password @@ -564,18 +552,8 @@ jsf.ActivateAccount=Activate Account # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.ValidateEmail=Validate Email # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.InactiveAccount=Inactive account -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.inactiveaccount.PleaseSelectOne=Your account has not yet been activated. Please select one of the following options\: -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.ResendActivationEmail=Re-send activation email -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.or=OR # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.inactiveaccount.UpdateAndResend=Update email address and re-send activation email\: -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.UpdateEmail=Update email address -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.InvalidActivationKey=Invalid activation key # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.ActivationLinkExpired=Activation link expired. Please sign in and click "Re-send activation email". @@ -703,14 +681,6 @@ jsf.Role=Role # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.RoleDetails=Role Details # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.EnabledByDefault=Enabled by default -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.AreYouSureYouWishToEnableThisLanguage=Are you sure you wish to enable this language? -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.AreYouSureYouWishToDisableThisLanguage=Are you sure you wish to disable this language? -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.TeamMembers=Team Members -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.language.validation.ReplaceUnderscores=Replace them. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.language.validation.Underscores=Underscores should be replaced with dashes. @@ -781,10 +751,6 @@ jsf.manageSearch.ProgressMessage={0} of {1} operations complete # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.manageSearch.CurrentTable=Processing table\: {0} # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.ManageSearch.ElapsedTime=Running for\: {0} -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov -jsf.ManageSearch.RemainingTime=Remaining (approx)\: {0} -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.ManageSearch.Abort=Abort # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author irooskov jsf.Untranslated=Untranslated diff --git a/zanata-war/src/main/resources/messages_eo.properties b/zanata-war/src/main/resources/messages_eo.properties index e69de29bb2..1a171e2476 100644 --- a/zanata-war/src/main/resources/messages_eo.properties +++ b/zanata-war/src/main/resources/messages_eo.properties @@ -0,0 +1,10 @@ +jsf.Zanata=Zanata +jsf.Total=Entute +jsf.Add=Aldoni +# translation auto-copied from project Indic On-screen Keyboard, version f18-1, document iok +jsf.Cancel=Rezigni +jsf.Done=Bone +jsf.CreateGroup=Krei grupon +jsf.Close=Fermi +jsf.Delete=Forigi +jsf.Description=Priskribo diff --git a/zanata-war/src/main/resources/messages_fr.properties b/zanata-war/src/main/resources/messages_fr.properties index a7283ee6d6..e592f263bc 100644 --- a/zanata-war/src/main/resources/messages_fr.properties +++ b/zanata-war/src/main/resources/messages_fr.properties @@ -186,11 +186,11 @@ jsf.dashboard.settings.profileSettings.label=Param\u00E8tres de profil jsf.dashboard.settings.usernameCannotBeChanged.message=Votre nom d'utilisateur ne peut \u00EAtre modifi\u00E9 jsf.dashboard.settings.updateProfile.label=Modifier le profil jsf.dashboard.settings.languageTeams.label=\u00C9quipes de langues -jsf.dashboard.settings.leaveLanguageTeam.confirm.message=\u00CAtes-vous s\u00FBr(e) de vouloir quitter l'\u00E9quipe {0}\u00A0? +jsf.dashboard.settings.leaveLanguageTeam.confirm.message=Voulez-vous vraiment quitter l\u2019\u00E9quipe {0}\u00A0? jsf.dashboard.settings.clientSettings.label=Param\u00E8tres du client jsf.dashboard.settings.apiKey.label=Cl\u00E9 API jsf.dashboard.settings.generateNewApiKey.label=G\u00E9n\u00E9rer une nouvelle cl\u00E9 d'API -jsf.dashboard.settings.leaveLangTeam.message=Vous avez quitt\u00E9 l'\u00E9quipe de la langue {0} +jsf.dashboard.settings.leaveLangTeam.message=Vous avez quitt\u00E9 l\u2019\u00E9quipe de la langue {0} jsf.dashboard.settings.joinLangTeam.message=Rejoindre une \u00E9quipe de langue jsf.dashboard.settings.clientConfigHelp.message=Aide\u00A0\: Configuration du client jsf.dashboard.settings.mavenClientConfigHelp.message=Aide\u00A0\: Configuration du greffon Maven @@ -325,7 +325,7 @@ jsf.copyVersion.label=Copie \u00E0 partir de la version pr\u00E9c\u00E9dente jsf.copyVersion.processedDocuments=Traitement du document {0} de {1} jsf.copyVersion.versionSettingsDisabled=Le r\u00E9glage des options est temporairement d\u00E9sactiv\u00E9 en raison de la copie de la version. jsf.copyVersion.processedDocumentsAndPercent=Traitement du document {0} de {1} - {2}% -jsf.copyVersion.cancel.confirm=\u00CAtes-vous s\u00FBr de vouloir arr\u00EAter ce processus de copie\u00A0? Cela pourrait laisser cette version dans l'\u00E9tat \u00AB\u00A0lecture seule\u00A0\u00BB (peut \u00EAtre mis \u00E0 jour par les mainteneurs du projet dans la page \u00AB\u00A0R\u00E9glages\u00A0\u00BB) +jsf.copyVersion.cancel.confirm=Voulez-vous vraiment arr\u00EAter ce processus de copie\u00A0? Cela pourrait laisser cette version dans l\u2019\u00E9tat \u00AB\u00A0lecture seule\u00A0\u00BB (peut \u00EAtre mis \u00E0 jour par les mainteneurs du projet dans la page \u00AB\u00A0R\u00E9glages\u00A0\u00BB) jsf.AddProjectMaintainer=Ajouter un mainteneur au projet jsf.AreYouSureYouWishToRemoveThisPersonAsProjectMaintainer=\u00CAtes-vous s\u00FBr de vouloir enlever cette personne de la liste des mainteneurs du projet\u00A0? jsf.AddGroupMaintainer=Nouveau mainteneur @@ -366,7 +366,7 @@ jsf.iteration.archive.Message=Cela d\u00E9sactivera cette version et la sortira jsf.iteration.unarchive.Message=Cela fixera l'\u00E9tat de la version \u00E0 \u00AB\u00A0active\u00BB et la rendra visible dans la liste des projets ouverts au public. jsf.iteration.readonly.Message=\u00AB\u00A0Lecture seul\u00A0\u00BB emp\u00EAche l'entr\u00E9e de traductions. Cette version sera toujours visible au public, mais aucune nouvelle traduction en pourra y \u00EAtre ajout\u00E9e. jsf.iteration.writable.Message=Cette version est visible au public et de nouvelles traductions peuvent y \u00EAtre ajout\u00E9es. -jsf.iteration.status.updated=Mise \u00E0 jour de l'\u00E9tat de la version \u00AB\u00A0{0}\u00A0\u00BB. +jsf.iteration.status.updated=Mise \u00E0 jour de l\u2019\u00E9tat de la version \u00AB\u00A0{0}\u00A0\u00BB. jsf.iteration.LanguageRemoved=La langue \u00AB\u00A0{0}\u00A0\u00BB a \u00E9t\u00E9 supprim\u00E9e de cette version. jsf.iteration.LanguageAdded=La langue \u00AB\u00A0{0}\u00A0\u00BB a \u00E9t\u00E9 ajout\u00E9e \u00E0 cette version. jsf.iteration.requireReview.enabled=Revue des traductions activ\u00E9e @@ -451,7 +451,6 @@ jsf.iteration.files.Path=Chemin jsf.iteration.files.Filter.title=Filtrer par nom de document jsf.Upload.Label=T\u00E9l\u00E9verser la traduction jsf.iteration.files.Merge=Fusionner -jsf.iteration.files.MergeCheckbox.Title=Si coch\u00E9, les traductions mises \u00E0 jour seront \u00E9crites, en laissant toutes les autres inchang\u00E9es. # translation auto-copied from project CFSE, version sam-1.2, document app, author samfreemanz jsf.iteration.files.Download=T\u00E9l\u00E9charger jsf.iteration.files.dotpot=.pot @@ -463,13 +462,11 @@ jsf.iteration.files.dotofflinepo=.po hors ligne jsf.iteration.files.dotofflinepo.description=Format \u00AB\u00A0po\u00A0\u00BB sp\u00E9cial utilisant \u00AB\u00A0msgctxt\u00A0\u00BB pour stocker les identifiants Zanata. jsf.iteration.files.dotofflinepo.purpose=Ce format est requis par Zanata quand on t\u00E9l\u00E9verse des traductions de .po pour un document qui n'\u00E9tait pas \u00E0 l'origine dans le format .po jsf.iteration.files.ConfirmDocDeletion=\u00CAtes-vous s\u00FBr de vouloir supprimer ce fichier source\u00A0? -jsf.iteration.files.ProcessDlgTitle=Traitement des fichiers du projet... jsf.iteration.files.UpdateDocument=Mise \u00E0 jour de ce document jsf.iteration.files.DeleteDocument=Supprimer ce document jsf.iteration.files.DownloadDocument=T\u00E9l\u00E9charger ce document [{0}] jsf.iteration.files.UploadNewSourceDocument=T\u00E9l\u00E9verser un nouveau document source jsf.iteration.files.FilenameWithSemicolonNotSupported=Zanata ne prend pas en charge des noms de fichiers comportant un point-virgule. -jsf.SupportedUploadFormats=Types pris en charge\u00A0\: .pot, .dtd, .txt, .html, .htm, .odt, .odp, .ods, .odg, .idml, .srt, .vtt, .sub, .sbt jsf.SourceLanguage=Langue source jsf.iteration.files.DocumentPath=Chemin vers document jsf.iteration.files.CustomParams=Param\u00E8tres d'analyse personnalis\u00E9s @@ -483,15 +480,13 @@ jsf.iteration.files.DownloadAll=T\u00E9l\u00E9chargement de la totalit\u00E9 (zi jsf.iteration.files.DownloadAllOfflinePo=T\u00E9l\u00E9chargement de la totalit\u00E9 pour traduction hors-ligne jsf.iteration.files.DownloadAllFiles.ProjectTypeNotAllowed=Le type du projet doit \u00EAtre fix\u00E9 \u00E0 \u00AB\u00A0Gettext\u00A0\u00BB ou \u00AB\u00A0Podir\u00A0\u00BB. jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=Le type du projet n'a pas \u00E9t\u00E9 d\u00E9fini pour cette phase. Contactez le mainteneur du projet. -jsf.iteration.files.ConfirmDownloadAllFiles=Votre t\u00E9l\u00E9chargement est en cours de pr\u00E9paration\u00A0; sa compl\u00E9tion peut prendre quelques minutes. \u00CAtes-vous d'accord\u00A0? -jsf.generatezip.ProgressLabel={0} de {1} jsf.iteration.files.WhyCantITranslate=Pourquoi ne puis-je pas traduire\u00A0? jsf.iteration.files.translateDenied.NotLoggedIn=Vous n'\u00EAtes pas connect\u00E9. jsf.iteration.files.translateDenied.VersionIsReadOnly=La version de ce projet est en lecture seule. jsf.iteration.files.translateDenied.VersionIsObsolete=La version de ce projet est obsol\u00E8te. -jsf.iteration.files.translateDenied.UserNotTranslatorInLanguageTeam=Vous n'\u00EAtes pas traducteur de l'\u00E9quipe de la langue {0}. +jsf.iteration.files.translateDenied.UserNotTranslatorInLanguageTeam=Vous n\u2019\u00EAtes pas traducteur de l\u2019\u00E9quipe de la langue {0}. jsf.iteration.files.translateDenied.UserNotInProjectRole=Vous devez avoir ces r\u00F4les d'utilisateur pour traduire ce projet\u00A0\: {0} -jsf.TranslationContainsError=La traduction \u00AB\u00A0{0}\u00A0\u00BB comporte l'erreur\u00A0\:\n{1} +jsf.TranslationContainsError=La traduction \u00AB\u00A0{0}\u00A0\u00BB comporte l\u2019erreur\u00A0\:\n{1} jsf.iteration.tooltip.VersionSettings=Param\u00E8tres de version jsf.iteration.NoDocumentInVersion=Aucun document dans cette version jsf.iteration.NoLanguagesInVersion=Aucune langue dans cette version @@ -528,7 +523,7 @@ jsf.upload.SessionTimedOut=Votre session a expir\u00E9. Connectez-vous \u00E0 no jsf.upload.ServerStoppedResponding=Certains fichiers ne peuvent pas \u00EAtre t\u00E9l\u00E9vers\u00E9s. Le serveur ne r\u00E9pond plus. jsf.upload.NotLoggedIn=Vous n'\u00EAtes pas connect\u00E9. Ouvrez un autre onglet ou fen\u00EAtre pour vous connecter, puis essayez \u00E0 nouveau. jsf.upload.UploadInProgress=Vous avez d\u00E9j\u00E0 un t\u00E9l\u00E9versement en cours. Attendez sa fin avant d'en tenter un autre. Les t\u00E9l\u00E9versements peuvent prendre jusqu'\u00E0 5 minutes pour finir le processus. -jsf.upload.ErrorWhileChecking=Obtention d'un erreur pendant la v\u00E9rification de la possibilit\u00E9 de t\u00E9l\u00E9versement\u00A0\: {error}. Si l'erreur persiste, veuillez la rapporter en utilisant le lien \u00AB\u00A0Rapporter un probl\u00E8me\u00A0\u00BB au bas de la page. +jsf.upload.ErrorWhileChecking=Obtention d\u2019un erreur pendant la v\u00E9rification de la possibilit\u00E9 de t\u00E9l\u00E9versement\u00A0\: {error}. Si l\u2019erreur persiste, veuillez la rapporter en utilisant le lien \u00AB\u00A0Rapporter un probl\u00E8me\u00A0\u00BB au bas de la page. jsf.upload.UploadedBytesExceedFileSize=Le nombre d'octets t\u00E9l\u00E9vers\u00E9s d\u00E9passe la taille du fichier jsf.NoGroups=Aucun groupe jsf.groups.ShowActiveGroups=Montrer les groupes actifs @@ -556,7 +551,7 @@ jsf.UnArchiveThisGroup=Sortir ce groupe des archives jsf.MaintainerRemoveFromGroup=Le mainteneur \u00AB\u00A0{0}\u00A0\u00BB a \u00E9t\u00E9 supprim\u00E9 du groupe. jsf.MaintainerAddedToGroup=Le mainteneur \u00AB\u00A0{0}\u00A0\u00BB a \u00E9t\u00E9 ajout\u00E9 au groupe. jsf.InvalidUsername=Nom d'utilisateur invalide. -jsf.UserIsAMaintainer=L'utilisateur \u00AB\u00A0{0}\u00A0\u00BB est d\u00E9j\u00E0 un mainteneur du groupe. +jsf.UserIsAMaintainer=L\u2019utilisateur \u00AB\u00A0{0}\u00A0\u00BB est d\u00E9j\u00E0 un mainteneur du groupe. jsf.ProjectMissingLanguage={0} projet manquant pour cette langue jsf.ProjectsMissingLanguage={0} projets manquants pour cette langue jsf.LanguageMissingProject={0} langue manquante pour ce projet @@ -648,7 +643,6 @@ jsf.ForgotYourPassword=Mot de passe oubli\u00E9\u00A0? # translation auto-copied from project CFSE, version sam-1.2, document app, author samfreemanz jsf.ResetPassword=R\u00E9initialiser le mot de passe jsf.SubmitRequest=Soumettre une requ\u00EAte -jsf.ResetYourPassword=R\u00E9initialiser votre mot de passe # translation auto-copied from project Ovirt Engine Reports, version master, document jasperserver_messages jsf.NewPassword=Nouveau mot de passe # translation auto-copied from project oVirt Engine jrs Branding, version JRS-5.1, document jasperserver_messages @@ -662,12 +656,7 @@ jsf.login.OrLoginUsing.label=ou se connecter avec jsf.UsernameNotAvailable=Le nom d'utilisateur \u00AB\u00A0{0}\u00A0\u00BB n'est pas disponible jsf.ActivateAccount=Activer un compte jsf.ValidateEmail=Valider un e-mail -jsf.InactiveAccount=Compte inactif -jsf.inactiveaccount.PleaseSelectOne=Votre compte n'a pas encore \u00E9t\u00E9 activ\u00E9. Veuillez choisir une des options suivantes\u00A0\: -jsf.ResendActivationEmail=Envoyez \u00E0 nouveau un courrier d'activation jsf.or=OU -jsf.inactiveaccount.UpdateAndResend=mettez \u00E0 jour l'adresse \u00E9lectronique et r\u00E9exp\u00E9diez un courrier d'activation\u00A0\: -jsf.UpdateEmail=Mettre \u00E0 jour l'adresse \u00E9lectronique jsf.InvalidActivationKey=Cl\u00E9 d'activation invalide jsf.ActivationLinkExpired=Le lien d'activation a expir\u00E9. Veuillez vous inscrire et cliquer sur \u00AB\u00A0R\u00E9exp\u00E9dier un courrier d'activation\u00A0\u00BB # translation auto-copied from project oVirt, version rhevm-3.2, document frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/org.ovirt.engine.ui.uicompat.Constants, author croe @@ -758,10 +747,6 @@ jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=\u00CAtes-vous s\u jsf.Role=R\u00F4le # translation auto-copied from project CFSE, version sam-1.2, document app, author samfreemanz jsf.RoleDetails=D\u00E9tails du r\u00F4le -jsf.EnabledByDefault=Activ\u00E9 par d\u00E9faut -jsf.AreYouSureYouWishToEnableThisLanguage=\u00CAtes-vous s\u00FBr de vouloir activer cette langue\u00A0? -jsf.AreYouSureYouWishToDisableThisLanguage=\u00CAtes-vous s\u00FBr de vouloir d\u00E9sactiver cette langue\u00A0? -jsf.TeamMembers=Membres des \u00E9quipes jsf.language.validation.ReplaceUnderscores=Les remplacer. jsf.language.validation.Underscores=Les soulign\u00E9s doivent \u00EAtre remplac\u00E9s par des tirets. jsf.CountryCode=Code r\u00E9gion @@ -800,8 +785,7 @@ jsf.manageSearch.ErrorMessage=En raison d'une erreur, certains objets ne peuvent jsf.manageSearch.PleaseReindex=Veuillez r\u00E9-indexer \u00E0 nouveau pour vous assurer que l'index de recherche est \u00E0 jour. jsf.manageSearch.ProgressMessage={0} op\u00E9ration(s) sur {1} termin\u00E9e(s) jsf.manageSearch.CurrentTable=Table de traitement\u00A0\: {0} -jsf.ManageSearch.ElapsedTime=Ex\u00E9cution pour\u00A0\: {0} -jsf.ManageSearch.RemainingTime=Restant (approx)\u00A0\: {0} +jsf.ManageSearch.ElapsedTime=Temps \u00E9coul\u00E9 jsf.ManageSearch.Abort=Abandonner jsf.TotalTranslators=Traducteurs jsf.TotalReviewers=Relecteurs diff --git a/zanata-war/src/main/resources/messages_hu.properties b/zanata-war/src/main/resources/messages_hu.properties index 713ee13d02..cf6c73ccee 100644 --- a/zanata-war/src/main/resources/messages_hu.properties +++ b/zanata-war/src/main/resources/messages_hu.properties @@ -346,7 +346,6 @@ jsf.iteration.files.Path=El\u00E9r\u00E9si \u00FAt jsf.iteration.files.Filter.title=Sz\u0171r\u00E9s a dokumentum neve alapj\u00E1n jsf.Upload.Label=Ford\u00EDt\u00E1s friss\u00EDt\u00E9se jsf.iteration.files.Merge=Egyes\u00EDt\u00E9s -jsf.iteration.files.MergeCheckbox.Title=Ha megjel\u00F6lte, akkor a felt\u00F6lt\u00F6tt ford\u00EDt\u00E1sok ker\u00FClnek r\u00F6gz\u00EDt\u00E9sre, minden m\u00E1st pedig v\u00E1ltozatlanul hagynak. jsf.iteration.files.Download=Let\u00F6lt\u00E9s jsf.iteration.files.dotpot=.pot jsf.iteration.files.dotofflinepot=offline .pot @@ -357,7 +356,6 @@ jsf.iteration.files.dotofflinepo=offline .po jsf.iteration.files.dotofflinepo.description=Speci\u00E1lis po form\u00E1tum, amit msgctxt alkalmaz, hogy t\u00E1rolhassa a Zanata ID-ben. jsf.iteration.files.dotofflinepo.purpose=Ez a form\u00E1tum sz\u00FCks\u00E9ges a Zanata sz\u00E1m\u00E1ra, amikor po ford\u00EDt\u00E1sokat t\u00F6lt\u00FCnk fel egy dokumentumhoz, ami eredetileg nem po-form\u00E1tumban volt. jsf.iteration.files.ConfirmDocDeletion=Biztos benne, hogy elt\u00E1vol\u00EDtja ezt a forr\u00E1sf\u00E1jlt? -jsf.iteration.files.ProcessDlgTitle=Projekt f\u00E1jlok feldolgoz\u00E1sa... jsf.iteration.files.UpdateDocument=Dokumentum friss\u00EDt\u00E9se jsf.iteration.files.DeleteDocument=Dokumentum t\u00F6rl\u00E9se jsf.iteration.files.DownloadDocument=Dokumentum let\u00F6lt\u00E9se [{0}] @@ -375,7 +373,6 @@ jsf.iteration.files.DownloadTranslated=Ford\u00EDt\u00E1s let\u00F6lt\u00E9se [{ jsf.iteration.files.DownloadAll=\u00D6sszes let\u00F6lt\u00E9se (zip) jsf.iteration.files.DownloadAllOfflinePo=Az \u00F6sszes offline ford\u00EDt\u00E1s let\u00F6lt\u00E9se jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=A projekt t\u00EDpusa Ehhez az iter\u00E1ci\u00F3 nincs be\u00E1ll\u00EDtva.L\u00E9pjen kapcsolatba a a projekt fentart\u00F3j\u00E1val. -jsf.iteration.files.ConfirmDownloadAllFiles=\ A let\u00F6lt\u00E9s hamarosan k\u00E9sz lesz,a lt\u00F6lt\u00E9s eltarthat n\u00E9h\u00E1ny percig. Ez rendben van? jsf.iteration.files.WhyCantITranslate=Mi\u00E9rt nem ford\u00EDthatok? jsf.iteration.files.translateDenied.NotLoggedIn=Nincs bejelentkezve. jsf.iteration.files.translateDenied.VersionIsReadOnly=Ez a projekt verzi\u00F3 csak olvashat\u00F3. @@ -475,7 +472,6 @@ jsf.register.LogIn.label=Bejelentkez\u00E9s jsf.ForgotYourPassword=Elfelejtette a jelszav\u00E1t? jsf.ResetPassword=Jelsz\u00F3 vissza\u00E1ll\u00EDt\u00E1s jsf.SubmitRequest=K\u00E9r\u00E9s elk\u00FCld\u00E9se -jsf.ResetYourPassword=Jelsz\u00F3 vissza\u00E1ll\u00EDt\u00E1s kezdem\u00E9nyez\u00E9se jsf.NewPassword=\u00DAj jelsz\u00F3 jsf.OldPassword=R\u00E9gi jelsz\u00F3 jsf.ChangePassword=Jelsz\u00F3 v\u00E1ltoztat\u00E1s @@ -484,12 +480,7 @@ jsf.login.WithZanata.label=Jelentkezzen be a felhaszn\u00E1l\u00F3i nev\u00E9vel jsf.login.DontHaveAnAccount.label=Nincs azonos\u00EDt\u00F3ja? jsf.ActivateAccount=Aktiv\u00E1lja az azonos\u00EDt\u00F3t jsf.ValidateEmail=\u00C9rv\u00E9nyes\u00EDtse az Email fi\u00F3kj\u00E1t -jsf.InactiveAccount=Inakt\u00EDv azonos\u00EDt\u00F3 -jsf.inactiveaccount.PleaseSelectOne=Az azonos\u00EDt\u00F3ja m\u00E9g nem ker\u00FClt aktiv\u00E1l\u00E1sra. K\u00E9rem v\u00E1lasszon egyet a k\u00F6vetkez\u0151 lehet\u0151s\u00E9gekb\u0151l\: -jsf.ResendActivationEmail=Aktiv\u00E1ci\u00F3s E-mail \u00FAjrak\u00FCld\u00E9se jsf.or=VAGY -jsf.inactiveaccount.UpdateAndResend=Friss\u00EDtse fel az E-mail c\u00EDmet \u00E9s k\u00FCldje \u00FAjra az aktiv\u00E1ci\u00F3s levelet\: -jsf.UpdateEmail=E-mail fi\u00F3k friss\u00EDt\u00E9se jsf.InvalidActivationKey=\u00C9rv\u00E9nytele aktiv\u00E1ci\u00F3s kulcs # translation auto-copied from project oVirt, version master, document frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/org.ovirt.engine.ui.uicompat.Constants, author Mukodj Vacak jsf.Error=Hiba @@ -560,10 +551,6 @@ jsf.CreateRole=Szerepk\u00F6r k\u00E9sz\u00EDt\u00E9se jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=Biztos benne, hogy t\u00F6rli ezt a szerepk\u00F6rt? A m\u0171velet nem visszavonhat\u00F3. jsf.Role=Szerepk\u00F6r jsf.RoleDetails=Szerepk\u00F6r r\u00E9szletei -jsf.EnabledByDefault=Alap\u00E9rtelmez\u00E9sben enged\u00E9lyezett -jsf.AreYouSureYouWishToEnableThisLanguage=Biztos benne, hogy enged\u00E9lyezi ezt a nyelvet? -jsf.AreYouSureYouWishToDisableThisLanguage=Biztos benne, hogy kiiktatja ezt a nyelvet? -jsf.TeamMembers=Csapattagok jsf.language.validation.ReplaceUnderscores=Cser\u00E9lje le \u0151ket. jsf.language.validation.Underscores=Als\u00F3 al\u00E1h\u00FAz\u00E1s jeleket musz\u00E1j kicser\u00E9lni k\u00F6t\u0151jelekre. jsf.CountryCode=Orsz\u00E1gk\u00F3d diff --git a/zanata-war/src/main/resources/messages_it.properties b/zanata-war/src/main/resources/messages_it.properties index 68e946af54..21266ee3d8 100644 --- a/zanata-war/src/main/resources/messages_it.properties +++ b/zanata-war/src/main/resources/messages_it.properties @@ -285,7 +285,6 @@ jsf.iteration.files.NoFiles=No Files Available jsf.iteration.files.Filter.title=Filter by document name jsf.Upload.Label=Upload translation jsf.iteration.files.Merge=Merge -jsf.iteration.files.MergeCheckbox.Title=When checked, updated translations will be written, leaving all others unchanged. jsf.iteration.files.dotpot=.pot jsf.iteration.files.dotofflinepot=offline .pot jsf.iteration.files.dotofflinepot.description=Special pot format that uses msgctxt to store Zanata id. @@ -295,7 +294,6 @@ jsf.iteration.files.dotofflinepo=offline .po jsf.iteration.files.dotofflinepo.description=Special po format that uses msgctxt to store Zanata id. jsf.iteration.files.dotofflinepo.purpose=This format is required by Zanata when uploading po translations for a document that was originally in a non-po format. jsf.iteration.files.ConfirmDocDeletion=Are you sure you want to remove this source file? -jsf.iteration.files.ProcessDlgTitle=Processing project files... jsf.iteration.files.UpdateDocument=Update this document jsf.iteration.files.DeleteDocument=Delete this document jsf.iteration.files.DownloadDocument=Download this document [{0}] @@ -313,7 +311,6 @@ jsf.iteration.files.DownloadTranslated=Download translated [{0}] jsf.iteration.files.DownloadAll=Download All (zip) jsf.iteration.files.DownloadAllOfflinePo=Download All for Offline Translation jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=The project type for this iteration has not been set. Contact the project maintainer. -jsf.iteration.files.ConfirmDownloadAllFiles=Your download will be prepared and may take a few minutes to complete. Is this ok? jsf.iteration.files.WhyCantITranslate=Why can't I translate? jsf.iteration.files.translateDenied.NotLoggedIn=You are not logged In. jsf.iteration.files.translateDenied.VersionIsReadOnly=This project version is Read-Only. @@ -401,18 +398,12 @@ jsf.register.AlreadyHaveAccount.label=Already have an account? jsf.register.LogIn.label=Log In jsf.ForgotYourPassword=Forgot your password? jsf.SubmitRequest=Submit Request -jsf.ResetYourPassword=Reset Your Password jsf.login.openid=Open ID jsf.login.WithZanata.label=Log in with your username jsf.login.DontHaveAnAccount.label=Don't have an account? jsf.ActivateAccount=Activate Account jsf.ValidateEmail=Validate Email -jsf.InactiveAccount=Inactive account -jsf.inactiveaccount.PleaseSelectOne=Your account has not yet been activated. Please select one of the following options\: -jsf.ResendActivationEmail=Re-send activation email jsf.or=OR -jsf.inactiveaccount.UpdateAndResend=Update email address and re-send activation email\: -jsf.UpdateEmail=Update email address jsf.InvalidActivationKey=Invalid activation key jsf.ErrorTitle=We're sorry jsf.YouCanHelpUs=But you can help us fix it\! @@ -471,10 +462,6 @@ jsf.UserManager.delete.constraintViolation.error=This user cannot be removed fro jsf.AccountEnabled=Account enabled jsf.CreateRole=Create Role jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=Are you sure you wish to delete this role? This action cannot be undone. -jsf.EnabledByDefault=Enabled by default -jsf.AreYouSureYouWishToEnableThisLanguage=Are you sure you wish to enable this language? -jsf.AreYouSureYouWishToDisableThisLanguage=Are you sure you wish to disable this language? -jsf.TeamMembers=Team Members jsf.language.validation.ReplaceUnderscores=Replace them. jsf.language.validation.Underscores=Underscores should be replaced with dashes. jsf.CountryCode=Country Code diff --git a/zanata-war/src/main/resources/messages_ja.properties b/zanata-war/src/main/resources/messages_ja.properties index cc3a4c0dd7..9a14685a52 100644 --- a/zanata-war/src/main/resources/messages_ja.properties +++ b/zanata-war/src/main/resources/messages_ja.properties @@ -170,7 +170,6 @@ jsf.iteration.files.NoFiles=\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u30 jsf.iteration.files.Path=\u30D1\u30B9 jsf.iteration.files.Filter.title=\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u540D\u3067\u30D5\u30A3\u30EB\u30BF\u30FC jsf.iteration.files.Merge=\u30DE\u30FC\u30B8 -jsf.iteration.files.MergeCheckbox.Title=\u30C1\u30A7\u30C3\u30AF\u3059\u308B\u3068\u66F4\u65B0\u3055\u308C\u305F\u7FFB\u8A33\u304C\u66F8\u304D\u8FBC\u307E\u308C\u3001 \u305D\u308C\u4EE5\u5916\u306F\u5909\u66F4\u3055\u308C\u307E\u305B\u3093\u3002 # translation auto-copied from project CFSE, version sam-1.2, document app, author nnakakit jsf.iteration.files.Download=\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9 jsf.iteration.files.dotpot=.pot @@ -181,13 +180,11 @@ jsf.iteration.files.dotpo=.po jsf.iteration.files.dotofflinepo=\u30AA\u30D5\u30E9\u30A4\u30F3 .po jsf.iteration.files.dotofflinepo.description=Zanata id \u3092\u683C\u7D0D\u3067\u304D\u308B msgctxt \u3092\u4F7F\u7528\u3057\u305F\u7279\u6B8A\u306A po \u5F62\u5F0F\u3067\u3059\u3002 jsf.iteration.files.dotofflinepo.purpose=\u30AA\u30EA\u30B8\u30CA\u30EB\u3067\u306F po \u4EE5\u5916\u306E\u5F62\u5F0F\u3060\u3063\u305F\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E po \u7FFB\u8A33\u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3059\u308B\u969B\u306B Zanata \u3067\u5FC5\u8981\u306B\u306A\u308B\u5F62\u5F0F\u3067\u3059\u3002 -jsf.iteration.files.ProcessDlgTitle=\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30D5\u30A1\u30A4\u30EB\u306E\u51E6\u7406\u4E2D... jsf.SourceLanguage=\u30BD\u30FC\u30B9\u8A00\u8A9E jsf.iteration.files.DocumentPath=\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u30D1\u30B9 jsf.ConfigFileForOfflineTranslation=\u30AA\u30D5\u30E9\u30A4\u30F3\u7FFB\u8A33\u306E\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB jsf.ConfigFileDisabledProjectNotSet=\u30E1\u30F3\u30C6\u30CA\u30FC\u306B\u3088\u308A\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30BF\u30A4\u30D7\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u7121\u52B9\u306B\u306A\u308A\u307E\u3059\u3002 jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=\u3053\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30BF\u30A4\u30D7\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30E1\u30F3\u30C6\u30CA\u30FC\u306B\u9023\u7D61\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -jsf.iteration.files.ConfirmDownloadAllFiles=\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u306E\u6E96\u5099\u3092\u884C\u306A\u3046\u305F\u3081\u3001 \u5B8C\u4E86\u306B\u306F\u6570\u5206\u304B\u304B\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002 \u3088\u308D\u3057\u3044\u3067\u3059\u304B? jsf.iteration.files.WhyCantITranslate=\u306A\u305C\u7FFB\u8A33\u3067\u304D\u306A\u3044\u306E\u3067\u3059\u304B? jsf.iteration.files.translateDenied.NotLoggedIn=\u30E6\u30FC\u30B6\u30FC\u306F\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u3044\u307E\u305B\u3093\u3002 jsf.iteration.files.translateDenied.VersionIsReadOnly=\u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30D0\u30FC\u30B8\u30E7\u30F3\u306F\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u3067\u3059\u3002 @@ -255,7 +252,6 @@ jsf.ForgotYourPassword=\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5FD8\u308C\u3066\u3 # translation auto-copied from project CFSE, version sam-1.2, document app, author nnakakit jsf.ResetPassword=\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u30EA\u30BB\u30C3\u30C8 jsf.SubmitRequest=\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1 -jsf.ResetYourPassword=\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u30EA\u30BB\u30C3\u30C8 # translation auto-copied from project Ovirt Engine Reports, version master, document jasperserver_messages jsf.NewPassword=\u65B0\u3057\u3044\u30D1\u30B9\u30EF\u30FC\u30C9 jsf.OldPassword=\u53E4\u3044\u30D1\u30B9\u30EF\u30FC\u30C9 @@ -264,12 +260,7 @@ jsf.ChangePassword=\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u5909\u66F4 jsf.login.openid=Open ID jsf.ActivateAccount=\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30A2\u30AF\u30C6\u30A3\u30D9\u30FC\u30C8 jsf.ValidateEmail=\u30E1\u30FC\u30EB\u306E\u8A8D\u8A3C -jsf.InactiveAccount=\u975E\u30A2\u30AF\u30C6\u30A3\u30D6\u306A\u30A2\u30AB\u30A6\u30F3\u30C8 -jsf.inactiveaccount.PleaseSelectOne=\u30E6\u30FC\u30B6\u30FC\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u306F\u307E\u3060\u30A2\u30AF\u30C6\u30A3\u30D9\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 \u6B21\u306E\u3044\u305A\u308C\u304B\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002 -jsf.ResendActivationEmail=\u30A2\u30AF\u30C6\u30A3\u30D9\u30FC\u30B7\u30E7\u30F3\u30E1\u30FC\u30EB\u306E\u518D\u9001\u4FE1 jsf.or=\u307E\u305F\u306F -jsf.inactiveaccount.UpdateAndResend=\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u66F4\u65B0\u3057\u3066\u304B\u3089\u30A2\u30AF\u30C6\u30A3\u30D9\u30FC\u30B7\u30E7\u30F3\u30E1\u30FC\u30EB\u3092\u518D\u9001\u4FE1\u3059\u308B\: -jsf.UpdateEmail=\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306E\u66F4\u65B0 jsf.InvalidActivationKey=\u7121\u52B9\u306A\u30A2\u30AF\u30C6\u30A3\u30D9\u30FC\u30B7\u30E7\u30F3\u30AD\u30FC # translation auto-copied from project oVirt, version rhevm-3.2, document frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/org.ovirt.engine.ui.uicompat.Constants, author miey jsf.Error=\u30A8\u30E9\u30FC @@ -335,10 +326,6 @@ jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=\u672C\u5F53\u306B jsf.Role=\u30ED\u30FC\u30EB # translation auto-copied from project CFSE, version sam-1.2, document app, author nnakakit jsf.RoleDetails=\u30ED\u30FC\u30EB\u306E\u8A73\u7D30 -jsf.EnabledByDefault=\u30C7\u30D5\u30A9\u30EB\u30C8\u3067\u6709\u52B9 -jsf.AreYouSureYouWishToEnableThisLanguage=\u672C\u5F53\u306B\u3053\u306E\u8A00\u8A9E\u3092\u6709\u52B9\u306B\u3057\u3066\u3088\u3044\u3067\u3059\u304B? -jsf.AreYouSureYouWishToDisableThisLanguage=\u672C\u5F53\u306B\u3053\u306E\u8A00\u8A9E\u3092\u7121\u52B9\u306B\u3057\u3066\u3088\u3044\u3067\u3059\u304B? -jsf.TeamMembers=\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC jsf.language.validation.ReplaceUnderscores=\u7F6E\u63DB jsf.language.validation.Underscores=\u4E0B\u7DDA\u3067\u306F\u306A\u304F\u30C0\u30C3\u30B7\u30E5\u8A18\u53F7\u306B\u7F6E\u304D\u63DB\u3048\u3066\u304F\u3060\u3055\u3044\u3002 jsf.CountryCode=\u56FD\u30B3\u30FC\u30C9 diff --git a/zanata-war/src/main/resources/messages_pt_BR.properties b/zanata-war/src/main/resources/messages_pt_BR.properties index 5415727ad0..5a1a1afbee 100644 --- a/zanata-war/src/main/resources/messages_pt_BR.properties +++ b/zanata-war/src/main/resources/messages_pt_BR.properties @@ -382,7 +382,6 @@ jsf.iteration.files.Path=Caminho jsf.iteration.files.Filter.title=Filtro por nome do documento jsf.Upload.Label=Carregar tradu\u00E7\u00E3o jsf.iteration.files.Merge=Mesclar -jsf.iteration.files.MergeCheckbox.Title=Quando marcada, tradu\u00E7\u00F5es atualizadas ser\u00E3o escritas, todas as outras inalteradas. # translation auto-copied from project CFSE, version sam-1.2, document app jsf.iteration.files.Download=Baixar jsf.iteration.files.dotpot=.pot @@ -394,7 +393,6 @@ jsf.iteration.files.dotofflinepo=off-line .po jsf.iteration.files.dotofflinepo.description=Formato de po especial que usa msgctxt para armazenar o id do Zanata. jsf.iteration.files.dotofflinepo.purpose=Este formato \u00E9 requerido pelo Zanata quando carregar tradu\u00E7\u00F5es de po para um documento que foi originalmente em um formato n\u00E3o po. jsf.iteration.files.ConfirmDocDeletion=Tem certeza que deseja remover este arquivo de origem? -jsf.iteration.files.ProcessDlgTitle=Processando arquivos do projeto... jsf.iteration.files.UpdateDocument=Atualizar este documento jsf.iteration.files.DeleteDocument=Deletar este documento jsf.iteration.files.DownloadDocument=Baixar este documento [{0}] @@ -412,7 +410,6 @@ jsf.iteration.files.DownloadTranslated=Baixar traduzido [{0}] jsf.iteration.files.DownloadAll=Baixar Todos (zip) jsf.iteration.files.DownloadAllOfflinePo=Baixar Todos para Tradu\u00E7\u00E3o Off-line jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=O tipo de projeto para esta intera\u00E7\u00E3o n\u00E3o foi definido. Entre em contato com o mantenedor do projeto. -jsf.iteration.files.ConfirmDownloadAllFiles=Seu download ser\u00E1 preparado e pode levar alguns minutos para completar. Isto est\u00E1 ok? jsf.iteration.files.WhyCantITranslate=Por que eu n\u00E3o posso traduzir? jsf.iteration.files.translateDenied.NotLoggedIn=Voc\u00EA n\u00E3o esta conectado. jsf.iteration.files.translateDenied.VersionIsReadOnly=Esta vers\u00E3o de projeto \u00E9 Somente Leitura. @@ -520,7 +517,6 @@ jsf.ForgotYourPassword=Esqueceu sua senha? # translation auto-copied from project CFSE, version sam-1.2, document app jsf.ResetPassword=Redefinir Senha jsf.SubmitRequest=Enviar solicita\u00E7\u00E3o -jsf.ResetYourPassword=Resete sua senha. # translation auto-copied from project oVirt Engine jrs Branding, version JRS-5.1, document jasperserver_messages, author ldelima jsf.NewPassword=Nova Senha # translation auto-copied from project oVirt Engine jrs Branding, version JRS-5.1, document jasperserver_messages, author ldelima @@ -532,12 +528,7 @@ jsf.login.WithZanata.label=Entrar com seu nome de usu\u00E1rio jsf.login.DontHaveAnAccount.label=N\u00E3o tem uma conta? jsf.ActivateAccount=Ativar Conta jsf.ValidateEmail=Validar Email -jsf.InactiveAccount=Conta Inativa -jsf.inactiveaccount.PleaseSelectOne=Sua conta ainda n\u00E3o foi ativada. Por favor, selecione uma das seguintes op\u00E7\u00F5es\: -jsf.ResendActivationEmail=Re-enviar email de ativa\u00E7\u00E3o jsf.or=OU -jsf.inactiveaccount.UpdateAndResend=Atualizar o endere\u00E7o de e-mail e re-enviar email de ativa\u00E7\u00E3o\: -jsf.UpdateEmail=Atualizar endere\u00E7o de email jsf.InvalidActivationKey=Chave de ativa\u00E7\u00E3o inv\u00E1lida # translation auto-copied from project oVirt, version rhevm-3.2, document frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/org.ovirt.engine.ui.uicompat.Constants, author ldelima jsf.Error=Erro @@ -602,10 +593,6 @@ jsf.AccountEnabled=Conta ativada jsf.Role=Papel # translation auto-copied from project CFSE, version sam-1.2, document app jsf.RoleDetails=Detalhes do Papel -jsf.EnabledByDefault=Ativo por padr\u00E3o -jsf.AreYouSureYouWishToEnableThisLanguage=Tem certeza que voc\u00EA gostaria de ativar esse idioma? -jsf.AreYouSureYouWishToDisableThisLanguage=Tem certeza que voc\u00EA gostaria de desativar esse idioma? -jsf.TeamMembers=Membros do Time jsf.language.validation.Underscores=Underscores (_) devem ser substitu\u00EDdos por h\u00EDfens (-) jsf.CountryCode=C\u00F3digo do Pa\u00EDs jsf.LanguageCode=C\u00F3digo do idioma diff --git a/zanata-war/src/main/resources/messages_ru.properties b/zanata-war/src/main/resources/messages_ru.properties index e69de29bb2..1585d9e69d 100644 --- a/zanata-war/src/main/resources/messages_ru.properties +++ b/zanata-war/src/main/resources/messages_ru.properties @@ -0,0 +1,129 @@ +jsf.Active=\u0410\u043A\u0442\u0438\u0432\u043D\u043E +jsf.ReadOnly=\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u0435\u043D\u0438\u0435 +jsf.RecordNotFound=\u0417\u0430\u043F\u0438\u0441\u044C \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430 +jsf.DuplicatedRecord=\u041F\u043E\u0432\u0442\u043E\u0440\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C +jsf.AnotherUserChangedTheSameDataPleaseTryAgain=\u0414\u0440\u0443\u0433\u043E\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0438\u0437\u043C\u0435\u043D\u0438\u043B \u0442\u0435 \u0436\u0435 \u0434\u0430\u043D\u043D\u044B\u0435. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0441\u043D\u043E\u0432\u0430. +jsf.YouDoNotHavePermissionToAccessThisResource=\u0423 \u0412\u0430\u0441 \u043D\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u044F \u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F \u043A \u044D\u0442\u043E\u043C\u0443 \u0440\u0435\u0441\u0443\u0440\u0441\u0443. +jsf.YourSessionHasTimedOutPleaseTryAgain=\u0412\u0440\u0435\u043C\u044F \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0432\u044B\u0448\u043B\u043E. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043F\u043E\u0431\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0441\u043D\u043E\u0432\u0430. +jsf.Done=\u0413\u043E\u0442\u043E\u0432\u043E +jsf.CreateGroup=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u0443 +jsf.CreationDate=\u0414\u0430\u0442\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F +jsf.HomepageContent=\u0421\u043E\u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0435 \u0434\u043E\u043C\u0430\u0448\u043D\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B +jsf.NewGroup.Label=\u041D\u043E\u0432\u0430\u044F \u0433\u0440\u0443\u043F\u043F\u0430 +jsf.NewProject.Label=\u041D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 +jsf.projectType=\u0422\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 +jsf.project.projectType.Description=\u041F\u043E\u043C\u043E\u0449\u044C\:\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0438 \u0442\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 +jsf.iteration.projectType.Description=\u041F\u043E\u043C\u043E\u0449\u044C\:\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0438 \u0442\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 +jsf.projectType.NotSpecifiedBehaviour=\u0415\u0441\u043B\u0438 \u043D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D \u0442\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442 \u0442\u0438\u043F \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0433\u043E \u0435\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430. +jsf.projectType.detail.File=\u0414\u043B\u044F \u043F\u0440\u043E\u0441\u0442\u043E\u0433\u043E \u0442\u0435\u043A\u0441\u0442\u0430, Libre Office, InDesign, HTML, \u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043E\u0432 \u0438 \u0442.\u0434. +jsf.projectType.detail.Gettext=\u0414\u043B\u044F \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u043D\u044B\u0445 \u0441\u0442\u0440\u043E\u043A gettext. +jsf.projectType.detail.Podir=\u0414\u043B\u044F \u0441\u0442\u0440\u043E\u043A publican/docbook. +jsf.projectType.detail.Properties=\u0414\u043B\u044F \u0444\u0430\u0439\u043B\u043E\u0432 Java properties. +jsf.projectType.detail.Utf8Properties=\u0414\u043B\u044F UTF8-encoded Java properties. +jsf.projectType.detail.Xliff=\u0414\u043B\u044F supported XLIFF files. +jsf.projectType.detail.Xml=\u0414\u043B\u044F XML from the Zanata REST API. +jsf.projectType.detail.noSelection=\u041E\u043F\u0446\u0438\u044F \u0434\u043B\u044F \u0441\u0442\u0430\u0440\u044B\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432. +jsf.projectType.detail.noSelection.message=\u0412\u044B \u043D\u0435 \u0441\u043C\u043E\u0436\u0435\u0442\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C \u0438\u0441\u0445\u043E\u0434\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B \u0438\u0437 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0441 \u044D\u0442\u043E\u0439 \u043E\u043F\u0446\u0438\u0435\u0439, \u043F\u043E\u043A\u0430 \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u0435 \u0442\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0432 \u0412\u0430\u0448 \u0444\u0430\u0439\u043B \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438. +jsf.projectType.NoSelection=\u041D\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043E +jsf.Project=\u041F\u0440\u043E\u0435\u043A\u0442 +jsf.Versions=\u0412\u0435\u0440\u0441\u0438\u0438 +jsf.Projects=\u041F\u0440\u043E\u0435\u043A\u0442\u044B +jsf.Languages=\u042F\u0437\u044B\u043A\u0438 +jsf.More=\u0415\u0449\u0451 +jsf.ReportAProblem=\u0421\u043E\u043E\u0431\u0449\u0438\u0442\u044C \u043E \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0435 +jsf.KnownIssues=\u0418\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B +jsf.MyProfile=\u041C\u043E\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C +jsf.Signup=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F +jsf.Menu=\u041C\u0435\u043D\u044E +jsf.SearchProjects=\u041D\u0430\u0439\u0442\u0438 \u043F\u0440\u043E\u0435\u043A\u0442 +jsf.project.search.IncludeObsoleteTooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0430\u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u044B \u0432 \u043F\u043E\u0438\u0441\u043A +# translation auto-copied from project Indic On-screen Keyboard, version f20, document iok +jsf.About=\u041E \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0435 +jsf.AboutZanata=\u041E Zanata +jsf.Wiki=Wiki +jsf.Blog=\u0411\u043B\u043E\u0433 +jsf.IrcHelp=\u041F\u043E\u043C\u043E\u0449\u044C IRC +jsf.FAQ=\u0427\u0430\u0412\u043E +jsf.SiteMap=\u041A\u0430\u0440\u0442\u0430 \u0441\u0430\u0439\u0442\u0430 +jsf.RunningVersionInfo={0} {1} ({2}) +jsf.CopyrightNotice=Copyright &\#169; 2008-14 Red Hat, Inc +jsf.server.EditHomePage.label=\u041F\u0440\u0430\u0432\u043A\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B +jsf.server.EditHomePageCode.label=\u041F\u0440\u0430\u0432\u043A\u0430 \u043A\u043E\u0434\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B +jsf.activity.today.label=\u0421\u0435\u0433\u043E\u0434\u043D\u044F +jsf.activity.week.label=\u042D\u0442\u0430 \u043D\u0435\u0434\u0435\u043B\u044F +jsf.activity.month.label=\u042D\u0442\u043E\u0442 \u043C\u0435\u0441\u044F\u0446 +jsf.activity.wordsTranslated=\u0421\u043B\u043E\u0432 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u043E +jsf.activity.messagesTranslated=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u043E +jsf.activity.documentsTranslated=\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u043E +jsf.activity.messagesReviewed=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E +jsf.activity.documentsReviewed=\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E +jsf.activity.wordsReviewed=\u0421\u043B\u043E\u0432 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E +jsf.activity.hoursOfTranslation=\u0427\u0430\u0441\u043E\u0432 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 +jsf.Reviewed=\u041F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E +jsf.UploadedTranslations=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u044B +jsf.UploadedSource=\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0435 \u0438\u0441\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u044B +jsf.YourActivity=\u0412\u0430\u0448\u0430 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C +jsf.YourLastActivity=\u0412\u0430\u0448\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C +jsf.moreActivity=\u0435\u0449\u0451 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C +jsf.NoVersions=\u041D\u0435\u0442 \u0432\u0435\u0440\u0441\u0438\u0439 +jsf.createAVersion=\u0441\u043E\u0437\u0434\u0430\u0442\u044C \u0432\u0435\u0440\u0441\u0438\u044E +jsf.YouHaveNoActivity=\u0423 \u0412\u0430\u0441 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0442 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u0438. +jsf.NoActivityMessage=\u041A\u0430\u043A \u0412\u044B \u043D\u0430\u0447\u043D\u0451\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C Zanata, \u0412\u0430\u0448\u0430 \u0440\u0430\u0431\u043E\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u0430 \u0437\u0434\u0435\u0441\u044C. +jsf.dashboard.activity.title=\u0410\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C +jsf.dashboard.activity.translate.message=\u0412\u044B \u043F\u0435\u0440\u0435\u0432\u0435\u043B\u0438 {0} \u0441\u043B\u043E\u0432 \u0432 {2}, \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0432 \u043D\u0430 &\#8220;{4}&\#8221; +jsf.dashboard.activity.review.message=\u0412\u044B \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B\u0438 {0} \u0441\u043B\u043E\u0432 \u0432 {2}, \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0432 \u043D\u0430 &\#8220;{4}"&\#8221; +jsf.dashboard.activity.uploadSource.message=\u0412\u044B \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043B\u0438 \u0438\u0441\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u044B \u0438\u0437 {0} \u0441\u043B\u043E\u0432 \u0432 {2} +jsf.dashboard.activity.uploadTranslation.message=\u0412\u044B \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u044B \u0438\u0437 {0} \u0441\u043B\u043E\u0432 \u0432 {2} +jsf.dashboard.activity.lastTranslatedBy.message=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u043F\u0435\u0440\u0435\u0432\u043E\u0434 \u043E\u0442 {0} +jsf.dashboard.projects.title=\u041F\u0440\u043E\u0435\u043A\u0442\u044B +jsf.dashboard.projects.newProject.label=\u041D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 +jsf.dashboard.projects.createNewProject.label=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 +jsf.dashboard.projects.projectVersions.label=\u0412\u0435\u0440\u0441\u0438\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 +jsf.dashboard.projects.versions.label=\u0412\u0435\u0440\u0441\u0438\u0438 +jsf.dashboard.projects.translateOptions.label=\u041E\u043F\u0446\u0438\u0438 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 +jsf.dashboard.projects.allVersions.label=\u0412\u0441\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 +jsf.dashboard.projects.search.placeholder=\u041F\u043E\u0438\u0441\u043A \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 +jsf.dashboard.settings.tab.title=\u041E\u043F\u0446\u0438\u0438 +jsf.dashboard.settings.title=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F +jsf.dashboard.settings.account.tab.label=\u0410\u043A\u043A\u0430\u0443\u043D\u0442 +jsf.dashboard.settings.profile.tab.label=\u041F\u0440\u043E\u0444\u0438\u043B\u044C +jsf.dashboard.settings.languages.tab.label=\u042F\u0437\u044B\u043A\u0438 +jsf.dashboard.settings.client.tab.label=\u041A\u043B\u0438\u0435\u043D\u0442 +jsf.dashboard.settings.accountSettings.label=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u0430 +jsf.dashboard.settings.setPassword.label=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C +jsf.dashboard.settings.changePassword.label=\u0421\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C +jsf.dashboard.settings.connectedAccounts.label=\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0435 \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u044B +jsf.dashboard.settings.removeAccount.label=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0430\u043A\u043A\u0430\u0443\u043D\u0442 +jsf.dashboard.settings.addAccount.label=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0434\u0440\u0443\u0433\u043E\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 +jsf.dashboard.settings.mergeAccounts.label=\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0438\u0442\u044C \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u044B +jsf.dashboard.settings.profileSettings.label=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043F\u0440\u043E\u0444\u0438\u043B\u044F +jsf.dashboard.settings.usernameCannotBeChanged.message=\u0412\u0430\u0448\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043E +jsf.dashboard.settings.updateProfile.label=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C +jsf.dashboard.settings.languageTeams.label=\u041A\u043E\u043C\u0430\u043D\u0434\u044B \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 +jsf.dashboard.settings.leaveLanguageTeam.confirm.message=\u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u043E\u043A\u0438\u043D\u0443\u0442\u044C \u043A\u043E\u043C\u0430\u043D\u0434\u0443 {0}? +jsf.dashboard.settings.clientSettings.label=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 +jsf.dashboard.settings.apiKey.label=API Key +jsf.dashboard.settings.generateNewApiKey.label=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0439 API key +jsf.dashboard.settings.leaveLangTeam.message=\u0412\u044B \u043F\u043E\u043A\u0438\u043D\u0443\u043B\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 {0} +jsf.dashboard.settings.joinLangTeam.message=\u0412\u0441\u0442\u0443\u043F\u0438\u0442\u044C \u0432 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 +jsf.dashboard.settings.clientConfigHelp.message=\u041F\u043E\u043C\u043E\u0449\u044C\:\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 +jsf.dashboard.settings.mavenClientConfigHelp.message=\u041F\u043E\u043C\u043E\u0449\u044C\:\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 Maven +jsf.dashboard.settings.profileUpdated.message=\u0412\u0430\u0448 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0431\u044B\u043B \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D +jsf.dashboard.settings.removeIdentity.confirm.message=\u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u044D\u0442\u043E\u0442 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442? +jsf.EditHomePage=\u041F\u0440\u0430\u0432\u043A\u0430 \u0434\u043E\u043C\u0430\u0448\u043D\u0435\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B +jsf.tooltip.MoreActions=\u0415\u0449\u0451 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F +jsf.label.review=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 +jsf.Translation=\u041F\u0435\u0440\u0435\u0432\u043E\u0434 +jsf.validation.source=\u0418\u0441\u0445\u043E\u0434\u043D\u0438\u043A +jsf.validation.target=\u0426\u0435\u043B\u044C +jsf.CopyTrans.run=\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u044B +jsf.CopyTrans.cancel=\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u043E\u0432 +jsf.CopyTrans=\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u044B +jsf.search.NoResult=\u041D\u0435\u0442 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0430 +jsf.Disabled=\u041E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043E +jsf.CreateProject=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442 +jsf.FilterActiveProjects=\u0424\u0438\u043B\u044C\u0442\u0440 \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 +jsf.FilterReadOnlyProjects=\u0424\u0438\u043B\u044C\u0442\u0440 \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u0435\u043D\u0438\u0435 +jsf.FilterObsoleteProjects=\u0424\u0438\u043B\u044C\u0442\u0440 \u0437\u0430\u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 +jsf.ProjectName=\u0418\u043C\u044F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 +jsf.NoProjectExists=\u041D\u0435\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430. diff --git a/zanata-war/src/main/resources/messages_tr.properties b/zanata-war/src/main/resources/messages_tr.properties index 0b42bf2a16..423adb8da1 100644 --- a/zanata-war/src/main/resources/messages_tr.properties +++ b/zanata-war/src/main/resources/messages_tr.properties @@ -225,18 +225,13 @@ jsf.register.AlreadyHaveAccount.label=Zaten bir hesab\u0131n\u0131z var m\u0131? jsf.register.LogIn.label=Giri\u015F Yap jsf.ForgotYourPassword=\u015Eifrenizi mi unuttunuz? jsf.ResetPassword=\u015Eifreyi S\u0131f\u0131rla -jsf.ResetYourPassword=\u015Eifreyi S\u0131f\u0131rla jsf.NewPassword=Yeni \u015Eifre jsf.OldPassword=Eski \u015Eifre jsf.ChangePassword=\u015Eifreyi De\u011Fi\u015Ftir jsf.login.openid=Open ID jsf.login.WithZanata.label=Kullan\u0131c\u0131 ad\u0131yla giri\u015F yap jsf.ActivateAccount=Hesab\u0131 Etkinle\u015Ftir -jsf.InactiveAccount=Etkinle\u015Ftirilmemi\u015F hesap -jsf.ResendActivationEmail=Etkinle\u015Ftirme postas\u0131n\u0131 tekrar g\u00F6nder jsf.or=YA DA -jsf.inactiveaccount.UpdateAndResend=E-posta adresini g\u00FCncelle ve etkinle\u015Ftirme postas\u0131n\u0131 tekrar g\u00F6nder -jsf.UpdateEmail=E-posta adresini g\u00FCncelle jsf.InvalidActivationKey=Ge\u00E7ersiz etkinle\u015Ftirme anahtar\u0131 jsf.Error=Hata jsf.ReportThisProblem=Bu sorunu raporla @@ -259,9 +254,6 @@ jsf.EmailDomainNameToolTip=E-posta Alan Ad\u0131 ornek.com format\u0131nda olmal jsf.CreateNewUser=Yeni kullan\u0131c\u0131 olu\u015Ftur jsf.Enabled=Etkinle\u015Ftirildi jsf.AccountEnabled=Hesap etkinle\u015Ftirildi -jsf.EnabledByDefault=Varsay\u0131lan olarak etkinle\u015Ftir -jsf.AreYouSureYouWishToEnableThisLanguage=Bu dili etkinle\u015Ftirmek istedi\u011Finize emin misiniz? -jsf.TeamMembers=Tak\u0131m \u00DCyeleri jsf.CountryCode=\u00DClke Kodu jsf.LanguageCode=Dil Kodu jsf.language.validation.Invalid=Ge\u00E7ersiz Dil \u0130smi diff --git a/zanata-war/src/main/resources/messages_uk.properties b/zanata-war/src/main/resources/messages_uk.properties index 7245dc6bf1..12f76bacf4 100644 --- a/zanata-war/src/main/resources/messages_uk.properties +++ b/zanata-war/src/main/resources/messages_uk.properties @@ -245,7 +245,6 @@ jsf.iteration.files.NoFiles=\u0424\u0430\u0439\u043B\u0438 \u0432\u0456\u0434\u0 jsf.iteration.files.Path=\u0428\u043B\u044F\u0445 jsf.iteration.files.Filter.title=\u0424\u0456\u043B\u044C\u0442\u0440\u0443\u0432\u0430\u0442\u0438 \u0437\u0430 \u043D\u0430\u0437\u0432\u043E\u044E \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430 jsf.iteration.files.Merge=\u041E\u0431\u2019\u0454\u0434\u043D\u0430\u0442\u0438 -jsf.iteration.files.MergeCheckbox.Title=\u042F\u043A\u0449\u043E \u043F\u043E\u0437\u043D\u0430\u0447\u0435\u043D\u043E, \u043E\u043D\u043E\u0432\u043B\u0435\u043D\u0456 \u0440\u044F\u0434\u043A\u0438 \u0431\u0443\u0434\u0443\u0442\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0456, \u0432\u0441\u0456 \u0456\u043D\u0448\u0456 \u0437\u0430\u043B\u0438\u0448\u0430\u0442\u044C\u0441\u044F \u0431\u0435\u0437 \u0437\u043C\u0456\u043D. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.iteration.files.Download=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks @@ -258,7 +257,6 @@ jsf.iteration.files.dotpo=.po jsf.iteration.files.dotofflinepo=\u0430\u0432\u0442\u043E\u043D\u043E\u043C\u043D\u0438\u0439 .po jsf.iteration.files.dotofflinepo.description=\u0421\u043F\u0435\u0446\u0456\u0430\u043B\u044C\u043D\u0438\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 po \u044F\u043A\u0438\u0439 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454 msgctxt \u0434\u043B\u044F \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043D\u044F Zanata id. jsf.iteration.files.dotofflinepo.purpose=Zanata \u0432\u0438\u043C\u0430\u0433\u0430\u0454, \u0449\u043E\u0431 \u0444\u0430\u0439\u043B\u0438 \u043F\u0435\u0440\u0435\u043A\u043B\u0430\u0434\u0443, \u044F\u043A\u0456 \u043F\u043E\u0447\u0430\u0442\u043A\u043E\u0432\u043E \u043D\u0435 \u0431\u0443\u043B\u0438 \u0443 \u0444\u043E\u0440\u043C\u0430\u0442\u0456 .po, \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0443\u0432\u0430\u043B\u0438\u0441\u044F \u0441\u0430\u043C\u0435 \u0432 \u0442\u0430\u043A\u043E\u043C\u0443 \u0444\u043E\u0440\u043C\u0430\u0442\u0456. -jsf.iteration.files.ProcessDlgTitle=\u041E\u0431\u0440\u043E\u0431\u043A\u0430 \u0444\u0430\u0439\u043B\u0456\u0432 \u043F\u0440\u043E\u0435\u043A\u0442\u0443... jsf.iteration.files.FilenameWithSemicolonNotSupported=Zanata \u043D\u0435 \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u0443\u0454 \u043A\u0440\u0430\u043F\u043A\u0443 \u0437 \u043A\u043E\u043C\u043E\u044E (;) \u0443 \u0456\u043C\u0435\u043D\u0430\u0445 \u0444\u0430\u0439\u043B\u0456\u0432. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.SourceLanguage=\u041C\u043E\u0432\u0430 \u043E\u0440\u0438\u0433\u0456\u043D\u0430\u043B\u0443 @@ -271,7 +269,6 @@ jsf.iteration.files.UploadFailed=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u043 jsf.ConfigFileForOfflineTranslation=\u0424\u0430\u0439\u043B \u043D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u044C \u0430\u0432\u0442\u043E\u043D\u043E\u043C\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u043A\u043B\u0430\u0434\u0443 jsf.ConfigFileDisabledProjectNotSet=\u0412\u0438\u043C\u043A\u043D\u0435\u043D\u043E, \u043E\u0441\u043A\u0456\u043B\u044C\u043A\u0438 \u043A\u043E\u043E\u0440\u0434\u0438\u043D\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u0432 \u0442\u0438\u043F \u0446\u044C\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0443. jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=\u041D\u0435 \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u0442\u0438\u043F \u043F\u0440\u043E\u0435\u043A\u0442\u0443 \u0434\u043B\u044F \u0442\u0430\u043A\u043E\u0457 \u0437\u043C\u0456\u043D\u0438. \u0421\u043A\u043E\u043D\u0442\u0430\u043A\u0442\u0443\u0439\u0442\u0435\u0441\u044F \u0437 \u043A\u043E\u043E\u0440\u0434\u0438\u043D\u0430\u0442\u043E\u0440\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0443. -jsf.iteration.files.ConfirmDownloadAllFiles=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u043F\u0456\u0434\u0433\u043E\u0442\u0443\u0454\u0442\u044C\u0441\u044F \u0437\u0430 \u043A\u0456\u043B\u044C\u043A\u0430 \u0445\u0432\u0438\u043B\u0438\u043D. \u0413\u0430\u0440\u0430\u0437\u0434? # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.iteration.files.WhyCantITranslate=\u0427\u043E\u043C\u0443 \u044F \u043D\u0435 \u043C\u043E\u0436\u0443 \u043F\u0435\u0440\u0435\u043A\u043B\u0430\u0434\u0430\u0442\u0438? # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks @@ -354,8 +351,6 @@ jsf.ForgotYourPassword=\u0417\u0430\u0431\u0443\u043B\u0438 \u043F\u0430\u0440\u # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Xander jsf.ResetPassword=\u0417\u043C\u0456\u043D\u0438\u0442\u0438 \u043F\u0430\u0440\u043E\u043B\u044C jsf.SubmitRequest=\u0412\u0456\u0434\u043F\u0440\u0430\u0432\u0438\u0442\u0438 \u0437\u0430\u044F\u0432\u043A\u0443 -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Xander -jsf.ResetYourPassword=\u0417\u043C\u0456\u043D\u0456\u0442\u044C \u0432\u0430\u0448 \u043F\u0430\u0440\u043E\u043B\u044C # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.NewPassword=\u041D\u043E\u0432\u0438\u0439 \u043F\u0430\u0440\u043E\u043B\u044C # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks @@ -367,18 +362,9 @@ jsf.login.openid=Open ID jsf.ActivateAccount=\u0417\u0430\u0434\u0456\u044F\u0442\u0438 \u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439 \u0437\u0430\u043F\u0438\u0441 # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.ValidateEmail=\u041F\u0435\u0440\u0435\u0432\u0456\u0440\u043A\u0430 Email -jsf.InactiveAccount=\u041D\u0435\u0430\u043A\u0442\u0438\u0432\u043D\u0438\u0439 \u043E\u0431\u043B. \u0437\u0430\u043F\u0438\u0441 -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks -jsf.inactiveaccount.PleaseSelectOne=\u0412\u0430\u0448 \u043E\u0431\u043B\u0456\u043A\u043E\u0432\u0438\u0439 \u0437\u0430\u043F\u0438\u0441 \u0449\u0435 \u043D\u0435 \u0430\u043A\u0442\u0438\u0432\u043E\u0432\u0430\u043D\u043E. \u0411\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u0432\u0438\u043A\u043E\u043D\u0430\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0437 \u043D\u0430\u0441\u0442\u0443\u043F\u043D\u0438\u0445 \u043A\u0440\u043E\u043A\u0456\u0432\: -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Xander -jsf.ResendActivationEmail=\u0429\u0435 \u0440\u0430\u0437 \u043D\u0430\u0434\u0456\u0441\u043B\u0430\u0442\u0438 \u043B\u0438\u0441\u0442 \u0456\u0437 \u0430\u043A\u0442\u0438\u0432\u0430\u0446\u0456\u0454\u044E # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Xander jsf.or=\u0410\u0411\u041E # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks -jsf.inactiveaccount.UpdateAndResend=\u041E\u043D\u043E\u0432\u0438\u0442\u0438 e-mail \u0430\u0434\u0440\u0435\u0441\u0443 \u0442\u0430 \u0432\u0456\u0434\u043F\u0440\u0430\u0432\u0438\u0442\u0438 \u043B\u0438\u0441\u0442 \u0437 \u0430\u043A\u0442\u0438\u0432\u0430\u0446\u0456\u0454\u044E \u0449\u0435 \u0440\u0430\u0437\: -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks -jsf.UpdateEmail=\u041E\u043D\u043E\u0432\u0438\u0442\u0438 \u0430\u0434\u0440\u0435\u0441\u0443 \u0435\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0457 \u043F\u043E\u0448\u0442\u0438 -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.InvalidActivationKey=\u041D\u0435\u0432\u0456\u0440\u043D\u0438\u0439 \u043A\u043E\u0434 \u0430\u043A\u0442\u0438\u0432\u0430\u0446\u0456\u0457 # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.Error=\u041F\u043E\u043C\u0438\u043B\u043A\u0430 @@ -455,12 +441,6 @@ jsf.CreateRole=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0440\u0456\u04 jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=\u0412\u0438 \u0434\u0456\u0439\u0441\u043D\u043E \u0445\u043E\u0447\u0435\u0442\u0435 \u0432\u0438\u0434\u0430\u043B\u0438\u0442\u0438 \u0446\u044C\u043E\u0433\u043E \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430? \u0426\u044F \u0434\u0456\u044F \u043D\u0435\u0432\u0456\u0434\u0432\u043E\u0440\u043E\u0442\u043D\u0430. jsf.Role=\u0420\u0456\u0432\u0435\u043D\u044C jsf.RoleDetails=\u0414\u0435\u0442\u0430\u043B\u0456 \u0440\u0456\u0432\u043D\u044F -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks -jsf.EnabledByDefault=\u0423\u0432\u0456\u043C\u043A\u043D\u0435\u043D\u043E \u0437\u0430 \u0437\u0430\u043C\u043E\u0432\u0447\u0435\u043D\u043D\u044F\u043C -jsf.AreYouSureYouWishToEnableThisLanguage=\u0412\u0438 \u0441\u043F\u0440\u0430\u0432\u0434\u0456 \u0445\u043E\u0447\u0435\u0442\u0435 \u0443\u0432\u0456\u043C\u043A\u043D\u0443\u0442\u0438 \u0446\u044E \u043C\u043E\u0432\u0443? -jsf.AreYouSureYouWishToDisableThisLanguage=\u0412\u0438 \u0441\u043F\u0440\u0430\u0432\u0434\u0456 \u0445\u043E\u0447\u0435\u0442\u0435 \u0432\u0438\u043C\u043A\u043D\u0443\u0442\u0438 \u0446\u044E \u043C\u043E\u0432\u0443? -# translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks -jsf.TeamMembers=\u0423\u0447\u0430\u0441\u043D\u0438\u043A\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0438 jsf.language.validation.ReplaceUnderscores=\u0417\u0430\u043C\u0456\u043D\u0438\u0442\u0438 \u0457\u0445. # translation auto-copied from project Zanata, version jsf-pages, document main/resources/messages, author Maks jsf.language.validation.Underscores=\u0417\u043D\u0430\u043A\u0438 \u043F\u0456\u0434\u043A\u0440\u0435\u0441\u043B\u0435\u043D\u043D\u044F (_) \u043C\u0430\u044E\u0442\u044C \u0431\u0443\u0442\u0438 \u0437\u0430\u043C\u0456\u043D\u0435\u043D\u0456 \u043D\u0430 \u0442\u0438\u0440\u0435 (-). diff --git a/zanata-war/src/main/resources/messages_zh_TW_Hant.properties b/zanata-war/src/main/resources/messages_zh_TW_Hant.properties index c088919b3f..4c6b9978dc 100644 --- a/zanata-war/src/main/resources/messages_zh_TW_Hant.properties +++ b/zanata-war/src/main/resources/messages_zh_TW_Hant.properties @@ -231,7 +231,6 @@ jsf.iteration.files.NoFiles=\u6C92\u6709\u53EF\u7528\u7684\u6A94\u6848 jsf.iteration.files.Path=\u8DEF\u5F91 jsf.iteration.files.Filter.title=\u4EE5\u6587\u4EF6\u540D\u7A31\u7BE9\u9078 jsf.iteration.files.Merge=\u5408\u4F75 -jsf.iteration.files.MergeCheckbox.Title=\u7576\u9078\u53D6\u6642\uFF0C\u5DF2\u66F4\u65B0\u7684\u7FFB\u8B6F\u5C07\u6703\u88AB\u5BEB\u5165\uFF0C\u5176\u5B83\u5167\u5BB9\u5247\u4FDD\u6301\u4E0D\u8B8A\u3002 # translation auto-copied from project CFSE, version sam-1.2, document app, author snowlet jsf.iteration.files.Download=\u4E0B\u8F09 jsf.iteration.files.dotpot=.pot @@ -242,7 +241,6 @@ jsf.iteration.files.dotpo=.po jsf.iteration.files.dotofflinepo=\u96E2\u7DDA .po jsf.iteration.files.dotofflinepo.description=\u4F7F\u7528 msgctxt \u4F86\u5132\u5B58 Zanata id \u7684\u7279\u6B8A po \u683C\u5F0F\u3002 jsf.iteration.files.dotofflinepo.purpose=\u7576\u60A8\u8981\u4E0A\u8F09\u4E00\u4EFD\u539F\u672C\u4E26\u975E po \u683C\u5F0F\u6587\u4EF6\u7684 po \u7FFB\u8B6F\u6642\uFF0CZanata \u5C07\u9700\u8981\u4F7F\u7528\u6B64\u683C\u5F0F\u3002 -jsf.iteration.files.ProcessDlgTitle=\u6B63\u5728\u8655\u7406\u5C08\u6848\u6A94\u6848... jsf.iteration.files.FilenameWithSemicolonNotSupported=Zanata \u4E0D\u652F\u63F4\u5305\u542B\u5192\u865F\u7684\u6A94\u6848\u540D\u7A31\u3002 jsf.SourceLanguage=\u539F\u59CB\u8A9E\u8A00 jsf.iteration.files.DocumentPath=\u6587\u4EF6\u8DEF\u5F91 @@ -253,7 +251,6 @@ jsf.iteration.files.UploadFailed=\u4E0A\u50B3\u5931\u6557\uFF01\u539F\u56E0\u4E4 jsf.ConfigFileForOfflineTranslation=\u96E2\u7DDA\u7FFB\u8B6F\u914D\u7F6E\u6A94\u6848 jsf.ConfigFileDisabledProjectNotSet=\u5DF2\u505C\u7528\uFF0C\u56E0\u70BA\u7DAD\u8B77\u8005\u5C1A\u672A\u70BA\u6B64\u5C08\u6848\u8A2D\u7F6E\u5C08\u6848\u985E\u578B\u3002 jsf.iteration.files.DownloadAllFiles.ProjectTypeNotSet=\u5C1A\u672A\u70BA\u6B64\u91CD\u8907\u9805\u76EE\u8A2D\u7F6E\u5C08\u6848\u985E\u578B\u3002\u8ACB\u806F\u7D61\u5C08\u6848\u7DAD\u8B77\u8005\u3002 -jsf.iteration.files.ConfirmDownloadAllFiles=\u6B63\u5728\u6E96\u5099\u60A8\u7684\u4E0B\u8F09\uFF0C\u4E26\u4E14\u5B8C\u6210\u53EF\u80FD\u6703\u82B1\u4E0A\u5E7E\u5206\u9418\u3002\u662F\u5426\u7E7C\u7E8C\uFF1F jsf.iteration.files.WhyCantITranslate=\u70BA\u4F55\u6211\u7121\u6CD5\u7FFB\u8B6F\uFF1F jsf.iteration.files.translateDenied.NotLoggedIn=\u60A8\u5C1A\u672A\u767B\u5165\u3002 jsf.iteration.files.translateDenied.VersionIsReadOnly=\u6B64\u5C08\u6848\u7248\u672C\u70BA\u552F\u8B80\u3002 @@ -341,7 +338,6 @@ jsf.ForgotYourPassword=\u5FD8\u8A18\u4E86\u60A8\u7684\u5BC6\u78BC\uFF1F # translation auto-copied from project CFSE, version sam-1.2, document app, author snowlet jsf.ResetPassword=\u91CD\u8A2D\u5BC6\u78BC jsf.SubmitRequest=\u63D0\u4EA4\u8ACB\u6C42 -jsf.ResetYourPassword=\u91CD\u8A2D\u60A8\u7684\u5BC6\u78BC jsf.NewPassword=\u65B0\u5BC6\u78BC jsf.OldPassword=\u820A\u5BC6\u78BC # translation auto-copied from project CFSE, version sam-1.2, document app, author snowlet @@ -351,12 +347,7 @@ jsf.login.WithZanata.label=\u4EE5\u60A8\u7684\u4F7F\u7528\u8005\u540D\u7A31\u767 jsf.login.DontHaveAnAccount.label=\u5C1A\u672A\u64C1\u6709\u5E33\u865F\uFF1F jsf.ActivateAccount=\u555F\u7528\u5E33\u865F jsf.ValidateEmail=\u9A57\u8B49\u96FB\u5B50\u90F5\u4EF6 -jsf.InactiveAccount=\u505C\u7528\u5E33\u865F -jsf.inactiveaccount.PleaseSelectOne=\u60A8\u7684\u5E33\u865F\u5C1A\u672A\u555F\u7528\u3002\u8ACB\u9078\u64C7\u4E0B\u5217\u9078\u9805\u4E4B\u4E00\uFF1A -jsf.ResendActivationEmail=\u91CD\u65B0\u50B3\u9001\u555F\u7528\u96FB\u5B50\u90F5\u4EF6 jsf.or=\u6216\u662F -jsf.inactiveaccount.UpdateAndResend=\u66F4\u65B0\u96FB\u5B50\u90F5\u4EF6\u5730\u5740\u4E26\u91CD\u65B0\u50B3\u9001\u555F\u7528\u96FB\u5B50\u90F5\u4EF6\uFF1A -jsf.UpdateEmail=\u66F4\u65B0\u96FB\u5B50\u90F5\u4EF6\u5730\u5740 jsf.InvalidActivationKey=\u555F\u52D5\u91D1\u9470\u7121\u6548 # translation auto-copied from project CFSE-cli, version sam-1.2, document keys, author snowlet jsf.Error=\u932F\u8AA4 @@ -431,10 +422,6 @@ jsf.AreYouSureYouWishToDeleteThisRoleThisActionCannotBeUndone=\u60A8\u662F\u5426 jsf.Role=\u89D2\u8272 # translation auto-copied from project CFSE, version sam-1.2, document app, author snowlet jsf.RoleDetails=\u89D2\u8272\u7684\u8A73\u7D30\u8CC7\u6599 -jsf.EnabledByDefault=\u5C31\u9810\u8A2D\u503C\u555F\u7528 -jsf.AreYouSureYouWishToEnableThisLanguage=\u60A8\u662F\u5426\u78BA\u8A8D\u8981\u555F\u7528\u6B64\u8A9E\u8A00\uFF1F -jsf.AreYouSureYouWishToDisableThisLanguage=\u60A8\u662F\u5426\u78BA\u8A8D\u8981\u505C\u7528\u6B64\u8A9E\u8A00\uFF1F -jsf.TeamMembers=\u5718\u968A\u6210\u54E1 jsf.language.validation.ReplaceUnderscores=\u66FF\u63DB\u5B83\u5011\u3002 jsf.language.validation.Underscores=\u5E95\u7DDA\u7B26\u865F\u61C9\u53D6\u4EE3\u70BA\u9023\u5B57\u7B26\u865F\u3002 jsf.CountryCode=\u570B\u78BC diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/persistence.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/persistence.xml index 5851432535..8257039542 100644 --- a/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/persistence.xml +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/persistence.xml @@ -87,6 +87,19 @@ + + + + diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml index 9b5da7e97e..57173dd45b 100644 --- a/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml @@ -11,6 +11,7 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/activity.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/activity.xhtml index 4430d2fd0e..47673fe66c 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/activity.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/activity.xhtml @@ -1,10 +1,9 @@ + xmlns:a4j="http://richfaces.org/a4j" + xmlns:zanata="http://java.sun.com/jsf/composite/zanata">
  • diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/projects.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/projects.xhtml index c4b6824c96..dedb806cd6 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/projects.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/projects.xhtml @@ -65,18 +65,18 @@ diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/edit_form.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/edit_form.xhtml index c6ba369996..f1fd7b44d8 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/edit_form.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/edit_form.xhtml @@ -179,13 +179,13 @@ id="#{status.index}:version:#{versionItem.version.slug}"/> diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/merge_trans_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/merge_trans_modal.xhtml new file mode 100644 index 0000000000..f07ab82ef8 --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/merge_trans_modal.xhtml @@ -0,0 +1,171 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/pages.xml b/zanata-war/src/main/webapp/WEB-INF/pages.xml index 7218189329..c28aea50ca 100644 --- a/zanata-war/src/main/webapp/WEB-INF/pages.xml +++ b/zanata-war/src/main/webapp/WEB-INF/pages.xml @@ -337,6 +337,12 @@ + + + + + + @@ -407,6 +413,9 @@ + + + @@ -427,6 +436,14 @@ value="#{versionHome.instance.slug}" /> + + + + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/template/banner.xhtml b/zanata-war/src/main/webapp/WEB-INF/template/banner.xhtml index bbe0204bf7..067528e512 100644 --- a/zanata-war/src/main/webapp/WEB-INF/template/banner.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/template/banner.xhtml @@ -30,13 +30,14 @@ @@ -46,7 +47,13 @@ - + + #{result.account.username} + + + + Search Zanata for '#{projectSearch.projectAutocomplete.query}' diff --git a/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml b/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml index f514d9209d..c65ef1c7ef 100644 --- a/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml +++ b/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml @@ -196,6 +196,18 @@ /profile/merge_account.seam$1 + + ^/profile/view/(.+)$ + /profile/home.seam\?username=$1 + + + + + ^/profile/$ + /profile/home.seam + + ^/tm/?(\?.*)?$ @@ -420,6 +432,11 @@ ^(/.+)?/profile/merge_account.seam(.+)?$ $1/profile/merge_account$2 + + ^(/.+)?/profile/home.seam\?username=(.+)$ + $1/profile/view/$2 + + ^(/.+)?/tm/create.seam(.+)?$ diff --git a/zanata-war/src/main/webapp/admin/server_configuration.xhtml b/zanata-war/src/main/webapp/admin/server_configuration.xhtml index f2324e7fd0..5cfd6b9842 100644 --- a/zanata-war/src/main/webapp/admin/server_configuration.xhtml +++ b/zanata-war/src/main/webapp/admin/server_configuration.xhtml @@ -134,7 +134,7 @@
    Enabled - diff --git a/zanata-war/src/main/webapp/iteration/view.xhtml b/zanata-war/src/main/webapp/iteration/view.xhtml index b7a475d218..98c0bbc00f 100644 --- a/zanata-war/src/main/webapp/iteration/view.xhtml +++ b/zanata-war/src/main/webapp/iteration/view.xhtml @@ -94,13 +94,6 @@ fileInput.value === ""; submitButton.disabled = noFileSelected; } - - function onCancelCopyVersion() { - if(confirm('#{msgs['jsf.copyVersion.cancel.confirm']}')) { - cancelCopyVersion(); - refreshStatistics(); - } - } @@ -132,14 +125,33 @@ action="#{versionHomeAction.setPageRendered(true)}"/> + render="copyTrans, copy-trans-buttons"/> + render="copyTrans, copy-trans-buttons"/> + render="version-info, version-more-actions, version-settings, copyVersion, messages"/> + + + + + + + + + @@ -157,20 +169,7 @@

    - -

    - - #{versionHomeAction.versionSlug} -

    -
    - -

    - - #{versionHomeAction.versionSlug} - -

    -
    +

    - - - - - - #{msgs['jsf.Cancel']} - - - -

    #{msgs['jsf.CopyTrans']}

    -
    - -
    - - - - - - - , - - - -
    -
    - - - - - - #{msgs['jsf.Cancel']} - - - -

    #{msgs['jsf.CopyVersion']}

    -
    - -
    - - - - - - - - - - - -
    - -
    + + + + +
    @@ -331,13 +272,13 @@ href="#{request.contextPath}/iteration/view/#{versionHomeAction.projectSlug}/#{versionHomeAction.versionSlug}/documents" class="js-url-mod" data-content="#documents"> - - #{msgs['jsf.Documents']} - - #{versionHomeAction.getDocuments().size} - - + + #{msgs['jsf.Documents']} + + #{versionHomeAction.getDocuments().size} + + @@ -346,12 +287,12 @@ href="#{request.contextPath}/iteration/view/#{versionHomeAction.projectSlug}/#{versionHomeAction.versionSlug}/groups" class="js-url-mod" data-content="#groups"> - - #{msgs['jsf.Groups']} - - #{versionHomeAction.groups.size} - - + + #{msgs['jsf.Groups']} + + #{versionHomeAction.groups.size} + + @@ -361,36 +302,47 @@ styleClass="dropdown dropdown--tab dropdown--small dropdown--right dropdown--inline js-dropdown" id="version-more-actions"> + rendered="#{identity.loggedIn}">
    + + + +
    - #{language.members.size} + title="#{msgs.format('jsf.language.members', languagesAction.getMemberSize(language.localeId))}"> + #{languagesAction.getMemberSize(language.localeId)}
    diff --git a/zanata-war/src/main/webapp/profile/home.xhtml b/zanata-war/src/main/webapp/profile/home.xhtml new file mode 100644 index 0000000000..d5eae19a51 --- /dev/null +++ b/zanata-war/src/main/webapp/profile/home.xhtml @@ -0,0 +1,47 @@ + + + + #{msgs['jsf.Profile']}:#{msgs['jsf.Profile.User']}:#{profileHome.username} + + +
    +
    + +
    +
    + #{profileHome.username} +
    +
    +

    #{profileHome.name}

    +
      +
    • #{profileHome.username} +
    • +
    • #{profileHome.userLanguageTeams} +
    • +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    diff --git a/zanata-war/src/main/webapp/profile/js/bundle.min.js b/zanata-war/src/main/webapp/profile/js/bundle.min.js new file mode 100644 index 0000000000..749900b6d8 --- /dev/null +++ b/zanata-war/src/main/webapp/profile/js/bundle.min.js @@ -0,0 +1,645 @@ +!function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([/*!******************!*\ + !*** multi main ***! + \******************/ +function(t,e,n){t.exports=n(/*! ./index.js */98)},/*!**********************************!*\ + !*** ./~/react/lib/invariant.js ***! + \**********************************/ +function(t){"use strict";var e=function(t,e,n,r,i,o,a,s){if(!t){var u;if(void 0===e)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,i,o,a,s],c=0;u=new Error("Invariant Violation: "+e.replace(/%s/g,function(){return l[c++]}))}throw u.framesToPop=1,u}};t.exports=e},/*!**************************************!*\ + !*** ./~/react/lib/Object.assign.js ***! + \**************************************/ +function(t){function e(t){if(null==t)throw new TypeError("Object.assign target cannot be null or undefined");for(var e=Object(t),n=Object.prototype.hasOwnProperty,r=1;r1){for(var p=Array(h),f=0;h>f;f++)p[f]=arguments[f+2];u.children=p}if(t&&t.defaultProps){var d=t.defaultProps;for(s in d)"undefined"==typeof u[s]&&(u[s]=d[s])}return new a(t,l,c,i.current,r.current,u)},a.createFactory=function(t){var e=a.createElement.bind(null,t);return e.type=t,e},a.cloneAndReplaceProps=function(t,e){var n=new a(t.type,t.key,t.ref,t._owner,t._context,e);return n},a.isValidElement=function(t){var e=!(!t||!t._isReactElement);return e},t.exports=a},/*!********************************!*\ + !*** ./~/react/lib/warning.js ***! + \********************************/ +function(t,e,n){"use strict";var r=n(/*! ./emptyFunction */10),i=r;t.exports=i},/*!*********************************************!*\ + !*** ./~/react/lib/ExecutionEnvironment.js ***! + \*********************************************/ +function(t){"use strict";var e=!("undefined"==typeof window||!window.document||!window.document.createElement),n={canUseDOM:e,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:e&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:e&&!!window.screen,isInWorker:!e};t.exports=n},/*!***************************************!*\ + !*** ./~/react/lib/EventConstants.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./keyMirror */28),i=r({bubbled:null,captured:null}),o=r({topBlur:null,topChange:null,topClick:null,topCompositionEnd:null,topCompositionStart:null,topCompositionUpdate:null,topContextMenu:null,topCopy:null,topCut:null,topDoubleClick:null,topDrag:null,topDragEnd:null,topDragEnter:null,topDragExit:null,topDragLeave:null,topDragOver:null,topDragStart:null,topDrop:null,topError:null,topFocus:null,topInput:null,topKeyDown:null,topKeyPress:null,topKeyUp:null,topLoad:null,topMouseDown:null,topMouseMove:null,topMouseOut:null,topMouseOver:null,topMouseUp:null,topPaste:null,topReset:null,topScroll:null,topSelectionChange:null,topSubmit:null,topTextInput:null,topTouchCancel:null,topTouchEnd:null,topTouchMove:null,topTouchStart:null,topWheel:null}),a={topLevelTypes:o,PropagationPhases:i};t.exports=a},/*!******************************!*\ + !*** ./~/react/lib/keyOf.js ***! + \******************************/ +function(t){var e=function(t){var e;for(e in t)if(t.hasOwnProperty(e))return e;return null};t.exports=e},/*!************************************************!*\ + !*** ./~/react/lib/ReactCompositeComponent.js ***! + \************************************************/ +function(t,e,n){"use strict";function r(t){var e=t._owner||null;return e&&e.constructor&&e.constructor.displayName?" Check the render method of `"+e.constructor.displayName+"`.":""}function i(t,e){for(var n in e)e.hasOwnProperty(n)&&S("function"==typeof e[n])}function o(t,e){var n=L.hasOwnProperty(e)?L[e]:null;F.hasOwnProperty(e)&&S(n===R.OVERRIDE_BASE),t.hasOwnProperty(e)&&S(n===R.DEFINE_MANY||n===R.DEFINE_MANY_MERGED)}function a(t){var e=t._compositeLifeCycleState;S(t.isMounted()||e===I.MOUNTING),S(null==d.current),S(e!==I.UNMOUNTING)}function s(t,e){if(e){S(!y.isValidFactory(e)),S(!m.isValidElement(e));var n=t.prototype;e.hasOwnProperty(O)&&N.mixins(t,e.mixins);for(var r in e)if(e.hasOwnProperty(r)&&r!==O){var i=e[r];if(o(n,r),N.hasOwnProperty(r))N[r](t,i);else{var a=L.hasOwnProperty(r),s=n.hasOwnProperty(r),u=i&&i.__reactDontBind,l="function"==typeof i,p=l&&!a&&!s&&!u;if(p)n.__reactAutoBindMap||(n.__reactAutoBindMap={}),n.__reactAutoBindMap[r]=i,n[r]=i;else if(s){var f=L[r];S(a&&(f===R.DEFINE_MANY_MERGED||f===R.DEFINE_MANY)),f===R.DEFINE_MANY_MERGED?n[r]=c(n[r],i):f===R.DEFINE_MANY&&(n[r]=h(n[r],i))}else n[r]=i}}}}function u(t,e){if(e)for(var n in e){var r=e[n];if(e.hasOwnProperty(n)){var i=n in N;S(!i);var o=n in t;S(!o),t[n]=r}}}function l(t,e){return S(t&&e&&"object"==typeof t&&"object"==typeof e),P(e,function(e,n){S(void 0===t[n]),t[n]=e}),t}function c(t,e){return function(){var n=t.apply(this,arguments),r=e.apply(this,arguments);return null==n?r:null==r?n:l(n,r)}}function h(t,e){return function(){t.apply(this,arguments),e.apply(this,arguments)}}var p=n(/*! ./ReactComponent */26),f=n(/*! ./ReactContext */44),d=n(/*! ./ReactCurrentOwner */18),m=n(/*! ./ReactElement */3),v=(n(/*! ./ReactElementValidator */45),n(/*! ./ReactEmptyComponent */36)),g=n(/*! ./ReactErrorUtils */153),y=n(/*! ./ReactLegacyElement */30),_=n(/*! ./ReactOwner */74),b=n(/*! ./ReactPerf */16),w=n(/*! ./ReactPropTransferer */75),C=n(/*! ./ReactPropTypeLocations */77),x=(n(/*! ./ReactPropTypeLocationNames */76),n(/*! ./ReactUpdates */9)),E=n(/*! ./Object.assign */2),D=n(/*! ./instantiateReactComponent */39),S=n(/*! ./invariant */1),M=n(/*! ./keyMirror */28),T=n(/*! ./keyOf */7),P=(n(/*! ./monitorCodeUse */40),n(/*! ./mapObject */92)),k=n(/*! ./shouldUpdateReactComponent */56),O=(n(/*! ./warning */4),T({mixins:null})),R=M({DEFINE_ONCE:null,DEFINE_MANY:null,OVERRIDE_BASE:null,DEFINE_MANY_MERGED:null}),A=[],L={mixins:R.DEFINE_MANY,statics:R.DEFINE_MANY,propTypes:R.DEFINE_MANY,contextTypes:R.DEFINE_MANY,childContextTypes:R.DEFINE_MANY,getDefaultProps:R.DEFINE_MANY_MERGED,getInitialState:R.DEFINE_MANY_MERGED,getChildContext:R.DEFINE_MANY_MERGED,render:R.DEFINE_ONCE,componentWillMount:R.DEFINE_MANY,componentDidMount:R.DEFINE_MANY,componentWillReceiveProps:R.DEFINE_MANY,shouldComponentUpdate:R.DEFINE_ONCE,componentWillUpdate:R.DEFINE_MANY,componentDidUpdate:R.DEFINE_MANY,componentWillUnmount:R.DEFINE_MANY,updateComponent:R.OVERRIDE_BASE},N={displayName:function(t,e){t.displayName=e},mixins:function(t,e){if(e)for(var n=0;nn;n++){var r=v[n];if(r.isMounted()){var i=r._pendingCallbacks;if(r._pendingCallbacks=null,r.performUpdateIfNecessary(t.reconcileTransaction),i)for(var o=0;oe||i.hasOverloadedBooleanValue[t]&&e===!1}var i=n(/*! ./DOMProperty */21),o=n(/*! ./escapeTextForBrowser */49),a=n(/*! ./memoizeStringOnly */93),s=(n(/*! ./warning */4),a(function(t){return o(t)+'="'})),u={createMarkupForID:function(t){return s(i.ID_ATTRIBUTE_NAME)+o(t)+'"'},createMarkupForProperty:function(t,e){if(i.isStandardName.hasOwnProperty(t)&&i.isStandardName[t]){if(r(t,e))return"";var n=i.getAttributeName[t];return i.hasBooleanValue[t]||i.hasOverloadedBooleanValue[t]&&e===!0?o(n):s(n)+o(e)+'"'}return i.isCustomAttribute(t)?null==e?"":s(t)+o(e)+'"':null},setValueForProperty:function(t,e,n){if(i.isStandardName.hasOwnProperty(e)&&i.isStandardName[e]){var o=i.getMutationMethod[e];if(o)o(t,n);else if(r(e,n))this.deleteValueForProperty(t,e);else if(i.mustUseAttribute[e])t.setAttribute(i.getAttributeName[e],""+n);else{var a=i.getPropertyName[e];i.hasSideEffects[e]&&""+t[a]==""+n||(t[a]=n)}}else i.isCustomAttribute(e)&&(null==n?t.removeAttribute(e):t.setAttribute(e,""+n))},deleteValueForProperty:function(t,e){if(i.isStandardName.hasOwnProperty(e)&&i.isStandardName[e]){var n=i.getMutationMethod[e];if(n)n(t,void 0);else if(i.mustUseAttribute[e])t.removeAttribute(i.getAttributeName[e]);else{var r=i.getPropertyName[e],o=i.getDefaultValueForProperty(t.nodeName,r);i.hasSideEffects[e]&&""+t[r]===o||(t[r]=o)}}else i.isCustomAttribute(e)&&t.removeAttribute(e)}};t.exports=u},/*!*****************************************!*\ + !*** ./~/react/lib/EventPropagators.js ***! + \*****************************************/ +function(t,e,n){"use strict";function r(t,e,n){var r=e.dispatchConfig.phasedRegistrationNames[n];return v(t,r)}function i(t,e,n){var i=e?m.bubbled:m.captured,o=r(t,n,i);o&&(n._dispatchListeners=f(n._dispatchListeners,o),n._dispatchIDs=f(n._dispatchIDs,t))}function o(t){t&&t.dispatchConfig.phasedRegistrationNames&&p.injection.getInstanceHandle().traverseTwoPhase(t.dispatchMarker,i,t)}function a(t,e,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,i=v(t,r);i&&(n._dispatchListeners=f(n._dispatchListeners,i),n._dispatchIDs=f(n._dispatchIDs,t))}}function s(t){t&&t.dispatchConfig.registrationName&&a(t.dispatchMarker,null,t)}function u(t){d(t,o)}function l(t,e,n,r){p.injection.getInstanceHandle().traverseEnterLeave(n,r,a,t,e)}function c(t){d(t,s)}var h=n(/*! ./EventConstants */6),p=n(/*! ./EventPluginHub */29),f=n(/*! ./accumulateInto */47),d=n(/*! ./forEachAccumulated */50),m=h.PropagationPhases,v=p.getListener,g={accumulateTwoPhaseDispatches:u,accumulateDirectDispatches:c,accumulateEnterLeaveDispatches:l};t.exports=g},/*!******************************!*\ + !*** ./~/react/lib/React.js ***! + \******************************/ +function(t,e,n){"use strict";var r=n(/*! ./DOMPropertyOperations */22),i=n(/*! ./EventPluginUtils */42),o=n(/*! ./ReactChildren */68),a=n(/*! ./ReactComponent */26),s=n(/*! ./ReactCompositeComponent */8),u=n(/*! ./ReactContext */44),l=n(/*! ./ReactCurrentOwner */18),c=n(/*! ./ReactElement */3),h=(n(/*! ./ReactElementValidator */45),n(/*! ./ReactDOM */15)),p=n(/*! ./ReactDOMComponent */69),f=n(/*! ./ReactDefaultInjection */152),d=n(/*! ./ReactInstanceHandles */27),m=n(/*! ./ReactLegacyElement */30),v=n(/*! ./ReactMount */13),g=n(/*! ./ReactMultiChild */71),y=n(/*! ./ReactPerf */16),_=n(/*! ./ReactPropTypes */78),b=n(/*! ./ReactServerRendering */159),w=n(/*! ./ReactTextComponent */81),C=n(/*! ./Object.assign */2),x=n(/*! ./deprecated */48),E=n(/*! ./onlyChild */94);f.inject();var D=c.createElement,S=c.createFactory;D=m.wrapCreateElement(D),S=m.wrapCreateFactory(S);var M=y.measure("React","render",v.render),T={Children:{map:o.map,forEach:o.forEach,count:o.count,only:E},DOM:h,PropTypes:_,initializeTouchEvents:function(t){i.useTouchEvents=t},createClass:s.createClass,createElement:D,createFactory:S,constructAndRenderComponent:v.constructAndRenderComponent,constructAndRenderComponentByID:v.constructAndRenderComponentByID,render:M,renderToString:b.renderToString,renderToStaticMarkup:b.renderToStaticMarkup,unmountComponentAtNode:v.unmountComponentAtNode,isValidClass:m.isValidClass,isValidElement:c.isValidElement,withContext:u.withContext,__spread:C,renderComponent:x("React","renderComponent","render",this,M),renderComponentToString:x("React","renderComponentToString","renderToString",this,b.renderToString),renderComponentToStaticMarkup:x("React","renderComponentToStaticMarkup","renderToStaticMarkup",this,b.renderToStaticMarkup),isValidComponent:x("React","isValidComponent","isValidElement",this,c.isValidElement)};"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({Component:a,CurrentOwner:l,DOMComponent:p,DOMPropertyOperations:r,InstanceHandles:d,Mount:v,MultiChild:g,TextComponent:w}),T.version="0.12.2",t.exports=T},/*!*************************************************!*\ + !*** ./~/react/lib/ReactBrowserEventEmitter.js ***! + \*************************************************/ +function(t,e,n){"use strict";function r(t){return Object.prototype.hasOwnProperty.call(t,m)||(t[m]=f++,h[t[m]]={}),h[t[m]]}var i=n(/*! ./EventConstants */6),o=n(/*! ./EventPluginHub */29),a=n(/*! ./EventPluginRegistry */66),s=n(/*! ./ReactEventEmitterMixin */154),u=n(/*! ./ViewportMetrics */83),l=n(/*! ./Object.assign */2),c=n(/*! ./isEventSupported */55),h={},p=!1,f=0,d={topBlur:"blur",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topScroll:"scroll",topSelectionChange:"selectionchange",topTextInput:"textInput",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topWheel:"wheel"},m="_reactListenersID"+String(Math.random()).slice(2),v=l({},s,{ReactEventListener:null,injection:{injectReactEventListener:function(t){t.setHandleTopLevel(v.handleTopLevel),v.ReactEventListener=t}},setEnabled:function(t){v.ReactEventListener&&v.ReactEventListener.setEnabled(t)},isEnabled:function(){return!(!v.ReactEventListener||!v.ReactEventListener.isEnabled())},listenTo:function(t,e){for(var n=e,o=r(n),s=a.registrationNameDependencies[t],u=i.topLevelTypes,l=0,h=s.length;h>l;l++){var p=s[l];o.hasOwnProperty(p)&&o[p]||(p===u.topWheel?c("wheel")?v.ReactEventListener.trapBubbledEvent(u.topWheel,"wheel",n):c("mousewheel")?v.ReactEventListener.trapBubbledEvent(u.topWheel,"mousewheel",n):v.ReactEventListener.trapBubbledEvent(u.topWheel,"DOMMouseScroll",n):p===u.topScroll?c("scroll",!0)?v.ReactEventListener.trapCapturedEvent(u.topScroll,"scroll",n):v.ReactEventListener.trapBubbledEvent(u.topScroll,"scroll",v.ReactEventListener.WINDOW_HANDLE):p===u.topFocus||p===u.topBlur?(c("focus",!0)?(v.ReactEventListener.trapCapturedEvent(u.topFocus,"focus",n),v.ReactEventListener.trapCapturedEvent(u.topBlur,"blur",n)):c("focusin")&&(v.ReactEventListener.trapBubbledEvent(u.topFocus,"focusin",n),v.ReactEventListener.trapBubbledEvent(u.topBlur,"focusout",n)),o[u.topBlur]=!0,o[u.topFocus]=!0):d.hasOwnProperty(p)&&v.ReactEventListener.trapBubbledEvent(p,d[p],n),o[p]=!0)}},trapBubbledEvent:function(t,e,n){return v.ReactEventListener.trapBubbledEvent(t,e,n)},trapCapturedEvent:function(t,e,n){return v.ReactEventListener.trapCapturedEvent(t,e,n)},ensureScrollValueMonitoring:function(){if(!p){var t=u.refreshScrollValues;v.ReactEventListener.monitorScrollValue(t),p=!0}},eventNameDispatchConfigs:o.eventNameDispatchConfigs,registrationNameModules:o.registrationNameModules,putListener:o.putListener,getListener:o.getListener,deleteListener:o.deleteListener,deleteAllListeners:o.deleteAllListeners});t.exports=v},/*!***************************************!*\ + !*** ./~/react/lib/ReactComponent.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./ReactElement */3),i=n(/*! ./ReactOwner */74),o=n(/*! ./ReactUpdates */9),a=n(/*! ./Object.assign */2),s=n(/*! ./invariant */1),u=n(/*! ./keyMirror */28),l=u({MOUNTED:null,UNMOUNTED:null}),c=!1,h=null,p=null,f={injection:{injectEnvironment:function(t){s(!c),p=t.mountImageIntoNode,h=t.unmountIDFromEnvironment,f.BackendIDOperations=t.BackendIDOperations,c=!0}},LifeCycle:l,BackendIDOperations:null,Mixin:{isMounted:function(){return this._lifeCycleState===l.MOUNTED},setProps:function(t,e){var n=this._pendingElement||this._currentElement;this.replaceProps(a({},n.props,t),e)},replaceProps:function(t,e){s(this.isMounted()),s(0===this._mountDepth),this._pendingElement=r.cloneAndReplaceProps(this._pendingElement||this._currentElement,t),o.enqueueUpdate(this,e)},_setPropsInternal:function(t,e){var n=this._pendingElement||this._currentElement;this._pendingElement=r.cloneAndReplaceProps(n,a({},n.props,t)),o.enqueueUpdate(this,e)},construct:function(t){this.props=t.props,this._owner=t._owner,this._lifeCycleState=l.UNMOUNTED,this._pendingCallbacks=null,this._currentElement=t,this._pendingElement=null},mountComponent:function(t,e,n){s(!this.isMounted());var r=this._currentElement.ref;if(null!=r){var o=this._currentElement._owner;i.addComponentAsRefTo(this,r,o)}this._rootNodeID=t,this._lifeCycleState=l.MOUNTED,this._mountDepth=n},unmountComponent:function(){s(this.isMounted());var t=this._currentElement.ref;null!=t&&i.removeComponentAsRefFrom(this,t,this._owner),h(this._rootNodeID),this._rootNodeID=null,this._lifeCycleState=l.UNMOUNTED},receiveComponent:function(t,e){s(this.isMounted()),this._pendingElement=t,this.performUpdateIfNecessary(e)},performUpdateIfNecessary:function(t){if(null!=this._pendingElement){var e=this._currentElement,n=this._pendingElement;this._currentElement=n,this.props=n.props,this._owner=n._owner,this._pendingElement=null,this.updateComponent(t,e)}},updateComponent:function(t,e){var n=this._currentElement;(n._owner!==e._owner||n.ref!==e.ref)&&(null!=e.ref&&i.removeComponentAsRefFrom(this,e.ref,e._owner),null!=n.ref&&i.addComponentAsRefTo(this,n.ref,n._owner))},mountComponentIntoNode:function(t,e,n){var r=o.ReactReconcileTransaction.getPooled();r.perform(this._mountComponentIntoNode,this,t,e,r,n),o.ReactReconcileTransaction.release(r)},_mountComponentIntoNode:function(t,e,n,r){var i=this.mountComponent(t,n,0);p(i,e,r)},isOwnedBy:function(t){return this._owner===t},getSiblingByRef:function(t){var e=this._owner;return e&&e.refs?e.refs[t]:null}}};t.exports=f},/*!*********************************************!*\ + !*** ./~/react/lib/ReactInstanceHandles.js ***! + \*********************************************/ +function(t,e,n){"use strict";function r(t){return f+t.toString(36)}function i(t,e){return t.charAt(e)===f||e===t.length}function o(t){return""===t||t.charAt(0)===f&&t.charAt(t.length-1)!==f}function a(t,e){return 0===e.indexOf(t)&&i(e,t.length)}function s(t){return t?t.substr(0,t.lastIndexOf(f)):""}function u(t,e){if(p(o(t)&&o(e)),p(a(t,e)),t===e)return t;for(var n=t.length+d,r=n;r=a;a++)if(i(t,a)&&i(e,a))r=a;else if(t.charAt(a)!==e.charAt(a))break;var s=t.substr(0,r);return p(o(s)),s}function c(t,e,n,r,i,o){t=t||"",e=e||"",p(t!==e);var l=a(e,t);p(l||a(t,e));for(var c=0,h=l?s:u,f=t;;f=h(f,e)){var d;if(i&&f===t||o&&f===e||(d=n(f,l,r)),d===!1||f===e)break;p(c++1){var e=t.indexOf(f,1);return e>-1?t.substr(0,e):t}return null},traverseEnterLeave:function(t,e,n,r,i){var o=l(t,e);o!==t&&c(t,o,n,r,!1,!0),o!==e&&c(o,e,n,i,!0,!1)},traverseTwoPhase:function(t,e,n){t&&(c("",t,e,n,!0,!1),c(t,"",e,n,!1,!0))},traverseAncestors:function(t,e,n){c("",t,e,n,!0,!1)},_getFirstCommonAncestorID:l,_getNextDescendantID:u,isAncestorIDOf:a,SEPARATOR:f};t.exports=v},/*!**********************************!*\ + !*** ./~/react/lib/keyMirror.js ***! + \**********************************/ +function(t,e,n){"use strict";var r=n(/*! ./invariant */1),i=function(t){var e,n={};r(t instanceof Object&&!Array.isArray(t));for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n};t.exports=i},/*!***************************************!*\ + !*** ./~/react/lib/EventPluginHub.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./EventPluginRegistry */66),i=n(/*! ./EventPluginUtils */42),o=n(/*! ./accumulateInto */47),a=n(/*! ./forEachAccumulated */50),s=n(/*! ./invariant */1),u={},l=null,c=function(t){if(t){var e=i.executeDispatch,n=r.getPluginModuleForEvent(t);n&&n.executeDispatch&&(e=n.executeDispatch),i.executeDispatchesInOrder(t,e),t.isPersistent()||t.constructor.release(t)}},h=null,p={injection:{injectMount:i.injection.injectMount,injectInstanceHandle:function(t){h=t},getInstanceHandle:function(){return h},injectEventPluginOrder:r.injectEventPluginOrder,injectEventPluginsByName:r.injectEventPluginsByName},eventNameDispatchConfigs:r.eventNameDispatchConfigs,registrationNameModules:r.registrationNameModules,putListener:function(t,e,n){s(!n||"function"==typeof n);var r=u[e]||(u[e]={});r[t]=n},getListener:function(t,e){var n=u[e];return n&&n[t]},deleteListener:function(t,e){var n=u[e];n&&delete n[t]},deleteAllListeners:function(t){for(var e in u)delete u[e][t]},extractEvents:function(t,e,n,i){for(var a,s=r.plugins,u=0,l=s.length;l>u;u++){var c=s[u];if(c){var h=c.extractEvents(t,e,n,i);h&&(a=o(a,h))}}return a},enqueueEvents:function(t){t&&(l=o(l,t))},processEventQueue:function(){var t=l;l=null,a(t,c),s(!l)},__purge:function(){u={}},__getListenerBank:function(){return u}};t.exports=p},/*!*******************************************!*\ + !*** ./~/react/lib/ReactLegacyElement.js ***! + \*******************************************/ +function(t,e,n){"use strict";function r(t,e){if("function"==typeof e)for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];if("function"==typeof r){var i=r.bind(e);for(var o in r)r.hasOwnProperty(o)&&(i[o]=r[o]);t[n]=i}else t[n]=r}}var i=(n(/*! ./ReactCurrentOwner */18),n(/*! ./invariant */1)),o=(n(/*! ./monitorCodeUse */40),n(/*! ./warning */4),{}),a={},s={};s.wrapCreateFactory=function(t){var e=function(e){return"function"!=typeof e?t(e):e.isReactNonLegacyFactory?t(e.type):e.isReactLegacyFactory?t(e.type):e};return e},s.wrapCreateElement=function(t){var e=function(e){if("function"!=typeof e)return t.apply(this,arguments);var n;return e.isReactNonLegacyFactory?(n=Array.prototype.slice.call(arguments,0),n[0]=e.type,t.apply(this,n)):e.isReactLegacyFactory?(e._isMockFunction&&(e.type._mockedReactClassConstructor=e),n=Array.prototype.slice.call(arguments,0),n[0]=e.type,t.apply(this,n)):e.apply(null,Array.prototype.slice.call(arguments,1))};return e},s.wrapFactory=function(t){i("function"==typeof t);var e=function(){return t.apply(this,arguments)};return r(e,t.type),e.isReactLegacyFactory=o,e.type=t.type,e},s.markNonLegacyFactory=function(t){return t.isReactNonLegacyFactory=a,t},s.isValidFactory=function(t){return"function"==typeof t&&t.isReactLegacyFactory===o},s.isValidClass=function(t){return s.isValidFactory(t)},s._isLegacyCallWarningEnabled=!0,t.exports=s},/*!*****************************************!*\ + !*** ./~/react/lib/SyntheticUIEvent.js ***! + \*****************************************/ +function(t,e,n){"use strict";function r(t,e,n){i.call(this,t,e,n)}var i=n(/*! ./SyntheticEvent */19),o=n(/*! ./getEventTarget */53),a={view:function(t){if(t.view)return t.view;var e=o(t);if(null!=e&&e.window===e)return e;var n=e.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(t){return t.detail||0}};i.augmentClass(r,a),t.exports=r},/*!********************************!*\ + !*** ./lib/actions/Actions.js ***! + \********************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! ../dispatchers/UserMatrixDispatcher */60)),o=r(n(/*! ../constants/ActionTypes */58)),a={changeDateRange:function(t){i.handleViewAction({actionType:o.DATE_RANGE_UPDATE,data:t})},changeContentState:function(t){i.handleViewAction({actionType:o.CONTENT_STATE_UPDATE,data:t})},onDaySelected:function(t){i.handleViewAction({actionType:o.DAY_SELECTED,data:t})},clearSelectedDay:function(){i.handleViewAction({actionType:o.DAY_SELECTED,data:null})}};t.exports=a},/*!*********************************!*\ + !*** ./lib/utils/DateHelper.js ***! + \*********************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! moment-range */61)),o={dateFormat:"YYYY-MM-DD",getDateRangeFromOption:function(t){var e,n,r,o=i(),a=this.dateFormat,s=[];switch(t){case"This Week":n=i().weekday(0),r=i().weekday(6);break;case"Last Week":n=i().weekday(-7),r=i().weekday(-1);break;case"This Month":n=i().date(1),r=i().month(o.month()+1).date(0);break;case"Last Month":n=i().month(o.month()-1).date(1),r=i().date(0);break;default:console.error("selectedDateRange [%s] can not be matched. Using (This Week) instead.",t),n=i().weekday(0),r=i()}return e=i().range(n,r),e.by("days",function(t){s.push(t.format(a))}),{fromDate:n.format(a),toDate:r.format(a),dates:s}},dayAsLabel:function(t,e,n){var r,o,a=i(t);return r=n?"dddd (Do MMM)":"ddd",o=n?"Do MMM (dddd)":"D",a.format(8>e?r:o)},isInFuture:function(t){return i(t).isAfter(i())}};t.exports=o},/*!***************************!*\ + !*** ./~/react/addons.js ***! + \***************************/ +function(t,e,n){t.exports=n(/*! ./lib/ReactWithAddons */164)},/*!***************************************!*\ + !*** ./~/react/lib/AutoFocusMixin.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./focusNode */86),i={componentDidMount:function(){this.props.autoFocus&&r(this.getDOMNode())}};t.exports=i},/*!********************************************!*\ + !*** ./~/react/lib/ReactEmptyComponent.js ***! + \********************************************/ +function(t,e,n){"use strict";function r(){return l(s),s()}function i(t){c[t]=!0}function o(t){delete c[t]}function a(t){return c[t]}var s,u=n(/*! ./ReactElement */3),l=n(/*! ./invariant */1),c={},h={injectEmptyComponent:function(t){s=u.createFactory(t)}},p={deregisterNullComponentID:o,getEmptyComponent:r,injection:h,isNullComponentID:a,registerNullComponentID:i};t.exports=p},/*!********************************************!*\ + !*** ./~/react/lib/SyntheticMouseEvent.js ***! + \********************************************/ +function(t,e,n){"use strict";function r(t,e,n){i.call(this,t,e,n)}var i=n(/*! ./SyntheticUIEvent */31),o=n(/*! ./ViewportMetrics */83),a=n(/*! ./getEventModifierState */52),s={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(t){var e=t.button;return"which"in t?e:2===e?2:4===e?1:0},buttons:null,relatedTarget:function(t){return t.relatedTarget||(t.fromElement===t.srcElement?t.toElement:t.fromElement)},pageX:function(t){return"pageX"in t?t.pageX:t.clientX+o.currentScrollLeft},pageY:function(t){return"pageY"in t?t.pageY:t.clientY+o.currentScrollTop}};i.augmentClass(r,s),t.exports=r},/*!************************************!*\ + !*** ./~/react/lib/Transaction.js ***! + \************************************/ +function(t,e,n){"use strict";var r=n(/*! ./invariant */1),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(t,e,n,i,o,a,s,u){r(!this.isInTransaction());var l,c;try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=t.call(e,n,i,o,a,s,u),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(h){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(t){for(var e=this.transactionWrappers,n=t;nn;n++)t[n].call(e[n]);t.length=0,e.length=0}},reset:function(){this._callbacks=null,this._contexts=null},destructor:function(){this.reset()}}),i.addPoolingTo(r),t.exports=r},/*!*****************************************!*\ + !*** ./~/react/lib/EventPluginUtils.js ***! + \*****************************************/ +function(t,e,n){"use strict";function r(t){return t===v.topMouseUp||t===v.topTouchEnd||t===v.topTouchCancel}function i(t){return t===v.topMouseMove||t===v.topTouchMove}function o(t){return t===v.topMouseDown||t===v.topTouchStart}function a(t,e){var n=t._dispatchListeners,r=t._dispatchIDs;if(Array.isArray(n))for(var i=0;i.";var l=null;n._owner&&n._owner!==p.current&&(l=n._owner.constructor.displayName,e+=" It was passed a child from "+l+"."),e+=" See http://fb.me/react-warning-keys for more information.",f(t,{component:s,componentOwner:l}),console.warn(e)}}function s(){var t=r()||"";m.hasOwnProperty(t)||(m[t]=!0,f("react_object_map_children"))}function u(t,e){if(Array.isArray(t))for(var n=0;n":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;t.exports=n},/*!*******************************************!*\ + !*** ./~/react/lib/forEachAccumulated.js ***! + \*******************************************/ +function(t){"use strict";var e=function(t,e,n){Array.isArray(t)?t.forEach(e,n):t&&e.call(n,t)};t.exports=e},/*!*****************************************!*\ + !*** ./~/react/lib/getEventCharCode.js ***! + \*****************************************/ +function(t){"use strict";function e(t){var e,n=t.keyCode;return"charCode"in t?(e=t.charCode,0===e&&13===n&&(e=13)):e=n,e>=32||13===e?e:0}t.exports=e},/*!**********************************************!*\ + !*** ./~/react/lib/getEventModifierState.js ***! + \**********************************************/ +function(t){"use strict";function e(t){var e=this,n=e.nativeEvent;if(n.getModifierState)return n.getModifierState(t);var i=r[t];return i?!!n[i]:!1}function n(){return e}var r={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};t.exports=n},/*!***************************************!*\ + !*** ./~/react/lib/getEventTarget.js ***! + \***************************************/ +function(t){"use strict";function e(t){var e=t.target||t.srcElement||window;return 3===e.nodeType?e.parentNode:e}t.exports=e},/*!***********************************************!*\ + !*** ./~/react/lib/getTextContentAccessor.js ***! + \***********************************************/ +function(t,e,n){"use strict";function r(){return!o&&i.canUseDOM&&(o="textContent"in document.documentElement?"textContent":"innerText"),o}var i=n(/*! ./ExecutionEnvironment */5),o=null;t.exports=r},/*!*****************************************!*\ + !*** ./~/react/lib/isEventSupported.js ***! + \*****************************************/ +function(t,e,n){"use strict";/** + * Checks if an event is supported in the current execution environment. + * + * NOTE: This will not work correctly for non-generic events such as `change`, + * `reset`, `load`, `error`, and `select`. + * + * Borrows from Modernizr. + * + * @param {string} eventNameSuffix Event name, e.g. "click". + * @param {?boolean} capture Check if the capture phase is supported. + * @return {boolean} True if the event is supported. + * @internal + * @license Modernizr 3.0.0pre (Custom Build) | MIT + */ +function r(t,e){if(!o.canUseDOM||e&&!("addEventListener"in document))return!1;var n="on"+t,r=n in document;if(!r){var a=document.createElement("div");a.setAttribute(n,"return;"),r="function"==typeof a[n]}return!r&&i&&"wheel"===t&&(r=document.implementation.hasFeature("Events.wheel","3.0")),r}var i,o=n(/*! ./ExecutionEnvironment */5);o.canUseDOM&&(i=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),t.exports=r},/*!***************************************************!*\ + !*** ./~/react/lib/shouldUpdateReactComponent.js ***! + \***************************************************/ +function(t){"use strict";function e(t,e){return t&&e&&t.type===e.type&&t.key===e.key&&t._owner===e._owner?!0:!1}t.exports=e},/*!***********************************!*\ + !*** (webpack)/buildin/module.js ***! + \***********************************/ +function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},/*!**************************************!*\ + !*** ./lib/constants/ActionTypes.js ***! + \**************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! keymirror */116)),o=i({DATE_RANGE_UPDATE:null,CONTENT_STATE_UPDATE:null,DAY_SELECTED:null});t.exports=o},/*!**********************************!*\ + !*** ./lib/constants/Configs.js ***! + \**********************************/ +function(t){"use strict";var e={baseUrl:null};t.exports=e},/*!*************************************************!*\ + !*** ./lib/dispatchers/UserMatrixDispatcher.js ***! + \*************************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! object-assign */63)),o=n(/*! flux */113).Dispatcher,a=i({},new o,o.prototype,{handleViewAction:function(t){this.dispatch({source:"VIEW_ACTION",action:t})}});t.exports=a},/*!********************************************!*\ + !*** ./~/moment-range/lib/moment-range.js ***! + \********************************************/ +function(t,e,n){!function(e,r){t.exports=r(n(/*! moment */62))}(this,function(t){var e,n;return n={year:!0,month:!0,week:!0,day:!0,hour:!0,minute:!0,second:!0},e=function(){function e(e,n){this.start=t(e),this.end=t(n)}return e.prototype.contains=function(t){return t instanceof e?this.start<=t.start&&this.end>=t.end:this.start<=t&&t<=this.end},e.prototype._by_string=function(e,n){var r,i;for(r=t(this.start),i=[];this.contains(r);)n.call(this,r.clone()),i.push(r.add(1,e));return i},e.prototype._by_range=function(e,n){var r,i,o,a;if(i=Math.floor(this/e),1/0===i)return this;for(a=[],r=o=0;i>=0?i>=o:o>=i;r=i>=0?++o:--o)a.push(n.call(this,t(this.start.valueOf()+e.valueOf()*r)));return a},e.prototype.overlaps=function(t){return null!==this.intersect(t)},e.prototype.intersect=function(t){var n,r,i,o,a,s,u,l;return this.start<=(r=t.start)&&r<(n=this.end)&&ne-o?(n=t.clone().add(i-1,"months"),r=(e-o)/(o-n)):(n=t.clone().add(i+1,"months"),r=(e-o)/(n-o)),-(i+r)}function m(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?(r=t.isPM(n),r&&12>e&&(e+=12),r||12!==e||(e=0),e):e}function v(){}function g(t,e){e!==!1&&U(t),b(this,t),this._d=new Date(+t._d),Sn===!1&&(Sn=!0,Se.updateOffset(this),Sn=!1)}function y(t){var e=O(t),n=e.year||0,r=e.quarter||0,i=e.month||0,o=e.week||0,a=e.day||0,s=e.hour||0,u=e.minute||0,l=e.second||0,c=e.millisecond||0;this._milliseconds=+c+1e3*l+6e4*u+36e5*s,this._days=+a+7*o,this._months=+i+3*r+12*n,this._data={},this._locale=Se.localeData(),this._bubble()}function _(t,e){for(var n in e)s(e,n)&&(t[n]=e[n]);return s(e,"toString")&&(t.toString=e.toString),s(e,"valueOf")&&(t.valueOf=e.valueOf),t}function b(t,e){var n,r,i;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ye.length>0)for(n in Ye)r=Ye[n],i=e[r],"undefined"!=typeof i&&(t[r]=i);return t}function w(t){return 0>t?Math.ceil(t):Math.floor(t)}function C(t,e,n){for(var r=""+Math.abs(t),i=t>=0;r.lengthr;r++)(n&&t[r]!==e[r]||!n&&A(t[r])!==A(e[r]))&&a++;return a+o}function k(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=gn[t]||yn[e]||e}return t}function O(t){var e,n,r={};for(n in t)s(t,n)&&(e=k(n),e&&(r[e]=t[n]));return r}function R(t){var e,n;if(0===t.indexOf("week"))e=7,n="day";else{if(0!==t.indexOf("month"))return;e=12,n="month"}Se[t]=function(r,i){var a,s,u=Se._locale[t],l=[];if("number"==typeof r&&(i=r,r=o),s=function(t){var e=Se().utc().set(n,t);return u.call(Se._locale,e,r||"")},null!=i)return s(i);for(a=0;e>a;a++)l.push(s(a));return l}}function A(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=e>=0?Math.floor(e):Math.ceil(e)),n}function L(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function N(t,e,n){return de(Se([t,11,31+e-n]),e,n).week}function I(t){return F(t)?366:365}function F(t){return t%4===0&&t%100!==0||t%400===0}function U(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[Le]<0||t._a[Le]>11?Le:t._a[Ne]<1||t._a[Ne]>L(t._a[Ae],t._a[Le])?Ne:t._a[Ie]<0||t._a[Ie]>24||24===t._a[Ie]&&(0!==t._a[Fe]||0!==t._a[Ue]||0!==t._a[We])?Ie:t._a[Fe]<0||t._a[Fe]>59?Fe:t._a[Ue]<0||t._a[Ue]>59?Ue:t._a[We]<0||t._a[We]>999?We:-1,t._pf._overflowDayOfYear&&(Ae>e||e>Ne)&&(e=Ne),t._pf.overflow=e)}function W(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length&&t._pf.bigHour===o)),t._isValid}function B(t){return t?t.toLowerCase().replace("_","-"):t}function Y(t){for(var e,n,r,i,o=0;o0;){if(r=j(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&P(i,n,!0)>=e-1)break;e--}o++}return null}function j(t){var e=null;if(!Be[t]&&je)try{e=Se.locale(),!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),Se.locale(e)}catch(n){}return Be[t]}function z(t,e){var n,r;return e._isUTC?(n=e.clone(),r=(Se.isMoment(t)||T(t)?+t:+Se(t))-+n,n._d.setTime(+n._d+r),Se.updateOffset(n,!1),n):Se(t).local()}function V(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function H(t){var e,n,r=t.match(Ge);for(e=0,n=r.length;n>e;e++)r[e]=xn[r[e]]?xn[r[e]]:V(r[e]);return function(i){var o="";for(e=0;n>e;e++)o+=r[e]instanceof Function?r[e].call(i,t):r[e];return o}}function G(t,e){return t.isValid()?(e=q(e,t.localeData()),_n[e]||(_n[e]=H(e)),_n[e](t)):t.localeData().invalidDate()}function q(t,e){function n(t){return e.longDateFormat(t)||t}var r=5;for(qe.lastIndex=0;r>=0&&qe.test(t);)t=t.replace(qe,n),qe.lastIndex=0,r-=1;return t}function K(t,e){var n,r=e._strict;switch(t){case"Q":return on;case"DDDD":return sn;case"YYYY":case"GGGG":case"gggg":return r?un:$e;case"Y":case"G":case"g":return cn;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return r?ln:Qe;case"S":if(r)return on;case"SS":if(r)return an;case"SSS":if(r)return sn;case"DDD":return Xe;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Je;case"a":case"A":return e._locale._meridiemParse;case"x":return nn;case"X":return rn;case"Z":case"ZZ":return tn;case"T":return en;case"SSSS":return Ze;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return r?an:Ke;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Ke;case"Do":return r?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return n=new RegExp(re(ne(t.replace("\\","")),"i"))}}function X(t){t=t||"";var e=t.match(tn)||[],n=e[e.length-1]||[],r=(n+"").match(mn)||["-",0,0],i=+(60*r[1])+A(r[2]);return"+"===r[0]?i:-i}function $(t,e,n){var r,i=n._a;switch(t){case"Q":null!=e&&(i[Le]=3*(A(e)-1));break;case"M":case"MM":null!=e&&(i[Le]=A(e)-1);break;case"MMM":case"MMMM":r=n._locale.monthsParse(e,t,n._strict),null!=r?i[Le]=r:n._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(i[Ne]=A(e));break;case"Do":null!=e&&(i[Ne]=A(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(n._dayOfYear=A(e));break;case"YY":i[Ae]=Se.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":i[Ae]=A(e);break;case"a":case"A":n._meridiem=e;break;case"h":case"hh":n._pf.bigHour=!0;case"H":case"HH":i[Ie]=A(e);break;case"m":case"mm":i[Fe]=A(e);break;case"s":case"ss":i[Ue]=A(e);break;case"S":case"SS":case"SSS":case"SSSS":i[We]=A(1e3*("0."+e));break;case"x":n._d=new Date(A(e));break;case"X":n._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=X(e);break;case"dd":case"ddd":case"dddd":r=n._locale.weekdaysParse(e),null!=r?(n._w=n._w||{},n._w.d=r):n._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(n._w=n._w||{},n._w[t]=A(e));break;case"gg":case"GG":n._w=n._w||{},n._w[t]=Se.parseTwoDigitYear(e)}}function Q(t){var e,n,r,i,o,s,u;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(o=1,s=4,n=a(e.GG,t._a[Ae],de(Se(),1,4).year),r=a(e.W,1),i=a(e.E,1)):(o=t._locale._week.dow,s=t._locale._week.doy,n=a(e.gg,t._a[Ae],de(Se(),o,s).year),r=a(e.w,1),null!=e.d?(i=e.d,o>i&&++r):i=null!=e.e?e.e+o:o),u=me(n,r,i,s,o),t._a[Ae]=u.year,t._dayOfYear=u.dayOfYear}function Z(t){var e,n,r,i,o=[];if(!t._d){for(r=te(t),t._w&&null==t._a[Ne]&&null==t._a[Le]&&Q(t),t._dayOfYear&&(i=a(t._a[Ae],r[Ae]),t._dayOfYear>I(i)&&(t._pf._overflowDayOfYear=!0),n=ce(i,0,t._dayOfYear),t._a[Le]=n.getUTCMonth(),t._a[Ne]=n.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=o[e]=r[e];for(;7>e;e++)t._a[e]=o[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Ie]&&0===t._a[Fe]&&0===t._a[Ue]&&0===t._a[We]&&(t._nextDay=!0,t._a[Ie]=0),t._d=(t._useUTC?ce:le).apply(null,o),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Ie]=24)}}function J(t){var e;t._d||(e=O(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],Z(t))}function te(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function ee(t){if(t._f===Se.ISO_8601)return void oe(t);t._a=[],t._pf.empty=!0;var e,n,r,i,a,s=""+t._i,u=s.length,l=0;for(r=q(t._f,t._locale).match(Ge)||[],e=0;e0&&t._pf.unusedInput.push(a),s=s.slice(s.indexOf(n)+n.length),l+=n.length),xn[i]?(n?t._pf.empty=!1:t._pf.unusedTokens.push(i),$(i,n,t)):t._strict&&!n&&t._pf.unusedTokens.push(i);t._pf.charsLeftOver=u-l,s.length>0&&t._pf.unusedInput.push(s),t._pf.bigHour===!0&&t._a[Ie]<=12&&(t._pf.bigHour=o),t._a[Ie]=m(t._locale,t._a[Ie],t._meridiem),Z(t),U(t)}function ne(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,r,i){return e||n||r||i})}function re(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ie(t){var e,n,r,i,o;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(i=0;io)&&(r=o,n=e));_(t,n||e)}function oe(t){var e,n,r=t._i,i=hn.exec(r);if(i){for(t._pf.iso=!0,e=0,n=fn.length;n>e;e++)if(fn[e][1].exec(r)){t._f=fn[e][0]+(i[6]||" ");break}for(e=0,n=dn.length;n>e;e++)if(dn[e][1].exec(r)){t._f+=dn[e][0];break}r.match(tn)&&(t._f+="Z"),ee(t)}else t._isValid=!1}function ae(t){oe(t),t._isValid===!1&&(delete t._isValid,Se.createFromInputFallback(t))}function se(t,e){var n,r=[];for(n=0;nt&&s.setFullYear(t),s}function ce(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function he(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function pe(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}function fe(t,e,n){var r=Se.duration(t).abs(),i=Oe(r.as("s")),o=Oe(r.as("m")),a=Oe(r.as("h")),s=Oe(r.as("d")),u=Oe(r.as("M")),l=Oe(r.as("y")),c=i0,c[4]=n,pe.apply({},c)}function de(t,e,n){var r,i=n-e,o=n-t.day();return o>i&&(o-=7),i-7>o&&(o+=7),r=Se(t).add(o,"d"),{week:Math.ceil(r.dayOfYear()/7),year:r.year()}}function me(t,e,n,r,i){var o,a,s=ce(t,0,1).getUTCDay();return s=0===s?7:s,n=null!=n?n:i,o=i-s+(s>r?7:0)-(i>s?7:0),a=7*(e-1)+(n-i)+o+1,{year:a>0?t:t-1,dayOfYear:a>0?a:I(t-1)+a}}function ve(t){var e,n=t._i,r=t._f;return t._locale=t._locale||Se.localeData(t._l),null===n||r===o&&""===n?Se.invalid({nullInput:!0}):("string"==typeof n&&(t._i=n=t._locale.preparse(n)),Se.isMoment(n)?new g(n,!0):(r?M(r)?ie(t):ee(t):ue(t),e=new g(t),e._nextDay&&(e.add(1,"d"),e._nextDay=o),e))}function ge(t,e){var n,r;if(1===e.length&&M(e[0])&&(e=e[0]),!e.length)return Se();for(n=e[0],r=1;r=0?"+":"-";return e+C(Math.abs(t),6)},gg:function(){return C(this.weekYear()%100,2)},gggg:function(){return C(this.weekYear(),4)},ggggg:function(){return C(this.weekYear(),5)},GG:function(){return C(this.isoWeekYear()%100,2)},GGGG:function(){return C(this.isoWeekYear(),4)},GGGGG:function(){return C(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return A(this.milliseconds()/100)},SS:function(){return C(A(this.milliseconds()/10),2)},SSS:function(){return C(this.milliseconds(),3)},SSSS:function(){return C(this.milliseconds(),3)},Z:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+C(A(t/60),2)+":"+C(A(t)%60,2)},ZZ:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+C(A(t/60),2)+C(A(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},En={},Dn=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],Sn=!1;wn.length;)Te=wn.pop(),xn[Te+"o"]=f(xn[Te],Te);for(;Cn.length;)Te=Cn.pop(),xn[Te+Te]=p(xn[Te],2);xn.DDDD=p(xn.DDD,3),_(v.prototype,{set:function(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,n){var r,i,o;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;12>r;r++){if(i=Se.utc([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(o="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(o.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,n,r;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(n=Se([2e3,1]).day(e),r="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(r.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,n){var r=this._calendar[t];return"function"==typeof r?r.apply(e,[n]):r},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,n,r){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,r):i.replace(/%d/i,t)},pastFuture:function(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return de(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Se=function(t,e,n,r){var i;return"boolean"==typeof n&&(r=n,n=o),i={},i._isAMomentObject=!0,i._i=t,i._f=e,i._l=n,i._strict=r,i._isUTC=!1,i._pf=u(),ve(i)},Se.suppressDeprecationWarnings=!1,Se.createFromInputFallback=c("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Se.min=function(){var t=[].slice.call(arguments,0);return ge("isBefore",t)},Se.max=function(){var t=[].slice.call(arguments,0);return ge("isAfter",t)},Se.utc=function(t,e,n,r){var i;return"boolean"==typeof n&&(r=n,n=o),i={},i._isAMomentObject=!0,i._useUTC=!0,i._isUTC=!0,i._l=n,i._i=t,i._f=e,i._strict=r,i._pf=u(),ve(i).utc()},Se.unix=function(t){return Se(1e3*t)},Se.duration=function(t,e){var n,r,i,o,a=t,u=null;return Se.isDuration(t)?a={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(a={},e?a[e]=t:a.milliseconds=t):(u=Ve.exec(t))?(n="-"===u[1]?-1:1,a={y:0,d:A(u[Ne])*n,h:A(u[Ie])*n,m:A(u[Fe])*n,s:A(u[Ue])*n,ms:A(u[We])*n}):(u=He.exec(t))?(n="-"===u[1]?-1:1,i=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*n},a={y:i(u[2]),M:i(u[3]),d:i(u[4]),h:i(u[5]),m:i(u[6]),s:i(u[7]),w:i(u[8])}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(o=E(Se(a.from),Se(a.to)),a={},a.ms=o.milliseconds,a.M=o.months),r=new y(a),Se.isDuration(t)&&s(t,"_locale")&&(r._locale=t._locale),r},Se.version=Pe,Se.defaultFormat=pn,Se.ISO_8601=function(){},Se.momentProperties=Ye,Se.updateOffset=function(){},Se.relativeTimeThreshold=function(t,e){return bn[t]===o?!1:e===o?bn[t]:(bn[t]=e,!0)},Se.lang=c("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Se.locale(t,e)}),Se.locale=function(t,e){var n;return t&&(n="undefined"!=typeof e?Se.defineLocale(t,e):Se.localeData(t),n&&(Se.duration._locale=Se._locale=n)),Se._locale._abbr},Se.defineLocale=function(t,e){return null!==e?(e.abbr=t,Be[t]||(Be[t]=new v),Be[t].set(e),Se.locale(t),Be[t]):(delete Be[t],null)},Se.langData=c("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Se.localeData(t)}),Se.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Se._locale;if(!M(t)){if(e=j(t))return e;t=[t]}return Y(t)},Se.isMoment=function(t){return t instanceof g||null!=t&&s(t,"_isAMomentObject")},Se.isDuration=function(t){return t instanceof y};for(Te=Dn.length-1;Te>=0;--Te)R(Dn[Te]);Se.normalizeUnits=function(t){return k(t)},Se.invalid=function(t){var e=Se.utc(0/0);return null!=t?_(e._pf,t):e._pf.userInvalidated=!0,e},Se.parseZone=function(){return Se.apply(null,arguments).parseZone()},Se.parseTwoDigitYear=function(t){return A(t)+(A(t)>68?1900:2e3)},Se.isDate=T,_(Se.fn=g.prototype,{clone:function(){return Se(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Se(this).utc();return 00:!1},parsingFlags:function(){return _({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.utcOffset(0,t)},local:function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(t){var e=G(this,t||Se.defaultFormat);return this.localeData().postformat(e)},add:D(1,"add"),subtract:D(-1,"subtract"),diff:function(t,e,n){var r,i,o=z(t,this),a=6e4*(o.utcOffset()-this.utcOffset());return e=k(e),"year"===e||"month"===e||"quarter"===e?(i=d(this,o),"quarter"===e?i/=3:"year"===e&&(i/=12)):(r=this-o,i="second"===e?r/1e3:"minute"===e?r/6e4:"hour"===e?r/36e5:"day"===e?(r-a)/864e5:"week"===e?(r-a)/6048e5:r),n?i:w(i)},from:function(t,e){return Se.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Se(),t)},calendar:function(t){var e=t||Se(),n=z(e,this).startOf("day"),r=this.diff(n,"days",!0),i=-6>r?"sameElse":-1>r?"lastWeek":0>r?"lastDay":1>r?"sameDay":2>r?"nextDay":7>r?"nextWeek":"sameElse";return this.format(this.localeData().calendar(i,this,Se(e)))},isLeapYear:function(){return F(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=he(t,this.localeData()),this.add(t-e,"d")):e},month:we("Month",!0),startOf:function(t){switch(t=k(t)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t?this.weekday(0):"isoWeek"===t&&this.isoWeekday(1),"quarter"===t&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(t){return t=k(t),t===o||"millisecond"===t?this:this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms")},isAfter:function(t,e){var n;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Se.isMoment(t)?t:Se(t),+this>+t):(n=Se.isMoment(t)?+t:+Se(t),n<+this.clone().startOf(e))},isBefore:function(t,e){var n;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Se.isMoment(t)?t:Se(t),+t>+this):(n=Se.isMoment(t)?+t:+Se(t),+this.clone().endOf(e)t?this:t}),max:c("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Se.apply(null,arguments),t>this?this:t}),zone:c("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),utcOffset:function(t,e){var n,r=this._offset||0;return null!=t?("string"==typeof t&&(t=X(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(n=this._dateUtcOffset()),this._offset=t,this._isUTC=!0,null!=n&&this.add(n,"m"),r!==t&&(!e||this._changeInProgress?S(this,Se.duration(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Se.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?r:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(X(this._i)),this},hasAlignedHourOffset:function(t){return t=t?Se(t).utcOffset():0,(this.utcOffset()-t)%60===0},daysInMonth:function(){return L(this.year(),this.month())},dayOfYear:function(t){var e=Oe((Se(this).startOf("day")-Se(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=de(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=de(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=de(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return N(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return N(this.year(),t.dow,t.doy)},get:function(t){return t=k(t),this[t]()},set:function(t,e){var n;if("object"==typeof t)for(n in t)this.set(n,t[n]);else t=k(t),"function"==typeof this[t]&&this[t](e);return this},locale:function(t){var e;return t===o?this._locale._abbr:(e=Se.localeData(t),null!=e&&(this._locale=e),this)},lang:c("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return t===o?this.localeData():this.locale(t)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),Se.fn.millisecond=Se.fn.milliseconds=we("Milliseconds",!1),Se.fn.second=Se.fn.seconds=we("Seconds",!1),Se.fn.minute=Se.fn.minutes=we("Minutes",!1),Se.fn.hour=Se.fn.hours=we("Hours",!0),Se.fn.date=we("Date",!0),Se.fn.dates=c("dates accessor is deprecated. Use date instead.",we("Date",!0)),Se.fn.year=we("FullYear",!0),Se.fn.years=c("years accessor is deprecated. Use year instead.",we("FullYear",!0)),Se.fn.days=Se.fn.day,Se.fn.months=Se.fn.month,Se.fn.weeks=Se.fn.week,Se.fn.isoWeeks=Se.fn.isoWeek,Se.fn.quarters=Se.fn.quarter,Se.fn.toJSON=Se.fn.toISOString,Se.fn.isUTC=Se.fn.isUtc,_(Se.duration.fn=y.prototype,{_bubble:function(){var t,e,n,r=this._milliseconds,i=this._days,o=this._months,a=this._data,s=0;a.milliseconds=r%1e3,t=w(r/1e3),a.seconds=t%60,e=w(t/60),a.minutes=e%60,n=w(e/60),a.hours=n%24,i+=w(n/24),s=w(Ce(i)),i-=w(xe(s)),o+=w(i/30),i%=30,s+=w(o/12),o%=12,a.days=i,a.months=o,a.years=s},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return w(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*A(this._months/12) +},humanize:function(t){var e=fe(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var n=Se.duration(t,e);return this._milliseconds+=n._milliseconds,this._days+=n._days,this._months+=n._months,this._bubble(),this},subtract:function(t,e){var n=Se.duration(t,e);return this._milliseconds-=n._milliseconds,this._days-=n._days,this._months-=n._months,this._bubble(),this},get:function(t){return t=k(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,n;if(t=k(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,n=this._months+12*Ce(e),"month"===t?n:n/12;switch(e=this._days+Math.round(xe(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second":return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Se.fn.lang,locale:Se.fn.locale,toIsoString:c("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),n=Math.abs(this.days()),r=Math.abs(this.hours()),i=Math.abs(this.minutes()),o=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(n?n+"D":"")+(r||i||o?"T":"")+(r?r+"H":"")+(i?i+"M":"")+(o?o+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),Se.duration.fn.toString=Se.duration.fn.toISOString;for(Te in vn)s(vn,Te)&&Ee(Te.toLowerCase());Se.duration.fn.asMilliseconds=function(){return this.as("ms")},Se.duration.fn.asSeconds=function(){return this.as("s")},Se.duration.fn.asMinutes=function(){return this.as("m")},Se.duration.fn.asHours=function(){return this.as("h")},Se.duration.fn.asDays=function(){return this.as("d")},Se.duration.fn.asWeeks=function(){return this.as("weeks")},Se.duration.fn.asMonths=function(){return this.as("M")},Se.duration.fn.asYears=function(){return this.as("y")},Se.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,n=1===A(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+n}}),je?i.exports=Se:(r=function(t,e,n){return n.config&&n.config()&&n.config().noGlobal===!0&&(ke.moment=Me),Se}.call(e,n,e,i),!(r!==o&&(i.exports=r)),De(!0))}).call(this)}).call(e,function(){return this}(),n(/*! (webpack)/buildin/module.js */57)(t))},/*!**********************************!*\ + !*** ./~/object-assign/index.js ***! + \**********************************/ +function(t){"use strict";function e(t){if(null==t)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}t.exports=Object.assign||function(t){for(var n,r,i=e(t),o=1;o-1),!l.plugins[n]){a(e.extractEvents),l.plugins[n]=e;var r=e.eventTypes;for(var o in r)a(i(r[o],e,o))}}}function i(t,e,n){a(!l.eventNameDispatchConfigs.hasOwnProperty(n)),l.eventNameDispatchConfigs[n]=t;var r=t.phasedRegistrationNames;if(r){for(var i in r)if(r.hasOwnProperty(i)){var s=r[i];o(s,e,n)}return!0}return t.registrationName?(o(t.registrationName,e,n),!0):!1}function o(t,e,n){a(!l.registrationNameModules[t]),l.registrationNameModules[t]=e,l.registrationNameDependencies[t]=e.eventTypes[n].dependencies}var a=n(/*! ./invariant */1),s=null,u={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},injectEventPluginOrder:function(t){a(!s),s=Array.prototype.slice.call(t),r()},injectEventPluginsByName:function(t){var e=!1;for(var n in t)if(t.hasOwnProperty(n)){var i=t[n];u.hasOwnProperty(n)&&u[n]===i||(a(!u[n]),u[n]=i,e=!0)}e&&r()},getPluginModuleForEvent:function(t){var e=t.dispatchConfig;if(e.registrationName)return l.registrationNameModules[e.registrationName]||null;for(var n in e.phasedRegistrationNames)if(e.phasedRegistrationNames.hasOwnProperty(n)){var r=l.registrationNameModules[e.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){s=null;for(var t in u)u.hasOwnProperty(t)&&delete u[t];l.plugins.length=0;var e=l.eventNameDispatchConfigs;for(var n in e)e.hasOwnProperty(n)&&delete e[n];var r=l.registrationNameModules;for(var i in r)r.hasOwnProperty(i)&&delete r[i]}};t.exports=l},/*!********************************************!*\ + !*** ./~/react/lib/LocalEventTrapMixin.js ***! + \********************************************/ +function(t,e,n){"use strict";function r(t){t.remove()}var i=n(/*! ./ReactBrowserEventEmitter */25),o=n(/*! ./accumulateInto */47),a=n(/*! ./forEachAccumulated */50),s=n(/*! ./invariant */1),u={trapBubbledEvent:function(t,e){s(this.isMounted());var n=i.trapBubbledEvent(t,e,this.getDOMNode());this._localEventListeners=o(this._localEventListeners,n)},componentWillUnmount:function(){this._localEventListeners&&a(this._localEventListeners,r)}};t.exports=u},/*!**************************************!*\ + !*** ./~/react/lib/ReactChildren.js ***! + \**************************************/ +function(t,e,n){"use strict";function r(t,e){this.forEachFunction=t,this.forEachContext=e}function i(t,e,n,r){var i=t;i.forEachFunction.call(i.forEachContext,e,r)}function o(t,e,n){if(null==t)return t;var o=r.getPooled(e,n);p(t,i,o),r.release(o)}function a(t,e,n){this.mapResult=t,this.mapFunction=e,this.mapContext=n}function s(t,e,n,r){var i=t,o=i.mapResult,a=!o.hasOwnProperty(n);if(a){var s=i.mapFunction.call(i.mapContext,e,r);o[n]=s}}function u(t,e,n){if(null==t)return t;var r={},i=a.getPooled(r,e,n);return p(t,s,i),a.release(i),r}function l(){return null}function c(t){return p(t,l,null)}var h=n(/*! ./PooledClass */14),p=n(/*! ./traverseAllChildren */97),f=(n(/*! ./warning */4),h.twoArgumentPooler),d=h.threeArgumentPooler;h.addPoolingTo(r,f),h.addPoolingTo(a,d);var m={forEach:o,map:u,count:c};t.exports=m},/*!******************************************!*\ + !*** ./~/react/lib/ReactDOMComponent.js ***! + \******************************************/ +function(t,e,n){"use strict";function r(t){t&&(y(null==t.children||null==t.dangerouslySetInnerHTML),y(null==t.style||"object"==typeof t.style))}function i(t,e,n,r){var i=f.findReactContainerForID(t);if(i){var o=i.nodeType===D?i.ownerDocument:i;w(e,o)}r.getPutListenerQueue().enqueuePutListener(t,e,n)}function o(t){P.call(T,t)||(y(M.test(t)),T[t]=!0)}function a(t){o(t),this._tag=t,this.tagName=t.toUpperCase()}var s=n(/*! ./CSSPropertyOperations */65),u=n(/*! ./DOMProperty */21),l=n(/*! ./DOMPropertyOperations */22),c=n(/*! ./ReactBrowserComponentMixin */12),h=n(/*! ./ReactComponent */26),p=n(/*! ./ReactBrowserEventEmitter */25),f=n(/*! ./ReactMount */13),d=n(/*! ./ReactMultiChild */71),m=n(/*! ./ReactPerf */16),v=n(/*! ./Object.assign */2),g=n(/*! ./escapeTextForBrowser */49),y=n(/*! ./invariant */1),_=(n(/*! ./isEventSupported */55),n(/*! ./keyOf */7)),b=(n(/*! ./monitorCodeUse */40),p.deleteListener),w=p.listenTo,C=p.registrationNameModules,x={string:!0,number:!0},E=_({style:null}),D=1,S={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},M=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,T={},P={}.hasOwnProperty;a.displayName="ReactDOMComponent",a.Mixin={mountComponent:m.measure("ReactDOMComponent","mountComponent",function(t,e,n){h.Mixin.mountComponent.call(this,t,e,n),r(this.props);var i=S[this._tag]?"":"";return this._createOpenTagMarkupAndPutListeners(e)+this._createContentMarkup(e)+i}),_createOpenTagMarkupAndPutListeners:function(t){var e=this.props,n="<"+this._tag;for(var r in e)if(e.hasOwnProperty(r)){var o=e[r];if(null!=o)if(C.hasOwnProperty(r))i(this._rootNodeID,r,o,t);else{r===E&&(o&&(o=e.style=v({},e.style)),o=s.createMarkupForStyles(o));var a=l.createMarkupForProperty(r,o);a&&(n+=" "+a)}}if(t.renderToStaticMarkup)return n+">";var u=l.createMarkupForID(this._rootNodeID);return n+" "+u+">"},_createContentMarkup:function(t){var e=this.props.dangerouslySetInnerHTML;if(null!=e){if(null!=e.__html)return e.__html}else{var n=x[typeof this.props.children]?this.props.children:null,r=null!=n?null:this.props.children;if(null!=n)return g(n);if(null!=r){var i=this.mountChildren(r,t);return i.join("")}}return""},receiveComponent:function(t,e){(t!==this._currentElement||null==t._owner)&&h.Mixin.receiveComponent.call(this,t,e)},updateComponent:m.measure("ReactDOMComponent","updateComponent",function(t,e){r(this._currentElement.props),h.Mixin.updateComponent.call(this,t,e),this._updateDOMProperties(e.props,t),this._updateDOMChildren(e.props,t)}),_updateDOMProperties:function(t,e){var n,r,o,a=this.props;for(n in t)if(!a.hasOwnProperty(n)&&t.hasOwnProperty(n))if(n===E){var s=t[n];for(r in s)s.hasOwnProperty(r)&&(o=o||{},o[r]="")}else C.hasOwnProperty(n)?b(this._rootNodeID,n):(u.isStandardName[n]||u.isCustomAttribute(n))&&h.BackendIDOperations.deletePropertyByID(this._rootNodeID,n);for(n in a){var l=a[n],c=t[n];if(a.hasOwnProperty(n)&&l!==c)if(n===E)if(l&&(l=a.style=v({},l)),c){for(r in c)!c.hasOwnProperty(r)||l&&l.hasOwnProperty(r)||(o=o||{},o[r]="");for(r in l)l.hasOwnProperty(r)&&c[r]!==l[r]&&(o=o||{},o[r]=l[r])}else o=l;else C.hasOwnProperty(n)?i(this._rootNodeID,n,l,e):(u.isStandardName[n]||u.isCustomAttribute(n))&&h.BackendIDOperations.updatePropertyByID(this._rootNodeID,n,l)}o&&h.BackendIDOperations.updateStylesByID(this._rootNodeID,o)},_updateDOMChildren:function(t,e){var n=this.props,r=x[typeof t.children]?t.children:null,i=x[typeof n.children]?n.children:null,o=t.dangerouslySetInnerHTML&&t.dangerouslySetInnerHTML.__html,a=n.dangerouslySetInnerHTML&&n.dangerouslySetInnerHTML.__html,s=null!=r?null:t.children,u=null!=i?null:n.children,l=null!=r||null!=o,c=null!=i||null!=a;null!=s&&null==u?this.updateChildren(null,e):l&&!c&&this.updateTextContent(""),null!=i?r!==i&&this.updateTextContent(""+i):null!=a?o!==a&&h.BackendIDOperations.updateInnerHTMLByID(this._rootNodeID,a):null!=u&&this.updateChildren(u,e)},unmountComponent:function(){this.unmountChildren(),p.deleteAllListeners(this._rootNodeID),h.Mixin.unmountComponent.call(this)}},v(a.prototype,h.Mixin,a.Mixin,d.Mixin,c),t.exports=a},/*!********************************************!*\ + !*** ./~/react/lib/ReactMarkupChecksum.js ***! + \********************************************/ +function(t,e,n){"use strict";var r=n(/*! ./adler32 */177),i={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(t){var e=r(t);return t.replace(">"," "+i.CHECKSUM_ATTR_NAME+'="'+e+'">')},canReuseMarkup:function(t,e){var n=e.getAttribute(i.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var o=r(t);return o===n}};t.exports=i},/*!****************************************!*\ + !*** ./~/react/lib/ReactMultiChild.js ***! + \****************************************/ +function(t,e,n){"use strict";function r(t,e,n){m.push({parentID:t,parentNode:null,type:c.INSERT_MARKUP,markupIndex:v.push(e)-1,textContent:null,fromIndex:null,toIndex:n})}function i(t,e,n){m.push({parentID:t,parentNode:null,type:c.MOVE_EXISTING,markupIndex:null,textContent:null,fromIndex:e,toIndex:n})}function o(t,e){m.push({parentID:t,parentNode:null,type:c.REMOVE_NODE,markupIndex:null,textContent:null,fromIndex:e,toIndex:null})}function a(t,e){m.push({parentID:t,parentNode:null,type:c.TEXT_CONTENT,markupIndex:null,textContent:e,fromIndex:null,toIndex:null})}function s(){m.length&&(l.BackendIDOperations.dangerouslyProcessChildrenUpdates(m,v),u())}function u(){m.length=0,v.length=0}var l=n(/*! ./ReactComponent */26),c=n(/*! ./ReactMultiChildUpdateTypes */72),h=n(/*! ./flattenChildren */186),p=n(/*! ./instantiateReactComponent */39),f=n(/*! ./shouldUpdateReactComponent */56),d=0,m=[],v=[],g={Mixin:{mountChildren:function(t,e){var n=h(t),r=[],i=0;this._renderedChildren=n;for(var o in n){var a=n[o];if(n.hasOwnProperty(o)){var s=p(a,null);n[o]=s;var u=this._rootNodeID+o,l=s.mountComponent(u,e,this._mountDepth+1);s._mountIndex=i,r.push(l),i++}}return r},updateTextContent:function(t){d++;var e=!0;try{var n=this._renderedChildren;for(var r in n)n.hasOwnProperty(r)&&this._unmountChildByName(n[r],r);this.setTextContent(t),e=!1}finally{d--,d||(e?u():s())}},updateChildren:function(t,e){d++;var n=!0;try{this._updateChildren(t,e),n=!1}finally{d--,d||(n?u():s())}},_updateChildren:function(t,e){var n=h(t),r=this._renderedChildren;if(n||r){var i,o=0,a=0;for(i in n)if(n.hasOwnProperty(i)){var s=r&&r[i],u=s&&s._currentElement,l=n[i];if(f(u,l))this.moveChild(s,a,o),o=Math.max(s._mountIndex,o),s.receiveComponent(l,e),s._mountIndex=a;else{s&&(o=Math.max(s._mountIndex,o),this._unmountChildByName(s,i));var c=p(l,null);this._mountChildByNameAtIndex(c,i,a,e)}a++}for(i in r)!r.hasOwnProperty(i)||n&&n[i]||this._unmountChildByName(r[i],i)}},unmountChildren:function(){var t=this._renderedChildren;for(var e in t){var n=t[e];n.unmountComponent&&n.unmountComponent()}this._renderedChildren=null},moveChild:function(t,e,n){t._mountIndex"+o+""},receiveComponent:function(t){var e=t.props;e!==this.props&&(this.props=e,i.BackendIDOperations.updateTextContentByID(this._rootNodeID,e))}});var l=function(t){return new o(u,null,null,null,null,t)};l.type=u,t.exports=l},/*!*********************************************!*\ + !*** ./~/react/lib/ReactTransitionGroup.js ***! + \*********************************************/ +function(t,e,n){"use strict";var r=n(/*! ./React */24),i=n(/*! ./ReactTransitionChildMapping */162),o=n(/*! ./Object.assign */2),a=n(/*! ./cloneWithProps */84),s=n(/*! ./emptyFunction */10),u=r.createClass({displayName:"ReactTransitionGroup",propTypes:{component:r.PropTypes.any,childFactory:r.PropTypes.func},getDefaultProps:function(){return{component:"span",childFactory:s.thatReturnsArgument}},getInitialState:function(){return{children:i.getChildMapping(this.props.children)}},componentWillReceiveProps:function(t){var e=i.getChildMapping(t.children),n=this.state.children;this.setState({children:i.mergeChildMappings(n,e)});var r;for(r in e){var o=n&&n.hasOwnProperty(r);!e[r]||o||this.currentlyTransitioningKeys[r]||this.keysToEnter.push(r)}for(r in n){var a=e&&e.hasOwnProperty(r);!n[r]||a||this.currentlyTransitioningKeys[r]||this.keysToLeave.push(r)}},componentWillMount:function(){this.currentlyTransitioningKeys={},this.keysToEnter=[],this.keysToLeave=[]},componentDidUpdate:function(){var t=this.keysToEnter;this.keysToEnter=[],t.forEach(this.performEnter);var e=this.keysToLeave;this.keysToLeave=[],e.forEach(this.performLeave)},performEnter:function(t){this.currentlyTransitioningKeys[t]=!0;var e=this.refs[t];e.componentWillEnter?e.componentWillEnter(this._handleDoneEntering.bind(this,t)):this._handleDoneEntering(t)},_handleDoneEntering:function(t){var e=this.refs[t];e.componentDidEnter&&e.componentDidEnter(),delete this.currentlyTransitioningKeys[t];var n=i.getChildMapping(this.props.children);n&&n.hasOwnProperty(t)||this.performLeave(t)},performLeave:function(t){this.currentlyTransitioningKeys[t]=!0;var e=this.refs[t];e.componentWillLeave?e.componentWillLeave(this._handleDoneLeaving.bind(this,t)):this._handleDoneLeaving(t)},_handleDoneLeaving:function(t){var e=this.refs[t];e.componentDidLeave&&e.componentDidLeave(),delete this.currentlyTransitioningKeys[t];var n=i.getChildMapping(this.props.children);if(n&&n.hasOwnProperty(t))this.performEnter(t);else{var r=o({},this.state.children);delete r[t],this.setState({children:r})}},render:function(){var t={};for(var e in this.state.children){var n=this.state.children[e];n&&(t[e]=a(this.props.childFactory(n),{ref:e}))}return r.createElement(this.props.component,this.props,t)}});t.exports=u},/*!****************************************!*\ + !*** ./~/react/lib/ViewportMetrics.js ***! + \****************************************/ +function(t,e,n){"use strict";var r=n(/*! ./getUnboundedScrollPosition */90),i={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(){var t=r(window);i.currentScrollLeft=t.x,i.currentScrollTop=t.y}};t.exports=i},/*!***************************************!*\ + !*** ./~/react/lib/cloneWithProps.js ***! + \***************************************/ +function(t,e,n){"use strict";function r(t,e){var n=o.mergeProps(e,t.props);return!n.hasOwnProperty(s)&&t.props.hasOwnProperty(s)&&(n.children=t.props.children),i.createElement(t.type,n)}var i=n(/*! ./ReactElement */3),o=n(/*! ./ReactPropTransferer */75),a=n(/*! ./keyOf */7),s=(n(/*! ./warning */4),a({children:null}));t.exports=r},/*!*************************************!*\ + !*** ./~/react/lib/containsNode.js ***! + \*************************************/ +function(t,e,n){function r(t,e){return t&&e?t===e?!0:i(t)?!1:i(e)?r(t,e.parentNode):t.contains?t.contains(e):t.compareDocumentPosition?!!(16&t.compareDocumentPosition(e)):!1:!1}var i=n(/*! ./isTextNode */192);t.exports=r},/*!**********************************!*\ + !*** ./~/react/lib/focusNode.js ***! + \**********************************/ +function(t){"use strict";function e(t){try{t.focus()}catch(e){}}t.exports=e},/*!*****************************************!*\ + !*** ./~/react/lib/getActiveElement.js ***! + \*****************************************/ +function(t){function e(){try{return document.activeElement||document.body}catch(t){return document.body}}t.exports=e},/*!**************************************!*\ + !*** ./~/react/lib/getMarkupWrap.js ***! + \**************************************/ +function(t,e,n){function r(t){return o(!!a),p.hasOwnProperty(t)||(t="*"),s.hasOwnProperty(t)||(a.innerHTML="*"===t?"":"<"+t+">",s[t]=!a.firstChild),s[t]?p[t]:null}var i=n(/*! ./ExecutionEnvironment */5),o=n(/*! ./invariant */1),a=i.canUseDOM?document.createElement("div"):null,s={circle:!0,defs:!0,ellipse:!0,g:!0,line:!0,linearGradient:!0,path:!0,polygon:!0,polyline:!0,radialGradient:!0,rect:!0,stop:!0,text:!0},u=[1,'"],l=[1,"","
    "],c=[3,"","
    "],h=[1,"",""],p={"*":[1,"?
    ","
    "],area:[1,"",""],col:[2,"","
    "],legend:[1,"
    ","
    "],param:[1,"",""],tr:[2,"","
    "],optgroup:u,option:u,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c,circle:h,defs:h,ellipse:h,g:h,line:h,linearGradient:h,path:h,polygon:h,polyline:h,radialGradient:h,rect:h,stop:h,text:h};t.exports=r},/*!*******************************************************!*\ + !*** ./~/react/lib/getReactRootElementInContainer.js ***! + \*******************************************************/ +function(t){"use strict";function e(t){return t?t.nodeType===n?t.documentElement:t.firstChild:null}var n=9;t.exports=e},/*!***************************************************!*\ + !*** ./~/react/lib/getUnboundedScrollPosition.js ***! + \***************************************************/ +function(t){"use strict";function e(t){return t===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:t.scrollLeft,y:t.scrollTop}}t.exports=e},/*!*******************************************!*\ + !*** ./~/react/lib/isTextInputElement.js ***! + \*******************************************/ +function(t){"use strict";function e(t){return t&&("INPUT"===t.nodeName&&n[t.type]||"TEXTAREA"===t.nodeName)}var n={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=e},/*!**********************************!*\ + !*** ./~/react/lib/mapObject.js ***! + \**********************************/ +function(t){"use strict";function e(t,e,r){if(!t)return null;var i={};for(var o in t)n.call(t,o)&&(i[o]=e.call(r,t[o],o,t));return i}var n=Object.prototype.hasOwnProperty;t.exports=e},/*!******************************************!*\ + !*** ./~/react/lib/memoizeStringOnly.js ***! + \******************************************/ +function(t){"use strict";function e(t){var e={};return function(n){return e.hasOwnProperty(n)?e[n]:e[n]=t.call(this,n)}}t.exports=e},/*!**********************************!*\ + !*** ./~/react/lib/onlyChild.js ***! + \**********************************/ +function(t,e,n){"use strict";function r(t){return o(i.isValidElement(t)),t}var i=n(/*! ./ReactElement */3),o=n(/*! ./invariant */1);t.exports=r},/*!*************************************!*\ + !*** ./~/react/lib/setInnerHTML.js ***! + \*************************************/ +function(t,e,n){"use strict";var r=n(/*! ./ExecutionEnvironment */5),i=/^[ \r\n\t\f]/,o=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(t,e){t.innerHTML=e};if(r.canUseDOM){var s=document.createElement("div");s.innerHTML=" ",""===s.innerHTML&&(a=function(t,e){if(t.parentNode&&t.parentNode.replaceChild(t,t),i.test(e)||"<"===e[0]&&o.test(e)){t.innerHTML=""+e;var n=t.firstChild;1===n.data.length?t.removeChild(n):n.deleteData(0,1)}else t.innerHTML=e})}t.exports=a},/*!*************************************!*\ + !*** ./~/react/lib/shallowEqual.js ***! + \*************************************/ +function(t){"use strict";function e(t,e){if(t===e)return!0;var n;for(n in t)if(t.hasOwnProperty(n)&&(!e.hasOwnProperty(n)||t[n]!==e[n]))return!1;for(n in e)if(e.hasOwnProperty(n)&&!t.hasOwnProperty(n))return!1;return!0}t.exports=e},/*!********************************************!*\ + !*** ./~/react/lib/traverseAllChildren.js ***! + \********************************************/ +function(t,e,n){"use strict";function r(t){return f[t]}function i(t,e){return t&&null!=t.key?a(t.key):e.toString(36)}function o(t){return(""+t).replace(d,r)}function a(t){return"$"+o(t)}function s(t,e,n){return null==t?0:m(t,"",0,e,n)}var u=n(/*! ./ReactElement */3),l=n(/*! ./ReactInstanceHandles */27),c=n(/*! ./invariant */1),h=l.SEPARATOR,p=":",f={"=":"=0",".":"=1",":":"=2"},d=/[=.:]/g,m=function(t,e,n,r,o){var s,l,f=0;if(Array.isArray(t))for(var d=0;dr;r++)t=e.weekday(r).format("ddd"),n.push(i.createElement("th",{className:"cal__heading",key:t},t));return{weekDays:n}},handleClearSelection:function(){s.clearSelectedDay()},render:function(){var t,e,n,r=this.props.selectedDay,s=i.addons.classSet,u=this.props.selectedDay?"":"is-hidden",c={"l--push-bottom-1":!0,cal:!0,"cal--highlight":this.props.selectedContentState===l[1],"cal--success":this.props.selectedContentState===l[2],"cal--unsure":this.props.selectedContentState===l[3]},h=this.props.matrixData,p=[],f=[];if(0==h.length)return i.createElement("table",null,i.createElement("tr",null,i.createElement("td",null,"Loading")));e=o(h[0].date);for(var d=e.weekday()-1;d>=0;d--)p.push(i.createElement("td",{className:"cal__day",key:e.weekday(d).format()}));for(h.forEach(function(t){var e=t.date;p.push(i.createElement(a,{key:e,dateLabel:o(e).format("Do"),date:e,wordCount:t.wordCount,selectedDay:r}))});p.length;)t=p.splice(0,7),f.push(i.createElement("tr",{className:"cal__week",key:this.props.dateRangeOption+"-week"+f.length}," ",t," "));return n=i.createElement("div",{className:"l--push-bottom-half g"},i.createElement("div",{className:"g__item w--1-2"},i.createElement("h3",{className:"epsilon txt--uppercase"},this.props.dateRangeOption,"'s Activity")),i.createElement("div",{className:"g__item w--1-2 txt--align-right"},i.createElement("p",{className:u},i.createElement("button",{className:"button--link",onClick:this.handleClearSelection},"Clear selection")))),i.createElement("div",null,n,i.createElement("table",{className:s(c)},i.createElement("thead",{className:"cal__head"},i.createElement("tr",null,this.props.weekDays)),i.createElement("tbody",null,f)))}});t.exports=h},/*!**************************************************!*\ + !*** ./lib/components/CalendarPeriodHeading.jsx ***! + \**************************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=r(n(/*! moment */62)),a=r(n(/*! ../utils/DateHelper */33)),s=i.createClass({displayName:"CalendarPeriodHeading",render:function(){var t,e=a.dateFormat,n="DD MMM, YYYY (dddd)",r="DD MMM, YYYY";return t=this.props.selectedDay?o(this.props.selectedDay,e).format(n):o(this.props.fromDate,e).format(r)+" … "+o(this.props.toDate,e).format(r)+" ("+this.props.dateRange+")",i.createElement("div",{className:"l--push-bottom-half"},i.createElement("h3",{className:"epsilon txt--uppercase"},"Activity Details"),i.createElement("p",{className:"txt--understated"},t))}});t.exports=s},/*!***********************************************!*\ + !*** ./lib/components/CategoryItemMatrix.jsx ***! + \***********************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=i.createClass({displayName:"CategoryItemMatrix",render:function(){return i.createElement("tr",null,i.createElement("td",{className:"l--pad-left-0 l--pad-v-0 w--1"},this.props.itemTitle," ",i.createElement("span",{className:"txt--understated"},"(",this.props.itemName,")")),i.createElement("td",{className:"txt--align-right l--pad-right-0 l--pad-v-0 txt--nowrap"},this.props.wordCount," ",i.createElement("span",{className:"txt--understated"},"words")))}});t.exports=o},/*!************************************************!*\ + !*** ./lib/components/CategoryMatrixTable.jsx ***! + \************************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=r(n(/*! lodash */117)),a=r(n(/*! ./CategoryItemMatrix */101)),s=i.createClass({displayName:"CategoryMatrixTable",render:function(){var t={},e=[],n=this.props.category,r=this.props.categoryTitle;return this.props.matrixData.forEach(function(e){var i=e[n];t[i]&&t[i].wordCount?t[i].wordCount+=e.wordCount:t[i]={wordCount:e.wordCount,title:e[r]}}),o.forOwn(t,function(t,n){e.push(i.createElement(a,{key:n,itemName:n,itemTitle:t.title,wordCount:t.wordCount}))}),i.createElement("div",null,i.createElement("h3",{className:"zeta txt--uppercase txt--understated"},this.props.categoryName),i.createElement("table",{className:"l--push-bottom-half"},i.createElement("tbody",null,e)))}});t.exports=s},/*!***********************************************!*\ + !*** ./lib/components/ContentStateFilter.jsx ***! + \***********************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=r(n(/*! ../actions/Actions */32)),a=n(/*! ../constants/Options */17),s=a.ContentStates,u=a.ContentStateStyles,l=n(/*! react/addons */34).PureRenderMixin,c=i.createClass({displayName:"ContentStateFilter",mixins:[l],propTypes:{selectedContentState:i.PropTypes.oneOf(s).isRequired},onFilterOptionClicked:function(t){this.props.selectedContentState!==t&&o.changeContentState(t)},render:function(){var t,e=this,n=this.props.selectedContentState,r=this.onFilterOptionClicked;return t=s.map(function(t,o){var a="pill--"+u[o],s="pill pill--inline ";return s+=n===t?a+" is-active":a,i.createElement("span",{key:t,onClick:r.bind(e,t),className:s},t)}),i.createElement("div",{className:"l--pad-bottom-half"},t)}});t.exports=c},/*!**********************************************!*\ + !*** ./lib/components/ContributionChart.jsx ***! + \**********************************************/ +function(t,e,n){"use strict";function r(t){var e={labels:[],datasets:[{label:"Total",fillColor:"rgba(65, 105, 136, .05)",strokeColor:"rgb(65, 105, 136)",pointColor:"rgb(65, 105, 136)",pointStrokeColor:"#fff",pointHighlightFill:"#fff",pointHighlightStroke:"rgb(65, 105, 136)",data:[]},{label:"Translated",fillColor:"rgba(112,169,139, .05)",strokeColor:"rgb(112,169,139)",pointColor:"rgb(112,169,139)",pointStrokeColor:"#fff",pointHighlightFill:"#fff",pointHighlightStroke:"rgb(112,169,139)",data:[]},{label:"Needs Work",fillColor:"rgba(224,195,80, .05)",strokeColor:"rgb(224,195,80)",pointColor:"rgb(224,195,80)",pointStrokeColor:"#fff",pointHighlightFill:"#fff",pointHighlightStroke:"rgb(224,195,80)",data:[]},{label:"Approved",fillColor:"rgba(78, 159, 221, .05)",strokeColor:"rgb(78, 159, 221)",pointColor:"rgb(78, 159, 221)",pointStrokeColor:"#fff",pointHighlightFill:"#fff",pointHighlightStroke:"rgb(78, 159, 221)",data:[]}]},n=t.length,r=!1;return t.forEach(function(t){var i=t.date;r=r||a.isInFuture(i),e.labels.push(a.dayAsLabel(i,n)),r||(e.datasets[0].data.push(t.totalActivity),e.datasets[1].data.push(t.totalTranslated),e.datasets[2].data.push(t.totalNeedsWork),e.datasets[3].data.push(t.totalApproved))}),e}var i=function(t){return t&&t.__esModule?t["default"]:t},o=i(n(/*! react */11)),a=i(n(/*! ../utils/DateHelper */33)),s=n(/*! react-chartjs */118).Line,u=n(/*! ../constants/Options */17).DateRanges,l=s,c={animationEasing:"easeOutQuint",bezierCurve:!0,bezierCurveTension:.4,pointDot:!0,pointDotRadius:4,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,responsive:!0,showTooltips:!0,scaleFontFamily:'"Source Sans Pro", "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif',scaleFontColor:"#7c96ac",scaleShowGridLines:!0,scaleShowVerticalLines:!1,scaleGridLineColor:"rgba(198, 210, 219, .1)",tooltipFillColor:"rgba(255,255,255,0.8)",tooltipFontFamily:'"Source Sans Pro", "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif',tooltipFontSize:14,tooltipFontStyle:"400",tooltipFontColor:"rgb(132, 168, 196)",tooltipTitleFontFamily:'"Source Sans Pro", "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif',tooltipTitleFontSize:14,tooltipTitleFontStyle:"400",tooltipTitleFontColor:"rgb(65, 105, 136)",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:6,tooltipCornerRadius:2,tooltipXOffset:10,multiTooltipTemplate:"<%= value %><%if (datasetLabel){%> (<%= datasetLabel %>)<%}%>"},h=o.createClass({displayName:"ContributionChart",propTypes:{dateRangeOption:o.PropTypes.oneOf(u).isRequired,wordCountForEachDay:o.PropTypes.arrayOf(o.PropTypes.shape({date:o.PropTypes.string.isRequired,totalActivity:o.PropTypes.number.isRequired,totalApproved:o.PropTypes.number.isRequired,totalTranslated:o.PropTypes.number.isRequired,totalNeedsWork:o.PropTypes.number.isRequired})).isRequired},getDefaultProps:function(){return{chartOptions:c}},shouldComponentUpdate:function(t){return this.props.dateRangeOption!==t.dateRangeOption||this.props.wordCountForEachDay.length!==t.wordCountForEachDay.length},render:function(){var t=r(this.props.wordCountForEachDay);return o.createElement(l,{data:t,options:this.props.chartOptions,width:"800",height:"250"})}});t.exports=h},/*!**************************************!*\ + !*** ./lib/components/DayMatrix.jsx ***! + \**************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=n(/*! react/addons */34),o=r(i),a=n(/*! ../constants/Options */17),s=a.ContentStates,u=a.ContentStateStyles,l=r(n(/*! ../actions/Actions */32)),c=i.PureRenderMixin,h=r(n(/*! ../utils/DateHelper */33)),p=o.createClass({displayName:"DayMatrix",mixins:[c],propTypes:{dateLabel:o.PropTypes.string.isRequired,date:o.PropTypes.string.isRequired,wordCount:o.PropTypes.number.isRequired,selectedDay:o.PropTypes.string},handleDayClick:function(){var t=this.props.date;this.props.selectedDay==t?l.clearSelectedDay():l.onDaySelected(t)},render:function(){var t,e=o.addons.classSet,n=this.props.selectedContentState,r=h.isInFuture(this.props.date),i=r?"":this.props.wordCount;return t={cal__day:!0,"is-disabled":r,"is-active":this.props.date===this.props.selectedDay},s.forEach(function(e,r){t[u[r]]=n===e}),o.createElement("td",{className:e(t),onClick:this.handleDayClick,title:this.props.wordCount+" words"},o.createElement("div",{className:"cal__date"},this.props.dateLabel),o.createElement("div",{className:"cal__date-info"},i))}});t.exports=p},/*!*************************************!*\ + !*** ./lib/components/DropDown.jsx ***! + \*************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=r(n(/*! ../actions/Actions */32)),a=n(/*! react/addons */34).PureRenderMixin,s=i.createClass({displayName:"DropDown",mixins:[a],getInitialState:function(){return{dropdownIsActive:!1}},handleOptionClick:function(t){var e=t.target.innerText;this.props.selectedOption!=e&&o.changeDateRange(e),this.setState({dropdownIsActive:!1})},handleButtonClick:function(t){t.preventDefault(),this.setState({dropdownIsActive:!this.state.dropdownIsActive})},render:function(){var t,e=this.props.options,n=this.props.selectedOption,r=this,o="Dropdown--simple";return o+=this.state.dropdownIsActive?" is-active":"",t=e.map(function(t){var e="button--link txt--nowrap";return e+=t===n?" is-active":"",i.createElement("li",{key:t,className:"Dropdown-item"},i.createElement("button",{className:e,onClick:r.handleOptionClick},t))}),i.createElement("div",{className:o},i.createElement("button",{className:"button--link",onClick:this.handleButtonClick},i.createElement("span",{className:"Dropdown-toggleIcon"},i.createElement("i",{className:"i i--arrow-down"}))," ",n),i.createElement("ul",{className:"Dropdown-content"},t))}});t.exports=s},/*!**************************************************!*\ + !*** ./lib/components/FilterableMatrixTable.jsx ***! + \**************************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react/addons */34)),o=r(n(/*! ./ContentStateFilter */103)),a=r(n(/*! ./CalendarMonthMatrix */99)),s=r(n(/*! ./CalendarPeriodHeading */100)),u=r(n(/*! ./CategoryMatrixTable */102)),l=n(/*! ../constants/Options */17),c=l.DateRanges,h=l.ContentStates,p=i.createClass({displayName:"FilterableMatrixTable",propTypes:{wordCountForEachDay:i.PropTypes.arrayOf(i.PropTypes.shape({date:i.PropTypes.string.isRequired,wordCount:i.PropTypes.number.isRequired})).isRequired,wordCountForSelectedDay:i.PropTypes.arrayOf(i.PropTypes.shape({savedDate:i.PropTypes.string.isRequired,projectSlug:i.PropTypes.string.isRequired,projectName:i.PropTypes.string.isRequired,versionSlug:i.PropTypes.string.isRequired,localeId:i.PropTypes.string.isRequired,localeDisplayName:i.PropTypes.string.isRequired,savedState:i.PropTypes.string.isRequired,wordCount:i.PropTypes.number.isRequired})).isRequired,fromDate:i.PropTypes.string.isRequired,toDate:i.PropTypes.string.isRequired,dateRangeOption:i.PropTypes.oneOf(c).isRequired,selectedContentState:i.PropTypes.oneOf(h).isRequired,selectedDay:i.PropTypes.string},render:function(){var t,e=this.props.selectedContentState,n=this.props.selectedDay;return t=this.props.wordCountForSelectedDay.length>0?[i.createElement(u,{key:"locales",matrixData:this.props.wordCountForSelectedDay,category:"localeId",categoryTitle:"localeDisplayName",categoryName:"Languages"}),i.createElement(u,{key:"projects",matrixData:this.props.wordCountForSelectedDay,category:"projectSlug",categoryTitle:"projectName",categoryName:"Projects"})]:i.createElement("div",null,"No contributions"),i.createElement("div",null,i.createElement(o,{selectedContentState:e}),i.createElement("div",{className:"g"},i.createElement("div",{className:"g__item w--1-2-l w--1-2-h"},i.createElement(a,{matrixData:this.props.wordCountForEachDay,selectedDay:n,selectedContentState:e,dateRangeOption:this.props.dateRangeOption})),i.createElement("div",{className:"g__item w--1-2-l w--1-2-h"},i.createElement(s,{fromDate:this.props.fromDate,toDate:this.props.toDate,dateRange:this.props.dateRangeOption,selectedDay:n}),t)))}});t.exports=p},/*!************************************************!*\ + !*** ./lib/components/RecentContributions.jsx ***! + \************************************************/ +function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t["default"]:t},i=r(n(/*! react */11)),o=r(n(/*! ./ContributionChart */104)),a=r(n(/*! ./DropDown */106)),s=r(n(/*! ./FilterableMatrixTable */107)),u=r(n(/*! ../stores/UserMatrixStore */109)),l=n(/*! ../constants/Options */17).DateRanges,c=i.createClass({displayName:"RecentContributions",getMatrixState:function(){return u.getMatrixState()},getInitialState:function(){return this.getMatrixState()},componentDidMount:function(){u.addChangeListener(this._onChange)},componentWillUnmount:function(){u.removeChangeListener(this._onChange)},_onChange:function(){this.setState(this.getMatrixState())},render:function(){var t=this.state.dateRange;return i.createElement("div",{className:"l__wrapper"},i.createElement("div",{className:"l--push-bottom-1"},i.createElement("div",{className:"l--float-right txt--uppercase"},i.createElement(a,{options:l,selectedOption:this.state.dateRangeOption})),i.createElement("h2",{className:"delta txt--uppercase"},"Recent Contributions")),i.createElement("div",{className:"l--push-bottom-1"},i.createElement(o,{wordCountForEachDay:this.state.matrixForAllDays,dateRangeOption:this.state.dateRangeOption})),i.createElement(s,{wordCountForSelectedDay:this.state.wordCountsForSelectedDayFilteredByContentState,wordCountForEachDay:this.state.wordCountsForEachDayFilteredByContentState,fromDate:t.fromDate,toDate:t.toDate,dateRangeOption:this.state.dateRangeOption,selectedContentState:this.state.contentStateOption,selectedDay:this.state.selectedDay}))}});t.exports=c},/*!***************************************!*\ + !*** ./lib/stores/UserMatrixStore.js ***! + \***************************************/ +function(t,e,n){"use strict";function r(){var t=C.dateRangeOption,e=_.getDateRangeFromOption(t),n=y.baseUrl+e.fromDate+".."+e.toDate;return C.dateRange=e,new p(function(t,e){b.get(n).set("Cache-Control","no-cache, no-store, must-revalidate").set("Pragma","no-cache").set("Expires",0).end(function(r){r.error?(console.error(n,r.status,r.error.toString()),e(Error(r.error.toString()))):t(r.body)})})}function i(t){var e=C.dateRange,n=o(t,e),r=C.contentStateOption,i=C.selectedDay;return C.matrix=t,C.matrixForAllDays=n,C.wordCountsForEachDayFilteredByContentState=s(n,r),C.wordCountsForSelectedDayFilteredByContentState=u(C.matrix,r,i),C}function o(t,e){var n=e.dates,r=[],i=0;return n.forEach(function(e){for(var n=t[i]||{},o=0,a=0,s=0;n.savedDate===e;){switch(n.savedState){case"Approved":o+=n.wordCount;break;case"Translated":a+=n.wordCount;break;case"NeedReview":s+=n.wordCount;break;default:throw new Error("unrecognized state:"+n.savedState)}i++,n=t[i]||{}}r.push({date:e,totalApproved:o,totalTranslated:a,totalNeedsWork:s,totalActivity:o+s+a})}),r}function a(t){switch(t){case"Total":return"totalActivity";case"Approved":return"totalApproved";case"Translated":return"totalTranslated";case"Needs Work":return"totalNeedsWork"}}function s(t,e){var n=a(e);return t.map(function(t){return{date:t.date,wordCount:t[n]}})}function u(t,e,n){var r,i=t,o=[];return e="Needs Work"===e?"NeedReview":e,n&&o.push(function(t){return t.savedDate===n}),"Total"!==e&&o.push(function(t){return t.savedState===e}),o.length>0&&(r=function(t){return o.every(function(e){return e.call({},t)})},i=t.filter(r)),i}var l=function(t){return t&&t.__esModule?t["default"]:t},c=l(n(/*! ../dispatchers/UserMatrixDispatcher */60)),h=l(n(/*! object-assign */63)),p=n(/*! es6-promise */111).Promise,f=n(/*! events */112).EventEmitter,d=n(/*! ../constants/Options */17),m=d.ContentStates,v=d.DateRanges,g=l(n(/*! ../constants/ActionTypes */58)),y=l(n(/*! ../constants/Configs */59)),_=l(n(/*! ../utils/DateHelper */33)),b=l(n(/*! superagent */196)),w="change",C={matrix:[],matrixForAllDays:[],wordCountsForEachDayFilteredByContentState:[],wordCountsForSelectedDayFilteredByContentState:[],dateRangeOption:v[0],selectedDay:null,contentStateOption:m[0],dateRange:function(t){return _.getDateRangeFromOption(t)}},x=h({},f.prototype,{getMatrixState:function(){return 0==C.matrixForAllDays.length&&r().then(i).then(function(){x.emitChange()}),C}.bind(void 0),emitChange:function(){this.emit(w)},addChangeListener:function(t){this.on(w,t)},removeChangeListener:function(t){this.removeListener(w,t)},dispatchToken:c.register(function(t){var e=t.action;switch(e.actionType){case g.DATE_RANGE_UPDATE:console.log("date range from %s -> %s",C.dateRangeOption,e.data),C.dateRangeOption=e.data,C.selectedDay=null,r().then(i).then(function(){x.emitChange()})["catch"](function(t){console.error("something bad happen:"+t.stack)});break;case g.CONTENT_STATE_UPDATE:console.log("content state from %s -> %s",C.contentStateOption,e.data),C.contentStateOption=e.data,C.wordCountsForEachDayFilteredByContentState=s(C.matrixForAllDays,C.contentStateOption),C.wordCountsForSelectedDayFilteredByContentState=u(C.matrix,C.contentStateOption,C.selectedDay),x.emitChange();break;case g.DAY_SELECTED:console.log("day selection from %s -> %s",C.selectedDay,e.data),C.selectedDay=e.data,C.wordCountsForSelectedDayFilteredByContentState=u(C.matrix,C.contentStateOption,C.selectedDay),x.emitChange()}})});t.exports=x},/*!*****************************!*\ + !*** ./~/chart.js/Chart.js ***! + \*****************************/ +function(t,e,n){var r;/*! + * Chart.js + * http://chartjs.org/ + * Version: 1.0.2 + * + * Copyright 2015 Nick Downie + * Released under the MIT license + * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md + */ +(function(){"use strict";var i=this,o=i.Chart,a=function(t){return this.canvas=t.canvas,this.ctx=t,this.width=t.canvas.width,this.height=t.canvas.height,this.aspectRatio=this.width/this.height,s.retinaScale(this),this};a.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",multiTooltipKeyBackground:"#fff",onAnimationProgress:function(){},onAnimationComplete:function(){}}},a.types={};var s=a.helpers={},u=s.each=function(t,e,n){var r=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var i;for(i=0;i=0;r--){var i=t[r];if(e(i))return i}},s.inherits=function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},r=function(){this.constructor=n};return r.prototype=e.prototype,n.prototype=new r,n.extend=f,t&&c(n.prototype,t),n.__super__=e.prototype,n}),d=s.noop=function(){},m=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),v=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},g=s.amd=!0&&n(/*! !webpack amd options */200),y=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},_=s.max=function(t){return Math.max.apply(Math,t)},b=s.min=function(t){return Math.min.apply(Math,t)},w=(s.cap=function(t,e,n){if(y(e)){if(t>e)return e}else if(y(n)&&n>t)return n;return t},s.getDecimalPlaces=function(t){return t%1!==0&&y(t)?t.toString().split(".")[1].length:0}),C=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,e){var n=e.x-t.x,r=e.y-t.y,i=Math.sqrt(n*n+r*r),o=2*Math.PI+Math.atan2(r,n);return 0>n&&0>r&&(o+=2*Math.PI),{angle:o,distance:i}},s.aliasPixel=function(t){return t%2===0?0:.5}),E=(s.splineCurve=function(t,e,n,r){var i=Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)),o=Math.sqrt(Math.pow(n.x-e.x,2)+Math.pow(n.y-e.y,2)),a=r*i/(i+o),s=r*o/(i+o);return{inner:{x:e.x-a*(n.x-t.x),y:e.y-a*(n.y-t.y)},outer:{x:e.x+s*(n.x-t.x),y:e.y+s*(n.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),D=(s.calculateScaleRange=function(t,e,n,r,i){var o=2,a=Math.floor(e/(1.5*n)),s=o>=a,u=_(t),l=b(t);u===l&&(u+=.5,l>=.5&&!r?l-=.5:u+=.5);for(var c=Math.abs(u-l),h=E(c),p=Math.ceil(u/(1*Math.pow(10,h)))*Math.pow(10,h),f=r?0:Math.floor(l/(1*Math.pow(10,h)))*Math.pow(10,h),d=p-f,m=Math.pow(10,h),v=Math.round(d/m);(v>a||a>2*v)&&!s;)if(v>a)m*=2,v=Math.round(d/m),v%1!==0&&(s=!0);else if(i&&h>=0){if(m/2%1!==0)break;m/=2,v=Math.round(d/m)}else m/=2,v=Math.round(d/m);return s&&(v=o,m=d/v),{steps:v,stepValue:m,min:f,max:f+v*m}},s.template=function(t,e){function n(t,e){var n=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):r[t]=r[t];return e?n(e):n}if(t instanceof Function)return t(e);var r={};return n(t,e)}),S=(s.generateLabels=function(t,e,n,r){var i=new Array(e);return labelTemplateString&&u(i,function(e,o){i[o]=D(t,{value:n+r*(o+1)})}),i},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,r=1;return 0===t?0:1==(t/=1)?1:(n||(n=.3),rt?-.5*r*Math.pow(2,10*(t-=1))*Math.sin(2*(1*t-e)*Math.PI/n):r*Math.pow(2,-10*(t-=1))*Math.sin(2*(1*t-e)*Math.PI/n)*.5+1)},easeInBack:function(t){var e=1.70158;return 1*(t/=1)*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return 1*((t=t/1-1)*t*((e+1)*t+e)+1)},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?.5*t*t*(((e*=1.525)+1)*t-e):.5*((t-=2)*t*(((e*=1.525)+1)*t+e)+2)},easeInBounce:function(t){return 1-S.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?7.5625*t*t:2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*S.easeInBounce(2*t):.5*S.easeOutBounce(2*t-1)+.5}}),M=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),T=(s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),s.animationLoop=function(t,e,n,r,i,o){var a=0,s=S[n]||S.linear,u=function(){a++;var n=a/e,l=s(n);t.call(o,l,n,a),r.call(o,l,n),e>a?o.animationFrame=M(u):i.apply(o)};M(u)},s.getRelativePosition=function(t){var e,n,r=t.originalEvent||t,i=t.currentTarget||t.srcElement,o=i.getBoundingClientRect();return r.touches?(e=r.touches[0].clientX-o.left,n=r.touches[0].clientY-o.top):(e=r.clientX-o.left,n=r.clientY-o.top),{x:e,y:n}},s.addEvent=function(t,e,n){t.addEventListener?t.addEventListener(e,n):t.attachEvent?t.attachEvent("on"+e,n):t["on"+e]=n}),P=s.removeEvent=function(t,e,n){t.removeEventListener?t.removeEventListener(e,n,!1):t.detachEvent?t.detachEvent("on"+e,n):t["on"+e]=d},k=(s.bindEvents=function(t,e,n){t.events||(t.events={}),u(e,function(e){t.events[e]=function(){n.apply(t,arguments)},T(t.chart.canvas,e,t.events[e])})},s.unbindEvents=function(t,e){u(e,function(e,n){P(t.chart.canvas,n,e)})}),O=s.getMaximumWidth=function(t){var e=t.parentNode;return e.clientWidth},R=s.getMaximumHeight=function(t){var e=t.parentNode;return e.clientHeight},A=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var e=t.ctx,n=t.canvas.width,r=t.canvas.height;window.devicePixelRatio&&(e.canvas.style.width=n+"px",e.canvas.style.height=r+"px",e.canvas.height=r*window.devicePixelRatio,e.canvas.width=n*window.devicePixelRatio,e.scale(window.devicePixelRatio,window.devicePixelRatio))}),L=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},N=s.fontString=function(t,e,n){return e+" "+t+"px "+n},I=s.longestText=function(t,e,n){t.font=e;var r=0;return u(n,function(e){var n=t.measureText(e).width;r=n>r?n:r}),r},F=s.drawRoundedRectangle=function(t,e,n,r,i,o){t.beginPath(),t.moveTo(e+o,n),t.lineTo(e+r-o,n),t.quadraticCurveTo(e+r,n,e+r,n+o),t.lineTo(e+r,n+i-o),t.quadraticCurveTo(e+r,n+i,e+r-o,n+i),t.lineTo(e+o,n+i),t.quadraticCurveTo(e,n+i,e,n+i-o),t.lineTo(e,n+o),t.quadraticCurveTo(e,n,e+o,n),t.closePath()};a.instances={},a.Type=function(t,e,n){this.options=e,this.chart=n,this.id=m(),a.instances[this.id]=this,e.responsive&&this.resize(),this.initialize.call(this,t)},c(a.Type.prototype,{initialize:function(){return this},clear:function(){return L(this.chart),this},stop:function(){return s.cancelAnimFrame.call(window,this.animationFrame),this},resize:function(t){this.stop();var e=this.chart.canvas,n=O(this.chart.canvas),r=this.options.maintainAspectRatio?n/this.chart.aspectRatio:R(this.chart.canvas);return e.width=this.chart.width=n,e.height=this.chart.height=r,A(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:d,render:function(t){return t&&this.reflow(),this.options.animation&&!t?s.animationLoop(this.draw,this.options.animationSteps,this.options.animationEasing,this.options.onAnimationProgress,this.options.onAnimationComplete,this):(this.draw(),this.options.onAnimationComplete.call(this)),this},generateLegend:function(){return D(this.options.legendTemplate,this)},destroy:function(){this.clear(),k(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete a.instances[this.id]},showTooltip:function(t,e){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var n=function(t){var e=!1;return t.length!==this.activeElements.length?e=!0:(u(t,function(t,n){t!==this.activeElements[n]&&(e=!0)},this),e)}.call(this,t);if(n||e){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var r,i,o=this.datasets.length-1;o>=0&&(r=this.datasets[o].points||this.datasets[o].bars||this.datasets[o].segments,i=p(r,t[0]),-1===i);o--);var l=[],c=[],h=function(){var t,e,n,r,o,a=[],u=[],h=[];return s.each(this.datasets,function(e){t=e.points||e.bars||e.segments,t[i]&&t[i].hasValue()&&a.push(t[i])}),s.each(a,function(t){u.push(t.x),h.push(t.y),l.push(s.template(this.options.multiTooltipTemplate,t)),c.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),o=b(h),n=_(h),r=b(u),e=_(u),{x:r>this.chart.width/2?r:e,y:(o+n)/2}}.call(this,i);new a.MultiTooltip({x:h.x,y:h.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:l,legendColors:c,legendColorBackground:this.options.multiTooltipKeyBackground,title:t[0].label,chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else u(t,function(t){var e=t.tooltipPosition();new a.Tooltip({x:Math.round(e.x),y:Math.round(e.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:D(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),a.Type.extend=function(t){var e=this,n=function(){return e.apply(this,arguments)};if(n.prototype=l(e.prototype),c(n.prototype,t),n.extend=a.Type.extend,t.name||e.prototype.name){var r=t.name||e.prototype.name,i=a.defaults[e.prototype.name]?l(a.defaults[e.prototype.name]):{};a.defaults[r]=c(i,t.defaults),a.types[r]=n,a.prototype[r]=function(t,e){var i=h(a.defaults.global,a.defaults[r],e||{});return new n(t,i,this)}}else v("Name not provided for this chart, so it hasn't been registered");return e},a.Element=function(t){c(this,t),this.initialize.apply(this,arguments),this.save()},c(a.Element.prototype,{initialize:function(){},restore:function(t){return t?u(t,function(t){this[t]=this._saved[t]},this):c(this,this._saved),this},save:function(){return this._saved=l(this),delete this._saved._saved,this},update:function(t){return u(t,function(t,e){this._saved[e]=this[e],this[e]=t},this),this},transition:function(t,e){return u(t,function(t,n){this[n]=(t-this._saved[n])*e+this._saved[n]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return y(this.value)}}),a.Element.extend=f,a.Point=a.Element.extend({display:!0,inRange:function(t,e){var n=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(e-this.y,2)=this.startAngle&&n.angle<=this.endAngle,i=n.distance>=this.innerRadius&&n.distance<=this.outerRadius;return r&&i},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,e=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*e,y:this.y+Math.sin(t)*e}},draw:function(){var t=this.ctx;t.beginPath(),t.arc(this.x,this.y,this.outerRadius,this.startAngle,this.endAngle),t.arc(this.x,this.y,this.innerRadius,this.endAngle,this.startAngle,!0),t.closePath(),t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.fillStyle=this.fillColor,t.fill(),t.lineJoin="bevel",this.showStroke&&t.stroke()}}),a.Rectangle=a.Element.extend({draw:function(){var t=this.ctx,e=this.width/2,n=this.x-e,r=this.x+e,i=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(n+=o,r-=o,i+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(n,this.base),t.lineTo(n,i),t.lineTo(r,i),t.lineTo(r,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,e){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&e>=this.y&&e<=this.base}}),a.Tooltip=a.Element.extend({draw:function(){var t=this.chart.ctx;t.font=N(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var e=this.caretPadding=2,n=t.measureText(this.text).width+2*this.xPadding,r=this.fontSize+2*this.yPadding,i=r+this.caretHeight+e;this.x+n/2>this.chart.width?this.xAlign="left":this.x-n/2<0&&(this.xAlign="right"),this.y-i<0&&(this.yAlign="below");var o=this.x-n/2,a=this.y-i;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-e),t.lineTo(this.x+this.caretHeight,this.y-(e+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(e+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+e+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+e),t.lineTo(this.x+this.caretHeight,this.y+e+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+e+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-n+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}F(t,o,a,n,r,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+n/2,a+r/2)}}}),a.MultiTooltip=a.Element.extend({initialize:function(){this.font=N(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=N(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+1.5*this.titleFontSize,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,e=I(this.ctx,this.font,this.labels)+this.fontSize+3,n=_([e,t]);this.width=n+2*this.xPadding;var r=this.height/2;this.y-r<0?this.y=r:this.y+r>this.chart.height&&(this.y=this.chart.height-r),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var e=this.y-this.height/2+this.yPadding,n=t-1;return 0===t?e+this.titleFontSize/2:e+(1.5*this.fontSize*n+this.fontSize/2)+1.5*this.titleFontSize},draw:function(){if(this.custom)this.custom(this);else{F(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(e,n){t.fillStyle=this.textColor,t.fillText(e,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(n+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(n+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[n].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(n+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),a.Scale=a.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=w(this.stepValue),e=0;e<=this.steps;e++)this.yLabels.push(D(this.templateString,{value:(this.min+e*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?I(this.ctx,this.font,this.yLabels):0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,e=this.endPoint-this.startPoint;for(this.calculateYRange(e),this.buildYLabels(),this.calculateXLabelRotation();e>this.endPoint-this.startPoint;)e=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(e),this.buildYLabels(),tthis.yLabelWidth+10?n/2:this.yLabelWidth+10,this.xLabelRotation=0,this.display){var i,o=I(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)i=Math.cos(C(this.xLabelRotation)),t=i*n,e=i*r,t+this.fontSize/2>this.yLabelWidth+8&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=i*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(C(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:d,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var e=this.drawingArea()/(this.min-this.max);return this.endPoint-e*(t-this.min)},calculateX:function(t){var e=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),n=e/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1),r=n*t+this.xScalePaddingLeft;return this.offsetGridLines&&(r+=n/2),Math.round(r)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,e=(this.endPoint-this.startPoint)/this.steps,n=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,u(this.yLabels,function(r,i){var o=this.endPoint-e*i,a=Math.round(o),u=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(r,n-10,o),0!==i||u||(u=!0),u&&t.beginPath(),i>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a+=s.aliasPixel(t.lineWidth),u&&(t.moveTo(n,a),t.lineTo(this.width,a),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n-5,a),t.lineTo(n,a),t.stroke(),t.closePath()},this),u(this.xLabels,function(e,n){var r=this.calculateX(n)+x(this.lineWidth),i=this.calculateX(n-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==n||a||(a=!0),a&&t.beginPath(),n>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(i,this.endPoint),t.lineTo(i,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(i,this.endPoint),t.lineTo(i,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(r,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*C(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(e,0,0),t.restore()},this))}}),a.RadialScale=a.Element.extend({initialize:function(){this.size=b([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var e=this.drawingArea/(this.max-this.min);return(t-this.min)*e},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=w(this.stepValue),e=0;e<=this.steps;e++)this.yLabels.push(D(this.templateString,{value:(this.min+e*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,e,n,r,i,o,a,s,u,l,c,h,p=b([this.height/2-this.pointLabelFontSize-5,this.width/2]),f=this.width,d=0;for(this.ctx.font=N(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),e=0;ef&&(f=t.x+r,i=e),t.x-rf&&(f=t.x+n,i=e):e>this.valuesCount/2&&t.x-n0){var r,i=n*(this.drawingArea/this.steps),o=this.yCenter-i;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,i,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;e--){if(this.angleLineWidth>0){var n=this.getPointPosition(e,this.calculateCenterOffset(this.max));t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(n.x,n.y),t.stroke(),t.closePath()}var r=this.getPointPosition(e,this.calculateCenterOffset(this.max)+5);t.font=N(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var i=this.labels.length,o=this.labels.length/2,a=o/2,s=a>e||e>i-a,l=e===a||e===i-a;t.textAlign=0===e?"center":e===o?"center":o>e?"left":"right",t.textBaseline=l?"middle":s?"bottom":"top",t.fillText(this.labels[e],r.x,r.y)}}}}}),s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){u(a.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),g?(r=function(){return a}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))):"object"==typeof t&&t.exports&&(t.exports=a),i.Chart=a,a.noConflict=function(){return i.Chart=o,a}}).call(this),function(){"use strict";var t=this,e=t.Chart,n=e.helpers,r={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'
      <% for (var i=0; i
    • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    '};e.Type.extend({name:"Bar",defaults:r,initialize:function(t){var r=this.options;this.ScaleClass=e.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,e,n){var i=this.calculateBaseWidth(),o=this.calculateX(n)-i/2,a=this.calculateBarWidth(t);return o+a*e+e*r.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*r.barValueSpacing},calculateBarWidth:function(t){var e=this.calculateBaseWidth()-(t-1)*r.barDatasetSpacing;return e/t}}),this.datasets=[],this.options.showTooltips&&n.bindEvents(this,this.options.tooltipEvents,function(t){var e="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),n.each(e,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(e)}),this.BarClass=e.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),n.each(t.datasets,function(e){var r={label:e.label||null,fillColor:e.fillColor,strokeColor:e.strokeColor,bars:[]};this.datasets.push(r),n.each(e.data,function(n,i){r.bars.push(new this.BarClass({value:n,label:t.labels[i],datasetLabel:e.label,strokeColor:e.strokeColor,fillColor:e.fillColor,highlightFill:e.highlightFill||e.fillColor,highlightStroke:e.highlightStroke||e.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,e,r){n.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,r,e),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),n.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){n.each(this.datasets,function(e,r){n.each(e.bars,t,this,r)},this)},getBarsAtEvent:function(t){for(var e,r=[],i=n.getRelativePosition(t),o=function(t){r.push(t.bars[e])},a=0;a<% for (var i=0; i
  • <%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>'};e.Type.extend({name:"Doughnut",defaults:r,initialize:function(t){this.segments=[],this.outerRadius=(n.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=e.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&n.bindEvents(this,this.options.tooltipEvents,function(t){var e="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];n.each(this.segments,function(t){t.restore(["fillColor"])}),n.each(e,function(t){t.fillColor=t.highlightColor}),this.showTooltip(e)}),this.calculateTotal(t),n.each(t,function(t,e){this.addData(t,e,!0)},this),this.render()},getSegmentsAtEvent:function(t){var e=[],r=n.getRelativePosition(t);return n.each(this.segments,function(t){t.inRange(r.x,r.y)&&e.push(t)},this),e},addData:function(t,e,n){var r=e||this.segments.length;this.segments.splice(r,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),n||(this.reflow(),this.update())},calculateCircumference:function(t){return 2*Math.PI*(Math.abs(t)/this.total)},calculateTotal:function(t){this.total=0,n.each(t,function(t){this.total+=Math.abs(t.value)},this)},update:function(){this.calculateTotal(this.segments),n.each(this.activeElements,function(t){t.restore(["fillColor"])}),n.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var e=n.isNumber(t)?t:this.segments.length-1;this.segments.splice(e,1),this.reflow(),this.update()},reflow:function(){n.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(n.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,n.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var e=t?t:1;this.clear(),n.each(this.segments,function(t,n){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},e),t.endAngle=t.startAngle+t.circumference,t.draw(),0===n&&(t.startAngle=1.5*Math.PI),n<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'};e.Type.extend({name:"Line",defaults:r,initialize:function(t){this.PointClass=e.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ethis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(r.lineTo(s[s.length-1].x,this.scale.endPoint),r.lineTo(s[0].x,this.scale.endPoint),r.fillStyle=t.fillColor,r.closePath(),r.fill()),n.each(s,function(t){t.draw()})},this)}})}.call(this),function(){"use strict";var t=this,e=t.Chart,n=e.helpers,r={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'
      <% for (var i=0; i
    • <%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    '};e.Type.extend({name:"PolarArea",defaults:r,initialize:function(t){this.segments=[],this.SegmentArc=e.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new e.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),n.each(t,function(t,e){this.addData(t,e,!0)},this),this.options.showTooltips&&n.bindEvents(this,this.options.tooltipEvents,function(t){var e="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];n.each(this.segments,function(t){t.restore(["fillColor"])}),n.each(e,function(t){t.fillColor=t.highlightColor}),this.showTooltip(e)}),this.render()},getSegmentsAtEvent:function(t){var e=[],r=n.getRelativePosition(t);return n.each(this.segments,function(t){t.inRange(r.x,r.y)&&e.push(t)},this),e},addData:function(t,e,n){var r=e||this.segments.length;this.segments.splice(r,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),n||(this.reflow(),this.update())},removeData:function(t){var e=n.isNumber(t)?t:this.segments.length-1;this.segments.splice(e,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,n.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var e=[];n.each(t,function(t){e.push(t.value)});var r=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:n.calculateScaleRange(e,n.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);n.extend(this.scale,r,{size:n.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),n.each(this.segments,function(t){t.save()}),this.reflow(),this.render()},reflow:function(){n.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),n.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),n.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var e=t||1;this.clear(),n.each(this.segments,function(t,n){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},e),t.endAngle=t.startAngle+t.circumference,0===n&&(t.startAngle=1.5*Math.PI),n<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'},initialize:function(t){this.PointClass=e.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&n.bindEvents(this,this.options.tooltipEvents,function(t){var e="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),n.each(e,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(e)}),n.each(t.datasets,function(e){var r={label:e.label||null,fillColor:e.fillColor,strokeColor:e.strokeColor,pointColor:e.pointColor,pointStrokeColor:e.pointStrokeColor,points:[]};this.datasets.push(r),n.each(e.data,function(n,i){var o;this.scale.animation||(o=this.scale.getPointPosition(i,this.scale.calculateCenterOffset(n))),r.points.push(new this.PointClass({value:n,label:t.labels[i],datasetLabel:e.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:e.pointStrokeColor,fillColor:e.pointColor,highlightFill:e.pointHighlightFill||e.pointColor,highlightStroke:e.pointHighlightStroke||e.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){n.each(this.datasets,function(e){n.each(e.points,t,this)},this)},getPointsAtEvent:function(t){var e=n.getRelativePosition(t),r=n.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},e),i=2*Math.PI/this.scale.valuesCount,o=Math.round((r.angle-1.5*Math.PI)/i),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),r.distance<=this.scale.drawingArea&&n.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new e.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var e=function(){var e=[];return n.each(t,function(t){t.data?e=e.concat(t.data):n.each(t.points,function(t){e.push(t.value)})}),e}(),r=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:n.calculateScaleRange(e,n.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);n.extend(this.scale,r)},addData:function(t,e){this.scale.valuesCount++,n.each(t,function(t,n){var r=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[n].points.push(new this.PointClass({value:t,label:e,x:r.x,y:r.y,strokeColor:this.datasets[n].pointStrokeColor,fillColor:this.datasets[n].pointColor}))},this),this.scale.labels.push(e),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),n.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){n.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:n.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var e=t||1,r=this.chart.ctx;this.clear(),this.scale.draw(),n.each(this.datasets,function(t){n.each(t.points,function(t,n){t.hasValue()&&t.transition(this.scale.getPointPosition(n,this.scale.calculateCenterOffset(t.value)),e)},this),r.lineWidth=this.options.datasetStrokeWidth,r.strokeStyle=t.strokeColor,r.beginPath(),n.each(t.points,function(t,e){0===e?r.moveTo(t.x,t.y):r.lineTo(t.x,t.y)},this),r.closePath(),r.stroke(),r.fillStyle=t.fillColor,r.fill(),n.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this)},/*!*******************************************!*\ + !*** ./~/es6-promise/dist/es6-promise.js ***! + \*******************************************/ +function(t,e,n){var r;(function(t,i,o){/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 2.0.1 + */ +(function(){"use strict";function a(t){return"function"==typeof t||"object"==typeof t&&null!==t}function s(t){return"function"==typeof t}function u(t){return"object"==typeof t&&null!==t}function l(){}function c(){return function(){t.nextTick(d)}}function h(){var t=0,e=new z(d),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function p(){var t=new MessageChannel;return t.port1.onmessage=d,function(){t.port2.postMessage(0)}}function f(){return function(){setTimeout(d,1)}}function d(){for(var t=0;B>t;t+=2){var e=H[t],n=H[t+1];e(n),H[t]=void 0,H[t+1]=void 0}B=0}function m(){}function v(){return new TypeError("You cannot resolve a promise with itself")}function g(){return new TypeError("A promises callback cannot return that same promise.")}function y(t){try{return t.then}catch(e){return X.error=e,X}}function _(t,e,n,r){try{t.call(e,n,r)}catch(i){return i}}function b(t,e,n){Y(function(t){var r=!1,i=_(n,e,function(n){r||(r=!0,e!==n?x(t,n):D(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&i&&(r=!0,S(t,i))},t)}function w(t,e){e._state===q?D(t,e._result):t._state===K?S(t,e._result):M(e,void 0,function(e){x(t,e)},function(e){S(t,e)})}function C(t,e){if(e.constructor===t.constructor)w(t,e);else{var n=y(e);n===X?S(t,X.error):void 0===n?D(t,e):s(n)?b(t,e,n):D(t,e)}}function x(t,e){t===e?S(t,v()):a(e)?C(t,e):D(t,e)}function E(t){t._onerror&&t._onerror(t._result),T(t)}function D(t,e){t._state===G&&(t._result=e,t._state=q,0===t._subscribers.length||Y(T,t))}function S(t,e){t._state===G&&(t._state=K,t._result=e,Y(E,t))}function M(t,e,n,r){var i=t._subscribers,o=i.length;t._onerror=null,i[o]=e,i[o+q]=n,i[o+K]=r,0===o&&t._state&&Y(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,i,o=t._result,a=0;a1)throw new Error("Second argument not supported");if("object"!=typeof t)throw new TypeError("Argument must be an object");return l.prototype=t,new l},0),Y=function(t,e){H[B]=t,H[B+1]=e,B+=2,2===B&&U()},j="undefined"!=typeof window?window:{},z=j.MutationObserver||j.WebKitMutationObserver,V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,H=new Array(1e3);U="undefined"!=typeof t&&"[object process]"==={}.toString.call(t)?c():z?h():V?p():f();var G=void 0,q=1,K=2,X=new P,$=new P;A.prototype._validateInput=function(t){return W(t)},A.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},A.prototype._init=function(){this._result=new Array(this.length)};var Q=A;A.prototype._enumerate=function(){for(var t=this.length,e=this.promise,n=this._input,r=0;e._state===G&&t>r;r++)this._eachEntry(n[r],r)},A.prototype._eachEntry=function(t,e){var n=this._instanceConstructor;u(t)?t.constructor===n&&t._state!==G?(t._onerror=null,this._settledAt(t._state,e,t._result)):this._willSettleAt(n.resolve(t),e):(this._remaining--,this._result[e]=this._makeResult(q,e,t))},A.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===G&&(this._remaining--,this._abortOnReject&&t===K?S(r,n):this._result[e]=this._makeResult(t,e,n)),0===this._remaining&&D(r,this._result)},A.prototype._makeResult=function(t,e,n){return n},A.prototype._willSettleAt=function(t,e){var n=this;M(t,void 0,function(t){n._settledAt(q,e,t)},function(t){n._settledAt(K,e,t)})};var Z=function(t,e){return new Q(this,t,!0,e).promise},J=function(t,e){function n(t){x(o,t)}function r(t){S(o,t)}var i=this,o=new i(m,e);if(!W(t))return S(o,new TypeError("You must pass an array to race.")),o;for(var a=t.length,s=0;o._state===G&&a>s;s++)M(i.resolve(t[s]),void 0,n,r);return o},te=function(t,e){var n=this;if(t&&"object"==typeof t&&t.constructor===n)return t;var r=new n(m,e);return x(r,t),r},ee=function(t,e){var n=this,r=new n(m,e);return S(r,t),r},ne=0,re=I;I.all=Z,I.race=J,I.resolve=te,I.reject=ee,I.prototype={constructor:I,then:function(t,e){var n=this,r=n._state;if(r===q&&!t||r===K&&!e)return this;var i=new this.constructor(m),o=n._result;if(r){var a=arguments[r-1];Y(function(){O(r,i,a,o)})}else M(n,i,t,e);return i},"catch":function(t){return this.then(null,t)}};var ie=function(){var t;t="undefined"!=typeof i?i:"undefined"!=typeof window&&window.document?window:self;var e="Promise"in t&&"resolve"in t.Promise&&"reject"in t.Promise&&"all"in t.Promise&&"race"in t.Promise&&function(){var e;return new t.Promise(function(t){e=t}),s(e)}();e||(t.Promise=re)},oe={Promise:re,polyfill:ie};n(/*! !webpack amd define */199).amd?(r=function(){return oe}.call(e,n,e,o),!(void 0!==r&&(o.exports=r))):"undefined"!=typeof o&&o.exports?o.exports=oe:"undefined"!=typeof this&&(this.ES6Promise=oe)}).call(this)}).call(e,n(/*! (webpack)/~/node-libs-browser/~/process/browser.js */201),function(){return this}(),n(/*! (webpack)/buildin/module.js */57)(t))},/*!****************************!*\ + !*** ./~/events/events.js ***! + \****************************/ +function(t){function e(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function n(t){return"function"==typeof t}function r(t){return"number"==typeof t}function i(t){return"object"==typeof t&&null!==t}function o(t){return void 0===t}t.exports=e,e.EventEmitter=e,e.prototype._events=void 0,e.prototype._maxListeners=void 0,e.defaultMaxListeners=10,e.prototype.setMaxListeners=function(t){if(!r(t)||0>t||isNaN(t))throw TypeError("n must be a positive number");return this._maxListeners=t,this},e.prototype.emit=function(t){var e,r,a,s,u,l;if(this._events||(this._events={}),"error"===t&&(!this._events.error||i(this._events.error)&&!this._events.error.length)){if(e=arguments[1],e instanceof Error)throw e;throw TypeError('Uncaught, unspecified "error" event.')}if(r=this._events[t],o(r))return!1;if(n(r))switch(arguments.length){case 1:r.call(this);break;case 2:r.call(this,arguments[1]);break;case 3:r.call(this,arguments[1],arguments[2]);break;default:for(a=arguments.length,s=new Array(a-1),u=1;a>u;u++)s[u-1]=arguments[u];r.apply(this,s)}else if(i(r)){for(a=arguments.length,s=new Array(a-1),u=1;a>u;u++)s[u-1]=arguments[u];for(l=r.slice(),a=l.length,u=0;a>u;u++)l[u].apply(this,s)}return!0},e.prototype.addListener=function(t,r){var a;if(!n(r))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",t,n(r.listener)?r.listener:r),this._events[t]?i(this._events[t])?this._events[t].push(r):this._events[t]=[this._events[t],r]:this._events[t]=r,i(this._events[t])&&!this._events[t].warned){var a;a=o(this._maxListeners)?e.defaultMaxListeners:this._maxListeners,a&&a>0&&this._events[t].length>a&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),"function"==typeof console.trace&&console.trace())}return this},e.prototype.on=e.prototype.addListener,e.prototype.once=function(t,e){function r(){this.removeListener(t,r),i||(i=!0,e.apply(this,arguments))}if(!n(e))throw TypeError("listener must be a function");var i=!1;return r.listener=e,this.on(t,r),this},e.prototype.removeListener=function(t,e){var r,o,a,s;if(!n(e))throw TypeError("listener must be a function");if(!this._events||!this._events[t])return this;if(r=this._events[t],a=r.length,o=-1,r===e||n(r.listener)&&r.listener===e)delete this._events[t],this._events.removeListener&&this.emit("removeListener",t,e);else if(i(r)){for(s=a;s-->0;)if(r[s]===e||r[s].listener&&r[s].listener===e){o=s;break}if(0>o)return this;1===r.length?(r.length=0,delete this._events[t]):r.splice(o,1),this._events.removeListener&&this.emit("removeListener",t,e)}return this},e.prototype.removeAllListeners=function(t){var e,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);return this.removeAllListeners("removeListener"),this._events={},this}if(r=this._events[t],n(r))this.removeListener(t,r);else for(;r.length;)this.removeListener(t,r[r.length-1]);return delete this._events[t],this},e.prototype.listeners=function(t){var e;return e=this._events&&this._events[t]?n(this._events[t])?[this._events[t]]:this._events[t].slice():[]},e.listenerCount=function(t,e){var r;return r=t._events&&t._events[e]?n(t._events[e])?1:t._events[e].length:0}},/*!*************************!*\ + !*** ./~/flux/index.js ***! + \*************************/ +function(t,e,n){t.exports.Dispatcher=n(/*! ./lib/Dispatcher */114)},/*!**********************************!*\ + !*** ./~/flux/lib/Dispatcher.js ***! + \**********************************/ +function(t,e,n){"use strict";function r(){this.$Dispatcher_callbacks={},this.$Dispatcher_isPending={},this.$Dispatcher_isHandled={},this.$Dispatcher_isDispatching=!1,this.$Dispatcher_pendingPayload=null}var i=n(/*! ./invariant */115),o=1,a="ID_";r.prototype.register=function(t){var e=a+o++;return this.$Dispatcher_callbacks[e]=t,e},r.prototype.unregister=function(t){i(this.$Dispatcher_callbacks[t],"Dispatcher.unregister(...): `%s` does not map to a registered callback.",t),delete this.$Dispatcher_callbacks[t]},r.prototype.waitFor=function(t){i(this.$Dispatcher_isDispatching,"Dispatcher.waitFor(...): Must be invoked while dispatching.");for(var e=0;ee||!n||"undefined"==typeof t&&r)return 1;if(e>t||!r||"undefined"==typeof e&&n)return-1}return 0}function a(t,e,n){if(e!==e)return g(t,n);for(var r=(n||0)-1,i=t.length;++r-1;);return n}function h(t,e){for(var n=t.length;n--&&e.indexOf(t.charAt(n))>-1;);return n}function p(t,e){return o(t.criteria,e.criteria)||t.index-e.index}function f(t,e){for(var n=-1,r=t.criteria,i=e.criteria,a=r.length;++n=t&&t>=9&&13>=t||32==t||160==t||5760==t||6158==t||t>=8192&&(8202>=t||8232==t||8233==t||8239==t||8287==t||12288==t||65279==t)}function b(t,e){for(var n=-1,r=t.length,i=-1,o=[];++ne,r=yr(0,t.length,this.__views__),i=r.start,o=r.end,a=o-i,s=this.__dropCount__,u=Es(a,this.__takeCount__),l=n?o:i-1,c=this.__iteratees__,h=c?c.length:0,p=0,f=[];t:for(;a--&&u>p;){l+=e;for(var d=-1,m=t[l];++dr&&(r=i)}return r}function on(t){for(var e=-1,n=t.length,r=ks;++ei&&(r=i)}return r}function an(t,e,n,r){var i=-1,o=t.length;for(r&&o&&(n=t[++i]);++i=200&&Bs(e),l=e.length;u&&(o=qe,s=!1,e=u);t:for(;++in&&(n=-n>i?0:i+n),r="undefined"==typeof r||r>i?i:+r||0,0>r&&(r+=i),i=n>r?0:r>>>0,n>>>=0;i>n;)t[n++]=e;return t}function xn(t,e){var n=[];return _n(t,function(t,r,i){e(t,r,i)&&n.push(t)}),n}function En(t,e,n,r){var i;return n(t,function(t,n,o){return e(t,n,o)?(i=r?n:t,!1):void 0}),i}function Dn(t,e,n,r){for(var i=(r||0)-1,o=t.length,a=-1,s=[];++ie&&(e=-e>i?0:i+e),n="undefined"==typeof n||n>i?i:+n||0,0>n&&(n+=i),i=e>n?0:n-e>>>0,e>>>=0;for(var o=Wa(i);++r=200,u=s&&Bs(),l=[];u?(r=qe,o=!1):(s=!1,u=e?[]:l);t:for(;++n=i){for(;i>r;){var o=r+i>>>1,a=t[o];(n?e>=a:e>a)?r=o+1:i=o}return i}return Qn(t,e,Ta,n)}function Qn(t,e,n,r){e=n(e);for(var i=0,o=t?t.length:0,a=e!==e,s="undefined"==typeof e;o>i;){var u=ls((i+o)/2),l=n(t[u]),c=l===l;if(a)var h=c||r;else h=s?c&&(r||"undefined"!=typeof l):r?e>=l:e>l;h?i=u+1:o=u}return Es(o,Rs)}function Zn(t,e,n){if("function"!=typeof t)return Ta;if("undefined"==typeof e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 3:return function(n,r,i){return t.call(e,n,r,i)};case 4:return function(n,r,i,o){return t.call(e,n,r,i,o)};case 5:return function(n,r,i,o,a){return t.call(e,n,r,i,o,a)}}return function(){return t.apply(e,arguments)}}function Jn(t){return as.call(t,0)}function tr(t,e,n){for(var r=n.length,i=-1,o=xs(t.length-r,0),a=-1,s=e.length,u=Wa(o+s);++ae||null==n)return n;if(e>3&&Er(arguments[1],arguments[2],arguments[3])&&(e=2),e>3&&"function"==typeof arguments[e-2])var r=Zn(arguments[--e-1],arguments[e--],5);else e>2&&"function"==typeof arguments[e-1]&&(r=arguments[--e]);for(var i=0;++i_){var D=s?$e(s):null,S=xs(l-_,0),M=d?E:null,k=d?null:E,O=d?C:null,R=d?null:C;e|=d?A:L,e&=~(d?L:A),m||(e&=~(T|P));var N=ur(t,e,n,O,M,R,k,D,u,S);return N.placeholder=x,N}}var I=p?n:this;return f&&(t=I[y]),s&&(C=kr(C,s)),h&&u=e||!ws(e))return"";var i=e-r;return n=null==n?" ":n+"",va(n,ss(i/n.length)).slice(0,i)}function cr(t,e,n,r){function i(){for(var e=-1,s=arguments.length,u=-1,l=r.length,c=Wa(s+l);++uu))return!1;for(;c&&++su:u>i)||u===r&&u===o)&&(i=u,o=t)}),o}function vr(t,n,r){var i=e.callback||Sa;return i=i===Sa?mn:i,r?i(t,n,r):i}function gr(t,n,r){var i=e.indexOf||Xr;return i=i===Xr?a:i,t?i(t,n,r):i}function yr(t,e,n){for(var r=-1,i=n?n.length:0;++r-1&&t%1==0&&e>t}function Er(t,e,n){if(!To(n))return!1;var r=typeof e;if("number"==r)var i=n.length,o=Dr(i)&&xr(e,i);else o="string"==r&&e in n;return o&&n[e]===t}function Dr(t){return"number"==typeof t&&t>-1&&t%1==0&&Ns>=t}function Sr(t){return t===t&&(0===t?1/t>0:!To(t))}function Mr(t,e){var n=t[1],r=e[1],i=n|r,o=I|N,a=T|P,s=o|a|k|R,u=n&I&&!(r&I),l=n&N&&!(r&N),c=(l?t:e)[7],h=(u?t:e)[8],p=!(n>=N&&r>a||n>a&&r>=N),f=i>=o&&s>=i&&(N>n||(l||u)&&c.length<=h);if(!p&&!f)return t;r&T&&(t[2]=e[2],i|=n&T?0:k);var d=e[3];if(d){var m=t[3];t[3]=m?tr(m,d,e[4]):$e(d),t[4]=m?b(t[3],H):$e(e[4])}return d=e[5],d&&(m=t[5],t[5]=m?er(m,d,e[6]):$e(d),t[6]=m?b(t[5],H):$e(e[6])),d=e[7],d&&(t[7]=$e(d)),r&I&&(t[8]=null==t[8]?e[8]:Es(t[8],e[8])),null==t[9]&&(t[9]=e[9]),t[0]=e[0],t[1]=i,t}function Tr(t,e){t=Lr(t);for(var n=-1,r=e.length,i={};++nr;)a[++o]=Hn(t,r,r+=e);return a}function Fr(t){for(var e=-1,n=t?t.length:0,r=-1,i=[];++ee?0:e)):[]}function Br(t,e,n){var r=t?t.length:0;return r?((n?Er(t,e,n):null==e)&&(e=1),e=r-(+e||0),Hn(t,0,0>e?0:e)):[]}function Yr(t,e,n){var r=t?t.length:0;if(!r)return[];for(e=vr(e,n,3);r--&&e(t[r],r,t););return Hn(t,0,r+1)}function jr(t,e,n){var r=t?t.length:0;if(!r)return[];var i=-1;for(e=vr(e,n,3);++in?xs(r+n,0):n||0;else if(n){var i=$n(t,e),o=t[i];return(e===e?e===o:o!==o)?i:-1}return a(t,e,n)}function $r(t){return Br(t,1)}function Qr(){for(var t=[],e=-1,n=arguments.length,r=[],i=gr(),o=i==a;++e=120&&Bs(e&&s)))}n=t.length;var u=t[0],l=-1,c=u?u.length:0,h=[],p=r[0];t:for(;++ln?xs(r+n,0):Es(n||0,r-1))+1;else if(n){i=$n(t,e,!0)-1;var o=t[i];return(e===e?e===o:o!==o)?i:-1}if(e!==e)return g(t,i,!0);for(;i--;)if(t[i]===e)return i;return-1}function ti(){var t=arguments[0];if(!t||!t.length)return t;for(var e=0,n=gr(),r=arguments.length;++e-1;)ms.call(t,i,1);return t}function ei(t){return jn(t||[],Dn(arguments,!1,!1,1))}function ni(t,e,n){var r=-1,i=t?t.length:0,o=[];for(e=vr(e,n,3);++re?0:e)):[]}function ui(t,e,n){var r=t?t.length:0;return r?((n?Er(t,e,n):null==e)&&(e=1),e=r-(+e||0),Hn(t,0>e?0:e)):[]}function li(t,e,n){var r=t?t.length:0;if(!r)return[];for(e=vr(e,n,3);r--&&e(t[r],r,t););return Hn(t,r+1)}function ci(t,e,n){var r=t?t.length:0;if(!r)return[];var i=-1;for(e=vr(e,n,3);++i>>0,r=Wa(n);++en?xs(r+n,0):n||0:0,"string"==typeof t||!$s(t)&&No(t)?r>n&&t.indexOf(e,n)>-1:gr(t,e,n)>-1):!1}function Pi(t,e,n){var r=$s(t)?tn:wn;return("function"!=typeof e||"undefined"!=typeof n)&&(e=vr(e,n,3)),r(t,e)}function ki(t,e,n){var r=$s(t)?en:xn;return e=vr(e,n,3),r(t,e)}function Oi(t,e,n){if($s(t)){var r=Vr(t,e,n);return r>-1?t[r]:S}return e=vr(e,n,3),En(t,e,_n)}function Ri(t,e,n){return e=vr(e,n,3),En(t,e,bn)}function Ai(t,e){return Oi(t,Fn(e))}function Li(t,e,n){return"function"==typeof e&&"undefined"==typeof n&&$s(t)?Qe(t,e):_n(t,Zn(e,n,3))}function Ni(t,e,n){return"function"==typeof e&&"undefined"==typeof n&&$s(t)?Je(t,e):bn(t,Zn(e,n,3))}function Ii(t,e){return Rn(t,e,Hn(arguments,2))}function Fi(t,e,n){var r=$s(t)?nn:In;return e=vr(e,n,3),r(t,e)}function Ui(t,e){return Fi(t,Yn(e))}function Wi(t,e,n,r){var i=$s(t)?an:Vn;return i(t,vr(e,r,4),n,arguments.length<3,_n)}function Bi(t,e,n,r){var i=$s(t)?sn:Vn;return i(t,vr(e,r,4),n,arguments.length<3,bn)}function Yi(t,e,n){var r=$s(t)?en:xn;return e=vr(e,n,3),r(t,function(t,n,r){return!e(t,n,r)})}function ji(t,e,n){if(n?Er(t,e,n):null==e){t=Ar(t);var r=t.length;return r>0?t[zn(0,r-1)]:S}var i=zi(t);return i.length=Es(0>e?0:+e||0,i.length),i}function zi(t){t=Ar(t);for(var e=-1,n=t.length,r=Wa(n);++e3&&Er(e[1],e[2],e[3])&&(e=[t,e[1]]);var n=-1,r=t?t.length:0,i=Dn(e,!1,!1,1),o=Dr(r)?Wa(r):[];return _n(t,function(t){for(var e=i.length,r=Wa(e);e--;)r[e]=null==t?S:t[i[e]];o[++n]={criteria:r,index:n,value:t}}),s(o,f)}function Ki(t,e){return ki(t,Fn(e))}function Xi(t,e){if("function"!=typeof e){if("function"!=typeof t)throw new Ka(V);var n=t;t=e,e=n}return t=ws(t=+t)?t:0,function(){return--t<1?e.apply(this,arguments):void 0}}function $i(t,e,n){return n&&Er(t,e,n)&&(e=null),e=t&&null==e?t.length:xs(+e||0,0),hr(t,I,null,null,null,null,e)}function Qi(t,e){var n;if("function"!=typeof e){if("function"!=typeof t)throw new Ka(V);var r=t;t=e,e=r}return function(){return--t>0?n=e.apply(this,arguments):e=null,n}}function Zi(t,e){var n=T;if(arguments.length>2){var r=Hn(arguments,2),i=b(r,Zi.placeholder);n|=A}return hr(t,n,e,r,i)}function Ji(t){return dn(t,arguments.length>1?Dn(arguments,!1,!1,1):Ko(t))}function to(t,e){var n=T|P;if(arguments.length>2){var r=Hn(arguments,2),i=b(r,to.placeholder);n|=A}return hr(e,n,t,r,i)}function eo(t,e,n){n&&Er(t,e,n)&&(e=null);var r=hr(t,O,null,null,null,null,null,e);return r.placeholder=eo.placeholder,r}function no(t,e,n){n&&Er(t,e,n)&&(e=null);var r=hr(t,R,null,null,null,null,null,e);return r.placeholder=no.placeholder,r}function ro(t,e,n){function r(){p&&us(p),u&&us(u),u=p=f=S}function i(){var n=e-(Xs()-c);if(0>=n||n>e){u&&us(u);var r=f;u=p=f=S,r&&(d=Xs(),l=t.apply(h,s),p||u||(s=h=null))}else p=ds(i,n)}function o(){p&&us(p),u=p=f=S,(v||m!==e)&&(d=Xs(),l=t.apply(h,s),p||u||(s=h=null))}function a(){if(s=arguments,c=Xs(),h=this,f=v&&(p||!g),m===!1)var n=g&&!p;else{u||g||(d=c);var r=m-(c-d),a=0>=r||r>m;a?(u&&(u=us(u)),d=c,l=t.apply(h,s)):u||(u=ds(o,r))}return a&&p?p=us(p):p||e===m||(p=ds(i,e)),n&&(a=!0,l=t.apply(h,s)),!a||p||u||(s=h=null),l}var s,u,l,c,h,p,f,d=0,m=!1,v=!0;if("function"!=typeof t)throw new Ka(V);if(e=0>e?0:e,n===!0){var g=!0;v=!1}else To(n)&&(g=n.leading,m="maxWait"in n&&xs(+n.maxWait||0,e),v="trailing"in n?n.trailing:v);return a.cancel=r,a}function io(t){return gn(t,1,arguments,1)}function oo(t,e){return gn(t,e,arguments,2)}function ao(){var t=arguments,e=t.length;if(!e)return function(){return arguments[0]};if(!tn(t,Mo))throw new Ka(V);return function(){for(var n=0,r=t[n].apply(this,arguments);++ne)return function(){return arguments[0]};if(!tn(t,Mo))throw new Ka(V);return function(){for(var n=e,r=t[n].apply(this,arguments);n--;)r=t[n].call(this,r);return r}}function uo(t,e){if("function"!=typeof t||e&&"function"!=typeof e)throw new Ka(V);var n=function(){var r=n.cache,i=e?e.apply(this,arguments):arguments[0];if(r.has(i))return r.get(i);var o=t.apply(this,arguments);return r.set(i,o),o};return n.cache=new uo.Cache,n}function lo(t){if("function"!=typeof t)throw new Ka(V);return function(){return!t.apply(this,arguments)}}function co(t){return Qi(t,2)}function ho(t){var e=Hn(arguments,1),n=b(e,ho.placeholder);return hr(t,A,null,e,n)}function po(t){var e=Hn(arguments,1),n=b(e,po.placeholder);return hr(t,L,null,e,n)}function fo(t){var e=Dn(arguments,!1,!1,1);return hr(t,N,null,null,null,e)}function mo(t){if("function"!=typeof t)throw new Ka(V);return function(e){return t.apply(this,e)}}function vo(t,e,n){var r=!0,i=!0;if("function"!=typeof t)throw new Ka(V);return n===!1?r=!1:To(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),je.leading=r,je.maxWait=+e,je.trailing=i,ro(t,e,je)}function go(t,e){return e=null==e?Ta:e,hr(e,A,null,[t],[])}function yo(t,e,n,r){return"boolean"!=typeof e&&null!=e&&(r=n,n=Er(t,e,r)?null:e,e=!1),n="function"==typeof n&&Zn(n,r,1),vn(t,e,n)}function _o(t,e,n){return e="function"==typeof e&&Zn(e,n,1),vn(t,!0,e)}function bo(t){var e=y(t)?t.length:S;return Dr(e)&&ns.call(t)==G||!1}function wo(t){return t===!0||t===!1||y(t)&&ns.call(t)==K||!1}function Co(t){return y(t)&&ns.call(t)==X||!1}function xo(t){return t&&1===t.nodeType&&y(t)&&ns.call(t).indexOf("Element")>-1||!1}function Eo(t){if(null==t)return!0;var e=t.length;return Dr(e)&&($s(t)||No(t)||bo(t)||y(t)&&Mo(t.splice))?!e:!tu(t).length}function Do(t,e,n,r){if(n="function"==typeof n&&Zn(n,r,3),!n&&Sr(t)&&Sr(e))return t===e;var i=n?n(t,e):S;return"undefined"==typeof i?An(t,e,n):!!i}function So(t){return y(t)&&"string"==typeof t.message&&ns.call(t)==$||!1}function Mo(t){return"function"==typeof t||!1}function To(t){var e=typeof t;return"function"==e||t&&"object"==e||!1}function Po(t,e,n,r){var i=tu(e),o=i.length;if(n="function"==typeof n&&Zn(n,r,3),!n&&1==o){var a=i[0],s=e[a];if(Sr(s))return null!=t&&s===t[a]&&ts.call(t,a)}for(var u=Wa(o),l=Wa(o);o--;)s=u[o]=e[i[o]],l[o]=Sr(s);return Nn(t,i,u,l,n)}function ko(t){return Ao(t)&&t!=+t}function Oo(t){return null==t?!1:ns.call(t)==Q?is.test(Za.call(t)):y(t)&&Pe.test(t)||!1}function Ro(t){return null===t}function Ao(t){return"number"==typeof t||y(t)&&ns.call(t)==J||!1}function Lo(t){return y(t)&&ns.call(t)==ee||!1}function No(t){return"string"==typeof t||y(t)&&ns.call(t)==re||!1}function Io(t){return y(t)&&Dr(t.length)&&Be[ns.call(t)]||!1}function Fo(t){return"undefined"==typeof t}function Uo(t){var e=t?t.length:0;return Dr(e)?e?$e(t):[]:ia(t)}function Wo(t){return fn(t,Qo(t))}function Bo(t,e,n){var r=Us(t);return n&&Er(t,e,n)&&(e=null),e?fn(e,r,tu(e)):r}function Yo(t){if(null==t)return t;var e=$e(arguments);return e.push(ln),Js.apply(S,e)}function jo(t,e,n){return e=vr(e,n,3),En(t,e,Pn,!0)}function zo(t,e,n){return e=vr(e,n,3),En(t,e,kn,!0)}function Vo(t,e,n){return("function"!=typeof e||"undefined"!=typeof n)&&(e=Zn(e,n,3)),Sn(t,e,Qo)}function Ho(t,e,n){return e=Zn(e,n,3),Mn(t,e,Qo)}function Go(t,e,n){return("function"!=typeof e||"undefined"!=typeof n)&&(e=Zn(e,n,3)),Pn(t,e)}function qo(t,e,n){return e=Zn(e,n,3),Mn(t,e,tu)}function Ko(t){return On(t,Qo(t))}function Xo(t,e){return t?ts.call(t,e):!1}function $o(t,e,n){n&&Er(t,e,n)&&(e=null);for(var r=-1,i=tu(t),o=i.length,a={};++r0;++rn?0:+n||0,r))-e.length,n>=0&&t.indexOf(e,n)==n +}function ca(t){return t=u(t),t&&we.test(t)?t.replace(_e,m):t}function ha(t){return t=u(t),t&&Ae.test(t)?t.replace(Re,"\\$&"):t}function pa(t,e,n){t=u(t),e=+e;var r=t.length;if(r>=e||!ws(e))return t;var i=(e-r)/2,o=ls(i),a=ss(i);return n=lr("",a,n),n.slice(0,o)+t+n}function fa(t,e,n){return t=u(t),t&&lr(t,e,n)+t}function da(t,e,n){return t=u(t),t&&t+lr(t,e,n)}function ma(t,e,n){return n&&Er(t,e,n)&&(e=0),Ms(t,e)}function va(t,e){var n="";if(t=u(t),e=+e,1>e||!t||!ws(e))return n;do e%2&&(n+=t),e=ls(e/2),t+=t;while(e);return n}function ga(t,e,n){return t=u(t),n=null==n?0:Es(0>n?0:+n||0,t.length),t.lastIndexOf(e,n)==n}function ya(t,n,r){var i=e.templateSettings;r&&Er(t,n,r)&&(n=r=null),t=u(t),n=hn(hn({},r||n),i,cn);var o,a,s=hn(hn({},n.imports),i.imports,cn),l=tu(s),c=Kn(s,l),h=0,p=n.interpolate||Oe,f="__p += '",d=Ga((n.escape||Oe).source+"|"+p.source+"|"+(p===Ee?De:Oe).source+"|"+(n.evaluate||Oe).source+"|$","g"),m="//# sourceURL="+("sourceURL"in n?n.sourceURL:"lodash.templateSources["+ ++We+"]")+"\n";t.replace(d,function(e,n,r,i,s,u){return r||(r=i),f+=t.slice(h,u).replace(Ne,v),n&&(o=!0,f+="' +\n__e("+n+") +\n'"),s&&(a=!0,f+="';\n"+s+";\n__p += '"),r&&(f+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),h=u+e.length,e}),f+="';\n";var g=n.variable;g||(f="with (obj) {\n"+f+"\n}\n"),f=(a?f.replace(me,""):f).replace(ve,"$1").replace(ge,"$1;"),f="function("+(g||"obj")+") {\n"+(g?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(o?", __e = _.escape":"")+(a?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+f+"return __p\n}";var y=Da(function(){return ja(l,m+"return "+f).apply(S,c)});if(y.source=f,So(y))throw y;return y}function _a(t,e,n){var r=t;return(t=u(t))?(n?Er(r,e,n):null==e)?t.slice(C(t),x(t)+1):(e+="",t.slice(c(t,e),h(t,e)+1)):t}function ba(t,e,n){var r=t;return t=u(t),t?t.slice((n?Er(r,e,n):null==e)?C(t):c(t,e+"")):t}function wa(t,e,n){var r=t;return t=u(t),t?(n?Er(r,e,n):null==e)?t.slice(0,x(t)+1):t.slice(0,h(t,e+"")+1):t}function Ca(t,e,n){n&&Er(t,e,n)&&(e=null);var r=F,i=U;if(null!=e)if(To(e)){var o="separator"in e?e.separator:o;r="length"in e?+e.length||0:r,i="omission"in e?u(e.omission):i}else r=+e||0;if(t=u(t),r>=t.length)return t;var a=r-i.length;if(1>a)return i;var s=t.slice(0,a);if(null==o)return s+i;if(Lo(o)){if(t.slice(a).search(o)){var l,c,h=t.slice(0,a);for(o.global||(o=Ga(o.source,(Se.exec(o)||"")+"g")),o.lastIndex=0;l=o.exec(h);)c=l.index;s=s.slice(0,null==c?a:c)}}else if(t.indexOf(o,a)!=a){var p=s.lastIndexOf(o);p>-1&&(s=s.slice(0,p))}return s+i}function xa(t){return t=u(t),t&&be.test(t)?t.replace(ye,E):t}function Ea(t,e,n){return n&&Er(t,e,n)&&(e=null),t=u(t),t.match(e||Ie)||[]}function Da(t){try{return t.apply(S,Hn(arguments,1))}catch(e){return So(e)?e:new Ya(e)}}function Sa(t,e,n){return n&&Er(t,e,n)&&(e=null),y(t)?Pa(t):mn(t,e)}function Ma(t){return function(){return t}}function Ta(t){return t}function Pa(t){return Fn(vn(t,!0))}function ka(t,e){return Un(t+"",vn(e,!0))}function Oa(t,e,n){if(null==n){var r=To(e),i=r&&tu(e),o=i&&i.length&&On(e,i);(o?o.length:r)||(o=!1,n=e,e=t,t=this)}o||(o=On(e,tu(e)));var a=!0,s=-1,u=Mo(t),l=o.length;n===!1?a=!1:To(n)&&"chain"in n&&(a=n.chain);for(;++st||!ws(t))return[];var r=-1,i=Wa(Es(t,Os));for(e=Zn(e,n,1);++rr?i[r]=e(r):e(r);return i}function Ua(t){var e=++es;return u(t)+e}t=t?Ze.defaults(Ke.Object(),t,Ze.pick(Ke,Ue)):Ke;var Wa=t.Array,Ba=t.Date,Ya=t.Error,ja=t.Function,za=t.Math,Va=t.Number,Ha=t.Object,Ga=t.RegExp,qa=t.String,Ka=t.TypeError,Xa=Wa.prototype,$a=Ha.prototype,Qa=(Qa=t.window)&&Qa.document,Za=ja.prototype.toString,Ja=Yn("length"),ts=$a.hasOwnProperty,es=0,ns=$a.toString,rs=t._,is=Ga("^"+ha(ns).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),os=Oo(os=t.ArrayBuffer)&&os,as=Oo(as=os&&new os(0).slice)&&as,ss=za.ceil,us=t.clearTimeout,ls=za.floor,cs=Oo(cs=Ha.getPrototypeOf)&&cs,hs=Xa.push,ps=$a.propertyIsEnumerable,fs=Oo(fs=t.Set)&&fs,ds=t.setTimeout,ms=Xa.splice,vs=Oo(vs=t.Uint8Array)&&vs,gs=Oo(gs=t.WeakMap)&&gs,ys=function(){try{var e=Oo(e=t.Float64Array)&&e,n=new e(new os(10),0,1)&&e}catch(r){}return n}(),_s=Oo(_s=Wa.isArray)&&_s,bs=Oo(bs=Ha.create)&&bs,ws=t.isFinite,Cs=Oo(Cs=Ha.keys)&&Cs,xs=za.max,Es=za.min,Ds=Oo(Ds=Ba.now)&&Ds,Ss=Oo(Ss=Va.isFinite)&&Ss,Ms=t.parseInt,Ts=za.random,Ps=Va.NEGATIVE_INFINITY,ks=Va.POSITIVE_INFINITY,Os=za.pow(2,32)-1,Rs=Os-1,As=Os>>>1,Ls=ys?ys.BYTES_PER_ELEMENT:0,Ns=za.pow(2,53)-1,Is=gs&&new gs,Fs=e.support={};!function(){Fs.funcDecomp=!Oo(t.WinRTError)&&Le.test(D),Fs.funcNames="string"==typeof ja.name;try{Fs.dom=11===Qa.createDocumentFragment().nodeType}catch(e){Fs.dom=!1}try{Fs.nonEnumArgs=!ps.call(arguments,1)}catch(e){Fs.nonEnumArgs=!0}}(0,0),e.templateSettings={escape:Ce,evaluate:xe,interpolate:Ee,variable:"",imports:{_:e}};var Us=function(){function e(){}return function(n){if(To(n)){e.prototype=n;var r=new e;e.prototype=null}return r||t.Object()}}(),Ws=Is?function(t,e){return Is.set(t,e),t}:Ta;as||(Jn=os&&vs?function(t){var e=t.byteLength,n=ys?ls(e/Ls):0,r=n*Ls,i=new os(e);if(n){var o=new ys(i,0,n);o.set(new ys(t,0,n))}return e!=r&&(o=new vs(i,r),o.set(new vs(t,r))),i}:Ma(null));var Bs=bs&&fs?function(t){return new Ge(t)}:Ma(null),Ys=Is?function(t){return Is.get(t)}:Aa,js=function(){var t=0,e=0;return function(n,r){var i=Xs(),o=B-(i-e);if(e=i,o>0){if(++t>=W)return n}else t=0;return Ws(n,r)}}(),zs=nr(function(t,e,n){ts.call(t,n)?++t[n]:t[n]=1}),Vs=nr(function(t,e,n){ts.call(t,n)?t[n].push(e):t[n]=[e]}),Hs=nr(function(t,e,n){t[n]=e}),Gs=sr(rn),qs=sr(on,!0),Ks=nr(function(t,e,n){t[n?0:1].push(e)},function(){return[[],[]]}),Xs=Ds||function(){return(new Ba).getTime()},$s=_s||function(t){return y(t)&&Dr(t.length)&&ns.call(t)==q||!1};Fs.dom||(xo=function(t){return t&&1===t.nodeType&&y(t)&&!Zs(t)||!1});var Qs=Ss||function(t){return"number"==typeof t&&ws(t)};(Mo(/x/)||vs&&!Mo(vs))&&(Mo=function(t){return ns.call(t)==Q});var Zs=cs?function(t){if(!t||ns.call(t)!=te)return!1;var e=t.valueOf,n=Oo(e)&&(n=cs(e))&&cs(n);return n?t==n||cs(t)==n:Or(t)}:Or,Js=rr(hn),tu=Cs?function(t){if(t)var e=t.constructor,n=t.length;return"function"==typeof e&&e.prototype===t||"function"!=typeof t&&n&&Dr(n)?Rr(t):To(t)?Cs(t):[]}:Rr,eu=rr(Wn),nu=or(function(t,e,n){return e=e.toLowerCase(),t+(n?e.charAt(0).toUpperCase()+e.slice(1):e)}),ru=or(function(t,e,n){return t+(n?"-":"")+e.toLowerCase()});8!=Ms(Fe+"08")&&(ma=function(t,e,n){return(n?Er(t,e,n):null==e)?e=0:e&&(e=+e),t=_a(t),Ms(t,e||(Te.test(t)?16:10))});var iu=or(function(t,e,n){return t+(n?"_":"")+e.toLowerCase()}),ou=or(function(t,e,n){return t+(n?" ":"")+(e.charAt(0).toUpperCase()+e.slice(1))});return n.prototype=Us(e.prototype),r.prototype=Us(n.prototype),r.prototype.constructor=r,ne.prototype["delete"]=ie,ne.prototype.get=ze,ne.prototype.has=Ve,ne.prototype.set=He,Ge.prototype.push=Xe,uo.Cache=ne,e.after=Xi,e.ary=$i,e.assign=Js,e.at=Mi,e.before=Qi,e.bind=Zi,e.bindAll=Ji,e.bindKey=to,e.callback=Sa,e.chain=yi,e.chunk=Ir,e.compact=Fr,e.constant=Ma,e.countBy=zs,e.create=Bo,e.curry=eo,e.curryRight=no,e.debounce=ro,e.defaults=Yo,e.defer=io,e.delay=oo,e.difference=Ur,e.drop=Wr,e.dropRight=Br,e.dropRightWhile=Yr,e.dropWhile=jr,e.fill=zr,e.filter=ki,e.flatten=qr,e.flattenDeep=Kr,e.flow=ao,e.flowRight=so,e.forEach=Li,e.forEachRight=Ni,e.forIn=Vo,e.forInRight=Ho,e.forOwn=Go,e.forOwnRight=qo,e.functions=Ko,e.groupBy=Vs,e.indexBy=Hs,e.initial=$r,e.intersection=Qr,e.invert=$o,e.invoke=Ii,e.keys=tu,e.keysIn=Qo,e.map=Fi,e.mapValues=Zo,e.matches=Pa,e.matchesProperty=ka,e.memoize=uo,e.merge=eu,e.mixin=Oa,e.negate=lo,e.omit=Jo,e.once=co,e.pairs=ta,e.partial=ho,e.partialRight=po,e.partition=Ks,e.pick=ea,e.pluck=Ui,e.property=La,e.propertyOf=Na,e.pull=ti,e.pullAt=ei,e.range=Ia,e.rearg=fo,e.reject=Yi,e.remove=ni,e.rest=ri,e.shuffle=zi,e.slice=ii,e.sortBy=Gi,e.sortByAll=qi,e.spread=mo,e.take=si,e.takeRight=ui,e.takeRightWhile=li,e.takeWhile=ci,e.tap=_i,e.throttle=vo,e.thru=bi,e.times=Fa,e.toArray=Uo,e.toPlainObject=Wo,e.transform=ra,e.union=hi,e.uniq=pi,e.unzip=fi,e.values=ia,e.valuesIn=oa,e.where=Ki,e.without=di,e.wrap=go,e.xor=mi,e.zip=vi,e.zipObject=gi,e.backflow=so,e.collect=Fi,e.compose=so,e.each=Li,e.eachRight=Ni,e.extend=Js,e.iteratee=Sa,e.methods=Ko,e.object=gi,e.select=ki,e.tail=ri,e.unique=pi,Oa(e,e),e.attempt=Da,e.camelCase=nu,e.capitalize=sa,e.clone=yo,e.cloneDeep=_o,e.deburr=ua,e.endsWith=la,e.escape=ca,e.escapeRegExp=ha,e.every=Pi,e.find=Oi,e.findIndex=Vr,e.findKey=jo,e.findLast=Ri,e.findLastIndex=Hr,e.findLastKey=zo,e.findWhere=Ai,e.first=Gr,e.has=Xo,e.identity=Ta,e.includes=Ti,e.indexOf=Xr,e.isArguments=bo,e.isArray=$s,e.isBoolean=wo,e.isDate=Co,e.isElement=xo,e.isEmpty=Eo,e.isEqual=Do,e.isError=So,e.isFinite=Qs,e.isFunction=Mo,e.isMatch=Po,e.isNaN=ko,e.isNative=Oo,e.isNull=Ro,e.isNumber=Ao,e.isObject=To,e.isPlainObject=Zs,e.isRegExp=Lo,e.isString=No,e.isTypedArray=Io,e.isUndefined=Fo,e.kebabCase=ru,e.last=Zr,e.lastIndexOf=Jr,e.max=Gs,e.min=qs,e.noConflict=Ra,e.noop=Aa,e.now=Xs,e.pad=pa,e.padLeft=fa,e.padRight=da,e.parseInt=ma,e.random=aa,e.reduce=Wi,e.reduceRight=Bi,e.repeat=va,e.result=na,e.runInContext=D,e.size=Vi,e.snakeCase=iu,e.some=Hi,e.sortedIndex=oi,e.sortedLastIndex=ai,e.startCase=ou,e.startsWith=ga,e.template=ya,e.trim=_a,e.trimLeft=ba,e.trimRight=wa,e.trunc=Ca,e.unescape=xa,e.uniqueId=Ua,e.words=Ea,e.all=Pi,e.any=Hi,e.contains=Ti,e.detect=Oi,e.foldl=Wi,e.foldr=Bi,e.head=Gr,e.include=Ti,e.inject=Wi,Oa(e,function(){var t={};return Pn(e,function(n,r){e.prototype[r]||(t[r]=n)}),t}(),!1),e.sample=ji,e.prototype.sample=function(t){return this.__chain__||null!=t?this.thru(function(e){return ji(e,t)}):ji(this.value())},e.VERSION=M,Qe(["bind","bindKey","curry","curryRight","partial","partialRight"],function(t){e[t].placeholder=e}),Qe(["filter","map","takeWhile"],function(t,e){var n=e==Y,i=e==z;r.prototype[t]=function(t,r){var o=this.clone(),a=o.__filtered__,s=o.__iteratees__||(o.__iteratees__=[]);return o.__filtered__=a||n||i&&o.__dir__<0,s.push({iteratee:vr(t,r,3),type:e}),o}}),Qe(["drop","take"],function(t,e){var n="__"+t+"Count__",i=t+"While";r.prototype[t]=function(r){r=null==r?1:xs(ls(r)||0,0);var i=this.clone();if(i.__filtered__){var o=i[n];i[n]=e?Es(o,r):o+r}else{var a=i.__views__||(i.__views__=[]);a.push({size:r,type:t+(i.__dir__<0?"Right":"")})}return i},r.prototype[t+"Right"]=function(e){return this.reverse()[t](e).reverse()},r.prototype[t+"RightWhile"]=function(t,e){return this.reverse()[i](t,e).reverse()}}),Qe(["first","last"],function(t,e){var n="take"+(e?"Right":"");r.prototype[t]=function(){return this[n](1).value()[0]}}),Qe(["initial","rest"],function(t,e){var n="drop"+(e?"":"Right");r.prototype[t]=function(){return this[n](1)}}),Qe(["pluck","where"],function(t,e){var n=e?"filter":"map",i=e?Fn:Yn;r.prototype[t]=function(t){return this[n](i(t))}}),r.prototype.compact=function(){return this.filter(Ta)},r.prototype.dropWhile=function(t,e){var n;return t=vr(t,e,3),this.filter(function(e,r,i){return n||(n=!t(e,r,i))})},r.prototype.reject=function(t,e){return t=vr(t,e,3),this.filter(function(e,n,r){return!t(e,n,r)})},r.prototype.slice=function(t,e){t=null==t?0:+t||0;var n=0>t?this.takeRight(-t):this.drop(t);return"undefined"!=typeof e&&(e=+e||0,n=0>e?n.dropRight(-e):n.take(e-t)),n},r.prototype.toArray=function(){return this.drop(0)},Pn(r.prototype,function(t,i){var o=e[i],a=/^(?:first|last)$/.test(i);e.prototype[i]=function(){var i=this.__wrapped__,s=arguments,u=this.__chain__,l=!!this.__actions__.length,c=i instanceof r,h=c&&!l;if(a&&!u)return h?t.call(i):o.call(e,this.value());var p=function(t){var n=[t];return hs.apply(n,s),o.apply(e,n)};if(c||$s(i)){var f=h?i:new r(this),d=t.apply(f,s);if(!a&&(l||d.__actions__)){var m=d.__actions__||(d.__actions__=[]);m.push({func:bi,args:[p],thisArg:e})}return new n(d,u)}return this.thru(p)}}),Qe(["concat","join","pop","push","shift","sort","splice","unshift"],function(t){var n=Xa[t],r=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",i=/^(?:join|pop|shift)$/.test(t);e.prototype[t]=function(){var t=arguments;return i&&!this.__chain__?n.apply(this.value(),t):this[r](function(e){return n.apply(e,t)})}}),r.prototype.clone=i,r.prototype.reverse=_,r.prototype.value=Z,e.prototype.chain=wi,e.prototype.commit=Ci,e.prototype.plant=xi,e.prototype.reverse=Ei,e.prototype.toString=Di,e.prototype.run=e.prototype.toJSON=e.prototype.valueOf=e.prototype.value=Si,e.prototype.collect=e.prototype.map,e.prototype.head=e.prototype.first,e.prototype.select=e.prototype.filter,e.prototype.tail=e.prototype.rest,e}var S,M="3.2.0",T=1,P=2,k=4,O=8,R=16,A=32,L=64,N=128,I=256,F=30,U="...",W=150,B=16,Y=0,j=1,z=2,V="Expected a function",H="__lodash_placeholder__",G="[object Arguments]",q="[object Array]",K="[object Boolean]",X="[object Date]",$="[object Error]",Q="[object Function]",Z="[object Map]",J="[object Number]",te="[object Object]",ee="[object RegExp]",ne="[object Set]",re="[object String]",ie="[object WeakMap]",oe="[object ArrayBuffer]",ae="[object Float32Array]",se="[object Float64Array]",ue="[object Int8Array]",le="[object Int16Array]",ce="[object Int32Array]",he="[object Uint8Array]",pe="[object Uint8ClampedArray]",fe="[object Uint16Array]",de="[object Uint32Array]",me=/\b__p \+= '';/g,ve=/\b(__p \+=) '' \+/g,ge=/(__e\(.*?\)|\b__t\)) \+\n'';/g,ye=/&(?:amp|lt|gt|quot|#39|#96);/g,_e=/[&<>"'`]/g,be=RegExp(ye.source),we=RegExp(_e.source),Ce=/<%-([\s\S]+?)%>/g,xe=/<%([\s\S]+?)%>/g,Ee=/<%=([\s\S]+?)%>/g,De=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Se=/\w*$/,Me=/^\s*function[ \n\r\t]+\w/,Te=/^0[xX]/,Pe=/^\[object .+?Constructor\]$/,ke=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,Oe=/($^)/,Re=/[.*+?^${}()|[\]\/\\]/g,Ae=RegExp(Re.source),Le=/\bthis\b/,Ne=/['\n\r\u2028\u2029\\]/g,Ie=function(){var t="[A-Z\\xc0-\\xd6\\xd8-\\xde]",e="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(t+"{2,}(?="+t+e+")|"+t+"?"+e+"|"+t+"+|[0-9]+","g")}(),Fe=" \f \n\r\u2028\u2029 ᠎              ",Ue=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","document","isFinite","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","window","WinRTError"],We=-1,Be={};Be[ae]=Be[se]=Be[ue]=Be[le]=Be[ce]=Be[he]=Be[pe]=Be[fe]=Be[de]=!0,Be[G]=Be[q]=Be[oe]=Be[K]=Be[X]=Be[$]=Be[Q]=Be[Z]=Be[J]=Be[te]=Be[ee]=Be[ne]=Be[re]=Be[ie]=!1;var Ye={};Ye[G]=Ye[q]=Ye[oe]=Ye[K]=Ye[X]=Ye[ae]=Ye[se]=Ye[ue]=Ye[le]=Ye[ce]=Ye[J]=Ye[te]=Ye[ee]=Ye[re]=Ye[he]=Ye[pe]=Ye[fe]=Ye[de]=!0,Ye[$]=Ye[Q]=Ye[Z]=Ye[ne]=Ye[ie]=!1;var je={leading:!1,maxWait:0,trailing:!1},ze={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"},Ve={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},He={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},Ge={"function":!0,object:!0},qe={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Ke=Ge[typeof window]&&window!==(this&&this.window)?window:this,Xe=Ge[typeof e]&&e&&!e.nodeType&&e,$e=Ge[typeof t]&&t&&!t.nodeType&&t,Qe=Xe&&$e&&"object"==typeof i&&i;!Qe||Qe.global!==Qe&&Qe.window!==Qe&&Qe.self!==Qe||(Ke=Qe);var Ze=($e&&$e.exports===Xe&&Xe,D());Ke._=Ze,r=function(){return Ze}.call(e,n,e,t),!(r!==S&&(t.exports=r))}).call(this)}).call(e,n(/*! (webpack)/buildin/module.js */57)(t),function(){return this}())},/*!**********************************!*\ + !*** ./~/react-chartjs/index.js ***! + \**********************************/ +function(t,e,n){t.exports={Bar:n(/*! ./lib/bar */119),Doughnut:n(/*! ./lib/doughnut */120),Line:n(/*! ./lib/line */121),Pie:n(/*! ./lib/pie */122),PolarArea:n(/*! ./lib/polar-area */123),Radar:n(/*! ./lib/radar */124)}},/*!************************************!*\ + !*** ./~/react-chartjs/lib/bar.js ***! + \************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("Bar",["getBarsAtEvent"])},/*!*****************************************!*\ + !*** ./~/react-chartjs/lib/doughnut.js ***! + \*****************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("Doughnut",["getSegmentsAtEvent"])},/*!*************************************!*\ + !*** ./~/react-chartjs/lib/line.js ***! + \*************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("Line",["getPointsAtEvent"])},/*!************************************!*\ + !*** ./~/react-chartjs/lib/pie.js ***! + \************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("Pie",["getSegmentsAtEvent"])},/*!*******************************************!*\ + !*** ./~/react-chartjs/lib/polar-area.js ***! + \*******************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("PolarArea",["getSegmentsAtEvent"])},/*!**************************************!*\ + !*** ./~/react-chartjs/lib/radar.js ***! + \**************************************/ +function(t,e,n){var r=n(/*! ./core */20);t.exports=r.createClass("Radar",["getPointsAtEvent"])},/*!***********************************************!*\ + !*** ./~/react/lib/BeforeInputEventPlugin.js ***! + \***********************************************/ +function(t,e,n){"use strict";function r(){var t=window.opera;return"object"==typeof t&&"function"==typeof t.version&&parseInt(t.version(),10)<=12}function i(t){return(t.ctrlKey||t.altKey||t.metaKey)&&!(t.ctrlKey&&t.altKey)}var o=n(/*! ./EventConstants */6),a=n(/*! ./EventPropagators */23),s=n(/*! ./ExecutionEnvironment */5),u=n(/*! ./SyntheticInputEvent */173),l=n(/*! ./keyOf */7),c=s.canUseDOM&&"TextEvent"in window&&!("documentMode"in document||r()),h=32,p=String.fromCharCode(h),f=o.topLevelTypes,d={beforeInput:{phasedRegistrationNames:{bubbled:l({onBeforeInput:null}),captured:l({onBeforeInputCapture:null})},dependencies:[f.topCompositionEnd,f.topKeyPress,f.topTextInput,f.topPaste]}},m=null,v=!1,g={eventTypes:d,extractEvents:function(t,e,n,r){var o;if(c)switch(t){case f.topKeyPress:var s=r.which;if(s!==h)return;v=!0,o=p;break;case f.topTextInput:if(o=r.data,o===p&&v)return;break;default:return}else{switch(t){case f.topPaste:m=null;break;case f.topKeyPress:r.which&&!i(r)&&(m=String.fromCharCode(r.which));break;case f.topCompositionEnd:m=r.data}if(null===m)return;o=m}if(o){var l=u.getPooled(d.beforeInput,n,r);return l.data=o,m=null,a.accumulateTwoPhaseDispatches(l),l}}};t.exports=g},/*!********************************!*\ + !*** ./~/react/lib/CSSCore.js ***! + \********************************/ +function(t,e,n){var r=n(/*! ./invariant */1),i={addClass:function(t,e){return r(!/\s/.test(e)),e&&(t.classList?t.classList.add(e):i.hasClass(t,e)||(t.className=t.className+" "+e)),t},removeClass:function(t,e){return r(!/\s/.test(e)),e&&(t.classList?t.classList.remove(e):i.hasClass(t,e)&&(t.className=t.className.replace(new RegExp("(^|\\s)"+e+"(?:\\s|$)","g"),"$1").replace(/\s+/g," ").replace(/^\s*|\s*$/g,""))),t},conditionClass:function(t,e,n){return(n?i.addClass:i.removeClass)(t,e)},hasClass:function(t,e){return r(!/\s/.test(e)),t.classList?!!e&&t.classList.contains(e):(" "+t.className+" ").indexOf(" "+e+" ")>-1}};t.exports=i},/*!******************************************!*\ + !*** ./~/react/lib/ChangeEventPlugin.js ***! + \******************************************/ +function(t,e,n){"use strict";function r(t){return"SELECT"===t.nodeName||"INPUT"===t.nodeName&&"file"===t.type}function i(t){var e=x.getPooled(T.change,k,t);b.accumulateTwoPhaseDispatches(e),C.batchedUpdates(o,e)}function o(t){_.enqueueEvents(t),_.processEventQueue()}function a(t,e){P=t,k=e,P.attachEvent("onchange",i)}function s(){P&&(P.detachEvent("onchange",i),P=null,k=null)}function u(t,e,n){return t===M.topChange?n:void 0}function l(t,e,n){t===M.topFocus?(s(),a(e,n)):t===M.topBlur&&s()}function c(t,e){P=t,k=e,O=t.value,R=Object.getOwnPropertyDescriptor(t.constructor.prototype,"value"),Object.defineProperty(P,"value",N),P.attachEvent("onpropertychange",p)}function h(){P&&(delete P.value,P.detachEvent("onpropertychange",p),P=null,k=null,O=null,R=null)}function p(t){if("value"===t.propertyName){var e=t.srcElement.value;e!==O&&(O=e,i(t))}}function f(t,e,n){return t===M.topInput?n:void 0}function d(t,e,n){t===M.topFocus?(h(),c(e,n)):t===M.topBlur&&h()}function m(t){return t!==M.topSelectionChange&&t!==M.topKeyUp&&t!==M.topKeyDown||!P||P.value===O?void 0:(O=P.value,k)}function v(t){return"INPUT"===t.nodeName&&("checkbox"===t.type||"radio"===t.type)}function g(t,e,n){return t===M.topClick?n:void 0}var y=n(/*! ./EventConstants */6),_=n(/*! ./EventPluginHub */29),b=n(/*! ./EventPropagators */23),w=n(/*! ./ExecutionEnvironment */5),C=n(/*! ./ReactUpdates */9),x=n(/*! ./SyntheticEvent */19),E=n(/*! ./isEventSupported */55),D=n(/*! ./isTextInputElement */91),S=n(/*! ./keyOf */7),M=y.topLevelTypes,T={change:{phasedRegistrationNames:{bubbled:S({onChange:null}),captured:S({onChangeCapture:null})},dependencies:[M.topBlur,M.topChange,M.topClick,M.topFocus,M.topInput,M.topKeyDown,M.topKeyUp,M.topSelectionChange]}},P=null,k=null,O=null,R=null,A=!1;w.canUseDOM&&(A=E("change")&&(!("documentMode"in document)||document.documentMode>8));var L=!1;w.canUseDOM&&(L=E("input")&&(!("documentMode"in document)||document.documentMode>9));var N={get:function(){return R.get.call(this)},set:function(t){O=""+t,R.set.call(this,t)}},I={eventTypes:T,extractEvents:function(t,e,n,i){var o,a;if(r(e)?A?o=u:a=l:D(e)?L?o=f:(o=m,a=d):v(e)&&(o=g),o){var s=o(t,e,n);if(s){var c=x.getPooled(T.change,s,i);return b.accumulateTwoPhaseDispatches(c),c}}a&&a(t,e,n)}};t.exports=I},/*!*********************************************!*\ + !*** ./~/react/lib/ClientReactRootIndex.js ***! + \*********************************************/ +function(t){"use strict";var e=0,n={createReactRootIndex:function(){return e++}};t.exports=n},/*!***********************************************!*\ + !*** ./~/react/lib/CompositionEventPlugin.js ***! + \***********************************************/ +function(t,e,n){"use strict";function r(t){switch(t){case y.topCompositionStart:return b.compositionStart;case y.topCompositionEnd:return b.compositionEnd;case y.topCompositionUpdate:return b.compositionUpdate}}function i(t,e){return t===y.topKeyDown&&e.keyCode===m}function o(t,e){switch(t){case y.topKeyUp:return-1!==d.indexOf(e.keyCode);case y.topKeyDown:return e.keyCode!==m;case y.topKeyPress:case y.topMouseDown:case y.topBlur:return!0;default:return!1}}function a(t){this.root=t,this.startSelection=c.getSelection(t),this.startValue=this.getText()}var s=n(/*! ./EventConstants */6),u=n(/*! ./EventPropagators */23),l=n(/*! ./ExecutionEnvironment */5),c=n(/*! ./ReactInputSelection */46),h=n(/*! ./SyntheticCompositionEvent */170),p=n(/*! ./getTextContentAccessor */54),f=n(/*! ./keyOf */7),d=[9,13,27,32],m=229,v=l.canUseDOM&&"CompositionEvent"in window,g=!v||"documentMode"in document&&document.documentMode>8&&document.documentMode<=11,y=s.topLevelTypes,_=null,b={compositionEnd:{phasedRegistrationNames:{bubbled:f({onCompositionEnd:null}),captured:f({onCompositionEndCapture:null})},dependencies:[y.topBlur,y.topCompositionEnd,y.topKeyDown,y.topKeyPress,y.topKeyUp,y.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:f({onCompositionStart:null}),captured:f({onCompositionStartCapture:null})},dependencies:[y.topBlur,y.topCompositionStart,y.topKeyDown,y.topKeyPress,y.topKeyUp,y.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:f({onCompositionUpdate:null}),captured:f({onCompositionUpdateCapture:null})},dependencies:[y.topBlur,y.topCompositionUpdate,y.topKeyDown,y.topKeyPress,y.topKeyUp,y.topMouseDown]}};a.prototype.getText=function(){return this.root.value||this.root[p()]},a.prototype.getData=function(){var t=this.getText(),e=this.startSelection.start,n=this.startValue.length-this.startSelection.end;return t.substr(e,t.length-n-e)};var w={eventTypes:b,extractEvents:function(t,e,n,s){var l,c;if(v?l=r(t):_?o(t,s)&&(l=b.compositionEnd):i(t,s)&&(l=b.compositionStart),g&&(_||l!==b.compositionStart?l===b.compositionEnd&&_&&(c=_.getData(),_=null):_=new a(e)),l){var p=h.getPooled(l,n,s);return c&&(p.data=c),u.accumulateTwoPhaseDispatches(p),p}}};t.exports=w},/*!**********************************************!*\ + !*** ./~/react/lib/DOMChildrenOperations.js ***! + \**********************************************/ +function(t,e,n){"use strict";function r(t,e,n){t.insertBefore(e,t.childNodes[n]||null)}var i,o=n(/*! ./Danger */131),a=n(/*! ./ReactMultiChildUpdateTypes */72),s=n(/*! ./getTextContentAccessor */54),u=n(/*! ./invariant */1),l=s();i="textContent"===l?function(t,e){t.textContent=e}:function(t,e){for(;t.firstChild;)t.removeChild(t.firstChild);if(e){var n=t.ownerDocument||document;t.appendChild(n.createTextNode(e))}};var c={dangerouslyReplaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup,updateTextContent:i,processUpdates:function(t,e){for(var n,s=null,l=null,c=0;n=t[c];c++)if(n.type===a.MOVE_EXISTING||n.type===a.REMOVE_NODE){var h=n.fromIndex,p=n.parentNode.childNodes[h],f=n.parentID;u(p),s=s||{},s[f]=s[f]||[],s[f][h]=p,l=l||[],l.push(p)}var d=o.dangerouslyRenderMarkup(e);if(l)for(var m=0;m]+)/,c="data-danger-index",h={dangerouslyRenderMarkup:function(t){u(i.canUseDOM);for(var e,n={},h=0;hl;l++){var f=u[l];if(f!==o&&f.form===o.form){var m=h.getID(f);d(m);var g=v[m];d(g),p.asap(r,g)}}}return e}});t.exports=g},/*!***************************************!*\ + !*** ./~/react/lib/ReactDOMOption.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./ReactBrowserComponentMixin */12),i=n(/*! ./ReactCompositeComponent */8),o=n(/*! ./ReactElement */3),a=n(/*! ./ReactDOM */15),s=(n(/*! ./warning */4),o.createFactory(a.option.type)),u=i.createClass({displayName:"ReactDOMOption",mixins:[r],componentWillMount:function(){},render:function(){return s(this.props,this.props.children)}});t.exports=u},/*!***************************************!*\ + !*** ./~/react/lib/ReactDOMSelect.js ***! + \***************************************/ +function(t,e,n){"use strict";function r(){this.isMounted()&&(this.setState({value:this._pendingValue}),this._pendingValue=0)}function i(t,e){if(null!=t[e])if(t.multiple){if(!Array.isArray(t[e]))return new Error("The `"+e+"` prop supplied to must be a scalar value if `multiple` is false.")}function o(t,e){var n,r,i,o=t.props.multiple,a=null!=e?e:t.state.value,s=t.getDOMNode().options;if(o)for(n={},r=0,i=a.length;i>r;++r)n[""+a[r]]=!0;else n=""+a;for(r=0,i=s.length;i>r;r++){var u=o?n.hasOwnProperty(s[r].value):s[r].value===n;u!==s[r].selected&&(s[r].selected=u)}}var a=n(/*! ./AutoFocusMixin */35),s=n(/*! ./LinkedValueUtils */43),u=n(/*! ./ReactBrowserComponentMixin */12),l=n(/*! ./ReactCompositeComponent */8),c=n(/*! ./ReactElement */3),h=n(/*! ./ReactDOM */15),p=n(/*! ./ReactUpdates */9),f=n(/*! ./Object.assign */2),d=c.createFactory(h.select.type),m=l.createClass({displayName:"ReactDOMSelect",mixins:[a,s.Mixin,u],propTypes:{defaultValue:i,value:i},getInitialState:function(){return{value:this.props.defaultValue||(this.props.multiple?[]:"")}},componentWillMount:function(){this._pendingValue=null},componentWillReceiveProps:function(t){!this.props.multiple&&t.multiple?this.setState({value:[this.state.value]}):this.props.multiple&&!t.multiple&&this.setState({value:this.state.value[0]})},render:function(){var t=f({},this.props);return t.onChange=this._handleChange,t.value=null,d(t,this.props.children)},componentDidMount:function(){o(this,s.getValue(this))},componentDidUpdate:function(t){var e=s.getValue(this),n=!!t.multiple,r=!!this.props.multiple;(null!=e||n!==r)&&o(this,e)},_handleChange:function(t){var e,n=s.getOnChange(this);n&&(e=n.call(this,t));var i;if(this.props.multiple){i=[];for(var o=t.target.options,a=0,u=o.length;u>a;a++)o[a].selected&&i.push(o[a].value)}else i=t.target.value;return this._pendingValue=i,p.asap(r,this),e}});t.exports=m},/*!******************************************!*\ + !*** ./~/react/lib/ReactDOMSelection.js ***! + \******************************************/ +function(t,e,n){"use strict";function r(t,e,n,r){return t===n&&e===r}function i(t){var e=document.selection,n=e.createRange(),r=n.text.length,i=n.duplicate();i.moveToElementText(t),i.setEndPoint("EndToStart",n);var o=i.text.length,a=o+r;return{start:o,end:a}}function o(t){var e=window.getSelection&&window.getSelection();if(!e||0===e.rangeCount)return null;var n=e.anchorNode,i=e.anchorOffset,o=e.focusNode,a=e.focusOffset,s=e.getRangeAt(0),u=r(e.anchorNode,e.anchorOffset,e.focusNode,e.focusOffset),l=u?0:s.toString().length,c=s.cloneRange();c.selectNodeContents(t),c.setEnd(s.startContainer,s.startOffset);var h=r(c.startContainer,c.startOffset,c.endContainer,c.endOffset),p=h?0:c.toString().length,f=p+l,d=document.createRange();d.setStart(n,i),d.setEnd(o,a);var m=d.collapsed;return{start:m?f:p,end:m?p:f}}function a(t,e){var n,r,i=document.selection.createRange().duplicate();"undefined"==typeof e.end?(n=e.start,r=n):e.start>e.end?(n=e.end,r=e.start):(n=e.start,r=e.end),i.moveToElementText(t),i.moveStart("character",n),i.setEndPoint("EndToStart",i),i.moveEnd("character",r-n),i.select()}function s(t,e){if(window.getSelection){var n=window.getSelection(),r=t[c()].length,i=Math.min(e.start,r),o="undefined"==typeof e.end?i:Math.min(e.end,r);if(!n.extend&&i>o){var a=o;o=i,i=a}var s=l(t,i),u=l(t,o);if(s&&u){var h=document.createRange();h.setStart(s.node,s.offset),n.removeAllRanges(),i>o?(n.addRange(h),n.extend(u.node,u.offset)):(h.setEnd(u.node,u.offset),n.addRange(h))}}}var u=n(/*! ./ExecutionEnvironment */5),l=n(/*! ./getNodeForCharacterOffset */188),c=n(/*! ./getTextContentAccessor */54),h=u.canUseDOM&&document.selection,p={getOffsets:h?i:o,setOffsets:h?a:s};t.exports=p},/*!*****************************************!*\ + !*** ./~/react/lib/ReactDOMTextarea.js ***! + \*****************************************/ +function(t,e,n){"use strict";function r(){this.isMounted()&&this.forceUpdate()}var i=n(/*! ./AutoFocusMixin */35),o=n(/*! ./DOMPropertyOperations */22),a=n(/*! ./LinkedValueUtils */43),s=n(/*! ./ReactBrowserComponentMixin */12),u=n(/*! ./ReactCompositeComponent */8),l=n(/*! ./ReactElement */3),c=n(/*! ./ReactDOM */15),h=n(/*! ./ReactUpdates */9),p=n(/*! ./Object.assign */2),f=n(/*! ./invariant */1),d=(n(/*! ./warning */4),l.createFactory(c.textarea.type)),m=u.createClass({displayName:"ReactDOMTextarea",mixins:[i,a.Mixin,s],getInitialState:function(){var t=this.props.defaultValue,e=this.props.children;null!=e&&(f(null==t),Array.isArray(e)&&(f(e.length<=1),e=e[0]),t=""+e),null==t&&(t="");var n=a.getValue(this);return{initialValue:""+(null!=n?n:t)}},render:function(){var t=p({},this.props);return f(null==t.dangerouslySetInnerHTML),t.defaultValue=null,t.value=null,t.onChange=this._handleChange,d(t,this.state.initialValue)},componentDidUpdate:function(){var t=a.getValue(this);if(null!=t){var e=this.getDOMNode();o.setValueForProperty(e,"value",""+t)}},_handleChange:function(t){var e,n=a.getOnChange(this);return n&&(e=n.call(this,t)),h.asap(r,this),e}});t.exports=m},/*!*****************************************************!*\ + !*** ./~/react/lib/ReactDefaultBatchingStrategy.js ***! + \*****************************************************/ +function(t,e,n){"use strict";function r(){this.reinitializeTransaction()}var i=n(/*! ./ReactUpdates */9),o=n(/*! ./Transaction */38),a=n(/*! ./Object.assign */2),s=n(/*! ./emptyFunction */10),u={initialize:s,close:function(){p.isBatchingUpdates=!1}},l={initialize:s,close:i.flushBatchedUpdates.bind(i)},c=[l,u];a(r.prototype,o.Mixin,{getTransactionWrappers:function(){return c}});var h=new r,p={isBatchingUpdates:!1,batchedUpdates:function(t,e,n){var r=p.isBatchingUpdates;p.isBatchingUpdates=!0,r?t(e,n):h.perform(t,null,e,n)}};t.exports=p},/*!**********************************************!*\ + !*** ./~/react/lib/ReactDefaultInjection.js ***! + \**********************************************/ +function(t,e,n){"use strict";function r(){D.EventEmitter.injectReactEventListener(E),D.EventPluginHub.injectEventPluginOrder(u),D.EventPluginHub.injectInstanceHandle(S),D.EventPluginHub.injectMount(M),D.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:k,EnterLeaveEventPlugin:l,ChangeEventPlugin:o,CompositionEventPlugin:s,MobileSafariClickEventPlugin:p,SelectEventPlugin:T,BeforeInputEventPlugin:i}),D.NativeComponent.injectGenericComponentClass(v),D.NativeComponent.injectComponentClasses({button:g,form:y,img:_,input:b,option:w,select:C,textarea:x,html:R("html"),head:R("head"),body:R("body")}),D.CompositeComponent.injectMixin(f),D.DOMProperty.injectDOMPropertyConfig(h),D.DOMProperty.injectDOMPropertyConfig(O),D.EmptyComponent.injectEmptyComponent("noscript"),D.Updates.injectReconcileTransaction(d.ReactReconcileTransaction),D.Updates.injectBatchingStrategy(m),D.RootIndex.injectCreateReactRootIndex(c.canUseDOM?a.createReactRootIndex:P.createReactRootIndex),D.Component.injectEnvironment(d)}var i=n(/*! ./BeforeInputEventPlugin */125),o=n(/*! ./ChangeEventPlugin */127),a=n(/*! ./ClientReactRootIndex */128),s=n(/*! ./CompositionEventPlugin */129),u=n(/*! ./DefaultEventPluginOrder */132),l=n(/*! ./EnterLeaveEventPlugin */133),c=n(/*! ./ExecutionEnvironment */5),h=n(/*! ./HTMLDOMPropertyConfig */135),p=n(/*! ./MobileSafariClickEventPlugin */137),f=n(/*! ./ReactBrowserComponentMixin */12),d=n(/*! ./ReactComponentBrowserEnvironment */140),m=n(/*! ./ReactDefaultBatchingStrategy */151),v=n(/*! ./ReactDOMComponent */69),g=n(/*! ./ReactDOMButton */142),y=n(/*! ./ReactDOMForm */143),_=n(/*! ./ReactDOMImg */145),b=n(/*! ./ReactDOMInput */146),w=n(/*! ./ReactDOMOption */147),C=n(/*! ./ReactDOMSelect */148),x=n(/*! ./ReactDOMTextarea */150),E=n(/*! ./ReactEventListener */155),D=n(/*! ./ReactInjection */156),S=n(/*! ./ReactInstanceHandles */27),M=n(/*! ./ReactMount */13),T=n(/*! ./SelectEventPlugin */166),P=n(/*! ./ServerReactRootIndex */167),k=n(/*! ./SimpleEventPlugin */168),O=n(/*! ./SVGDOMPropertyConfig */165),R=n(/*! ./createFullPageComponent */181);t.exports={inject:r}},/*!****************************************!*\ + !*** ./~/react/lib/ReactErrorUtils.js ***! + \****************************************/ +function(t){"use strict";var e={guard:function(t){return t}};t.exports=e},/*!***********************************************!*\ + !*** ./~/react/lib/ReactEventEmitterMixin.js ***! + \***********************************************/ +function(t,e,n){"use strict";function r(t){i.enqueueEvents(t),i.processEventQueue()}var i=n(/*! ./EventPluginHub */29),o={handleTopLevel:function(t,e,n,o){var a=i.extractEvents(t,e,n,o);r(a)}};t.exports=o},/*!*******************************************!*\ + !*** ./~/react/lib/ReactEventListener.js ***! + \*******************************************/ +function(t,e,n){"use strict";function r(t){var e=h.getID(t),n=c.getReactRootIDFromNodeID(e),r=h.findReactContainerForID(n),i=h.getFirstReactDOM(r);return i}function i(t,e){this.topLevelType=t,this.nativeEvent=e,this.ancestors=[]}function o(t){for(var e=h.getFirstReactDOM(d(t.nativeEvent))||window,n=e;n;)t.ancestors.push(n),n=r(n);for(var i=0,o=t.ancestors.length;o>i;i++){e=t.ancestors[i];var a=h.getID(e)||"";v._handleTopLevel(t.topLevelType,e,a,t.nativeEvent)}}function a(t){var e=m(window);t(e)}var s=n(/*! ./EventListener */134),u=n(/*! ./ExecutionEnvironment */5),l=n(/*! ./PooledClass */14),c=n(/*! ./ReactInstanceHandles */27),h=n(/*! ./ReactMount */13),p=n(/*! ./ReactUpdates */9),f=n(/*! ./Object.assign */2),d=n(/*! ./getEventTarget */53),m=n(/*! ./getUnboundedScrollPosition */90);f(i.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),l.addPoolingTo(i,l.twoArgumentPooler);var v={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:u.canUseDOM?window:null,setHandleTopLevel:function(t){v._handleTopLevel=t},setEnabled:function(t){v._enabled=!!t},isEnabled:function(){return v._enabled},trapBubbledEvent:function(t,e,n){var r=n;return r?s.listen(r,e,v.dispatchEvent.bind(null,t)):void 0},trapCapturedEvent:function(t,e,n){var r=n;return r?s.capture(r,e,v.dispatchEvent.bind(null,t)):void 0},monitorScrollValue:function(t){var e=a.bind(null,t);s.listen(window,"scroll",e),s.listen(window,"resize",e)},dispatchEvent:function(t,e){if(v._enabled){var n=i.getPooled(t,e);try{p.batchedUpdates(o,n)}finally{i.release(n)}}}};t.exports=v},/*!***************************************!*\ + !*** ./~/react/lib/ReactInjection.js ***! + \***************************************/ +function(t,e,n){"use strict";var r=n(/*! ./DOMProperty */21),i=n(/*! ./EventPluginHub */29),o=n(/*! ./ReactComponent */26),a=n(/*! ./ReactCompositeComponent */8),s=n(/*! ./ReactEmptyComponent */36),u=n(/*! ./ReactBrowserEventEmitter */25),l=n(/*! ./ReactNativeComponent */73),c=n(/*! ./ReactPerf */16),h=n(/*! ./ReactRootIndex */80),p=n(/*! ./ReactUpdates */9),f={Component:o.injection,CompositeComponent:a.injection,DOMProperty:r.injection,EmptyComponent:s.injection,EventPluginHub:i.injection,EventEmitter:u.injection,NativeComponent:l.injection,Perf:c.injection,RootIndex:h.injection,Updates:p.injection};t.exports=f},/*!**********************************!*\ + !*** ./~/react/lib/ReactLink.js ***! + \**********************************/ +function(t,e,n){"use strict";function r(t,e){this.value=t,this.requestChange=e}function i(t){var e={value:"undefined"==typeof t?o.PropTypes.any.isRequired:t.isRequired,requestChange:o.PropTypes.func.isRequired};return o.PropTypes.shape(e)}var o=n(/*! ./React */24);r.PropTypes={link:i},t.exports=r},/*!**************************************************!*\ + !*** ./~/react/lib/ReactReconcileTransaction.js ***! + \**************************************************/ +function(t,e,n){"use strict";function r(){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=i.getPooled(null),this.putListenerQueue=u.getPooled()}var i=n(/*! ./CallbackQueue */41),o=n(/*! ./PooledClass */14),a=n(/*! ./ReactBrowserEventEmitter */25),s=n(/*! ./ReactInputSelection */46),u=n(/*! ./ReactPutListenerQueue */79),l=n(/*! ./Transaction */38),c=n(/*! ./Object.assign */2),h={initialize:s.getSelectionInformation,close:s.restoreSelection},p={initialize:function(){var t=a.isEnabled();return a.setEnabled(!1),t},close:function(t){a.setEnabled(t)}},f={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},d={initialize:function(){this.putListenerQueue.reset()},close:function(){this.putListenerQueue.putListeners()}},m=[d,h,p,f],v={getTransactionWrappers:function(){return m},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null,u.release(this.putListenerQueue),this.putListenerQueue=null}};c(r.prototype,l.Mixin,v),o.addPoolingTo(r),t.exports=r},/*!*********************************************!*\ + !*** ./~/react/lib/ReactServerRendering.js ***! + \*********************************************/ +function(t,e,n){"use strict";function r(t){c(o.isValidElement(t));var e;try{var n=a.createReactRootID();return e=u.getPooled(!1),e.perform(function(){var r=l(t,null),i=r.mountComponent(n,e,0);return s.addChecksumToMarkup(i)},null)}finally{u.release(e)}}function i(t){c(o.isValidElement(t));var e;try{var n=a.createReactRootID();return e=u.getPooled(!0),e.perform(function(){var r=l(t,null);return r.mountComponent(n,e,0)},null)}finally{u.release(e)}}var o=n(/*! ./ReactElement */3),a=n(/*! ./ReactInstanceHandles */27),s=n(/*! ./ReactMarkupChecksum */70),u=n(/*! ./ReactServerRenderingTransaction */160),l=n(/*! ./instantiateReactComponent */39),c=n(/*! ./invariant */1);t.exports={renderToString:r,renderToStaticMarkup:i}},/*!********************************************************!*\ + !*** ./~/react/lib/ReactServerRenderingTransaction.js ***! + \********************************************************/ +function(t,e,n){"use strict";function r(t){this.reinitializeTransaction(),this.renderToStaticMarkup=t,this.reactMountReady=o.getPooled(null),this.putListenerQueue=a.getPooled()}var i=n(/*! ./PooledClass */14),o=n(/*! ./CallbackQueue */41),a=n(/*! ./ReactPutListenerQueue */79),s=n(/*! ./Transaction */38),u=n(/*! ./Object.assign */2),l=n(/*! ./emptyFunction */10),c={initialize:function(){this.reactMountReady.reset()},close:l},h={initialize:function(){this.putListenerQueue.reset()},close:l},p=[h,c],f={getTransactionWrappers:function(){return p},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){o.release(this.reactMountReady),this.reactMountReady=null,a.release(this.putListenerQueue),this.putListenerQueue=null}};u(r.prototype,s.Mixin,f),i.addPoolingTo(r),t.exports=r},/*!******************************************!*\ + !*** ./~/react/lib/ReactStateSetters.js ***! + \******************************************/ +function(t){"use strict";function e(t,e){var n={};return function(r){n[e]=r,t.setState(n)}}var n={createStateSetter:function(t,e){return function(n,r,i,o,a,s){var u=e.call(t,n,r,i,o,a,s);u&&t.setState(u)}},createStateKeySetter:function(t,n){var r=t.__keySetters||(t.__keySetters={});return r[n]||(r[n]=e(t,n))}};n.Mixin={createStateSetter:function(t){return n.createStateSetter(this,t)},createStateKeySetter:function(t){return n.createStateKeySetter(this,t)}},t.exports=n},/*!****************************************************!*\ + !*** ./~/react/lib/ReactTransitionChildMapping.js ***! + \****************************************************/ +function(t,e,n){"use strict";var r=n(/*! ./ReactChildren */68),i={getChildMapping:function(t){return r.map(t,function(t){return t})},mergeChildMappings:function(t,e){function n(n){return e.hasOwnProperty(n)?e[n]:t[n]}t=t||{},e=e||{};var r={},i=[];for(var o in t)e.hasOwnProperty(o)?i.length&&(r[o]=i,i=[]):i.push(o);var a,s={};for(var u in e){if(r.hasOwnProperty(u))for(a=0;a=o&&a>=r)return{node:i,offset:r-o};o=a}i=e(n(i))}}t.exports=r},/*!**********************************!*\ + !*** ./~/react/lib/hyphenate.js ***! + \**********************************/ +function(t){function e(t){return t.replace(n,"-$1").toLowerCase()}var n=/([A-Z])/g;t.exports=e},/*!*******************************************!*\ + !*** ./~/react/lib/hyphenateStyleName.js ***! + \*******************************************/ +function(t,e,n){"use strict";function r(t){return i(t).replace(o,"-ms-")}var i=n(/*! ./hyphenate */189),o=/^ms-/;t.exports=r},/*!*******************************!*\ + !*** ./~/react/lib/isNode.js ***! + \*******************************/ +function(t){function e(t){return!(!t||!("function"==typeof Node?t instanceof Node:"object"==typeof t&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName))}t.exports=e},/*!***********************************!*\ + !*** ./~/react/lib/isTextNode.js ***! + \***********************************/ +function(t,e,n){function r(t){return i(t)&&3==t.nodeType}var i=n(/*! ./isNode */191);t.exports=r},/*!************************************!*\ + !*** ./~/react/lib/joinClasses.js ***! + \************************************/ +function(t){"use strict";function e(t){t||(t="");var e,n=arguments.length;if(n>1)for(var r=1;n>r;r++)e=arguments[r],e&&(t=(t?t+" ":"")+e);return t}t.exports=e},/*!********************************!*\ + !*** ./~/react/lib/toArray.js ***! + \********************************/ +function(t,e,n){function r(t){var e=t.length;if(i(!Array.isArray(t)&&("object"==typeof t||"function"==typeof t)),i("number"==typeof e),i(0===e||e-1 in t),t.hasOwnProperty)try{return Array.prototype.slice.call(t)}catch(n){}for(var r=Array(e),o=0;e>o;o++)r[o]=t[o];return r}var i=n(/*! ./invariant */1);t.exports=r},/*!*******************************!*\ + !*** ./~/react/lib/update.js ***! + \*******************************/ +function(t,e,n){"use strict";function r(t){return Array.isArray(t)?t.concat():t&&"object"==typeof t?a(new t.constructor,t):t}function i(t,e,n){u(Array.isArray(t));var r=e[n];u(Array.isArray(r))}function o(t,e){if(u("object"==typeof e),e.hasOwnProperty(p))return u(1===Object.keys(e).length),e[p];var n=r(t);if(e.hasOwnProperty(f)){var s=e[f];u(s&&"object"==typeof s),u(n&&"object"==typeof n),a(n,e[f])}e.hasOwnProperty(l)&&(i(t,e,l),e[l].forEach(function(t){n.push(t)})),e.hasOwnProperty(c)&&(i(t,e,c),e[c].forEach(function(t){n.unshift(t)})),e.hasOwnProperty(h)&&(u(Array.isArray(t)),u(Array.isArray(e[h])),e[h].forEach(function(t){u(Array.isArray(t)),n.splice.apply(n,t)})),e.hasOwnProperty(d)&&(u("function"==typeof e[d]),n=e[d](n));for(var m in e)v.hasOwnProperty(m)&&v[m]||(n[m]=o(t[m],e[m]));return n}var a=n(/*! ./Object.assign */2),s=n(/*! ./keyOf */7),u=n(/*! ./invariant */1),l=s({$push:null}),c=s({$unshift:null}),h=s({$splice:null}),p=s({$set:null}),f=s({$merge:null}),d=s({$apply:null}),m=[l,c,h,p,f,d],v={};m.forEach(function(t){v[t]=!0}),t.exports=o},/*!************************************!*\ + !*** ./~/superagent/lib/client.js ***! + \************************************/ +function(t,e,n){function r(){}function i(t){var e={}.toString.call(t);switch(e){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function o(){if(g.XMLHttpRequest&&("file:"!=g.location.protocol||!g.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(t){}return!1}function a(t){return t===Object(t)}function s(t){if(!a(t))return t;var e=[];for(var n in t)null!=t[n]&&e.push(encodeURIComponent(n)+"="+encodeURIComponent(t[n]));return e.join("&")}function u(t){for(var e,n,r={},i=t.split("&"),o=0,a=i.length;a>o;++o)n=i[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r}function l(t){var e,n,r,i,o=t.split(/\r?\n/),a={};o.pop();for(var s=0,u=o.length;u>s;++s)n=o[s],e=n.indexOf(":"),r=n.slice(0,e).toLowerCase(),i=y(n.slice(e+1)),a[r]=i;return a}function c(t){return t.split(/ *; */).shift()}function h(t){return v(t.split(/ *; */),function(t,e){var n=e.split(/ *= */),r=n.shift(),i=n.shift();return r&&i&&(t[r]=i),t},{})}function p(t,e){e=e||{},this.req=t,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method?this.xhr.responseText:null,this.setStatusProperties(this.xhr.status),this.header=this.headers=l(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text):null}function f(t,e){var n=this;m.call(this),this._query=this._query||[],this.method=t,this.url=e,this.header={},this._header={},this.on("end",function(){var t=null,e=null;try{e=new p(n)}catch(r){t=new Error("Parser is unable to parse the response"),t.parse=!0,t.original=r}n.callback(t,e)})}function d(t,e){return"function"==typeof e?new f("GET",t).end(e):1==arguments.length?new f("GET",t):new f(t,e)}var m=n(/*! emitter */197),v=n(/*! reduce */198),g="undefined"==typeof window?this:window,y="".trim?function(t){return t.trim()}:function(t){return t.replace(/(^\s*|\s*$)/g,"")};d.serializeObject=s,d.parseString=u,d.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},d.serialize={"application/x-www-form-urlencoded":s,"application/json":JSON.stringify},d.parse={"application/x-www-form-urlencoded":u,"application/json":JSON.parse},p.prototype.get=function(t){return this.header[t.toLowerCase()]},p.prototype.setHeaderProperties=function(){var t=this.header["content-type"]||"";this.type=c(t);var e=h(t);for(var n in e)this[n]=e[n]},p.prototype.parseBody=function(t){var e=d.parse[this.type];return e&&t&&t.length?e(t):null},p.prototype.setStatusProperties=function(t){var e=t/100|0;this.status=t,this.statusType=e,this.info=1==e,this.ok=2==e,this.clientError=4==e,this.serverError=5==e,this.error=4==e||5==e?this.toError():!1,this.accepted=202==t,this.noContent=204==t||1223==t,this.badRequest=400==t,this.unauthorized=401==t,this.notAcceptable=406==t,this.notFound=404==t,this.forbidden=403==t},p.prototype.toError=function(){var t=this.req,e=t.method,n=t.url,r="cannot "+e+" "+n+" ("+this.status+")",i=new Error(r);return i.status=this.status,i.method=e,i.url=n,i},d.Response=p,m(f.prototype),f.prototype.use=function(t){return t(this),this},f.prototype.timeout=function(t){return this._timeout=t,this},f.prototype.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},f.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},f.prototype.set=function(t,e){if(a(t)){for(var n in t)this.set(n,t[n]);return this}return this._header[t.toLowerCase()]=e,this.header[t]=e,this},f.prototype.unset=function(t){return delete this._header[t.toLowerCase()],delete this.header[t],this},f.prototype.getHeader=function(t){return this._header[t.toLowerCase()]},f.prototype.type=function(t){return this.set("Content-Type",d.types[t]||t),this},f.prototype.accept=function(t){return this.set("Accept",d.types[t]||t),this},f.prototype.auth=function(t,e){var n=btoa(t+":"+e);return this.set("Authorization","Basic "+n),this},f.prototype.query=function(t){return"string"!=typeof t&&(t=s(t)),t&&this._query.push(t),this},f.prototype.field=function(t,e){return this._formData||(this._formData=new FormData),this._formData.append(t,e),this},f.prototype.attach=function(t,e,n){return this._formData||(this._formData=new FormData),this._formData.append(t,e,n),this},f.prototype.send=function(t){var e=a(t),n=this.getHeader("Content-Type");if(e&&a(this._data))for(var r in t)this._data[r]=t[r];else"string"==typeof t?(n||this.type("form"),n=this.getHeader("Content-Type"),this._data="application/x-www-form-urlencoded"==n?this._data?this._data+"&"+t:t:(this._data||"")+t):this._data=t;return e?(n||this.type("json"),this):this},f.prototype.callback=function(t,e){var n=this._callback;return this.clearTimeout(),2==n.length?n(t,e):t?this.emit("error",t):void n(e)},f.prototype.crossDomainError=function(){var t=new Error("Origin is not allowed by Access-Control-Allow-Origin");t.crossDomain=!0,this.callback(t)},f.prototype.timeoutError=function(){var t=this._timeout,e=new Error("timeout of "+t+"ms exceeded");e.timeout=t,this.callback(e)},f.prototype.withCredentials=function(){return this._withCredentials=!0,this},f.prototype.end=function(t){var e=this,n=this.xhr=o(),a=this._query.join("&"),s=this._timeout,u=this._formData||this._data;if(this._callback=t||r,n.onreadystatechange=function(){return 4==n.readyState?0==n.status?e.aborted?e.timeoutError():e.crossDomainError():void e.emit("end"):void 0},n.upload&&(n.upload.onprogress=function(t){t.percent=t.loaded/t.total*100,e.emit("progress",t)}),s&&!this._timer&&(this._timer=setTimeout(function(){e.abort()},s)),a&&(a=d.serializeObject(a),this.url+=~this.url.indexOf("?")?"&"+a:"?"+a),n.open(this.method,this.url,!0),this._withCredentials&&(n.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof u&&!i(u)){var l=d.serialize[this.getHeader("Content-Type")];l&&(u=l(u))}for(var c in this.header)null!=this.header[c]&&n.setRequestHeader(c,this.header[c]);return this.emit("request",this),n.send(u),this},d.Request=f,d.get=function(t,e,n){var r=d("GET",t);return"function"==typeof e&&(n=e,e=null),e&&r.query(e),n&&r.end(n),r},d.head=function(t,e,n){var r=d("HEAD",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},d.del=function(t,e){var n=d("DELETE",t);return e&&n.end(e),n},d.patch=function(t,e,n){var r=d("PATCH",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},d.post=function(t,e,n){var r=d("POST",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},d.put=function(t,e,n){var r=d("PUT",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},t.exports=d},/*!***************************************************!*\ + !*** ./~/superagent/~/component-emitter/index.js ***! + \***************************************************/ +function(t){function e(t){return t?n(t):void 0}function n(t){for(var n in e.prototype)t[n]=e.prototype[n];return t}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},e.prototype.once=function(t,e){function n(){r.off(t,n),e.apply(this,arguments)}var r=this;return this._callbacks=this._callbacks||{},n.fn=e,this.on(t,n),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks[t];if(!n)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var r,i=0;ir;++r)n[r].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},/*!**************************************************!*\ + !*** ./~/superagent/~/reduce-component/index.js ***! + \**************************************************/ +function(t){t.exports=function(t,e,n){for(var r=0,i=t.length,o=3==arguments.length?n:t[r++];i>r;)o=e.call(null,o,t[r],++r,t);return o}},/*!***************************************!*\ + !*** (webpack)/buildin/amd-define.js ***! + \***************************************/ +function(t){t.exports=function(){throw new Error("define cannot be used indirect")}},/*!****************************************!*\ + !*** (webpack)/buildin/amd-options.js ***! + \****************************************/ +function(t,e){(function(e){t.exports=e}).call(e,{})},/*!**********************************************************!*\ + !*** (webpack)/~/node-libs-browser/~/process/browser.js ***! + \**********************************************************/ +function(t){function e(){}var n=t.exports={};n.nextTick=function(){var t="undefined"!=typeof window&&window.setImmediate,e="undefined"!=typeof window&&window.MutationObserver,n="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(t)return function(t){return window.setImmediate(t)};var r=[];if(e){var i=document.createElement("div"),o=new MutationObserver(function(){var t=r.slice();r.length=0,t.forEach(function(t){t()})});return o.observe(i,{attributes:!0}),function(t){r.length||i.setAttribute("yes","no"),r.push(t)}}return n?(window.addEventListener("message",function(t){var e=t.source;if((e===window||null===e)&&"process-tick"===t.data&&(t.stopPropagation(),r.length>0)){var n=r.shift();n()}},!0),function(t){r.push(t),window.postMessage("process-tick","*")}):function(t){setTimeout(t,0)}}(),n.title="browser",n.browser=!0,n.env={},n.argv=[],n.on=e,n.addListener=e,n.once=e,n.off=e,n.removeListener=e,n.removeAllListeners=e,n.emit=e,n.binding=function(){throw new Error("process.binding is not supported")},n.cwd=function(){return"/"},n.chdir=function(){throw new Error("process.chdir is not supported")}}]); diff --git a/zanata-war/src/main/webapp/resources/zanata/activity-entry.xhtml b/zanata-war/src/main/webapp/resources/zanata/activity-entry.xhtml index 9c4be9db4b..43cffec120 100644 --- a/zanata-war/src/main/webapp/resources/zanata/activity-entry.xhtml +++ b/zanata-war/src/main/webapp/resources/zanata/activity-entry.xhtml @@ -3,7 +3,8 @@ + xmlns:s="http://jboss.org/schema/seam/taglib" + xmlns:zanata="http://java.sun.com/jsf/composite/zanata"> - #{versionName} + + + diff --git a/zanata-war/src/main/webapp/resources/zanata/autocomplete.xhtml b/zanata-war/src/main/webapp/resources/zanata/autocomplete.xhtml index d594efdb04..01b431a8b6 100644 --- a/zanata-war/src/main/webapp/resources/zanata/autocomplete.xhtml +++ b/zanata-war/src/main/webapp/resources/zanata/autocomplete.xhtml @@ -34,6 +34,9 @@ + + @@ -55,6 +58,7 @@ + @@ -97,6 +101,18 @@ delay: 400 }); }); + + + + jQuery(function() { + jQuery('#' + '#{cc.attrs.id}-autocomplete__input').on('focus', function() { + $(this).attr('placeholder', '#{cc.attrs.activePlaceholder}'); + }); + jQuery('#' + '#{cc.attrs.id}-autocomplete__input').on('blur', function() { + $(this).attr('placeholder', '#{cc.attrs.placeholder}'); + }); + }); + @@ -122,7 +138,7 @@ class="js-autocomplete__input autocomplete__input js-tabs-nav-focus-input #{cc.attrs.inputClass}" onfocus="onInputFocus(this, #{cc.attrs.id}RenderResult)" id="#{cc.attrs.id}-autocomplete__input" - placeholder="#{cc.attrs.placeholder}"/> + placeholder="#{cc.attrs.placeholder}" /> diff --git a/zanata-war/src/main/webapp/resources/zanata/checkbox.xhtml b/zanata-war/src/main/webapp/resources/zanata/checkbox.xhtml index eaefd116ef..2e38ab6e6e 100644 --- a/zanata-war/src/main/webapp/resources/zanata/checkbox.xhtml +++ b/zanata-war/src/main/webapp/resources/zanata/checkbox.xhtml @@ -2,7 +2,8 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + xmlns:s="http://jboss.org/schema/seam/taglib" + xmlns:h="http://java.sun.com/jsf/html"> + + @@ -38,8 +43,9 @@ class="form__checkbox__input js-form__checkbox__input"/> + diff --git a/zanata-war/src/main/webapp/resources/zanata/progressBar.xhtml b/zanata-war/src/main/webapp/resources/zanata/progressBar.xhtml index b786753e5a..a8bb0ae9fc 100644 --- a/zanata-war/src/main/webapp/resources/zanata/progressBar.xhtml +++ b/zanata-war/src/main/webapp/resources/zanata/progressBar.xhtml @@ -22,15 +22,9 @@ - - - - - @@ -38,36 +32,34 @@ - - - - diff --git a/zanata-war/src/main/webapp/resources/zanata/version-copy-action-loader.xhtml b/zanata-war/src/main/webapp/resources/zanata/version-copy-action-loader.xhtml new file mode 100644 index 0000000000..49e915d652 --- /dev/null +++ b/zanata-war/src/main/webapp/resources/zanata/version-copy-action-loader.xhtml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + +
    + + + + #{msgs['jsf.Cancel']} + + +

    #{cc.attrs.header}

    +
    + +
    + + + + + + + + +
    +
    +
    +
    + diff --git a/zanata-war/src/main/webapp/resources/zanata/version-label.xhtml b/zanata-war/src/main/webapp/resources/zanata/version-label.xhtml new file mode 100644 index 0000000000..0c65f14cea --- /dev/null +++ b/zanata-war/src/main/webapp/resources/zanata/version-label.xhtml @@ -0,0 +1,37 @@ + + + + + + + + + + + #{cc.attrs.version.slug} + + + #{cc.attrs.version.slug} + + + + + + + #{cc.attrs.version.slug} + + + + + + + + + + diff --git a/zanata-war/src/main/webapp/version-group/request_to_join.xhtml b/zanata-war/src/main/webapp/version-group/request_to_join.xhtml index 6eed9d22bd..553ef102cd 100644 --- a/zanata-war/src/main/webapp/version-group/request_to_join.xhtml +++ b/zanata-war/src/main/webapp/version-group/request_to_join.xhtml @@ -6,7 +6,8 @@ xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" - template="../WEB-INF/template/template_2x.xhtml"> + template="../WEB-INF/template/template_2x.xhtml" + xmlns:z="http://java.sun.com/jsf/composite/zanata"> @@ -34,7 +35,9 @@ #{msgs['jsf.Version']} - #{selectableVersion.projectIteration.slug} +
    + +
    diff --git a/zanata-war/src/test/java/org/zanata/ArquillianTest.java b/zanata-war/src/test/java/org/zanata/ArquillianTest.java index 8399897603..280e22a1e6 100644 --- a/zanata-war/src/test/java/org/zanata/ArquillianTest.java +++ b/zanata-war/src/test/java/org/zanata/ArquillianTest.java @@ -26,6 +26,7 @@ import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.ext.h2.H2DataTypeFactory; +import org.dbunit.operation.DatabaseOperation; import org.jboss.arquillian.junit.Arquillian; import org.jboss.seam.util.Naming; import org.junit.After; @@ -73,7 +74,13 @@ protected IDatabaseConnection getConnection() { */ @Before public void prepareDataBeforeTest() { + addBeforeTestOperation(new DBUnitProvider.DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.DELETE_ALL)); prepareDBUnitOperations(); + addAfterTestOperation(new DBUnitProvider.DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.DELETE_ALL)); dbUnitProvider.prepareDataBeforeTest(); } diff --git a/zanata-war/src/test/java/org/zanata/RestTest.java b/zanata-war/src/test/java/org/zanata/RestTest.java index f680f8714e..e9e246aacc 100644 --- a/zanata-war/src/test/java/org/zanata/RestTest.java +++ b/zanata-war/src/test/java/org/zanata/RestTest.java @@ -32,10 +32,11 @@ import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.ext.h2.H2DataTypeFactory; +import org.dbunit.operation.DatabaseOperation; +import org.infinispan.manager.CacheContainer; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.resteasy.client.ClientRequest; -import org.jboss.resteasy.client.ProxyFactory; import org.jboss.seam.util.Naming; import org.junit.After; import org.junit.Before; @@ -46,7 +47,7 @@ import org.zanata.rest.ResourceRequestEnvironment; import org.zanata.rest.client.ZanataProxyFactory; import org.zanata.rest.dto.VersionInfo; -import org.zanata.rest.helper.RemoteTestSignaler; +import org.zanata.util.ServiceLocator; /** * Provides basic test utilities to test raw REST APIs and compatibility. @@ -112,8 +113,17 @@ protected IDatabaseConnection getConnection() { */ @RemoteBefore public void prepareDataBeforeTest() { + addBeforeTestOperation(new DBUnitProvider.DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.DELETE_ALL)); prepareDBUnitOperations(); + addAfterTestOperation(new DBUnitProvider.DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.DELETE_ALL)); dbUnitProvider.prepareDataBeforeTest(); + // Clear the hibernate cache + ServiceLocator.instance().getEntityManagerFactory().getCache() + .evictAll(); } /** diff --git a/zanata-war/src/test/java/org/zanata/ZanataJpaTest.java b/zanata-war/src/test/java/org/zanata/ZanataJpaTest.java index 54374155f1..24651bcc36 100644 --- a/zanata-war/src/test/java/org/zanata/ZanataJpaTest.java +++ b/zanata-war/src/test/java/org/zanata/ZanataJpaTest.java @@ -8,7 +8,6 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; -import net.sf.ehcache.CacheManager; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -42,7 +41,7 @@ public abstract class ZanataJpaTest { @Before protected void setupEM() { log.debug("Setting up EM"); - CacheManager.getInstance().clearAll(); + emf.getCache().evictAll(); em = emf.createEntityManager(); em.getTransaction().begin(); } @@ -57,7 +56,7 @@ protected void shutdownEM() { em.close(); } em = null; - CacheManager.getInstance().clearAll(); + emf.getCache().evictAll(); } protected EntityManager getEm() { diff --git a/zanata-war/src/test/java/org/zanata/cache/InfinispanTestCacheContainer.java b/zanata-war/src/test/java/org/zanata/cache/InfinispanTestCacheContainer.java new file mode 100644 index 0000000000..b99f068330 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/cache/InfinispanTestCacheContainer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This 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 2.1 of the License, or (at your option) + * any later version. + * + * This software 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 this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.cache; + +import lombok.Delegate; +import org.infinispan.configuration.global.GlobalConfiguration; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.lifecycle.Lifecycle; +import org.infinispan.manager.CacheContainer; +import org.infinispan.manager.DefaultCacheManager; + +/** + * A cache container to be used in tests. Everything is kept in memory in a + * real Infinispan cache which can be restarted / reset at will. + * @author Carlos Munoz camunoz@redhat.com + */ +public class InfinispanTestCacheContainer implements CacheContainer { + + @Delegate(types = CacheContainer.class, excludes = Lifecycle.class) + private DefaultCacheManager delegate; + + public InfinispanTestCacheContainer() { + start(); + } + + @Override + public void start() { + stop(); + this.delegate = + new DefaultCacheManager(getCacheManagerGlobalConfiguration()); + this.delegate.start(); + } + + @Override + public void stop() { + if(delegate != null) { + delegate.stop(); + } + } + + private GlobalConfiguration getCacheManagerGlobalConfiguration() { + /* This allows multiple concurrent tests to run. + See https://issues.jboss.org/browse/ISPN-2886 for the exception that + is thrown when this is not used. + */ + return new GlobalConfigurationBuilder() + .globalJmxStatistics() + .allowDuplicateDomains(true) + .build(); + } +} diff --git a/zanata-war/src/test/java/org/zanata/dao/TextFlowDAOTest.java b/zanata-war/src/test/java/org/zanata/dao/TextFlowDAOTest.java index d4896bba37..0e57e073fb 100644 --- a/zanata-war/src/test/java/org/zanata/dao/TextFlowDAOTest.java +++ b/zanata-war/src/test/java/org/zanata/dao/TextFlowDAOTest.java @@ -3,18 +3,23 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import java.util.Collection; import java.util.List; import java.util.Map; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; import org.dbunit.operation.DatabaseOperation; import org.hamcrest.Matchers; import org.hibernate.Session; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.common.ContentState; import org.zanata.model.HLocale; +import org.zanata.model.HProjectIteration; import org.zanata.model.HTextFlow; import org.zanata.model.HTextFlowTarget; import org.zanata.search.FilterConstraints; @@ -213,4 +218,63 @@ public void testGetTextFlowByDocumentIdWithConstraint() { assertThat(result, Matchers.hasSize(1)); } + @Test + public void testGetTranslationsByMatchedContext() { + + List testOperations = Lists.newArrayList(); + + testOperations.add(new DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); + testOperations.add(new DataSetOperation( + "org/zanata/test/model/AccountData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); + testOperations.add(new DataSetOperation( + "org/zanata/test/model/LocalesData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); + testOperations.add(new DataSetOperation( + "org/zanata/test/model/MergeTranslationsData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); + + for(DataSetOperation operation: testOperations) { + operation.prepare(this); + } + + executeOperations(testOperations); + + String projectSlug = "sample-project"; + + String fromVersionSlug = "1.0"; + String toVersionSlug = "2.0"; + + ProjectIterationDAO projectIterationDAO = new + ProjectIterationDAO((Session) getEm().getDelegate()); + + HProjectIteration fromVersion = + projectIterationDAO.getBySlug(projectSlug, fromVersionSlug); + Assertions.assertThat(fromVersion).isNotNull(); + + HProjectIteration toVersion = + projectIterationDAO.getBySlug(projectSlug, toVersionSlug); + Assertions.assertThat(toVersion).isNotNull(); + + List results = + dao.getSourceByMatchedContext( + fromVersion.getId(), toVersion.getId(), 0, 100); + + Assertions.assertThat(results).isNotEmpty(); + + for(HTextFlow[] result: results) { + Assertions.assertThat(result[0].getContentHash()).isEqualTo( + result[1].getContentHash()); + Assertions + .assertThat(result[0].getDocument().getDocId()) + .isEqualTo(result[1].getDocument().getDocId()); + Assertions + .assertThat(result[0].getResId()) + .isEqualTo(result[1].getResId()); + Assertions.assertThat(result[0]).isNotEqualTo(result[1]); + } + } + } diff --git a/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetHistoryDAOTest.java b/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetHistoryDAOTest.java new file mode 100644 index 0000000000..839cf7b31b --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetHistoryDAOTest.java @@ -0,0 +1,257 @@ +package org.zanata.dao; + +import java.util.List; + +import org.hibernate.transform.ResultTransformer; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.zanata.ZanataJpaTest; +import org.zanata.common.ContentState; +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.TranslationMatrix; +import org.zanata.rest.service.StatisticsServiceImpl; +import org.zanata.model.HAccount; +import org.zanata.model.HDocument; +import org.zanata.model.HLocale; +import org.zanata.model.HPerson; +import org.zanata.model.HProject; +import org.zanata.model.HTextFlow; +import org.zanata.model.HTextFlowBuilder; +import org.zanata.model.HTextFlowTarget; +import org.zanata.model.HTextFlowTargetHistory; + +import com.github.huangp.entityunit.entity.EntityMakerBuilder; +import com.github.huangp.entityunit.maker.FixedValueMaker; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import lombok.RequiredArgsConstructor; + +import static org.assertj.core.api.Assertions.assertThat; + +@Test(groups = { "jpa-tests" }) +public class TextFlowTargetHistoryDAOTest extends ZanataJpaTest { + private TextFlowTargetHistoryDAO historyDAO; + private HPerson user; + private HLocale hLocale; + private DateTime today = new DateTime(); + private DateTime yesterday = new DateTime().minusDays(1); + private DateTime twoDaysAgo = new DateTime().minusDays(2); + private HDocument hDocument; + private ResultTransformer resultTransformer; + private static final DateTimeFormatter dateFormatter = DateTimeFormat.mediumDate(); + + @BeforeMethod + public void setUp() throws Exception { + resultTransformer = new StatisticsServiceImpl.UserMatrixResultTransformer(getEm(), + dateFormatter); + historyDAO = new TextFlowTargetHistoryDAO(getSession()) { + @Override + protected String stripTimeFromDateTimeFunction(String columnName) { + // we override mysql function with a h2 one + return "formatdatetime(" + columnName + ", 'yyyy-MM-dd')"; + } + }; + deleteAllTables(); + hLocale = new HLocale(LocaleId.DE); + getEm().persist(hLocale); + user = makePerson("admin123"); + + hDocument = EntityMakerBuilder + .builder() + .addFieldOrPropertyMaker(HProject.class, + "sourceViewURL", + FixedValueMaker.EMPTY_STRING_MAKER) + .build() + .makeAndPersist(getEm(), HDocument.class); + + } + + private HPerson makePerson(String username) { + return EntityMakerBuilder + .builder() + .addFieldOrPropertyMaker(HAccount.class, "username", + FixedValueMaker.fix(username)) + .includeOptionalOneToOne().build() + .makeAndPersist(getEm(), HPerson.class); + } + + @Test + public void canGetUserTranslationMatrix() { + HTextFlowBuilder baseBuilder = + new HTextFlowBuilder().withDocument(hDocument) + .withTargetLocale(hLocale); + HTextFlowBuilder modifiedYesterday = + baseBuilder.withLastModifiedDate(yesterday).withLastModifiedBy( + user); + // make some text flows (all with 2 words) and some text flow targets + // 1. one Approved target done yesterday + modifiedYesterday.withResId("res1").withSourceContent("source 1") + .withTargetContent("target 1") + .withTargetState(ContentState.Approved).build(); + // 2. one Fuzzy target done yesterday + HTextFlow res2 = modifiedYesterday.withResId("res2") + .withSourceContent("source 2") + .withTargetContent("target 2") + .withTargetState(ContentState.NeedReview).build(); + // 3. one Translated target done yesterday + modifiedYesterday.withResId("res3").withSourceContent("source 3") + .withTargetContent("target 3") + .withTargetState(ContentState.Translated).build(); + + HTextFlowBuilder modifiedTwoDaysAgo = + baseBuilder.withLastModifiedDate(twoDaysAgo) + .withLastModifiedBy(user); + // 4. one Fuzzy target done 2 days ago + HTextFlow res4 = modifiedTwoDaysAgo.withResId("res4") + .withSourceContent("source 4") + .withTargetContent("target 4") + .withTargetState(ContentState.NeedReview).build(); + getEm().flush(); + + // 5. one Fuzzy target history done yesterday + HTextFlowTarget target2 = res2.getTargets().get(hLocale.getId()); + getEm().persist(new HTextFlowTargetHistory(target2)); + + // 6. one Fuzzy target history done 2 days ago + HTextFlowTarget target4 = res4.getTargets().get(hLocale.getId()); + getEm().persist(new HTextFlowTargetHistory(target4)); + + // 7. one Translated target done today (which should not appear in + // result) + baseBuilder.withLastModifiedBy(user).withLastModifiedDate(today) + .withResId("res7") + .withSourceContent("source 7") + .withTargetContent("target 7") + .withTargetState(ContentState.Translated).build(); + getEm().flush(); + + List result = + historyDAO + .getUserTranslationMatrix(user, + twoDaysAgo.withTimeAtStartOfDay(), + today.withTimeAtStartOfDay(), + Optional. absent(), + DateTimeZone.getDefault(), resultTransformer); + + assertThat(result).hasSize(4); + final SavedDatePredicate yesterdayPredicate = + new SavedDatePredicate(yesterday); + + Iterable yesterdayFuzzy = + Iterables + .filter(result, Predicates.and(yesterdayPredicate, + new ContentStatePredicate( + ContentState.NeedReview))); + + assertThat(yesterdayFuzzy) + .describedAs("saved as fuzzy yesterday") + .hasSize(1); + assertThat(yesterdayFuzzy.iterator().next().getWordCount()) + .describedAs("total words saved as fuzzy yesterday") + .isEqualTo(4); + + Iterable yesterdayApproved = + Iterables.filter(result, + Predicates + .and(yesterdayPredicate, + new ContentStatePredicate( + ContentState.Approved))); + assertThat(yesterdayApproved) + .describedAs("saved as approved yesterday") + .hasSize(1); + assertThat(yesterdayApproved.iterator().next().getWordCount()) + .describedAs("total words saved as approved yesterday") + .isEqualTo(2); + + Iterable yesterdayTranslated = + Iterables.filter(result, + Predicates.and(yesterdayPredicate, + new ContentStatePredicate( + ContentState.Translated))); + assertThat(yesterdayTranslated) + .describedAs("saved as translated yesterday") + .hasSize(1); + assertThat(yesterdayTranslated.iterator().next().getWordCount()) + .describedAs("total words saved as translated yesterday") + .isEqualTo(2); + + Iterable twoDaysAgoFuzzy = + Iterables.filter(result, + Predicates.and(new SavedDatePredicate(twoDaysAgo), + new ContentStatePredicate( + ContentState.NeedReview))); + assertThat(twoDaysAgoFuzzy) + .describedAs("saved as fuzzy two days ago") + .hasSize(1); + assertThat(twoDaysAgoFuzzy.iterator().next().getWordCount()) + .describedAs("total words saved as fuzzy two days ago") + .isEqualTo(4); + } + + @Test + public void whenSettingParameterIt() { + HTextFlowBuilder baseBuilder = + new HTextFlowBuilder().withDocument(hDocument) + .withTargetLocale(hLocale); + + DateTimeZone zone = DateTimeZone + .forID("Australia/Brisbane"); + baseBuilder + .withLastModifiedDate( + new DateTime(2015, 2, 1, 1, 0, zone)) + .withLastModifiedBy(user) + .withResId("res1").withSourceContent("source 1") + .withTargetContent("target 1") + .withTargetState(ContentState.Approved).build(); + getEm().flush(); + + List result = historyDAO + .getUserTranslationMatrix(user, + new DateTime(2015, 2, 1, 1, 1, zone), + new DateTime(zone), Optional. absent(), + DateTimeZone.getDefault(), resultTransformer); + assertThat(result).isEmpty(); + } + + @Test + public void canConvertTimeZoneIfUSerSuppliedDifferentZone() { + String result = historyDAO.convertTimeZoneFunction("lastChanged", + Optional.of(DateTimeZone.forID("Australia/Brisbane")), + DateTimeZone.forID("America/Chicago")); + + assertThat(result).isEqualToIgnoringCase( + "convert_tz(lastChanged, '-06:00', '10:00')"); + } + + @RequiredArgsConstructor + private static class ContentStatePredicate + implements Predicate { + private final ContentState state; + + @Override + public boolean apply(TranslationMatrix input) { + return input.getSavedState() == state; + } + } + + private static class SavedDatePredicate + implements Predicate { + private final String theDate; + + private SavedDatePredicate(DateTime theDate) { + this.theDate = dateFormatter.print(theDate.withTimeAtStartOfDay()); + } + + @Override + public boolean apply(TranslationMatrix input) { + return theDate.equals(input.getSavedDate()); + } + } +} diff --git a/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetReviewCommentsDAOJPATest.java b/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetReviewCommentsDAOJPATest.java index af9304faf5..d59eb1ac14 100644 --- a/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetReviewCommentsDAOJPATest.java +++ b/zanata-war/src/test/java/org/zanata/dao/TextFlowTargetReviewCommentsDAOJPATest.java @@ -41,6 +41,7 @@ * @author Patrick Huang pahuang@redhat.com */ +@Test(groups = { "jpa-tests" }) public class TextFlowTargetReviewCommentsDAOJPATest extends ZanataDbunitJpaTest { private TextFlowTargetReviewCommentsDAO reviewCommentsDAO; private TextFlowTargetDAO textFlowTargetDAO; diff --git a/zanata-war/src/test/java/org/zanata/email/EmailStrategyTest.java b/zanata-war/src/test/java/org/zanata/email/EmailStrategyTest.java index 4b6b121a1e..c1b59c6fa6 100644 --- a/zanata-war/src/test/java/org/zanata/email/EmailStrategyTest.java +++ b/zanata-war/src/test/java/org/zanata/email/EmailStrategyTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Collection; +import java.util.Locale; import java.util.Properties; import javax.mail.BodyPart; @@ -42,6 +43,7 @@ import org.testng.annotations.Test; import org.zanata.common.ProjectType; import org.zanata.i18n.Messages; +import org.zanata.i18n.MessagesFactory; import org.zanata.webtrans.shared.model.ProjectIterationId; /** @@ -64,13 +66,20 @@ public String format(String key, Object... args) { } }; String fromAddress = "zanata@example.com"; - String fromName = "SERVER_NAME[测试]"; + String fromName = msgs.get("jsf.Zanata"); String toName = "User Name[测试]"; String toAddress = "username@example.com"; String serverPath = "https://zanata.example.com"; InternetAddress toAddr; InternetAddress[] toAddresses; + MessagesFactory msgsFactory = new MessagesFactory() { + @Override + public Messages getMessages(Locale locale) { + return msgs; + } + }; + Session session = Session.getDefaultInstance(new Properties()); EmailBuilder.Context context = new EmailBuilder.Context() { @Override @@ -82,13 +91,8 @@ String getFromAddress() { String getServerPath() { return serverPath; } - - @Override - String getFromName() { - return fromName; - } }; - EmailBuilder builder = new EmailBuilder(session, context, msgs); + EmailBuilder builder = new EmailBuilder(session, context, msgsFactory); MimeMessage message; // context values needed for some templates: diff --git a/zanata-war/src/test/java/org/zanata/model/HTextFlowBuilder.java b/zanata-war/src/test/java/org/zanata/model/HTextFlowBuilder.java index 77e2a4127b..4e648e104c 100644 --- a/zanata-war/src/test/java/org/zanata/model/HTextFlowBuilder.java +++ b/zanata-war/src/test/java/org/zanata/model/HTextFlowBuilder.java @@ -62,6 +62,11 @@ public HTextFlow build() { hTextFlow.getTargets().put(targetLocale.getId(), target); target.setContents(targetContent); target.setLastModifiedBy(lastModifiedBy); + if (targetState == ContentState.Approved || targetState == ContentState.Approved) { + target.setReviewer(lastModifiedBy); + } else { + target.setTranslator(lastModifiedBy); + } if (lastModifiedDate != null) { target.setLastChanged(lastModifiedDate.toDate()); } else { diff --git a/zanata-war/src/test/java/org/zanata/model/TestFixture.java b/zanata-war/src/test/java/org/zanata/model/TestFixture.java index 8149e15311..9e9af6cdd7 100644 --- a/zanata-war/src/test/java/org/zanata/model/TestFixture.java +++ b/zanata-war/src/test/java/org/zanata/model/TestFixture.java @@ -121,7 +121,7 @@ public static UserWorkspaceContext userWorkspaceContext( ProjectIterationId projectIterationId = new ProjectIterationId(projectSlug, iterationSlug, projectType); WorkspaceRestrictions workspaceRestrictions = - new WorkspaceRestrictions(projectActive, hasWriteAccess, true, + new WorkspaceRestrictions(projectActive, false, hasWriteAccess, true, true, true); return new UserWorkspaceContext(new WorkspaceContext(new WorkspaceId( projectIterationId, LocaleId.EN_US), "workspaceName", @@ -131,7 +131,7 @@ public static UserWorkspaceContext userWorkspaceContext( public static UserWorkspaceContext userWorkspaceContext( boolean projectActive, boolean hasWriteAccess) { WorkspaceRestrictions workspaceRestrictions = - new WorkspaceRestrictions(projectActive, hasWriteAccess, true, + new WorkspaceRestrictions(projectActive, false, hasWriteAccess, true, true, true); return new UserWorkspaceContext(new WorkspaceContext(workspaceId(), "workspaceName", LocaleId.EN_US.getId()), workspaceRestrictions); diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationCompatibilityITCase.java index 866e42ad24..5188ced07c 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationCompatibilityITCase.java @@ -38,6 +38,9 @@ public class ProjectIterationCompatibilityITCase extends RestTest { @Override protected void prepareDBUnitOperations() { + addBeforeTestOperation(new DataSetOperation( + "org/zanata/test/model/AccountData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); addBeforeTestOperation(new DataSetOperation( "org/zanata/test/model/ProjectsData.dbunit.xml", DatabaseOperation.CLEAN_INSERT)); diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationRawCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationRawCompatibilityITCase.java index 211c848426..93ba249ec5 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationRawCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectIterationRawCompatibilityITCase.java @@ -45,6 +45,9 @@ public class ProjectIterationRawCompatibilityITCase extends RestTest { @Override protected void prepareDBUnitOperations() { + addBeforeTestOperation(new DataSetOperation( + "org/zanata/test/model/AccountData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); addBeforeTestOperation(new DataSetOperation( "org/zanata/test/model/ProjectsData.dbunit.xml", DatabaseOperation.CLEAN_INSERT)); diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectRawCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectRawCompatibilityITCase.java index 424a758c21..12c454c59a 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/ProjectRawCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/ProjectRawCompatibilityITCase.java @@ -66,6 +66,9 @@ public class ProjectRawCompatibilityITCase extends RestTest { @Override protected void prepareDBUnitOperations() { + addBeforeTestOperation(new DataSetOperation( + "org/zanata/test/model/AccountData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); addBeforeTestOperation(new DataSetOperation( "org/zanata/test/model/ProjectsData.dbunit.xml", DatabaseOperation.CLEAN_INSERT)); diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsCompatibilityITCase.java index 36765ddc5b..c3d23ab7cd 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsCompatibilityITCase.java @@ -72,7 +72,7 @@ protected void prepareDBUnitOperations() { DatabaseOperation.CLEAN_INSERT)); addAfterTestOperation(new DataSetOperation( - "org/zanata/test/model/HistoryTestData.dbunit.xml", + "org/zanata/test/model/ClearAllTables.dbunit.xml", DatabaseOperation.DELETE_ALL)); } @@ -407,8 +407,8 @@ public void getTranslations() throws Exception { assertThat(tft3.getTranslator().getEmail(), is("user1@localhost")); } - @Test - @RunAsClient +// @Test +// @RunAsClient public void putTranslations() throws Exception { ITranslatedDocResource translationsClient = super.createProxy(createClientProxyFactory(ADMIN, ADMIN_KEY), diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsRawCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsRawCompatibilityITCase.java index 697498d3cd..883a583de5 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsRawCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/TranslationsRawCompatibilityITCase.java @@ -59,6 +59,9 @@ public class TranslationsRawCompatibilityITCase extends RestTest { @Override protected void prepareDBUnitOperations() { + addBeforeTestOperation(new DataSetOperation( + "org/zanata/test/model/ClearAllTables.dbunit.xml", + DatabaseOperation.DELETE_ALL)); addBeforeTestOperation(new DataSetOperation( "org/zanata/test/model/AccountData.dbunit.xml", DatabaseOperation.CLEAN_INSERT)); @@ -74,10 +77,6 @@ protected void prepareDBUnitOperations() { addBeforeTestOperation(new DataSetOperation( "org/zanata/test/model/TextFlowTestData.dbunit.xml", DatabaseOperation.CLEAN_INSERT)); - - addAfterTestOperation(new DataSetOperation( - "org/zanata/test/model/HistoryTestData.dbunit.xml", - DatabaseOperation.DELETE_ALL)); } @Test @@ -407,6 +406,7 @@ protected void onResponse(ClientResponse response) { is("user1@localhost")); } }.run(); + } @Test diff --git a/zanata-war/src/test/java/org/zanata/rest/compat/VersionCompatibilityITCase.java b/zanata-war/src/test/java/org/zanata/rest/compat/VersionCompatibilityITCase.java index 4dc471529f..d6914a042b 100644 --- a/zanata-war/src/test/java/org/zanata/rest/compat/VersionCompatibilityITCase.java +++ b/zanata-war/src/test/java/org/zanata/rest/compat/VersionCompatibilityITCase.java @@ -20,11 +20,13 @@ */ package org.zanata.rest.compat; +import org.dbunit.operation.DatabaseOperation; import org.jboss.arquillian.container.test.api.RunAsClient; import org.junit.Test; import org.zanata.RestTest; import org.zanata.apicompat.rest.client.IVersionResource; import org.zanata.apicompat.rest.dto.VersionInfo; +import org.zanata.provider.DBUnitProvider; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; @@ -33,6 +35,9 @@ public class VersionCompatibilityITCase extends RestTest { @Override protected void prepareDBUnitOperations() { + addBeforeTestOperation(new DBUnitProvider.DataSetOperation( + "org/zanata/test/model/AccountData.dbunit.xml", + DatabaseOperation.CLEAN_INSERT)); } @Test diff --git a/zanata-war/src/test/java/org/zanata/rest/service/DateRangeTest.java b/zanata-war/src/test/java/org/zanata/rest/service/DateRangeTest.java new file mode 100644 index 0000000000..ac08603002 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/service/DateRangeTest.java @@ -0,0 +1,48 @@ +package org.zanata.rest.service; + +import org.assertj.core.api.Assertions; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.Hours; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.testng.annotations.Test; + +public class DateRangeTest { + + @Test + public void testConcept() { + + DateTimeZone bneZone = DateTimeZone.forID("Australia/Brisbane"); + DateTimeZone shanghaiZone = DateTimeZone.forID("Asia/Shanghai"); // 2 hours late + + DateTime bneTime = new DateTime(2015, 2, 1, 0, 0, bneZone); + DateTime shanghaiTime = new DateTime(2015, 2, 1, 0, 0, shanghaiZone); + int hours = Hours.hoursBetween(bneTime, shanghaiTime).getHours(); + Assertions.assertThat(hours).isEqualTo(2); + + DateTime bneToShanghai = bneTime.toDateTime(shanghaiZone); + Assertions.assertThat(bneToShanghai.getMonthOfYear()).isEqualTo(1); + Assertions.assertThat(bneToShanghai.getDayOfMonth()).isEqualTo(31); + Assertions.assertThat(bneToShanghai.getHourOfDay()).isEqualTo(22); + + // now test our parser + DateTimeFormatter bneFormat = + DateTimeFormat.forPattern("yyyy-MM-dd").withZone(bneZone); + + DateTimeFormatter shanghaiFormat = bneFormat.withZone(shanghaiZone); + DateTime timeAsShanghai = shanghaiFormat.parseDateTime("2015-02-01"); + DateTime timeInBne = timeAsShanghai.toDateTime(bneZone); + Assertions.assertThat(timeInBne.getMonthOfYear()).isEqualTo(2); + Assertions.assertThat(timeInBne.getHourOfDay()).isEqualTo(2); + + timeAsShanghai = shanghaiFormat.parseDateTime("2015-02-01"); + DateTime endOfDayShanghai = timeAsShanghai.plusDays(1).minusMillis(1); + + timeInBne = endOfDayShanghai.toDateTime(bneZone); + Assertions.assertThat(timeInBne.getMonthOfYear()).isEqualTo(2); + Assertions.assertThat(timeInBne.getDayOfMonth()).isEqualTo(2); + + } + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/service/ResourceServiceRestTest.java b/zanata-war/src/test/java/org/zanata/rest/service/ResourceServiceRestTest.java index 45d6f3152b..53f973280a 100644 --- a/zanata-war/src/test/java/org/zanata/rest/service/ResourceServiceRestTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/service/ResourceServiceRestTest.java @@ -5,6 +5,7 @@ import javax.ws.rs.core.Response.Status; +import org.infinispan.manager.CacheContainer; import org.jboss.resteasy.client.ClientResponse; import org.jboss.seam.security.management.JpaIdentityStore; import org.mockito.Mock; @@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.model.HAccount; import org.zanata.model.HPerson; import org.zanata.rest.StringSet; @@ -37,10 +39,10 @@ public class ResourceServiceRestTest extends ResourceTranslationServiceRestTest @Override protected void prepareResources() { MockitoAnnotations.initMocks(this); - SeamAutowire seamAutowire = getSeamAutowire(); seamAutowire.use("session", getSession()).use("entityManager", getEm()) .use("identity", mockIdentity).useImpl(LocaleServiceImpl.class) + .use("cacheContainer", new InfinispanTestCacheContainer()) .useImpl(CopyTransServiceImpl.class) .useImpl(DocumentServiceImpl.class) .useImpl(VersionStateCacheImpl.class) diff --git a/zanata-war/src/test/java/org/zanata/rest/service/StatisticsResourceRestTest.java b/zanata-war/src/test/java/org/zanata/rest/service/StatisticsResourceRestTest.java index febf77d997..1ff32cda61 100644 --- a/zanata-war/src/test/java/org/zanata/rest/service/StatisticsResourceRestTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/service/StatisticsResourceRestTest.java @@ -23,7 +23,6 @@ import org.dbunit.operation.DatabaseOperation; import org.zanata.ZanataRestTest; import org.zanata.seam.SeamAutowire; -import org.zanata.service.impl.StatisticsServiceImpl; /** * TODO This method doesn't have tests yet. It is a placeholder for when a diff --git a/zanata-war/src/test/java/org/zanata/service/impl/StatisticsServiceImplTest.java b/zanata-war/src/test/java/org/zanata/rest/service/StatisticsServiceImplTest.java similarity index 98% rename from zanata-war/src/test/java/org/zanata/service/impl/StatisticsServiceImplTest.java rename to zanata-war/src/test/java/org/zanata/rest/service/StatisticsServiceImplTest.java index 0beee881b8..2f6362ef6b 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/StatisticsServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/service/StatisticsServiceImplTest.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ -package org.zanata.service.impl; +package org.zanata.rest.service; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; @@ -28,15 +28,13 @@ import java.util.Arrays; import java.util.Date; -import net.sf.ehcache.CacheManager; - import org.dbunit.operation.DatabaseOperation; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.common.ContentState; import org.zanata.common.LocaleId; import org.zanata.dao.PersonDAO; @@ -50,9 +48,9 @@ import org.zanata.rest.dto.stats.contribution.BaseContributionStatistic; import org.zanata.rest.dto.stats.contribution.ContributionStatistics; import org.zanata.rest.dto.stats.contribution.LocaleStatistics; -import org.zanata.rest.service.StatisticsResource; import org.zanata.seam.SeamAutowire; import org.zanata.service.ValidationService; +import org.zanata.service.impl.TranslationStateCacheImpl; /** * @author Carlos Munoz preMergeData = + textFlowDAO.getSourceByMatchedContext( + expectedSourceVersion.getId(), expectedTargetVersion.getId(), 0, + 100); + + List locales = + service.getSupportedLocales(projectSlug, targetVersionSlug); + + List expectedMergeData = Lists.newArrayList(); + for (HTextFlow[] data : preMergeData) { + for(HLocale locale: locales) { + HTextFlowTarget sourceTft = + data[0].getTargets().get(locale.getId()); + HTextFlowTarget targetTft = + data[1].getTargets().get(locale.getId()); + + if(targetTft == null) { + targetTft = textFlowTargetDAO + .getOrCreateTarget(data[1], locale); + } + if (MergeTranslationsServiceImpl.shouldMerge(sourceTft, + targetTft, useNewerTranslation)) { + expectedMergeData.add(new HTextFlowTarget[] { sourceTft, + targetTft }); + } + } + } + + MergeTranslationsServiceImpl spyService = spy(service); + spyService.startMergeTranslations(projectSlug, sourceVersionSlug, + projectSlug, targetVersionSlug, useNewerTranslation, null); + + //entity in preMergeData should be updated after merge process + + verify(spyService).mergeTranslationBatch(Matchers.eq( + expectedSourceVersion), Matchers.eq(expectedTargetVersion), + Matchers.anyList(), Matchers.eq(useNewerTranslation), + Matchers.anyInt(), Matchers.anyInt()); + + // check all results has same contents and states + // check generated comments in [1] + // check non translated/approved is not being used + // check use latest translated if enabled + for(HTextFlowTarget[] data: expectedMergeData) { + assertThat(data[0].getState()).isIn(ContentState.TRANSLATED_STATES); + assertThat(data[1].getState()).isIn(ContentState.TRANSLATED_STATES); + assertThat(data[0].getContents()).isEqualTo(data[1].getContents()); + assertThat(data[0].getState()).isEqualTo(data[1].getState()); + assertThat(data[1].getRevisionComment()).contains( + MessageGenerator.PREFIX_MERGE_TRANS); + } + } + + @Test + public void testMergeTranslationWorkIsNotTranslated1() { + Date now = new Date(); + HTextFlowTarget target1 = generateTarget(ContentState.NeedReview, now, "string1"); + HTextFlowTarget target2 = generateTarget(ContentState.Translated, now, "string2"); + + testShouldMergeCondition(target1, target2, false, false); + } + + @Test + public void testMergeTranslationWorkIsNotTranslated2() { + Date now = new Date(); + HTextFlowTarget target1 = generateTarget(ContentState.Translated, now, "string1"); + HTextFlowTarget target2 = generateTarget(ContentState.NeedReview, now, "string2"); + + testShouldMergeCondition(target1, target2, false, true); + } + + // target tft is has same modify date as source tft + @Test + public void testMergeTranslationWorkCheckDate1() { + Date now = new Date(); + HTextFlowTarget target1 = generateTarget(ContentState.Translated, now, "string1"); + HTextFlowTarget target2 = generateTarget(ContentState.Translated, now, "string2"); + + testShouldMergeCondition(target1, target2, false, false); + } + + // target tft is newer than source tft + @Test + public void testMergeTranslationWorkCheckDate2() { + Calendar c = new GregorianCalendar(); + HTextFlowTarget target1 = + generateTarget(ContentState.Translated, c.getTime(), "string1"); + c.add(Calendar.DATE, 30); + HTextFlowTarget target2 = + generateTarget(ContentState.Translated, c.getTime(), "string2"); + + testShouldMergeCondition(target1, target2, true, false); + } + + // target tft is older than source tft + @Test + public void testMergeTranslationWorkCheckDate3() { + Calendar c = new GregorianCalendar(); + HTextFlowTarget target2 = + generateTarget(ContentState.Translated, c.getTime(), "string1"); + c.add(Calendar.DATE, 30); + HTextFlowTarget target1 = + generateTarget(ContentState.Translated, c.getTime(), "string2"); + + testShouldMergeCondition(target1, target2, true, true); + } + + // target tft is older than source tft, but same content + @Test + public void testMergeTranslationWorkSameStateAndContent() { + String content = "content0"; + Calendar c = new GregorianCalendar(); + HTextFlowTarget target2 = + generateTarget(ContentState.Translated, c.getTime(), content); + c.add(Calendar.DATE, 30); + HTextFlowTarget target1 = + generateTarget(ContentState.Translated, c.getTime(), content); + testShouldMergeCondition(target1, target2, false, false); + } + + private HTextFlowTarget generateTarget(ContentState state, + Date lastChanged, String content) { + HTextFlowTarget target = new HTextFlowTarget(); + target.setState(state); + target.setLastChanged(lastChanged); + target.setContents(content); + return target; + } + + private void testShouldMergeCondition(HTextFlowTarget target1, + HTextFlowTarget target2, boolean useNewerTranslation, + boolean expectedResult) { + boolean result = + service.shouldMerge(target1, target2, useNewerTranslation); + assertThat(result).isEqualTo(expectedResult); + } +} diff --git a/zanata-war/src/test/java/org/zanata/service/impl/TranslationStateCacheImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/TranslationStateCacheImplTest.java index db8ac49cda..6a144ab254 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/TranslationStateCacheImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/TranslationStateCacheImplTest.java @@ -29,13 +29,17 @@ import java.util.HashMap; import java.util.Map; +import org.infinispan.manager.CacheContainer; +import org.infinispan.manager.DefaultCacheManager; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.common.LocaleId; import org.zanata.dao.TextFlowTargetDAO; +import org.zanata.seam.SeamAutowire; import org.zanata.service.impl.TranslationStateCacheImpl.DocumentLocaleKey; import org.zanata.ui.model.statistic.WordStatistic; import org.zanata.webtrans.shared.model.DocumentId; @@ -51,7 +55,7 @@ public class TranslationStateCacheImplTest { private CacheLoader docStatisticLoader; @Mock - private CacheLoader docStatsLoader; + private CacheLoader docStatusLoader; @Mock private TextFlowTargetDAO textFlowTargetDAO; @Mock @@ -61,22 +65,17 @@ public class TranslationStateCacheImplTest { public void beforeMethod() { MockitoAnnotations.initMocks(this); tsCache = - new TranslationStateCacheImpl(docStatisticLoader, docStatsLoader, - targetValidationLoader) { - @Override - TextFlowTargetDAO getTextFlowTargetDAO() { - return textFlowTargetDAO; - } - }; + new TranslationStateCacheImpl(docStatisticLoader, + docStatusLoader, targetValidationLoader); - tsCache.create(); - tsCache.destroy(); - tsCache.create(); - } + SeamAutowire seam = SeamAutowire.instance(); + seam.reset() + .use("textFlowTargetDAO", textFlowTargetDAO) + .use("cacheContainer", new InfinispanTestCacheContainer()) + .ignoreNonResolvable(); + tsCache = seam.autowire(tsCache); - @AfterMethod - public void afterMethod() { - tsCache.destroy(); + tsCache.create(); } public void testGetLastModifiedTextFlowTarget() throws Exception { @@ -90,7 +89,7 @@ public void testGetLastModifiedTextFlowTarget() throws Exception { ""); // When: - when(docStatsLoader.load(key)).thenReturn(docStats); + when(docStatusLoader.load(key)).thenReturn(docStats); DocumentStatus result1 = tsCache.getDocumentStatus(documentId, testLocaleId); @@ -98,7 +97,7 @@ public void testGetLastModifiedTextFlowTarget() throws Exception { tsCache.getDocumentStatus(documentId, testLocaleId); // Then: - verify(docStatsLoader).load(key); // only load the value once + verify(docStatusLoader).load(key); // only load the value once assertThat(result1, equalTo(docStats)); assertThat(result2, equalTo(docStats)); } diff --git a/zanata-war/src/test/java/org/zanata/service/impl/VersionGroupServiceImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/VersionGroupServiceImplTest.java index 86d7fe21ae..47c2ad2794 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/VersionGroupServiceImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/VersionGroupServiceImplTest.java @@ -30,9 +30,11 @@ import org.dbunit.operation.DatabaseOperation; import org.hamcrest.Matchers; +import org.infinispan.manager.CacheContainer; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.common.LocaleId; import org.zanata.dao.PersonDAO; import org.zanata.dao.ProjectIterationDAO; @@ -86,6 +88,7 @@ public void initializeSeam() { .use("projectIterationDAO", new ProjectIterationDAO(getSession())) .use("session", getSession()) + .use("cacheContainer", new InfinispanTestCacheContainer()) .useImpl(VersionStateCacheImpl.class).useImpl(LocaleServiceImpl.class).ignoreNonResolvable(); versionGroupServiceImpl = seam.autowire(VersionGroupServiceImpl.class); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/VersionStateCacheImplTest.java b/zanata-war/src/test/java/org/zanata/service/impl/VersionStateCacheImplTest.java index bde0d9c56c..a3ed25c281 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/VersionStateCacheImplTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/VersionStateCacheImplTest.java @@ -5,11 +5,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.infinispan.manager.CacheContainer; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.common.LocaleId; import org.zanata.service.VersionLocaleKey; import org.zanata.ui.model.statistic.WordStatistic; @@ -31,17 +33,10 @@ public class VersionStateCacheImplTest { public void beforeMethod() { MockitoAnnotations.initMocks(this); cache = new VersionStateCacheImpl(versionStatisticLoader); - - cache.create(); - cache.destroy(); + cache.setCacheContainer(new InfinispanTestCacheContainer()); cache.create(); } - @AfterMethod - public void afterMethod() { - cache.destroy(); - } - public void getStatisticTest() throws Exception { Long projectIterationId = 1L; diff --git a/zanata-war/src/test/java/org/zanata/webtrans/client/rpc/DummyActivateWorkspaceCommand.java b/zanata-war/src/test/java/org/zanata/webtrans/client/rpc/DummyActivateWorkspaceCommand.java index 810133b2a3..33f1b975ef 100644 --- a/zanata-war/src/test/java/org/zanata/webtrans/client/rpc/DummyActivateWorkspaceCommand.java +++ b/zanata-war/src/test/java/org/zanata/webtrans/client/rpc/DummyActivateWorkspaceCommand.java @@ -45,7 +45,7 @@ public void execute() { new WorkspaceContext(action.getWorkspaceId(), "Dummy Workspace", "Mock Sweedish"); WorkspaceRestrictions workspaceRestrictions = - new WorkspaceRestrictions(true, true, true, true, true); + new WorkspaceRestrictions(true, false, true, true, true, true); UserWorkspaceContext userWorkspaceContext = new UserWorkspaceContext(context, workspaceRestrictions); userWorkspaceContext.setSelectedDoc(new DocumentInfo(new DocumentId( diff --git a/zanata-war/src/test/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandlerTest.java b/zanata-war/src/test/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandlerTest.java index 24d14819d7..45592086de 100644 --- a/zanata-war/src/test/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandlerTest.java +++ b/zanata-war/src/test/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandlerTest.java @@ -14,11 +14,13 @@ import org.hamcrest.Matchers; import org.hibernate.search.impl.FullTextSessionImpl; import org.hibernate.search.jpa.impl.FullTextEntityManagerImpl; +import org.infinispan.manager.CacheContainer; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.common.LocaleId; import org.zanata.common.ProjectType; import org.zanata.dao.DocumentDAO; @@ -61,7 +63,6 @@ public class GetTransUnitListHandlerTest extends ZanataDbunitJpaTest { private final DocumentInfo document = TestFixture.documentInfo(1L, ""); private final LocaleId localeId = new LocaleId("ja"); private HLocale jaHLocale; - private SeamAutowire seam = SeamAutowire.instance(); @Override @@ -92,6 +93,7 @@ public void setUp() throws Exception { .use("transUnitTransformer", transUnitTransformer) .use("webtrans.gwt.GetTransUnitsNavigationHandler", getTransUnitsNavigationService) + .use("cacheContainer", new InfinispanTestCacheContainer()) .useImpl(TranslationStateCacheImpl.class) .useImpl(TextFlowSearchServiceImpl.class) .useImpl(ValidationServiceImpl.class).allowCycles(); diff --git a/zanata-war/src/test/resources/arquillian/persistence.xml b/zanata-war/src/test/resources/arquillian/persistence.xml index d531fb737c..7622f8846a 100755 --- a/zanata-war/src/test/resources/arquillian/persistence.xml +++ b/zanata-war/src/test/resources/arquillian/persistence.xml @@ -5,10 +5,10 @@ version="2.0"> + transaction-type="JTA"> org.hibernate.ejb.HibernatePersistence - java:jboss/datasources/zanataTestDatasource + java:jboss/datasources/zanataTestDatasource + + + + @@ -85,8 +93,6 @@ - diff --git a/zanata-war/src/test/resources/arquillian/standalone-arquillian-wildfly.xml b/zanata-war/src/test/resources/arquillian/standalone-arquillian-wildfly.xml index c1837c4fd8..b9034e4f9f 100644 --- a/zanata-war/src/test/resources/arquillian/standalone-arquillian-wildfly.xml +++ b/zanata-war/src/test/resources/arquillian/standalone-arquillian-wildfly.xml @@ -29,6 +29,7 @@ + @@ -106,6 +107,18 @@ + + + + + + + + + + + + @@ -246,7 +259,8 @@ - + @@ -262,6 +276,18 @@ + + + + + + + +
    diff --git a/zanata-war/src/test/resources/arquillian/standalone-arquillian.xml b/zanata-war/src/test/resources/arquillian/standalone-arquillian.xml index e89da36638..b61c01a07f 100644 --- a/zanata-war/src/test/resources/arquillian/standalone-arquillian.xml +++ b/zanata-war/src/test/resources/arquillian/standalone-arquillian.xml @@ -31,6 +31,7 @@ + @@ -83,6 +84,18 @@ + + + + + + + + + + + + @@ -180,6 +193,7 @@ @@ -196,6 +210,18 @@ + + + + + + + + diff --git a/zanata-war/src/test/resources/org/zanata/test/model/MergeTranslationsData.dbunit.xml b/zanata-war/src/test/resources/org/zanata/test/model/MergeTranslationsData.dbunit.xml new file mode 100644 index 0000000000..1d27db35c9 --- /dev/null +++ b/zanata-war/src/test/resources/org/zanata/test/model/MergeTranslationsData.dbunit.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +