diff --git a/.classpath b/.classpath index db19c50be..84016272a 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,12 @@ + + + + @@ -11,15 +15,9 @@ - - - - - - @@ -43,8 +41,6 @@ - - @@ -64,7 +60,6 @@ - @@ -86,6 +81,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..aa1af857e --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,22 @@ +When reporting a bug please provide the following information to help reproduce the bug: + +Version of OpenRefine used (Google Refine 2.6, OpenRefine2.8, an other distribution?): +----- + +Operating Systems and version: +----- + +Browser + version used - Please note that OpenRefine doesn't support Internet Explorer but works OK in most cases: +----- + +Steps followed to create the issue: +----- + +If you are allowed and are OK with making your data public, it would be awesome if you can include the data causing the issue or a URL pointing to where the data is (if your concerned about keeping your data private, ping us on our mailing list): +----- + +Current Results: +----- + +Expected Results: +----- diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope index c237e4f6b..000a6e8e0 100644 --- a/.settings/.jsdtscope +++ b/.settings/.jsdtscope @@ -1,6 +1,6 @@ - + diff --git a/.settings/org.eclipse.wst.jsdt.core.prefs b/.settings/org.eclipse.wst.jsdt.core.prefs index 7b594a52b..291cdc4c8 100644 --- a/.settings/org.eclipse.wst.jsdt.core.prefs +++ b/.settings/org.eclipse.wst.jsdt.core.prefs @@ -57,6 +57,7 @@ org.eclipse.wst.jsdt.core.compiler.problem.unusedParameterIncludeDocCommentRefer org.eclipse.wst.jsdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.wst.jsdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.wst.jsdt.core.compiler.source=1.3 +org.eclipse.wst.jsdt.core.compiler.source.type=script org.eclipse.wst.jsdt.core.compiler.taskCaseSensitive=enabled org.eclipse.wst.jsdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL org.eclipse.wst.jsdt.core.compiler.taskTags=TODO,FIXME,XXX @@ -318,4 +319,5 @@ org.eclipse.wst.jsdt.core.formatter.tabulation.char=space org.eclipse.wst.jsdt.core.formatter.tabulation.size=4 org.eclipse.wst.jsdt.core.formatter.use_tabs_only_for_leading_indentations=false org.eclipse.wst.jsdt.core.formatter.wrap_before_binary_operator=true -semanticValidation=enabled +semanticValidation=disabled +strictOnKeywordUsage=disabled diff --git a/.settings/org.eclipse.wst.validation.prefs b/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 000000000..b09608453 --- /dev/null +++ b/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,8 @@ +DELEGATES_PREFERENCE=delegateValidatorList +USER_BUILD_PREFERENCE=enabledBuildValidatorList +USER_MANUAL_PREFERENCE=enabledManualValidatorList +USER_PREFERENCE=overrideGlobalPreferencestruedisableAllValidationtrueversion1.2.700.v201508251749 +eclipse.preferences.version=1 +override=true +suspend=true +vf.version=3 diff --git a/.travis.yml b/.travis.yml index 0563e8378..b0184efcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,13 @@ language: java jdk: - oraclejdk8 + +addons: + mariadb: '10.0' + +services: + - mysql + - postgresql env: # encrypted Codacy key, see https://docs.travis-ci.com/user/encryption-keys/ @@ -8,6 +15,12 @@ env: before_install: - wget -O ~/codacy-coverage-reporter-assembly-latest.jar https://github.com/codacy/codacy-coverage-reporter/releases/download/2.0.0/codacy-coverage-reporter-2.0.0-assembly.jar + # create test database for mysql, mariadb and postgresql + - mysql -u root -e 'CREATE DATABASE test_db;' + - mysql -u root test_db < extensions/database/test/conf/travis-mysql.sql + - psql -c 'CREATE DATABASE test_db;' -U postgres + - psql -U postgres test_db < extensions/database/test/conf/travis-pgsql.sql + - cp extensions/database/test/conf/travis_tests.xml extensions/database/test/conf/tests.xml script: - ./refine server_test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9e08690a..410e10483 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -This document present how you can contribute to the OpenRefine project. +This document present how you can contribute to the OpenRefine project. Please review also our [Governance model](https://github.com/OpenRefine/OpenRefine/blob/master/GOVERNANCE.md) ## Documentation, Questions or Problem @@ -11,18 +11,18 @@ If you really want to file a bug or request a feature, go to this [issue list](h ### Guidelines for Reporting a Bug When reporting a bug please provide the following information to help reproduce the bug: -- Version of OpenRefine used (Google Refine 2.6, OpenRefine2.6, an other distribution?) +- Version of OpenRefine used (Google Refine 2.6, OpenRefine2.7, an other distribution?) - Operating Systems and version - Browser + version used - Please note that OpenRefine doesn't support Internet Explorer -- Steps followed to create the issues -- If you are allowed, it is awesome if you can join the data generating the issue +- Steps followed to create the issue +- If you are allowed, it is awesome if you can include the data generating the issue - Current Results - Expected Results ## Promote OpenRefine -You don't need to be coder to contribute to OpenRefine. Did you wrote a tutorial or article about OpenRefine on your blog or site? Are you organizing a workshop or presentation OpenRefine in your city? Let us know via our [user discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine) or twitter account ([@OpenRefine](http://twitter.com/OpenRefine)). We will share the news via our monthly update and via our twitter handle. +You don't need to be coder to contribute to OpenRefine. Did you write a tutorial or article about OpenRefine on your blog or site? Are you organizing a workshop or presentation for OpenRefine in your city? Let us know via our [user discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine) or Twitter account ([@OpenRefine](http://twitter.com/OpenRefine)). We will share the news via our monthly update and via our Twitter handle. ## Contributing translations @@ -30,38 +30,42 @@ You can help us [translate OpenRefine](https://github.com/OpenRefine/OpenRefine/ ## Contributing code -You can contribute code in three different way: -- fix minor bug -- develop an OpenRefine extension -- start your own distribution. +You can contribute code in three different ways: +- Fix minor bugs - you can check the issues flagged as [help wanted](https://github.com/OpenRefine/OpenRefine/labels/help%20wanted) or [good first issue](https://github.com/OpenRefine/OpenRefine/labels/good%20first%20issue) +- Develop an OpenRefine extension +- Start your own distribution or fork -All developers including new distributions and plugin developers are invited to leverage existing OpenRefine project managements tools to avoid the dispersion of the community in different communication channels. -- the [wiki](https://github.com/OpenRefine/OpenRefine/wiki) for shared documentation between distribution both user and [documentation for developer](https://github.com/OpenRefine/OpenRefine/wiki/Documentation-For-Developers) -- the [developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev) for technical questions, new feature development and anything code related. We invite you to share you idea first via the developer mailing list. Someone may be able to point out to existing development saving your hours of research and development. -- [OpenRefine github issue tracker](https://github.com/OpenRefine/OpenRefine/issues) for new feature and bug reports common with OpenRefine. +All developers including new distributions and plugin developers are invited to leverage the following OpenRefine project management areas to avoid the dispersion of the community in different communication channels. +- the [wiki](https://github.com/OpenRefine/OpenRefine/wiki) for shared documentation between both user docs and [documentation for developer](https://github.com/OpenRefine/OpenRefine/wiki/Documentation-For-Developers) +- the [developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev) for technical questions, new feature development and anything code related. We invite you to share you idea first via the developer mailing list. Someone may be able to point out to existing development saving you hours of research and development. +- [OpenRefine github issue tracker](https://github.com/OpenRefine/OpenRefine/issues) for requesting new features and bug reports. -### How to submit minor changes and bug fix +### How to submit PR's (pull requests), patches, and bug fixes +- Avoid merging master in your branch because it makes code review a lot harder. +- If you want to keep your branch up to date with our master, it would be nicer if you could just rebase your branch instead. That would keep the history a lot cleaner. +- Please avoid adding unrelated changes in the PR. Do a separate PR and rebase once they get merged can work really well. + If you make trivial changes, you can send them directly via a pull request. **Please make your changes in a new git branch and send your patch**, including appropriate test cases. We want to keep the quality of the trunk at a very high level, since this is ultimately where the Stable Releases are built from after bugs are fixed. Please take the time to test your changes (including travis-ci) before sending a pull request. -OpenRefine is volunteer supported. Pull Request are reviewed and merged by volunteers. All Pull Request will be answered, however it may take some time to get back to you. Thank you in advance for your patience. +OpenRefine is volunteer supported. Pull Requests are reviewed and merged by volunteers. All Pull Requests will be answered, however it may take some time to get back to you. Thank you in advance for your patience. -If you don't where to start and are looking for a bug to fix, please see our [issue list](https://github.com/OpenRefine/OpenRefine/issues). +If you don't know where to start and are looking for a bug to fix, please see our [issue list](https://github.com/OpenRefine/OpenRefine/issues). -### New functionalities via plugin +### New functionalities via extensions OpenRefine support a plugin architecture to extend its functionality. You can find more information on how to write extension on [our wiki](https://github.com/OpenRefine/OpenRefine/wiki/Write-An-Extension). Giuliano Tortoreto wrote a separate documentation detailling how to build extension for OpenRefine. A [LaTeX](https://github.com/OpenRefine/OpenRefineExtensionDoc) and [PDF version](https://github.com/OpenRefine/OpenRefineExtensionDoc/blob/master/main.pdf) are available. If you want to list your extension on the download page, please edit [this file](https://github.com/OpenRefine/openrefine.github.com/blob/master/download.md). -### New distribution +### New distributions -OpenRefine is already available in many different distribution (see the [download page](http://openrefine.org/download.html)). New distribution often package OpenRefine for a specific usage or port it. We are fine with new fork ([see discussion](https://groups.google.com/forum/#!msg/openrefine/pasNnMDJ3p8/LrZz_GiFCwAJ)) but we invite you to engage with the community to share your roadmap and progress. +OpenRefine is already available in many different distributions (see the [download page](http://openrefine.org/download.html)). New distributions often package OpenRefine for a specific usage or port it. We are fine with new forks ([see discussion](https://groups.google.com/forum/#!msg/openrefine/pasNnMDJ3p8/LrZz_GiFCwAJ)) but we invite you to engage with the community to share your roadmap and progress. -Github offer a powerful system to work between different repository and we encourage you to leverage it: -- You can cross reference issues and pull request between Github repository using `user/repository#number` ([see more here](https://github.com/blog/967-github-secrets#cross-repository-issue-references)) -- If you want to merge a Pull Request that is pending for review to you own repository check the pull request locally ([see more here](https://help.github.com/articles/checking-out-pull-requests-locally/)). +Github offers a powerful system to work between different repositories and we encourage you to leverage it: +- You can cross reference issues and pull requests between Github repository using `user/repository#number` ([see more here](https://github.com/blog/967-github-secrets#cross-repository-issue-references)) +- If you want to merge a Pull Request that is pending for review to your own repository check the pull request locally ([see more here](https://help.github.com/articles/checking-out-pull-requests-locally/)). -Don't forget to contribute to the upstream (main OpenRefine) repository so your changes can be reviewed and merge and to keep other developers aware of your progress. If you want to list your distribution on the download page, please edit [this file](https://github.com/OpenRefine/openrefine.github.com/blob/master/download.md). +Don't forget to contribute to the upstream (main OpenRefine) repository so your changes from your distribution can be reviewed and merged and to keep other developers aware of your progress. If you want to list your distribution on the download page, please edit [this file](https://github.com/OpenRefine/openrefine.github.com/blob/master/download.md). diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 000000000..e4db80f53 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,79 @@ +# OpenRefine Governance Model +Draft for discussion. Please submit pull requests to edit this document. Major changes will be discussed in the pull request comment section. + +## Summary / Overview +OpenRefine is a free, open-source, powerful tool for working with messy data. OpenRefine has a plugin architecture and is distributed under the [new BSD license](http://opensource.org/licenses/BSD-3-Clause) allowing modification, distribution and name changes. + +## Roles And Responsibilities +OpenRefine development is based on user consensus and open discussion between users. Anyone with an interest in the project can join the community, contribute to the project design, and participate in the decision making process. This document describes how that participation takes place. + +### Users +Users are community members who have a need for the project. Through their usage, they give the project a purpose. Users are encouraged to participate in the project life by providing feedback on how their needs are satisfied. + +Users activities include (but are not limited to): + +- Evangelisation and advocating for the project +- Informing developers of strengths and weaknesses from a new user perspective through the [user discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine) or the [issue list](https://github.com/OpenRefine/OpenRefine/issues?state=open) +- Providing moral support (a ‘thank you’ goes a long way) +- Writing tutorials + +How to become an OpenRefine user? [Download OpenRefine](http://openrefine.org/download.html) and start refining! + +### Contributors +Contributors are users contributing in concrete ways to the project. Contribution may be a one-time participation or occur over time. Contribution can take place in many ways: + +- Supporting new users via the [user discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine) +- Submitting patches to fix bugs or add features +- Identifying requirements +- Providing graphics and web design +- Writing and maintaining the [documentation](https://github.com/OpenRefine/OpenRefine/wiki) + +How to become an OpenRefine contributors? The [documentation for developers](https://github.com/OpenRefine/OpenRefine/wiki/Documentation-For-Developers) is a good place to start getting familiar with the code base. We invite contributors to share their feature development and patch ideas through the [developer discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev) or the [issue list](https://github.com/OpenRefine/OpenRefine/issues?state=open). It is recommended to start with small patches and share your code early so the community can provide feedback and guidance. + +### Committers +Committers are contributors who have shown dedication to OpenRefine, have a deep understanding of the code base and project strategy and work well with contributors and users. Committers have no more authorities than contributors, and they should engage with the community through the [developer discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev) or the [issue list](https://github.com/OpenRefine/OpenRefine/issues?state=open) regarding their intention. However, committers have earned enough trust from the community to have direct access to the project code base without having to submit pull request. Committers seek approval after the contribution is made, rather than before. + +Therefore committers: + +- Help contributors via the [developer discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev). +- Merge pull requests submitted by contributors +- Have direct access to the code base +- Nominate and vote for new committers + +Committers also participate in strategic planning, release planning, and approving changes to the governance model. + +How to become an OpenRefine Committers? Be a contributor and be nominated as a committer. OpenRefine selects and elects new committers using the [Apache model](https://community.apache.org/newcommitter.html). You may nominate yourself. Nomination should be sent to the [developer discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev) + + +## Decision Making Process + +From a practical point of view, the direction that the project takes is controlled by the contributors, not the users (unless they're contributors too). Development is made based on contributors and committers donating their time and money to the community. OpenRefine use the [Apache Decision Making](http://community.apache.org/committers/decisionMaking.html) process to decide the future of the project. + +### Lazy Concensus + +The [lazy consensus](http://community.apache.org/committers/lazyConsensus.html) is used for most of the contributions ranging from bug fixes to minor changes where the contributor assumes to have the support of the community to tackle the issue. + +If you are not sure you have the support of the community for a change, you can state a lazy consensus. Members of the community have then 72h to provide feedback on the proposal. + +In all cases silence is consent. + +### Consensus Building + +If you feel that lazy consensus isn't appropriate for your proposal, you can explicitly request for feedback on the [developer discussion list](https://groups.google.com/forum/?fromgroups#!forum/openrefine-dev). [Building concensus](http://community.apache.org/committers/consensusBuilding.html) help contributors and committers to gather feedback early on and pool the interest by the community for a new feature. + +Everyone is invited to express their opinion of any given feature or pull request. Support is expressed using: + +- +1 means: "I agree with this and will help make it happen." +- +0.9 means: 'This is a cool idea and I like it, but I don't have time/the skills necessary to help out.' +- +0 means: "I agree with this but probably won't make it happen, so my opinion is not that important." +- -0 means: "I don't agree with this, but I'm offering no alternative so my opinion is not that important." +- -0.9 means: 'I really don't like this, but I'm not going to stand in the way if everyone else wants to go ahead with it.' +- -1 means: "I don't agree and I am offering an alternative that I am able to help implement." + + +### Open Development Process + +OpenRefine development is based on user consensus and open discussion between users. Decision making must be done in a transparent, open fashion (ie. using discussion list and issue list). No decisions about the project’s direction, bug fixes or features may be done in private without community involvement and participation. Discussions must begin at the earliest possible point on a topic; the community’s participation is vital during the entire decision-making process. + +This document is licensed under a [Creative Commons Attribution-ShareAlike 2.0.](http://creativecommons.org/licenses/by-sa/2.0/) +This work is based upon ["Meritocratic Governance Model"](http://www.oss-watch.ac.uk/resources/meritocraticGovernanceModel) by University of Oxford, the [OWIN Project Governance model](https://docs.google.com/document/d/1mn3dY6zNyKBU3P_TWoR-RdYpScJDbsXU2TRhwpSAha8) and guidelines available for the [Apache Community](http://community.apache.org). diff --git a/LICENSE.txt b/LICENSE.txt index 930f1f4c5..58393644c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,143 +1,11 @@ -/* +Copyright 2010, Google Inc. All rights reserved. -Copyright 2010, Google Inc. -All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -*/ - -/* - * External Licenses - */ - -See the 'licenses' directory for a list of the licenses for the libraries we depend on, -ordered here by license: - -Apache License 2.0 ------------------- - -licenses/apache2.0.LICENSE.txt - calendar-parser (package com.google.refine.expr.util) - ant-tools - commons-lang - commons-codec - commons-fileupload - jackson - jdatapath - jetty - jetty-util - log4j - poi - poi-ooxml - poi-ooxml-schemas - servlet-api - xmlbeans - signpost - opencsv - textng - swc-parser-lazy - -Apache License 1.1 ------------------- -licenses/jrdf.LICENSE.txt - jrdf - -LGPL ----- - -licenses/marc4j.LICENSE.txt - marc4j - - -BSD ---- - -licenses/secondstring.LICENSE.txt - secondstring - -licenses/dom4j.LICENSE.txt - dom4j - -licenses/simile.LICENSE.txt - simile vicino - -licenses/simile-ajax.2.3.0.LICENSE.txt - Simile Ajax - -licenses/arithcode.LICENSE.txt - arithcode - -licenses/freebase_suggest.LICENSE.txt - freebase_suggest - -licenses/chrome_frame.LICENSE.txt - CFInstall - -MIT ---- - -licenses/jquery.LICENSE.txt - jquery - -licenses/jquery_ui.LICENSE.txt - jquery_ui - -licenses/jquery.eventstack.LICENSE.txt - jquery eventstack - -licenses/datejs.LICENSE.txt - datejs - -licenses/imgareaselect.LICENSE.txt - imgareaselect - -licenses/slf4j.LICENSE.txt - slf4j-api - slf4j-log4j12 - jcl-over-slf4j - -licenses/icu4j.LICENSE.txt - icu4j - -licenses/json.LICENSE.txt - json - -licenses/mockito.LICENSE.txt - mockito - -licenses/jsoup.LICENSE.txt - jsoup - -http://www.opensource.org/licenses/mit-license.php - jquery.cookie - -Others ------- - -Flag icon - http://pixel-mixer.com/category/free-icons/ +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index bf371739d..f21ab6e8d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ OpenRefine is a power tool that allows you to load data, understand it, clean it up, reconcile it, and augment it with data coming from the web. All with the comfort and privacy of your own computer. -[](http://openrefine.org) +[](http://openrefine.org) Download ----------------------- @@ -22,6 +22,7 @@ Contributing to the project --------------------------- * [Developers Guide & Architecture](https://github.com/OpenRefine/OpenRefine/wiki/Documentation-For-Developers) * [Contributing Guide](https://github.com/OpenRefine/OpenRefine/blob/master/CONTRIBUTING.md) +* [Project Governance](https://github.com/OpenRefine/OpenRefine/blob/master/GOVERNANCE.md) Contact us ---------- @@ -31,7 +32,7 @@ Contact us Licensing and legal issues -------------------------- OpenRefine is open source software and is licensed under the BSD license -located in the [LICENSE.txt](LICENSE.txt). See that file also for information on open source +located in the [LICENSE.txt](LICENSE.txt). See the folder `licenses` for information on open source libraries that OpenRefine depends on. Credits @@ -114,3 +115,4 @@ CURRENT CONTRIBUTORS - Matthias Findeisen - Mathieu Saby - Allan Nordhøy + - Tony Opara diff --git a/appveyor.yml b/appveyor.yml index fb77ba7d2..dfdb09941 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,15 @@ version: 1.0.{build} +services: +- mysql +- postgresql96 + init: - cmd: java -version 2>&1 | find "version" -clone_script: -- cmd: git clone --recursive https://github.com/OpenRefine/OpenRefine +clone_depth: 5 +skip_branch_with_pr: true + environment: ANT_HOME: C:\apache-ant-1.10.1 JAVA_HOME: C:\Program Files (x86)\Java\jdk1.8.0 @@ -20,17 +25,24 @@ cache: - apache-ant-1.10.1-bin.zip build: off -# scripts to run before build -before_build: +# scripts to run before test +before_test: - cmd: echo Running scripts before build... +- cmd: |- + PATH=C:\Program Files\PostgreSQL\9.6\bin\;C:\Program Files\MySQL\MySQL Server 5.7\bin\;%PATH% + SET MYSQL_PWD=Password12! + mysql -u root --password=Password12! -e "create database test_db;" + mysql -u root test_db --password=Password12! < extensions\database\test\conf\travis-mysql.sql + echo "localhost:*:test_db:postgres:Password12!" > C:\Program Files\PostgreSQL\9.6\pgpass.conf + echo "localhost:*:test_db:postgres:Password12!" > pgpass.conf + echo "localhost:*:test_db:postgres:Password12!" > %userprofile%\pgpass.conf + SET PGPASSFILE=C:\Program Files\PostgreSQL\9.6\pgpass.conf + SET PGPASSWORD=Password12! + SET PGUSER=postgres + createdb test_db + psql -U postgres test_db < extensions\database\test\conf\travis-pgsql.sql -build_script: -# - cmd: >- -# echo Running refine build... -# -# cd OpenRefine -# -# refine build + copy extensions\database\test\conf\appveyor_tests.xml extensions\database\test\conf\tests.xml test_script: - cmd: echo Running test_script... @@ -39,8 +51,6 @@ test_script: path - cd OpenRefine - refine server_test - + refine extensions_test diff --git a/broker/appengine/.classpath b/broker/appengine/.classpath deleted file mode 100644 index 4f8426eef..000000000 --- a/broker/appengine/.classpath +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/broker/appengine/.project b/broker/appengine/.project deleted file mode 100644 index 7b4860b54..000000000 --- a/broker/appengine/.project +++ /dev/null @@ -1,43 +0,0 @@ - - - grefine-appengine-broker - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.ui.externaltools.ExternalToolBuilder - full,incremental, - - - LaunchConfigHandle - <project>/.externalToolBuilders/com.google.gdt.eclipse.core.webAppProjectValidator.launch - - - - - com.google.appengine.eclipse.core.enhancerbuilder - - - - - org.eclipse.ui.externaltools.ExternalToolBuilder - full,incremental, - - - LaunchConfigHandle - <project>/.externalToolBuilders/com.google.appengine.eclipse.core.projectValidator.launch - - - - - - org.eclipse.jdt.core.javanature - com.google.appengine.eclipse.core.gaeNature - - diff --git a/broker/core/.classpath b/broker/core/.classpath deleted file mode 100644 index ef2854438..000000000 --- a/broker/core/.classpath +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/broker/core/.project b/broker/core/.project deleted file mode 100644 index 4538782c6..000000000 --- a/broker/core/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - grefine-broker - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/build.xml b/build.xml index 4ad005eba..1135d638b 100644 --- a/build.xml +++ b/build.xml @@ -140,6 +140,10 @@ + + + + diff --git a/extensions/build.xml b/extensions/build.xml index c2378e31e..ed44ecab7 100644 --- a/extensions/build.xml +++ b/extensions/build.xml @@ -13,6 +13,7 @@ + @@ -21,10 +22,12 @@ + + diff --git a/extensions/database/.classpath b/extensions/database/.classpath new file mode 100644 index 000000000..e0f342dff --- /dev/null +++ b/extensions/database/.classpath @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/.eclipse-pmd b/extensions/database/.eclipse-pmd new file mode 100644 index 000000000..4a705f215 --- /dev/null +++ b/extensions/database/.eclipse-pmd @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/extensions/database/.eslintrc.json b/extensions/database/.eslintrc.json new file mode 100644 index 000000000..d15f78754 --- /dev/null +++ b/extensions/database/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +} \ No newline at end of file diff --git a/extensions/sample/.project b/extensions/database/.project similarity index 76% rename from extensions/sample/.project rename to extensions/database/.project index 8fd50839d..350d6ee87 100644 --- a/extensions/sample/.project +++ b/extensions/database/.project @@ -1,6 +1,6 @@ - grefine-sample-extension + refine-database-extension @@ -20,10 +20,16 @@ + + ch.acanda.eclipse.pmd.builder.PMDBuilder + + + org.eclipse.jdt.core.javanature org.eclipse.wst.common.project.facet.core.nature org.eclipse.wst.jsdt.core.jsNature + ch.acanda.eclipse.pmd.builder.PMDNature diff --git a/extensions/gdata/.settings/.jsdtscope b/extensions/database/.settings/.jsdtscope similarity index 100% rename from extensions/gdata/.settings/.jsdtscope rename to extensions/database/.settings/.jsdtscope diff --git a/extensions/database/.settings/org.eclipse.jdt.core.prefs b/extensions/database/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d6721a52c --- /dev/null +++ b/extensions/database/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,280 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=33 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=1 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=1 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=8 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/extensions/database/.settings/org.eclipse.jdt.ui.prefs b/extensions/database/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..e3ddcd8c4 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Mon Sep 27 15:02:46 PDT 2010 +eclipse.preferences.version=1 +formatter_profile=_Google Refine +formatter_settings_version=11 diff --git a/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml b/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..8ca4d66e9 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 000000000..3bd5d0a48 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 000000000..05bd71b6e --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/extensions/database/.travis.yml b/extensions/database/.travis.yml new file mode 100644 index 000000000..800ed8b08 --- /dev/null +++ b/extensions/database/.travis.yml @@ -0,0 +1,24 @@ +language: java + +jdk: + - oraclejdk8 + +addons: + mariadb: '10.0' + +services: + - mysql + - postgresql + +before_install: + + - mysql -u root -e 'CREATE DATABASE test_db;' + - mysql -u root test_db < test/conf/travis-mysql.sql + + - psql -c 'CREATE DATABASE test_db;' -U postgres + - psql -U postgres test_db < test/conf/travis-pgsql.sql + + +script: + - ant test + diff --git a/extensions/database/README.md b/extensions/database/README.md new file mode 100644 index 000000000..28231b7df --- /dev/null +++ b/extensions/database/README.md @@ -0,0 +1,41 @@ +This project is an extension for OpenRefine that provides a way to import database data using JDBC. + + +INSTALL + +1. Before installing this extension download OpenRefine code from http://code.google.com/p/google-refine/source/checkout. + +2. Pull this extension's code into folder database under folder /extensions. +For more information on how to write a OpenRefine extensions and where to put the files see http://code.google.com/p/google-refine/wiki/WriteAnExtension + +The folder structure should resemble this: +grefine-all/ +----------/extensions +--------------/database +------------------/module +------------------/src +------------------build.xml +------------------README (this file) + +3. Update build.xml in folder /extensions with build and clean ant tasks for database: + + + + + + + + + + + + + + + + + + + + +4. If using Eclipse, make sure that you build project with ant diff --git a/extensions/database/build.xml b/extensions/database/build.xml new file mode 100644 index 000000000..b6ee98f30 --- /dev/null +++ b/extensions/database/build.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/licenses/jdbc-client.LICENSE.txt b/extensions/database/licenses/jdbc-client.LICENSE.txt new file mode 100644 index 000000000..a14b06b91 --- /dev/null +++ b/extensions/database/licenses/jdbc-client.LICENSE.txt @@ -0,0 +1,188 @@ +Copyright 2006 Google + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/extensions/gdata/module/MOD-INF/.gitignore b/extensions/database/module/MOD-INF/.gitignore similarity index 100% rename from extensions/gdata/module/MOD-INF/.gitignore rename to extensions/database/module/MOD-INF/.gitignore diff --git a/extensions/database/module/MOD-INF/controller.js b/extensions/database/module/MOD-INF/controller.js new file mode 100644 index 000000000..5df821fbf --- /dev/null +++ b/extensions/database/module/MOD-INF/controller.js @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Controller for JDBC Database extension. + * + * This is run in the Butterfly (ie Refine) server context using the Rhino + * Javascript interpreter. + */ + +var html = "text/html"; +var encoding = "UTF-8"; +var version = "0.1"; +var ClientSideResourceManager = Packages.com.google.refine.ClientSideResourceManager; + +var logger = Packages.org.slf4j.LoggerFactory.getLogger("database-extension"), +File = Packages.java.io.File, +refineServlet = Packages.com.google.refine.RefineServlet; + +/* + * Register our custom commands. + */ +function registerCommands() { + + logger.info("Registering Database Extension Commands......"); + var RS = Packages.com.google.refine.RefineServlet; + RS.registerCommand(module, "test-connect", Packages.com.google.refine.extension.database.cmd.TestConnectCommand()); + RS.registerCommand(module, "connect", Packages.com.google.refine.extension.database.cmd.ConnectCommand()); + RS.registerCommand(module, "saved-connection", Packages.com.google.refine.extension.database.cmd.SavedConnectionCommand()); + RS.registerCommand(module, "execute-query", Packages.com.google.refine.extension.database.cmd.ExecuteQueryCommand()); + RS.registerCommand(module, "test-query", Packages.com.google.refine.extension.database.cmd.TestQueryCommand()); + logger.info("Database Extension Command Registeration done!!"); +} + +function registerOperations() { + logger.info("Database Operations Registered successfully..."); +} + +function registerFunctions() { + logger.info("Database Functions Registered successfully..."); +} + + +/* + * Function invoked to initialize the extension. + */ +function init() { + + logger.info("Initializing OpenRefine Database..."); + logger.info("Database Extension Mount point " + module.getMountPoint()); + + registerCommands(); + registerOperations(); + registerFunctions(); + + + // Register importer and exporter + var IM = Packages.com.google.refine.importing.ImportingManager; + + IM.registerController( + module, + "database-import-controller", + new Packages.com.google.refine.extension.database.DatabaseImportController() + ); + + + // Script files to inject into /index page + ClientSideResourceManager.addPaths( + "index/scripts", + module, + [ + "scripts/index/jquery.contextMenu.min.js", + "scripts/index/jquery.ui.position.min.js", + "scripts/database-extension.js", + "scripts/index/database-import-controller.js", + "scripts/index/database-source-ui.js" + ] + ); + // Style files to inject into /index page + ClientSideResourceManager.addPaths( + "index/styles", + module, + [ + "styles/jquery.contextMenu.css", + "styles/pure.css", + "styles/bootstrap.css", + "styles/database-import.less" + + ] + ); + + // Script files to inject into /project page + ClientSideResourceManager.addPaths( + "project/scripts", + module, + [ + "scripts/database-extension.js", + "scripts/project/database-exporters.js" + ] + ); +} + +/* + * Function invoked to handle each request in a custom way. + */ +function process(path, request, response) { + + + var method = request.getMethod(); + + logger.info('receiving request for ' + path); + logger.info('receiving method for ' + method); + + if (path == "/" || path == "") { + var context = {}; + context.version = version; + send(request, response, "index.vt", context); + } +} + +function send(request, response, template, context) { + butterfly.sendTextFromTemplate(request, response, context, template, encoding, html); +} diff --git a/extensions/database/module/MOD-INF/dbextension.properties b/extensions/database/module/MOD-INF/dbextension.properties new file mode 100644 index 000000000..db482f687 --- /dev/null +++ b/extensions/database/module/MOD-INF/dbextension.properties @@ -0,0 +1,3 @@ +# Batch size for import data +preview.batchSize = 100 +create.batchSize = 1000 diff --git a/main/webapp/WEB-INF/lib/commons-collections-3.2.1.jar b/extensions/database/module/MOD-INF/lib/commons-collections-3.2.1.jar similarity index 100% rename from main/webapp/WEB-INF/lib/commons-collections-3.2.1.jar rename to extensions/database/module/MOD-INF/lib/commons-collections-3.2.1.jar diff --git a/main/webapp/WEB-INF/lib/commons-io-1.4.jar b/extensions/database/module/MOD-INF/lib/commons-io-1.4.jar similarity index 100% rename from main/webapp/WEB-INF/lib/commons-io-1.4.jar rename to extensions/database/module/MOD-INF/lib/commons-io-1.4.jar diff --git a/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar b/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar new file mode 100644 index 000000000..82887fb3e Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar b/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar new file mode 100644 index 000000000..674aea920 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar b/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar new file mode 100644 index 000000000..ad32285ab Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar b/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar new file mode 100644 index 000000000..4aef35e2f Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar b/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar new file mode 100644 index 000000000..bb4fe1da1 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar b/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar new file mode 100644 index 000000000..0f2073fc7 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar b/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar new file mode 100644 index 000000000..c22a7e6d5 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar differ diff --git a/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar b/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar new file mode 100644 index 000000000..dfd5856d0 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar b/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar new file mode 100644 index 000000000..7575fec64 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar b/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar new file mode 100644 index 000000000..2f2e32d51 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar differ diff --git a/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar b/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar new file mode 100644 index 000000000..08a54b105 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar differ diff --git a/extensions/database/module/MOD-INF/module.properties b/extensions/database/module/MOD-INF/module.properties new file mode 100644 index 000000000..2b0cef961 --- /dev/null +++ b/extensions/database/module/MOD-INF/module.properties @@ -0,0 +1,7 @@ +name = database +description = Database importer/exporter for OpenRefine +templating.macros = macros.vm +requires = core + +# Use our custom class for a module implementation. +module-impl = com.google.refine.extension.database.DatabaseModuleImpl diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot b/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg b/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf b/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/extensions/database/module/images/more-option-horiz-16.png b/extensions/database/module/images/more-option-horiz-16.png new file mode 100644 index 000000000..0620d0a57 Binary files /dev/null and b/extensions/database/module/images/more-option-horiz-16.png differ diff --git a/extensions/database/module/images/more_option-vert-16.png b/extensions/database/module/images/more_option-vert-16.png new file mode 100644 index 000000000..fa41b4f3b Binary files /dev/null and b/extensions/database/module/images/more_option-vert-16.png differ diff --git a/extensions/database/module/index.vt b/extensions/database/module/index.vt new file mode 100644 index 000000000..147cb507b --- /dev/null +++ b/extensions/database/module/index.vt @@ -0,0 +1,20 @@ +#* + * Access this page at the URL /extension/database/ + *# + + + OpenRefine Database Extension v$version + + +

OpenRefine DATABASE Extension v$version

+

by Tony Opara

+

The JDBC extension allows OpenRefine to read + (and write, soon) data from JDBC compatible databases(MySQL, PostgreSQL, MariaDB). +

+

Known Limitations

+
    +
  • No write support
  • + +
+ + \ No newline at end of file diff --git a/extensions/database/module/langs/translation-en.json b/extensions/database/module/langs/translation-en.json new file mode 100644 index 000000000..c29b6930d --- /dev/null +++ b/extensions/database/module/langs/translation-en.json @@ -0,0 +1,58 @@ +{ + "database-import": { + "title": "Database Servers", + "preparing": "Preparing Result ...", + "checking": "Validating Query ...", + "creating": "Creating project ..." + }, + "database-source": { + "alert-host": "You must specify a Database Host", + "alert-port": "You must specify a Database Port", + "alert-user": "You must specify a Database User", + "alert-password": "You must specify a Database Password", + "alert-connection-name": "You must specify a Connection Name", + "alert-initial-database": "You must specify an Initial Database", + "alert-query": "You must specify a valid Query", + "alert-invalid-query-keyword": "Query cannot contain Data Manipulation keyword:", + "alert-invalid-query-select": "Query must start with SELECT Keyword", + "form-validation-failure" : "New Connection From is Invalid!", + "alert-connection-edit": "Connection edited successfully", + "connectionNameLabel": "Name:", + "databaseTypeLabel": "Type:", + "databaseHostLabel": "Host:", + "databasePortLabel": "Port:", + "databaseUserLabel": "User:", + "databasePasswordLabel": "Password:", + "databaseNameLabel": "Database:", + "databaseSchemaLabel": "Schema:", + "databaseTestButton": "Test", + "databaseSaveButton": "Save", + "databaseConnectButton": "Connect", + "newConnectionButtonDiv": "New Connection", + "savedConnectionSpan": "Saved Connections" + + + }, + "database-parsing": { + "start-over": "« Start Over", + "conf-pars": "Configure Parsing Options", + "proj-name": "Project name", + "create-proj": "Create Project »", + "updating-preview": "Updating preview ...", + "worksheet": "Worksheets", + "option": "Options", + "preview-button": "Update Preview", + "ignore-first": "Ignore first", + "ignore": "line(s) at beginning of file", + "parse-next": "Parse next", + "parse": "line(s) as column headers", + "discard-next": "Discard initial", + "discard": "row(s) of data", + "limit-next": "Load at most", + "limit": "row(s) of data", + "store-row": "Store blank rows", + "store-cell": "Store blank cells as nulls" + } + +} + \ No newline at end of file diff --git a/extensions/database/module/langs/translation-fr.json b/extensions/database/module/langs/translation-fr.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-fr.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/langs/translation-he.json b/extensions/database/module/langs/translation-he.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-he.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/langs/translation-it.json b/extensions/database/module/langs/translation-it.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-it.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/macros.vm b/extensions/database/module/macros.vm new file mode 100644 index 000000000..18a83bfce --- /dev/null +++ b/extensions/database/module/macros.vm @@ -0,0 +1,14 @@ +#* + This file contains common velocity macros used in all .vt files. + For Velocity documentation, see: + + http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html +*# + +#macro( makeAList $list ) +
    + #foreach($item in $list) +
  • $item
  • + #end +
+#end \ No newline at end of file diff --git a/extensions/database/module/scripts/database-extension.js b/extensions/database/module/scripts/database-extension.js new file mode 100644 index 000000000..4c4347935 --- /dev/null +++ b/extensions/database/module/scripts/database-extension.js @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var DatabaseExtension = {}; + +DatabaseExtension.currentConnection = {}; + +DatabaseExtension.handleSavedConnectionClicked = function(menuKey, connectionName) { + var jdbcConnectionInfo = {}; + jdbcConnectionInfo.connectionName = connectionName; + + if(menuKey === "edit"){ + DatabaseExtension.handleEditConnectionClicked(connectionName); + + }else if(menuKey === "delete"){ + DatabaseExtension.handleDeleteConnectionClicked(connectionName); + + }else if(menuKey === "connect"){ + DatabaseExtension.handleConnectClicked(connectionName); + } + +}; + + +DatabaseExtension.handleConnectClicked = function(connectionName) { + + $.get( + "command/database/saved-connection" + '?' + $.param({"connectionName": connectionName}), + null, + + function(savedDatabaseConfig) { + + if(savedDatabaseConfig){ + + var savedConfig = savedDatabaseConfig.savedConnections[0]; + var databaseConfig = {}; + databaseConfig.connectionName = savedConfig.connectionName; + databaseConfig.databaseType = savedConfig.databaseType; + databaseConfig.databaseServer = savedConfig.databaseHost; + databaseConfig.databasePort = savedConfig.databasePort; + databaseConfig.databaseUser = savedConfig.databaseUser; + databaseConfig.databasePassword = savedConfig.databasePassword; + databaseConfig.initialDatabase = savedConfig.databaseName; + databaseConfig.initialSchema = savedConfig.databaseSchema; + + $.post( + "command/database/connect", + databaseConfig, + + function(databaseInfo) { + + if(databaseInfo){ + DatabaseExtension.currentConnection.databaseInfo; + $( "#currentConnectionNameInput" ).val(databaseConfig.connectionName); + $( "#currentDatabaseTypeInput" ).val(databaseConfig.databaseType); + $( "#currentDatabaseUserInput" ).val(databaseConfig.databaseUser); + $( "#currentDatabasePasswordInput" ).val(databaseConfig.databasePassword); + $( "#currentDatabaseHostInput" ).val(databaseConfig.databaseServer); + $( "#currentDatabasePortInput" ).val(databaseConfig.databasePort); + $( "#currentInitialDatabaseInput" ).val(databaseConfig.initialDatabase); + + var connectionParam = "Connection[" + databaseConfig.connectionName + "] :: " + + "jdbc:" + + databaseConfig.databaseType + "://" + + databaseConfig.databaseServer + ":" + + databaseConfig.databasePort + "/" + + databaseConfig.initialDatabase; + + $( "#connectionParameterSpan" ).text(connectionParam); + $( "#newConnectionDiv" ).hide(); + $( "#sqlEditorDiv" ).show(); + + }else{ + window.alert("Unable to establish connection to database"); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + + } + + }, + "json" + ); +}; + +DatabaseExtension.handleDeleteConnectionClicked = function(connectionName) { + $.ajax({ + url: 'command/database/saved-connection' + '?' + $.param({"connectionName": connectionName}), + type: 'DELETE', + contentType:'application/json', + data: null, + success: function(settings) { + if(settings){ + + $( "#menuListUl" ).empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + $( "#menuListUl" ).append(items.join('')); + } + } + }).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); +} + +DatabaseExtension.handleEditConnectionClicked = function(connectionName) { + + $.get( + "command/database/saved-connection" + '?' + $.param({"connectionName": connectionName}), + null, + function(savedDatabaseConfig) { + + if(savedDatabaseConfig){ + + var savedConfig = savedDatabaseConfig.savedConnections[0]; + $( "#connectionName" ).val(savedConfig.connectionName); + $( "#databaseTypeSelect" ).val(savedConfig.databaseType); + $( "#databaseHost" ).val(savedConfig.databaseHost); + $( "#databasePort" ).val(savedConfig.databasePort); + $( "#databaseUser" ).val(savedConfig.databaseUser); + $( "#databasePassword" ).val(savedConfig.databasePassword); + $( "#initialDatabase" ).val(savedConfig.databaseName); + $( "#initialSchema" ).val(savedConfig.databaseSchema); + $( "#newConnectionControlDiv" ).hide(); + $( "#editConnectionControlDiv" ).show(); + $( "#newConnectionDiv" ).show(); + $('#sqlEditorDiv').hide(); + $("#connectionName").attr('readonly', 'readonly'); + + } + + }, + "json" + ); + +} diff --git a/extensions/database/module/scripts/index/database-import-controller.js b/extensions/database/module/scripts/index/database-import-controller.js new file mode 100644 index 000000000..e76dfe845 --- /dev/null +++ b/extensions/database/module/scripts/index/database-import-controller.js @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//Internationalization init +var lang = navigator.language.split("-")[0] + || navigator.userLanguage.split("-")[0]; +var dictionary = ""; +$.ajax({ + url : "command/core/load-language?", + type : "POST", + async : false, + data : { + module : "database", + }, + success : function(data) { + dictionary = data; + } +}); +$.i18n.setDictionary(dictionary); +// End internationalization + +Refine.DatabaseImportController = function(createProjectUI) { + this._createProjectUI = createProjectUI; + + this._parsingPanel = createProjectUI.addCustomPanel(); + + createProjectUI.addSourceSelectionUI({ + label: "Database", + id: "database-source", + ui: new Refine.DatabaseSourceUI(this) + }); + +}; +Refine.CreateProjectUI.controllers.push(Refine.DatabaseImportController); + +Refine.DatabaseImportController.prototype.startImportingDocument = function(queryInfo) { + var dismiss = DialogSystem.showBusy($.i18n._('database-import')["preparing"]); + //alert(queryInfo.query); + var self = this; + + $.post( + "command/core/create-importing-job", + null, + function(data) { + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "subCommand": "initialize-parser-ui" + }), + queryInfo, + + function(data2) { + dismiss(); + + if (data2.status == 'ok') { + self._queryInfo = queryInfo; + self._jobID = data.jobID; + self._options = data2.options; + + self._showParsingPanel(); + + } else { + alert(data2.message); + } + }, + "json" + ); + }, + "json" + ); +}; + +Refine.DatabaseImportController.prototype.getOptions = function() { + var options = { + + }; + + var parseIntDefault = function(s, def) { + try { + var n = parseInt(s); + if (!isNaN(n)) { + return n; + } + } catch (e) { + // Ignore + } + return def; + }; + + + if (this._parsingPanelElmts.skipCheckbox[0].checked) { + options.skipDataLines = parseIntDefault(this._parsingPanelElmts.skipInput[0].value, 0); + } else { + options.skipDataLines = 0; + } + if (this._parsingPanelElmts.limitCheckbox[0].checked) { + options.limit = parseIntDefault(this._parsingPanelElmts.limitInput[0].value, -1); + } else { + options.limit = -1; + } + options.storeBlankRows = this._parsingPanelElmts.storeBlankRowsCheckbox[0].checked; + options.storeBlankCellsAsNulls = this._parsingPanelElmts.storeBlankCellsAsNullsCheckbox[0].checked; + + return options; +}; + +Refine.DatabaseImportController.prototype._showParsingPanel = function() { + var self = this; + + this._parsingPanel.unbind().empty().html( + DOM.loadHTML("database",'scripts/index/database-parsing-panel.html')); + + this._parsingPanelElmts = DOM.bind(this._parsingPanel); + + this._parsingPanelElmts.startOverButton.html($.i18n._('database-parsing')["start-over"]); + this._parsingPanelElmts.database_conf_pars.html($.i18n._('database-parsing')["conf-pars"]); + this._parsingPanelElmts.database_proj_name.html($.i18n._('database-parsing')["proj-name"]); + this._parsingPanelElmts.createProjectButton.html($.i18n._('database-parsing')["create-proj"]); + this._parsingPanelElmts.database_options.html($.i18n._('database-parsing')["option"]); + this._parsingPanelElmts.previewButton.html($.i18n._('database-parsing')["preview-button"]); + this._parsingPanelElmts.database_updating.html($.i18n._('database-parsing')["updating-preview"]); + this._parsingPanelElmts.database_discard_next.html($.i18n._('database-parsing')["discard-next"]); + this._parsingPanelElmts.database_discard.html($.i18n._('database-parsing')["discard"]); + this._parsingPanelElmts.database_limit_next.html($.i18n._('database-parsing')["limit-next"]); + this._parsingPanelElmts.database_limit.html($.i18n._('database-parsing')["limit"]); + this._parsingPanelElmts.database_store_row.html($.i18n._('database-parsing')["store-row"]); + this._parsingPanelElmts.database_store_cell.html($.i18n._('database-parsing')["store-cell"]); + + if (this._parsingPanelResizer) { + $(window).unbind('resize', this._parsingPanelResizer); + } + + this._parsingPanelResizer = function() { + var elmts = self._parsingPanelElmts; + var width = self._parsingPanel.width(); + var height = self._parsingPanel.height(); + var headerHeight = elmts.wizardHeader.outerHeight(true); + var controlPanelHeight = 250; + + elmts.dataPanel + .css("left", "0px") + .css("top", headerHeight + "px") + .css("width", (width - DOM.getHPaddings(elmts.dataPanel)) + "px") + .css("height", (height - headerHeight - controlPanelHeight - DOM.getVPaddings(elmts.dataPanel)) + "px"); + elmts.progressPanel + .css("left", "0px") + .css("top", headerHeight + "px") + .css("width", (width - DOM.getHPaddings(elmts.progressPanel)) + "px") + .css("height", (height - headerHeight - controlPanelHeight - DOM.getVPaddings(elmts.progressPanel)) + "px"); + + elmts.controlPanel + .css("left", "0px") + .css("top", (height - controlPanelHeight) + "px") + .css("width", (width - DOM.getHPaddings(elmts.controlPanel)) + "px") + .css("height", (controlPanelHeight - DOM.getVPaddings(elmts.controlPanel)) + "px"); + }; + + $(window).resize(this._parsingPanelResizer); + this._parsingPanelResizer(); + + this._parsingPanelElmts.startOverButton.click(function() { + // explicitly cancel the import job + Refine.CreateProjectUI.cancelImportingJob(self._jobID); + + delete self._jobID; + delete self._options; + + self._createProjectUI.showSourceSelectionPanel(); + }); + + this._parsingPanelElmts.createProjectButton.click(function() { self._createProject(); }); + this._parsingPanelElmts.previewButton.click(function() { self._updatePreview(); }); + //alert("datetime::" + $.now()); + //this._parsingPanelElmts.projectNameInput[0].value = this._queryInfo.connectionName + "_" + this._queryInfo.databaseUser + "_" + $.now(); + this._parsingPanelElmts.projectNameInput[0].value = this._queryInfo.databaseServer + "_" + this._queryInfo.initialDatabase + "_" + $.now(); + + + if (this._options.limit > 0) { + this._parsingPanelElmts.limitCheckbox.prop("checked", true); + this._parsingPanelElmts.limitInput[0].value = this._options.limit.toString(); + } + if (this._options.skipDataLines > 0) { + this._parsingPanelElmts.skipCheckbox.prop("checked", true); + this._parsingPanelElmts.skipInput.value[0].value = this._options.skipDataLines.toString(); + } + if (this._options.storeBlankRows) { + this._parsingPanelElmts.storeBlankRowsCheckbox.prop("checked", true); + } + if (this._options.storeBlankCellsAsNulls) { + this._parsingPanelElmts.storeBlankCellsAsNullsCheckbox.prop("checked", true); + } + + var onChange = function() { + self._scheduleUpdatePreview(); + }; + this._parsingPanel.find("input").bind("change", onChange); + this._parsingPanel.find("select").bind("change", onChange); + + this._createProjectUI.showCustomPanel(this._parsingPanel); + this._updatePreview(); +}; + +Refine.DatabaseImportController.prototype._scheduleUpdatePreview = function() { + if (this._timerID != null) { + window.clearTimeout(this._timerID); + this._timerID = null; + } + + var self = this; + this._timerID = window.setTimeout(function() { + self._timerID = null; + self._updatePreview(); + }, 500); // 0.5 second + }; + +Refine.DatabaseImportController.prototype._updatePreview = function() { + var self = this; + // alert("query::" + this._queryInfo.query); + this._parsingPanelElmts.dataPanel.hide(); + this._parsingPanelElmts.progressPanel.show(); + this._queryInfo.options = JSON.stringify(this.getOptions()); + //alert("options:" + this._queryInfo.options); + + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "jobID": this._jobID, + "subCommand": "parse-preview" + }), + + this._queryInfo, + + function(result) { + if (result.status == "ok") { + self._getPreviewData(function(projectData) { + self._parsingPanelElmts.progressPanel.hide(); + self._parsingPanelElmts.dataPanel.show(); + + new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty()); + }); + } else { + + alert('Errors:\n' + (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job)); + self._parsingPanelElmts.progressPanel.hide(); + + Refine.CreateProjectUI.cancelImportingJob(self._jobID); + + delete self._jobID; + delete self._options; + + self._createProjectUI.showSourceSelectionPanel(); + + + } + }, + "json" + ); + }; + +Refine.DatabaseImportController.prototype._getPreviewData = function(callback, numRows) { + var self = this; + var result = {}; + + $.post( + "command/core/get-models?" + $.param({ "importingJobID" : this._jobID }), + null, + function(data) { + for (var n in data) { + if (data.hasOwnProperty(n)) { + result[n] = data[n]; + } + } + + $.post( + "command/core/get-rows?" + $.param({ + "importingJobID" : self._jobID, + "start" : 0, + "limit" : numRows || 100 // More than we parse for preview anyway + }), + null, + function(data) { + result.rowModel = data; + callback(result); + }, + "jsonp" + ); + }, + "json" + ); +}; + +Refine.DatabaseImportController.prototype._createProject = function() { + var projectName = $.trim(this._parsingPanelElmts.projectNameInput[0].value); + if (projectName.length == 0) { + window.alert("Please name the project."); + this._parsingPanelElmts.projectNameInput.focus(); + return; + } + + var self = this; + var options = this.getOptions(); + options.projectName = projectName; + + this._queryInfo.options = JSON.stringify(options); + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "jobID": this._jobID, + "subCommand": "create-project" + }), + this._queryInfo, + function(o) { + if (o.status == 'error') { + alert(o.message); + } else { + var start = new Date(); + var timerID = window.setInterval( + function() { + self._createProjectUI.pollImportJob( + start, + self._jobID, + timerID, + function(job) { + return "projectID" in job.config; + }, + function(jobID, job) { + //alert("jobID::" + jobID + " job :" + job); + window.clearInterval(timerID); + Refine.CreateProjectUI.cancelImportingJob(jobID); + document.location = "project?project=" + job.config.projectID; + }, + function(job) { + alert(Refine.CreateProjectUI.composeErrorMessage(job)); + } + ); + }, + 1000 + ); + self._createProjectUI.showImportProgressPanel($.i18n._('database-import')["creating"], function() { + // stop the timed polling + window.clearInterval(timerID); + + // explicitly cancel the import job + Refine.CreateProjectUI.cancelImportingJob(jobID); + + self._createProjectUI.showSourceSelectionPanel(); + }); + } + }, + "json" + ); +}; diff --git a/extensions/database/module/scripts/index/database-import-form.html b/extensions/database/module/scripts/index/database-import-form.html new file mode 100644 index 000000000..de1f96f08 --- /dev/null +++ b/extensions/database/module/scripts/index/database-import-form.html @@ -0,0 +1,144 @@ +
    + +
    + +
    +
    +
    +
    +
    + +
    + Saved Connections + +
    + +
    +
    + +
    + +
    + + +
    +
    + + + New Connection Editor + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    + + + +
    + + + +
    +
    + + + + + +
    + + + +
    + +
    + +
    + + +
    + \ No newline at end of file diff --git a/extensions/database/module/scripts/index/database-parsing-panel.html b/extensions/database/module/scripts/index/database-parsing-panel.html new file mode 100644 index 000000000..4ac5a1c75 --- /dev/null +++ b/extensions/database/module/scripts/index/database-parsing-panel.html @@ -0,0 +1,53 @@ +
    +
    + + + + + + + + +
    +
    +
    +
    +
    + + +
    +
    +
    + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/extensions/database/module/scripts/index/database-source-ui.js b/extensions/database/module/scripts/index/database-source-ui.js new file mode 100644 index 000000000..f93ad77ad --- /dev/null +++ b/extensions/database/module/scripts/index/database-source-ui.js @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +$(function(){ + $.contextMenu({ + selector: '.context-menu-one', + trigger: 'left', + build: function($trigger, e) { + + return { + callback: function(key, options) { + var m = "clicked: " + key; + DatabaseExtension.handleSavedConnectionClicked(key, $(this).text()); + + }, + + items: { + "edit": {name: " Edit "}, + "sep0": "", + "delete": {name: " Delete "}, + "sep1": "---------", + "connect": {name: " Connect "}, + "dummy": {name: "", icon: ""} + } + }; + } + }); +}); + +Refine.DatabaseSourceUI = function(controller) { + this._controller = controller; +}; + +Refine.DatabaseSourceUI.prototype.attachUI = function(body) { + this._body = body; + + this._body.html(DOM.loadHTML("database", "scripts/index/database-import-form.html")); + this._elmts = DOM.bind(this._body); + var self = this; + + $('#database-title').text($.i18n._("database-import")["title"]); + $('#connectionNameLabel').html($.i18n._("database-source")["connectionNameLabel"]); + $('#databaseTypeLabel').html($.i18n._("database-source")["databaseTypeLabel"]); + $('#databaseHostLabel').text($.i18n._("database-source")["databaseHostLabel"]); + $('#databasePortLabel').text($.i18n._("database-source")["databasePortLabel"]); + $('#databaseUserLabel').text($.i18n._("database-source")["databaseUserLabel"]); + $('#databasePasswordLabel').text($.i18n._("database-source")["databasePasswordLabel"]); + $('#databaseNameLabel').text($.i18n._("database-source")["databaseNameLabel"]); + $('#databaseSchemaLabel').text($.i18n._("database-source")["databaseSchemaLabel"]); + $('#databaseTestButton').text($.i18n._("database-source")["databaseTestButton"]); + $('#databaseSaveButton').text($.i18n._("database-source")["databaseSaveButton"]); + $('#databaseConnectButton').text($.i18n._("database-source")["databaseConnectButton"]); + $('#newConnectionButtonDiv').text($.i18n._("database-source")["newConnectionButtonDiv"]); + $('#savedConnectionSpan').text($.i18n._("database-source")["savedConnectionSpan"]); + + + this._elmts.newConnectionButton.click(function(evt) { + self._resetDatabaseImportForm(); + $( "#newConnectionDiv" ).show(); + $( "#sqlEditorDiv" ).hide(); + // self._body.find('.newConnectionDiv').show(); + // self._body.find('.sqlEditorDiv').hide(); + + }); + + + this._elmts.databaseTypeSelect.change(function(event) { + var type = $( "#databaseTypeSelect" ).val(); + if(type === "postgresql"){ + $( "#databaseUser" ).val("postgres"); + $( "#databasePort" ).val("5432"); + }else if(type === "mysql"){ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + }else if(type === "mariadb"){ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + }else{ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + } + }); + + this._elmts.testDatabaseButton.click(function(evt) { + + if(self._validateNewConnectionForm() === true){ + self._testDatabaseConnect(self._getConnectionInfo()); + } + + }); + + this._elmts.databaseConnectButton.click(function(evt) { + + if(self._validateNewConnectionForm() === true){ + self._connect(self._getConnectionInfo()); + } + + + }); + + this._elmts.saveConnectionButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + if (connectionNameInput.length === 0) { + window.alert($.i18n._('database-source')["alert-connection-name"]); + } else{ + self._saveConnection(self._getConnectionInfo()); + } + + } + + }); + + this._elmts.executeQueryButton.click(function(evt) { + var jdbcQueryInfo = {}; + jdbcQueryInfo.connectionName = $( "#currentConnectionNameInput" ).val(); + jdbcQueryInfo.databaseType = $( "#currentDatabaseTypeInput" ).val(); + jdbcQueryInfo.databaseServer = $( "#currentDatabaseHostInput" ).val(); + jdbcQueryInfo.databasePort = $( "#currentDatabasePortInput" ).val(); + jdbcQueryInfo.databaseUser = $( "#currentDatabaseUserInput" ).val(); + jdbcQueryInfo.databasePassword = $( "#currentDatabasePasswordInput" ).val(); + jdbcQueryInfo.initialDatabase = $( "#currentInitialDatabaseInput" ).val(); + jdbcQueryInfo.query = $.trim($( "#queryTextArea" ).val()); + +// if(jdbcQueryInfo.query && jdbcQueryInfo.query.length > 0 ) { +// self._executeQuery(jdbcQueryInfo); +// }else{ +// window.alert($.i18n._('database-source')["alert-query"]); +// } + + if(self.validateQuery(jdbcQueryInfo.query)) { + self._executeQuery(jdbcQueryInfo); + } + + + + + }); + + + + this._elmts.editConnectionButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + if (connectionNameInput.length === 0) { + window.alert($.i18n._('database-source')["alert-connection-name"]); + } else{ + self._editConnection(self._getConnectionInfo()); + } + + } + + }); + + this._elmts.cancelEditConnectionButton.click(function(evt) { + self._resetDatabaseImportForm(); + + }); + + //load saved connections from settings file in user home directory + self._loadSavedConnections(); + +};//end Refine.createUI + + +Refine.DatabaseSourceUI.prototype.focus = function() { +}; + + +Refine.DatabaseSourceUI.prototype.validateQuery = function(query) { + //alert("query::" + query); + if(!query || query.length <= 0 ) { + window.alert($.i18n._('database-source')["alert-query"]); + return false; + } + + var allCapsQuery = query.toUpperCase(); + + if(allCapsQuery.indexOf('DROP') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " DROP"); + return false; + }else if(allCapsQuery.indexOf('TRUNCATE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " TRUNCATE"); + return false; + }else if(allCapsQuery.indexOf('DELETE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " DELETE"); + return false; + }else if(allCapsQuery.indexOf('ROLLBACK') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " ROLLBACK"); + return false; + }else if(allCapsQuery.indexOf('SHUTDOWN') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " SHUTDOWN"); + return false; + }else if(allCapsQuery.indexOf('INSERT') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " INSERT"); + return false; + }else if(allCapsQuery.indexOf('ALTER') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " ALTER"); + return false; + }else if(allCapsQuery.indexOf('UPDATE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " UPDATE"); + return false; + }else if(allCapsQuery.indexOf('LIMIT') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " LIMIT"); + return false; + } + +// if ((allCapsQuery.indexOf('DROP') > -1) || (allCapsQuery.indexOf('TRUNCATE') > -1) || +// (allCapsQuery.indexOf('DELETE') > -1) || (allCapsQuery.indexOf('ROLLBACK') > -1) +// || (allCapsQuery.indexOf('SHUTDOWN') > -1) || (allCapsQuery.indexOf('INSERT') > -1) +// || (allCapsQuery.indexOf('ALTER') > -1) || (allCapsQuery.indexOf('UPDATE') > -1)) +// { +// window.alert($.i18n._('database-source')["alert-invalid-query-keyword"]); +// return false; +// } + + if(!allCapsQuery.startsWith('SELECT')) { + window.alert($.i18n._('database-source')["alert-invalid-query-select"]); + return false; + } + + return true; +}; + +Refine.DatabaseSourceUI.prototype._editConnection = function(connectionInfo) { + //alert("database user:" + connectionInfo.databaseUser); + var self = this; + $.ajax({ + url: 'command/database/saved-connection', + type: 'PUT', + contentType:'application/x-www-form-urlencoded', + data: connectionInfo, + success: function(settings) { + if(settings){ + $( "#menuListUl" ).empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + $( "#menuListUl" ).append(items.join('')); + window.alert($.i18n._('database-source')["alert-connection-edit"]); + } + } + }).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) { + var self = this; + //remove start line + + var dismiss = DialogSystem.showBusy($.i18n._('database-import')["checking"]); + //$("#executeQueryBtn").text('Please wait ...').attr('disabled','disabled'); + + $.post( + "command/database/test-query", + jdbcQueryInfo, + function(jdbcConnectionResult) { + // $("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); + self._controller.startImportingDocument(jdbcQueryInfo); + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + //$("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); + alert( textStatus + ':' + errorThrown ); + }); + //remove end line + + //self._controller.startImportingDocument(jdbcQueryInfo); +} + +Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo) { + var self = this; + $.post( + "command/database/saved-connection", + jdbcConnectionInfo, + function(settings) { + if(settings){ + +// self._elmts.scListGroupDiv.empty(); + self._elmts.menuListUl.empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + self._elmts.menuListUl.append(items.join('')); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._loadSavedConnections = function() { + var self = this; + $.get( + "command/database/saved-connection", + null, + function(settings) { + if(settings){ + + self._elmts.menuListUl.empty(); + //self._elmts.scListGroupDiv.empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + + }) + + self._elmts.menuListUl.append(items.join('')); + // self._elmts.scListGroupDiv.append(items.join('')); + } + + }, + "json" + ); + +}; + +Refine.DatabaseSourceUI.prototype._testDatabaseConnect = function(jdbcConnectionInfo) { + + var self = this; + $.post( + "command/database/test-connect", + jdbcConnectionInfo, + function(jdbcConnectionResult) { + if(jdbcConnectionResult && jdbcConnectionResult.connectionResult == true){ + window.alert("Test Connection Succeeded!"); + }else{ + window.alert("Unable to establish connection to database"); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); +}; + +Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) { + + var self = this; + $.post( + "command/database/connect", + jdbcConnectionInfo, + function(databaseInfo) { + + if(databaseInfo){ + $( "#currentConnectionNameInput" ).val(jdbcConnectionInfo.connectionName); + $( "#currentDatabaseTypeInput" ).val(jdbcConnectionInfo.databaseType); + $( "#currentDatabaseUserInput" ).val(jdbcConnectionInfo.databaseUser); + $( "#currentDatabasePasswordInput" ).val(jdbcConnectionInfo.databasePassword); + $( "#currentDatabaseHostInput" ).val(jdbcConnectionInfo.databaseServer); + $( "#currentDatabasePortInput" ).val(jdbcConnectionInfo.databasePort); + $( "#currentInitialDatabaseInput" ).val(jdbcConnectionInfo.initialDatabase); + + var connectionParam = "Connection :: " + + "jdbc:" + + jdbcConnectionInfo.databaseType + "://" + + jdbcConnectionInfo.databaseServer + ":" + + jdbcConnectionInfo.databasePort + "/" + + jdbcConnectionInfo.initialDatabase; + + //alert("connectionParam::" + connectionParam); + $( "#connectionParameterSpan" ).text(connectionParam); + // self._body.find('.newConnectionDiv').hide(); + // self._body.find('.sqlEditorDiv').show(); + $( "#newConnectionDiv" ).hide(); + $( "#sqlEditorDiv" ).show(); + + }else{ + window.alert("Unable to establish connection to database"); + return; + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._getConnectionInfo = function() { + var self = this; + var jdbcConnectionInfo = {}; + jdbcConnectionInfo.connectionName = $.trim(self._elmts.connectionNameInput[0].value); + jdbcConnectionInfo.databaseType = $.trim(self._elmts.databaseTypeSelect[0].value); + jdbcConnectionInfo.databaseServer = $.trim(self._elmts.databaseHostInput[0].value); + jdbcConnectionInfo.databasePort = $.trim(self._elmts.databasePortInput[0].value); + jdbcConnectionInfo.databaseUser = $.trim(self._elmts.databaseUserInput[0].value); + jdbcConnectionInfo.databasePassword = $.trim(self._elmts.databasePasswordInput[0].value); + jdbcConnectionInfo.initialDatabase = $.trim(self._elmts.initialDatabaseInput[0].value); + jdbcConnectionInfo.initialSchema = $.trim(self._elmts.initialSchemaInput[0].value); + return jdbcConnectionInfo; + +} + +Refine.DatabaseSourceUI.prototype._validateNewConnectionForm = function() { + + var self = this; + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + var databaseTypeSelect = $.trim(self._elmts.databaseTypeSelect[0].value); + var databaseHostInput = $.trim(self._elmts.databaseHostInput[0].value); + var databasePortInput = $.trim(self._elmts.databasePortInput[0].value); + var databaseUserInput = $.trim(self._elmts.databaseUserInput[0].value); + var databasePasswordInput = $.trim(self._elmts.databasePasswordInput[0].value); + var initialDatabaseInput = $.trim(self._elmts.initialDatabaseInput[0].value); + var initialSchemaInput = $.trim(self._elmts.initialSchemaInput[0].value); + + if (databaseHostInput.length === 0) { + window.alert($.i18n._('database-source')["alert-server"]); + return false; + }else if(databasePortInput.length === 0){ + window.alert($.i18n._('database-source')["alert-port"]); + return false; + }else if(databaseUserInput.length === 0){ + window.alert($.i18n._('database-source')["alert-user"]); + return false; + }else if(initialDatabaseInput.length === 0){ + window.alert($.i18n._('database-source')["alert-initial-database"]); + return false; + } + else{ + return true; + + } + + return true; +}; + +Refine.DatabaseSourceUI.prototype._resetDatabaseImportForm = function() { + var self = this; + $( "#connectionName" ).val("127.0.0.1"); + $( "#databaseTypeSelect" ).val("postgresql"); + $( "#databaseHost" ).val("127.0.0.1"); + $( "#databasePort" ).val("5432"); + $( "#databaseUser" ).val("postgres"); + $( "#databasePassword" ).val(""); + $( "#initialDatabase" ).val(""); + $( "#initialSchema" ).val(""); + + $( "#editConnectionControlDiv" ).hide(); + $( "#newConnectionControlDiv" ).show(); + $('#connectionName').removeAttr('readonly'); + +}; diff --git a/extensions/database/module/scripts/index/jquery.contextMenu.min.js b/extensions/database/module/scripts/index/jquery.contextMenu.min.js new file mode 100644 index 000000000..bf6b6169f --- /dev/null +++ b/extensions/database/module/scripts/index/jquery.contextMenu.min.js @@ -0,0 +1,2 @@ +!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){"use strict";function t(e){for(var t,n=e.split(/\s+/),a=[],o=0;t=n[o];o++)t=t.charAt(0).toUpperCase(),a.push(t);return a}function n(t){return t.id&&e('label[for="'+t.id+'"]').val()||t.name}function a(t,o,s){return s||(s=0),o.each(function(){var o,i,c=e(this),l=this,r=this.nodeName.toLowerCase();switch("label"===r&&c.find("input, textarea, select").length&&(o=c.text(),r=(l=(c=c.children().first()).get(0)).nodeName.toLowerCase()),r){case"menu":i={name:c.attr("label"),items:{}},s=a(i.items,c.children(),s);break;case"a":case"button":i={name:c.text(),disabled:!!c.attr("disabled"),callback:function(){c.get(0).click()}};break;case"menuitem":case"command":switch(c.attr("type")){case void 0:case"command":case"menuitem":i={name:c.attr("label"),disabled:!!c.attr("disabled"),icon:c.attr("icon"),callback:function(){c.get(0).click()}};break;case"checkbox":i={type:"checkbox",disabled:!!c.attr("disabled"),name:c.attr("label"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",disabled:!!c.attr("disabled"),name:c.attr("label"),radio:c.attr("radiogroup"),value:c.attr("id"),selected:!!c.attr("checked")};break;default:i=void 0}break;case"hr":i="-------";break;case"input":switch(c.attr("type")){case"text":i={type:"text",name:o||n(l),disabled:!!c.attr("disabled"),value:c.val()};break;case"checkbox":i={type:"checkbox",name:o||n(l),disabled:!!c.attr("disabled"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",name:o||n(l),disabled:!!c.attr("disabled"),radio:!!c.attr("name"),value:c.val(),selected:!!c.attr("checked")};break;default:i=void 0}break;case"select":i={type:"select",name:o||n(l),disabled:!!c.attr("disabled"),selected:c.val(),options:{}},c.children().each(function(){i.options[this.value]=e(this).text()});break;case"textarea":i={type:"textarea",name:o||n(l),disabled:!!c.attr("disabled"),value:c.val()};break;case"label":break;default:i={type:"html",html:c.clone(!0)}}i&&(t["key"+ ++s]=i)}),s}e.support.htmlMenuitem="HTMLMenuItemElement"in window,e.support.htmlCommand="HTMLCommandElement"in window,e.support.eventSelectstart="onselectstart"in document.documentElement,e.ui&&e.widget||(e.cleanData=function(t){return function(n){var a,o,s;for(s=0;null!=n[s];s++){o=n[s];try{(a=e._data(o,"events"))&&a.remove&&e(o).triggerHandler("remove")}catch(e){}}t(n)}}(e.cleanData));var o=null,s=!1,i=e(window),c=0,l={},r={},u={},d={selector:null,appendTo:null,trigger:"right",autoHide:!1,delay:200,reposition:!0,hideOnSecondTrigger:!1,selectableSubMenu:!1,classNames:{hover:"context-menu-hover",disabled:"context-menu-disabled",visible:"context-menu-visible",notSelectable:"context-menu-not-selectable",icon:"context-menu-icon",iconEdit:"context-menu-icon-edit",iconCut:"context-menu-icon-cut",iconCopy:"context-menu-icon-copy",iconPaste:"context-menu-icon-paste",iconDelete:"context-menu-icon-delete",iconAdd:"context-menu-icon-add",iconQuit:"context-menu-icon-quit",iconLoadingClass:"context-menu-icon-loading"},determinePosition:function(t){if(e.ui&&e.ui.position)t.css("display","block").position({my:"center top",at:"center bottom",of:this,offset:"0 5",collision:"fit"}).css("display","none");else{var n=this.offset();n.top+=this.outerHeight(),n.left+=this.outerWidth()/2-t.outerWidth()/2,t.css(n)}},position:function(e,t,n){var a;if(t||n){if("maintain"===t&&"maintain"===n)a=e.$menu.position();else{var o=e.$menu.offsetParent().offset();a={top:n-o.top,left:t-o.left}}var s=i.scrollTop()+i.height(),c=i.scrollLeft()+i.width(),l=e.$menu.outerHeight(),r=e.$menu.outerWidth();a.top+l>s&&(a.top-=l),a.top<0&&(a.top=0),a.left+r>c&&(a.left-=r),a.left<0&&(a.left=0),e.$menu.css(a)}else e.determinePosition.call(this,e.$menu)},positionSubmenu:function(t){if(void 0!==t)if(e.ui&&e.ui.position)t.css("display","block").position({my:"left top-5",at:"right top",of:this,collision:"flipfit fit"}).css("display","");else{var n={top:-9,left:this.outerWidth()-5};t.css(n)}},zIndex:1,animation:{duration:50,show:"slideDown",hide:"slideUp"},events:{show:e.noop,hide:e.noop,activated:e.noop},callback:null,items:{}},m={timer:null,pageX:null,pageY:null},p=function(e){for(var t=0,n=e;;)if(t=Math.max(t,parseInt(n.css("z-index"),10)||0),!(n=n.parent())||!n.length||"html body".indexOf(n.prop("nodeName").toLowerCase())>-1)break;return t},f={abortevent:function(e){e.preventDefault(),e.stopImmediatePropagation()},contextmenu:function(t){var n=e(this);if("right"===t.data.trigger&&(t.preventDefault(),t.stopImmediatePropagation()),!("right"!==t.data.trigger&&"demand"!==t.data.trigger&&t.originalEvent||!(void 0===t.mouseButton||!t.data||"left"===t.data.trigger&&0===t.mouseButton||"right"===t.data.trigger&&2===t.mouseButton)||n.hasClass("context-menu-active")||n.hasClass("context-menu-disabled"))){if(o=n,t.data.build){var a=t.data.build(o,t);if(!1===a)return;if(t.data=e.extend(!0,{},d,t.data,a||{}),!t.data.items||e.isEmptyObject(t.data.items))throw window.console&&(console.error||console.log).call(console,"No items specified to show in contextMenu"),new Error("No Items specified");t.data.$trigger=o,h.create(t.data)}var s=!1;for(var i in t.data.items)if(t.data.items.hasOwnProperty(i)){(e.isFunction(t.data.items[i].visible)?t.data.items[i].visible.call(e(t.currentTarget),i,t.data):void 0===t.data.items[i]||!t.data.items[i].visible||!0===t.data.items[i].visible)&&(s=!0)}s&&h.show.call(n,t.data,t.pageX,t.pageY)}},click:function(t){t.preventDefault(),t.stopImmediatePropagation(),e(this).trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))},mousedown:function(t){var n=e(this);o&&o.length&&!o.is(n)&&o.data("contextMenu").$menu.trigger("contextmenu:hide"),2===t.button&&(o=n.data("contextMenuActive",!0))},mouseup:function(t){var n=e(this);n.data("contextMenuActive")&&o&&o.length&&o.is(n)&&!n.hasClass("context-menu-disabled")&&(t.preventDefault(),t.stopImmediatePropagation(),o=n,n.trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))),n.removeData("contextMenuActive")},mouseenter:function(t){var n=e(this),a=e(t.relatedTarget),s=e(document);a.is(".context-menu-list")||a.closest(".context-menu-list").length||o&&o.length||(m.pageX=t.pageX,m.pageY=t.pageY,m.data=t.data,s.on("mousemove.contextMenuShow",f.mousemove),m.timer=setTimeout(function(){m.timer=null,s.off("mousemove.contextMenuShow"),o=n,n.trigger(e.Event("contextmenu",{data:m.data,pageX:m.pageX,pageY:m.pageY}))},t.data.delay))},mousemove:function(e){m.pageX=e.pageX,m.pageY=e.pageY},mouseleave:function(t){var n=e(t.relatedTarget);if(!n.is(".context-menu-list")&&!n.closest(".context-menu-list").length){try{clearTimeout(m.timer)}catch(t){}m.timer=null}},layerClick:function(t){var n,a,o=e(this).data("contextMenuRoot"),s=t.button,c=t.pageX,l=t.pageY;t.preventDefault(),setTimeout(function(){var r,u="left"===o.trigger&&0===s||"right"===o.trigger&&2===s;if(document.elementFromPoint&&o.$layer){if(o.$layer.hide(),(n=document.elementFromPoint(c-i.scrollLeft(),l-i.scrollTop())).isContentEditable){var d=document.createRange(),m=window.getSelection();d.selectNode(n),d.collapse(!0),m.removeAllRanges(),m.addRange(d)}e(n).trigger(t),o.$layer.show()}if(o.hideOnSecondTrigger&&u&&null!==o.$menu&&void 0!==o.$menu)o.$menu.trigger("contextmenu:hide");else{if(o.reposition&&u)if(document.elementFromPoint){if(o.$trigger.is(n))return void o.position.call(o.$trigger,o,c,l)}else if(a=o.$trigger.offset(),r=e(window),a.top+=r.scrollTop(),a.top<=t.pageY&&(a.left+=r.scrollLeft(),a.left<=t.pageX&&(a.bottom=a.top+o.$trigger.outerHeight(),a.bottom>=t.pageY&&(a.right=a.left+o.$trigger.outerWidth(),a.right>=t.pageX))))return void o.position.call(o.$trigger,o,c,l);n&&u&&o.$trigger.one("contextmenu:hidden",function(){e(n).contextMenu({x:c,y:l,button:s})}),null!==o&&void 0!==o&&null!==o.$menu&&void 0!==o.$menu&&o.$menu.trigger("contextmenu:hide")}},50)},keyStop:function(e,t){t.isInput||e.preventDefault(),e.stopPropagation()},key:function(e){var t={};o&&(t=o.data("contextMenu")||{}),void 0===t.zIndex&&(t.zIndex=0);var n=0,a=function(e){""!==e.style.zIndex?n=e.style.zIndex:null!==e.offsetParent&&void 0!==e.offsetParent?a(e.offsetParent):null!==e.parentElement&&void 0!==e.parentElement&&a(e.parentElement)};if(a(e.target),!(t.$menu&&parseInt(n,10)>parseInt(t.$menu.css("zIndex"),10))){switch(e.keyCode){case 9:case 38:if(f.keyStop(e,t),t.isInput){if(9===e.keyCode&&e.shiftKey)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("prevcommand"));if(38===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault()}else if(9!==e.keyCode||e.shiftKey)return void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("prevcommand"));break;case 40:if(f.keyStop(e,t),!t.isInput)return void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("nextcommand"));if(9===e.keyCode)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("nextcommand"));if(40===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault();break;case 37:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;if(!t.$selected.parent().hasClass("context-menu-root")){var s=t.$selected.parent().parent();return t.$selected.trigger("contextmenu:blur"),void(t.$selected=s)}break;case 39:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;var i=t.$selected.data("contextMenu")||{};if(i.$menu&&t.$selected.hasClass("context-menu-submenu"))return t.$selected=null,i.$selected=null,void i.$menu.trigger("nextcommand");break;case 35:case 36:return t.$selected&&t.$selected.find("input, textarea, select").length?void 0:((t.$selected&&t.$selected.parent()||t.$menu).children(":not(."+t.classNames.disabled+", ."+t.classNames.notSelectable+")")[36===e.keyCode?"first":"last"]().trigger("contextmenu:focus"),void e.preventDefault());case 13:if(f.keyStop(e,t),t.isInput){if(t.$selected&&!t.$selected.is("textarea, select"))return void e.preventDefault();break}return void(void 0!==t.$selected&&null!==t.$selected&&t.$selected.trigger("mouseup"));case 32:case 33:case 34:return void f.keyStop(e,t);case 27:return f.keyStop(e,t),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("contextmenu:hide"));default:var c=String.fromCharCode(e.keyCode).toUpperCase();if(t.accesskeys&&t.accesskeys[c])return void t.accesskeys[c].$node.trigger(t.accesskeys[c].$menu?"contextmenu:focus":"mouseup")}e.stopPropagation(),void 0!==t.$selected&&null!==t.$selected&&t.$selected.trigger(e)}},prevItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;(n=n.$selected.parent().data("contextMenu")||{}).$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.prev().length?n.$selected.prev():s.last(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable)||i.is(":hidden");)if((i=i.prev().length?i.prev():s.last()).is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var l=i.find("input, textarea, select");l.length&&l.focus()},nextItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;(n=n.$selected.parent().data("contextMenu")||{}).$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.next().length?n.$selected.next():s.first(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable)||i.is(":hidden");)if((i=i.next().length?i.next():s.first()).is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var l=i.find("input, textarea, select");l.length&&l.focus()},focusInput:function(){var t=e(this).closest(".context-menu-item"),n=t.data(),a=n.contextMenu,o=n.contextMenuRoot;o.$selected=a.$selected=t,o.isInput=a.isInput=!0},blurInput:function(){var t=e(this).closest(".context-menu-item").data(),n=t.contextMenu;t.contextMenuRoot.isInput=n.isInput=!1},menuMouseenter:function(){e(this).data().contextMenuRoot.hovering=!0},menuMouseleave:function(t){var n=e(this).data().contextMenuRoot;n.$layer&&n.$layer.is(t.relatedTarget)&&(n.hovering=!1)},itemMouseenter:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;s.hovering=!0,t&&s.$layer&&s.$layer.is(t.relatedTarget)&&(t.preventDefault(),t.stopImmediatePropagation()),(o.$menu?o:s).$menu.children("."+s.classNames.hover).trigger("contextmenu:blur").children(".hover").trigger("contextmenu:blur"),n.hasClass(s.classNames.disabled)||n.hasClass(s.classNames.notSelectable)?o.$selected=null:n.trigger("contextmenu:focus")},itemMouseleave:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;if(s!==o&&s.$layer&&s.$layer.is(t.relatedTarget))return void 0!==s.$selected&&null!==s.$selected&&s.$selected.trigger("contextmenu:blur"),t.preventDefault(),t.stopImmediatePropagation(),void(s.$selected=o.$selected=o.$node);o&&o.$menu&&o.$menu.hasClass("context-menu-visible")||n.trigger("contextmenu:blur")},itemClick:function(t){var n,a=e(this),o=a.data(),s=o.contextMenu,i=o.contextMenuRoot,c=o.contextMenuKey;if(!(!s.items[c]||a.is("."+i.classNames.disabled+", .context-menu-separator, ."+i.classNames.notSelectable)||a.is(".context-menu-submenu")&&!1===i.selectableSubMenu)){if(t.preventDefault(),t.stopImmediatePropagation(),e.isFunction(s.callbacks[c])&&Object.prototype.hasOwnProperty.call(s.callbacks,c))n=s.callbacks[c];else{if(!e.isFunction(i.callback))return;n=i.callback}!1!==n.call(i.$trigger,c,i,t)?i.$menu.trigger("contextmenu:hide"):i.$menu.parent().length&&h.update.call(i.$trigger,i)}},inputClick:function(e){e.stopImmediatePropagation()},hideMenu:function(t,n){var a=e(this).data("contextMenuRoot");h.hide.call(a.$trigger,a,n&&n.force)},focusItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;n.hasClass(s.classNames.disabled)||n.hasClass(s.classNames.notSelectable)||(n.addClass([s.classNames.hover,s.classNames.visible].join(" ")).parent().find(".context-menu-item").not(n).removeClass(s.classNames.visible).filter("."+s.classNames.hover).trigger("contextmenu:blur"),o.$selected=s.$selected=n,o&&o.$node&&o.$node.hasClass("context-menu-submenu")&&o.$node.addClass(s.classNames.hover),o.$node&&s.positionSubmenu.call(o.$node,o.$menu))},blurItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;o.autoHide&&n.removeClass(s.classNames.visible),n.removeClass(s.classNames.hover),o.$selected=null}},h={show:function(t,n,a){var s=e(this),i={};if(e("#context-menu-layer").trigger("mousedown"),t.$trigger=s,!1!==t.events.show.call(s,t)){if(h.update.call(s,t),t.position.call(s,t,n,a),t.zIndex){var c=t.zIndex;"function"==typeof t.zIndex&&(c=t.zIndex.call(s,t)),i.zIndex=p(s)+c}h.layer.call(t.$menu,t,i.zIndex),t.$menu.find("ul").css("zIndex",i.zIndex+1),t.$menu.css(i)[t.animation.show](t.animation.duration,function(){s.trigger("contextmenu:visible"),h.activated(t),t.events.activated()}),s.data("contextMenu",t).addClass("context-menu-active"),e(document).off("keydown.contextMenu").on("keydown.contextMenu",f.key),t.autoHide&&e(document).on("mousemove.contextMenuAutoHide",function(e){var n=s.offset();n.right=n.left+s.outerWidth(),n.bottom=n.top+s.outerHeight(),!t.$layer||t.hovering||e.pageX>=n.left&&e.pageX<=n.right&&e.pageY>=n.top&&e.pageY<=n.bottom||setTimeout(function(){t.hovering||null===t.$menu||void 0===t.$menu||t.$menu.trigger("contextmenu:hide")},50)})}else o=null},hide:function(t,n){var a=e(this);if(t||(t=a.data("contextMenu")||{}),n||!t.events||!1!==t.events.hide.call(a,t)){if(a.removeData("contextMenu").removeClass("context-menu-active"),t.$layer){setTimeout(function(e){return function(){e.remove()}}(t.$layer),10);try{delete t.$layer}catch(e){t.$layer=null}}o=null,t.$menu.find("."+t.classNames.hover).trigger("contextmenu:blur"),t.$selected=null,t.$menu.find("."+t.classNames.visible).removeClass(t.classNames.visible),e(document).off(".contextMenuAutoHide").off("keydown.contextMenu"),t.$menu&&t.$menu[t.animation.hide](t.animation.duration,function(){t.build&&(t.$menu.remove(),e.each(t,function(e){switch(e){case"ns":case"selector":case"build":case"trigger":return!0;default:t[e]=void 0;try{delete t[e]}catch(e){}return!0}})),setTimeout(function(){a.trigger("contextmenu:hidden")},10)})}},create:function(n,a){function o(t){var n=e("");if(t._accesskey)t._beforeAccesskey&&n.append(document.createTextNode(t._beforeAccesskey)),e("").addClass("context-menu-accesskey").text(t._accesskey).appendTo(n),t._afterAccesskey&&n.append(document.createTextNode(t._afterAccesskey));else if(t.isHtmlName){if(void 0!==t.accesskey)throw new Error("accesskeys are not compatible with HTML names and cannot be used together in the same item");n.html(t.name)}else n.text(t.name);return n}void 0===a&&(a=n),n.$menu=e('
      ').addClass(n.className||"").data({contextMenu:n,contextMenuRoot:a}),e.each(["callbacks","commands","inputs"],function(e,t){n[t]={},a[t]||(a[t]={})}),a.accesskeys||(a.accesskeys={}),e.each(n.items,function(s,i){var c=e('
    • ').addClass(i.className||""),l=null,r=null;if(c.on("click",e.noop),"string"!=typeof i&&"cm_separator"!==i.type||(i={type:"cm_seperator"}),i.$node=c.data({contextMenu:n,contextMenuRoot:a,contextMenuKey:s}),void 0!==i.accesskey)for(var d,m=t(i.accesskey),p=0;d=m[p];p++)if(!a.accesskeys[d]){a.accesskeys[d]=i;var v=i.name.match(new RegExp("^(.*?)("+d+")(.*)$","i"));v&&(i._beforeAccesskey=v[1],i._accesskey=v[2],i._afterAccesskey=v[3]);break}if(i.type&&u[i.type])u[i.type].call(c,i,n,a),e.each([n,a],function(t,a){a.commands[s]=i,!e.isFunction(i.callback)||void 0!==a.callbacks[s]&&void 0!==n.type||(a.callbacks[s]=i.callback)});else{switch("cm_seperator"===i.type?c.addClass("context-menu-separator "+a.classNames.notSelectable):"html"===i.type?c.addClass("context-menu-html "+a.classNames.notSelectable):"sub"===i.type||(i.type?(l=e("").appendTo(c),o(i).appendTo(l),c.addClass("context-menu-input"),n.hasTypes=!0,e.each([n,a],function(e,t){t.commands[s]=i,t.inputs[s]=i})):i.items&&(i.type="sub")),i.type){case"cm_seperator":break;case"text":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(l);break;case"textarea":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(l),i.height&&r.height(i.height);break;case"checkbox":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").prop("checked",!!i.selected).prependTo(l);break;case"radio":r=e('').attr("name","context-menu-input-"+i.radio).val(i.value||"").prop("checked",!!i.selected).prependTo(l);break;case"select":r=e('').attr("name","context-menu-input-"+s).appendTo(l),i.options&&(e.each(i.options,function(t,n){e("").val(t).text(n).appendTo(r)}),r.val(i.selected));break;case"sub":o(i).appendTo(c),i.appendTo=i.$node,c.data("contextMenu",i).addClass("context-menu-submenu"),i.callback=null,"function"==typeof i.items.then?h.processPromises(i,a,i.items):h.create(i,a);break;case"html":e(i.html).appendTo(c);break;default:e.each([n,a],function(t,a){a.commands[s]=i,!e.isFunction(i.callback)||void 0!==a.callbacks[s]&&void 0!==n.type||(a.callbacks[s]=i.callback)}),o(i).appendTo(c)}i.type&&"sub"!==i.type&&"html"!==i.type&&"cm_seperator"!==i.type&&(r.on("focus",f.focusInput).on("blur",f.blurInput),i.events&&r.on(i.events,n)),i.icon&&(e.isFunction(i.icon)?i._icon=i.icon.call(this,this,c,s,i):"string"==typeof i.icon&&"fa-"===i.icon.substring(0,3)?i._icon=a.classNames.icon+" "+a.classNames.icon+"--fa fa "+i.icon:i._icon=a.classNames.icon+" "+a.classNames.icon+"-"+i.icon,c.addClass(i._icon))}i.$input=r,i.$label=l,c.appendTo(n.$menu),!n.hasTypes&&e.support.eventSelectstart&&c.on("selectstart.disableTextSelect",f.abortevent)}),n.$node||n.$menu.css("display","none").addClass("context-menu-root"),n.$menu.appendTo(n.appendTo||document.body)},resize:function(t,n){var a;t.css({position:"absolute",display:"block"}),t.data("width",(a=t.get(0)).getBoundingClientRect?Math.ceil(a.getBoundingClientRect().width):t.outerWidth()+1),t.css({position:"static",minWidth:"0px",maxWidth:"100000px"}),t.find("> li > ul").each(function(){h.resize(e(this),!0)}),n||t.find("ul").addBack().css({position:"",display:"",minWidth:"",maxWidth:""}).outerWidth(function(){return e(this).data("width")})},update:function(t,n){var a=this;void 0===n&&(n=t,h.resize(t.$menu)),t.$menu.children().each(function(){var o,s=e(this),i=s.data("contextMenuKey"),c=t.items[i],l=e.isFunction(c.disabled)&&c.disabled.call(a,i,n)||!0===c.disabled;if(o=e.isFunction(c.visible)?c.visible.call(a,i,n):void 0===c.visible||!0===c.visible,s[o?"show":"hide"](),s[l?"addClass":"removeClass"](n.classNames.disabled),e.isFunction(c.icon)&&(s.removeClass(c._icon),c._icon=c.icon.call(this,a,s,i,c),s.addClass(c._icon)),c.type)switch(s.find("input, select, textarea").prop("disabled",l),c.type){case"text":case"textarea":c.$input.val(c.value||"");break;case"checkbox":case"radio":c.$input.val(c.value||"").prop("checked",!!c.selected);break;case"select":c.$input.val((0===c.selected?"0":c.selected)||"")}c.$menu&&h.update.call(a,c,n)})},layer:function(t,n){var a=t.$layer=e('
      ').css({height:i.height(),width:i.width(),display:"block",position:"fixed","z-index":n,top:0,left:0,opacity:0,filter:"alpha(opacity=0)","background-color":"#000"}).data("contextMenuRoot",t).insertBefore(this).on("contextmenu",f.abortevent).on("mousedown",f.layerClick);return void 0===document.body.style.maxWidth&&a.css({position:"absolute",height:e(document).height()}),a},processPromises:function(e,t,n){function a(e,t,n){void 0===n?(n={error:{name:"No items and no error item",icon:"context-menu-icon context-menu-icon-quit"}},window.console&&(console.error||console.log).call(console,'When you reject a promise, provide an "items" object, equal to normal sub-menu items')):"string"==typeof n&&(n={error:{name:n}}),o(e,t,n)}function o(e,t,n){void 0!==t.$menu&&t.$menu.is(":visible")&&(e.$node.removeClass(t.classNames.iconLoadingClass),e.items=n,h.create(e,t,!0),h.update(e,t),t.positionSubmenu.call(e.$node,e.$menu))}e.$node.addClass(t.classNames.iconLoadingClass),n.then(function(e,t,n){void 0===n&&a(void 0),o(e,t,n)}.bind(this,e,t),a.bind(this,e,t))},activated:function(t){var n=t.$menu,a=n.offset(),o=e(window).height(),s=e(window).scrollTop(),i=n.height();i>o?n.css({height:o+"px","overflow-x":"hidden","overflow-y":"auto",top:s+"px"}):(a.tops+o)&&n.css({top:"0px"})}};e.fn.contextMenu=function(t){var n=this,a=t;if(this.length>0)if(void 0===t)this.first().trigger("contextmenu");else if(void 0!==t.x&&void 0!==t.y)this.first().trigger(e.Event("contextmenu",{pageX:t.x,pageY:t.y,mouseButton:t.button}));else if("hide"===t){var o=this.first().data("contextMenu")?this.first().data("contextMenu").$menu:null;o&&o.trigger("contextmenu:hide")}else"destroy"===t?e.contextMenu("destroy",{context:this}):e.isPlainObject(t)?(t.context=this,e.contextMenu("create",t)):t?this.removeClass("context-menu-disabled"):t||this.addClass("context-menu-disabled");else e.each(r,function(){this.selector===n.selector&&(a.data=this,e.extend(a.data,{trigger:"demand"}))}),f.contextmenu.call(a.target,a);return this},e.contextMenu=function(t,n){"string"!=typeof t&&(n=t,t="create"),"string"==typeof n?n={selector:n}:void 0===n&&(n={});var a=e.extend(!0,{},d,n||{}),o=e(document),i=o,u=!1;switch(a.context&&a.context.length?(i=e(a.context).first(),a.context=i.get(0),u=!e(a.context).is(document)):a.context=document,t){case"update":if(u)h.update(i);else for(var m in r)r.hasOwnProperty(m)&&h.update(r[m]);break;case"create":if(!a.selector)throw new Error("No selector specified");if(a.selector.match(/.context-menu-(list|item|input)($|\s)/))throw new Error('Cannot bind to selector "'+a.selector+'" as it contains a reserved className');if(!a.build&&(!a.items||e.isEmptyObject(a.items)))throw new Error("No Items specified");if(c++,a.ns=".contextMenu"+c,u||(l[a.selector]=a.ns),r[a.ns]=a,a.trigger||(a.trigger="right"),!s){var p="click"===a.itemClickEvent?"click.contextMenu":"mouseup.contextMenu",v={"contextmenu:focus.contextMenu":f.focusItem,"contextmenu:blur.contextMenu":f.blurItem,"contextmenu.contextMenu":f.abortevent,"mouseenter.contextMenu":f.itemMouseenter,"mouseleave.contextMenu":f.itemMouseleave};v[p]=f.itemClick,o.on({"contextmenu:hide.contextMenu":f.hideMenu,"prevcommand.contextMenu":f.prevItem,"nextcommand.contextMenu":f.nextItem,"contextmenu.contextMenu":f.abortevent,"mouseenter.contextMenu":f.menuMouseenter,"mouseleave.contextMenu":f.menuMouseleave},".context-menu-list").on("mouseup.contextMenu",".context-menu-input",f.inputClick).on(v,".context-menu-item"),s=!0}switch(i.on("contextmenu"+a.ns,a.selector,a,f.contextmenu),u&&i.on("remove"+a.ns,function(){e(this).contextMenu("destroy")}),a.trigger){case"hover":i.on("mouseenter"+a.ns,a.selector,a,f.mouseenter).on("mouseleave"+a.ns,a.selector,a,f.mouseleave);break;case"left":i.on("click"+a.ns,a.selector,a,f.click);break;case"touchstart":i.on("touchstart"+a.ns,a.selector,a,f.click)}a.build||h.create(a);break;case"destroy":var x;if(u){var g=a.context;e.each(r,function(t,n){if(!n)return!0;if(!e(g).is(n.selector))return!0;(x=e(".context-menu-list").filter(":visible")).length&&x.data().contextMenuRoot.$trigger.is(e(n.context).find(n.selector))&&x.trigger("contextmenu:hide",{force:!0});try{r[n.ns].$menu&&r[n.ns].$menu.remove(),delete r[n.ns]}catch(e){r[n.ns]=null}return e(n.context).off(n.ns),!0})}else if(a.selector){if(l[a.selector]){(x=e(".context-menu-list").filter(":visible")).length&&x.data().contextMenuRoot.$trigger.is(a.selector)&&x.trigger("contextmenu:hide",{force:!0});try{r[l[a.selector]].$menu&&r[l[a.selector]].$menu.remove(),delete r[l[a.selector]]}catch(e){r[l[a.selector]]=null}o.off(l[a.selector])}}else o.off(".contextMenu .contextMenuAutoHide"),e.each(r,function(t,n){e(n.context).off(n.ns)}),l={},r={},c=0,s=!1,e("#context-menu-layer, .context-menu-list").remove();break;case"html5":(!e.support.htmlCommand&&!e.support.htmlMenuitem||"boolean"==typeof n&&n)&&e('menu[type="context"]').each(function(){this.id&&e.contextMenu({selector:"[contextmenu="+this.id+"]",items:e.contextMenu.fromMenu(this)})}).css("display","none");break;default:throw new Error('Unknown operation "'+t+'"')}return this},e.contextMenu.setInputValues=function(t,n){void 0===n&&(n={}),e.each(t.inputs,function(e,t){switch(t.type){case"text":case"textarea":t.value=n[e]||"";break;case"checkbox":t.selected=!!n[e];break;case"radio":t.selected=(n[t.radio]||"")===t.value;break;case"select":t.selected=n[e]||""}})},e.contextMenu.getInputValues=function(t,n){return void 0===n&&(n={}),e.each(t.inputs,function(e,t){switch(t.type){case"text":case"textarea":case"select":n[e]=t.$input.val();break;case"checkbox":n[e]=t.$input.prop("checked");break;case"radio":t.$input.prop("checked")&&(n[t.radio]=t.value)}}),n},e.contextMenu.fromMenu=function(t){var n={};return a(n,e(t).children()),n},e.contextMenu.defaults=d,e.contextMenu.types=u,e.contextMenu.handle=f,e.contextMenu.op=h,e.contextMenu.menus=r}); +//# sourceMappingURL=jquery.contextMenu.min.js.map diff --git a/extensions/database/module/scripts/index/jquery.ui.position.min.js b/extensions/database/module/scripts/index/jquery.ui.position.min.js new file mode 100644 index 000000000..48d6a99b4 --- /dev/null +++ b/extensions/database/module/scripts/index/jquery.ui.position.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.1 - 2016-09-16 + * http://jqueryui.com + * Includes: position.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1",function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
      "),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position}); \ No newline at end of file diff --git a/extensions/database/module/scripts/project/database-exporters.js b/extensions/database/module/scripts/project/database-exporters.js new file mode 100644 index 000000000..eacb5a488 --- /dev/null +++ b/extensions/database/module/scripts/project/database-exporters.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var dictionary = ""; +$.ajax({ + url : "command/core/load-language?", + type : "POST", + async : false, + data : { + module : "database", + + }, + success : function(data) { + dictionary = data; + } +}); +$.i18n.setDictionary(dictionary); +// End internationalization + +(function() { + +})(); diff --git a/extensions/database/module/styles/bootstrap.css b/extensions/database/module/styles/bootstrap.css new file mode 100644 index 000000000..39e2a1597 --- /dev/null +++ b/extensions/database/module/styles/bootstrap.css @@ -0,0 +1,1701 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2017 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! + * Generated using the Bootstrap Customizer (http://getbootstrap.com/docs/3.3/customize/?id=3426e25f732fc7882717e871fd08de17) + * Config saved to config.json and https://gist.github.com/3426e25f732fc7882717e871fd08de17 + */ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #dddddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #dddddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} +.panel-default { + border-color: #dddddd; +} +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #dddddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #dddddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.clearfix:before, +.clearfix:after, +.nav:before, +.nav:after, +.panel-body:before, +.panel-body:after { + content: " "; + display: table; +} +.clearfix:after, +.nav:after, +.panel-body:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #3c763d; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #31708f; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + background-color: #fcf8e3; + border-color: #faebcc; + color: #8a6d3b; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + background-color: #f2dede; + border-color: #ebccd1; + color: #a94442; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('../images/fonts/glyphicons-halflings-regular.eot'); + src: url('../images/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../images/fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../images/fonts/glyphicons-halflings-regular.woff') format('woff'), url('../images/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../images/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} + +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #777777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777777; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #dddddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid #dddddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +/* +* List Group +*/ +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd; +} +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + text-decoration: none; + color: #555555; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + background-color: #eeeeee; + color: #777777; + cursor: not-allowed; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} diff --git a/extensions/database/module/styles/database-import.less b/extensions/database/module/styles/database-import.less new file mode 100644 index 000000000..04cae2dae --- /dev/null +++ b/extensions/database/module/styles/database-import.less @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +@import-less url("theme.less"); + +.database-container { +// margin-left: 200px; + // width: 50%; +// min-height: 50%; +} + +.cleared { + clear: both; +} + +.custom-restricted-width { + /* To limit the menu width to the content of the menu: */ + display: inline-block; + /* Or set the width explicitly: */ + /* width: 10em; */ +} + + +.scrollable { + height: 400px; + overflow: auto; +} + +.database-importing-wizard-header { + font-size: 1.3em; + background: @chrome_primary; + padding: @padding_tight; + } + +.database-importing-parsing-data-panel { + font-size: 1.1em; + position: absolute; + overflow: auto; + } + +.database-importing-progress-data-panel { + position: absolute; + overflow: auto; + font-size: 200%; + padding: 3em; + background: rgba(255, 255, 255, 0.7); + text-align: center; + } + +.database-importing-parsing-control-panel { + font-size: 1.3em; + position: absolute; + overflow: auto; + border-top: 5px solid @chrome_primary; + background: white; + padding: @padding_looser; + } + +.context-menu-text { + padding: 4px 4px 2px 2px; +} + + +.custom-restricted { + height: 300px; + width: 200px; + border: 1px solid #bce8f1; + border-radius: 4px; + margin-right:10px; + padding-right:10px; + margin-bottom:10px; +} +.custom-restricted-width { + /* To limit the menu width to the content of the menu: */ + display: inline-block; + /* Or set the width explicitly: */ + /* width: 10em; */ + margin-right:10px; + padding-right:10px; + margin-bottom:10px; +} +.no-resize { + resize: none; +} +.layout-div { + width : 100%; + +} +.layout-div .connection-div-layout { + min-width: 400px; +} +.new-connection-legend { + padding-left: 2px; + margin-left: 2px; + border-color: #bce8f1; +} + +.sql-editor-div { + margin-left:40px; + padding-left:20px; +} +.new-connection-div { + margin-left:40px; + padding-left:20px; +} +.new-connection-fieldset { + border: 1px solid gray; +} +.sc-list { + border-bottom: 1px dotted #bce8f1; +} + +.sc-context-more-vert:after { + content: ""; + display: inline-block; + background: url("../images/more-option-horiz-16.png") no-repeat top right; + // line-height: 1; + width: 16px; + height: 16px; + -webkit-font-smoothing: antialiased; + +} diff --git a/extensions/database/module/styles/jquery.contextMenu.css b/extensions/database/module/styles/jquery.contextMenu.css new file mode 100644 index 000000000..0253e6e1b --- /dev/null +++ b/extensions/database/module/styles/jquery.contextMenu.css @@ -0,0 +1,292 @@ +@charset "UTF-8"; +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: v2.6.3 + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-2017 SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: 2017-10-30T19:03:13.936Z + */ +@-webkit-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@-o-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} + +@font-face { + font-family: "context-menu-icons"; + font-style: normal; + font-weight: normal; + + src: url("font/context-menu-icons.eot?2wp27"); + src: url("font/context-menu-icons.eot?2wp27#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2wp27") format("woff2"), url("font/context-menu-icons.woff?2wp27") format("woff"), url("font/context-menu-icons.ttf?2wp27") format("truetype"); +} + +.context-menu-icon-add:before { + content: "\EA01"; +} + +.context-menu-icon-copy:before { + content: "\EA02"; +} + +.context-menu-icon-cut:before { + content: "\EA03"; +} + +.context-menu-icon-delete:before { + content: "\EA04"; +} + +.context-menu-icon-edit:before { + content: "\EA05"; +} + +.context-menu-icon-loading:before { + content: "\EA06"; +} + +.context-menu-icon-paste:before { + content: "\EA07"; +} + +.context-menu-icon-quit:before { + content: "\EA08"; +} + +.context-menu-icon::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: "context-menu-icons"; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.context-menu-icon.context-menu-hover:before { + color: #fff; +} + +.context-menu-icon.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-icon.context-menu-icon-loading:before { + -webkit-animation: cm-spin 2s infinite; + -o-animation: cm-spin 2s infinite; + animation: cm-spin 2s infinite; +} + +.context-menu-icon.context-menu-icon--fa { + display: list-item; + font-family: inherit; +} +.context-menu-icon.context-menu-icon--fa::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: FontAwesome; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.context-menu-icon.context-menu-icon--fa.context-menu-hover:before { + color: #fff; +} +.context-menu-icon.context-menu-icon--fa.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-list { + position: absolute; + display: inline-block; + min-width: 13em; + max-width: 26em; + padding: .25em 0; + margin: .3em; + font-family: inherit; + font-size: inherit; + list-style-type: none; + background: #fff; + border: 1px solid #bebebe; + border-radius: .2em; + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5); + box-shadow: 0 2px 5px rgba(0, 0, 0, .5); +} + +.context-menu-item { + position: relative; + padding: .2em 2em; + color: #2f2f2f; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #fff; + font: 15px "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.context-menu-separator { + padding: 0; + margin: .35em 0; + border-bottom: 1px solid #e6e6e6; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.context-menu-hover { + color: #fff; + cursor: pointer; + background-color: #2980b9; +} + +.context-menu-item.context-menu-disabled { + color: #bbb; + cursor: default; + background-color: #fff; +} + +.context-menu-input.context-menu-hover { + color: #2f2f2f; + cursor: default; +} + +.context-menu-submenu:after { + position: absolute; + top: 50%; + right: .5em; + z-index: 1; + width: 0; + height: 0; + content: ''; + border-color: transparent transparent transparent #2f2f2f; + border-style: solid; + border-width: .25em 0 .25em .25em; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); +} + +/** + * Inputs + */ +.context-menu-item.context-menu-input { + padding: .3em .6em; +} + +/* vertically align inside labels */ +.context-menu-input > label > * { + vertical-align: top; +} + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + position: relative; + top: .12em; + margin-right: .4em; +} + +.context-menu-input > label { + margin: 0; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 7em; +} + +.context-menu-item > .context-menu-list { + top: .3em; + /* re-positioned by js */ + right: -.3em; + display: none; +} + +.context-menu-item.context-menu-visible > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} +/*Custom to display icons*/ + + + diff --git a/extensions/database/module/styles/pure.css b/extensions/database/module/styles/pure.css new file mode 100644 index 000000000..6b9d53736 --- /dev/null +++ b/extensions/database/module/styles/pure.css @@ -0,0 +1,1549 @@ +/*! +Pure v1.0.0 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + box-sizing: content-box; /* 2 */ +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} + +/*csslint important:false*/ + +/* ========================================================================== + Pure Base Extras + ========================================================================== */ + +/** + * Extra rules that Pure adds on top of Normalize.css + */ + +/** + * Always hide an element when it has the `hidden` HTML attribute. + */ + +.hidden, +[hidden] { + display: none !important; +} + +/** + * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining + * aspect ratio. + */ +.pure-img { + max-width: 100%; + height: auto; + display: block; +} + +/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/ + +.pure-g { + letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ + *letter-spacing: normal; /* reset IE < 8 */ + *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ + + /* + Sets the font stack to fonts known to work properly with the above letter + and word spacings. See: https://github.com/yahoo/pure/issues/41/ + + The following font stack makes Pure Grids work on all known environments. + + * FreeSans: Ships with many Linux distros, including Ubuntu + + * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and + Arial to get picked up by the browser, even though neither is available + in Chrome OS. + + * Droid Sans: Ships with all versions of Android. + + * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows. + */ + font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; + + /* Use flexbox when possible to avoid `letter-spacing` side-effects. */ + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + + /* Prevents distributing space between rows */ + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start; +} + +/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */ +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + table .pure-g { + display: block; + } +} + +/* Opera as of 12 on Windows needs word-spacing. + The ".opera-only" selector is used to prevent actual prefocus styling + and is not required in markup. +*/ +.opera-only :-o-prefocus, +.pure-g { + word-spacing: -0.43em; +} + +.pure-u { + display: inline-block; + *display: inline; /* IE < 8: fake inline-block */ + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +/* +Resets the font family back to the OS/browser's default sans-serif font, +this the same font stack that Normalize.css sets for the `body`. +*/ +.pure-g [class *= "pure-u"] { + font-family: sans-serif; +} + +.pure-u-1, +.pure-u-1-1, +.pure-u-1-2, +.pure-u-1-3, +.pure-u-2-3, +.pure-u-1-4, +.pure-u-3-4, +.pure-u-1-5, +.pure-u-2-5, +.pure-u-3-5, +.pure-u-4-5, +.pure-u-5-5, +.pure-u-1-6, +.pure-u-5-6, +.pure-u-1-8, +.pure-u-3-8, +.pure-u-5-8, +.pure-u-7-8, +.pure-u-1-12, +.pure-u-5-12, +.pure-u-7-12, +.pure-u-11-12, +.pure-u-1-24, +.pure-u-2-24, +.pure-u-3-24, +.pure-u-4-24, +.pure-u-5-24, +.pure-u-6-24, +.pure-u-7-24, +.pure-u-8-24, +.pure-u-9-24, +.pure-u-10-24, +.pure-u-11-24, +.pure-u-12-24, +.pure-u-13-24, +.pure-u-14-24, +.pure-u-15-24, +.pure-u-16-24, +.pure-u-17-24, +.pure-u-18-24, +.pure-u-19-24, +.pure-u-20-24, +.pure-u-21-24, +.pure-u-22-24, +.pure-u-23-24, +.pure-u-24-24 { + display: inline-block; + *display: inline; + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +.pure-u-1-24 { + width: 4.1667%; + *width: 4.1357%; +} + +.pure-u-1-12, +.pure-u-2-24 { + width: 8.3333%; + *width: 8.3023%; +} + +.pure-u-1-8, +.pure-u-3-24 { + width: 12.5000%; + *width: 12.4690%; +} + +.pure-u-1-6, +.pure-u-4-24 { + width: 16.6667%; + *width: 16.6357%; +} + +.pure-u-1-5 { + width: 20%; + *width: 19.9690%; +} + +.pure-u-5-24 { + width: 20.8333%; + *width: 20.8023%; +} + +.pure-u-1-4, +.pure-u-6-24 { + width: 25%; + *width: 24.9690%; +} + +.pure-u-7-24 { + width: 29.1667%; + *width: 29.1357%; +} + +.pure-u-1-3, +.pure-u-8-24 { + width: 33.3333%; + *width: 33.3023%; +} + +.pure-u-3-8, +.pure-u-9-24 { + width: 37.5000%; + *width: 37.4690%; +} + +.pure-u-2-5 { + width: 40%; + *width: 39.9690%; +} + +.pure-u-5-12, +.pure-u-10-24 { + width: 41.6667%; + *width: 41.6357%; +} + +.pure-u-11-24 { + width: 45.8333%; + *width: 45.8023%; +} + +.pure-u-1-2, +.pure-u-12-24 { + width: 50%; + *width: 49.9690%; +} + +.pure-u-13-24 { + width: 54.1667%; + *width: 54.1357%; +} + +.pure-u-7-12, +.pure-u-14-24 { + width: 58.3333%; + *width: 58.3023%; +} + +.pure-u-3-5 { + width: 60%; + *width: 59.9690%; +} + +.pure-u-5-8, +.pure-u-15-24 { + width: 62.5000%; + *width: 62.4690%; +} + +.pure-u-2-3, +.pure-u-16-24 { + width: 66.6667%; + *width: 66.6357%; +} + +.pure-u-17-24 { + width: 70.8333%; + *width: 70.8023%; +} + +.pure-u-3-4, +.pure-u-18-24 { + width: 75%; + *width: 74.9690%; +} + +.pure-u-19-24 { + width: 79.1667%; + *width: 79.1357%; +} + +.pure-u-4-5 { + width: 80%; + *width: 79.9690%; +} + +.pure-u-5-6, +.pure-u-20-24 { + width: 83.3333%; + *width: 83.3023%; +} + +.pure-u-7-8, +.pure-u-21-24 { + width: 87.5000%; + *width: 87.4690%; +} + +.pure-u-11-12, +.pure-u-22-24 { + width: 91.6667%; + *width: 91.6357%; +} + +.pure-u-23-24 { + width: 95.8333%; + *width: 95.8023%; +} + +.pure-u-1, +.pure-u-1-1, +.pure-u-5-5, +.pure-u-24-24 { + width: 100%; +} +.pure-button { + /* Structure */ + display: inline-block; + zoom: 1; + line-height: normal; + white-space: nowrap; + vertical-align: middle; + text-align: center; + cursor: pointer; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-sizing: border-box; +} + +/* Firefox: Get rid of the inner focus border */ +.pure-button::-moz-focus-inner { + padding: 0; + border: 0; +} + +/* Inherit .pure-g styles */ +.pure-button-group { + letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ + *letter-spacing: normal; /* reset IE < 8 */ + *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ +} + +.opera-only :-o-prefocus, +.pure-button-group { + word-spacing: -0.43em; +} + +.pure-button-group .pure-button { + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +/*csslint outline-none:false*/ + +.pure-button { + font-family: inherit; + font-size: 100%; + padding: 0.5em 1em; + color: #444; /* rgba not supported (IE 8) */ + color: rgba(0, 0, 0, 0.80); /* rgba supported */ + border: 1px solid #999; /*IE 6/7/8*/ + border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ + background-color: #E6E6E6; + text-decoration: none; + border-radius: 2px; +} + +.pure-button-hover, +.pure-button:hover, +.pure-button:focus { + /* csslint ignore:start */ + filter: alpha(opacity=90); + /* csslint ignore:end */ + background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); +} +.pure-button:focus { + outline: 0; +} +.pure-button-active, +.pure-button:active { + box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; + border-color: #000\9; +} + +.pure-button[disabled], +.pure-button-disabled, +.pure-button-disabled:hover, +.pure-button-disabled:focus, +.pure-button-disabled:active { + border: none; + background-image: none; + /* csslint ignore:start */ + filter: alpha(opacity=40); + /* csslint ignore:end */ + opacity: 0.40; + cursor: not-allowed; + box-shadow: none; + pointer-events: none; +} + +.pure-button-hidden { + display: none; +} + +.pure-button-primary, +.pure-button-selected, +a.pure-button-primary, +a.pure-button-selected { + background-color: rgb(0, 120, 231); + color: #fff; +} + +/* Button Groups */ +.pure-button-group .pure-button { + margin: 0; + border-radius: 0; + border-right: 1px solid #111; /* fallback color for rgba() for IE7/8 */ + border-right: 1px solid rgba(0, 0, 0, 0.2); + +} + +.pure-button-group .pure-button:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} +.pure-button-group .pure-button:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-right: none; +} + +/*csslint box-model:false*/ +/* +Box-model set to false because we're setting a height on select elements, which +also have border and padding. This is done because some browsers don't render +the padding. We explicitly set the box-model for select elements to border-box, +so we can ignore the csslint warning. +*/ + +.pure-form input[type="text"], +.pure-form input[type="password"], +.pure-form input[type="email"], +.pure-form input[type="url"], +.pure-form input[type="date"], +.pure-form input[type="month"], +.pure-form input[type="time"], +.pure-form input[type="datetime"], +.pure-form input[type="datetime-local"], +.pure-form input[type="week"], +.pure-form input[type="number"], +.pure-form input[type="search"], +.pure-form input[type="tel"], +.pure-form input[type="color"], +.pure-form select, +.pure-form textarea { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + vertical-align: middle; + box-sizing: border-box; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]) { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + box-sizing: border-box; +} + + +/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */ +/* May be able to remove this tweak as color inputs become more standardized across browsers. */ +.pure-form input[type="color"] { + padding: 0.2em 0.5em; +} + + +.pure-form input[type="text"]:focus, +.pure-form input[type="password"]:focus, +.pure-form input[type="email"]:focus, +.pure-form input[type="url"]:focus, +.pure-form input[type="date"]:focus, +.pure-form input[type="month"]:focus, +.pure-form input[type="time"]:focus, +.pure-form input[type="datetime"]:focus, +.pure-form input[type="datetime-local"]:focus, +.pure-form input[type="week"]:focus, +.pure-form input[type="number"]:focus, +.pure-form input[type="search"]:focus, +.pure-form input[type="tel"]:focus, +.pure-form input[type="color"]:focus, +.pure-form select:focus, +.pure-form textarea:focus { + outline: 0; + border-color: #129FEA; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]):focus { + outline: 0; + border-color: #129FEA; +} + +.pure-form input[type="file"]:focus, +.pure-form input[type="radio"]:focus, +.pure-form input[type="checkbox"]:focus { + outline: thin solid #129FEA; + outline: 1px auto #129FEA; +} +.pure-form .pure-checkbox, +.pure-form .pure-radio { + margin: 0.5em 0; + display: block; +} + +.pure-form input[type="text"][disabled], +.pure-form input[type="password"][disabled], +.pure-form input[type="email"][disabled], +.pure-form input[type="url"][disabled], +.pure-form input[type="date"][disabled], +.pure-form input[type="month"][disabled], +.pure-form input[type="time"][disabled], +.pure-form input[type="datetime"][disabled], +.pure-form input[type="datetime-local"][disabled], +.pure-form input[type="week"][disabled], +.pure-form input[type="number"][disabled], +.pure-form input[type="search"][disabled], +.pure-form input[type="tel"][disabled], +.pure-form input[type="color"][disabled], +.pure-form select[disabled], +.pure-form textarea[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type])[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} +.pure-form input[readonly], +.pure-form select[readonly], +.pure-form textarea[readonly] { + background-color: #eee; /* menu hover bg color */ + color: #777; /* menu text color */ + border-color: #ccc; +} + +.pure-form input:focus:invalid, +.pure-form textarea:focus:invalid, +.pure-form select:focus:invalid { + color: #b94a48; + border-color: #e9322d; +} +.pure-form input[type="file"]:focus:invalid:focus, +.pure-form input[type="radio"]:focus:invalid:focus, +.pure-form input[type="checkbox"]:focus:invalid:focus { + outline-color: #e9322d; +} +.pure-form select { + /* Normalizes the height; padding is not sufficient. */ + height: 2.25em; + border: 1px solid #ccc; + background-color: white; +} +.pure-form select[multiple] { + height: auto; +} +.pure-form label { + margin: 0.5em 0 0.2em; +} +.pure-form fieldset { + margin: 0; + padding: 0.35em 0 0.75em; + border: 0; +} +.pure-form legend { + display: block; + width: 50%; + padding: 0.3em 0; + margin-bottom: 0.3em; + margin-left: 5px; + color: #333; + /* border-bottom: 1px solid #e5e5e5; */ + border-bottom: 1px solid #bce8f1; +} + +.pure-form-stacked input[type="text"], +.pure-form-stacked input[type="password"], +.pure-form-stacked input[type="email"], +.pure-form-stacked input[type="url"], +.pure-form-stacked input[type="date"], +.pure-form-stacked input[type="month"], +.pure-form-stacked input[type="time"], +.pure-form-stacked input[type="datetime"], +.pure-form-stacked input[type="datetime-local"], +.pure-form-stacked input[type="week"], +.pure-form-stacked input[type="number"], +.pure-form-stacked input[type="search"], +.pure-form-stacked input[type="tel"], +.pure-form-stacked input[type="color"], +.pure-form-stacked input[type="file"], +.pure-form-stacked select, +.pure-form-stacked label, +.pure-form-stacked textarea { + display: block; + margin: 0.25em 0; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form-stacked input:not([type]) { + display: block; + margin: 0.25em 0; +} +.pure-form-aligned input, +.pure-form-aligned textarea, +.pure-form-aligned select, +/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ +.pure-form-aligned .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + *display: inline; + *zoom: 1; + vertical-align: middle; +} +.pure-form-aligned textarea { + vertical-align: top; +} + +/* Aligned Forms */ +.pure-form-aligned .pure-control-group { + margin-bottom: 0.5em; +} +.pure-form-aligned .pure-control-group label { + text-align: right; + display: inline-block; + vertical-align: middle; + width: 10em; + margin: 0 1em 0 0; +} +.pure-form-aligned .pure-controls { + margin: 1.5em 0 0 11em; +} + +/* Rounded Inputs */ +.pure-form input.pure-input-rounded, +.pure-form .pure-input-rounded { + border-radius: 2em; + padding: 0.5em 1em; +} + +/* Grouped Inputs */ +.pure-form .pure-group fieldset { + margin-bottom: 10px; +} +.pure-form .pure-group input, +.pure-form .pure-group textarea { + display: block; + padding: 10px; + margin: 0 0 -1px; + border-radius: 0; + position: relative; + top: -1px; +} +.pure-form .pure-group input:focus, +.pure-form .pure-group textarea:focus { + z-index: 3; +} +.pure-form .pure-group input:first-child, +.pure-form .pure-group textarea:first-child { + top: 1px; + border-radius: 4px 4px 0 0; + margin: 0; +} +.pure-form .pure-group input:first-child:last-child, +.pure-form .pure-group textarea:first-child:last-child { + top: 1px; + border-radius: 4px; + margin: 0; +} +.pure-form .pure-group input:last-child, +.pure-form .pure-group textarea:last-child { + top: -2px; + border-radius: 0 0 4px 4px; + margin: 0; +} +.pure-form .pure-group button { + margin: 0.35em 0; +} + +.pure-form .pure-input-1 { + width: 100%; +} +.pure-form .pure-input-3-4 { + width: 75%; +} +.pure-form .pure-input-2-3 { + width: 66%; +} +.pure-form .pure-input-1-2 { + width: 50%; +} +.pure-form .pure-input-1-3 { + width: 33%; +} +.pure-form .pure-input-1-4 { + width: 25%; +} + +/* Inline help for forms */ +/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ +.pure-form .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + padding-left: 0.3em; + color: #666; + vertical-align: middle; + font-size: 0.875em; +} + +/* Block help for forms */ +.pure-form-message { + display: block; + color: #666; + font-size: 0.875em; +} + +@media only screen and (max-width : 480px) { + .pure-form button[type="submit"] { + margin: 0.7em 0 0; + } + + .pure-form input:not([type]), + .pure-form input[type="text"], + .pure-form input[type="password"], + .pure-form input[type="email"], + .pure-form input[type="url"], + .pure-form input[type="date"], + .pure-form input[type="month"], + .pure-form input[type="time"], + .pure-form input[type="datetime"], + .pure-form input[type="datetime-local"], + .pure-form input[type="week"], + .pure-form input[type="number"], + .pure-form input[type="search"], + .pure-form input[type="tel"], + .pure-form input[type="color"], + .pure-form label { + margin-bottom: 0.3em; + display: block; + } + + .pure-group input:not([type]), + .pure-group input[type="text"], + .pure-group input[type="password"], + .pure-group input[type="email"], + .pure-group input[type="url"], + .pure-group input[type="date"], + .pure-group input[type="month"], + .pure-group input[type="time"], + .pure-group input[type="datetime"], + .pure-group input[type="datetime-local"], + .pure-group input[type="week"], + .pure-group input[type="number"], + .pure-group input[type="search"], + .pure-group input[type="tel"], + .pure-group input[type="color"] { + margin-bottom: 0; + } + + .pure-form-aligned .pure-control-group label { + margin-bottom: 0.3em; + text-align: left; + display: block; + width: 100%; + } + + .pure-form-aligned .pure-controls { + margin: 1.5em 0 0 0; + } + + /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ + .pure-form .pure-help-inline, + .pure-form-message-inline, + .pure-form-message { + display: block; + font-size: 0.75em; + /* Increased bottom padding to make it group with its related input element. */ + padding: 0.2em 0 0.8em; + } +} + +/*csslint adjoining-classes: false, box-model:false*/ +.pure-menu { + box-sizing: border-box; +} + +.pure-menu-fixed { + position: fixed; + left: 0; + top: 0; + z-index: 3; +} + +.pure-menu-list, +.pure-menu-item { + position: relative; +} + +.pure-menu-list { + list-style: none; + margin: 0; + padding: 0; +} + +.pure-menu-item { + padding: 0; + margin: 0; + height: 100%; +} + +.pure-menu-link, +.pure-menu-heading { + display: block; + text-decoration: none; + white-space: nowrap; +} + +/* HORIZONTAL MENU */ +.pure-menu-horizontal { + width: 100%; + white-space: nowrap; +} + +.pure-menu-horizontal .pure-menu-list { + display: inline-block; +} + +/* Initial menus should be inline-block so that they are horizontal */ +.pure-menu-horizontal .pure-menu-item, +.pure-menu-horizontal .pure-menu-heading, +.pure-menu-horizontal .pure-menu-separator { + display: inline-block; + *display: inline; + zoom: 1; + vertical-align: middle; +} + +/* Submenus should still be display: block; */ +.pure-menu-item .pure-menu-item { + display: block; +} + +.pure-menu-children { + display: none; + position: absolute; + left: 100%; + top: 0; + margin: 0; + padding: 0; + z-index: 3; +} + +.pure-menu-horizontal .pure-menu-children { + left: 0; + top: auto; + width: inherit; +} + +.pure-menu-allow-hover:hover > .pure-menu-children, +.pure-menu-active > .pure-menu-children { + display: block; + position: absolute; +} + +/* Vertical Menus - show the dropdown arrow */ +.pure-menu-has-children > .pure-menu-link:after { + padding-left: 0.5em; + content: "\25B8"; + font-size: small; +} + +/* Horizontal Menus - show the dropdown arrow */ +.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after { + content: "\25BE"; +} + +/* scrollable menus */ +.pure-menu-scrollable { + overflow-y: scroll; + overflow-x: scroll; +} + +.pure-menu-scrollable .pure-menu-list { + display: block; +} + +.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { + display: inline-block; +} + +.pure-menu-horizontal.pure-menu-scrollable { + white-space: nowrap; + overflow-y: hidden; + overflow-x: auto; + -ms-overflow-style: none; + -webkit-overflow-scrolling: touch; + /* a little extra padding for this style to allow for scrollbars */ + padding: .5em 0; +} + +.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar { + display: none; +} + +/* misc default styling */ + +.pure-menu-separator, +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + background-color: #ccc; + height: 1px; + margin: .3em 0; +} + +.pure-menu-horizontal .pure-menu-separator { + width: 1px; + height: 1.3em; + margin: 0 .3em ; +} + +/* Need to reset the separator since submenu is vertical */ +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + display: block; + width: auto; +} + +.pure-menu-heading { + text-transform: uppercase; + color: #565d64; +} + +.pure-menu-link { + /* color: #777; */ +} + +.pure-menu-children { + background-color: #fff; +} + +.pure-menu-link, +.pure-menu-disabled, +.pure-menu-heading { + padding: .5em 1em; +} + +.pure-menu-disabled { + opacity: .5; +} + +.pure-menu-disabled .pure-menu-link:hover { + background-color: transparent; +} + +.pure-menu-active > .pure-menu-link, +.pure-menu-link:hover, +.pure-menu-link:focus { + background-color: #eee; +} + +.pure-menu-selected .pure-menu-link, +.pure-menu-selected .pure-menu-link:visited { + color: #000; +} + +.pure-table { + /* Remove spacing between table cells (from Normalize.css) */ + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + border: 1px solid #cbcbcb; +} + +.pure-table caption { + color: #000; + font: italic 85%/1 arial, sans-serif; + padding: 1em 0; + text-align: center; +} + +.pure-table td, +.pure-table th { + border-left: 1px solid #cbcbcb;/* inner column border */ + border-width: 0 0 0 1px; + font-size: inherit; + margin: 0; + overflow: visible; /*to make ths where the title is really long work*/ + padding: 0.5em 1em; /* cell padding */ +} + +/* Consider removing this next declaration block, as it causes problems when +there's a rowspan on the first cell. Case added to the tests. issue#432 */ +.pure-table td:first-child, +.pure-table th:first-child { + border-left-width: 0; +} + +.pure-table thead { + background-color: #e0e0e0; + color: #000; + text-align: left; + vertical-align: bottom; +} + +/* +striping: + even - #fff (white) + odd - #f2f2f2 (light gray) +*/ +.pure-table td { + background-color: transparent; +} +.pure-table-odd td { + background-color: #f2f2f2; +} + +/* nth-child selector for modern browsers */ +.pure-table-striped tr:nth-child(2n-1) td { + background-color: #f2f2f2; +} + +/* BORDERED TABLES */ +.pure-table-bordered td { + border-bottom: 1px solid #cbcbcb; +} +.pure-table-bordered tbody > tr:last-child > td { + border-bottom-width: 0; +} + + +/* HORIZONTAL BORDERED TABLES */ + +.pure-table-horizontal td, +.pure-table-horizontal th { + border-width: 0 0 1px 0; + border-bottom: 1px solid #cbcbcb; +} +.pure-table-horizontal tbody > tr:last-child > td { + border-bottom-width: 0; +} + + +/* + * -- HELPER STYLES -- + * Over-riding some of the .pure-button styles to make my buttons look unique + */ +.button-success, +.button-error, +.button-warning, +.button-secondary { + color: white; + border-radius: 4px; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); +} + +.button-success { + background: rgb(28, 184, 65); /* this is a green */ +} + +.button-error { + background: rgb(202, 60, 60); /* this is a maroon */ +} + +.button-warning { + background: rgb(223, 117, 20); /* this is an orange */ +} + +.button-secondary { + background: rgb(66, 184, 221); /* this is a light blue */ +} + + +.custom-restricted { + height: 160px; + width: 150px; + border: 1px solid gray; + border-radius: 4px; +} + diff --git a/extensions/database/module/styles/theme.less b/extensions/database/module/styles/theme.less new file mode 100644 index 000000000..443a411bc --- /dev/null +++ b/extensions/database/module/styles/theme.less @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +@import-less url("../../../../main/webapp/modules/core/styles/theme.less"); diff --git a/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java b/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java new file mode 100644 index 000000000..0da3fa055 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.importers.TabularImportingParserBase.TableDataReader; +import com.google.refine.importing.ImportingJob; + + +public class DBQueryResultImportReader implements TableDataReader { + + private static final Logger logger = LoggerFactory.getLogger("DBQueryResultImportReader"); + + private final ImportingJob job; + private final String querySource; + private List dbColumns; + private final int batchSize; + + private int nextRow = 0; // 0-based + private int batchRowStart = 0; // 0-based + private boolean end = false; + private List> rowsOfCells = null; + private boolean usedHeaders = false; + private DatabaseService databaseService; + private DatabaseQueryInfo dbQueryInfo; + private int processedRows = 0; + private static int progress = 0; + + + public DBQueryResultImportReader( + ImportingJob job, + DatabaseService databaseService, + String querySource, + List columns, + DatabaseQueryInfo dbQueryInfo, + int batchSize) { + + this.job = job; + this.querySource = querySource; + this.batchSize = batchSize; + this.dbColumns = columns; + this.databaseService = databaseService; + this.dbQueryInfo = dbQueryInfo; + if(logger.isDebugEnabled()) { + logger.debug("batchSize:" + batchSize); + } + + } + + @Override + public List getNextRowOfCells() throws IOException { + + try { + + if (!usedHeaders) { + List row = new ArrayList(dbColumns.size()); + for (DatabaseColumn cd : dbColumns) { + row.add(cd.getName()); + } + usedHeaders = true; + //logger.info("Exit::getNextRowOfCells return header::row:" + row); + return row; + } + + if (rowsOfCells == null || (nextRow >= batchRowStart + rowsOfCells.size() && !end)) { + int newBatchRowStart = batchRowStart + (rowsOfCells == null ? 0 : rowsOfCells.size()); + rowsOfCells = getRowsOfCells(newBatchRowStart); + processedRows = processedRows + rowsOfCells.size(); + batchRowStart = newBatchRowStart; + setProgress(job, querySource, -1 /* batchRowStart * 100 / totalRows */); + } + + if (rowsOfCells != null && nextRow - batchRowStart < rowsOfCells.size()) { + List result = rowsOfCells.get(nextRow++ - batchRowStart); + if(nextRow >= batchSize) { + rowsOfCells = getRowsOfCells(processedRows); + processedRows = processedRows + rowsOfCells.size(); + + if(logger.isDebugEnabled()) { + logger.debug("[[ Returning last row in batch:nextRow::{}, processedRows:{} ]]", nextRow, processedRows); + } + + nextRow = 0; + if(processedRows % 100 == 0) { + setProgress(job, querySource, progress++); + } + if(processedRows % 10000 == 0) { + if(logger.isDebugEnabled()) { + logger.debug("[[ {} rows processed... ]]",processedRows); + } + } + } + return result; + } else { + if(logger.isDebugEnabled()) { + logger.debug("[[processedRows:{} ]]", processedRows); + } + return null; + } + + + }catch(DatabaseServiceException e) { + logger.error("DatabaseServiceException::{}", e); + throw new IOException(e); + + } + + + } + + /** + * @param startRow + * @return + * @throws IOException + * @throws DatabaseServiceException + */ + private List> getRowsOfCells(int startRow) throws IOException, DatabaseServiceException { + //logger.info("Entry getRowsOfCells::startRow:" + startRow); + + List> rowsOfCells = new ArrayList>(batchSize); + + String query = databaseService.buildLimitQuery(batchSize, startRow, dbQueryInfo.getQuery()); + //logger.info("batchSize::" + batchSize + " startRow::" + startRow + " query::" + query ); + + List dbRows = databaseService.getRows(dbQueryInfo.getDbConfig(), query); + + if(dbRows != null && !dbRows.isEmpty() && dbRows.size() > 0) { + + for(DatabaseRow dbRow: dbRows) { + List row = dbRow.getValues(); + List rowOfCells = new ArrayList(row.size()); + + for (int j = 0; j < row.size() && j < dbColumns.size(); j++) { + + String text = row.get(j); + if (text == null || text.isEmpty()) { + rowOfCells.add(null); + }else { + DatabaseColumn col = dbColumns.get(j); + if(col.getType() == DatabaseColumnType.NUMBER) { + try { + rowOfCells.add(Long.parseLong(text)); + continue; + } catch (NumberFormatException e) {} + + }else if(col.getType() == DatabaseColumnType.DOUBLE || col.getType() == DatabaseColumnType.FLOAT ) { + try { + double d = Double.parseDouble(text); + if (!Double.isInfinite(d) && !Double.isNaN(d)) { + rowOfCells.add(d); + continue; + } + } catch (NumberFormatException e) {} + + } + + rowOfCells.add(text); + } + + } + + rowsOfCells.add(rowOfCells); + + } + + } + end = dbRows.size() < batchSize + 1; + //logger.info("Exit::getRowsOfCells::rowsOfCells:{}", rowsOfCells); + return rowsOfCells; + + } + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } + + public List getColumns() { + return dbColumns; + } + + + public void setColumns(List columns) { + this.dbColumns = columns; + } + + + public int getNextRow() { + return nextRow; + } + + + public void setNextRow(int nextRow) { + this.nextRow = nextRow; + } + + + public int getBatchRowStart() { + return batchRowStart; + } + + + public void setBatchRowStart(int batchRowStart) { + this.batchRowStart = batchRowStart; + } + + + public boolean isEnd() { + return end; + } + + + public void setEnd(boolean end) { + this.end = end; + } + + + public List> getRowsOfCells() { + return rowsOfCells; + } + + + public void setRowsOfCells(List> rowsOfCells) { + this.rowsOfCells = rowsOfCells; + } + + + public boolean isUsedHeaders() { + return usedHeaders; + } + + + public void setUsedHeaders(boolean usedHeaders) { + this.usedHeaders = usedHeaders; + } + + + public ImportingJob getJob() { + return job; + } + + + public String getQuerySource() { + return querySource; + } + + + public int getBatchSize() { + return batchSize; + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java b/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java new file mode 100644 index 000000000..95f8a6cf7 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.importers.TabularImportingParserBase.TableDataReader; +import com.google.refine.importing.ImportingJob; + + +public class DBQueryResultPreviewReader implements TableDataReader { + + private static final Logger logger = LoggerFactory.getLogger("DBQueryResultPreviewReader"); + + private final ImportingJob job; + private final String querySource; + private List dbColumns; + private final int batchSize; + + private int nextRow = 0; // 0-based + private int batchRowStart = 0; // 0-based + private boolean end = false; + private List> rowsOfCells = null; + private boolean usedHeaders = false; + private DatabaseService databaseService; + private DatabaseQueryInfo dbQueryInfo; + + + public DBQueryResultPreviewReader( + ImportingJob job, + DatabaseService databaseService, + String querySource, + List columns, + DatabaseQueryInfo dbQueryInfo, + int batchSize) { + + this.job = job; + this.querySource = querySource; + this.batchSize = batchSize; + this.dbColumns = columns; + this.databaseService = databaseService; + this.dbQueryInfo = dbQueryInfo; + logger.debug("DBQueryResultPreviewReader::batchSize:" + batchSize); + + } + + @Override + public List getNextRowOfCells() throws IOException { + + // logger.info("Entry::getNextRowOfCells"); + + try { + + if (!usedHeaders) { + List row = new ArrayList(dbColumns.size()); + for (DatabaseColumn cd : dbColumns) { + row.add(cd.getName()); + } + usedHeaders = true; + // logger.debug("Exit::getNextRowOfCells return header::row:" + row); + return row; + } + + if (rowsOfCells == null || (nextRow >= batchRowStart + rowsOfCells.size() && !end)) { + int newBatchRowStart = batchRowStart + (rowsOfCells == null ? 0 : rowsOfCells.size()); + rowsOfCells = getRowsOfCells(newBatchRowStart); + batchRowStart = newBatchRowStart; + setProgress(job, querySource, -1 /* batchRowStart * 100 / totalRows */); + // logger.info("getNextRowOfCells:: rowsOfCellsIsNull::rowsOfCells size:" + rowsOfCells.size() + ":batchRowStart:" + batchRowStart + " ::nextRow:" + nextRow); + } + + if (rowsOfCells != null && nextRow - batchRowStart < rowsOfCells.size()) { + //logger.info("Exit::getNextRowOfCells :rowsOfCellsNotNull::rowsOfCells size:" + rowsOfCells.size() + ":batchRowStart:" + batchRowStart + " ::nextRow:" + nextRow); + return rowsOfCells.get(nextRow++ - batchRowStart); + } else { + if(logger.isDebugEnabled()) { + logger.debug("nextRow:{}, batchRowStart:{}", nextRow, batchRowStart); + } + + return null; + } + + + }catch(DatabaseServiceException e) { + logger.error("DatabaseServiceException::preview:{}", e.getMessage()); + IOException ioEx = new IOException(e.getMessage(), e); + throw ioEx; + + } + + } + + /** + * + * @param startRow + * @return + * @throws IOException + * @throws DatabaseServiceException + */ + private List> getRowsOfCells(int startRow) throws IOException, DatabaseServiceException { + //logger.info("Entry getRowsOfCells::startRow:" + startRow); + + List> rowsOfCells = new ArrayList>(batchSize); + + String query = databaseService.buildLimitQuery(batchSize, startRow, dbQueryInfo.getQuery()); + if(logger.isDebugEnabled()) { + logger.debug("batchSize::" + batchSize + " startRow::" + startRow + " query::" + query ); + } + + List dbRows = databaseService.getRows(dbQueryInfo.getDbConfig(), query); + + if(dbRows != null && !dbRows.isEmpty() && dbRows.size() > 0) { + + for(DatabaseRow dbRow: dbRows) { + List row = dbRow.getValues(); + List rowOfCells = new ArrayList(row.size()); + + for (int j = 0; j < row.size() && j < dbColumns.size(); j++) { + + String text = row.get(j); + if (text == null || text.isEmpty()) { + rowOfCells.add(null); + }else { + DatabaseColumn col = dbColumns.get(j); + if(col.getType() == DatabaseColumnType.NUMBER) { + try { + rowOfCells.add(Long.parseLong(text)); + continue; + } catch (NumberFormatException e) {} + + }else if(col.getType() == DatabaseColumnType.DOUBLE || col.getType() == DatabaseColumnType.FLOAT ) { + try { + double d = Double.parseDouble(text); + if (!Double.isInfinite(d) && !Double.isNaN(d)) { + rowOfCells.add(d); + continue; + } + } catch (NumberFormatException e) {} + + } + + rowOfCells.add(text); + } + + } + rowsOfCells.add(rowOfCells); + + } + + } + end = dbRows.size() < batchSize + 1; + //logger.info("Exit::getRowsOfCells::rowsOfCells:{}", rowsOfCells); + return rowsOfCells; + + } + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } + + public List getColumns() { + return dbColumns; + } + + + public void setColumns(List columns) { + this.dbColumns = columns; + } + + + public int getNextRow() { + return nextRow; + } + + + public void setNextRow(int nextRow) { + this.nextRow = nextRow; + } + + + public int getBatchRowStart() { + return batchRowStart; + } + + + public void setBatchRowStart(int batchRowStart) { + this.batchRowStart = batchRowStart; + } + + + public boolean isEnd() { + return end; + } + + + public void setEnd(boolean end) { + this.end = end; + } + + + public List> getRowsOfCells() { + return rowsOfCells; + } + + + public void setRowsOfCells(List> rowsOfCells) { + this.rowsOfCells = rowsOfCells; + } + + + public boolean isUsedHeaders() { + return usedHeaders; + } + + + public void setUsedHeaders(boolean usedHeaders) { + this.usedHeaders = usedHeaders; + } + + + public ImportingJob getJob() { + return job; + } + + + public String getQuerySource() { + return querySource; + } + + + public int getBatchSize() { + return batchSize; + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java b/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java new file mode 100644 index 000000000..79a0c8f3a --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + + +public enum DatabaseColumnType { + + STRING, + NUMBER, + DATETIME, + LOCATION, + BOOLEAN, + DATE, + DOUBLE, + FLOAT + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java new file mode 100644 index 000000000..85c4cd6e8 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + + +public class DatabaseConfiguration { + + private String connectionName; + private String databaseType; + private String databaseHost; + private int databasePort; + private String databaseUser; + private String databasePassword; + private String databaseName; + private String databaseSchema; + + //optional parameters + private boolean useSSL; + + + public String getConnectionName() { + return connectionName; + } + + public void setConnectionName(String connectionName) { + this.connectionName = connectionName; + } + + public String getDatabaseType() { + return databaseType; + } + + public void setDatabaseType(String databaseType) { + this.databaseType = databaseType; + } + + public String getDatabaseHost() { + return databaseHost; + } + + public void setDatabaseHost(String databaseServer) { + this.databaseHost = databaseServer; + } + + public int getDatabasePort() { + return databasePort; + } + + public void setDatabasePort(int databasePort) { + this.databasePort = databasePort; + } + + public String getDatabaseUser() { + return databaseUser; + } + + public void setDatabaseUser(String databaseUser) { + this.databaseUser = databaseUser; + } + + public String getDatabasePassword() { + return databasePassword; + } + + public void setDatabasePassword(String databasePassword) { + this.databasePassword = databasePassword; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String initialDatabase) { + this.databaseName = initialDatabase; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public void setDatabaseSchema(String initialSchema) { + this.databaseSchema = initialSchema; + } + + + + public boolean isUseSSL() { + return useSSL; + } + + public void setUseSSL(boolean useSSL) { + this.useSSL = useSSL; + } + + @Override + public String toString() { + return "DatabaseConfiguration [connectionName=" + connectionName + ", databaseType=" + databaseType + + ", databaseHost=" + databaseHost + ", databasePort=" + databasePort + ", databaseUser=" + databaseUser + + ", databaseName=" + databaseName + ", databaseSchema=" + + databaseSchema + ", useSSL=" + useSSL + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java b/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java new file mode 100644 index 000000000..f5650acf6 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.refine.extension.database; + +import java.io.IOException; +import java.io.Writer; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.ProjectManager; +import com.google.refine.RefineServlet; +import com.google.refine.commands.HttpUtilities; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.importers.TabularImportingParserBase; +import com.google.refine.importing.ImportingController; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.util.JSONUtilities; +import com.google.refine.util.ParsingUtilities; + + +public class DatabaseImportController implements ImportingController { + + private static final Logger logger = LoggerFactory.getLogger("DatabaseImportController"); + protected RefineServlet servlet; + public static int DEFAULT_PREVIEW_LIMIT = 100; + public static String OPTIONS_KEY = "options"; + + @Override + public void init(RefineServlet servlet) { + this.servlet = servlet; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + HttpUtilities.respond(response, "error", "GET not implemented"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + if(logger.isDebugEnabled()){ + logger.debug("doPost Query String::{}", request.getQueryString()); + } + response.setCharacterEncoding("UTF-8"); + Properties parameters = ParsingUtilities.parseUrlParameters(request); + + String subCommand = parameters.getProperty("subCommand"); + + if(logger.isDebugEnabled()){ + logger.info("doPost::subCommand::{}", subCommand); + } + + if ("initialize-parser-ui".equals(subCommand)) { + doInitializeParserUI(request, response, parameters); + } else if ("parse-preview".equals(subCommand)) { + try { + + doParsePreview(request, response, parameters); + + } catch (DatabaseServiceException e) { + logger.error("doPost::DatabaseServiceException::{}", e); + HttpUtilities.respond(response, "error", getDbServiceException(e)); + } + } else if ("create-project".equals(subCommand)) { + doCreateProject(request, response, parameters); + } else { + HttpUtilities.respond(response, "error", "No such sub command"); + } + + } + + private String getDbServiceException(Exception ex) { + String message = ""; + if(ex instanceof DatabaseServiceException) { + DatabaseServiceException dbEx = (DatabaseServiceException) ex; + if(dbEx.isSqlException()) { + message = message + dbEx.getSqlCode() + " " + dbEx.getSqlState(); + } + } + message = message + ex.getMessage(); + + return message; + } + + /** + * + * @param request + * @param response + * @param parameters + * @throws ServletException + * @throws IOException + */ + private void doInitializeParserUI(HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException { + if(logger.isDebugEnabled()) { + logger.debug("::doInitializeParserUI::"); + } + + + JSONObject result = new JSONObject(); + JSONObject options = new JSONObject(); + JSONUtilities.safePut(result, "status", "ok"); + JSONUtilities.safePut(result, OPTIONS_KEY, options); + + JSONUtilities.safePut(options, "skipDataLines", 0); + JSONUtilities.safePut(options, "storeBlankRows", true); + JSONUtilities.safePut(options, "storeBlankCellsAsNulls", true); + if(logger.isDebugEnabled()) { + logger.debug("doInitializeParserUI:::{}", result.toString()); + } + + HttpUtilities.respond(response, result.toString()); + + } + + + /** + * doParsePreview + * @param request + * @param response + * @param parameters + * @throws ServletException + * @throws IOException + * @throws DatabaseServiceException + */ + private void doParsePreview( + HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException, DatabaseServiceException { + if(logger.isDebugEnabled()) { + logger.debug("JobID::{}", parameters.getProperty("jobID")); + } + + + long jobID = Long.parseLong(parameters.getProperty("jobID")); + ImportingJob job = ImportingManager.getJob(jobID); + if (job == null) { + HttpUtilities.respond(response, "error", "No such import job"); + return; + } + + + DatabaseQueryInfo databaseQueryInfo = getQueryInfo(request); + + + if(databaseQueryInfo == null) { + HttpUtilities.respond(response, "error", "Invalid or missing Query Info"); + } + + job.updating = true; + try { + JSONObject optionObj = ParsingUtilities.evaluateJsonStringToObject( + request.getParameter("options")); + + List exceptions = new LinkedList(); + + job.prepareNewProject(); + + parsePreview( + databaseQueryInfo, + job.project, + job.metadata, + job, + DEFAULT_PREVIEW_LIMIT , + optionObj, + exceptions + ); +// String exStr = getExceptionString(exceptions); +// logger.info("exceptions::" + exStr); + + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + try { + writer.object(); + if (exceptions.size() == 0) { + job.project.update(); // update all internal models, indexes, caches, etc. + writer.key("status"); + writer.value("ok"); + } else { + writer.key("status"); + writer.value("error"); + writer.key("message"); + writer.value(getExceptionString(exceptions)); +// writer.array(); +// writeErrors(writer, exceptions); +// writer.endArray(); + } + writer.endObject(); + } catch (JSONException e) { + throw new ServletException(e); + } finally { + w.flush(); + w.close(); + } + + } catch (JSONException e) { + throw new ServletException(e); + } finally { + job.touch(); + job.updating = false; + } + } + + + + private String getExceptionString(List exceptions) { + String ex = ""; + for(Exception e: exceptions) { + ex = ex + e.getLocalizedMessage() + "\n"; + } + // TODO Auto-generated method stub + return ex; + } + + /** + * + * @param dbQueryInfo + * @param project + * @param metadata + * @param job + * @param limit + * @param options + * @param exceptions + * @throws DatabaseServiceException + */ + private static void parsePreview( + DatabaseQueryInfo dbQueryInfo, + Project project, + ProjectMetadata metadata, + final ImportingJob job, + int limit, + JSONObject options, + List exceptions) throws DatabaseServiceException{ + + + DatabaseService databaseService = DatabaseService.get(dbQueryInfo.getDbConfig().getDatabaseType()); + String querySource = getQuerySource(dbQueryInfo); + + List columns = databaseService.getColumns(dbQueryInfo.getDbConfig(), dbQueryInfo.getQuery()); + + + setProgress(job, querySource, -1); + + JSONUtilities.safePut(options, "ignoreLines", 0); // number of blank lines at the beginning to ignore + JSONUtilities.safePut(options, "headerLines", 1); // number of header lines + + + TabularImportingParserBase.readTable( + project, + metadata, + job, + new DBQueryResultPreviewReader(job, databaseService, querySource, columns, dbQueryInfo, 100), + querySource, + limit, + options, + exceptions + ); + + setProgress(job, querySource, 100); + + } + + + /** + * doCreateProject + * @param request + * @param response + * @param parameters + */ + private void doCreateProject(HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException{ + if(logger.isDebugEnabled()) { + logger.debug("DatabaseImportController::doCreateProject:::{}", parameters.getProperty("jobID")); + } + + long jobID = Long.parseLong(parameters.getProperty("jobID")); + final ImportingJob job = ImportingManager.getJob(jobID); + if (job == null) { + HttpUtilities.respond(response, "error", "No such import job"); + return; + } + + final DatabaseQueryInfo databaseQueryInfo = getQueryInfo(request); + if(databaseQueryInfo == null) { + HttpUtilities.respond(response, "error", "Invalid or missing Query Info"); + } + + job.updating = true; + try { + final JSONObject optionObj = ParsingUtilities.evaluateJsonStringToObject( + request.getParameter("options")); + + final List exceptions = new LinkedList(); + + job.setState("creating-project"); + + final Project project = new Project(); + + new Thread() { + @Override + public void run() { + ProjectMetadata pm = new ProjectMetadata(); + pm.setName(JSONUtilities.getString(optionObj, "projectName", "Untitled")); + pm.setEncoding(JSONUtilities.getString(optionObj, "encoding", "UTF-8")); + + try { + parseCreate( + databaseQueryInfo, + project, + pm, + job, + -1, + optionObj, + exceptions + ); + } catch (DatabaseServiceException e) { + logger.error("DatabaseImportController::doCreateProject:::run{}", e); + // throw new RuntimeException("DatabaseServiceException::", e); + } + + if (!job.canceled) { + if (exceptions.size() > 0) { + job.setError(exceptions); + } else { + project.update(); // update all internal models, indexes, caches, etc. + ProjectManager.singleton.registerProject(project, pm); + job.setState("created-project"); + job.setProjectID(project.id); + // logger.info("DatabaseImportController::doCreateProject:::run::projectID :{}", project.id); + } + + job.touch(); + job.updating = false; + } + } + }.start(); + + HttpUtilities.respond(response, "ok", "done"); + } catch (JSONException e) { + throw new ServletException(e); + } + } + + + /** + * @param dbQueryInfo + * @param project + * @param metadata + * @param job + * @param limit + * @param options + * @param exceptions + * @throws DatabaseServiceException + */ + private static void parseCreate( + DatabaseQueryInfo dbQueryInfo, + Project project, + ProjectMetadata metadata, + final ImportingJob job, + int limit, + JSONObject options, + List exceptions) throws DatabaseServiceException{ + + + DatabaseService databaseService = DatabaseService.get(dbQueryInfo.getDbConfig().getDatabaseType()); + String querySource = getQuerySource(dbQueryInfo); + + List columns = databaseService.getColumns(dbQueryInfo.getDbConfig(), dbQueryInfo.getQuery()); + + setProgress(job, querySource, -1); + + JSONUtilities.safePut(options, "ignoreLines", 0); // number of blank lines at the beginning to ignore + JSONUtilities.safePut(options, "headerLines", 1); // number of header lines + + long startTime = System.currentTimeMillis() ; + + TabularImportingParserBase.readTable( + project, + metadata, + job, + new DBQueryResultImportReader(job, databaseService, querySource, columns, dbQueryInfo, getCreateBatchSize()), + querySource, + limit, + options, + exceptions + ); + + long endTime = System.currentTimeMillis() ; + if(logger.isDebugEnabled()) { + logger.debug("Execution Time: {}", endTime - startTime); + } + + setProgress(job, querySource, 100); + + } + + private static int getCreateBatchSize() { + String propBatchSize = DatabaseModuleImpl.getImportCreateBatchSize(); + int batchSize = 100; + if(propBatchSize != null && !propBatchSize.isEmpty()) { + try { + batchSize = Integer.parseInt(propBatchSize); + }catch(NumberFormatException nfe) { + + } + } + return batchSize; + } + + /** + * @param request + * @return + */ + private DatabaseQueryInfo getQueryInfo(HttpServletRequest request) { + DatabaseConfiguration jdbcConfig = new DatabaseConfiguration(); + jdbcConfig.setConnectionName(request.getParameter("connectionName")); + jdbcConfig.setDatabaseType(request.getParameter("databaseType")); + jdbcConfig.setDatabaseHost(request.getParameter("databaseServer")); + jdbcConfig.setDatabasePort(Integer.parseInt(request.getParameter("databasePort"))); + jdbcConfig.setDatabaseUser(request.getParameter("databaseUser")); + jdbcConfig.setDatabasePassword(request.getParameter("databasePassword")); + jdbcConfig.setDatabaseName(request.getParameter("initialDatabase")); + jdbcConfig.setDatabaseSchema(request.getParameter("initialSchema")); + + String query = request.getParameter("query"); + if(logger.isDebugEnabled()) { + logger.debug("jdbcConfig::{}, query::{}", jdbcConfig, query); + } + if (jdbcConfig.getDatabaseHost() == null || jdbcConfig.getDatabaseName() == null + || jdbcConfig.getDatabasePassword() == null || jdbcConfig.getDatabaseType() == null + || jdbcConfig.getDatabaseUser() == null || query == null) { + if(logger.isDebugEnabled()) { + logger.debug("Missing Database Configuration::{}", jdbcConfig); + } + return null; + } + + return new DatabaseQueryInfo(jdbcConfig, query); + } + + + private static String getQuerySource(DatabaseQueryInfo dbQueryInfo) { + String dbType = dbQueryInfo.getDbConfig().getDatabaseType(); + return DatabaseService.get(dbType).getDatabaseUrl(dbQueryInfo.getDbConfig()); + } + + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java b/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java new file mode 100644 index 000000000..45bb6aff7 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; + +import javax.servlet.ServletConfig; + +import org.json.JSONException; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.Jsonizable; + +import edu.mit.simile.butterfly.ButterflyModuleImpl; + + +public class DatabaseModuleImpl extends ButterflyModuleImpl implements Jsonizable { + + private static final Logger logger = LoggerFactory.getLogger("DatabaseModuleImpl"); + + public static DatabaseModuleImpl instance; + + public static Properties extensionProperties; + + private static String DEFAULT_CREATE_PROJ_BATCH_SIZE = "100"; + private static String DEFAULT_PREVIEW_BATCH_SIZE = "100"; + + + + @Override + public void init(ServletConfig config) + throws Exception { + // TODO Auto-generated method stub + super.init(config); + + + readModuleProperty(); + + // Set the singleton. + instance = this; + + logger.info("*** Database Extension Module Initialization Completed!!***"); + } + + public static String getImportCreateBatchSize() { + if(extensionProperties == null) { + return DEFAULT_CREATE_PROJ_BATCH_SIZE; + } + return extensionProperties.getProperty("create.batchSize", DEFAULT_CREATE_PROJ_BATCH_SIZE); + } + + public static String getImportPreviewBatchSize() { + if(extensionProperties == null) { + return DEFAULT_PREVIEW_BATCH_SIZE; + } + return extensionProperties.getProperty("preview.batchSize", DEFAULT_PREVIEW_BATCH_SIZE); + } + + private void readModuleProperty() { + // The module path + File f = getPath(); + if(logger.isDebugEnabled()) { + logger.debug("Module getPath(): {}", f.getPath()); + } + + // Load our custom properties. + File modFile = new File(f,"MOD-INF"); + if(logger.isDebugEnabled()) { + logger.debug("Module File: {}", modFile.getPath()); + } + + if (modFile.exists()) { + + extensionProperties = loadProperties (new File(modFile,"dbextension.properties")); + + } + + } + + private Properties loadProperties(File propFile) { + Properties ps = new Properties(); + try { + if (propFile.exists()) { + if(logger.isDebugEnabled()) { + logger.debug("Loading Extension properties ({})", propFile); + } + BufferedInputStream stream = null; + try { + ps = new Properties(); + stream = new BufferedInputStream(new FileInputStream(propFile)); + ps.load(stream); + + } finally { + // Close the stream. + if (stream != null) stream.close(); + } + + } + } catch (Exception e) { + logger.error("Error loading Database properties", e); + } + return ps; + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + // TODO Auto-generated method stub + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseService.java b/extensions/database/src/com/google/refine/extension/database/DatabaseService.java new file mode 100644 index 000000000..13668a98f --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseService.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.sql.Connection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.mariadb.MariaDBDatabaseService; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + +public abstract class DatabaseService { + + private static final Logger logger = LoggerFactory.getLogger("DatabaseService"); + + + public static class DBType { + private static Map databaseServiceMap = new HashMap(); + + static { + try { + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + + } catch (Exception e) { + logger.error("Exception occurred while trying to prepare databases!", e); + } + } + + public static void registerDatabase(String name, DatabaseService db) { + + if (!databaseServiceMap.containsKey(name)) { + //throw new DatabaseServiceException(name + " cannot be registered. Database Type already exists"); + databaseServiceMap.put(name, db); + logger.info(String.format("Registered %s Database", name)); + }else { + if(logger.isDebugEnabled()) { + logger.debug(name + " Database Type already exists"); + } + + } + + } + + public static DatabaseService getJdbcServiceFromType(String name) { + return databaseServiceMap.get(name); + } + + } + + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + } + + /** + * get Database + * @param dbType + * @return + */ + public static DatabaseService get(String dbType) { + logger.debug("get called on DatabaseService with, {}", dbType); + DatabaseService databaseService = DatabaseService.DBType.getJdbcServiceFromType(dbType.toLowerCase()); + + logger.debug("DatabaseService found: {}", databaseService.getClass()); + return databaseService; + + } + + + //Database Service APIs + public abstract Connection getConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract String buildLimitQuery(Integer limit, Integer offset, String query); + + public abstract List getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract List getRows(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java b/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java new file mode 100644 index 000000000..cb37f9040 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.sql.SQLException; + +public class DatabaseServiceException extends Exception { + + + private static final long serialVersionUID = 1L; + + private boolean sqlException; + private String sqlState; + private int sqlCode; + + + public DatabaseServiceException(String exception) { + super(exception); + } + + + public DatabaseServiceException(boolean sqlException, String sqlState, int sqlCode, String message) { + super(message); + this.sqlException = sqlException; + this.sqlState = sqlState; + this.sqlCode = sqlCode; + + } + + + public boolean isSqlException() { + return sqlException; + } + + + public void setSqlException(boolean sqlException) { + this.sqlException = sqlException; + } + + + public String getSqlState() { + return sqlState; + } + + + public void setSqlState(String sqlState) { + this.sqlState = sqlState; + } + + + public int getSqlCode() { + return sqlCode; + } + + + public void setSqlCode(int sqlCode) { + this.sqlCode = sqlCode; + } + + public DatabaseServiceException(String string, SQLException e) { + super(string, e); + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java new file mode 100644 index 000000000..db6960581 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.ProjectManager; +import com.google.refine.io.FileProjectManager; + +public class DatabaseUtils { + + private static final Logger logger = LoggerFactory.getLogger("DatabaseUtils"); + + + public final static String DATABASE_EXTENSION_DIR = "dbextension"; + public final static String SETTINGS_FILE_NAME = ".saved-db-connections.json"; + public final static String SAVED_CONNECTION_KEY = "savedConnections"; + + private static SimpleTextEncryptor textEncryptor = new SimpleTextEncryptor("Aa1Gb@tY7_Y"); + + + public static int getSavedConnectionsSize() { + List scList = getSavedConnections(); + if(scList == null || scList.isEmpty()) { + return 0; + } + + return scList.size(); + } + /** + * GET saved connections + * @return + */ + public static List getSavedConnections() { + ObjectMapper mapper = new ObjectMapper(); + try { + String filename = getExtensionFilePath(); + + File file = new File(filename); + if (!file.exists()) { + //logger.debug("saved connections file not found, creating new: {}", filename); + + String dirPath = getExtensionFolder(); + File dirFile = new File(dirPath); + boolean dirExists = true; + if(!dirFile.exists()) { + dirExists = dirFile.mkdir(); + } + + if(dirExists) { + + SavedConnectionContainer sc = new SavedConnectionContainer(new ArrayList()); + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(filename), sc); + return sc.getSavedConnections(); + //return decryptAll(sc.getSavedConnections()); + + } + + } + //logger.debug("saved connections file found {}", filename); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(filename), SavedConnectionContainer.class); + //return decryptAll(savedConnectionContainer.getSavedConnections()); + return savedConnectionContainer.getSavedConnections(); + + } catch (JsonParseException e) { + logger.error("JsonParseException: {}", e); + } catch (JsonMappingException e) { + logger.error("JsonMappingException: {}", e); + } catch (IOException e) { + logger.error("IOException: {}", e); + } + return null; + } + + + + + + /** + * GET one saved connection + * @param connectionName + * @return + */ + public static DatabaseConfiguration getSavedConnection(String connectionName) { + //logger.debug("get saved connection called with connectionName: {}", connectionName); + List savedConfigurations = getSavedConnections(); + + for (DatabaseConfiguration dc : savedConfigurations) { + //logger.debug("Saved Connection : {}", dc.getConnectionName()); + if (dc.getConnectionName().equalsIgnoreCase(connectionName.trim())) { + //logger.debug("Saved Connection Found : {}", dc); + //dc.setDatabasePassword(decrypt(dc.getDatabasePassword())); + return dc; + } + } + + return null; + } + + public static String encrypt(String plainPassword) { + return textEncryptor.encrypt(plainPassword); + } + + public static String decrypt(String encodedPassword) { + return textEncryptor.decrypt(encodedPassword); + } + + public static List decryptAll(List savedConnections) { + List dbConfigs = new ArrayList(savedConnections.size()); + + for(DatabaseConfiguration d: savedConnections) { + d.setDatabasePassword(decrypt(d.getDatabasePassword())); + dbConfigs.add(d); + + } + return dbConfigs; + } + + + /** + * ADD to saved connections + * @param dbConfig + */ + public static void addToSavedConnections(DatabaseConfiguration dbConfig){ + + try { + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + savedConnectionContainer.getSavedConnections().add(dbConfig); + + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + // e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + // e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + // e1.printStackTrace(); + } + } + + + public static void deleteAllSavedConnections() { + if(logger.isDebugEnabled()) { + logger.debug("delete All Saved Connections called..."); + } + + try { + + List savedConnections = getSavedConnections(); + if(logger.isDebugEnabled()) { + logger.debug("Size before delete SavedConnections :: {}", savedConnections.size()); + } + + ArrayList newSavedConns = new ArrayList(); + + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + savedConnectionContainer.setSavedConnections(newSavedConns); + + if(logger.isDebugEnabled()) { + logger.debug("Size after delete SavedConnections :: {}", savedConnectionContainer.getSavedConnections().size()); + } + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + // e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + // e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + // e1.printStackTrace(); + } + + } + + /** + * DELETE saved connections + * @param connectionName + */ + public static void deleteSavedConnections(String connectionName) { + if(logger.isDebugEnabled()) { + logger.debug("deleteSavedConnections called with: {}", connectionName); + } + + try { + + List savedConnections = getSavedConnections();; + if(logger.isDebugEnabled()) { + logger.debug("Size before delete SavedConnections :: {}", savedConnections.size()); + } + + ArrayList newSavedConns = new ArrayList(); + for(DatabaseConfiguration dc: savedConnections) { + if(!dc.getConnectionName().equalsIgnoreCase(connectionName.trim())) { + newSavedConns.add(dc); + } + + } + + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + savedConnectionContainer.setSavedConnections(newSavedConns); + + if(logger.isDebugEnabled()) { + logger.debug("Size after delete SavedConnections :: {}", savedConnectionContainer.getSavedConnections().size()); + } + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + // e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + // e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + // e1.printStackTrace(); + } + } + + /** + * EDIT saved connections + * @param jdbcConfig + */ + public static void editSavedConnections(DatabaseConfiguration jdbcConfig) { + if(logger.isDebugEnabled()) { + logger.debug("Edit SavedConnections called with: {}", jdbcConfig); + } + + + try { + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + + List savedConnections = savedConnectionContainer.getSavedConnections(); + + ListIterator savedConnArrayIter = (ListIterator) savedConnections.listIterator(); + + while (savedConnArrayIter.hasNext()) { + DatabaseConfiguration sc = (DatabaseConfiguration) savedConnArrayIter.next(); + + if (sc.getConnectionName().equals(jdbcConfig.getConnectionName())) { + savedConnArrayIter.remove(); + } + + } + + savedConnections.add(jdbcConfig); + savedConnectionContainer.setSavedConnections(savedConnections); + + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + e1.printStackTrace(); + } + } + + public static String getExtensionFilePath(){ + File dir = ((FileProjectManager) ProjectManager.singleton).getWorkspaceDir(); + String fileSep = System.getProperty("file.separator"); + String filename = dir.getPath() + fileSep + DATABASE_EXTENSION_DIR + fileSep + SETTINGS_FILE_NAME; + + // logger.info("** extension file name: {} **", filename); + return filename; + } + public static String getExtensionFolder(){ + File dir = ((FileProjectManager) ProjectManager.singleton).getWorkspaceDir(); + String fileSep = System.getProperty("file.separator"); + String filename = dir.getPath() + fileSep + DATABASE_EXTENSION_DIR; + return filename; + } + + public static DatabaseColumnType getDbColumnType(int dbColumnType) { + + switch (dbColumnType) { + case java.sql.Types.BIGINT: + return DatabaseColumnType.NUMBER; + case java.sql.Types.FLOAT: + return DatabaseColumnType.FLOAT; + case java.sql.Types.REAL: + return DatabaseColumnType.DOUBLE; + case java.sql.Types.DOUBLE: + return DatabaseColumnType.DOUBLE; + case java.sql.Types.NUMERIC: + return DatabaseColumnType.NUMBER; + case java.sql.Types.DECIMAL: + return DatabaseColumnType.STRING; + case java.sql.Types.CHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.VARCHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.LONGVARCHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.DATE: + return DatabaseColumnType.DATE; + case java.sql.Types.TIME: + return DatabaseColumnType.DATETIME; + case java.sql.Types.TIMESTAMP: + return DatabaseColumnType.DATETIME; + case java.sql.Types.BINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.VARBINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.LONGVARBINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.NULL: + return DatabaseColumnType.STRING; + case java.sql.Types.OTHER: + return DatabaseColumnType.STRING; + case java.sql.Types.JAVA_OBJECT: + return DatabaseColumnType.STRING; + case java.sql.Types.DISTINCT: + return DatabaseColumnType.STRING; + case java.sql.Types.STRUCT: + return DatabaseColumnType.STRING; + case java.sql.Types.ARRAY: + return DatabaseColumnType.STRING; + case java.sql.Types.BLOB: + return DatabaseColumnType.STRING; + case java.sql.Types.CLOB: + return DatabaseColumnType.STRING; + case java.sql.Types.REF: + return DatabaseColumnType.STRING; + case java.sql.Types.BOOLEAN: + return DatabaseColumnType.BOOLEAN; + case java.sql.Types.INTEGER: + return DatabaseColumnType.NUMBER; + + default: + return DatabaseColumnType.STRING; + } + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/SQLType.java b/extensions/database/src/com/google/refine/extension/database/SQLType.java new file mode 100644 index 000000000..2871b6abd --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SQLType.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + + + +import java.util.HashMap; +import java.util.Map; + + +public final class SQLType { + + private static final Map jdbcDriverRegistry = new HashMap(); + private final DriverContainer driverContainer; + + private SQLType(DriverContainer container) { + this.driverContainer = container; + } + + public static SQLType forName(String name) { + for (SQLType sqlType : jdbcDriverRegistry.values()) { + if (sqlType.getIdentifier().equalsIgnoreCase(name)) { + return sqlType; + } + } + return null; + } + + public static SQLType registerSQLDriver(String identifier, String classpath) { + return registerSQLDriver(identifier, classpath, true); + } + + public static SQLType registerSQLDriver(String identifier, String classpath, boolean useJDBCManager) { + DriverContainer driverContainer = new DriverContainer(identifier, classpath, useJDBCManager); + if (!jdbcDriverRegistry.containsKey(driverContainer)) { + SQLType newType = new SQLType(driverContainer); + jdbcDriverRegistry.put(driverContainer, newType); + return newType; + } + return null; + } + + + public String getClassPath() { + return this.driverContainer.classpath; + } + + public String getIdentifier() { + return this.driverContainer.identifier; + } + + public boolean usesJDBCManager() { + return this.driverContainer.useJDBCManager; + } + + + private static class DriverContainer { + + public final String classpath; + public final String identifier; + public final boolean useJDBCManager; + + private DriverContainer(String identifier, String classpath, boolean useJDBCManager) { + this.classpath = classpath; + this.identifier = identifier; + this.useJDBCManager = useJDBCManager; + } + + public final boolean equals(Object obj) { + return obj instanceof DriverContainer && ((DriverContainer) obj).classpath.equals(this.classpath) + && ((DriverContainer) obj).identifier.equals(this.identifier) + && ((DriverContainer) obj).useJDBCManager == this.useJDBCManager; + } + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java b/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java new file mode 100644 index 000000000..15a45f731 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import java.util.List; + +public class SavedConnectionContainer { + private List savedConnections; + + public List getSavedConnections() { + return savedConnections; + } + + + public void setSavedConnections(List savedConnections) { + this.savedConnections = savedConnections; + } + + + + public SavedConnectionContainer(List savedConnections) { + super(); + this.savedConnections = savedConnections; + } + + + public SavedConnectionContainer() { + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java b/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java new file mode 100644 index 000000000..ad5f20d95 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.util.text.TextEncryptor; + + +public class SimpleTextEncryptor implements TextEncryptor { + + private final StandardPBEStringEncryptor encryptor; + + @Override + public String decrypt(String encryptedMessage) { + return this.encryptor.decrypt(encryptedMessage); + } + + @Override + public String encrypt(String message) { + return this.encryptor.encrypt(message); + } + + public SimpleTextEncryptor(String passwordChars) { + super(); + + this.encryptor = new StandardPBEStringEncryptor(); + this.encryptor.setAlgorithm("PBEWithMD5AndDES"); + this.encryptor.setPasswordCharArray(passwordChars.toCharArray()); + } + + + + public void setPassword(final String password) { + this.encryptor.setPassword(password); + } + + + /** + * Sets a password, as a char[] + * + * @since 1.8 + * @param password the password to be set. + */ + public void setPasswordCharArray(final char[] password) { + this.encryptor.setPasswordCharArray(password); + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java new file mode 100644 index 000000000..014b30930 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class ConnectCommand extends DatabaseCommand { + + private static final Logger logger = LoggerFactory.getLogger("ConnectCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + if(logger.isDebugEnabled()) { + logger.debug("ConnectCommand::Post::{}", databaseConfiguration); + } + // ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + ObjectMapper mapperObj = new ObjectMapper(); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .connect(databaseConfiguration); + String databaseInfoString = mapperObj.writeValueAsString(databaseInfo); + response.setStatus(HttpStatus.SC_OK); + writer.object(); + writer.key("code"); + writer.value("ok"); + writer.key("databaseInfo"); + writer.value(databaseInfoString); + + writer.endObject(); + + } catch (DatabaseServiceException e) { + logger.error("ConnectCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + }catch (Exception e) { + logger.error("ConnectCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + } finally { + // w.flush(); + w.close(); + } + } catch (Exception e) { + logger.error("ConnectCommand::Post::Exception::{}", e); + throw new ServletException(e); + } +// finally { +// // ProjectManager.singleton.setBusy(false); +// } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java new file mode 100644 index 000000000..ccd967d95 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.commands.Command; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; + +public abstract class DatabaseCommand extends Command { + + private static final Logger logger = LoggerFactory.getLogger("DatabaseCommand"); + + /** + * + * @param request + * @return + */ + protected DatabaseConfiguration getJdbcConfiguration(HttpServletRequest request) { + DatabaseConfiguration jdbcConfig = new DatabaseConfiguration(); + + jdbcConfig.setConnectionName(request.getParameter("connectionName")); + jdbcConfig.setDatabaseType(request.getParameter("databaseType")); + jdbcConfig.setDatabaseHost(request.getParameter("databaseServer")); + + String dbPort = request.getParameter("databasePort"); + if(dbPort != null) { + try { + jdbcConfig.setDatabasePort(Integer.parseInt(dbPort)); + }catch(NumberFormatException nfe) {} + } + + jdbcConfig.setDatabaseUser(request.getParameter("databaseUser")); + jdbcConfig.setDatabasePassword(request.getParameter("databasePassword")); + jdbcConfig.setDatabaseName(request.getParameter("initialDatabase")); + jdbcConfig.setDatabaseSchema(request.getParameter("initialSchema")); + + if(logger.isDebugEnabled()) { + logger.debug("JDBC Configuration: {}", jdbcConfig); + } + return jdbcConfig; + } + /** + * + * @param status + * @param response + * @param writer + * @param e + * @throws IOException + */ + protected void sendError(int status, HttpServletResponse response, JSONWriter writer, Exception e) + throws IOException { + + //logger.info("sendError::{}", writer); + response.sendError(status, e.getMessage()); + + } + /** + * + * @param status + * @param response + * @param writer + * @param e + * @throws IOException + */ + protected void sendError(int status, HttpServletResponse response, JSONWriter writer, DatabaseServiceException e) + throws IOException { + + String message = ""; + + if(e.getSqlState() != null) { + + message = message + "SqlCode:" + e.getSqlCode() + "SqlState" + e.getSqlState(); + } + + message = message + e.getMessage(); + + response.sendError(status, e.getMessage()); + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java new file mode 100644 index 000000000..90a0dd387 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class ExecuteQueryCommand extends DatabaseCommand { + + private static final Logger logger = LoggerFactory.getLogger("ExecuteQueryCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + String query = request.getParameter("queryString"); + if(logger.isDebugEnabled()) { + logger.debug("QueryCommand::Post::DatabaseConfiguration::{}::Query::{} " ,databaseConfiguration, query); + } + + //ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .executeQuery(databaseConfiguration, query); + ObjectMapper mapperObj = new ObjectMapper(); + + response.setStatus(HttpStatus.SC_OK); + String jsonStr = mapperObj.writeValueAsString(databaseInfo); + + if(logger.isDebugEnabled()) { + logger.debug("QueryCommand::Post::Result::{} " ,jsonStr); + } + + + writer.object(); + writer.key("code"); + writer.value("ok"); + writer.key("QueryResult"); + writer.value(jsonStr); + writer.endObject(); + + + } catch (DatabaseServiceException e) { + logger.error("QueryCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST, response, writer, e); + + } catch (Exception e) { + logger.error("QueryCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST,response, writer, e); + } finally { + w.close(); + } + } catch (Exception e) { + logger.error("QueryCommand::Post::Exception::{}", e); + throw new ServletException(e); + } +// finally { +// // ProjectManager.singleton.setBusy(false); +// } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java new file mode 100644 index 000000000..74b3af2ea --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.json.JSONException; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseUtils; + + +public class SavedConnectionCommand extends DatabaseCommand { + + private static final Logger logger = LoggerFactory.getLogger("SavedConnectionCommand"); + + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + if(logger.isDebugEnabled()) { + logger.debug("SavedConnectionCommand::Get::connectionName::{}", request.getParameter("connectionName")); + } + + String connectionName = request.getParameter("connectionName"); + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + if(connectionName == null || connectionName.isEmpty()) { + writeSavedConnectionResponse(response); + }else { + + DatabaseConfiguration savedConnection = DatabaseUtils.getSavedConnection(connectionName); + writeSavedConnectionResponse(response, savedConnection); + + } + + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + } + + + @Override + public void doDelete(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + + if(logger.isDebugEnabled()) { + logger.debug("SavedConnectionCommand::Delete Connection: {}", request.getParameter("connectionName")); + } + + String connectionName = request.getParameter("connectionName"); + + DatabaseConfiguration savedConn = DatabaseUtils.getSavedConnection(connectionName); + if(savedConn == null) { + //logger.error("Connection With name:: {} does not exist!", request.getParameter("connectionName")); + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection with name " + connectionName + " does not exists!"); + response.flushBuffer(); + return; + } + + try { + + DatabaseUtils.deleteSavedConnections(connectionName); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + + } catch (Exception e) { + logger.error("Exception while Deleting Connection with name: {}, error:{}",connectionName, e); + } + } + + /** + * + * @param response + * @param savedConnection + * @throws IOException + * @throws JSONException + */ + private void writeSavedConnectionResponse(HttpServletResponse response, DatabaseConfiguration savedConnection) throws IOException, JSONException { + Writer w = response.getWriter(); + try { + JSONWriter writer = new JSONWriter(w); + + writer.object(); + writer.key(DatabaseUtils.SAVED_CONNECTION_KEY); + writer.array(); + + writer.object(); + writer.key("connectionName"); + writer.value(savedConnection.getConnectionName()); + + writer.key("databaseType"); + writer.value(savedConnection.getDatabaseType()); + + writer.key("databaseHost"); + writer.value(savedConnection.getDatabaseHost()); + + writer.key("databasePort"); + writer.value(savedConnection.getDatabasePort()); + + writer.key("databaseName"); + writer.value(savedConnection.getDatabaseName()); + + writer.key("databasePassword"); + + // + String dbPasswd = savedConnection.getDatabasePassword(); + if(dbPasswd != null && !dbPasswd.isEmpty()) { + dbPasswd = DatabaseUtils.decrypt(savedConnection.getDatabasePassword()); + //logger.info("Decrypted Password::" + dbPasswd); + } + writer.value(dbPasswd); + // + + // writer.value(savedConnection.getDatabasePassword()); + + writer.key("databaseSchema"); + writer.value(savedConnection.getDatabaseSchema()); + + writer.key("databaseUser"); + writer.value(savedConnection.getDatabaseUser()); + + writer.endObject(); + writer.endArray(); + + writer.endObject(); + + }finally { + w.flush(); + w.close(); + } + + } + /** + * + * @param response + * @throws IOException + * @throws JSONException + */ + private void writeSavedConnectionResponse(HttpServletResponse response) throws IOException, JSONException { + Writer w = response.getWriter(); + try { + + List savedConnections = DatabaseUtils.getSavedConnections(); + JSONWriter writer = new JSONWriter(w); + + writer.object(); + writer.key(DatabaseUtils.SAVED_CONNECTION_KEY); + writer.array(); + + int size = savedConnections.size(); + + for (int i = 0; i < size; i++) { + + writer.object(); + DatabaseConfiguration dbConfig = (DatabaseConfiguration) savedConnections.get(i); + + writer.key("connectionName"); + writer.value(dbConfig.getConnectionName()); + + writer.key("databaseType"); + writer.value(dbConfig.getDatabaseType()); + + writer.key("databaseHost"); + writer.value(dbConfig.getDatabaseHost()); + + writer.key("databasePort"); + writer.value(dbConfig.getDatabasePort()); + + writer.key("databaseName"); + writer.value(dbConfig.getDatabaseName()); + + writer.key("databasePassword"); + + String dbPasswd = dbConfig.getDatabasePassword(); + if(dbPasswd != null && !dbPasswd.isEmpty()) { + dbPasswd = DatabaseUtils.decrypt(dbConfig.getDatabasePassword()); + } + // writer.value(dbConfig.getDatabasePassword()); + writer.value(dbPasswd); + + writer.key("databaseSchema"); + writer.value(dbConfig.getDatabaseSchema()); + + writer.key("databaseUser"); + writer.value(dbConfig.getDatabaseUser()); + + writer.endObject(); + + } + writer.endArray(); + writer.endObject(); + // logger.info("Saved Connection Get Response sent"); + } finally { + w.flush(); + w.close(); + } + } + + /** + * Add a new Saved JDBC connection configuration + */ + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + if(logger.isDebugEnabled()) { + logger.debug("doPost Connection: {}", request.getParameter("connectionName")); + } + + DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request); + + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + if(jdbcConfig.getConnectionName() == null) { + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection Name is Required!"); + response.flushBuffer(); + return; + } + + DatabaseConfiguration savedConn = DatabaseUtils.getSavedConnection(jdbcConfig.getConnectionName()); + if(savedConn != null) { + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection with name " + jdbcConfig.getConnectionName() + " already exists!"); + response.flushBuffer(); + return; + } + + + if(jdbcConfig.getDatabasePassword() != null) { + //logger.debug("SavedConnectionCommand::Post::password::{}", jdbcConfig.getDatabasePassword()); + jdbcConfig.setDatabasePassword(DatabaseUtils.encrypt(jdbcConfig.getDatabasePassword())); + } + + DatabaseUtils.addToSavedConnections(jdbcConfig); + + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + + } + + + + @Override + public void doPut(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + + if(logger.isDebugEnabled()) { + logger.debug("databaseType::{} " , request.getParameter("databaseHost")); + } + // logger.info("databaseHost::{} " , request.getParameter("databaseServer")); + + DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request); + StringBuilder sb = new StringBuilder(); + boolean error = false; + if(jdbcConfig.getConnectionName() == null) { + sb.append("Connection Name, "); + error = true; + } + if(jdbcConfig.getDatabaseHost() == null) { + sb.append("Database Host, "); + error = true; + } + if(jdbcConfig.getDatabaseUser() == null) { + sb.append("Database User, "); + error = true; + } + if(jdbcConfig.getDatabaseName() == null) { + sb.append("Database Name, "); + error = true; + } + if(error) { + sb.append(" is missing"); + logger.debug("Connection Parameter errors::{}", sb.toString()); + response.sendError(HttpStatus.SC_BAD_REQUEST, sb.toString()); + } + + if(logger.isDebugEnabled()) { + logger.debug("Connection Config:: {}", jdbcConfig.getConnectionName()); + } + + if(jdbcConfig.getDatabasePassword() != null) { + jdbcConfig.setDatabasePassword(DatabaseUtils.encrypt(jdbcConfig.getDatabasePassword())); + } + + DatabaseUtils.editSavedConnections(jdbcConfig); + + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java new file mode 100644 index 000000000..b4cd0dd0b --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + + + +public class TestConnectCommand extends DatabaseCommand { + + private static final Logger logger = LoggerFactory.getLogger("TestConnectCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + if(logger.isDebugEnabled()) { + logger.debug("TestConnectCommand::Post::{}", databaseConfiguration); + } + + + //ProjectManager.singleton.setBusy(true); + try { + + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + + boolean connectionTestResult = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .testConnection(databaseConfiguration); + + response.setStatus(HttpStatus.SC_OK); + writer.object(); + + writer.key("connectionResult"); + writer.value(connectionTestResult); + writer.key("code"); + writer.value("ok"); + writer.endObject(); + + } catch (DatabaseServiceException e) { + logger.error("TestConnectCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + } finally { + // writer.endObject(); + // w.flush(); + w.close(); + } + } catch (Exception e) { + logger.error("TestConnectCommand::Post::Exception::{}", e); + throw new ServletException(e); + } finally { + //ProjectManager.singleton.setBusy(false); + } + + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java new file mode 100644 index 000000000..a728fc1bd --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class TestQueryCommand extends DatabaseCommand { + + private static final Logger logger = LoggerFactory.getLogger("TestQueryCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + DatabaseConfiguration dbConfig = getJdbcConfiguration(request); + String query = request.getParameter("query"); + + if(logger.isDebugEnabled()) { + logger.debug("TestQueryCommand::Post::DatabaseConfiguration::{}::Query::{} " ,dbConfig, query); + } + + + //ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(dbConfig.getDatabaseType()) + .testQuery(dbConfig, query); + ObjectMapper mapperObj = new ObjectMapper(); + + response.setStatus(HttpStatus.SC_OK); + String jsonStr = mapperObj.writeValueAsString(databaseInfo); + if(logger.isDebugEnabled()) { + logger.debug("TestQueryCommand::Post::Result::{} " ,jsonStr); + } + + writer.object(); + writer.key("code"); + writer.value("ok"); + writer.key("QueryResult"); + writer.value(jsonStr); + writer.endObject(); + + + } catch (DatabaseServiceException e) { + logger.error("TestQueryCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST, response, writer, e); + + } catch (Exception e) { + logger.error("TestQueryCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST,response, writer, e); + } finally { + w.close(); + } + } catch (Exception e) { + logger.error("TestQueryCommand::Post::Exception::{}", e); + throw new ServletException(e); + } +// finally { +// // ProjectManager.singleton.setBusy(false); +// } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java new file mode 100644 index 000000000..4af014afc --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + + +public class MariaDBConnectionManager { + + private static final Logger logger = LoggerFactory.getLogger("MariaDBConnectionManager"); + private Connection connection; + private SQLType type; + + private static MariaDBConnectionManager instance; + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private MariaDBConnectionManager() { + type = SQLType.forName(MariaDBDatabaseService.DB_NAME); + + } + + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static MariaDBConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + //logger.info("::Creating new MariaDB Connection Manager ::"); + instance = new MariaDBConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection); + + if (connection != null && !forceNewConnection) { + // logger.debug("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + if(logger.isDebugEnabled()) { + logger.debug("Returning existing connection::{}", connection); + } + + + return connection; + } + } + + Class.forName(type.getClassPath()); + DriverManager.setLoginTimeout(10); + String dbURL = getDatabaseUrl(databaseConfiguration); + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + if(logger.isDebugEnabled()) { + logger.debug("*** Acquired New connection for ::{} **** ", dbURL); + } + + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + + private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java new file mode 100644 index 000000000..f5760a6b2 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.mariadb.jdbc.MariaDbResultSetMetaData; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLConnectionManager; + + + +public class MariaDBDatabaseService extends DatabaseService { + + private static final Logger logger = LoggerFactory.getLogger("MariaDBDatabaseService"); + + public static final String DB_NAME = "mariadb"; + public static final String DB_DRIVER = "org.mariadb.jdbc.Driver"; + + private static MariaDBDatabaseService instance; + + private MariaDBDatabaseService() { + } + + public static MariaDBDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER); + instance = new MariaDBDatabaseService(); + if(logger.isDebugEnabled()) { + logger.debug("MariaDBDatabaseService Instance: {}", instance); + } + } + return instance; + } + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return MariaDBConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + MariaDBConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + return null; + + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { +// if(logger.isDebugEnabled()) { +// logger.info( "<<< original input query::{} >>>" , query ); +// } +// + final int len = query.length(); + String parsedQuery = len > 0 && query.endsWith(";") ? query.substring(0, len - 1) : query; + + + StringBuilder sb = new StringBuilder(); + sb.append(parsedQuery); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + sb.append(";"); + String parsedQueryOut = sb.toString(); + +// if(logger.isDebugEnabled()) { +// logger.info( "<<>>" , parsedQueryOut ); +// } + + return parsedQueryOut; + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + // TODO Auto-generated method stub + return MariaDBConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + MySQLConnectionManager.getInstance().shutdown(); + } + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java new file mode 100644 index 000000000..35620146c --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.model; + +import com.google.refine.extension.database.DatabaseColumnType; + +public class DatabaseColumn { + + + private String name; + private int size; + private DatabaseColumnType type; + private String label; + + public DatabaseColumnType getType() { + return type; + } + + + + public void setType(DatabaseColumnType type) { + this.type = type; + } + + + + public String getLabel() { + return label; + } + + + + public void setLabel(String label) { + this.label = label; + } + + + public DatabaseColumn(String name, int size, DatabaseColumnType type) { + super(); + this.name = name; + this.size = size; + this.type = type; + } + + + public DatabaseColumn(String name, String label, DatabaseColumnType type, int size) { + this.name = name; + this.label = label; + this.size = size; + this.type = type; + } + + + public String getName() { + return name; + } + + + public void setName(String name) { + this.name = name; + } + + + public int getSize() { + return size; + } + + + public void setSize(int size) { + this.size = size; + } + + + + + @Override + public String toString() { + return "DatabaseColumn [name=" + name + ", size=" + size + ", type=" + type + ", label=" + label + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java new file mode 100644 index 000000000..c5755e9a7 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.model; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseInfo { + + private List tables; + private int dbMajorVersion; + private int dbMinorVersion; + private String dbProductVersion; + private String dbProductName; + + private ArrayList columns; + private List rows; + + public DatabaseInfo() { + // TODO Auto-generated constructor stub + } + + public List getTables() { + return tables; + } + + public void setTables(List tables) { + this.tables = tables; + } + + public void setDatabaseMajorVersion(int dbMajorVersion) { + this.dbMajorVersion = dbMajorVersion; + + } + + public void setDatabaseMinorVersion(int dbMinorVersion) { + this.dbMinorVersion = dbMinorVersion; + + } + + public void setDatabaseProductVersion(String dbProductVersion) { + this.dbProductVersion = dbProductVersion; + + } + + public void setDatabaseProductName(String dbProductName) { + this.dbProductName = dbProductName; + + } + + public int getDbMajorVersion() { + return dbMajorVersion; + } + + public void setDbMajorVersion(int dbMajorVersion) { + this.dbMajorVersion = dbMajorVersion; + } + + public int getDbMinorVersion() { + return dbMinorVersion; + } + + public void setDbMinorVersion(int dbMinorVersion) { + this.dbMinorVersion = dbMinorVersion; + } + + public String getDbProductVersion() { + return dbProductVersion; + } + + public void setDbProductVersion(String dbProductVersion) { + this.dbProductVersion = dbProductVersion; + } + + public String getDbProductName() { + return dbProductName; + } + + public void setDbProductName(String dbProductName) { + this.dbProductName = dbProductName; + } + + public void setColumns(ArrayList columns) { + this.columns = columns; + + } + + public void setRows(List rows) { + this.rows = rows; + + } + + public ArrayList getColumns() { + return columns; + } + + + public List getRows() { + return rows; + } + + + @Override + public String toString() { + return "DatabaseInfo [tables=" + tables + ", dbMajorVersion=" + dbMajorVersion + ", dbMinorVersion=" + + dbMinorVersion + ", dbProductVersion=" + dbProductVersion + ", dbProductName=" + dbProductName + "]"; + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java new file mode 100644 index 000000000..e8f1fb8c8 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.model; + +import com.google.refine.extension.database.DatabaseConfiguration; + +public class DatabaseQueryInfo { + + private DatabaseConfiguration dbConfig; + + private String query; + + public DatabaseQueryInfo(DatabaseConfiguration databaseConfig, String query) { + super(); + this.dbConfig = databaseConfig; + this.query = query; + } + + + public DatabaseConfiguration getDbConfig() { + return dbConfig; + } + + + public void setDbConfig(DatabaseConfiguration databaseConfig) { + this.dbConfig = databaseConfig; + } + + + public String getQuery() { + return query; + } + + + public void setQuery(String query) { + this.query = query; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java new file mode 100644 index 000000000..3fd6b9979 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.model; + +import java.util.List; + +public class DatabaseRow { + + private int index; + + private List values; + + + public int getIndex() { + return index; + } + + + public void setIndex(int index) { + this.index = index; + } + + + public List getValues() { + return values; + } + + + public void setValues(List values) { + this.values = values; + } + + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java new file mode 100644 index 000000000..c7758de27 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.model; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseTable { + + + private List columns = new ArrayList(); + + private String name; + + + public DatabaseTable(String name) { + this.name = name; + } + + + public List getColumns() { + return columns; + } + + + public void setColumns(List columns) { + this.columns = columns; + } + + + public String getName() { + return name; + } + + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "DatabaseTable [columns=" + columns + ", name=" + name + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java new file mode 100644 index 000000000..9e81fd264 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + +public class MySQLConnectionManager { + + private static final Logger logger = LoggerFactory.getLogger("MySQLConnectionManager"); + private Connection connection; + private SQLType type; + + private static MySQLConnectionManager instance; + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private MySQLConnectionManager() { + type = SQLType.forName(MySQLDatabaseService.DB_NAME); + + } + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static MySQLConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + logger.debug("::Creating new MySQLConnectionManager ::"); + instance = new MySQLConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + if (connection != null && !forceNewConnection) { + //logger.info("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + if(logger.isDebugEnabled()){ + logger.debug("Returning existing connection::{}", connection); + } + + return connection; + } + } + String dbURL = getDatabaseUrl(databaseConfiguration); + Class.forName(type.getClassPath()); + + //logger.info("*** type.getClassPath() ::{}, {}**** ", type.getClassPath()); + + DriverManager.setLoginTimeout(10); + + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + if(logger.isDebugEnabled()) { + logger.debug("*** Acquired New connection for ::{} **** ", dbURL); + } + + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + private String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java new file mode 100644 index 000000000..503936e16 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +//import com.mysql.jdbc.ResultSetMetaData; + +public class MySQLDatabaseService extends DatabaseService { + + + private static final Logger logger = LoggerFactory.getLogger("MySQLDatabaseService"); + + public static final String DB_NAME = "mysql"; + public static final String DB_DRIVER = "com.mysql.jdbc.Driver"; + + private static MySQLDatabaseService instance; + + private MySQLDatabaseService() { + } + + public static MySQLDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER, false); + instance = new MySQLDatabaseService(); + if(logger.isDebugEnabled()) { + logger.debug("MySQLDatabaseService Instance: {}", instance); + } + } + return instance; + } + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return MySQLConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + java.sql.ResultSetMetaData metadata = queryResult.getMetaData(); + + if(metadata instanceof com.mysql.jdbc.ResultSetMetaData) { + metadata = (com.mysql.jdbc.ResultSetMetaData)metadata; + } + //ResultSetMetaData metadata = (ResultSetMetaData)queryResult.getMetaData(); + + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + }finally { + MySQLConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + return null; + + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { + if(logger.isDebugEnabled()) { + logger.info( "<<< original input query::{} >>>" , query ); + } + + final int len = query.length(); + String parsedQuery = len > 0 && query.endsWith(";") ? query.substring(0, len - 1) : query; + + + StringBuilder sb = new StringBuilder(); + sb.append(parsedQuery); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + sb.append(";"); + String parsedQueryOut = sb.toString(); + + if(logger.isDebugEnabled()) { + logger.info( "<<>>" , parsedQueryOut ); + } + + return parsedQueryOut; + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + java.sql.ResultSetMetaData metadata = queryResult.getMetaData(); + if(metadata instanceof com.mysql.jdbc.ResultSetMetaData) { + metadata = (com.mysql.jdbc.ResultSetMetaData)metadata; + } + + //ResultSetMetaData metadata = (ResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, false); + + Statement statement = connection.createStatement(); + statement.setFetchSize(10); + ResultSet queryResult = statement.executeQuery(query); + + java.sql.ResultSetMetaData metadata = queryResult.getMetaData(); + if(metadata instanceof com.mysql.jdbc.ResultSetMetaData) { + metadata = (com.mysql.jdbc.ResultSetMetaData)metadata; + } + //logger.info("metadata class::" + metadata.getClass()); + + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + // TODO Auto-generated method stub + return MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + MySQLConnectionManager.getInstance().shutdown(); + } + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java new file mode 100644 index 000000000..bef6c9a69 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + + +public class PgSQLConnectionManager { + + private static final Logger logger = LoggerFactory.getLogger("PgSQLConnectionManager"); + private Connection connection; + private SQLType type; + + private static PgSQLConnectionManager instance; + + + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private PgSQLConnectionManager() { + type = SQLType.forName(PgSQLDatabaseService.DB_NAME); + + } + + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static PgSQLConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + if(logger.isDebugEnabled()) { + logger.debug("::Creating new PgSQL ConnectionManager ::"); + } + + instance = new PgSQLConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection); + + if (connection != null && !forceNewConnection) { + // logger.info("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + if(logger.isDebugEnabled()){ + logger.debug("Returning existing connection::{}", connection); + } + return connection; + } + } + + Class.forName(type.getClassPath()); + DriverManager.setLoginTimeout(10); + String dbURL = getDatabaseUrl(databaseConfiguration); + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + logger.debug("*** Acquired New connection for ::{} **** ", dbURL); + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java new file mode 100644 index 000000000..9a6308373 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Google nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.postgresql.jdbc.PgResultSetMetaData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLConnectionManager; + +public class PgSQLDatabaseService extends DatabaseService { + + private static final Logger logger = LoggerFactory.getLogger("PgSQLDatabaseService"); + + public static final String DB_NAME = "postgresql"; + public static final String DB_DRIVER = "org.postgresql.Driver"; + + private static PgSQLDatabaseService instance; + + private PgSQLDatabaseService() { + } + + public static PgSQLDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER); + instance = new PgSQLDatabaseService(); + if(logger.isDebugEnabled()) { + logger.debug("PgSQLDatabaseService Instance: {}", instance); + } + } + return instance; + } + + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return PgSQLConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + }finally { + PgSQLConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + return null; + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { + if(logger.isDebugEnabled()) { + logger.debug( "<<< original input query::{} >>>" , query ); + } + + final int len = query.length(); + String parsedQuery = len > 0 && query.endsWith(";") ? query.substring(0, len - 1) : query; + + + StringBuilder sb = new StringBuilder(); + sb.append(parsedQuery); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + sb.append(";"); + String parsedQueryOut = sb.toString(); + + if(logger.isDebugEnabled()) { + logger.debug( "<<>>" , parsedQueryOut ); + } + + return parsedQueryOut; + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + statement.setFetchSize(10); + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + values.add(queryResult.getString(i)); + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::{}::{}", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + return PgSQLConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //MySQLConnectionManager.getInstance().shutdown(); + } + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java b/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java new file mode 100644 index 000000000..c8df3d8b3 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java @@ -0,0 +1,434 @@ +package com.google.refine.extension.database; + +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import org.apache.commons.lang.text.StrSubstitutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DBExtensionTestUtils { + + private static final Logger logger = LoggerFactory.getLogger("DBExtensionTestUtils"); + + private static final String MYSQL_DB_NAME = "mysql"; + private static final String DEFAULT_MYSQL_HOST = "127.0.0.1"; + private static final int DEFAULT_MYSQL_PORT = 3306; + private static final String DEFAULT_MYSQL_USER = "root"; + private static final String DEFAULT_MYSQL_PASSWORD = "secret"; + private static final String DEFAULT_MYSQL_DB_NAME = "testdb"; + + private static final String PGSQL_DB_NAME = "postgresql"; + private static final String DEFAULT_PGSQL_HOST = "127.0.0.1"; + private static final int DEFAULT_PGSQL_PORT = 5432; + private static final String DEFAULT_PGSQL_USER = "postgres"; + private static final String DEFAULT_PGSQL_PASSWORD = ""; + private static final String DEFAULT_PGSQL_DB_NAME = "openrefine"; + + private static final String DEFAULT_TEST_TABLE = "test_data"; + + private static final int SAMPLE_SIZE = 500000; + private static final int BATCH_SIZE = 1000; + + private static Random rand = new Random(); + + private Map mncMap; + private Map mccMap; + + + /** + * Create Test Table with one row of Data + * @param dbConfig DatabaseConfiguration to test + * @param tableName + * @throws DatabaseServiceException + * @throws SQLException + */ + public static void initTestData(DatabaseConfiguration dbConfig, String tableName) + throws DatabaseServiceException, SQLException { + + Statement stmt = null; + Connection conn = null; + try { + DatabaseService dbService = DatabaseService.get(dbConfig.getDatabaseType()); + conn = dbService.getConnection(dbConfig); + stmt = conn.createStatement(); + + DatabaseMetaData dbm = conn.getMetaData(); + // check if "employee" table is there + ResultSet tables = dbm.getTables(null, null, tableName, null); + if (tables.next()) { + stmt.executeUpdate("DROP TABLE " + tableName); + //System.out.println("Drop Table Result::" + dropResult); + } + + String createSQL = " CREATE TABLE " + tableName + " ( " + + " ID INT NOT NULL, " + + " NAME VARCHAR (20) NOT NULL, " + + " CITY VARCHAR (20) NOT NULL," + + " PRIMARY KEY (ID) );"; + + + stmt.executeUpdate(createSQL); + //System.out.println("Create Table Result::" + createResult); + + String insertTableSQL = "INSERT INTO " + tableName + + "(ID, NAME, CITY) " + "VALUES" + + "(1,'frank lens','Dallas')"; + + stmt.executeUpdate(insertTableSQL); + // System.out.println("Insert Data Result::" + insertResult); + + logger.info("Database Test Init Data Created!!!"); + + } finally { + if(stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if(conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + } + + + public static void initTestData(DatabaseConfiguration dbConfig) + throws DatabaseServiceException, SQLException { + + Statement stmt = null; + Connection conn = null; + try { + DatabaseService dbService = DatabaseService.get(dbConfig.getDatabaseType()); + conn = dbService.getConnection(dbConfig); + stmt = conn.createStatement(); + + DatabaseMetaData dbm = conn.getMetaData(); + // check if "employee" table is there + ResultSet tables = dbm.getTables(null, null, DEFAULT_TEST_TABLE, null); + if (tables.next()) { + stmt.executeUpdate("DROP TABLE " + DEFAULT_TEST_TABLE); + //System.out.println("Drop Table Result::" + dropResult); + } + + String createSQL = " CREATE TABLE TEST_DATA( " + + " ID INT NOT NULL, " + + " NAME VARCHAR (20) NOT NULL, " + + " CITY VARCHAR (20) NOT NULL," + + " PRIMARY KEY (ID) );"; + + + stmt.executeUpdate(createSQL); + //System.out.println("Create Table Result::" + createResult); + + String insertTableSQL = "INSERT INTO TEST_DATA" + + "(ID, NAME, CITY) " + "VALUES" + + "(1,'frank lens','Dallas')"; + + stmt.executeUpdate(insertTableSQL); + // System.out.println("Insert Data Result::" + insertResult); + + logger.info("Database Test Init Data Created!!!"); + + } finally { + if(stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if(conn != null) { + try { + conn.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + } + + /** + * CREATE test data in MySQL + * Table name: test_data + * @param sampleSize + * @param batchSize + * @throws DatabaseServiceException + * @throws SQLException + */ + public void generateMySQLTestData(int sampleSize, int batchSize) + throws DatabaseServiceException, SQLException { + mncMap = new HashMap(); + mccMap = new HashMap(); + mccMap.put(0, 302); + mccMap.put(1, 311); + mccMap.put(2, 730); + mccMap.put(1, 622); + + mncMap.put(0, 006); + mncMap.put(1, 140); + mncMap.put(2, 380); + mncMap.put(3, 710); + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost(DEFAULT_MYSQL_HOST); + dc.setDatabaseName(DEFAULT_MYSQL_DB_NAME); + dc.setDatabasePassword(DEFAULT_MYSQL_PASSWORD); + dc.setDatabasePort(DEFAULT_MYSQL_PORT); + dc.setDatabaseType(MYSQL_DB_NAME); + dc.setDatabaseUser(DEFAULT_MYSQL_USER); + dc.setUseSSL(false); + + String truncateTableSQL = "TRUNCATE test_data"; + + String insertTableSQL = "INSERT INTO test_data(" + + "id, ue_id, start_time, end_date, bytes_upload, bytes_download, cell_id, mcc, mnc, lac, imei)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + + Connection conn = DatabaseService.get(MYSQL_DB_NAME).getConnection(dc); + + Statement truncateStmt = conn.createStatement(); + int result = truncateStmt.executeUpdate(truncateTableSQL); + System.out.println("Truncate Table Result::" + result); + truncateStmt.close(); + + + conn.setAutoCommit(false); + + PreparedStatement stmt = conn.prepareStatement(insertTableSQL); + + int counter=1; + for (int i = 0; i < sampleSize; i++) { + stmt.setLong(1, i); + stmt.setString(2, getNextUeId()); + stmt.setDate(3, getNextStartDate()); + stmt.setDate(4, getNextEndDate()); + stmt.setInt(5, rand.nextInt()); + stmt.setInt(6, rand.nextInt()); + stmt.setInt(7, rand.nextInt(10)); + stmt.setInt(8, getMCC()); + stmt.setInt(9, getMNC()); + stmt.setInt(10, rand.nextInt(100)); + stmt.setString(11, getNextIMEI()); + + stmt.addBatch(); + + //Execute batch of 1000 records + if(i%batchSize==0){ + stmt.executeBatch(); + conn.commit(); + System.out.println("Batch "+(counter++)+" executed successfully"); + } + } + //execute final batch + stmt.executeBatch(); + System.out.println("Final Batch Executed "+(counter++)+" executed successfully"); + conn.commit(); + conn.close(); + } + + /** + * + * @param sampleSize + * @param batchSize + * @throws DatabaseServiceException + * @throws SQLException + */ + public void generatePgSQLTestData(int sampleSize, int batchSize) throws DatabaseServiceException, SQLException { + mncMap = new HashMap(); + mccMap = new HashMap(); + mccMap.put(0, 302); + mccMap.put(1, 311); + mccMap.put(2, 730); + mccMap.put(1, 622); + + mncMap.put(0, 006); + mncMap.put(1, 140); + mncMap.put(2, 380); + mncMap.put(3, 710); + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost(DEFAULT_PGSQL_HOST); + dc.setDatabaseName(DEFAULT_PGSQL_DB_NAME); + dc.setDatabasePassword(DEFAULT_PGSQL_PASSWORD); + dc.setDatabasePort(DEFAULT_PGSQL_PORT); + dc.setDatabaseType(PGSQL_DB_NAME); + dc.setDatabaseUser(DEFAULT_PGSQL_USER); + dc.setUseSSL(false); + + String truncateTableSQL = "TRUNCATE public.test_data"; + + String insertTableSQL = "INSERT INTO public.test_data(" + + "id, ue_id, start_time, end_date, bytes_upload, bytes_download, cell_id, mcc, mnc, lac, imei)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + + Connection conn = DatabaseService.get(PGSQL_DB_NAME).getConnection(dc); + + Statement truncateStmt = conn.createStatement(); + int result = truncateStmt.executeUpdate(truncateTableSQL); + System.out.println("Truncate Table Result::" + result); + truncateStmt.close(); + + + conn.setAutoCommit(false); + + PreparedStatement stmt = conn.prepareStatement(insertTableSQL); + + int counter=1; + for (int i = 0; i < sampleSize; i++) { + stmt.setLong(1, i); + stmt.setString(2, getNextUeId()); + stmt.setDate(3, getNextStartDate()); + stmt.setDate(4, getNextEndDate()); + stmt.setInt(5, rand.nextInt()); + stmt.setInt(6, rand.nextInt()); + stmt.setInt(7, rand.nextInt(10)); + stmt.setInt(8, getMCC()); + stmt.setInt(9, getMNC()); + stmt.setInt(10, rand.nextInt(100)); + stmt.setString(11, getNextIMEI()); + + stmt.addBatch(); + + //Execute batch of 1000 records + if(i%batchSize==0){ + stmt.executeBatch(); + conn.commit(); + System.out.println("Batch "+(counter++)+" executed successfully"); + } + } + //execute final batch + stmt.executeBatch(); + System.out.println("Final Batch Executed "+(counter++)+" executed successfully"); + conn.commit(); + conn.close(); + + } + + private String getNextIMEI() { + int n = 1000000000 + rand.nextInt(900000000); + return "" + n; + } + + + + private int getMNC() { + + return mncMap.get(rand.nextInt(3)); + } + + private int getMCC() { + + return mccMap.get(rand.nextInt(3)); + } + + private Date getNextEndDate() { + + return new Date(System.currentTimeMillis() + 1); + } + + private Date getNextStartDate() { + + return new Date(System.currentTimeMillis()); + } + + private String getNextUeId() { + + int n = 300000000 + rand.nextInt(900000000); + + return "" + n; + } + + public static void main(String[] args) throws DatabaseServiceException, SQLException { + DBExtensionTestUtils testUtil = new DBExtensionTestUtils(); + testUtil.generatePgSQLTestData(SAMPLE_SIZE, BATCH_SIZE); + // testUtil.generateMySQLTestData(); + } + + + public static void cleanUpTestData(DatabaseConfiguration dbConfig) { + Statement stmt = null; + Connection conn = null; + try { + DatabaseService dbService = DatabaseService.get(dbConfig.getDatabaseType()); + conn = dbService.getConnection(dbConfig); + stmt = conn.createStatement(); + + DatabaseMetaData dbm = conn.getMetaData(); + // check if "employee" table is there + ResultSet tables = dbm.getTables(null, null, DEFAULT_TEST_TABLE, null); + if (tables.next()) { + stmt.executeUpdate("DROP TABLE " + DEFAULT_TEST_TABLE); + //System.out.println("Drop Table Result::" + dropResult); + } + + logger.info("Database Test Cleanup Done"); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if(stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if(conn != null) { + try { + conn.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + } + + public static File createTempDirectory(String name) + throws IOException { + File dir = File.createTempFile(name, ""); + dir.delete(); + dir.mkdir(); + return dir; + } + + public static String getJDBCUrl(DatabaseConfiguration dbConfig) { + Map substitutes = new HashMap(); + substitutes.put("dbType", dbConfig.getDatabaseType()); + substitutes.put("host", dbConfig.getDatabaseHost()); + substitutes.put("port", "" + dbConfig.getDatabasePort()); + substitutes.put("dbName", dbConfig.getDatabaseName()); + substitutes.put("useSSL", dbConfig.isUseSSL()); + String urlTemplate = "jdbc:${dbType}://${host}:${port}/${dbName}?useSSL=${useSSL}"; + StrSubstitutor strSub = new StrSubstitutor(substitutes); + return strSub.replace(urlTemplate); + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DBExtensionTests.java b/extensions/database/test/com/google/refine/extension/database/DBExtensionTests.java new file mode 100644 index 000000000..7d31085ef --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DBExtensionTests.java @@ -0,0 +1,70 @@ +/* + +Copyright 2010,2011 Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.extension.database; + +import java.util.Properties; + +import org.slf4j.Logger; + +public class DBExtensionTests { + + protected final String MYSQL_DB_NAME = "mysql"; + protected final String DEFAULT_MYSQL_HOST = "127.0.0.1"; + protected final String DEFAULT_MYSQL_PORT = "3306"; + protected final String DEFAULT_MYSQL_USER = "root"; + protected final String DEFAULT_MYSQL_PASSWORD = "secret"; + protected final String DEFAULT_MYSQL_DB_NAME = "testdb"; + + protected final String PGSQL_DB_NAME = "postgresql"; + protected final String DEFAULT_PGSQL_HOST = "127.0.0.1"; + protected final String DEFAULT_PGSQL_PORT = "5432"; + protected final String DEFAULT_PGSQL_USER = "postgres"; + protected final String DEFAULT_PGSQL_PASSWORD = ""; + protected final String DEFAULT_PGSQL_DB_NAME = "testdb"; + + + protected final String MARIA_DB_NAME = "mariadb"; + protected final String DEFAULT_MARIADB_HOST = "127.0.0.1"; + protected final String DEFAULT_MARIADB_PORT = "3306"; + protected final String DEFAULT_MARIADB_USER = "root"; + protected final String DEFAULT_MARIADB_PASSWORD = "secret"; + protected final String DEFAULT_MARIADB_NAME = "testdb"; + + protected final String DEFAULT_TEST_TABLE = "test_data"; + + protected Properties properties; + + protected Logger logger; + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java b/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java new file mode 100644 index 000000000..df75a81db --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java @@ -0,0 +1,259 @@ +package com.google.refine.extension.database; + + +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.ProjectManager; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.RefineServlet; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.stub.RefineDbServletStub; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.io.FileProjectManager; +import com.google.refine.model.Project; + + + +@Test(groups = { "requiresMySQL" }) +public class DatabaseImportControllerTest extends DBExtensionTests{ + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + private Project project; + private ProjectMetadata metadata; + private ImportingJob job; + private RefineServlet servlet; + + private String JSON_OPTION = "{\"mode\":\"row-based\"}}"; + + private DatabaseConfiguration testDbConfig; + + private String query; + + //System under test + private DatabaseImportController SUT = null; + + @BeforeMethod + public void setUp() throws JSONException, IOException { + MockitoAnnotations.initMocks(this); + + File dir = DBExtensionTestUtils.createTempDirectory("OR_DBExtension_Test_WorkspaceDir"); + FileProjectManager.initialize(dir); + + servlet = new RefineDbServletStub(); + ImportingManager.initialize(servlet); + project = new Project(); + metadata = new ProjectMetadata(); + job = ImportingManager.createJob(); + + metadata.setName("Database Import Test Project"); + ProjectManager.singleton.registerProject(project, metadata); + SUT = new DatabaseImportController(); + + } + + @AfterMethod + public void tearDown() { + SUT = null; + request = null; + response = null; + project = null; + metadata = null; + ImportingManager.disposeJob(job.id); + job = null; + //options = null; + } + + + @Test + public void testDoGet() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + + SUT.doGet(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("status"); + String message = json.getString("message"); + Assert.assertNotNull(code); + Assert.assertNotNull(message); + Assert.assertEquals(code, "error"); + Assert.assertEquals(message, "GET not implemented"); + + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testDoPostInvalidSubCommand() throws IOException, ServletException, JSONException { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + when(request.getQueryString()).thenReturn( + "http://127.0.0.1:3333/command/core/importing-controller?controller=database/database-import-controller&subCommand=invalid-sub-command"); + + when(response.getWriter()).thenReturn(pw); + //test + SUT.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("status"); + String message = json.getString("message"); + Assert.assertNotNull(code); + Assert.assertNotNull(message); + Assert.assertEquals(code, "error"); + Assert.assertEquals(message, "No such sub command"); + } + + + + @Test + public void testDoPostInitializeParser() throws ServletException, IOException, JSONException { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(request.getQueryString()).thenReturn( + "http://127.0.0.1:3333/command/core/importing-controller?controller=database/database-import-controller&subCommand=initialize-parser-ui"); + when(response.getWriter()).thenReturn(pw); + + SUT.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String status = json.getString("status"); + //System.out.println("json::" + json); + Assert.assertEquals(status, "ok"); + } + + @Test + public void testDoPostParsePreview() throws IOException, ServletException, JSONException { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + long jobId = job.id; + + when(request.getQueryString()).thenReturn( + "http://127.0.0.1:3333/command/core/importing-controller?controller=database%2Fdatabase-import-controller&jobID=" + + jobId + "&subCommand=parse-preview"); + when(response.getWriter()).thenReturn(pw); + + + when(request.getParameter("databaseType")).thenReturn(testDbConfig.getDatabaseType()); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + when(request.getParameter("query")).thenReturn(query); + when(request.getParameter("options")).thenReturn(JSON_OPTION); + + SUT.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String status = json.getString("status"); + //System.out.println("json::" + json); + Assert.assertEquals(status, "ok"); + } + + @Test + public void testDoPostCreateProject() throws IOException, ServletException, JSONException { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + long jobId = job.id; + + when(request.getQueryString()).thenReturn( + "http://127.0.0.1:3333/command/core/importing-controller?controller=database%2Fdatabase-import-controller&jobID=" + + jobId + "&subCommand=create-project"); + when(response.getWriter()).thenReturn(pw); + + + when(request.getParameter("databaseType")).thenReturn(testDbConfig.getDatabaseType()); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + when(request.getParameter("query")).thenReturn(query); + when(request.getParameter("options")).thenReturn(JSON_OPTION); + + + SUT.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String status = json.getString("status"); + //System.out.println("json::" + json); + Assert.assertEquals(status, "ok"); + } + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest( + @Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + query = "SELECT count(*) FROM " + mySqlTestTable; + + //testTable = mySqlTestTable; + + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java new file mode 100644 index 000000000..2999d36c5 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java @@ -0,0 +1,150 @@ +package com.google.refine.extension.database; + + +import java.sql.Connection; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.mariadb.MariaDBDatabaseService; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + + + + +public class DatabaseServiceTest extends DBExtensionTests{ + + private DatabaseConfiguration testDbConfig; + private String testTable; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + testTable = mySqlTestTable; + // DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + + } + + + + @Test + public void testGetDatabaseUrl() { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + String dbUrl = dbService.getDatabaseUrl(testDbConfig); + Assert.assertNotNull(dbUrl); + Assert.assertEquals(dbUrl, DBExtensionTestUtils.getJDBCUrl(testDbConfig)); + } + + + @Test(groups = {"requiresPgSQL"}) + public void testGetPgSQLDBService() { + DatabaseService dbService = DatabaseService.get(PgSQLDatabaseService.DB_NAME); + Assert.assertNotNull(dbService); + Assert.assertEquals(dbService.getClass(), PgSQLDatabaseService.class); + } + + @Test(groups = {"requiresMySQL"}) + public void testGetMySQLDBService() { + + DatabaseService dbService = DatabaseService.get(MySQLDatabaseService.DB_NAME); + Assert.assertNotNull(dbService); + Assert.assertEquals(dbService.getClass(), MySQLDatabaseService.class); + } + + @Test(groups = {"requiresMariaDB"}) + public void testGetMariaDBSQLDBService() { + + DatabaseService dbService = DatabaseService.get(MariaDBDatabaseService.DB_NAME); + Assert.assertNotNull(dbService); + Assert.assertEquals(dbService.getClass(), MariaDBDatabaseService.class); + } + + @Test(groups = {"requiresMySQL"}) + public void testGetConnection() throws DatabaseServiceException { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + Connection conn = dbService.getConnection(testDbConfig); + Assert.assertNotNull(conn); + } + + @Test(groups = {"requiresMySQL"}) + public void testTestConnection() throws DatabaseServiceException { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + boolean result = dbService.testConnection(testDbConfig); + Assert.assertEquals(result, true); + } + + @Test(groups = {"requiresMySQL"}) + public void testConnect() throws DatabaseServiceException { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + DatabaseInfo databaseInfo = dbService.connect(testDbConfig); + Assert.assertNotNull(databaseInfo); + } + + @Test(groups = {"requiresMySQL"}) + public void testExecuteQuery() throws DatabaseServiceException { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + DatabaseInfo databaseInfo = dbService.testQuery(testDbConfig, + "SELECT * FROM " + testTable); + + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testBuildLimitQuery() { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + String limitQuery = dbService.buildLimitQuery(100, 0, "SELECT * FROM " + testTable); + + Assert.assertNotNull(limitQuery); + + Assert.assertEquals(limitQuery, "SELECT * FROM " + testTable + " LIMIT " + 100 + " OFFSET " + 0 + ";"); + } + + @Test(groups = {"requiresMySQL"}) + public void testGetColumns() throws DatabaseServiceException { + List dbColumns; + + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + dbColumns = dbService.getColumns(testDbConfig,"SELECT * FROM " + testTable); + Assert.assertNotNull(dbColumns); + + int cols = dbColumns.size(); + Assert.assertEquals(cols, 10); + } + + @Test(groups = {"requiresMySQL"}) + public void testGetRows() throws DatabaseServiceException { + DatabaseService dbService = DatabaseService.get(testDbConfig.getDatabaseType()); + List dbRows = dbService.getRows(testDbConfig, + "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbRows); + Assert.assertEquals(dbRows.size(), 1); + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DatabaseTestConfig.java b/extensions/database/test/com/google/refine/extension/database/DatabaseTestConfig.java new file mode 100644 index 000000000..ffebbe8e6 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DatabaseTestConfig.java @@ -0,0 +1,81 @@ +package com.google.refine.extension.database; + +import java.sql.SQLException; + +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; + +import com.google.refine.extension.database.mariadb.MariaDBDatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + +public class DatabaseTestConfig extends DBExtensionTests { + + private DatabaseConfiguration mysqlDbConfig; + private DatabaseConfiguration pgsqlDbConfig; + private DatabaseConfiguration mariadbDbConfig; + + @BeforeSuite + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable", + "pgSqlDbName", "pgSqlDbHost", "pgSqlDbPort", "pgSqlDbUser", "pgSqlDbPassword", "pgSqlTestTable", + "mariadbDbName", "mariadbDbHost", "mariadbDbPort", "mariadbyDbUser", "mariadbDbPassword", "mariadbTestTable"}) + public void beforeSuite( + @Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable, + + @Optional(DEFAULT_PGSQL_DB_NAME) String pgSqlDbName, @Optional(DEFAULT_PGSQL_HOST) String pgSqlDbHost, + @Optional(DEFAULT_PGSQL_PORT) String pgSqlDbPort, @Optional(DEFAULT_PGSQL_USER) String pgSqlDbUser, + @Optional(DEFAULT_PGSQL_PASSWORD) String pgSqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String pgSqlTestTable, + + @Optional(DEFAULT_MARIADB_NAME) String mariadbDbName, @Optional(DEFAULT_MARIADB_HOST) String mariadbDbHost, + @Optional(DEFAULT_MARIADB_PORT) String mariadbDbPort, @Optional(DEFAULT_MARIADB_USER) String mariadbyDbUser, + @Optional(DEFAULT_MARIADB_PASSWORD) String mariadbDbPassword, @Optional(DEFAULT_TEST_TABLE) String mariadbTestTable) + throws DatabaseServiceException, SQLException { + + //System.out.println("@BeforeSuite\n"); + mysqlDbConfig = new DatabaseConfiguration(); + mysqlDbConfig.setDatabaseHost(mySqlDbHost); + mysqlDbConfig.setDatabaseName(mySqlDbName); + mysqlDbConfig.setDatabasePassword(mySqlDbPassword); + mysqlDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + mysqlDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + mysqlDbConfig.setDatabaseUser(mySqlDbUser); + mysqlDbConfig.setUseSSL(false); + + pgsqlDbConfig = new DatabaseConfiguration(); + pgsqlDbConfig.setDatabaseHost(pgSqlDbHost); + pgsqlDbConfig.setDatabaseName(pgSqlDbName); + pgsqlDbConfig.setDatabasePassword(pgSqlDbPassword); + pgsqlDbConfig.setDatabasePort(Integer.parseInt(pgSqlDbPort)); + pgsqlDbConfig.setDatabaseType(PgSQLDatabaseService.DB_NAME); + pgsqlDbConfig.setDatabaseUser(pgSqlDbUser); + pgsqlDbConfig.setUseSSL(false); + + mariadbDbConfig = new DatabaseConfiguration(); + mariadbDbConfig.setDatabaseHost(mariadbDbHost); + mariadbDbConfig.setDatabaseName(mariadbDbName); + mariadbDbConfig.setDatabasePassword(mariadbDbPassword); + mariadbDbConfig.setDatabasePort(Integer.parseInt(mariadbDbPort)); + mariadbDbConfig.setDatabaseType(MariaDBDatabaseService.DB_NAME); + mariadbDbConfig.setDatabaseUser(mariadbyDbUser); + mariadbDbConfig.setUseSSL(false); + + DBExtensionTestUtils.initTestData(mysqlDbConfig); + DBExtensionTestUtils.initTestData(pgsqlDbConfig); + DBExtensionTestUtils.initTestData(mariadbDbConfig); + } + + @AfterSuite + public void afterSuite() { + // System.out.println("@AfterSuite"); + + DBExtensionTestUtils.cleanUpTestData(mysqlDbConfig); + DBExtensionTestUtils.cleanUpTestData(pgsqlDbConfig); + DBExtensionTestUtils.cleanUpTestData(mariadbDbConfig); + } + +} + diff --git a/extensions/database/test/com/google/refine/extension/database/SimpleTextEncryptorTest.java b/extensions/database/test/com/google/refine/extension/database/SimpleTextEncryptorTest.java new file mode 100644 index 000000000..1ef21450f --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/SimpleTextEncryptorTest.java @@ -0,0 +1,28 @@ +package com.google.refine.extension.database; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SimpleTextEncryptorTest { + + @Test + public void encrypt() { + SimpleTextEncryptor textEncryptor = new SimpleTextEncryptor("WEWssa!@d445d"); + String password = "testpass"; + String encPass = textEncryptor.encrypt(password); + Assert.assertNotNull(encPass); + Assert.assertNotEquals(encPass, password); + + } + + @Test + public void decrypt() { + SimpleTextEncryptor textEncryptor = new SimpleTextEncryptor("OOEWssa!@d445d"); + String password = "testpass"; + String encPass = textEncryptor.encrypt(password); + Assert.assertNotNull(encPass); + Assert.assertNotEquals(encPass, password); + String decPass = textEncryptor.decrypt(encPass); + Assert.assertEquals(decPass, password); + } +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java new file mode 100644 index 000000000..13463a1bd --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java @@ -0,0 +1,98 @@ + +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; + + +@Test(groups = { "requiresMySQL" }) +public class ConnectCommandTest extends DBExtensionTests { + + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + private DatabaseConfiguration testDbConfig; + // private String testTable; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + //testTable = mySqlTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + @Test + public void testDoPost() throws IOException, ServletException, JSONException { + + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + ConnectCommand connectCommand = new ConnectCommand(); + + connectCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + String databaseInfo = json.getString("databaseInfo"); + Assert.assertNotNull(databaseInfo); + } + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java new file mode 100644 index 000000000..935c9e04f --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java @@ -0,0 +1,96 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; + +@Test(groups = { "requiresMySQL" }) +public class ExecuteQueryCommandTest extends DBExtensionTests { + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + + private DatabaseConfiguration testDbConfig; + private String testTable; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + testTable = mySqlTestTable; + // DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + @Test + public void testDoPost() throws IOException, ServletException, JSONException { + + when(request.getParameter("databaseType")).thenReturn(testDbConfig.getDatabaseType()); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + when(request.getParameter("queryString")).thenReturn("SELECT count(*) FROM " + testTable); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + ExecuteQueryCommand executeQueryCommand = new ExecuteQueryCommand(); + + executeQueryCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + String queryResult = json.getString("QueryResult"); + Assert.assertNotNull(queryResult); + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java new file mode 100644 index 000000000..0dc52016c --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java @@ -0,0 +1,293 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.ProjectManager; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.RefineServlet; +import com.google.refine.extension.database.DBExtensionTestUtils; +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.stub.RefineDbServletStub; +import com.google.refine.importing.ImportingManager; +import com.google.refine.io.FileProjectManager; +import com.google.refine.model.Project; + +public class SavedConnectionCommandTest extends DBExtensionTests{ + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + private DatabaseConfiguration testDbConfig; + + private Project project; + private ProjectMetadata metadata; + //private ImportingJob job; + private RefineServlet servlet; + + // private String JSON_OPTION = "{\"mode\":\"row-based\"}}"; + + + //System under test + private SavedConnectionCommand SUT = null; + + @BeforeMethod + public void setUp() throws JSONException, IOException { + MockitoAnnotations.initMocks(this); + + File dir = DBExtensionTestUtils.createTempDirectory("OR_DBExtension_Test_WorkspaceDir"); + FileProjectManager.initialize(dir); + + servlet = new RefineDbServletStub(); + ImportingManager.initialize(servlet); + project = new Project(); + metadata = new ProjectMetadata(); + //job = ImportingManager.createJob(); + + metadata.setName("Save DB Config Test Project"); + ProjectManager.singleton.registerProject(project, metadata); + SUT = new SavedConnectionCommand(); + + } + + @AfterMethod + public void tearDown() { + SUT = null; + request = null; + response = null; + project = null; + metadata = null; + // ImportingManager.disposeJob(job.id); + // job = null; + //options = null; + } + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + // MockitoAnnotations.initMocks(this); + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + + private void saveDatabaseConfiguration(String savedDbName) { + + when(request.getParameter("connectionName")).thenReturn(savedDbName); + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + + SUT.doPost(request, response); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + @Test + public void testDoPost() throws IOException, ServletException, JSONException { + + when(request.getParameter("connectionName")).thenReturn("test-db-name"); + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + SUT.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + + JSONObject json = new JSONObject(result); + + JSONArray savedConnections = json.getJSONArray("savedConnections"); + Assert.assertNotNull(savedConnections); + + int len = savedConnections.length(); + + Assert.assertEquals(len, 1); + } + + @Test + public void testDoGet() throws IOException, ServletException, JSONException { + String testDbName = "testLocalDb"; + //add saved connection + saveDatabaseConfiguration(testDbName); + + + when(request.getParameter("connectionName")).thenReturn(testDbName); + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + SUT.doGet(request, response); + + JSONObject json = new JSONObject(sw.getBuffer().toString().trim()); + + JSONArray savedConnections = json.getJSONArray("savedConnections"); + Assert.assertNotNull(savedConnections); + + Assert.assertEquals(savedConnections.length(), 1); + + JSONObject sc = (JSONObject)savedConnections.get(0); + // System.out.println("sc" + sc); + String connName = sc.getString("connectionName"); + Assert.assertEquals(connName, testDbName); + } + + @Test + public void testDoPut() throws IOException, ServletException, JSONException { + String testDbName = "testLocalDb"; + saveDatabaseConfiguration(testDbName); + + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + + //modify database config + String newHost = "localhost"; + when(request.getParameter("connectionName")).thenReturn(testDbName); + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(newHost); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + SUT.doPut(request, response); + + JSONObject json = new JSONObject(sw.getBuffer().toString().trim()); + JSONArray savedConnections = json.getJSONArray("savedConnections"); + Assert.assertNotNull(savedConnections); + + Assert.assertEquals(savedConnections.length(), 1); + + JSONObject sc = (JSONObject)savedConnections.get(0); + System.out.println("sc" + sc); + String newDbHost = sc.getString("databaseHost"); + Assert.assertEquals(newDbHost, newHost); + } + + @Test + public void testDoDeleteValidConnectionName() { + String testDbName = "testLocalDb"; + saveDatabaseConfiguration(testDbName); + + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + when(request.getParameter("connectionName")).thenReturn(testDbName); + SUT.doDelete(request, response); + + JSONObject json = new JSONObject(sw.getBuffer().toString().trim()); + JSONArray savedConnections = json.getJSONArray("savedConnections"); + Assert.assertNotNull(savedConnections); + + Assert.assertEquals(savedConnections.length(), 0); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testDoDeleteInValidConnectionName() { + String testDbName = "testLocalDb"; + saveDatabaseConfiguration(testDbName); + + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + + when(request.getParameter("connectionName")).thenReturn("noDbName"); + + SUT.doDelete(request, response); + + // String result = sw.getBuffer().toString().trim(); + + JSONObject json = new JSONObject(); + + Assert.assertNotNull(json); + + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java new file mode 100644 index 000000000..178344ada --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java @@ -0,0 +1,96 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; + + +@Test(groups = { "requiresMySQL" }) +public class TestConnectCommandTest extends DBExtensionTests{ + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + private DatabaseConfiguration testDbConfig; + // private String testTable; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + //testTable = mySqlTestTable; + // DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + + @Test + public void testDoPost() throws IOException, ServletException, JSONException { + + when(request.getParameter("databaseType")).thenReturn(MySQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + TestConnectCommand connectCommand = new TestConnectCommand(); + + connectCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/TestQueryCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/TestQueryCommandTest.java new file mode 100644 index 000000000..8ef3047e6 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/TestQueryCommandTest.java @@ -0,0 +1,98 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; + +@Test(groups = { "requiresMySQL" }) +public class TestQueryCommandTest extends DBExtensionTests { + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + + private DatabaseConfiguration testDbConfig; + private String testTable; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + testTable = mySqlTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + @Test + public void testDoPost() throws IOException, ServletException, JSONException { + + when(request.getParameter("databaseType")).thenReturn(testDbConfig.getDatabaseType()); + when(request.getParameter("databaseServer")).thenReturn(testDbConfig.getDatabaseHost()); + when(request.getParameter("databasePort")).thenReturn("" + testDbConfig.getDatabasePort()); + when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser()); + when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword()); + when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName()); + when(request.getParameter("query")).thenReturn("SELECT count(*) FROM " + testTable); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + when(response.getWriter()).thenReturn(pw); + TestQueryCommand executeQueryCommand = new TestQueryCommand(); + + executeQueryCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + String queryResult = json.getString("QueryResult"); + Assert.assertNotNull(queryResult); + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java new file mode 100644 index 000000000..ba1aeb849 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java @@ -0,0 +1,76 @@ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + +@Test(groups = { "requiresMariaDB" }) +public class MariaDBConnectionManagerTest extends DBExtensionTests { + + + + private DatabaseConfiguration testDbConfig; + + + @BeforeTest + @Parameters({ "mariaDbName", "mariaDbHost", "mariaDbPort", "mariaDbUser", "mariaDbPassword", "mariaDbTestTable"}) + public void beforeTest(@Optional(DEFAULT_MARIADB_NAME) String mariaDbName, @Optional(DEFAULT_MARIADB_HOST) String mariaDbHost, + @Optional(DEFAULT_MARIADB_PORT) String mariaDbPort, @Optional(DEFAULT_MARIADB_USER) String mariaDbUser, + @Optional(DEFAULT_MARIADB_PASSWORD) String mariaDbPassword, @Optional(DEFAULT_TEST_TABLE) String mariaDbTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mariaDbHost); + testDbConfig.setDatabaseName(mariaDbName); + testDbConfig.setDatabasePassword(mariaDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mariaDbPort)); + testDbConfig.setDatabaseType(MariaDBDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mariaDbUser); + testDbConfig.setUseSSL(false); + +// testTable = mariaDbTestTable; + // DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + + } + + + @Test + public void testTestConnection() throws DatabaseServiceException { + boolean conn = MariaDBConnectionManager.getInstance().testConnection(testDbConfig); + Assert.assertEquals(conn, true); + } + + @Test + public void testGetConnection() throws DatabaseServiceException { + Connection conn = MariaDBConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + } + + @Test + public void testShutdown() throws DatabaseServiceException, SQLException { + Connection conn = MariaDBConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + + MariaDBConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java new file mode 100644 index 000000000..927eb05d8 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java @@ -0,0 +1,135 @@ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.util.List; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTestUtils; +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; + +@Test(groups = { "requiresMariaDB" }) +public class MariaDBDatabaseServiceTest extends DBExtensionTests{ + + + + private DatabaseConfiguration testDbConfig; + + private String testTable; + + + @BeforeTest + @Parameters({ "mariaDbName", "mariaDbHost", "mariaDbPort", "mariaDbUser", "mariaDbPassword", "mariaDbTestTable"}) + public void beforeTest(@Optional(DEFAULT_MARIADB_NAME) String mariaDbName, @Optional(DEFAULT_MARIADB_HOST) String mariaDbHost, + @Optional(DEFAULT_MARIADB_PORT) String mariaDbPort, @Optional(DEFAULT_MARIADB_USER) String mariaDbUser, + @Optional(DEFAULT_MARIADB_PASSWORD) String mariaDbPassword, @Optional(DEFAULT_TEST_TABLE) String mariaDbTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mariaDbHost); + testDbConfig.setDatabaseName(mariaDbName); + testDbConfig.setDatabasePassword(mariaDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mariaDbPort)); + testDbConfig.setDatabaseType(MariaDBDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mariaDbUser); + testDbConfig.setUseSSL(false); + + testTable = mariaDbTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + + } + + @Test + public void testGetDatabaseUrl() { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService)DatabaseService.get(MariaDBDatabaseService.DB_NAME); + String dbUrl = pgSqlService.getDatabaseUrl(testDbConfig); + //System.out.println("dbUrl:" + dbUrl); + Assert.assertNotNull(dbUrl); + Assert.assertEquals(dbUrl, DBExtensionTestUtils.getJDBCUrl(testDbConfig)); + } + + @Test + public void testGetConnection() throws DatabaseServiceException { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService)DatabaseService.get(MariaDBDatabaseService.DB_NAME); + Connection conn = pgSqlService.getConnection(testDbConfig); + + Assert.assertNotNull(conn); + } + + @Test + public void testTestConnection() throws DatabaseServiceException { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService)DatabaseService.get(MariaDBDatabaseService.DB_NAME); + + boolean result = pgSqlService.testConnection(testDbConfig); + Assert.assertEquals(result, true); + } + + @Test + public void testConnect() throws DatabaseServiceException { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService)DatabaseService.get(MariaDBDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.connect(testDbConfig); + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testExecuteQuery() throws DatabaseServiceException { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService) DatabaseService + .get(MariaDBDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.testQuery(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testBuildLimitQuery() { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService) DatabaseService.get(MariaDBDatabaseService.DB_NAME); + String limitQuery = pgSqlService.buildLimitQuery(100, 0, "SELECT * FROM " + testTable); + Assert.assertNotNull(limitQuery); + + Assert.assertEquals(limitQuery, "SELECT * FROM " + testTable + " LIMIT " + 100 + " OFFSET " + 0 + ";"); + + } + + @Test + public void testGetRows() throws DatabaseServiceException { + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService) DatabaseService + .get(MariaDBDatabaseService.DB_NAME); + List dbRows = pgSqlService.getRows(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbRows); + } + + @Test + public void testGetInstance() { + MariaDBDatabaseService instance = MariaDBDatabaseService.getInstance(); + Assert.assertNotNull(instance); + } + + @Test + public void testGetColumns() throws DatabaseServiceException { + List dbColumns; + + MariaDBDatabaseService pgSqlService = (MariaDBDatabaseService) DatabaseService + .get(MariaDBDatabaseService.DB_NAME); + + dbColumns = pgSqlService.getColumns(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbColumns); + } + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java new file mode 100644 index 000000000..bd01aba75 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java @@ -0,0 +1,76 @@ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + +@Test(groups = { "requiresMySQL" }) +public class MySQLConnectionManagerTest extends DBExtensionTests { + + private DatabaseConfiguration testDbConfig; + + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + //testTable = mySqlTestTable; + // DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + @Test + public void testTestConnection() throws DatabaseServiceException { + + boolean conn = MySQLConnectionManager.getInstance().testConnection(testDbConfig); + Assert.assertEquals(conn, true); + } + + @Test + public void testGetConnection() throws DatabaseServiceException { + + Connection conn = MySQLConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + } + + @Test + public void testShutdown() throws DatabaseServiceException, SQLException { + + Connection conn = MySQLConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + + MySQLConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java new file mode 100644 index 000000000..653d468df --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java @@ -0,0 +1,135 @@ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.util.List; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTestUtils; +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; + + +@Test(groups = { "requiresMySQL" }) +public class MySQLDatabaseServiceTest extends DBExtensionTests{ + + private DatabaseConfiguration testDbConfig; + private String testTable; + + @BeforeTest + @Parameters({ "mySqlDbName", "mySqlDbHost", "mySqlDbPort", "mySqlDbUser", "mySqlDbPassword", "mySqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_MYSQL_DB_NAME) String mySqlDbName, @Optional(DEFAULT_MYSQL_HOST) String mySqlDbHost, + @Optional(DEFAULT_MYSQL_PORT) String mySqlDbPort, @Optional(DEFAULT_MYSQL_USER) String mySqlDbUser, + @Optional(DEFAULT_MYSQL_PASSWORD) String mySqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String mySqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(mySqlDbHost); + testDbConfig.setDatabaseName(mySqlDbName); + testDbConfig.setDatabasePassword(mySqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(mySqlDbPort)); + testDbConfig.setDatabaseType(MySQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(mySqlDbUser); + testDbConfig.setUseSSL(false); + + testTable = mySqlTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + + } + + + @Test + public void testGetDatabaseUrl() { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService)DatabaseService.get(MySQLDatabaseService.DB_NAME); + String dbUrl = pgSqlService.getDatabaseUrl(testDbConfig); + //System.out.println("dbUrl:" + dbUrl); + Assert.assertNotNull(dbUrl); + Assert.assertEquals(dbUrl, DBExtensionTestUtils.getJDBCUrl(testDbConfig)); + } + + + @Test + public void testGetConnection() throws DatabaseServiceException { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService)DatabaseService.get(MySQLDatabaseService.DB_NAME); + Connection conn = pgSqlService.getConnection(testDbConfig); + + Assert.assertNotNull(conn); + } + + @Test + public void testTestConnection() throws DatabaseServiceException { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService)DatabaseService.get(MySQLDatabaseService.DB_NAME); + + boolean result = pgSqlService.testConnection(testDbConfig); + Assert.assertEquals(result, true); + } + + @Test + public void testConnect() throws DatabaseServiceException { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService)DatabaseService.get(MySQLDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.connect(testDbConfig); + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testExecuteQuery() throws DatabaseServiceException { + + MySQLDatabaseService pgSqlService = (MySQLDatabaseService) DatabaseService + .get(MySQLDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.testQuery(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testBuildLimitQuery() { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService) DatabaseService.get(MySQLDatabaseService.DB_NAME); + String limitQuery = pgSqlService.buildLimitQuery(100, 0, "SELECT * FROM " + testTable); + Assert.assertNotNull(limitQuery); + + Assert.assertEquals(limitQuery, "SELECT * FROM " + testTable + " LIMIT " + 100 + " OFFSET " + 0 + ";"); + + } + + @Test + public void testGetRows() throws DatabaseServiceException { + MySQLDatabaseService pgSqlService = (MySQLDatabaseService) DatabaseService + .get(MySQLDatabaseService.DB_NAME); + List dbRows = pgSqlService.getRows(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbRows); + } + + @Test + public void testGetInstance() { + MySQLDatabaseService instance = MySQLDatabaseService.getInstance(); + Assert.assertNotNull(instance); + } + + @Test + public void testGetColumns() throws DatabaseServiceException { + List dbColumns; + MySQLDatabaseService pgSqlService = (MySQLDatabaseService) DatabaseService + .get(MySQLDatabaseService.DB_NAME); + + dbColumns = pgSqlService.getColumns(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbColumns); + } + + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java new file mode 100644 index 000000000..4c4d744d8 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java @@ -0,0 +1,79 @@ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + + +@Test(groups = { "requiresPgSQL" }) +public class PgSQLConnectionManagerTest extends DBExtensionTests { + + private DatabaseConfiguration testDbConfig; + + + @BeforeTest + @Parameters({ "pgSqlDbName", "pgSqlDbHost", "pgSqlDbPort", "pgSqlDbUser", "pgSqlDbPassword", "pgSqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_PGSQL_DB_NAME) String pgSqlDbName, @Optional(DEFAULT_PGSQL_HOST) String pgSqlDbHost, + @Optional(DEFAULT_PGSQL_PORT) String pgSqlDbPort, @Optional(DEFAULT_PGSQL_USER) String pgSqlDbUser, + @Optional(DEFAULT_PGSQL_PASSWORD) String pgSqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String pgSqlTestTable) { + + MockitoAnnotations.initMocks(this); + + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(pgSqlDbHost); + testDbConfig.setDatabaseName(pgSqlDbName); + testDbConfig.setDatabasePassword(pgSqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(pgSqlDbPort)); + testDbConfig.setDatabaseType(PgSQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(pgSqlDbUser); + testDbConfig.setUseSSL(false); + + //testTable = mySqlTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + + } + + + + @Test + public void testTestConnection() throws DatabaseServiceException { + + boolean isConnected = PgSQLConnectionManager.getInstance().testConnection(testDbConfig); + Assert.assertEquals(isConnected, true); + } + + @Test + public void testGetConnection() throws DatabaseServiceException { + + Connection conn = PgSQLConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + } + + @Test + public void testShutdown() throws DatabaseServiceException, SQLException { + + Connection conn = PgSQLConnectionManager.getInstance().getConnection(testDbConfig, true); + Assert.assertNotNull(conn); + + PgSQLConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java new file mode 100644 index 000000000..161d669b7 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java @@ -0,0 +1,137 @@ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.util.List; + +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DBExtensionTestUtils; +import com.google.refine.extension.database.DBExtensionTests; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; + +@Test(groups = { "requiresPgSQL" }) +public class PgSQLDatabaseServiceTest extends DBExtensionTests{ + + private DatabaseConfiguration testDbConfig; + private String testTable; + + + @BeforeTest + @Parameters({ "pgSqlDbName", "pgSqlDbHost", "pgSqlDbPort", "pgSqlDbUser", "pgSqlDbPassword", "pgSqlTestTable"}) + public void beforeTest(@Optional(DEFAULT_PGSQL_DB_NAME) String pgSqlDbName, @Optional(DEFAULT_PGSQL_HOST) String pgSqlDbHost, + @Optional(DEFAULT_PGSQL_PORT) String pgSqlDbPort, @Optional(DEFAULT_PGSQL_USER) String pgSqlDbUser, + @Optional(DEFAULT_PGSQL_PASSWORD) String pgSqlDbPassword, @Optional(DEFAULT_TEST_TABLE) String pgSqlTestTable) { + + MockitoAnnotations.initMocks(this); + testDbConfig = new DatabaseConfiguration(); + testDbConfig.setDatabaseHost(pgSqlDbHost); + testDbConfig.setDatabaseName(pgSqlDbName); + testDbConfig.setDatabasePassword(pgSqlDbPassword); + testDbConfig.setDatabasePort(Integer.parseInt(pgSqlDbPort)); + testDbConfig.setDatabaseType(PgSQLDatabaseService.DB_NAME); + testDbConfig.setDatabaseUser(pgSqlDbUser); + testDbConfig.setUseSSL(false); + + testTable = pgSqlTestTable; + //DBExtensionTestUtils.initTestData(testDbConfig); + + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + } + + + + @Test + public void testGetDatabaseUrl() { + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService)DatabaseService.get(PgSQLDatabaseService.DB_NAME); + String dbUrl = pgSqlService.getDatabaseUrl(testDbConfig); + + Assert.assertNotNull(dbUrl); + Assert.assertEquals(dbUrl, DBExtensionTestUtils.getJDBCUrl(testDbConfig)); + } + + + @Test + public void testGetConnection() throws DatabaseServiceException { + + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService)DatabaseService.get(PgSQLDatabaseService.DB_NAME); + Connection conn = pgSqlService.getConnection(testDbConfig); + + Assert.assertNotNull(conn); + } + + @Test + public void testTestConnection() throws DatabaseServiceException { + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService)DatabaseService.get(PgSQLDatabaseService.DB_NAME); + + boolean result = pgSqlService.testConnection(testDbConfig); + Assert.assertEquals(result, true); + } + + @Test + public void testConnect() throws DatabaseServiceException { + + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService)DatabaseService.get(PgSQLDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.connect(testDbConfig); + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testExecuteQuery() throws DatabaseServiceException { + + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService) DatabaseService + .get(PgSQLDatabaseService.DB_NAME); + DatabaseInfo databaseInfo = pgSqlService.testQuery(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(databaseInfo); + } + + @Test + public void testBuildLimitQuery() { + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService) DatabaseService.get(PgSQLDatabaseService.DB_NAME); + String limitQuery = pgSqlService.buildLimitQuery(100, 0, "SELECT * FROM " + testTable); + Assert.assertNotNull(limitQuery); + + Assert.assertEquals(limitQuery, "SELECT * FROM " + testTable + " LIMIT " + 100 + " OFFSET " + 0 + ";"); + + } + + @Test + public void testGetRows() throws DatabaseServiceException { + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService) DatabaseService + .get(PgSQLDatabaseService.DB_NAME); + List dbRows = pgSqlService.getRows(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbRows); + } + + @Test + public void testGetInstance() { + PgSQLDatabaseService instance = PgSQLDatabaseService.getInstance(); + Assert.assertNotNull(instance); + } + + @Test + public void testGetColumns() throws DatabaseServiceException { + List dbColumns; + + PgSQLDatabaseService pgSqlService = (PgSQLDatabaseService) DatabaseService + .get(PgSQLDatabaseService.DB_NAME); + + dbColumns = pgSqlService.getColumns(testDbConfig, "SELECT * FROM " + testTable); + + Assert.assertNotNull(dbColumns); + + } + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/stub/RefineDbServletStub.java b/extensions/database/test/com/google/refine/extension/database/stub/RefineDbServletStub.java new file mode 100644 index 000000000..c76e85c26 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/stub/RefineDbServletStub.java @@ -0,0 +1,95 @@ +/* + +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.extension.database.stub; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.refine.RefineServlet; +import com.google.refine.commands.Command; + +/** + * Exposes protected methods of com.google.refine.RefineServlet as public for unit testing + * + */ +public class RefineDbServletStub extends RefineServlet { + + private static File tempDir = null; + + //requirement of extending HttpServlet, not required for testing + private static final long serialVersionUID = 1L; + + public void wrapService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ + super.service(request, response); + } + + public String wrapGetCommandName(HttpServletRequest request){ + return super.getCommandKey(request); + } + + @Override + public File getTempDir() { + if (tempDir == null) { + try { + tempDir = File.createTempFile("refine-test-dir", ""); + tempDir.deleteOnExit(); + } catch (IOException e) { + throw new RuntimeException("Failed to create temp directory",e); + } + } + return tempDir; + } + + //-------------------helper methods-------------- + /** + * Helper method for inserting a mock object + * @param commandName + * @param command + */ + public void insertCommand(String commandName, Command command ){ + registerOneCommand("core/" + commandName, command); + } + + /** + * Helper method for clearing up after testing + * @param commandName + */ + public void removeCommand( String commandName ){ + unregisterCommand("core/" + commandName); + } +} diff --git a/extensions/database/test/conf/appveyor_tests.xml b/extensions/database/test/conf/appveyor_tests.xml new file mode 100644 index 000000000..1d906be12 --- /dev/null +++ b/extensions/database/test/conf/appveyor_tests.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/test/conf/tests.xml b/extensions/database/test/conf/tests.xml new file mode 100644 index 000000000..f14cc34ad --- /dev/null +++ b/extensions/database/test/conf/tests.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/test/conf/travis-mariadb.sql b/extensions/database/test/conf/travis-mariadb.sql new file mode 100644 index 000000000..c5716c5a8 --- /dev/null +++ b/extensions/database/test/conf/travis-mariadb.sql @@ -0,0 +1,18 @@ +CREATE DATABASE IF NOT EXISTS `test_db` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; +USE test_db; + +CREATE TABLE IF NOT EXISTS `test_table` ( + `id` int(11) NOT NULL, + `ue_id` char(8) NOT NULL, + `start_time` timestamp NOT NULL, + `end_date` date DEFAULT NULL, + `bytes_upload` int(11) NOT NULL, + `bytes_download` int(11) NOT NULL, + `mcc` char(3) DEFAULT NULL, + `mnc` char(3) NOT NULL, + `lac` varchar(11) DEFAULT NULL, + `imei` char(16) NOT NULL +); + +INSERT INTO test_table(id, ue_id, start_time, end_date, bytes_upload, bytes_download, mcc, mnc, lac, imei) + VALUES (1, '11100022', now(), now(), 1024, 2048, 321, 543, 12209823498, 1344498988877487); \ No newline at end of file diff --git a/extensions/database/test/conf/travis-mysql.sql b/extensions/database/test/conf/travis-mysql.sql new file mode 100644 index 000000000..d2b53c80e --- /dev/null +++ b/extensions/database/test/conf/travis-mysql.sql @@ -0,0 +1,17 @@ +USE test_db; + +CREATE TABLE IF NOT EXISTS `test_table` ( + `id` int(11) NOT NULL, + `ue_id` char(8) NOT NULL, + `start_time` timestamp NOT NULL, + `end_date` date DEFAULT NULL, + `bytes_upload` int(11) NOT NULL, + `bytes_download` int(11) NOT NULL, + `mcc` char(3) DEFAULT NULL, + `mnc` char(3) NOT NULL, + `lac` varchar(11) DEFAULT NULL, + `imei` char(16) NOT NULL +); + +INSERT INTO test_table(id, ue_id, start_time, end_date, bytes_upload, bytes_download, mcc, mnc, lac, imei) + VALUES (1, '11100022', now(), now(), 1024, 2048, 321, 543, 12209823498, 1344498988877487); \ No newline at end of file diff --git a/extensions/database/test/conf/travis-pgsql.sql b/extensions/database/test/conf/travis-pgsql.sql new file mode 100644 index 000000000..f8299a964 --- /dev/null +++ b/extensions/database/test/conf/travis-pgsql.sql @@ -0,0 +1,17 @@ +USE test_db; + +CREATE TABLE IF NOT EXISTS test_table ( + id integer NOT NULL, + ue_id char(8) NOT NULL, + start_time timestamp NOT NULL, + end_date date DEFAULT NULL, + bytes_upload integer NOT NULL, + bytes_download integer NOT NULL, + mcc char(3) DEFAULT NULL, + mnc char(3) NOT NULL, + lac varchar(11) DEFAULT NULL, + imei char(16) NOT NULL +); + +INSERT INTO test_table(id, ue_id, start_time, end_date, bytes_upload, bytes_download, mcc, mnc, lac, imei) + VALUES (1, '11100022', now(), now(), 1024, 2048, 321, 543, 12209823498, 1344498988877487); \ No newline at end of file diff --git a/extensions/database/test/conf/travis_tests.xml b/extensions/database/test/conf/travis_tests.xml new file mode 100644 index 000000000..fcf2204bf --- /dev/null +++ b/extensions/database/test/conf/travis_tests.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extensions/database/test/log4j-test.properties b/extensions/database/test/log4j-test.properties new file mode 100644 index 000000000..0ed0d9f64 --- /dev/null +++ b/extensions/database/test/log4j-test.properties @@ -0,0 +1,5 @@ +log4j.rootLogger=ERROR, console +log4j.logger.com.google.refine.extension.database=DEBUG + +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.layout=com.google.refine.logging.IndentingLayout diff --git a/extensions/gdata/.classpath b/extensions/gdata/.classpath deleted file mode 100644 index f6d5efd90..000000000 --- a/extensions/gdata/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extensions/gdata/.project b/extensions/gdata/.project deleted file mode 100644 index c6f5e416a..000000000 --- a/extensions/gdata/.project +++ /dev/null @@ -1,31 +0,0 @@ - - - grefine-gdata-extension - - - gridworks - gridworks-server - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.jsdt.core.jsNature - - diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableImporter.java b/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableImporter.java index 8e4dbfbb7..c02b3253f 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableImporter.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/FusionTableImporter.java @@ -39,11 +39,11 @@ import com.google.api.services.fusiontables.model.Column; import com.google.api.services.fusiontables.model.Sqlresponse; import com.google.api.services.fusiontables.model.Table; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.TabularImportingParserBase; import com.google.refine.importers.TabularImportingParserBase.TableDataReader; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; /** diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/GDataImporter.java b/extensions/gdata/src/com/google/refine/extension/gdata/GDataImporter.java index edbb4a8e1..a4d58c2a6 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/GDataImporter.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/GDataImporter.java @@ -45,11 +45,11 @@ import com.google.gdata.data.spreadsheet.SpreadsheetEntry; import com.google.gdata.data.spreadsheet.WorksheetEntry; import com.google.gdata.util.ServiceException; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.TabularImportingParserBase; import com.google.refine.importers.TabularImportingParserBase.TableDataReader; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; /** diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/GDataImportingController.java b/extensions/gdata/src/com/google/refine/extension/gdata/GDataImportingController.java index fb654c83c..3e9e47da1 100644 --- a/extensions/gdata/src/com/google/refine/extension/gdata/GDataImportingController.java +++ b/extensions/gdata/src/com/google/refine/extension/gdata/GDataImportingController.java @@ -65,7 +65,6 @@ import com.google.gdata.util.AuthenticationException; import com.google.gdata.util.ServiceException; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.commands.HttpUtilities; import com.google.refine.importing.DefaultImportingController; @@ -73,6 +72,7 @@ import com.google.refine.importing.ImportingController; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; import com.google.refine.util.ParsingUtilities; diff --git a/extensions/jython/.classpath b/extensions/jython/.classpath deleted file mode 100644 index fc48762f5..000000000 --- a/extensions/jython/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/extensions/jython/.project b/extensions/jython/.project deleted file mode 100644 index 8d1297098..000000000 --- a/extensions/jython/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - grefine-jython - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/extensions/jython/src/com/google/refine/jython/JythonHasFieldsWrapper.java b/extensions/jython/src/com/google/refine/jython/JythonHasFieldsWrapper.java index 55b49207c..f2eb3fda6 100644 --- a/extensions/jython/src/com/google/refine/jython/JythonHasFieldsWrapper.java +++ b/extensions/jython/src/com/google/refine/jython/JythonHasFieldsWrapper.java @@ -55,7 +55,12 @@ public class JythonHasFieldsWrapper extends PyObject { @Override public PyObject __finditem__(PyObject key) { String k = (String) key.__tojava__(String.class); - Object v = _obj.getField(k, _bindings); + return __findattr_ex__(k); + } + + @Override + public PyObject __findattr_ex__(String name) { + Object v = _obj.getField(name, _bindings); if (v != null) { if (v instanceof PyObject) { return (PyObject) v; @@ -70,5 +75,4 @@ public class JythonHasFieldsWrapper extends PyObject { return null; } } - } diff --git a/extensions/jython/tests/src/com/google/refine/jython/JythonAttributeTest.java b/extensions/jython/tests/src/com/google/refine/jython/JythonAttributeTest.java new file mode 100644 index 000000000..c97a071fc --- /dev/null +++ b/extensions/jython/tests/src/com/google/refine/jython/JythonAttributeTest.java @@ -0,0 +1,50 @@ +package com.google.refine.jython; + +import java.util.Properties; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.refine.expr.Evaluable; +import com.google.refine.expr.HasFields; +import com.google.refine.model.Cell; +import com.google.refine.model.Row; + +public class JythonAttributeTest { + + class MyFieldObject implements HasFields { + + @Override + public Object getField(String name, Properties bindings) { + if ("sunshine".equals(name)) { + return "hammock"; + } + return null; + } + + @Override + public boolean fieldAlsoHasFields(String name) { + return true; + } + + } + + @Test + public void testWrappedObjectsHaveAttributes() { + Row row = new Row(2); + row.setCell(0, new Cell("sunshine", null)); + row.setCell(1, new Cell("hammock", null)); + + Properties props = new Properties(); + + MyFieldObject obj = new MyFieldObject(); + JythonHasFieldsWrapper wrapper = new JythonHasFieldsWrapper(obj, props); + Assert.assertEquals(wrapper.__findattr__("sunshine").toString(), "hammock"); + + props.put("cell", obj); + + Evaluable eval = new JythonEvaluable("return cell.sunshine"); + String result = (String)eval.evaluate(props).toString(); + Assert.assertEquals(result, "hammock"); + } +} diff --git a/extensions/pc-axis/.classpath b/extensions/pc-axis/.classpath deleted file mode 100644 index f68b34a90..000000000 --- a/extensions/pc-axis/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/extensions/pc-axis/.project b/extensions/pc-axis/.project deleted file mode 100644 index eef3b87d9..000000000 --- a/extensions/pc-axis/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - refine-pd-extension - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/extensions/pc-axis/module/MOD-INF/.gitignore b/extensions/pc-axis/module/MOD-INF/.gitignore deleted file mode 100644 index 840e7d312..000000000 --- a/extensions/pc-axis/module/MOD-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/classes/ diff --git a/extensions/pc-axis/src/com/google/refine/pcaxis/PCAxisImporter.java b/extensions/pc-axis/src/com/google/refine/pcaxis/PCAxisImporter.java index 64e78a67d..ea137ec7e 100644 --- a/extensions/pc-axis/src/com/google/refine/pcaxis/PCAxisImporter.java +++ b/extensions/pc-axis/src/com/google/refine/pcaxis/PCAxisImporter.java @@ -41,10 +41,10 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.TabularImportingParserBase; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class PCAxisImporter extends TabularImportingParserBase { diff --git a/extensions/sample/.classpath b/extensions/sample/.classpath deleted file mode 100644 index 03c606eeb..000000000 --- a/extensions/sample/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/extensions/sample/.settings/.jsdtscope b/extensions/sample/.settings/.jsdtscope deleted file mode 100644 index b13d30357..000000000 --- a/extensions/sample/.settings/.jsdtscope +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/extensions/sample/module/MOD-INF/.gitignore b/extensions/sample/module/MOD-INF/.gitignore deleted file mode 100644 index 840e7d312..000000000 --- a/extensions/sample/module/MOD-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/classes/ diff --git a/graphics/icon/google-refine-128px.png b/graphics/icon/google-refine-128px.png deleted file mode 100644 index 2f63bcc93..000000000 Binary files a/graphics/icon/google-refine-128px.png and /dev/null differ diff --git a/graphics/icon/google-refine-16px.png b/graphics/icon/google-refine-16px.png deleted file mode 100644 index 33bbd2aa9..000000000 Binary files a/graphics/icon/google-refine-16px.png and /dev/null differ diff --git a/graphics/icon/google-refine-256px.png b/graphics/icon/google-refine-256px.png deleted file mode 100644 index aa6554616..000000000 Binary files a/graphics/icon/google-refine-256px.png and /dev/null differ diff --git a/graphics/icon/google-refine-32px.png b/graphics/icon/google-refine-32px.png deleted file mode 100644 index 924e40904..000000000 Binary files a/graphics/icon/google-refine-32px.png and /dev/null differ diff --git a/graphics/icon/google-refine-48px.png b/graphics/icon/google-refine-48px.png deleted file mode 100644 index 1fcd254a9..000000000 Binary files a/graphics/icon/google-refine-48px.png and /dev/null differ diff --git a/graphics/icon/google-refine-512px.png b/graphics/icon/google-refine-512px.png deleted file mode 100644 index 39375f567..000000000 Binary files a/graphics/icon/google-refine-512px.png and /dev/null differ diff --git a/graphics/icon/google-refine-logo.ai b/graphics/icon/google-refine-logo.ai deleted file mode 100644 index 207e6e67c..000000000 --- a/graphics/icon/google-refine-logo.ai +++ /dev/null @@ -1,4393 +0,0 @@ -%PDF-1.5 % -1 0 obj <>/OCGs[5 0 R 65 0 R 193 0 R 345 0 R 433 0 R 609 0 R 857 0 R 1105 0 R 1106 0 R 1107 0 R 1108 0 R 1109 0 R 1110 0 R 1111 0 R 1296 0 R 1297 0 R 1298 0 R 1299 0 R 1300 0 R 1301 0 R 1302 0 R 1487 0 R 1488 0 R 1489 0 R 1490 0 R 1491 0 R 1492 0 R 1493 0 R 1494 0 R 1710 0 R 1711 0 R 1712 0 R 1713 0 R 1714 0 R 1715 0 R 1716 0 R 1717 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream - - - - - application/pdf - - - Print - - - - - 2010-10-18T14:36:50-07:00 - 2010-10-18T14:36:50-07:00 - 2010-09-27T16:24:05-07:00 - Adobe Illustrator CS5 - - - - 224 - 256 - JPEG - /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADgAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9UkgAkmgG5JxV5lr3mzW vNWpS6B5VYxWMfw32qAkArWh4sOi+FN2+WbbDpo4o8eTn0DrcueWQ8MOXe0v5QaVGitDqV3Hc0HO UFKFu5AABA/2RyX8oS6gUj8kO82vH5e+Zbbex813cdOkbeoF79aS0/DB+bxnnAfj4J/LzHKZbGi/ mzaj/RtfgnA7SgEnp/PE/wCvBx6Y84n8fFeHOOUvx8ly6l+clr9q0s74DufTHj/LJCcHh6U9SEjJ qB0BXDz7+YFsaXnlV5qdfq/qfhxE2D8nhPKf4+xP5rKOcG/+VwrB/wAdHQby0/m3rT/g1iwfybf0 yBX8+BziQnvl/wDMjytrkoggna2umNEt7oCNm/1SCyH5cq5j5tFkxiyLHk34tVCew2LKMxHJdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYN+a+t3drpNtpFiSLzWZDACOvpigYD/AFi6 r8q5sOz8QlIyPKLha3IRHhHOSO8vaXpXl/SorCKWMMo5TyEgNJIR8TH+HtjllPJK6RjEccatHPrG nL/u4E/5IJ/hkRgmejI5496i3mCzH2VdvoAH68mNLJidTFSbzD/JD9Jb+zJDS+bE6nyXLql4wSSQ CK3ckCRVr+s4DhjyG5SMsuZ2CYJEXUMZ3cHcFSFFP9iMoMq6NwF9VdNORhUry/12LfrrlZysxjSP zH+X2gayhae3EFz2u7cBH/2W1G+kZfh1k4cjfvaculjLoxiO4/MHyXQN/ue0NP8AWMiIPf4nT/hl GZJjhz/0JtAllxf0osx8tefPLvmBVS1n9K8I+Kzmoslf8ns/+xOYOfSTx8xt3uZi1MJ8ubIcxm92 KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5n+cCPb32gamwrbwTOkp8CSjj7wrfdm27LP1R db2gPpLXIg0OxGZ1OFa4SYKW1wkwUm1e1je4nSFPtOafIdzkJkRFs4CzTLFgiEAgKgxgceJ8M1XE bvq7LhFUgOU2mPvWSyY/ShOZFDIO6TTZx/1WQW91DPGrowIYbZgSgYndzYzBCozKqkt0yIDIoKu+ 3TLmli3mH8vNB1hjcRqbC/ryW5twBVutXTYN8xQ++ZeHVzhtzDjZdNGW42KSxeYfPnk4rHrMJ1nR 12F6hLSIvu53/wCRg+TZacGHN9Hpl3fj9DWM2XF9Xqizjy95v0HX4g+n3IaWlXtnosy/NK/iKjNf m008Z9Qc7FnjPkU5yhtdirsVdirsVdirsVdirsVdirsVdirsVedfmN5g1nTtbggsbuSCJrZXZEIo WMjiv3AZtdDhhKBJF7uu1mWUZUD0ZF5B1C9v/LyXF5M08xlkUyN1oDtmJrICOSg5GlmZQsoX81NN +u+S7wheUloUuU/2DUc/8AzZPQT4co89kayF4z5MV0C/tdX0iB0kBuoUWOcftBwKbjwalRmzlcJe TrQBIeaq/ONirihGXCiLDUbDYkxpbZF5ctaRtdON3+GP5DqfvzX6ue/C52lhtxJ3XMJy3MFZSrCq nYg9KYQaVK3WfS5PVhrJZMaundD4jMkEZRR+pxyDjNj6U2ivfrMIdH5xt/nvmKcfCXJGTiDdcCt1 xV2xFD0PXFLENd/LXSb2UXmlu2k6ih5JLb1CcvHgCOJ91I+nMzFrJAVL1RcXJpYnePpKAt/OvnDy q6W3mqzN9YV4pqUFC1Pc7K3ybi2SlpcWXfGaPcxjqcmPaYsd7PdE8xaNrdt9Y0y6Sdf20Gzp7Oh+ Jc12XDLGakHOx5YzFgpjlTY7FXYq7FXYq7FXYq7FXYq7FVG9meG1llSnJBUV6ZKAs0xmaFsZubiC 9Z5ryytbiWOGQo8sKuwEaM6irVNOWZoiYjYkb97icXEdwCwLyx+Z+vyavp2mJbWVvaXN1DFIkEJj ossiqxADUrQ+GZ2fQw4TKySB3uLh1cuIRoUS9iv7SO9sbmzk/u7mJ4X+UilT+vNJCXCQe52so2CH h3lSxkME6xP9W1XT5mj5jdWU/wC65F/aXkG+XbOkykH3F0MBXvDK7e5S9UwXKehexirxVr/s4z+0 n+Zocx94GxybtpbHmpegyXCRSsERiAZD0p3OZAncbDRw0aLMre804RpHDPGVQBVUOtaD6c1E8c7s guzjONUCESGBFQajKqbLXVxS40IIIqDsQcVSmeCfTZWurQcrY/30HgPEZlRkMg4Zc+9xpROM3Hkm Vrdw3UQliaqnqO4Pgcx5wMTRciExIWFauQZN1xS3XAq2RI5Y2jkUPG4IdGAKkHqCDhGynd595p8n to1zBr/lZDbXlu5eW2QkowAqeK+4BBXoR0zY4M/iAwybhwc2HgIlBnvlbzJZeYdHi1C2+Fj8FxDW pjlA+JT+se2avUYDjlwl2GHKMkbCb5S2uxV2KuxV2KuxV2KuxV2KoTVp4oNNuJZU5xotWSvGo+eW YgTIAMMhqJtgt15z0q2V6aaX5KyH/SCNnUqf2D2ObIaWR/i+x151ER/D9rEtK1PybDrun/V/Lzxz /WofRlN9IwV/UHFuJShod6ZlZMeUwNz2o/wtGPJj4hUeve9yzn3dPHbqD9F/mPq9mBxivR9Zj92c CQ/izjN/hlxYYnu2dLnjw5SO9Hag3ERSgfHG9UbuDQ5dijZILTklVFE291Bex+m4Afuv8RlU8csZ sNkZiYooO6tZLc1+1GejeHzzIx5BL3tE8Zj7lOOd0NUYqfY0yZiCxEqRUWr6gn2bmT5FiR+OVHBA 9A2DNIdUVH5j1Resocf5Sr/ADKjpIdzYNVPvRUfmq7/bijYe1R/E5WdFHoS2DVy7kOmqtHd+taL9 X5/bjJ5IT9woMmcFxqW7AZqlcdk6XXJYaC+tmiB/3YnxKf8AP55hnTA/Sbcoagj6gmFtf2lz/cyq x/l6H7jvlE8Uo8w3xyRlyKIrlbN1cVUL+NpLSQL9tRzQ9+S7jLMRqTDILiwOaaXyhrUfmKxQtoOp EJqlqnSKQn7SjtvUr9I2qMzZQ8WJxn648nEjPwzxj6TzepW1zb3VvHc28glgmUPFIpqGVhUEZppR INF2oIIsKmBLsVdirsVdirsVdirsVSbznKYvK2pSD9mEn8RmRpReSPvadQf3ZeC3WplwanOjEHQm Sjocpk8xaYe31uD/AJOLjmHol7iyxfWPe+mc5R6J5d+a0H1LzJoWsqKK9baZh2CtX8Vlb7s3HZsr hKPxdXr41ISUNTb/AEcezD9RzP0/1ODl5IB2MU3wkqRRlPehFR+vMkCw0HYprZarHKBFcUDHbkej fPMPLpzHeLlY8wOxau9NIq9vuO8ff6MOPP0kjJh6hL+ZBodiOozKpx7XCTBSbXiTI0m14kwUm2R+ XtXVwLG4Ne0LHuP5T/DNdqsFeoOdps1+kplcaLp8xqE9J/5o/h/DpmPDUTHm3ywRPko/Vdatf95r gXMY/wB1y9fvP9cnx45cxXuY8GSPI2uXXfSYJfW727fzUqp/z+nAdNe8TajUV9QpMLe8trgVhlV/ EA7j5jrmPLHKPMN8ZiXIpPFBbST3ui3iCS1uQQIz0oRXb6P1ZlZLMRMcw48KEjA8ik3lLU7nylrp 8q6pIW0y6YvpF23QFj9gntyP3N7Ng1OMZoeJH6hzZaeZxy4Jcuj0vNU7F2KuxV2KuxV2KuxV2Kpb 5k0qTVtDvNOikEUl1GUWRgSAag1NMtwZOCYl3NeWHFEjveYH8j9XPXVIP+AfNt/Ksf5pdd/J0u9E 6Z+TGp2epWl22owMtvNHKyhHqQjBqD7shk7TjKJFHcMoaCQINvWM07s2F/m5p31vydNMoq9lLHOt OtK+m34PXM7s6fDlrvcTWwvH7mHG7+taJBcVqXVGc/5VKN+ObzCKm6bIfSt1AU+rSf78hQk+4FMu w9R3EteXofJCh8tprtMrHWHhoktXi7H9oZjZdMJbjm3485Gx5JlLb2l9H6kbDkeki/xGYkZyxmi5 BhGYsJXc2lxbn4xVOzjpmZDJGXJxpwMVESZZTC14kyNJtekpUgg0I3BHUYDFILNNC1hb6DhIf9Jj Hxj+YfzD+OabU6fgNjkXaafNxjfmmtcxXJaYKylWAZT1B3GI2QUBc6Lp7VlWtsy7l0PECnsdhmRD UT5c2mWCPPkxuW8kF2JVmaUxEenK3UhTt45sRjHDVVbgmZ4ru048w6HZ+aNCEZok1PUtZu8cg7H2 7NmuxzOGbnziMsF35e+ari/hl0TV6x67pvwTK/2pY12EnuRty8dj3yrWacRPHH6JNulzmQ4ZfUGZ ZguW7FXYq7FXYq7FXYq7FXYq7FXYqhNXsE1DSryxf7N1DJF8uakA/Rk8c+GQPcWM48USO94doU7N 5emt5KiS1l4FT1ALA/rJzq4fWHm5/SU4v15aPZzD9j4T8iP+bccJrLILkHoBSsPmXTjrgxpXscFJ Vre7mgfnE5U9/A/MZXPGJCiyjMjknVrrkEg4XA4MdieqnMHJpSN4uVDUA810+l286+pbMFJ6U3Q/ d0wQ1Eo7STLCDuEtnt7i3NJUIHZuoP05lwnGXJx5RMeamHyVMbRFpezWs6TwtR0NR4HxByvJjEhR ZwmYmwz7TtRhvrVZ4u+zp3Vu4OaLNiMJUXb4sgmLCKrlTaxXX9d9dzaW7fuFNJHH7ZHYewzaaXTc PqPN12o1F+kckmEmZlOLbI/K2oVL2bnr8cX/ABsP45rtbi/ic7SZP4UJ500O8WaHzLovwaxp3xOF /wB2xL1BHcgV+Y28Mq0+QVwS+ktuaBB44/UGY+WNftte0W31OBSglBEkR3KSKaMv39PbNfnwnHMx Ln4conESCaZS2OxV2KuxV2KuxV2KuxV2KuxV2KvDNbtv0X5v1+wpxjuCLmIdvjIkoPl6h+7Oo0M+ KED8Hn9XDhnIJpGnreWqdSqMw/2DE/wyRPDnYAXiY+HzZOGiLj92sUfQ8A7fN9/+I0yuO9lkVMPk qQqRB5CQgqQCx9gBUnIyIHNIFqsF5PA3KJyh706fSMjPGJcwmMyOSZ2/mA043MYYHqy/0OYk9H/N LkR1PeFf0tHvN4nEch7D4T/wJ2+7K+LLDnuGXDjly2UpdFuU3iYSDw+yf6ZOOqieezGWAjkqaVf3 mk3QeSNhA9BMtNiPEHpUY5sccsaB3TinLGd+SaeYPMkbx/VbJ+SuP3sq+BH2R/HMXS6Qg8Um/Uam xUWOCTNjTg2uEmCk2iLS8kt7iOdD8UbAj39vpyueMSBBZwnwmw9AhnjuLdZUPKOReQ+RGaGUTE0X cxkCLSH8mQw8rXK9UW+lEZ8V9OP+Ncn2n/eD+r+tGg+j4s8zXOc7FXYq7FXYq7FXYq7FXYq7FXYq 8k/OGzNr5g0vVFFFuYWt5CPGM9T9Ev4ZvOycmxj3F1HaUNwe8N6BSTR41b7PxqfkWP8AXMnVbZCX Gw7wY3Dbu96LU/a58G9qHfNmclR4nCEd6XX0we8mYfZ5FV+S7D8BhxCohZc1tvFLPKsUS8nY0Awz kIiyiMSTQZBc2sWnaRKFNZZAFZ/Ek9PlTNfDIcmQdwcuUBCB72Ph82VOGuD4KSv5Eddu+RpUTBqV 3DQRykAfsncfccqngjLmGcckhyKYQ+YpBtNEGHipp+BrmNLRDoW+OpPUIgXui3H94gRj4rQ/euV+ FljyZceOXNx07TpCPRuAC2ygMrVPgB1x8eY5hPhRPIrH0W4X7Eit86g/xyQ1UeoYnTlRbT75OsZI 8VIP6ssGaB6sDikOiMHmpNG0G7huSUuVRvqMZFGZn22B7KxqcxM+nE5gjl1cnDm4YkH4Mx/LzRZd I8p2VtMvC5kBnnU7ENKeQBHiq0BzT6zLx5CRydrpcfDAAskzFch2KuxV2KuxV2KuxV2KuxV2KuxV hH5wab9a8otcqP3lhNHMCOvFj6bf8TB+jM/s2dZa73D10Lx33MU8pSiTR1b/ACzX7gc22qPq+Dq8 HJQaJLfWb6c/7rjMqD/WFSf15cJcWOI+DVIVMlIoEmuJljiUvI52AzOlIRFlxwCTQZjpemRWMNB8 Uzf3kn8B7Zqc2czPk5+PGIjzQPmmfjBBFX7bFj/sRT/jbL9DHclq1J2AY8HzZOGiFAiQSOPibeND /wASPtkbvYJ5KfqEkkmpPUnJUhcHxpVysSaDrgISiEt7lnCBDzPVe4+fh9OVmcQLtkIlNLbS4VFZ wJW/lIqv3HrmHkzk7BvhjA5rrpbC0i9R7hrNO3CVo1+hK8T92UAX0ttukmXzDrF1di10FrnUpf5W hQinj8CK1PdsMoQiLlsmMpSNR3Zl5P8Ay5vDfjXPNJE98CGt7OoZI6dC9PhqOyjYdeua7Va4VwY+ Xe5+n0hB4p83ouat2DsVdirsVdirsVdirsVdirsVdirsVQWt6cupaPe2B/4+oJIgT2LKQp+g75Zi nwyEu4sMkeKJHe8e8iy/7ipoG2khmYMp6gED+IOdBqOY9zpMXJd5pZ4ZUlQf70RNA30MG/jluk3F dxtqz87TLR9JisIan4rhx+8fw/yR7ZTmzGZ8m3HjER5pjXKG1ifmi45aiIx0iQA/M7/qIzaaMVC+ 9wtQbkhLeJI4vrNwKr/uqP8AmP8ATLpTJNBpA6qMk7yuXc1ZuuWRFCkFVtre5uG4wRtIe9BsPmeg wSyRjzKiJPJNE0RIIxLqE4iTsibsT4fP5ZjHVGRqAtuGGt5FFQwgilvH9Vh/nO8zD5n7OVyn/OPE fs/ayA7tvvULrW9H01TGZAXHWKP4mr7+/wAzkCJS3KRQasIvOvmGn6HsDa2jf8fs/wAK08QzCh/2 KnKcmbFj+o79zdjwTnyGzKNH/J7T1kFzr95JqVwd2iUskQPhyrzb7x8s1+XtOR2gKc7HoIj6jbO7 DTrDT7cW9jbx20A39OJQgr4mnU5rpzlI2TbnRgIigKRGRZOxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV40bcaV5/1rTqcYrlvrEI7fH+8AHyEhH0Zv4S48MT3bOlyR4cpCM1Wy+tpbilfTnR2/wBWtG/X hxT4b9zDJG696OrlbY4sqqWYgKBUk9ABirBby9gm1Se4cGWMufTUbBgNlr7UGbeESIAOvmbkSjE0 vVtSkEpj9CKlE5/CAPYdT92V+NDGK5lkMcpJxZ+WrGAc7gmdhua/Cg+gfxOY89XI8tm6OADm3ca1 bQxmOyEaxpsZ2okK/LpyPyyMcJ5z/ak5OkUjOvRSXQSyhl1XUH2UhT9yKAT9wy6Ww39MWsAk95T/ AE/8vfO2t0fVbhdJs23MK/FIQf8AIU/8Sb6Mwcmvxw+n1FzMeinL6tma6D+W3lTR+Lpai7uVofrF 1SQ1H8q04L9Armtza3JPrQ8nPx6SEOlllGYjkuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2 KvLfzTt/qHmjRNaUUSUG2nb/AFT1P+xkP3Zt+zpcUJR+LrNdGpCSIKUjV/5iR91P65O92qlWRPSt 0r/eTfFTwQbD/gjgBs+5JFBKtRsbi+HoGb0bT/dnDd39qnZR9+X45iO9WWmcTLbovstI06zoYIhz H+7G+JvvPT6MZ5pS5lMcYChqnmLS9OBEsoeYdIE+Jq+/8v04wxGSymAk1unnPzU9NNsmWyr/AHjf BD/snaganhv8sullxYeZ3+1hHFky8hsyzR/ybiZkm8wX73bqNraAlYx7cz8RH+qFzX5e1D/AK97n Y+zwPqLPtK0TSNJh9HTrSK1To3pqAzU/mb7TfSc1uTLKZuRtzoY4x5CkblbN2KuxV2KuxV2KuxV2 KuxV2KuxV2KuxV2KuxV2KuxV2KuxVh35saX9e8nXEirWWxdLlPGgPF/+EcnM7s/Jw5R57OJrYcWM +SVeXIzquj6VNX4ZVkad/DhxV/xFMyc0uCUnFxR4ohrWry3huJJZ5FhQbfGQqqBsq79wBv74cMTW yMshbGp/N9vJMLbS7eXULptkSNWoT7ChY/dmUMNC5Gg0eJewFpjZeR/PuuUbUZ10ayb/AHUu8pH+ qpr/AME4+WUT1mKH0+ot8NJknz9IZdoX5X+VNKKyNbm+uRv611RwD7JQJ+FcwMuvyT60PJzMejhH pbLFVVUKoAUCgA2AAzDcpvFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVqyxM5RX UuOqggkfRhpFrsCVK7tobq1mtZhyhnRopF8VcFSPuOGMiDY6IkLFF5VpflX80tKjl0fTmhhsfUcx 3zvGdmoDxrzkUGlacOubjJqNPP1Su+51cMGaPpHLvTXTPyetGkFz5g1CbUZzu0SEpH7gsSXI+XHK cnaR5QHC2w0A5yNs40vRdJ0qH0dOtIrVO/pqAW/1m6t9JzAyZZTNyNubDHGPIUjcrZuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KsY83atf/AFm10HTJhbXl8rS3N53t7ZCAzr/l sTxXMzTYgQZyFgdO8uLqMhFRGxP2BLB5R8iRxIklvFK6dZmkYys1almZTUsTmR42Y8raPDxBaPL3 lxWCafc6hbMdlFrdXAOw7c2bDxZP4hH4gIqH8Jl8CUXD5f1niG07zFfqDuv1horgfc0f6zlUskB9 UI/DZtEJfwyP3qjXfnfRG9S7VfMFhSshhjWC7TxKxr+7kHsKHIcGHJy9B+YZcWSHP1D7U60XzJo2 sxlrG4DSp/fWz/BNGehDxn4h+rMfLglDmG/HljPkUzypsdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirAvzBiS11/SNReT04biOWymkb7KnaWL7zyGbTs+fpkPi67XR3B+CXs7 xtxfY5sAARYcEkjmn3l23JDXTjY/DF/E/wAMwdXL+FzNNH+JN/3tvIZ7cVrvLD/N7r/lfrzF2kKL k7xNhFC+juIVaP7J6/Mdsq8MxO7Z4gkNko1fy1pWpyC4dWt79P7rULZjFcIRsKOvX5NXLseaUduY 7jyap4oy35HvQkeseadC+HU4TrWmL/x/2qhbpB4yw1o/zT54Tix5Pp9Mu48vmgZZw+r1DvHNkWk6 3pWr2wuNOuUuI/2uJ+JSezqfiU+xGYuTFKBqQpyYZIyFgo7K2bsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVYr+Zdn6/lWW44CRtPliuwhFQRG1HB/2DNmZoJ1lA79nF1kbxnyYrF6E QSFpK2sgrbs25TpQBv5fCvT5dNsInmObqyRyKbJ5hurGNIXt0aJRxRlJFQPv3yn8rHIbB3bRqJQ2 rZExebrc/wB5Ay/6rBv18chLQHoWY1o6hc2v2HMz25ZJD/eRsvwuB8q0bwOAaWdUeSfzMbsJpBq1 pIqsWMXMVX1BxBr4H7J+/MaWCQ83IjmifJGBwRUHbKabbY/rFhLYalHr2kxAX6grdW6igu4/tNG1 P26AlG8QBmRjPFEwly+5pmOGXFHn97KtN1G01KwhvrN/Ut51DI3f3BHYg7EeOYM4GJIPMOZCYkLC JyDJ2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVD6lZR32n3VlJ/d3MTwt8nUr/HJQ lwyB7mM48QI73j1jLJNplrHJtPCksUi9+cGx/wCIZ1ENiT7vtedn3e9EWeq8F9GcepAex6j5ZPJp 73jsWMM1bHkiJLTknrWjerEew+0Mrjl3qWxZnH1juEMs5BodiO2W8LVae+X9aSJ/qdyQbeQ/CW3C sfn2OYOq09+qPMOZp89ek8mS/Uol3t2a3Pgh+H/gDVfwzXeITz3c7wx02QmqXN1b2hMxjejKY5Fq rBgaj4TUH78twwjKW1teaUox3QlnqCaJeLqKGmhaq4+up2trpjx9b2R2+F/A75HLi4xw/wAceXmP xyZ4snD6v4ZfYWbZrXPdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiryPULf6h50 v7Ej929ybmMdit1EWansH5Z0WmnxYL8vuLotRHhzV+N0koVuJIif7ssG/wBhX+mba7APe68ijSpb X08LhonKsew7/RkZ4hIbpjMjkmJ1K1nYpexFJVNDLHsajbcHMYYZR3gdm45AfqG64Wfqb2syTj+W vFvuODxa+oUvh39Jtkeja+YbVodS5RvAtUkYfbUdvds1+o03FK4b25uHUUKn0STUdZlv7kyN8MY2 ij7KP65m4tOICnEy5jM2m3lq8huEn0y5USQzqSEbcMCKOv0jMTW4iKmOjlaTJdxLIfI80x0me1kd pE0+7ns7eR92MMTfu6nvxU8a+2avVgcV94BdlpieGu40yHMVyHYq7FXYqoX63LWNytqeNyYnEDeE hU8T9+ShXEL5MZ3RrmxDTvN0caRsZ2QUQtDOTIJAxp8DN8at4VJGbPLouodfj1bNs1TsnYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXnP5jWfoeaNE1EbLcq1rIR/Mjc0r8+bZuOzsnonF1evh6oyYtrEYt7 i8k7zGNUHf4viY/embfTzsRHdbrMsaJRmkaX9Xj+u3Yo6gskZ/ZAFan3yvUajiPDFnixcPqKSLK8 shP7TElj+JJzP2AcVd6wDfATQdD/ABx4VtH2uqaiBSN2dR1DDkPxzHyYMfVtjlkOSYc3aIy3MECA CrA1jPzLgnj92YstjUSW4b8wFHTZ7y+vFTQLJ57uNxS4Vma0jNeskpVdvYdcry5RGPrIr7WzFiMj 6R+p6l5f0n9FaXHaNJ605Ly3M9Kc5pWLyNTsOR29s0GbJxyt3eLHwxpMcqbHYq7FXYqo3twbaznu QhkMMbyCMGhbgpNK79aZKEbIHexlKgSl2gWOkCBbu0s/QkeoDuGZ+Pbg8nxemR9mm2W55zuibasM Y1YFJvlDe7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqxT8y7Uv5a+uqKyaZcQ3ajvRW4v/wjk5maCVZK /nCnF1kbhfduxKWzSfUYLo0eNEJHhzBHA/czZtY5KiQ6wwuQK3XrpYNLmqwVpB6ag9TyND+GHTxu YRmNRKQ22lX1xGEt4ykRoZJ5KqG+QPxU+jM+WojE2ebixxE8laSLRtPIFxMLic7BBULU7bKtWY18 Pwys55y5ekMvDiPMp1pmgeatU4m0sRYW3a6vR6dAf99wL8XyJ65hZdZjj1s+X63Lx6SculMp038t dIiZZtWmk1e5Xek/wwA/5MC/D/wVc1uXtCcto+kfjq5+PRQjz3LLIYYYIlihjWKJBRI0AVQPAAbZ hEk7lywK5L8CXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqhtSsY7/Trqyl/u7q J4W+TqV/jkoS4ZA9zGceIEd7zjy6GGgpc3PGNrBXgnLGgE0TemA1flyzcZT66HX7nVY4+mz0QKXs V/dBNMs5NVuo/hBgTlGhP80rfAlfGuWn0j1HhDWBxHYWU8tfIXmHUgDrF6unWx62dl8cpHg8zbA/ 6oIzFlroR+gWe8uTHRyl9Rr3Mp0Tyh5d0WjWNmiz97mT95MfH42qRX22zBy6meT6i5mPBCHIJzlD c7FXYq7FXYq7FXYq7FVO5keO2lkjXk6IzIviQKgZDLIxiSOYDKAsgFg2j61qM84mOon636zqbObl 6TqGKgVX+7qOnwnKtH2fLJihnGSfqiCRYMb6jhrpy57NWXWgTljIjsSPPy3/AGM3s7uK6t1mj6N1 GxIPcGmZU4GJophISFhWyLJ2KuxV2KuxV2KuxV2KuxV2KuxVD32o2FhEJr24jt4mPFXkYKCx7Cvf JQhKRoC2MpiO5NIe38w6BcmlvqVrK3SiTRsa/INkjhmOYPyYjLE8iEsfyF5Uub+XUJbY3DXEhnaJ pGaAyt9p/TB4kt3rlw1eQR4Qar5tf5aBN0n8Fvb28Sw28SQwoKJHGoVQPYCgzGJJNlvAA5KmBLsV dirsVdirsVdirsVdirsVdirHrnyTpl1qUt5MzLFK6yfV4gIxyCBDyYb0PGvw0Na75lYdVLFDggK5 /abcbJpYznxS3Tuzs7Wzt0trWMRQp9lB77k77knuTmPKZkbPNvjERFBWyLJ2KuxV2KuxV2KuxV2K uxV2KuxVhnme+ls/NVu/wkvZ0s+e/FhI3r8O3JlMde9Bmy0kIyxkHv8A7HX6qZjMEdyJsE/SduZ7 1I5lJpGsiKQQKg9vemHMBA1FcRMxcldtF0+N/W0cnSbsCjm3CiNj29WE/u3+dOXgRlHHLlP1D8dW 7gH8PpKrH5hubKia5AIkA/46NuGe2PvIN3h/2VV/ysgcAP0H4df2/jZkMxH1fPp+xPgQQCDUHcEZ jOQ7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWI/mLbL9UsL4ry9C cwyf6lwtP+Tqx5sOz5esx73B10fSD3MYi1efm0S3DW8yEj7RCN8+wObc4BV1Y+11YzHldFFQ+YdR guE+su5CncbVI9jTfKzpYSHpbBqJxO7LYrpZrcSqyTWzqTU/D8NN69vn0zVShRrkXYidi+YUvLmp xx+nZFq2shZbF/5WXdoD9HxR/wCTt+zuNVhIN9ev6/1stNlBFfL9TI8wnLdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSnzZYNf+W9QtkFZTC0kI/4ti/eR/wDDoMv00+HI D5tWeHFAh5Q9vLcywmDf60kcob+VeAqx+ZOdRDMBHfo87LGSdurUt/Jbzywo3O3ibhxk+IFl2NAe m/hk44xIAn6ixMjE10R9tqs7WL2/B4LZzyZg1E/4foPGhyjJhAlxWCQ2xyExrkE38qR399MkVvDK 2n+tDcPeOoWIGCVZP3bVPqc+HH4fpzX63LCt/q3Hzc3SY5X/AEXpGaR27sVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVeVRw2+nSXWn15XdtcPbQW0amSZogxeIiJKvxEb r275uxMyAPSrdOYcJI62idM8iaxdMJHiSwQ7+vdUmnNTWqwoeC/Nmr7Y5O0QNhv7th8049CTz2ZZ pvkfQrNlmnRtQuV3E12RJQ+Kx0Ea/MLX3zXZdZkltdDyc7HpYR8z5sgzFcl2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KpFquuXVtrCWEbRQxmBZjPMjMtSzqRUPGBTiP vzKw4BKHFvzcbLmMZV5K9nqi+t/pEUYMzBEvYKGOQ9FD/tIfAGo/yq7ZCeIgbfJlDKCf0ptlDe7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWKefLFpUsblCwZHe H92CW/eLzBAFSTWLiB4tmw0GXhJBcHXY+IApXo2geap4PTk4Wdo7h2E68mI/4xAjr3DccydRqsN7 Cz+Px1cfBpstb7BnyrxULUmgpU9T880ztm8VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVf//Z - - - - - - uuid:fc6e5f3d-7fcc-7942-b7c9-1536cd5e2491 - xmp.did:F87F1174072068118C14A1D6B52AC88B - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - xmp.iid:F77F1174072068118C14A1D6B52AC88B - xmp.did:F77F1174072068118C14A1D6B52AC88B - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - - - - saved - xmp.iid:F77F1174072068118C14A1D6B52AC88B - 2010-09-17T16:55:22-07:00 - Adobe Illustrator CS5 - / - - - saved - xmp.iid:F87F1174072068118C14A1D6B52AC88B - 2010-09-27T16:24:05-07:00 - Adobe Illustrator CS5 - / - - - - - - Document - Print - - - False - True - 1 - - 612.000000 - 1090.666992 - Pixels - - - - Cyan - Magenta - Yellow - Black - - - - - - Default Swatch Group - 0 - - - - White - RGB - PROCESS - 255 - 255 - 255 - - - Black - RGB - PROCESS - 35 - 31 - 32 - - - CMYK Red - RGB - PROCESS - 237 - 28 - 36 - - - CMYK Yellow - RGB - PROCESS - 255 - 242 - 0 - - - CMYK Green - RGB - PROCESS - 0 - 166 - 81 - - - CMYK Cyan - RGB - PROCESS - 0 - 174 - 239 - - - CMYK Blue - RGB - PROCESS - 46 - 49 - 146 - - - CMYK Magenta - RGB - PROCESS - 236 - 0 - 140 - - - C=15 M=100 Y=90 K=10 - RGB - PROCESS - 190 - 30 - 45 - - - C=0 M=90 Y=85 K=0 - RGB - PROCESS - 239 - 65 - 54 - - - C=0 M=80 Y=95 K=0 - RGB - PROCESS - 241 - 90 - 41 - - - C=0 M=50 Y=100 K=0 - RGB - PROCESS - 247 - 148 - 30 - - - C=0 M=35 Y=85 K=0 - RGB - PROCESS - 251 - 176 - 64 - - - C=5 M=0 Y=90 K=0 - RGB - PROCESS - 249 - 237 - 50 - - - C=20 M=0 Y=100 K=0 - RGB - PROCESS - 215 - 223 - 35 - - - C=50 M=0 Y=100 K=0 - RGB - PROCESS - 141 - 198 - 63 - - - C=75 M=0 Y=100 K=0 - RGB - PROCESS - 57 - 181 - 74 - - - C=85 M=10 Y=100 K=10 - RGB - PROCESS - 0 - 148 - 68 - - - C=90 M=30 Y=95 K=30 - RGB - PROCESS - 0 - 104 - 56 - - - C=75 M=0 Y=75 K=0 - RGB - PROCESS - 43 - 182 - 115 - - - C=80 M=10 Y=45 K=0 - RGB - PROCESS - 0 - 167 - 157 - - - C=70 M=15 Y=0 K=0 - RGB - PROCESS - 39 - 170 - 225 - - - C=85 M=50 Y=0 K=0 - RGB - PROCESS - 28 - 117 - 188 - - - C=100 M=95 Y=5 K=0 - RGB - PROCESS - 43 - 57 - 144 - - - C=100 M=100 Y=25 K=25 - RGB - PROCESS - 38 - 34 - 98 - - - C=75 M=100 Y=0 K=0 - RGB - PROCESS - 102 - 45 - 145 - - - C=50 M=100 Y=0 K=0 - RGB - PROCESS - 146 - 39 - 143 - - - C=35 M=100 Y=35 K=10 - RGB - PROCESS - 158 - 31 - 99 - - - C=10 M=100 Y=50 K=0 - RGB - PROCESS - 218 - 28 - 92 - - - C=0 M=95 Y=20 K=0 - RGB - PROCESS - 238 - 42 - 123 - - - C=25 M=25 Y=40 K=0 - RGB - PROCESS - 194 - 181 - 155 - - - C=40 M=45 Y=50 K=5 - RGB - PROCESS - 155 - 133 - 121 - - - C=50 M=50 Y=60 K=25 - RGB - PROCESS - 114 - 102 - 88 - - - C=55 M=60 Y=65 K=40 - RGB - PROCESS - 89 - 74 - 66 - - - C=25 M=40 Y=65 K=0 - RGB - PROCESS - 196 - 154 - 108 - - - C=30 M=50 Y=75 K=10 - RGB - PROCESS - 169 - 124 - 80 - - - C=35 M=60 Y=80 K=25 - RGB - PROCESS - 139 - 94 - 60 - - - C=40 M=65 Y=90 K=35 - RGB - PROCESS - 117 - 76 - 41 - - - C=40 M=70 Y=100 K=50 - RGB - PROCESS - 96 - 57 - 19 - - - C=50 M=70 Y=80 K=70 - RGB - PROCESS - 60 - 36 - 21 - - - - - - Grays - 1 - - - - C=0 M=0 Y=0 K=100 - RGB - PROCESS - 35 - 31 - 32 - - - C=0 M=0 Y=0 K=90 - RGB - PROCESS - 65 - 64 - 66 - - - C=0 M=0 Y=0 K=80 - RGB - PROCESS - 88 - 89 - 91 - - - C=0 M=0 Y=0 K=70 - RGB - PROCESS - 109 - 110 - 113 - - - C=0 M=0 Y=0 K=60 - RGB - PROCESS - 128 - 130 - 133 - - - C=0 M=0 Y=0 K=50 - RGB - PROCESS - 147 - 149 - 152 - - - C=0 M=0 Y=0 K=40 - RGB - PROCESS - 167 - 169 - 172 - - - C=0 M=0 Y=0 K=30 - RGB - PROCESS - 188 - 190 - 192 - - - C=0 M=0 Y=0 K=20 - RGB - PROCESS - 209 - 211 - 212 - - - C=0 M=0 Y=0 K=10 - RGB - PROCESS - 230 - 231 - 232 - - - C=0 M=0 Y=0 K=5 - RGB - PROCESS - 241 - 242 - 242 - - - - - - Brights - 1 - - - - C=0 M=100 Y=100 K=0 - RGB - PROCESS - 237 - 28 - 36 - - - C=0 M=75 Y=100 K=0 - RGB - PROCESS - 242 - 101 - 34 - - - C=0 M=10 Y=95 K=0 - RGB - PROCESS - 255 - 222 - 23 - - - C=85 M=10 Y=100 K=0 - RGB - PROCESS - 0 - 161 - 75 - - - C=100 M=90 Y=0 K=0 - RGB - PROCESS - 33 - 64 - 154 - - - C=60 M=90 Y=0 K=0 - RGB - PROCESS - 127 - 63 - 152 - - - - - - - - - Adobe PDF library 9.90 - - - - - - - - - - - - - - - - - - - - - - - - - endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/Properties<>/Shading<>/XObject<>>>/Thumb 1815 0 R/TrimBox[0.0 0.0 612.0 1090.67]/Type/Page>> endobj 1719 0 obj <>stream -HWn]7߯8?p|5/&{a^X8SU#^IHهlxv6mnoZyl}V{hnCSۓѦSQM^J#94uoɤƖ\_zILYx䱗߫{7}>khFK55ipV{aSp >>,-\KcU%mwwm7/Om~9JK2j_a6)NiؕK3 -NMAsFnQgg'`UTgKtC=wihC+H-=LUx6q`&;}hj*'y{ݾ5d T?0+ !sӵ&pmVDQ:T!#DX(ݝ5آeAMHA3#CeK1jzW R3fcQpݬaVZLW(+Mx=`7!9]é :dN:nuy]dAӄt<(WO<<=Co(Kq:=mF}g}y+}^|/K_7oo._ҏ+}_z4#Ipb gXoBy$ ab杉eNOz@cP~VL&-> `!(<)g-M"QCe~ᣬe"pwشVLO0f%pHr@.1 kn{oM҄($Q?L L05֪:Lu qJ[ ű(BpBYc*-f -&!$2j$"y"#D s4ryAN8:CAdN)LALXPHŖC$'šXc" dH[njƢ(h5"0r?36 H0j4t*JX~!BпuN½,C).ü>p}*E/e3JS$me5NF  (LlIC|3kĭEot`RV˱jJH(;9e&j|(T*P9YPHLwȡbXX#?LJ$0.(v!i+&"BpgN䓍U-EV$CČ}t!'ot[r$F@hғ(ق2SkpRkS?5Yz%(?>vֶw!=WPIDЧd`|܇\M)%"OǛުF^] *z8]k65cm2H1%$0'CFC\0s!S3jjJæ_>(:УspmB,"ĸӦW!EAL'D1qG8N;gϧuP8jYc*)5T0Ec~~:| -Z,9DX.J´׍t[#mx [3.C 'lvbuGHRP= &)4l%^ԞVBϐM^ --j\87ݽ%#ҡcDXwx0cc}C^FMmn( I&uWS[C?+Һ(c{K4h -(t*_3]-xJޖ-K%ŝW.)^R^RI5%8P P1zXTgUsAb ( .wrRT@y?ĸS iɔbYIIP7֣nd]O  WrVs%dD pXqz XIGtݫ|pR6"3jpr['%>YuWWu.,~M9#;?Iej靤*н! 9L kÚB򴨤*2kfgFA"%R@2IiЙUfisWù.]Uz| Pj0 Sف^ZD(DԀInFr2#;ƀwm.ұ mgD -%Ko4n,u7کq4^ o(n햛CÄ4ԅe-6~}ݨI1"(C$=,bÂeSf L׆tm֏x/! -)YR>z"mPϓg0BO.uEEYD[-)*H,Ay̥S +mϐ@m&]hbќ:\iT: - Ie.N?-۳T^8R%eƺ BQm 4դ]C-1օ{J'Le[G|d/GV Y`L7FAS.1 DZ@F~D9ԭ4'F[4E8BX N]=)uGH.m~aJśUn* _/z 'RKhlMoG[IJ.D-`:[!ľT3PLذB1<03{PV, Y[v G E M%Sn`0 0aMΐxLC`\'!oYt(]*oKU< pJ98jm}*5P  AFy;_17 ,@Vč°'1Q:fyLHp:g e;zwלHZWJ/-;UDS. UeKeJ>)7IeCXJeN83&XC̐q,ٿs#4#/ǽx?@%Cmi*ER $~ (=K=|QF4jQV0U 8uy %l眷5 VI>" eۭgH{ѤJW7yI+AZQ12`iA^ ulIA O<ѐOT$aD1kfi$5j==Ν0S[ jI9 he.P#J5a-vS}.H]]țeEa ݽD҆_[QE3|zT'!pY*veH7k@XCQU# aɺV~[X!W%ܢcW="c$+pubv1|EL:,s9éBwt^?u&snc=mZ? #YQN~e=f/̪̇֕LrBI -$f۠Tq:'D$.3q3#TK+@ZelA<`.n+T ҃NhGOxG0K,Cs%°۬cTZvKR12)wݎH 6I%%51Rn_yBm_`p+Osn4MdU]3R,U4vW}^BVsU/>}?q?5F}(=?ǘ3V{}1 $)Öj+`2 D8y=+:h%6"o\{s̈| \gg@A:" -.cQò[y_B~)'5Fb8[>%v]`l^G-gL_T1GS]5!Eg9 4(͊bͮ;*2^&m8jP\R1ܚ’Qr&:=z:YkVg xQ_g\k 9`0tW8|i7ΚѭwJ}9ykюxvOP`=1r; {GrsB׷{1m SmU'GN7?ՌHv]J`!pFV|i8L[?)9dO1g3ϛe:9rEPqmqZ -O-족udvhG犆FB(s]ma)c8K>oT۱*+׸[8HKc'^pRxX:YAv;J0ր{\̸1YQOfFkXT pq.Wt+WѧAͷb"7PZZlU笋rw'h c#q/{Me.u1a=DŽO4nq-6C wP%crw)\)r|}ӊxCXO ⸂;/1@Z#]AӸDݥ58HbXv/0D2Fiu %~M׫"mG2sBl<ރ&Df#H!2a= -ZWErH,}?[ 4{RBP(#cH6V3?Zmp6B$ftu[9լ2bER&$|Y57uG2[ֿ?^g\˙nfh -u&EMxαAG˹RpFHa=>kŮ ly&iiz5ԑ[$?[}K_ɯp+#0/ ')Crsq>0_Xe.J5:GrCfI Ȱ}݈&\XU l싻--x]=&f}0]D -cP\Eڙ36 w[բgλuU-vv:sa'W#y}kD -567E"˔DK6~#կjoVryrX|̰!msh ' ^A3̀on飳|}] &H):1ck85,׀r6A[pF̦Erk0$V *Hic-AoP-/ST})K~s:Ϟhza'jNaHQ/h~png&4,LY=z$QY{Y˭Ifrd4N}`cWnlN׌>O >)5(q&gŃe<(fcμwbZheXm`qU7}hƻh@t=n:DIL!ЬwQm6`3̬1+Kr,7 ؓ²:b\I Ĉ1Tm/26(0.Fp'0# IΜu쓷$RAnD!:!80Q#EvΉnlzT@{nI8$'; {{" Չ'M% )}&zA]xPi 2v7:BXgnI&i3^9Vr"'a 4Ͻ޷q#u#F"ՁMyfڤevjgKfEL;J ¯ =Ky$ߏTO7,Mk?16#*jU} jXw1 (^8YRm:3PhZF<\2 GGڎQ^#>' endstream endobj 1720 0 obj <> endobj 1815 0 obj <>stream -8;Z]"4-nVT$q6L*bM@rQcIMKKC: -?fR*g-G#c0lsR8dE`_XmrQ,>m4ijh7QX*6/qn\bE;EU-A:B0:MG_^E`>mKYJmj@a= -0qJRoB:UJV\qb/l7F#rB#N>p?'I7:/?I[:o[E(id`/[,Y[XGlli%Y6]/$Gph5^dp= -!-tg%FF2="(krZ;'Ac-WV6;rOZ2BYrQ:C=HS;ut28:&nhRfB-NOa*X -j7cN4".>/&'4+YK`>ESd[S-.Dbh2g&c@[^@$8W*e$t7&kRg6.$mO;DphW:"b]WEk7D3%Y" -?^b`gX^5,Ec`'"U+_P*>MV;1)AmJ'tOY3-_Y+p:8^A\gS?.6)\)T0*gZK3_39Q+g^ -8#cuuEk,hgSoCStWXNP]9$^JJ@C`gXV=qHP(eWs&`f!g^I&qrCo+Y2NUpfE0ge+/2 -4k7]78h+U0,Ml -c#f2JJ"4$kRCMgh)Y`[`/"Yu`Q>ZGRm<#l1`-1f;>*uTZYX=NV`$M_ -@HRs\`3e!B=$ma)^lQKMXgdC:" -oZZgQfrpl/425Ag?E^8B;iq=!9r<-GLR%+&V;L<[I#n;;r#5WJh'XW~> endstream endobj 1816 0 obj [/Indexed/DeviceRGB 255 1817 0 R] endobj 1817 0 obj <>stream -8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 1731 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 441.5117 395.4277 cm -0 0 m --4.873 -0.767 l -2.608 -7.65 l -12.751 -11.167 l -11.083 -5.884 l -h -f -Q - endstream endobj 1732 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 436.0674 394.2627 cm -0 0 m --2.864 -5.601 l -7.425 -6.912 l -h -f -Q - endstream endobj 1733 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 434.6992 367.0195 cm -0 0 m -1.477 2.21 l -0.373 0.917 l -1.948 4.775 l --0.252 0.788 l --0.425 3.946 l --0.669 0.034 l -h -f -Q - endstream endobj 1734 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 453.1357 388.915 cm -0 0 m -5.884 -7.854 l -6.419 -10.221 5.326 -11.319 v -h -f -Q - endstream endobj 1735 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 425.2734 447.5381 cm -0 0 m --1.674 -0.265 l -0.896 -2.629 l -4.381 -3.837 l -3.808 -2.022 l -h -f -Q - endstream endobj 1736 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 423.4033 447.1377 cm -0 0 m --0.983 -1.925 l -2.551 -2.375 l -h -f -Q - endstream endobj 1737 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 431.0068 441.3438 cm -0 0 m --3.637 0.512 -5.481 1.509 v --7.326 2.506 -7.774 2.853 -10.397 5.159 c --10.266 4.607 l --7.902 2.271 -6.177 1.438 v --4.451 0.606 -2.058 -0.045 -0.256 -0.268 c -h -f -Q - endstream endobj 1738 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 429.541 441.3193 cm -0 0 m -0.2 -0.018 0.185 -0.024 1.25 -0.23 c --3.426 -2.119 l -h -f -Q - endstream endobj 1739 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 421.624 445.1787 cm -0 0 m --0.063 0.131 -0.906 0.817 y -0.168 -3.603 l -f -Q - endstream endobj 1740 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 425.0635 447.6074 cm -0 0 m --3.673 0.4 l --4.419 -0.126 -4.528 -0.633 v -h -f -Q - endstream endobj 1741 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 429.5742 443.4785 cm -0 0 m --3.335 1.15 l --1.288 -1.461 l -h -f -Q - endstream endobj 1742 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 423.4629 442.668 cm -0 0 m --0.591 -1.782 l --0.481 -2.335 l -h -f -Q - endstream endobj 1743 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 458.1992 377.4004 cm -0 0 m --10.584 1.487 -15.955 4.389 v --21.325 7.29 -22.626 8.302 -30.261 15.014 c --29.879 13.407 l --23.003 6.608 -17.98 4.185 v --12.957 1.763 -5.99 -0.13 -0.746 -0.778 c -h -f -Q - endstream endobj 1744 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 426.5088 441.4678 cm -0 0 m --1.745 -1.783 l --1.399 -1.701 l -h -f -Q - endstream endobj 1745 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 428.5039 440.8438 cm -0 0 m --0.033 0.058 -4.622 -1.874 y --4.032 -1.258 l --3.518 1.27 l --3.519 1.268 l --3.518 1.271 l --5.311 -0.582 l --5.486 -1.417 l --6.486 3.46 -6.552 3.455 v --6.618 3.451 -6.33 -0.93 y --5.77 -2.949 -5.763 -2.965 v --5.771 -2.981 l --5.757 -2.981 l --5.755 -2.984 -5.753 -2.987 -5.75 -2.99 c --5.743 -2.977 l --5.726 -2.973 -3.742 -2.295 y -0.034 -0.058 0 0 v -f -Q - endstream endobj 1746 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 422.9326 437.7773 cm -0 0 m -0.507 0.761 l -0.129 0.314 l -0.67 1.642 l --0.086 0.271 l --0.146 1.356 l --0.229 0.012 l -h -f -Q - endstream endobj 1747 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 429.2676 445.3008 cm -0 0 m -2.021 -2.698 l -2.205 -3.512 1.83 -3.889 v -h -f -Q - endstream endobj 1748 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 434.2529 484.8359 cm -0 0 m --4.639 -0.73 l -2.482 -7.283 l -12.139 -10.63 l -10.551 -5.603 l -h -f -Q - endstream endobj 1749 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 429.0703 483.7266 cm -0 0 m --2.727 -5.331 l -7.068 -6.58 l -h -f -Q - endstream endobj 1750 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 450.1377 467.6738 cm -0 0 m --10.075 1.417 -15.188 4.179 v --20.299 6.941 -21.538 7.903 -28.806 14.292 c --28.442 12.764 l --21.896 6.291 -17.115 3.984 v --12.333 1.679 -5.702 -0.123 -0.709 -0.74 c -h -f -Q - endstream endobj 1751 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 446.0762 467.6064 cm -0 0 m -0.557 -0.047 0.515 -0.068 3.465 -0.638 c --9.489 -5.873 l -h -f -Q - endstream endobj 1752 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 424.1416 478.3008 cm -0 0 m --0.175 0.36 -2.511 2.264 y -0.467 -9.981 l -f -Q - endstream endobj 1753 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 433.6699 485.0254 cm -0 0 m --10.176 1.11 l --12.243 -0.346 -12.547 -1.751 v -h -f -Q - endstream endobj 1754 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 453.9316 377.3291 cm -0 0 m -0.585 -0.049 0.542 -0.072 3.64 -0.67 c --9.968 -6.17 l -h -f -Q - endstream endobj 1755 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 446.1689 473.5859 cm -0 0 m --9.243 3.188 l --3.571 -4.046 l -h -f -Q - endstream endobj 1756 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 429.2363 471.3438 cm -0 0 m --1.637 -4.938 l --1.332 -6.473 l -h -f -Q - endstream endobj 1757 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 437.6768 468.0156 cm -0 0 m --4.836 -4.942 l --3.88 -4.711 l -h -f -Q - endstream endobj 1758 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 443.2051 466.2881 cm -0 0 m --0.094 0.159 -12.81 -5.194 y --11.175 -3.485 l --9.745 3.518 l --9.75 3.513 l --9.747 3.522 l --14.716 -1.614 l --15.204 -3.929 l --17.971 9.588 -18.155 9.575 v --18.34 9.563 -17.543 -2.573 y --15.988 -8.172 -15.968 -8.215 v --15.99 -8.258 l --15.978 -8.261 -15.965 -8.262 -15.952 -8.261 c --15.947 -8.269 -15.941 -8.276 -15.934 -8.284 c --15.915 -8.247 l --15.867 -8.24 -10.37 -6.359 y -0.094 -0.158 0 0 v -f -Q - endstream endobj 1759 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 427.7676 457.792 cm -0 0 m -1.405 2.104 l -0.355 0.874 l -1.855 4.546 l --0.24 0.751 l --0.404 3.758 l --0.638 0.033 l -h -f -Q - endstream endobj 1760 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 445.3184 478.6357 cm -0 0 m -5.601 -7.477 l -6.11 -9.729 5.07 -10.775 v -h -f -Q - endstream endobj 1761 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 31.3882 224.3379 cm -0 0 m --6.938 -1.093 l -3.713 -10.892 l -18.155 -15.898 l -15.78 -8.378 l -h -f -Q - endstream endobj 1762 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 23.6372 222.6807 cm -0 0 m --4.078 -7.976 l -10.571 -9.844 l -h -f -Q - endstream endobj 1763 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 55.146 198.6709 cm -0 0 m --15.068 2.118 -22.714 6.249 v --30.359 10.38 -32.213 11.82 -43.083 21.374 c --42.54 19.089 l --32.749 9.408 -25.598 5.958 v --18.446 2.509 -8.528 -0.186 -1.06 -1.107 c -h -f -Q - endstream endobj 1764 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 49.0718 198.5703 cm -0 0 m -0.833 -0.071 0.77 -0.104 5.183 -0.955 c --14.192 -8.785 l -h -f -Q - endstream endobj 1765 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 430.8896 388.5625 cm -0 0 m --0.183 0.379 -2.637 2.379 y -0.49 -10.483 l -f -Q - endstream endobj 1766 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 16.2661 214.5635 cm -0 0 m --0.261 0.54 -3.755 3.387 y -0.698 -14.928 l -f -Q - endstream endobj 1767 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 30.5156 224.6221 cm -0 0 m --15.218 1.66 l --18.311 -0.517 -18.765 -2.619 v -h -f -Q - endstream endobj 1768 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 49.2109 207.5127 cm -0 0 m --13.825 4.769 l --5.343 -6.05 l -h -f -Q - endstream endobj 1769 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 23.8848 204.1582 cm -0 0 m --2.447 -7.385 l --1.992 -9.68 l -h -f -Q - endstream endobj 1770 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 36.5088 199.1816 cm -0 0 m --7.232 -7.393 l --5.802 -7.047 l -h -f -Q - endstream endobj 1771 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 44.7773 196.5967 cm -0 0 m --0.14 0.238 -19.158 -7.77 y --16.713 -5.212 l --14.575 5.262 l --14.582 5.255 l --14.578 5.271 l --22.009 -2.413 l --22.739 -5.875 l --26.877 14.34 -27.153 14.321 v --27.429 14.304 -26.237 -3.848 y --23.912 -12.222 -23.881 -12.285 v --23.916 -12.351 l --23.896 -12.355 -23.876 -12.357 -23.858 -12.354 c --23.851 -12.366 -23.842 -12.377 -23.832 -12.389 c --23.803 -12.335 l --23.732 -12.323 -15.509 -9.51 y -0.14 -0.237 0 0 v -f -Q - endstream endobj 1772 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 21.688 183.8916 cm -0 0 m -2.103 3.146 l -0.533 1.306 l -2.776 6.797 l --0.359 1.122 l --0.605 5.619 l --0.953 0.047 l -h -f -Q - endstream endobj 1773 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 47.939 215.0645 cm -0 0 m -8.376 -11.181 l -9.138 -14.551 7.583 -16.116 v -h -f -Q - endstream endobj 1774 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 66.4028 347.583 cm -0 0 m --18.829 -2.966 l -10.076 -29.561 l -49.271 -43.147 l -42.825 -22.739 l -h -f -Q - endstream endobj 1775 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 45.3696 343.0811 cm -0 0 m --11.067 -21.641 l -28.688 -26.71 l -h -f -Q - endstream endobj 1776 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 440.8994 395.627 cm -0 0 m --10.689 1.166 l --12.86 -0.363 -13.18 -1.84 v -h -f -Q - endstream endobj 1777 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 130.8779 277.9219 cm -0 0 m --40.894 5.753 -61.643 16.964 v --82.391 28.175 -87.421 32.082 -116.921 58.01 c --115.445 51.81 l --88.875 25.535 -69.467 16.174 v --50.059 6.813 -23.144 -0.498 -2.877 -3.003 c -h -f -Q - endstream endobj 1778 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 114.3916 277.6504 cm -0 0 m -2.261 -0.191 2.09 -0.278 14.066 -2.591 c --38.512 -23.839 l -h -f -Q - endstream endobj 1779 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 25.3643 321.0557 cm -0 0 m --0.71 1.465 -10.192 9.189 y -1.893 -40.511 l -f -Q - endstream endobj 1780 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 64.0347 348.3506 cm -0 0 m --41.301 4.509 l --49.691 -1.4 -50.925 -7.104 v -h -f -Q - endstream endobj 1781 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 114.772 301.9189 cm -0 0 m --37.519 12.942 l --14.5 -16.419 l -h -f -Q - endstream endobj 1782 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 46.04 292.8184 cm -0 0 m --6.641 -20.043 l --5.406 -26.271 l -h -f -Q - endstream endobj 1783 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 80.2993 279.3115 cm -0 0 m --19.626 -20.063 l --15.746 -19.123 l -h -f -Q - endstream endobj 1784 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 102.7388 272.2998 cm -0 0 m --0.381 0.645 -51.991 -21.086 y --45.358 -14.147 l --39.553 14.275 l --39.572 14.257 l --39.562 14.298 l --59.73 -6.552 l --61.71 -15.946 l --72.941 38.914 -73.69 38.863 v --74.437 38.814 -71.203 -10.446 y --64.895 -33.17 -64.811 -33.346 v --64.903 -33.521 l --64.85 -33.531 -64.797 -33.536 -64.747 -33.529 c --64.726 -33.563 -64.704 -33.595 -64.675 -33.625 c --64.596 -33.477 l --64.404 -33.446 -42.089 -25.813 y -0.381 -0.645 0 0 v -f -Q - endstream endobj 1785 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 40.0776 237.8154 cm -0 0 m -5.707 8.541 l -1.447 3.545 l -7.533 18.448 l --0.974 3.048 l --1.64 15.251 l --2.584 0.131 l -h -f -Q - endstream endobj 1786 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 111.3179 322.416 cm -0 0 m -22.732 -30.345 l -24.801 -39.489 20.579 -43.737 v -h -f -Q - endstream endobj 1787 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 454.0293 383.6113 cm -0 0 m --9.71 3.349 l --3.752 -4.251 l -h -f -Q - endstream endobj 1788 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 122.6548 590.7227 cm -0 0 m --37.471 -5.896 l -20.051 -58.823 l -98.05 -85.864 l -85.222 -45.25 l -h -f -Q - endstream endobj 1789 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 80.7993 581.7666 cm -0 0 m --22.025 -43.067 l -57.087 -53.152 l -h -f -Q - endstream endobj 1790 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 250.9619 452.0996 cm -0 0 m --81.379 11.447 -122.671 33.755 v --163.959 56.068 -173.97 63.843 -232.677 115.439 c --229.74 103.101 l --176.867 50.816 -138.244 32.187 v --99.62 13.555 -46.057 -0.992 -5.728 -5.978 c -h -f -Q - endstream endobj 1791 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 218.1528 451.5586 cm -0 0 m -4.499 -0.383 4.16 -0.555 27.994 -5.155 c --76.639 -47.44 l -h -f -Q - endstream endobj 1792 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 40.9873 537.9385 cm -0 0 m --1.413 2.914 -20.283 18.282 y -3.768 -80.617 l -f -Q - endstream endobj 1793 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 117.9419 592.2549 cm -0 0 m --82.188 8.972 l --98.887 -2.789 -101.341 -14.138 v -h -f -Q - endstream endobj 1794 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 218.9072 499.8525 cm -0 0 m --74.661 25.758 l --28.851 -32.67 l -h -f -Q - endstream endobj 1795 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 82.1328 481.7441 cm -0 0 m --13.216 -39.889 l --10.76 -52.279 l -h -f -Q - endstream endobj 1796 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 150.3091 454.8604 cm -0 0 m --39.055 -39.92 l --31.334 -38.047 l -h -f -Q - endstream endobj 1797 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 194.9639 440.9111 cm -0 0 m --0.759 1.283 -103.465 -41.963 y --90.265 -28.15 l --78.709 28.408 l --78.749 28.367 l --78.73 28.452 l --118.865 -13.037 l --122.804 -31.736 l --145.154 77.439 -146.643 77.339 v --148.132 77.241 -141.696 -20.787 y --129.142 -66.011 -128.974 -66.361 v --129.158 -66.705 l --129.053 -66.728 -128.948 -66.735 -128.849 -66.725 c --128.807 -66.791 -128.763 -66.856 -128.703 -66.915 c --128.547 -66.621 l --128.167 -66.563 -83.758 -51.365 y -0.758 -1.283 0 0 v -f -Q - endstream endobj 1798 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 436.2422 381.2549 cm -0 0 m --1.719 -5.188 l --1.399 -6.799 l -h -f -Q - endstream endobj 1799 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 70.2671 372.2891 cm -0 0 m -11.356 16.993 l -2.879 7.05 l -14.991 36.711 l --1.938 6.063 l --3.265 30.349 l --5.144 0.257 l -h -f -Q - endstream endobj 1800 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 212.0366 540.6436 cm -0 0 m -45.237 -60.387 l -49.354 -78.582 40.95 -87.039 v -h -f -Q - endstream endobj 1801 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 236.0601 1054.4551 cm -0 0 m --75.259 -11.844 l -40.272 -118.142 l -196.93 -172.452 l -171.162 -90.884 l -h -f -Q - endstream endobj 1802 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 151.9956 1036.4688 cm -0 0 m --44.236 -86.497 l -114.657 -106.755 l -h -f -Q - endstream endobj 1803 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 493.7598 776.0391 cm -0 0 m --163.448 22.99 -246.38 67.796 v --329.306 112.608 -349.412 128.224 -467.322 231.853 c --461.422 207.072 l --355.229 102.062 -277.656 64.643 v --200.081 27.223 -92.505 -1.993 -11.507 -12.007 c -h -f -Q - endstream endobj 1804 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 427.8623 774.9521 cm -0 0 m -9.036 -0.769 8.354 -1.114 56.223 -10.354 c --153.924 -95.281 l -h -f -Q - endstream endobj 1805 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 72.0361 948.4395 cm -0 0 m --2.839 5.854 -40.738 36.722 y -7.567 -161.914 l -f -Q - endstream endobj 1806 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 226.5947 1057.5327 cm -0 0 m --165.071 18.018 l --198.609 -5.603 -203.54 -28.396 v -h -f -Q - endstream endobj 1807 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 429.3799 871.9478 cm -0 0 m --149.953 51.734 l --57.947 -65.618 l -h -f -Q - endstream endobj 1808 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 154.6733 835.5776 cm -0 0 m --26.543 -80.113 l --21.61 -105 l -h -f -Q - endstream endobj 1809 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 445.1084 377.7588 cm -0 0 m --5.08 -5.191 l --4.075 -4.948 l -h -f -Q - endstream endobj 1810 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 291.6035 781.584 cm -0 0 m --78.443 -80.179 l --62.934 -76.418 l -h -f -Q - endstream endobj 1811 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 381.2881 753.5664 cm -0 0 m --1.525 2.578 -207.802 -84.28 y --181.291 -56.54 l --158.084 57.056 l --158.162 56.976 l --158.125 57.145 l --238.734 -26.186 l --246.644 -63.739 l --291.533 155.534 -294.523 155.332 v --297.514 155.134 -284.587 -41.751 y --259.375 -132.581 -259.037 -133.283 v --259.406 -133.974 l --259.194 -134.02 -258.984 -134.037 -258.785 -134.014 c --258.7 -134.143 -258.611 -134.276 -258.493 -134.394 c --258.18 -133.804 l --257.416 -133.687 -168.224 -103.167 y -1.524 -2.577 0 0 v -f -Q - endstream endobj 1812 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 130.8423 615.7412 cm -0 0 m -22.81 34.131 l -5.783 14.161 l -30.108 73.732 l --3.891 12.177 l --6.559 60.954 l --10.331 0.518 l -h -f -Q - endstream endobj 1813 0 obj <>>>/Subtype/Form>>stream -1 1 1 rg -/GS0 gs -q 1 0 0 1 415.5801 953.8745 cm -0 0 m -90.854 -121.284 l -99.123 -157.827 82.247 -174.815 v -h -f -Q - endstream endobj 1814 0 obj <>>>/Subtype/Form>>stream -0.71 0.871 0.965 rg -/GS0 gs -q 1 0 0 1 450.916 375.9443 cm -0 0 m --0.1 0.166 -13.455 -5.457 y --11.739 -3.661 l --10.236 3.695 l --10.241 3.69 l --10.239 3.7 l --15.459 -1.697 l --15.973 -4.128 l --18.878 10.072 -19.071 10.059 v --19.266 10.046 -18.428 -2.704 y --16.795 -8.585 -16.773 -8.63 v --16.798 -8.675 l --16.784 -8.678 -16.771 -8.679 -16.758 -8.678 c --16.753 -8.687 -16.746 -8.694 -16.738 -8.703 c --16.718 -8.664 l --16.668 -8.656 -10.894 -6.68 y -0.099 -0.167 0 0 v -f -Q - endstream endobj 1901 0 obj <> endobj 1722 0 obj <> endobj 1900 0 obj <> endobj 1899 0 obj <> endobj 1898 0 obj <> endobj 1897 0 obj <> endobj 1896 0 obj <> endobj 1895 0 obj <> endobj 1894 0 obj <> endobj 1893 0 obj <> endobj 1892 0 obj <> endobj 1891 0 obj <> endobj 1890 0 obj <> endobj 1889 0 obj <> endobj 1888 0 obj <> endobj 1887 0 obj <> endobj 1886 0 obj <> endobj 1885 0 obj <> endobj 1884 0 obj <> endobj 1883 0 obj <> endobj 1882 0 obj <> endobj 1881 0 obj <> endobj 1880 0 obj <> endobj 1879 0 obj <> endobj 1878 0 obj <> endobj 1877 0 obj <> endobj 1876 0 obj <> endobj 1875 0 obj <> endobj 1874 0 obj <> endobj 1873 0 obj <> endobj 1872 0 obj <> endobj 1871 0 obj <> endobj 1870 0 obj <> endobj 1869 0 obj <> endobj 1868 0 obj <> endobj 1867 0 obj <> endobj 1866 0 obj <> endobj 1865 0 obj <> endobj 1864 0 obj <> endobj 1863 0 obj <> endobj 1862 0 obj <> endobj 1861 0 obj <> endobj 1860 0 obj <> endobj 1859 0 obj <> endobj 1858 0 obj <> endobj 1857 0 obj <> endobj 1856 0 obj <> endobj 1855 0 obj <> endobj 1854 0 obj <> endobj 1853 0 obj <> endobj 1852 0 obj <> endobj 1851 0 obj <> endobj 1850 0 obj <> endobj 1849 0 obj <> endobj 1848 0 obj <> endobj 1847 0 obj <> endobj 1846 0 obj <> endobj 1845 0 obj <> endobj 1844 0 obj <> endobj 1843 0 obj <> endobj 1842 0 obj <> endobj 1841 0 obj <> endobj 1840 0 obj <> endobj 1839 0 obj <> endobj 1838 0 obj <> endobj 1837 0 obj <> endobj 1836 0 obj <> endobj 1835 0 obj <> endobj 1834 0 obj <> endobj 1833 0 obj <> endobj 1832 0 obj <> endobj 1831 0 obj <> endobj 1830 0 obj <> endobj 1829 0 obj <> endobj 1828 0 obj <> endobj 1827 0 obj <> endobj 1826 0 obj <> endobj 1825 0 obj <> endobj 1824 0 obj <> endobj 1823 0 obj <> endobj 1822 0 obj <> endobj 1821 0 obj <> endobj 1820 0 obj <> endobj 1819 0 obj <> endobj 1818 0 obj <> endobj 1729 0 obj <> endobj 1730 0 obj <> endobj 1902 0 obj /DeviceRGB endobj 1904 0 obj <> endobj 1905 0 obj <> endobj 1906 0 obj <> endobj 1903 0 obj <> endobj 1907 0 obj <> endobj 1908 0 obj <> endobj 1710 0 obj <> endobj 1711 0 obj <> endobj 1712 0 obj <> endobj 1713 0 obj <> endobj 1714 0 obj <> endobj 1715 0 obj <> endobj 1716 0 obj <> endobj 1717 0 obj <> endobj 1923 0 obj [/View/Design] endobj 1924 0 obj <>>> endobj 1921 0 obj [/View/Design] endobj 1922 0 obj <>>> endobj 1919 0 obj [/View/Design] endobj 1920 0 obj <>>> endobj 1917 0 obj [/View/Design] endobj 1918 0 obj <>>> endobj 1915 0 obj [/View/Design] endobj 1916 0 obj <>>> endobj 1913 0 obj [/View/Design] endobj 1914 0 obj <>>> endobj 1911 0 obj [/View/Design] endobj 1912 0 obj <>>> endobj 1909 0 obj [/View/Design] endobj 1910 0 obj <>>> endobj 1723 0 obj <> endobj 1724 0 obj <> endobj 1725 0 obj <> endobj 1726 0 obj <> endobj 1727 0 obj <> endobj 1728 0 obj <> endobj 1721 0 obj <> endobj 1925 0 obj <> endobj 1926 0 obj <>stream -%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.0 %%For: (Heather Campbell) () %%Title: (refine-logo-final.ai) %%CreationDate: 10/18/10 2:36 PM %%Canvassize: 16383 %%BoundingBox: 0 -909 787 -5 %%HiResBoundingBox: 0 -909 787 -5.55371 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 -1090.667 612 0 %AI3_TemplateBox: 306.5 -396.5 306.5 -396.5 %AI3_TileBox: 18 -901.334 594 -167.3335 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 8 %AI9_OpenToView: 367.25 -671.75 16 2471 1217 18 1 0 43 183 0 0 0 1 0 0 1 0 0 1 %AI5_OpenViewLayers: 33333377 %%PageOrigin:0 -792 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 1927 0 obj <>stream -%%BoundingBox: 0 -909 787 -5 %%HiResBoundingBox: 0 -909 787 -5.55371 %AI7_Thumbnail: 112 128 8 %%BeginData: 11484 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD07FF845A305A5A857EA984AFA9FD64FF5A305A855A5A365A3036 %2F36305A5A7E7E8584A9A8FD5AFF5A3684FD05FFAFFFA9A984857E5A5A5A %305A305A305A5A857E85A8FD52FF5A3084FFA9FFA9FFA9FFA9FFFFFFAFFF %AFFFA9FFA8A97E855A5A30362F305AAFFD4FFF7E3684FFFFFFAFFFFFFFAF %FFFFFFAFFD0BFFA9FFAFAF845A3085A9FD4CFFA8365AFFA9FFAFFFA9FFAF %FFA9FFAFFFA9FFA9AFA9AFA9AFA9FFA9FFA9FFFFFFA88530305AFD4BFF7E %30FFFFFFAFFFAFFFA9FFA9AFA9AF85A985AF85A9A9FFFFFFAFFD09FF5A36 %3085FD49FF2F7EA9AFA9A985A985858485858584FD0485FFA985A9FFA9FF %A9FD07FFA9FFA85A2F5A7EFD46FFAF5A5AAF858585AF85A985AF85A985AF %85AF85AFFFFFA9AFAFFFAFFD0CFFAF5A365AA9FD3DFFAFA9FD06FF305BFD %0D85A98585A9FFAFFF8585A9FD09FFA9FFAFFFFFFF845A2F5A84FD3AFFA9 %365A5AAFFD04FF5A5AFD0685A9858585A9858585A9A9FFAFFFFFFFA9A9A9 %FD09FFAFFFFFFFAFFFFFA936365AAFFD38FF845B5A5AA9FD04FF5A5A8585 %7E858585848585858485858584FFA9FFA9FFA9FFA98585FFA9FD05FFA9FF %A9FFA9FFA9FFFFFF7E5A2F5AA8FD36FF85305A36A9FD04FFA930FD0A85A9 %858585AFFFFFAFFFFFFFAFFFAF8585FD07FFAFFFFFFFAFFD05FFAF85305A %5AFD35FFA9A9A9AFFD05FFA85A5AAF84FD0B85A9FFA9FFA9FFA9FFA9FFA9 %8585FFAFFFA9FFAFFFA9FFAFFFA9FFAFFFA9FFFFAF5A362F84A8FD31FFA9 %AFAFFF7E845AAFFFFFFF5A5AA9AFFD0A85A9FFAFFFAFFFAFFFAFFFAFFFAF %8585FFFFFFAFFFAFFFAFFFFFFFAFFFFFFFAFFFFFFFA985305A7EFD2FFFA9 %AFA9AFA85A30307EFFFFFF7E2F5A85A9848561855A85858584AFA9FFA9FF %A9FFA9FFA9FFA9FFA98584FFA9FFA9FFA9FFA9FFA9FFA9FFA9FFA9FFA9FF %AFAF5A302F85AFFD2CFFAF84A9A9A92F5A2F84FFFFFF845A5A5B85AFA9FD %0685AFFFFFAFFFAFFFAFFFAFFFAFFFAFFFA9AF85FD05FFAFFFFFFFAFFFFF %FFAFFFFFFFAFFFFFFFA985305AA9FD2BFFA9AFA9AFA95A5A5A84FFFFFFA9 %2F855A5A84AF85855B856185A9FFA9FFA9FFA9FFA9FFA9FFA9FFA9FFA985 %85AFA9FFA9FFA9FFA9FFA9FFA9FFA9FFAFFFA9FFAFFFAFA9305AFD2CFFA9 %FFAFFFA8FFA8FD05FF5A5AAF855B84AFA9856185A9FFAFFFAFFFAFFFAFFF %A9FFAFFFA9FFAFFFA9AF85AFAFFFAFFFAFFFAFFFAFFFAFFFAFFFAFFFAFFD %04FFA93085FD2AFFA9A984855A855A5A84FD04FF5A5A84A9845A7EA98485 %5A8585A985AFA9AFA9FFA9FFA9FFA9FFA9FFA9FFA98585AFA9FFA9FFA9FF %A9FFA9FFA9FFA9FFA9FFA9FFA9FFA9852FA9FD29FFA95A84FD045A2F84FD %04FFA9308585AF858585AF85AF858561FD0485A985AF85AFA9FFAFFFAFFF %AFFFFFAF85AFFFFFAFFFAFFFAFFFAFFFAFFFFFFFAFFFFFFFAFFFA98530FD %29FF85855A85FD045A7EFD04FFA8365AA985855B8585A984A985855B8561 %855B8561855BFD0485A985A985AFA98561A9A9AFA9FFA9FFA9FFA9FFA9FF %A9FFA9FFA9FFA9AFA95A30FD28FF85FD045A365A2F85FD05FF365B85A985 %855B8585AF85AF85856185858561FD078561FD0A85A9A9FFFFFFAFFFAFFF %AFFFAFFFAFFFAFFF85AFA9365AFD27FF7E5A5A5A365A365A7EFD05FF5A30 %85848561615A8585A984A985855A855B855A855B855A855B855A8561855A %856185A9AF85855AA9A9AFA9FFA9FFA9FFA9FFA9FFA9AF84FF84307EFD26 %FF7E305A305A2F5A2F84FD05FF845A85AF85855B85618585AFA9AF858561 %8561FD1085AFFFFFA9AF858585AFAFFFAFFFAFFFAFFFAFFFAFAFA9FF5A5A %A9FD25FF5A5A365A305A365A7EFD05FFA930FD0485615B855B615BA985A9 %84855B855B855B855B8561855B8561855B8585855BAFAFFFA9FFA9A98585 %85A9A9FFAFFFA9FFA9FF8585AFFF305AFD25FF5A2F5A30362F5A2F84FD05 %FFAF5A5AA985855B855B8561855BA985A9858561855B8561856185858561 %8585856185858561AFAFFFA9FFAFFFA9AF858585AFAFFFAFFFFFAF85AFFF %AF3085FD24FFA9AFA8AFA9AFA8AFAFFD06FF5A5A85855A615B615A855B61 %5A8584A984855B615A855B855A855B855A855B855A855B855AAFA9AFA9FF %A9FFA9FFA9855A8585A9A9FFA98584FFAF852FA9FD32FF85308585855B85 %618561855B85618585AF85AF85856185618561856185858561FD0685FFAF %FFAFFFAFFFAFFFAFFFA9AF858585AF8585A9FFFF8530FD32FF84365A855A %6185855B855B615B855B8584A985A984855B615B855B855B855B855B855B %855B8585FFA9FFA9AFA9FFA9AFA9FFAFAF85A985856185A9FFA95A36FD32 %FF308585855B8585615B855B855B855B8585AF85A9858561855B8561855B %8561855B858585618585FFA9FFAFFFA9FFAFFFA9FFAFFFAFAF858585AFAF %FFA95A7EFD31FF5A3085855B5A855B615A615B615A615B615A8584A984A9 %5B615B855B615A855B855A855B855A8585FFA9FFA9AFA9FFA9AFA9FFA9AF %5A85858584FFA9FF7E3084FD30FF7E5A85855B8585855B855B855B855B85 %5B855B8585AF85AF858561855B8561856185618561856185A9FFAFFFAFFF %AFFFAFFFAFAFFD0685A9FFAFFF5A5AA9FD2FFFA92F85855B5BA95B5B5B61 %5B615B615B615B615B6184A985A984855B615B855B615B855B855B855B85 %A9FFA9AFA9FFA9AFA9AF85855B8561855B85A9FFA9FF365AFD2FFFAF5A5A %855B8585855B615B615B615B855B615B855B615B8585A985AF85855B8561 %855B8561855B855B85AFFFAFFFA9FFAFFFFD04856185858561AFAFFFAFAF %3085FD2FFF5A5A5A5B5BA95A5B5B5B5A615B615A615B5B5A615B5B5A8585 %8584A984855A615B615A855B615A855BA9A9AFA9AFA9FF85615A855B855A %855B8584FFA9AFA9852FA9FD2EFF8530855B8585A95B615B855B615B855B %615B855B615B855B615B8585AF85AF85855B855B855B8561855BAFAFFFAF %FF858561858585618585856185A9FFAFFFAF5A84FD2EFF84365A615B8584 %615B5B5B615B855B615B5B5B615B615B615B615B615B8584A9858584855B %615B855B615BAFA9FF85855B855B855B855B855B855B85A9AFA9AF3085FD %2FFF305B5B6185A95B615B615B8561615B615B615B615B615B855B615B61 %5B8561A985AF85AF85855B855B6185FF85855B8561855B8561855B856185 %5BAFAFFFA95A84FD2FFF5A365B36858485365B5B5B5A615B5B5A5B5B5B5A %5B5B855A615B5B5A615B5B5A855B8584A9848584855B615A615B615A855B %615A855B855A855B6185FFA9AF2FA9FD2FFF7E5A5B6185AF85615B615B85 %5B615B615B615B615BA961615B615B855B615B855B615BFD0485AFA9AFFD %04855B855B855B8561855B8561855B85AFFFA95AA9FD2FFFA92F5B5A8585 %855A5B5B61FD055B615B5B5B8585615B615B5B5B615B5B5B615B615B615B %615B8585A984A98585848561855B855B615B855B855B85AF852FFD30FFA9 %5A5A5B85AF85615B6185855B615B615B615B8585A95B615B615B615B615B %615B615B615B855B615B85618585AF85AF85AF85A9FD04855B8561855BFF %5A7EFD31FF2F5A5A8584A95A5B36855B5B365B5B5B368584855A5B5B5B36 %5B5B5B5A5B5B5B5A615B5B5A615B5B5A615B5B5A855B855A8584A984A985 %85848585853636A8FD31FF85305B85AF85855B8585615B615B615BA985A9 %85855B615B615B615B615B855B615B615B855B615B855B615B855B615B85 %61857E85848585AFA9A95A3684FD32FF8436368584A9845B5A855A5B365B %5B8584A985855AFD045B615B5B5B855B5B5B615B5B5B615B5B5B615B5B5B %615B615A8585855A855A5B5A5A303084FD34FF305B85A985A95B85FD055B %8585A985AF85615B615B615B615B855B615B615B615B615B615B615B615B %615BFD09855A5A2F5AA9FD35FF5A3085848584615A61365B368584858485 %84855A5B365B5A5B5A855B5B365B5B5B365B5B5B365B5B615A615B858485 %858584855A5A2F365AA9A9FD36FF7E5A85AF85AF5B855B5B5B8585AF85A9 %85AF855B5B615B5B5B855B615B615B615B615B615B61618561FD09853636 %3085A9FD39FFA92F85858584855A5B3685858584A985858485365B365B5B %855A5B5B5B365B5B5B365B5B8584855B615AFD05855A362F5A84FD3CFFA9 %5A5AAF85A9845B5A8585A985A985A985A95AFD045B855B5B5B615B5B5B61 %5B8585AF85855BFD05855A5A305A7EAFFD3FFF305A84858485365B848584 %8584858485845B365B5A855A5B365B365B365B5A8584A95A615A8584855A %5A2F365A84A8FD41FF8530A985A985855AA985AF85A985AF85855A5B5B85 %FD055B615B8585AF85855BFD05855A363085A9FD44FF84305AA984A95A85 %848584A9848584A95A5B5A855A5B365B5A617EA985A984855A8584855A36 %2F5A7EFD48FF308585A985855AAF85A985A985A985855A5B5A5B5A615AA9 %85A985AF85615B855A5A305A5AA9FD4AFF5A368584858485848584858485 %8485365B365B5A858485848584855A5B5A5A2F303084A8FD4CFF7E5A84AF %85A985A985AF85AF85855A5B5A8585AF85A985AF85855A615A5A3085A9FD %4FFFA9308584A9848584A9848584855A5B5AA9848584A984A97E5B30362F %5A7EAFFD51FFA95A5AA984A98585A9A985A95A8585A984A985A985A95A5A %305A5AA9AFFD54FF5A5A84A9848584A97E8584858485848584A9845A2F30 %3084A8FD57FFA93085A9AF85FF85A985A985A985A985855A5A307F84FD5A %FFA8365AAF84AFA9858485848584855A5A2F5A7EA9FD5DFF305BA9A9A9AF %85A985A97E5A305A5AA9AFFD5FFF7E2FAFA8FFA8857E5A2F363084A8FD62 %FF845AA9FFA9855A5A3085A9FD66FF2F5A5A362F5A7EAFFD68FFA9305A7E %AFFD5FFFA9A9A8FD0BFFA9FD60FFA95A5A855A855A8584A984FFAFFD62FF %A85A84FFA9FFA9A984857E84FD045A84FD5FFF5A84FFFFFFAFFFFFFFA9FF %FFFFA9FF855A5AFD5DFFA95AAFA9AF85A984A985A985FFAFFFFFFFAF855A %7EA8FD5AFF848585A9858585AF85AFFFFFA9FD07FF845A7EFD59FFAF5AFD %0785A9FFA9AF85FFFFFFAFFFA9FFA9845A85A9FD57FF5AFD0785FFAFFFAF %FFA9FD05FFAFFFFFFF5A5A84FD56FF5A5A85855A8585AFA9FFA9FFA9AF85 %AFA9FFA9FFA9FFAFFFA8845AA9FD54FFA95A8585AF85AFFFFFAFFFAFFFFF %FF85FFFFFFAFFFFFFFAFFFFFAF5A85FD53FFA85B8585848585A9A9AFA9FF %A9FFAFFF85AFAFFFA9FFA9FFA9FFFFFF5AA9FD53FF5AA9858585A961FD04 %85A985AFA9AF85AFAFFFAFFFAFFFAFFFFFAF5AFD53FF7E5A855B8584A95B %855A855B855A85858584AF85A9A9FFA9FFA9FFA9855AFD52FF848585855B %8585AFFD0A85A9FFA9AFA9AFA9FFFFAFA9A984FD51FFA95A855B855B8585 %A984855B855B8561855B85A9FFA9FFA9A985AFA9A9A98484FD51FF5A8585 %85615B8585AF858561855B8585855B85AFFFA9FFAFAFA9AF85AFA985AFFD %50FF5A855A855B615A615B8584855B615A855B855A85A9AFA9FFA9FF8585 %85FF845AFD50FF855A8585615B855B615B8585AF858561855B8561AFAFFF %AFFFA9858585A9FF8485FD17FFAFAFFD36FFA85A5B855B5B5B615B615B85 %858584855B615B855BAFA9FFA9855B855B85A9FF5AA9FD15FFA985AF84FD %36FF5A6185855B615B615B615B615B8585AF85855B8561AFA98561855B85 %61AFFF8584FD15FFA98585AFA9FD35FF7E5A855A5B5B6136615B615A615B %5B5A858585848585855A615B615A8585FF7EA9FD16FF5A855A85A9FD34FF %845B85855B855B615B6185855B615B615B615B8585A985AF858561856185 %A985A9FD16FFA985AFFD36FFA93085845B5B5B365B85855B615B5B5B615B %5B5B615B855B8584858485855B5AFD17FFA9FD38FFAF5A85A95B855B5B85 %AF85615B615B615B615B615B615B615B8585855A5A5AFD17FFA9A9FD39FF %5A8584855A5B7E858485365B5A615B5B365B5B5B5A8561857E855A5A7EFD %18FFAF84FD39FF855AAF856185AF85AF615B5B855B615B615BFD05855A5B %7EAFFD55FF845A85855AA9848584615A61365B5A617E8585855A5A5A84A8 %FD58FF5AA9858585A985A95A855B5B5AA985855A615A7E84FD5BFF5A5A85 %848584A95A5B5A8584A95A5B305A7EAFFD5DFF7E8585A985AF858585AFA9 %855A5B5AA9FD26FFC9CFFD38FFA95AA984A9848585855A5A5A84A8FD26FF %CFA0A7A0A8FD38FF5A84AFA9A984855A7E84FD29FFC98385FFA8FD38FF5A %85A8855A5A5AAFFD2BFFCF7B855BAF84FD37FFA95A855AA9FD2FFFCF5A85 %5BA9FD38FFA9A8FD32FF8584FDA3FFA8847EA984A9A8FD68FFA985A9FFA9 %A984A984A9FD66FFA985A985AFA9FFFFFF848484FD64FF84FD0485FFAFFD %04FFA97EAFFD62FFA95A8585AFA9FFA9AFA9FFAFFF7E85A9FD61FF8585A9 %85AFA9AFA9AFAFFFFFFFA9A9FD61FF7E8585855B85618561AFA9AFA9FF84 %A9FD60FFA95A8561858585618585FFAFAFA9AF85AFFD5FFFA85A5B615B85 %84855B6185FFA9AF5BAF84FD60FF5A855B855BFD0685AF858585AF84FD5F %FF845A615B5B5B615B855A8585855B8585A9FD5FFF8485616185855B615B %615B8585855A85FD60FFAF5A855AA95A5B5A5B5B855A615A84A8FD61FF85 %85A985855B8585855A85A9FD64FF7E8584855A855A857EAFFD66FFA984AF %5A857EA9AFFD68FFA8847E84A8FD6CFFA9FD6CFFA8A9A8FD6CFF8485AFFF %84FD6BFFA95AAFA9AF84AFFD6AFF85618585FF85FD6AFF7E855B855A85FD %6AFFA984855BA9A9FD6AFFA885A8FD6AFFFF %%EndData endstream endobj 1928 0 obj <>stream -%AI12_CompressedDatax$'|?-sN.,Y4z47B.I[]ը>I#=324'U+;A;IwWa8ׯ>۟~?;0?V>O?ӟ7w^8_}ÿ~g||޼{{՛a˫1???Ɵ_^W??Oawzӻ޼pjCo~O<]}Շ߾?O~Wyo~{{5>?{~EX!#{ο?w'; 2_ZxDg2?|0M00'?Jw! - "!a)/A>W??L>~0O˯zo޽sk߽~ /߿}Eς?_e}f<_5^Ds p #CNcG~1N*^9ê ->w?w|W7а?9<_ M_|{\$x[~-,گ>yc__O޻eJ^} ;ÿ~,7??{t G2ۗ׿Vνw̰ջ?<=޾zÁ_w+xFuAaB.̟q!@^26Noq}8>2l2tS/~//wWW*wɣ_}^7n]`JOX޿}u?_ͷo_?ixlDnue7xknoʀ@-+w݇W߽ "/_㫏0? w_u_ 3N/ЮoiH>|_SiN c:s+4C)Ni<-q:M2]}s&H V-Ў/Ю~K4yh<_|ylX2.a0 Fe9.'h\r;Gcttqv R~|}8p.Ю?~p_c9B "4xx[`~DNL wq+rDxi8. 'yvWhz/Ct1B0?|ym(wi4.+;~< =A~wO 3C NJ6 `]`۝C g>;t;;4i7ϰ';7-_ G#CXqgSYe8m N]7)  6Mpe8Wg w} m NԎ.35<5BTxjxFnЮ竣u O ;0stv2>;98%rvtp5UD KIOŦF|i4z9yv.$X"m.-Ky%2QZ6Jn2˥YcXeW@x8gxPGzpHl=VA&q"1 qta9bEGbPWVL oFLiBHRh"R Q31fBپD%4ig6 t&BL4PRfibUt]~1S*t(6GLH5==gCN-,+8C}A7<|Е7yHD>x !mؽufx]%Oڧ=}jGO%6VܣK=>`7wnN>qMדej[J;6siB9"JMX5ut">h H|$H[2m2-7vt9dڹinZ[i̹}!mZwmҵӪWR7 -R"XDM3*$e Vщz*W^|zj qlAw(U'Tw,"k"E(EԊDѣ+($Hd3B癄M=<^" (,D8l{ ʲ5N L@Tg}&H> ,7>=FQ.X@cs[K;PږvZUJJ(G>OY7*kTMvS5z}cu 5hDnC)U*|4*q#di%Ω<[C5ߵoERdVR[o݋@h ɺG3tvO)3rQI8ďŎV D'|2 OV|b (kCVbGC -NP)3j*fUL3UXES%4>55xy]ם΢Ɗspq6eđ\OzX7ZJM[.m.(,i2 -ZA<'1{Ŕ"~vQL{^򺮛U00ְ-m%:u=Ca]K-c ИR^tOm=gSsk{6ڱkiS[usN[Q6J^b%o1[_kMj{\AnEn|BHpbŪP%t%GS}?AmE}=\w5 <҉V\? -c8&4g6, _yaJv:=5sI2:e`ɚ|9̴ ~ kkl[7X~ݫ_w3 Ϣ؏DZQR3I9Ҭ1~՚HYX5꟭Q07NIYxR٭W#'Kx_Kclǒnl(<b S=7m9s9Ur-VZIk?o؍)ˢ9ʒ_̊{A&ĂL 2 MPdT GS2! 2a.ƌ 3͉E"pB'Td&^@^}&dZL ,h1Oqѻ".mEl]NLFOuԻƒbm֦hՅne[aUް7 =WyDC&'3^Cc6&D&Gў %#u4FPҺTKcuQ m"+ԑV"b& -*=`)Kr͉ RTsj >-HB]pH忉sՓMFRwּKqgQ:S'<~Ip]ͤVR.].97;bjvB>',q#[rqqu{1T#f$^L=_[֍"&RmȪGA2xZeH#䞨 -8xA| @Z]!VP /Us'nR=qVg\!)ԢR-tsiÍe}eÿu|| QQcJ# HfMKJ fi4xsa:A902ll;EbiOQ56?|xamalЋ,* YGd5^22D#xt;b|#w4+IV)* Y=,'LW{b%T''bMPc '7ZEs581ȩ-^p wR# L'ҪMm+7RQ&\ (BlHLjWKJb>)J=(`bэ"X(%ƓBxGHbђ#e#v1ubp")I{R1 7h`,^^z/Y!E5KU[xHK2MqhWō~&[8T\}wjr50Ev׼g],7{uo(Ÿc@uz $ՋCG}2qի'x(\o$#n6&,o]?'f<.#kmi:5|iV(5a8^_|?ˠgO:g! -Y -O` @qMAOzx\?Umف&asld'\lpij::V)抱E+NYzݬYqJjVEC -WpxQZwח8b(wV2+"7͡(F K^xYx),ebsLh7,AL>41<M{g6MC_F&7נ0-siGNM#ىma7c2\YdZ6m6m1XcFØ{P߭rY^2CN4y>x-l?4| C2E'Y JFO8 #J)I S&$S'D𐸱`p ȇ8XdH m -/@N<A3cnK-–HBJ3bQq'gqv0l,S=|q 9?&~b<bVmlm[^= kGU!4ϒI+ʤ%IHAzc -g)Ͷlf;&r;rY۵oNuۉ@Yg(F,` MT0YF줺$6aWlOvH]^fBtXa-q -Nހrv6Y!vBNBU@:uϛxԫ.wW]mR8y~:}SZa[AZ ```-HVPs -jn׊r,\ -/2OnX2࣯V<5IDoI"WO>Uȃ_?ݔl -aS z2Zfd+rNQsm>cv}*ġ pM(ԫ[f6uCqkj0ڐx3K†!C2<_34\%3ge dc8S 3^q!s社qz ҰؼyУ9y pL4e*+T25<NbUCdAlBzWU\髱N^۩8D :ӱL淳GpjVgZYM [p^nzy}xZ ժ.WuEf=tj]Ueo5:np$@kVxMUch8IZQpjfjT]s1Wr55=rnl}q[ۼ?U|~:Th2LYYYY|2||W1888#pgZ*l"-1[lk(\*EA.ƒwBv-jΌ$A "Xk4Іml -0T<̑SM&4p5suQ3fJ'VFB &^ Μl؉ٓ F5 w. uHT':FW \.S,Q/KDוUܘY[k1Ϲbc+hĴ&BaNmcזU˛M4֛8Zo:W$ - UmlZc ̻퐛f-Vh}t?\ m.-. R~sJbZ~ؾ[I=}7$7=k\Seln~Fvoۄ,Z'UP۴/iMO|Y=C_@t_ pO N)Ϟ\PhnS|fZ)}vs/VC,gboˀ_2,WY 8k Y@@)&1` -;{ a -dto\1]Gq%J2.%\u/Oͽ`Cr~-9\M -${,X -U1uujӞ V) -d@l`enUBP+*L+)ԠJ ԎLܢԒK]ZV")hW{P_*-U{dX=zY#u5` ZYmz) -\6Ls%OA}q6}w~߃H3XZZ-b+ ] -p xX|<7>n@ # [Py.o!Zn޵k}*W+(5^m:^˻mnk~]mgR>[tor3T.=C/046pk0v t6z@q3 #tI %\FI N%6P^kO3&VmAu#8uVwl+9g]BU>sZ_ؑO\V `PߝT/K)ca":_80@[dHJ!_u|n*mP*Ee8  X0q[3N q \> 5E3E3znKW2 w^0e} -Y@Sr7?gn͞,IQmu5ǜ)92cfs5T o9Brb{.p܈BވAnC]l#㏻d)!ɉbQ9'EHlJ1\DADTNQKHkAHOvAkG "Xh,.;L35@ XH!8+RN>g>6qG=tWLE4>R-j䱧3/eĮIGRl*B)5M 781uj6nקx-AjURnSvO.ϋ?V9MTcWtT.訫Xd*Ha{hJSQlW4ԕoRcQ'Hej)3KFhήa87h8,:4\lQ]4*  Wkߖ>5߰QmlK&n9Q uոl,Hְl\e1ƪFj%qy23Q~cjlHmuvTRu/^Q"8cp7'P$TOW~!V%2!@果T6!6P9ǟ?Oas?˅< 2Q)آ٥ԠRl -yupTGevqnAb1SXH/!i>! -riSh6H6D 0/ `O_Gĕ Mn;sܦM!0AD55P -&mSbrO6 U0,UQVDY=OeoպlZa|u.q,fZ"lfU -Xd+fhxAVۉ^N^O,5`XhFbQ/zXXSL(?I[giuy )Oyt_wV];ck%InNDfKWUVhƵH -nYWR6x6?y&[yN1eX'Ga&\v35(Ni]6&dIڤ%A"T'LLyN,r8a*5 :zw%NRrZHEH]Ɇ8Sb(xmx T;߁uƩ|̶1;na)chꎗ]KI#ǝW:FTxe#͈QXf=;7F:9ZȄBT%|1ؐo ZZ4V7랃-y&DH-dc MtdFcS6HaVH ]_G꿩(?H<t$-B> -(zsj,js*MBȫ3dm%DOGv*(lu`? f7/ğ)kjhgi\mZ0̞_ftGXy[xPŅl@ĕh(Pd4fI2K1mZӡup=~eLi/o^&iy& W<8058nJ(-Qi#>(`8զ`tk+AIRZDU7ѭ82o$Wnu.is5i,vf.I>ʥP([Hk~bs#oJDqHJ$c?Q~iNvꩠY8@w\rf6PրX誂kYJ59%3pswBxԉ :)c؟ Վ?iDZF -%$pނ-@JnM5u(eFeS&Gy+l1qN~H c&&@R'Ov6L@ n#Ne_@bʪ*LlSDGMG(1(B+ǨPXx -fvh~Z D ASY w}1 kR-hDnJQ⤹9+kXA6>W4>&1 7 -/1 Kt&7@kE*ܽu\ A!;¹9(8C?t kwUP킳C䡮^qRˎ'2l6g$ʏ)vd{4/r<}y7eaEP=1|3Rq5V筈dZv>6n@FE1?ؔ|1 yK\ aڅxHдr^_"ǂ*J -NèB`hڬ5NFcjʡb%0AW;Yw`=W,Xjy_&IDѱGWj:ђ>b&RoPi5YC4 wyI@6){^mϬ/lޥv*pQ:sN!Q6MUA1&̩6a!nlyVXۋʱ$R<±B -&4Y"p+#cʷ}cI -q'L=JLW fLa!Hg@ڠ!!82`Rר݇4L~CN{tj."B852gVNN(ɚ)$əN@j(((JBoMIjKU\jrE-$bɘ %Sy?]~`ws)\~'a.QfI'EĜHi&̷^ӳm*dRUVVRE-k9яU;D*gU -&>K:t*eQYC;U&*6]3u֛$+ֱqix7r -KxҸCmx-/߯f˫xX[=Ykk2 Eoo'Qf+2C$69O9D$XZ{iAwq?CBʋǹ6.KrXc$bR -h%~5TJ޳ d:;TiKyQQd{#D)$jEP!embdG Ô F9q $A*'nڮl5D]L(bNYodd7p4 FH4P.ȧqđbZ Km[)nSyx= Q8HPƚGCqUjوŐ;]ѹ::'$gaX|XHg٨IuF8U q|L+S=bn۪YHxc52&<cX-Xh 2ٲ|_˪A2>Ey5ƒv]F <*3ag8uʍfe|<<18\Kp#s前e;I4.< r\iSe%<[J:=y8EmlsS$ܛ^>E69oZ?w,݆SynT uꚧ&4Ʌ :G%qe b?T%CA~\΋1N DoB*iiZ*(oqT -d8IaF"hJ!48;,n#'=罯4YKm1ɶщ+zF.3r+kKZz_#GbGqv_1ё[7.㱇-'X QH-:;uKF"?`007al!maD=4(O?GҎnCX -5vB+Mrfy?dzfzL vhV$T_6(փifFkBIJSuH䊉9+-ylYE *t26p,o{is5Qʹv~(֊Sȹw -lFxysxN&d}nsN{ .ܓUP[Xא䷖,B?@n[z9&mq&׀"ΙSLɑFĎayXi&׈/{ٗF~~F~u6REcNa\i""?Ob5rF0Fβ<t/ yrUi"% /H!sQL7Qk"J<Խ E#ȵL| xKkڒ#^#+;j#hzSp[NLpgFUyl%IFpF#$+qzVޓ%4ʙܜ -3.Nį8UD ~?Y3@ Eqz|6=FniaՄw kBՕWʾfc3BxVl J2*[rı$XM+ϵV-O5@7Ew7c;1\qkssmaCă>Z:JIQk}[E_:SyV#VE3iQou:T**h1L}(Ew^J<8JG ';cbsC\I6K9v*+uT:E(nvts7'6E_';p*"}Ʈ^-KT:b+<4+wVˀZE_M8C5-A?*h?Vcn돟6a^>RDy6.ܹOϛ)/_`29Gv4Σ;dGة8hM~4$蓤^LWmUpy#3ܺjaWߔjjӭX]T]gRۜݶue#֫ [s]~uB44,kfbs|#E/Q.tzP/eb{ DoBkV 0[K+ۧtM ?bM-VQ Lv7vLȾJGGmO ͆W[BR !]ֺ)qGMTV%^?wmiMʢ6MkFy5_RBН,KPM0UKqTTLvR͑y1 5R@15qT0Jr7JBS -*:U *BBI?a|uL_U\5JUUW - j=AL4Xur) -I:pTu;Ki]D5*Fԣ=K}dɳjM[JM3U)($N*xm{h<q6I7WlکiǦlThkS*J -bHtR!wG.C6M]7ڥmtys2=<l9?],@\xe vj -\.eg%|yjEAފ\i*i[x v)W_ռժiH+S P)Yt3b6ǖۗvZ!uER25iNj1$B#&NDY8K[CJ\19H:â$︆ \ -8 -g`)6vW;]~]CtOsNmsRi_M:NЧ@IIDDMZJ1rCוduOۦuxE&mc,m"KcAɬCd-ewYHq8.0LyFhxh]wM\ ˘5shbl6a^hx278O Io ۶ R^a,6U05H*uuѶ%}PIV^MwPsf#"@ҺJ2h~k"; RW">tEl26H gxng#=~X-bCtRo|*T繖(wET{n,G*5.Jg4Zm6E6?vzьVa֟J+}ԽXbyvG$u&ɐ">q".t3}[%$ [f(Uc86QZT_nBjsl -`,:KXn%DTۨZNB -Qe^U+z"ygo8.=உ֦hUXJNx v~4炖7n+jօucWkiARS=M25,"j%1kX"b[++0R^ kV c1]xqw{UYYM"%FBGFTW!,6&Vsc*&3df,qLVPu'Mm^)M-DҎ-tR}iRA0-F!%sDu2xNs,ʥ5l7AmBM`q66!Gc#j &6$8BIڲ, -pYb#@UM  -D90p6`gjlg:v_`H~HS[mA x>`*sXIEQMh`D*}fgFz&ls/Iq(q=0g΁U 8YVbs!_UM늦k h[:DQCXAuVi#+kJN&R뤴~XRj -~ŵo5Y[T. -?5c@MRf^ŀ΢Q W2MhQm+4栳dԽu&dnxwzzNGY'S| -6j4 -(u:OpʔG}pcmbP˜LQraGt_g -Vhlu~"dT-`Ǎ }9 Wd3l2yH鞨%L$P;c!PH-Bcsj\q ܲ,ŭ+ TeLiq(=Ԅ!;wI;$KҭV_ OJ;BKtI?Q-MޠVs: -jmiQkl5ΙEhI9ui?z + t9G(m%J+?IE6AP"*_tҹUdD:Jc-p)[a?n/+2\ Ԧ+_I&:TY)|8KiXy e`*3Yɦax,9Rǭ<.E=AEüT+긴gp6=M^*t&;cN͚oj%#voW}g72ۯ $CN_z 5ԚKi.Xq*8O3خ,xO_z-8 XLϮA|R,@33M$͚szDobYhq})؇S)F>W "U0J϶I[[Aen`I1eA793{U0lQc)Hc2=?6aOb*e'kO Ӈ޾>:^Q} @몟y*\e" k*-:TͮOHi|'2%_ -`əʚ-G%g Bޕǒ"a+6Jv xrIvGrZ? VW\$KQrH83&Ϯ5`l8 VT[vى%I⦚I[ MkFOOonU}{VKŹJ \M՘f|2_6JeBY,TzjWKbSd@] hέrWM @.t| [#ᜁ [(SUH\]WmVn}{,QAQ-0d2\ c7{-Y+Xqoc[#qV׹{qy⼽9[7P'|p[3G9P4|rʑ _Ҧ w0kðj;i`=ﵭ>n1Ʀ0`Q#Q /hzB{Լl17?x8}>ӛ߿{럹GV R\,&\.tiNwtsa:LTNq9I~i#p؇)1&ܡapC~߿]濤pA?0ݻW߿{x8y/ e#MjzRܹ[%Խ - d܂E09[ݐK6m٢JR 9gs!0 7~6I",TF`U42.NJDD'a>Ϙqv*+c3J!$ )YSv0 FFi~l.%wC&ZdbVܨb-Sw),~:EHbEJ6VlwdU<+׌]4s7Jf"gDzr 5Q͈~nCGrzHE,dRԉ -ldZ@-N".S2ɰ2x4L zG51Q LP; o(DQy݆m?ȇ6/i'k)jna%IKv m" ;jTr&{$4qfԛ}oro'Vg׶WWpX#m?}خ1c֎Bb?:# ##: /‘5kmSş ~YS+xn جRkS׊3 hVsݟJ;ww`{w1[ϒY"+@zkc~MMRQcjMM)n߻ͼ5Gj6͙(6CQKwN~8\ŭnμXXhO`HG_(r5K1QJ®d(p- -'K+j|+Nː2G1<{MܚL *it_2cw`ClGFyqkߘ/3R3bkJx -L^Ijh+m3g4">-SJ`RGaE| )ý*$k8|MqNָZQ WJԪS!UkW_S~Ck3cZ= nbh^N̞'f ' hە0yw! };6̧.$.{ۗMd E6[% BOg4)1/D Ҏ:9LND2j>šJ> (&rqr -;xUNP/̞3g?QE鎂/f+HV3ߔܧ&`{fʚM+N pEm gp߆b|-c:Ѹy-h>|y3Bk)P}&M.Z׋4BĊ:_ր,&^9ɥG%ZMvՠ&bT&9ɚZPkS1K]SFW*Ԛ{`o,rW[\n) -ՕHgn‚ -EXTuY7lMfh捶lN g[58lV^/ ȦãqS.&eդxԢ>b_ 8ݺMmȐ ^ i}NE$d~.S,UXUɯ(vM L4T})%>Bc} CK[!,Uv_7!-hDK!z`k(š?wu7S_A~nF}, $NKKŻq#LU פj2lU6Bu-n-2(1Crk_lfhጓ뎯T07k__>^5gv}^vۻkUV_O*mu攮Ϩ9?gmr˝3#|Y:/:z?;ʻ9&9<P眗0D+5>aa?3z`=!y\ct#%CC3|wf.)?eā&s Ä%=~%`7sK¢ksS94=2٩LC -Lr|X|HanTuJEǐv7<  {ҥ瞐-2Iw"rCP/_(3Cj\T*S&`.\ӂ2e-70y3͕ ws's)w)s刺9?,Xާ=CJ1|ۜjA`6gt{L钙!d]bPPm雀`]ܖ1eǗͩ>=9:,=}SjL?l@rc:OcTќ>t [GE0r͡L_3Cj\T*S&sHUw|ݜ''GГױOgX>#7PuJ̥ܥ̵>9jރ{=KNNBNV9}:2^/Sd&ei7z$92N"aD6@3txpKd[)&9AbsfB(s3BL!NaKX9D*!`2ZJŴt9JRr,rx63U-"j0+N$x; -*]W-$f]3z3RZNG8+ϲprQݕ{îHWYgdVY' vF:#]OѿtJ:%:']^¶sU9aqWVArӏqi~OQ|R3@%xZK *3An7|k;C53iD`'oBYȪ$Z^+yPaaӓ~,#`tfTv_FMF_4"Np{W#U/ ]d;~Zj;2c.@ -_6tu _o[%EBFVt EKwQ8dv_t S\jvzew?R6~1nW@b*Sn6渚~GOps i5NwA6$%~`p9,P:Nww$AyWf.u2]; vØn;3Nw{E'Ϋf:NwwQKV梵]֏ǜ׏v7AvF1h@j?.Z ;ELJi r]? uuQrbf:NwwNFz@AvEooXi#/bϽ7 -E햯ݏҟ0 - ]"h)́/ ;xP÷@^Vs@مGmnRI 4{ t1DD uƉ |@HI81-"IíJ<hzH{OIf0NH@d1%wVD4lSIeK-xKhl@3BaʸpɁVc<ɻnKګD",xi5r/bl|/7C|b`X[nS}2 3H#N,X.ػkt;%<t.@Db+YH'sv22>ƂIoD-6oHr)P>sEgLӨ׋>l*ˣ+kUt\ hN0U/x1 1F鄇-:z Y!"O')tr)G_OP -Uvkڙ,Exh=C!."0x&v@w K{%iLP'ia:4k<3  ߟ` z.3N KcC֋MSSD*Q;Zs\<22bl0x! fʁD,,O6X^`J dqF~3$_Q?+>GD:c J{B̝1Ilg0C]qX#.cXpSK:`(aFvjDLք$,UȊsW 0C_`c:A:ɸpiOgܐ U78 -G -i,Y -XL5ĨȌޫ$V1A:ANڛgQ4vp3{P`ih\B*pjg16 I;BEH"y"{oSH⢷[Ovsg KHTj@~=%Nˍ(*Q-އb&Y%Q争E,A'Qe`W!~.B# lYyA7,'8tEzDzhs*u|{ ţ$Lذ3RSM6C錸SX$!f[l2;w¸G'G۱Oe-,S11hP;'V('|D#FC~gh'h^~jB(dBl΃#,Ix^ `ofZ 6>L9DϋJgBgd:॓؎N*Ѳ͝:L<96Qlk*d_UI -ԂQIXG3 BIg)ədR kB -AIDCF똽  -oo@̕NXdH @*n$Ox=#8GuFއ<j*ӌ!F+,2R>K@ht-[{"T=v%#g :{&6aEf@HeqAdä)<\jK%4I]?ђH[^ibfJU=49'٥Owf9Ѝ܍IF! im ^<W-5畔S)$q.ԯwn`aO_4B*zvcg!Ba~wfc'%q\BN.ׄr=HPX\kY{„MqFE4 $}/ZhOvQzΝs9WнqyY5^:')3bgBg X_tȴq~m{.oC*<\\5b6p#VaUbs"Bw.vJ''TJR b<ƀ[3/'2;tsAaby(ʞ*hRph -PjM #ɣ}U`{Vv4xZr 1 ,zÄaXZfg9':Fj^D Fԟyʹq]H< g1-A@;<%Eh`Z# 3oF6Cw:G?gEh=$ՁLLpT֋x:qh&_14d2tq~+ kQIF?m߰lQͬSz: S751݄44.NJB;Q~j8iV =^]Ac5M 7ɵF|-Ϗ"Lp4y5詽%V,NE~6 ԰9dݙbgOկKf58:㠎bbz}xwؖeIe1DX,wR%Z##/ $ξX DV4,):gpiڥ{I*l@WZG MnUMdE&N Ic>Pωfr -:pOc:ҐN 7K&J)Q;Ţ c6JA?Ũ>bQoS*zxYRLbǍߕANR:Y(a;Lnd,ќY%w&6Pd!\0|է`BeFY5 -V#:r\6iOΪYDD< NP_^ઌ8s#=. i,1TRg. VʐŘHۈ%@& -r`f ^lU:RP5EmPhEx;~) _XBFn^!>QLDBMSr($DUmǺ9{y1 u>\J _hÆJ$ PB%33"C MkժUHB]-w)pfش/~ނ1 wrLZX[$Ú]&݈TҌ"Ḧd$;|qWgaAJя])ݚznU٪vp!C gPhUkT,Xnp4*_sJ$# s1!p]+- ¨mrG"u%?V(d5C.;XĜ?.Zs `fܛ\\=^3ؒn8͘1 X5|ayPvF̈v`7 -+ FAؾ2d` ̮ lg]^ܞa#N+< -a0`o0_Qc64j8aR)]< o,GB*V38j8;:щFv:ɏx1hێdָQx(67MvŵB&b{\mhwepӿ9b0M"ʾ f#;O #D:㖫ބƟ^~~Uv8vGu7۴"mIUi]{9y\ Cm؅Mf8!~cceq/GMZs$* '=v.`S|Эk窐~qIV%!|,1I"l͟`2A֪IndZ\bA7ux QXa}g#!?CAL,6y3@V~,M`Zhh8 ̓G3S2liQܞ a𭊠8`6X -8cD.JoZdE`~sgXq/;LK#\i y~, (L98z%; !H&P۶5+Kl :3},x#:8(n6kPznn#Yt&-NS`. lLZplm VCZIUc4?0b۸=Űu[kb(u@ȅ7+L8?Bifp5>!ɨOgNQb vSGlфJv/GEa1X|bՓUpxnv~vJQWS&H/П:#U5niv[URG RerҴ`}Fs$q{-gGIdQf:w٘sknb] G -ROОԗaHJyK„b3 Uh( !c3Ч/ -og]̈tY"lE q`qco(T>teGaBr(7')^fA,js?]2i)e{ 7rWKua}r(,.b8ߦ68f_""zįXK@oqt$Cl_&gB)lʽ;ğobQ\g!Q:b^ViōqY̛YkW$Ƶ*z -ctحZ L/fqf"3wKvIe"3ژʜ|3Ǩ _ >6QD,P -Tw1U1)(Y8ig#6H yVt1΢DƲn QoCZ%y5Ok4} *>qmЏtfd<\ eHGI\fXm -OP{AQr{-{1!U%X\L-wZqo 8C,>Pd/Qf@kev5f e^, mTx=,Q[FtϢhi ݧk7dұA$y%Rt`޸f^?,E]y4Rd^L̠%PSg,,o'V1 W\[evA`a֬10a+^#%X  5[@J&,2)Syl-Ywx;Ga>e1HimnLk-w2c;+Vk}WwO+cȊ wڧn^Z+İpu >&:jkĥp0Wa,N( 5Hxzέp$]ڨX]umAc zCxР f~T]&Ѱ^G?'<0AYбPRP'** !-&$gnb#B6}"+ Nk_dFNkjF*F-؂/BG{FpF㞏Ͼ(qoZCd0H1"KׁW`E Z)w|]УË~ ;C$Aj'U6B }2pn0(Q@MG֐}R$0[*ˀm 1fMN)⾦LwU۷P͕wedR߿sˆp("-E# mb_ fq8pV߉&~.3 f3;9aWz{,6DPoV:bi(S*M.CU25'W3jˠӪ:S-|R?bx(M"uĞ45m:b.Kc1@݂)0"Ǵ}=`P-|;"w}]yen(k@yR(mG9bzޕ͑s C$vKDX#@1F׶PN!B9t(K‡-[Qmx)ڻDuR*)eӱsfR5ଢmcHj|xL[+X-[lh(1dՎTȯjVhXVnl<1xg3M/,YBuaN lVEF{,{Ԡu ((%!<{ZC1z2d*#_\6ЊgZJj -eW -1ʖD-'vcL^1F&vﴜAi*nj[$efSw&m&b+{/6*ϋ4sޙ=[0#Y5)a-H~6K 4V Uy%_bJZl٩>x/^IJH1Aڪo+ D1ғ IA>c'NOq؜S -Φ{# XC嗼}[OQuכIn}c=d~tqsP!HQlB& SȰwLhn>O^qGWiNZM~}G|nj$~z|lzO$~z؟a=)Iз(CiO} _ );iLwN%3= * 8/ ~y;Ӗih9,V~2@fAlΤJ4<,c?Ը6p^GAA^*CPK6b P_ڣN=(A^*C[Pu> -'ifo85a?}'a~^vV+}VTG ?5H<Ԣ=tEG"+'K̙;YY{a[0BG!bYl,JQ"-SJ\u,".JOI+ aYL*: BpeV2P8+qyjTsvuGYm-P3Ǻ- *GGjThcfٞ {y>ȥV=Җsh0pM DmH:N'c-=W=yF N*Ne&ږm1H'(I愰0&rjteI4"3z8^*H@1޵N>Qm(jç |rv+; *g3v:!c,oڔO{u$'f8l(Cq=6ΒM K1f좧e`#L yy߽)wdF{1>KÐh.mUh~W -J;Sx( <wpW픩*{m^9:ld~jqX%s઼5!@)ol%m1.sQ6wt}^NYu:g3 qGH~|O# !ؼEI XhN, |ʢ/cdN˛vw?i\e1p2J=RhRǁvV ](;6C6f^ef̞fӯx?bfqҙ@&9nFF}$d2-(A,]LE)^ijWj0~ٮp~y>FkTFtgY ~xzFqҒVu)qր{|ښbFt 1ۍ~drs|g,=Z')r6=M -g,Ol/W=u{{gQǢuv ˵T`QB_:CUa+i`LjS2dmEbͫ 뷌@W\$HZm6.eDDnG [b(`Ϲu7p9e0޹"g#@a1B p!ѯV懇fPWĩ \%Fg&Q_x5ckamZ1]'}w1=Aj\SiMsX2nU2|?ςӛrgI2a *"}~bPG47H'xDx] Y8O3ZoͿ????~-o8o?o/zXOKB5z8 -Vg~>KSᅮ~i2%anu"ɋRI ;>å=\"[``=]B p sY\8~Dd#Pqe 4>T!UKѢ/cX0}!_u UrSƨ91].{1ÂVCybypUkX8TD[nǯ2'WgX!6E7!CBoQQK/W\Zbonkej1l^s`y&-d(|_"akO!40p?!FٜAgb8ޭHBo\]џ_yi E}C¨ Gbr3M.vnsLS-Aoݤp o0fҸ{/h}9+om]b E:A'{mgB,pvP*L&i.wcF˥1&e]a]M1Y`[,Yɍّ/<K!~yTs -϶Y/LŐ^g͢mklZ,ie\]8_ħf%4a:k`BvHsZHVKA{#Boq6xܰAz?cݖ*0i/qG~bC{-^ y -EEWZnG戮rHK f<{k)9 ܆RPqo[Ʌ}W̸'meL8#bG :"A%:67r/vaYA qǯw§3>Nq@3׊jFitfm*~zgf (s|JGb -Foͅ5N(– ~IG|УLOcB-Jm⿍n401O Fl٘QxboR q +?p[Xܮqa7 *ǍkĠpچ:%U;ҁy! ïuceɯtcvV2 T51ζYeFۋ 5X"„)xaǽ|~y>m;2_C'ZøxNCd|3Nꔞ!ll {TX J(ޙyw d:Nr, -Fl<˜ט?734&_cyӕf~YiO -Ϛb1GM ΩI -,YoT 3XP -U-!v7ΘXy+lI`ZiA,)Ns $5_Y%_(lpb=DbřGyڒ -dAXK -?jeWRڝZ+N/f.K1[$+pz58$`Q -8hbs\֮GeP渐3XfcK]C#0iXⷠF_,YքkȜ [^3]`,Ԅ#D߆V,$qZxW7wVjsAX⵱ܗqVL\xEήw`mۖ 7xKs^,";8UuzhNJ.uinsiR T%\g<#TŶL8OdLMogcij<0c㉏Kp5,Eacl׆BYףuƮ>+o ిW:[7r"j\o·L6 -FVIXж&(zwj )Y*j[ ! -&ppB߻-2jqZz-a_{ՈϺޖ.A K)acmFf8!,Xq7?v_ *8[0‰=mR~iʍq 93})+7!Q!s0P L6*IJsop[kzo>´`t0ƀon{zc2:=f)Oᘲ35bm~@C d̈́r#B_r=,[¥( „nsV@he9 gD4B\怆"tIeSw={혫AٿA; pp?Ǟ!,=6 Ď*!V1?΀N yRycY `BaGT&8ձ^3`+;ڝ-5ҔNc"cu.x=l(XhwcB'Y m͞qaCx׳*ToKyn\i^ާtqtRNhDS!_gix*MO鸙{'-vXAp0Uja4u-ofacEZFyl7f1l%8䂌h"Zh  1A?A+K=q[ӡ;FʔM2O"9Pǘ)>|2B(?G9َL<͸,)4[T'c)b6Qabg\,6SR?YG.0 -</i$itK_ .}XVᵻ> N#= 3!&D榗QmZv5nЬYZ|VM'TĈUԂ}B' R3~Wʼn5Ugbث&Yv&- ‰p `՗NHxO;\Owؼ&Na5 -C&^ ui0Y&"ď?*"5؉4qNb7&N5Ff6!V4aNL6~k C+EqoI!j2 i"%}[|[! FӄSAưk9)LS֋ h*Hx:@" ]a6aOİ2o9(0 xr G&s@3wfy\E @$l6hb=d73qFH'2Y9 \r<3{r6mPo~h6;\|$ӎB8SBDЩmM1n>|<1lp -nD>Ʀp! {1/q `(8'a_rphQ։*ځ X6c|>zlM>XDɪ'/&|`7$w9gg;. y > f,r -gB`צ@̀=mfp22Y'z[9_:̆SsPI]ʍG4wiڣ/{!|'\cH6ƦMQ>;;f$}U~AV H((m:ظ 8חmj >'gyXYF ȹhx[8C3]trP7}fpw7!Bx;k=wrCƵ%q! -$^WIJ_3(P+h1dAId) Oi׊bKiDG\ ,BIuSiOPar{^4.#rȓ^ϯ -$X"mxZgd}/7YĜŽ.ؿo$ 蓧>n˳8@ş#(5@B'BMPǥ#V6YOa';PAzB$p \hҴ3EXohQ?oVڀBVg6ɀ4 ")h:&* ub\"]}@-\JsP>1v&cn|AB7 Eť7@]".k~ݛ)wd~ BEh"%74sAxVx7ZUnypb9rs3ф.[Ɗg+mfVq\%~Es;FB:SX~AZ/C[Kr;c]{ -ik41e{k}?Θ}), afIA6Vxb5gp -F, !m^(۔N o?I,V?+*8( N8 *`h-,.paRElք׆cAy&H7S> 7"&y}X]dz%>"62ь}T9oz@i#ah^d\oxu'tȅ`lhܸoW%8s3ݪjW)܋/ ApGX S*ۼ*K,^ .ѹd F^tD-raYyЪw!hhQQ͋=kIjQ[JHZîQ76#v.ń)hDuLWP7=D=ьPp~[h15=`]gy SAzܔ>O{XX6XCfJRxha߄Ml-xS_7A@\v# -:.rV-.&&9%pt* 0K",?^[*3:p$^'U0vwY` u @^wשj3KF*loqsIhL`J0:x; .@f k$ -XƠթ!-^4Qc$]W0*6vU|Z+M(+T Wp)x?BR ,\ #Uޡ~Ot iV>qNAhax+WLGхLi9@L)9]]3/JH-Y5Wuo-X:{]$ȭ?y⺵a ׽=F!A͕'ĬW -P/ { G0d*_:Cpbb[4pzԸ=0!)[*;[ -- *mc!MězT jW\$g @b$!\C<la. 0]8Q -B*4ƭxVuޤ|%Z@1 b7nC'Jh-eu}7c?%/R¬@Ż'}1Xf8qSB`9$R*q8.tf9A#2PDž}VL~(ǴC:?jY w`'Tݎ YOqϻ"8}tө|OϗJ J-4,#}̖Ҋ"X/_. )P^Z: |*V)l -tHa51Ĉ5wX/X#$j Fla'5_i"1C.BuH9Y -2^OnXXYId;I%!?l@fqr:F R#zeNk`; $ڲsDXXN3~`a[[*!-[,EU@0[싙[pPXe#edvL(ZC6cw)mxc}, -ƏcqQ:e -%IBx^ + ^=ޞ'J+`RtyW.]1:~:d$3: M$NаR擣*6N 13_#TvJBw(Sm aZ? AtbǽʵAV_u3Kv }uim:4f ^ḽ@:BL¾MH 7֖$}gq sIDg%کq9uVu* yO-؛mTGE QP7MH9& w8-ANOKd17o !,!,1c`r ĔxFvwH >"[>v[Xu IhЗ+[firô~󽬖מ*AoF2!دSʏ%f[eOC,NW'/ -ᬊ;(~eEy|UXhڰ؁jnknu61=-JMdT4%--_d}?Esbe0_QyY/p2 YmcWE5!*Al(L|r9pٯfk)vs«!q%gPr1~I\a!h\K(4l#07vh~5a'᮰&;-{zAg\b }n!ظROIDuhV -Oᗹ=9rjW"azV[Q/s^SC@g'2YT3pL)L E] ժѸZ'p *ڹ?֢HmTZ&/9%"tEj 3ssgĀڧI,(:j T+޷]^%ܘKCJύ*#2ZgeqiiPtN>q|uѵ6O(Asb<HFx3+_}DA2GIfr#;S0bB݇Vc̰[̶k[D_VZ c+º.WDEjܼC_sgd}xK,gYCQ[kTz)p% -~)0gnBYXqk:뀪V2n(.^K'~ܶ7#? 5]'7sMc@MX϶+11la-ϳߨ_8`A˼ZMh kiހ&29,Rv?b!áeD햃7ےo)6Z s!ͱ\dl6&>N[yb-.OU%xy1oXɣ&2[h+BJm]tCx /ieL,TQA8$ pCv*h^H`+l2xY]%đ^45(d'7` h!3Xf.u+E3+qTCMh| NA#b{dz:>gr -~Ъ'kKs6i#B=P a0;;3Dy5&J ,iD13zT[ksh1ݙ&Y<} -F~"'͕GGk ؍eefܲqu2o`"*&`6α{n\jothNCFAQ^3XhT巅^sC@Pׇu1`m(<ofcI{0'U`ֹ21obzi Z^M/v`b7P&قvAƘ@F7b$^vV~,!(bP3o=dP%F>ӌ+߱I[׍#0]`]]-MFz^I{g9ŽرŁQ}Tye!j4^v°ߐ$r~m202U59|w8Ƞ,Hyؼr}^v]NYYl|me-a1⥲eBJUz]N+$! x u]YIJڍtqd6IHvwZ#mI;ð<3ܔTc,ҽ|`lE -fO.OJ8gXv8LKѭQykIޫg>`S@  _B`RG}L؈`~qL<- -mF4{!:Z}V5"mX9(c+rR6@Æl)+ɏV -*b&#m<>&a7Q( օY$Pt5B xtW47:~ٙ>Kce\aw..V9hqkim{ $c,.mdHۜZ/|=_1*vY(91exȅ; ̚40ԻO,:=|r6s5^{m_s~{GŸ}S8kįAAs"#N16'4h>~p~¦. 7\6:m_ߜLMҼD[银Ѩ}PCI= }wTfVqnU\0aЙ*=[iW-.#Du IM"ۖIgȈM <]]k+sm'dp|3Rugx-f0K`EVLfzt3Ph%Յ#܏6H?ۆ+<# L`Ũlg7#^Cn>x'īq45=qaZKc_"+0f_R 7T%T܄Fe)PWQ$(j-S<= &qU2ߊs*\ #TdKwT=8K&pjGػ -Dmnb3/:m1l<~Z >ꉟhܭyHW6˟@oZ᭓@WxuynK5qt".AwIHd$r0v]S&qE0h溾q•l#j4|f -BH:wkSe]xEJ VB\fGm%uLC08Kq$2tZt.cܢ.'25<abLeC8(ntO7\gߠ ijmIe2t[ftHdO8:1TcY !&٧Y펢tVznAI0EÔr҄F;MM3(bZWN@,bBeQ5'ͰlSZ9(BwwyECےҎ,׵ԛw!ޒ:%51vl!On51<d!1AT-!,XmٕAT;tz:Ma<8L@-8B Y1ȘBX&:.J]@|B{kE=:[WVA7dy􃽶// -۩p'0KDCxD:%1R8Ly絑nU3V=rz<_̒f+j -3|5`[CmZƣWʥ+bK؂+އiQ+U$+-E\+<ԿK̯Yo1 a)=L1 s4AR?[r9Զ+Cxt(1:P-Eh֪RG?G-Ur:x`$H ]^Wey4IZ(fFXNx >>obQZ~2H22==˲Y3#Eň[mL+,aEXC:>94bcxuY ˼h n-"K!Ɏ<|tB֟T]Sqbb*:Q+|ŲD[f~Q>G\P~߅16uQE-l*CF?>@NB'ǠPm̻&j[P"$lUirf %"%wԚ9;r۟C5X&Bo yNoob̷xIpi=ZD8FfG+0LFcV>ÑeMmQkw/UbԱ|brRY3*e=mWʜJW]3qb t '_M[M_oBkdPa)tJ#XyO?YҘ)s"#T:kjNT2)g;[VνE z^!q+59"V);=}K+l)OH'?>}\^ﮂ=J+Qk_Q'TfKA$%Y<̃H -U!$wP3&/EԷ)0gmɴ.G܁DZ~bb;Q#*bUT/(7QRD;od4-8ԣeǞ`x55*T;veF)lldG? 6WӀL3YzF_9){^\m`$'o{~SfBipe6O(e.{Rp 6rq{e\$L},8wVnȄSK'Yha!O7{ ظB]?Zda)^Yj=!y010Xdn;=v)eiUvQT2jZbP# =[xQ ,76ՖF#4?#thxlkRD$Vc([(>G,̳W[cNm-׾K'$QZO`_5xmANBҨ%_S -f3“C -,1Y]@8Fkb#ͺ/#̥Խ&-ul&FIT[B=tY(n%~BL`il\A@ab}h9BqzU!.;;6v"0%Uo`Y*% O3$h/ tPͨю:s6[Kf&ϑb!O 7:7~?~ :f=)6D/sT/B|P ?ߊ(4.&{< -("^To sx -. W3+}on\M̎B]eUXDֻ"B1[>SC}Iw!茒7hy -LlR}` )i,XXq:Vys: azJEuH38.ulW6Cf~=(!N@Aşj#7u;W2Dᒇξ]eJGU^Lဍ }kէu8y89gy?uRqVw+U']%tO?ZfÂm6g=:dǨ7{VeUUf[a>bf -RY(~Kg5:xJ_.U&.߷قs'_5HD=ҬV%ؔ^^-4Ɏ>1 Kg0wVŰ= -rNmMV`~eL%cL!<0b"pRR[`E$NPvC8ԧ]j=ճ::\+Tq{(2q0Zc,\*D~gUk4G㗌S FtcS`E]$CawċP}|+J,C5B -Q˿gn 1hߧΝ^J9RM -n`{C>EFVf3EVU\.I7?ɔ;L˂7b7W{ƟXfAW에9ual[!$7T%*?ݎv8O-@z,A41R{:Im% #7%hׄqZe.<;G {\f֏c_+s^;{>8Ԣ63?Mgub;Ch&bhD(H/vE ļ Qk?H勷s0;۴sUeӀ+` 须C[IޞoKјL>F_0Tٳ(f]$7HL_N&ލR6q$JgB6@b%fTET -3NRv#P,0!bdWl1Tgo͏P+y/8- -`"MEHdz`$!52q9N'F6vqqz~fUC*{Tl(|NةÈkp:Z>{ŐѼY+YÉ^;51p?EHd jKM|UqC 듚_fnc(͆Fv)pfKT6&0{Z$/umo4>٠<}bq:1 '-`HUMZ:PI,-Ԍ%S/LZ+8q -=Mwl'O!MHQՉc\+5hV czDS͖(274Cl/oA.~|}-R_9wzu-SPX9b/߬o:mrvv\JsQN?!2]oZr`۩Zj" whAWkS/yjhEX);`YHS-,򉙸M]5dꩬ^Bu\l)$ 214beT^ظOY7d! !~uY! z>D>V?۵D&ƞ0;.û<*7# vysnb@bO 9nTeSfdKU0ڶ$%Uh5m̦)ΨAV+xu{Z*FZc:G2?ߛ#GWn89_FoI(b qU -a.İ -XV@0;s5_ai=kg QY6TDV6kye -g{*ǃ aa-IbDD(WXu'URe[3[vp|fW8\Rĺ*(YgWZUNrxd v,{HSpdI|5^EԌ1W~(,IFǝ>ID/ѬpOWem]YV$3غMکl,c)I~ǶE- -NQ 1r1f,35AA|%7ɳj -~t@m^bs/gb4i3 Ϣ0ݶ6T0.&wr.AR :3 Sj>oFIֻަi`ki`]6=CBZٳxXa I`tBZ׵}ؐܞo@cO4F4૕~hq-+wr|hEEu3|^ ཇ8@Q4Wk$JW/nZUM2=#:F/۴RjEcjLFNW,iM%-3rDæ jEB -v$eD`OP)ʹiX:pl#CY&>8L2U)׭.BzWkgaCώ4SЦRI{Wq05XWf-y8 Hb!7:)>-%gh4JbI~e漮m&D ឪ->d a`,f8H%½)W{VWScIo!@\{:$1h9] fI.MlSҝf͢cqo=ckf{Fz~y] -a k;͚39&\!$^|'6I~s>z 3zMJܓeZ"$"YJLpkaW`Dc ZCzguvȆjJgB5=bǩu`L/BEFcWIv-(|Q&GahpM-L֖`;SurhJC3ϛD qY4 \`b_㻛J)ă/of:DL&.}CĄ~U„)[[ Ɖlm~#H\g\jM8֓u˲uP -: T|<47J;`- -K+* *ucI.OmԇvpSv1(U) -N[u ؄SAzs+D;We>b[kksP[*b1f}ZVmsq-.+o]såT24-n6A"I9vp2<PreՔ|+’T8V'v8dJ6dYFjCjBڴBv೮nWlp#UGYJu68\G1n5v9~Cƾn0MN{+Nv87!XD(*i{uۚZ,`r[.̒|ۖ_z˽veiǸx,݄jR w<̰v񟞎[^mP.Y] ӜZ}A n-D.:tɇ@>Ɛiy#l<ކS?e0,*pCoi4C䰅?S9obKE,cܧЁ@ ȕ0WwZY4fʱHr +701d$9@uH#5U}y?fh 1f(>X>_(vKUB"M6W( 9$27ڊ -\EQ7!HLxtܳ..u90_QanX~*PI8MHLvUDz)6OWn vaHU{اf J/-19[`&S'*8XAMB 5Di㌭0r3$o`/ipWa} n!k#NwN©o5^q -j/ASpT. AK-^"AFN(F9rdC[3+2(ńs"ڊPe$l,E9!c8ߏW\*Hxea+41&̒j}Np=j'ie_˯Uw&BAlGKL_Yx .r;BJ$EA[SzǩY䀰;D;(ES-8h,oCpWɋb|lMΆm ,zU&Eao+b?ЌȄ|!c:Kl}d"kW0ꗛy`E`_ Re(`\H 1Ň_s=Njk8."$-H؇NԞx -Yv,=tT8v|"dln󙍷녤&,V#}o{ *5(},Qz0D!<+$SP/v -Ic?3W 'zٽq/2P. z\s<=֡0πS3 R%I}RۧFr!>1@AsA̟ȧD_3͢s)ǃfA%w@)bv +ֈLas7822 L>V Ƒwbguv?]\~zBt8/CooB%?ƐY0 8z{ nįCӤTOvw✫{<9;{񙡯H5x})Vb8040=R\4k.$?C87Sl_l>1}|n b{`RCH${J= 55PL,SIt\WF}~qWW2=drjsDP5 +U$ͷ3# Gtz -47銭bks_}I+`B5.G57ɦ^bn&& ǼROQoaD}볣enrzoH;ROS Ij0sхUz:ls.*wBd| 3Lv's2WeaP{ϕ" we*ee5f\o*%(B>C5tt>bb$4i}Mi׼OVӛfΧ&CM$`3ül-ѧyBi,?lj]OV8믴;s7r% JIVĸ.*"fW#iprC=nMEC=s^;9nP=67lKzZCx9@,??GyzߦBwY֦C*|eLܬ}cJ/L~|j0 {.uC%x9xS߮E51M1nFk_yR;w] { -o C>Uv"}wBJL(IdU`+M|x6Pv5\8J0o t;R];a{uj6@/lGZ*J}L&]lOҬO:O}7iK@Ν_QWޝ/}Ē?KCbE -7%w -L2÷6 al{tFK=fwU!d3Buߍ 5upfL8p0%}g7(rQuMV "&[@=eC6I/g/V|J_E_7ZzHKԦ7bg]8l';?#f]/' -3'N( 0y/Z HMjclػ.Yy]9Oml -"<_jm͉/гn䞲>xrba.W G۳L׿)@S -zqsm߄Fd\{=ofa XT|JȹmaSM5:l}3o(JS)Ɲ:FxQlJgCL>3Uݧң]:Ͷ fmn.ShaQ'=^ M! WpYp(!=xZ=W=zZj6[&px9/g66^·cC>Clr̂z SR( iUwG9uz?߮o>s/~gG|vыGW6o'_'&7eoaWY;^ɿ[(`VOdm2 fS z}xbe _yO _Ծ=x_[_Cd#\Y6"-L "pc^3\Z*ݭftexm.%F6E]LY@K S֤gFX|a2?fpD5 /@|UYN̟= q," g`뚃rJvG)8C|g ->4hۛ|Xj= AA5MbwǶ'0`ppe^O2& Xה2Fm~4$0 $Ss9r?F؋ "0tlEs~Pv%|x6 -!fmLC!hYaTh NAID/;@M8:mlOSs2`$<:?p:B64-BTF -Ahz@5:>jfV Li6nE{MMRbz]'#ٌ'. D6ɚZft*/Z8rk6 i]6KU~c)oikYCel2MT˕ B΋3/-٘|!&dk>L8ǥ}aMMɣJRI:pXi,e쭏yZ⸭3UU@%3vD9M4Q;o\,5\bnheZ:n 6Ԉ0k+w '_ikY0dC}RVf y&?T͞8_Rś?+ D7xIW;yJ\" B @YH'`>*` 7 i)m^Ppf81Dc o=33Z&ɞ-;a'QFKǓ\'iI>,ʓcY!ʼn\Cgѝ"f`XҤ ŝ%4a ,c4".{!M;P+SuބTJx| \NKHzI=LE2dyD"e3| j!7BlǤ=> ^ƀyц|:)S\$W4PR׃@ڒ2xA1 9')4xE3%th1x2_5ыuL r6Ms-3rK 4[u*kx GxE1ɻL8[>3DP_ұٌ\H5.A=m"b@T=YIY-_T 䆊3;zg点W֏ؘN4{ 3;ZWR1*KV/J~:}t˦'O^4¥*iڟ M"%KA}a(| qc[%ӤG7`{@'9FWS:^yv(|/p; ]҇M,۱`cY\e ̠qO_M -3p% ɕQ `F'nsk5g*6/Q58vWG4|x6OqPҹsw&.ͭ}c0BKWLi8 l(J&Mac!v" *?V#Z*?HYڑfE&1t(\K^'<;͂kg3%gKIeQݴ+C]'G8I"f`9y䨦e(8!,1DSfdVl'EYn;SCĴ}{(9܄:ekX|)m%5b `w3$:c"0s_mafn +ή"{usݿhZ \ʩW#a:u\1F%6:[en+ה@W:*Luv9vW\"]!'`;D:Os E3YpU6Dwmv.N%7U#J}0IıV%UÍl -پDY6`+u)P[Pjk3^#1)c(sAlicQ~cC>42[mfV̀-i^}veQ{vcpO/ :ŽbWz 2rHO| -gǧǧ߬&?;KOx|b揫g/}߮~G?<9[!nN^w>o77?z_>9:=<>~{|ËӃ~LJ6ՅlfmW2Z9?+} ,;p7Ws|~O}{|Uܛt8~qٙ0MYԆG|{W%ͳ[O]W}tx {ُZ\_ꝕeۖz _:~/"q?_~rݷ_Ew?z{*kSNzvCE;sogElg̽3vۙ{ \C̽e﫹ׯ^>;ys|^vWZrv}Q˒jpzT{ WG92}`Whq,abK yqW-da _ -f ΎO/>ucq\6shE -WҖZ]w,{Wv-Ocf88eQ^čE>Ã򻾀O?~wvzt\^M͝Ub ZDK>PFk-5%R@*389mR}߾9XOw+Kz(O5:xq߰4yS?[{?x3eێ}0ԯ?s*iecբzl~h0qmaCհE-X3C-uTaa S]?.~%8xa+~|z.%dYA2ͪ+d<ឱZZabɑ^Y10e%= -G88?ncq%*A(BTᴭ-!KآiX[.Rrכ,.ϭϷtOˮ~@vb2KEyw -~vg'o\l?ʲDՕɏ,w]8''!ǟv!]۩]yB~Ȃv!]wBKZ.|_:Ul;3n5;3ngƽfܛ>ccGr]||~ougߝ8Z3vFއg-Ȼ^|>\ne&L㗯^//kY?vlH3 8%-&x=U/> +aQk-=}NpYkj=?;~/{)(wth;NɽL;_;N;nV{6Ή9~bW^}ߑ6}eQ*_ m^~ûVGp#QEn|8ߕX*ݓJ'M|-’ \ԯvƲ"y [}kg|xsYWd'@_!-+_STsm]U;is>?888?7oZBzC2wn188'w},jqזq}_ͮw^Š\;& -ݹ[]7N`Y* 3Eva)+uzq,)=̈ Y]<1}VNnQK%SYp&;xO [3- ؚ-is{gdnK˷|.W7ZVY|ˢȝŷvY|.ig񽟖soYەM&c0L;횢PbYϮS?wg3-h5[aWY^}wm -P.q&ܹ 3NJCg_}qC -~.߅ڃcȽb[}T~~z'cˋcw^XƖ?:9ܧL뻇\Ҳ?v^;\>[_}~xpgbYw/ąfy?|j7q~>kz~vJ\} Rwޣ{-U;^Eyӣ*~%2~_{CD~/!a -sEǣpo_-89"݀ -o7\LBbL,o,fa'u_.$dc';O "p':w ]t.l!&Ѻr*߅SYzxdDԇX>ڳXf]=3,/Џ99/b9qgMa -{w; ~N=o=l_-d,>Z -WpͨO;*[$մZֺDgvtL: 1;{zE <>_?P@"7{BjvȒ}X~n`)z݋Q?99YԮ\ݙ{u* kUݟ]N?-n%^|s~05~ɟN_l@a`˿~f aa}s|zߑOyG~޴^o37OSΥ1Bh)&L42)!LOx&Z]}qw(ۆ~X_V endstream endobj 1929 0 obj <>stream -N~!պ2Z[kJuޯŶ-Wh2b]}%:[Ojm\RHE={maRߟ:P=m]~)UkbCmcMޏ^OScm1æUmr {}^/iLu~ e}֒5b `k?T ^˽[ֽ7lmk.!B;H>a6EX;eۦ)s_efn4B00A0 #Uh %fT‹&xFbo;ZȌH;!%`QC&0]F) Nu1)VD5+Atb&Ŕ{J%h`:6!~pb̴[(iڶ 6]v$V*L>㶛>xukKNg#?p$(l8̑yX66W+>Gb7Fd):]qn3P0q]ly=JƜY&N ^ǫ%v=;WP\R!dRP{IDn|ԍE1H9JHGdY̔|w0NßvnyYFN $_q)FT/8T')-Q: 0*ƶL.qTM\fiBr4M@/nۼ6lנ˜76fʼn]Im<&^Yaʳ&fzW.cvL2fBt -W3~doωbq\ Ŕ\wAқ Sp~dZ㥘 e AW<ȖH1  CSX- TD{TdQQF9\.Ʃ3!:c(=uAuOli( ѱ %"CxO;38 t%e{:W-Z(ŕ QFh{s5хhPꥤ!ynx} ~b"n+46. ->9,@_a2%0@, !0^ -LpH]CN)P6=6[ۢɛD{ ao -IOXQ1%#Tn/@bk/nc}7- 4C"$+|+/[u>T9}1Á TeN%#rjgXly{pʭg`j6RxMHjLZ H8sBO_P8o$?KDxe -,kDg\RD'*'Q&|Əހ2D!ExE-a$ :C+}Aoz?OW( Pl "CY~B*x)6}@9Cu8[O:u<ADtl -ipyX xX!Ƃ{sL4V=6 -mto>;rmďʘ}J6[CEQC /%OUţ^,8I?A?KNeWr;i:ws  -ۼ+^`&Mׇ'y8 Z n5Djjr4jDU21<&'J:  AU][N1ga~ .p(d`ʾ_sus/1TF)HͬBSQCmgL+Ҵ˜/[*n" [&P!12m۶n-tDAֹV1qS]NvUSjtΛ=[1R:;5ۃx+jG5ǖ8IHyr= `qvH$wcSjuz!0&P3U-HoŅI7{з[-~Ygz"n|4}0j|!Eu^c)\ܩ#_* -AɟI ~/jјH[&qdLEo[G'Qa\e*`?&]V[y]RK(@2_DLb2 -QJ(&D@ W%0?Qxhڿ}78{\Vٷ{ab Tooa"J°z͞$Mz4f?'Ha}N^^}#{Xڿ[h?@0=L\*__|M5T_nu6>LPA"- -A>7VS]tzr˟Ώ_|vrpz+g4dl 3K~!0 )FNc7Y4i IOoyMNˮ -|3wTljD̀q))m|OR_4+HZ ""J٩¦p97_tn14E|_X4q4$%M+S724)r?Z/ѧSr8&7eEMG$.vCKvhg -ӆ*Lrn )4ɶCNd-:ӘqHOuC=00C7^tF􈈏!] 0I,0@nç/{iüU[ B##r".~;w522"7;,f~rKoA1uOgZh4gvbmKkL.77dpm|\R`]_IPr:6l4n& -!H^FZ%}\8SB Іn#hM -]䧓1sѺϏ+$拞2:1>4l`A8#AlW=mBx0'dldi5^.P ޙНV$>tEO6c'mƤٗSI+F֘&*ZxE`KIߗ}&BT#tT@Shƻp&`/qu醳`B4oc)6DL7-~@Iۛ%|l'~T5)^PF(̳4 GTBϵ&zr733'<ٶ ɉȐtyL<I[_{oZ!P Z!(FQ]}~QHdX7yuO.PI!]_4)F$|?($mƧ}O֕KԾ .ם Xmt iCJ&ntK(>e.=3#/E<0V6XLP D|?28?1|8ή/|W-+ `O043|ЉCoG -+l'tEWQO9%_[E=Vo̿4󚩳g= ~a - |<'Ϸ|ޚ) nkowⶁ~tvᶍ.Džw }Cc.H~l(?n+>jeԴ_h_:/~vx?ɗVct҂o3n]-GU[ -Qna'ew\7+Q~WՕzkgGxI|Y7ǯ.C|&NQqO_ox*NDv"̒oA2lnxk/}~xpr|  -{d'ggmQ`'#@P@] AP2!- `O48H89g\D& z>cF^_B$t}@"--#e$TvQT0:MEb]&cAExbbiHH _U+.?9h!&X12Ɉ9C7F^8D(514ǁJ5`?mqPڨs%;%Sl{H4gk_%RApi#@0pSY XO'Zl)2#INbp,ݤȂae݁d#<) -21rz`;54Ύ86!E_[?a:YT6 IZf%4lSGoh֌@)"CZQ|H)u-in5\+1"$QGw)PSpȟ+ I7ĕI -*SZrU 4""=m邵mׇ=QPc惌l)lqŏdP%!U '%iq( O7?CD))F$]mfgςWOFض$.% .𝤰L^: o&`Ft9d9#]*5ZT>1;Lⓩ!17_+HyCAmыmO>H18ތDH6 =.rѶTmh'b0׀÷ÃF1DP$O0D(R8r:W LHSlAш|yN2@BD%Y3„ Ĥ)Pb)EKd-H|B*%{@GdN#V3C9?fᇙOx:ϭvk "`rNdm!d` MUH Z_ JpH=9UsUM`Bl zXlbµ>U/-WUEɞ i -]NRG@2]i`i;E J -4£pT@FcUܴQHԽ=RdE5:&x@,E$v m@LM}EBƍ<nE4 -@AYÃ"Haڎ>ӪA]At#3Ep !x Bv^V[6?al~neٯX.0AL "M3U))$:0S:LCLlȂf+22 -N~ U,?K):س.;HrwԷ -]([L>E"#$I!˄y Io DВQ/SH q6X0 Fb"g&)p;:*>^A[|h$C ugIĢį1ߘ2xsu9tܑ@;Y娤v P[G[GưӳӕMs)l3EP 3LO pAءF?UZ**b7YV:!2ɯ&`^EIFB]4FÁH B>uYQƀ:Yc &|R'&ҝA5]3Xs>xQև{06gK_-L؅o| -Ƙ.JGEEtC<%C *Lc((L*sx9Jj/$م9)@G2B5RB&q2Z&>Ӑ/aIV7%&f{ -8ƹ:1  RDvv -}(~"(^ԋ -l7u( pT8nB% {Ȟ #*s:֢RX( M{,j) X2s -tX`Jn:tnU6.>"\I!I)VjM2uTmSL`fc<)q5ڥfk CMU8jcIx,׈2ARݏdF/t1sZn36% ƒHx6mf16j\Kt Q -N: -`"=î139cVck$Y(x9y M\b+*=Xb}oN/ '*I $pCiF=8]"$&Ԉ`񣦳GNU 8 .rY_b6!3AgJLYh1TFر Ahm&߮0f '@VM:f}]@ lbY8>;HdȪ1FhFDW ʁ b^ -G#*&]DӄHm H=͢v1e^8+]-^8kܿ>T@ah#BHr$J![2af Drà!G(Ȉ S(LU4h `$eB&WqiD -IvgrB)C#ndpd$RKIRGB ,JNJUQмABMbHi@*(`e4ڣX ҐVWw^K񾊂`ztP-nI/6f, ZЯBaa&iQA)-vˌߘRv;Zͺ)ԸnR%5Lh.ձ2+uNqLߧY R$>d쳱<8 -19uJ xxǧ0 SDXP'ʡ#E4,N!$TzBůg0`9"g%`Pp> \@I| gxyΈs()èA L z/(QK`P)-rD/P"cbC*CpAIXRvt>Vi++%]cwDsEic&؜pj&*4)cLf{W07^ -/7XL">p)L[$*H:V2ZVxՈS!33>7OtBrT)?aBe:0|hDB/Uat:GUO g >8Ǩi`K(α s%X4!(q傎"!ߠ/lPhGb#kPefAGprU誋VIvSb0f6>ޚj{`r*N*C8E8><HO?7GrJynp*V*F  H@<% -x%zXg! =3oHo]LQ!s: қI= 4;B"XĘQog$R෵ 1 -n v7i xFݦ…v u0pբ4-2dD%\ ,2!ؘoFz(D -ՙp%XLo(^p^C.*0pbŇ茸qn+aӗA3lC[Y5~g ,TT2. -%# `&-5$C#<X6N6-td9>ϡhR-˪> (~CۨJľլ#cQ6C BJ8"`DÀ#CN^E .h܃E_&ƢS,ȅa Dt -$0N6C 5Nn6 -Wwt疈54&9o9.9Oh{ ՙŊXs.$r(Z(;)zXKұo3A ttCD4b0FcF&! N HOM3:{Ɋ4,.5]((D!{ҽ -h 8".}:d:B7؅ ."ͷn{.7hc[O`;ˉ"ҍ,Ϥ KIs T -U-BE‚0\TgӔ]׃cxX4f=Qf7 R#vɠgUnKpxxW2LE?Fg[diZAqp=H "\$} G-a'#0@|P&gV90*Pt^ -qΆJG4S>,N!^đX`Ďt JA/QIAh(KQ> صTL8Yю*_t?ArHSADа'a["(}Vv10'T0F<]u)Ky+ā@dNop1t ,;jZ}_躊JN(KeH;\mJ:Ǭx| -9:t6_ pxA865I7vwp|9 Wt_›WԚՎbdWj.j=Mp`eR/(bt Xw &BJr4# -QˬrM73{mL+3) #tЭUHy}]CE ^)ˁA -,t,B 冑B}ڤއMCȥ] q؀ 6٤e 䶆̀hj$uŽ\H&|бA<5Ѐ"n9j j%1B⚨[nK~r7Qk솣& WԳ\nAq\xAv)s`&hËt1搸7θv6yc \+SbE"0yNJJl8z"%ʬY{,0 C7E |$9*ksT<~H|;TH+{`7P;x[I:ȟOI ;|،|b䉡Z8I#Ѝ!;BM6$o੔;mi-54+)Q - }D*BSvy?!#Y14FM|ep Hpڑ77y*==XLd֩6z֑ԤCD9dE9E,83 ~ 5 I qxCs8 B`B1J#wxIT!99c/NU% AY q*l q0  l5&ڍ*2Pmbu?A̻ɯhԣʎ0OT=b̜ -;l$<#`@o4P S>dMv /_5}MIx5%DJb -ʓwOP>cRsRd22]D3} :^2Ea>JfY>.2 //H#qX]2pD.ϔbMH;"Qd%, s=,r*$.*r9`pG>>݆)}Ntd} %Qq(X8j|E0u2jLWu>ą_^=G_A)I:dqa!&3?8 19f63(EN{2SߤM%\&WSse&6xVe:9QMţ))(>՜(3)kG9A0: &A&+tt/TtкLHR&E:3jv]`P}L*$lN =C -0M`'PF&ʏCYHbULQ3#p `&zA@D/pq%>m!Iۀ% X#*m ? y&GtYLx"䤬"% ;#zhIȞڟo!z/)޲f*_$Ee Y3"]loً g;J&ps R,}q'SbPPX) A=$ڋgP#Fb0G7smsB&HDPbulؗ꺀E:hWBo^#Cu}"_- 9H4J.6G ^h{#0yF0-bϰ4^2ɋ/[1  Lf%v`Y3@50")1BL _,6uhJYMa*= ~v -Ϲzr -A-@#*'] +ˆa@,`O{MSgF90.N°}~iT(cp QBSC#DI0OX)eBP -- t 'y2!YLWV$&/nteyޙ0!#zUEFMx -{CF -L@ l*{ -$J}寧#XveT;0O@`,ÞX%&oLpCf(44EպfFzЍwF|me\ƹ<g:syU QSpNHVgs>grO.m"M -Uv:5>荟'Oz[SS|e* -if]}XQCD駴98!4GV&ok/u5ET㽳X\?Lz  <)/yF&-2ï'03Q -uPeV 6 5+wqs͈&},G,D-p<*V4GaEJO 0y0(kj 6J:t%ĝ -z*`#ql @^+"?<ę÷H-nja _DZ ^SRanI4h4 z߀B3'~vt'^:~qgg_}rzٹ^m?ˣ_Ӌjoz}oBd+3 -<q GՋ"fV2`K@+ -.#6!Mߞ!3VzhZTMsLTiuX[ -2Jӊ C?UUa g.ƉQ"# UJ]xO!>{wa KUomotVbnsQ%N1b5iwII/7O? -;oGg,\P.$P ?Ø7mhsuwFcn×GXH/5d֤7N(<eG7: 1 -n h.1+$B+}h7lVMM{CMA *q9` ظ7G]tI:k* ]0Ӥ gŤLg׆%,+YP7?֣ o pCVvCl߇u D@ouFS̳-GgF-%b+d*(<|'`0:r<_0Rt+xt!t/{0.*Q2KLnsIXC AJtn쌬A` <|8̚q~Uy -S PZz -HEcRj}FF44jf \>%&-Wz -నtq֩ -q"(Ł+;GB}ՉCuU2@*39b]@G!H 4ؽ,뢈pi'$O*#?l 4n+̎n%/i/v yݽ $a~SgE쌠C.bn{R gH(VB PQeHB ը8Ƈ@$d- -BQ Gd&zơb l4NscSI%kⅫP9#\h޸#{x2@dYXjY+ȃ&5IDQx E -*QP|Gsut'yT-8`.U[}ItSeʚAX -Q  SP4L=]ڋ4lqbCIP/$h\W='$@k *ާZx * -`;ЎRR"mxjPp@;IŁ^0|ە0ľNVX #[mXScF`7S#dQH){cA/D9ZU∁l,_bPt= -&ATC,ݲ>%^;EOttwj Hαmډm+J[BDFTxZ,rDQ" .N\j^0aϢ{ 7%"5I&w0=7qRȍ nTOpӧP u% 'ijS∐^"^hvLIYHMDP0&Bek=Ay'>έڪ(r#sCjOɨJt_"`aU6s&\#=N>&7X'#I|ff`}@9#L<^3k*ڲnZ07㋾l&0öS^#8h@x52r 8yh sl -ĭA02Aٙ%skP"D ytF>{{z] -}d?@^fb*kF&.ԏ)݈`\HbU abz@dFTh%&H"AieO=UPKFN[X&=Hkv19gDjuF!<%BY D_*m}?5Y掱j(s.JJ@Efh?‰f`d9bvښMl|9PJ0K ըR9(؀gkVl_OX- >pXlt-XeGS!j~:DM۲'82a 0u*fU ѫw M+롦0<Wm]G[gmllEvlI8&6x&`yԨ@I& -bf;yZ 6:?W+UmP@R!l*0An)?%C(-{%L&,e' NvOAa)Sչyи/VvGrn2Z+'CffR6f`>A֦V*;(>?L [pFxX -' !2{V>b - .c" *ߑODfqЛl!&`7V%zm-*Sl@:Ģ,,*pGAL`I(\~ \čqhIqSܝXH!/HVQ3f,LNAm&wL<)ZQ|3O brHΩUЯQ喙> 5}r(ju3LJr'+,*,&pRҖ0η C8FmT-V]*ς[ёThpL'¤*6qO"E @Tc$f֢IUqRIBt_:-QX?_P#vӦF RvШH杼Zs֧'*NDgot!,uS6(@"iV! J9'?I%bTKV*O7gNtC#lV9Dt^1x0Ќl.R  p!A,ϴ+.@ZD3HނÖpxɰ5miK҂ -ZgfGG$i8M6^&E/=2OjgpTވу>#hy'y̏QxaA9ӉLR;y"6`)xF<_*%UX[! -\$ GkZb -.DCoh*A^Sݶ]h~Ii0*BFRpG~%t ]|GX/9;p! SgnAVL+oth`h^ۀFM'zOeJSF"Gvk$1$ˊ1*+=f?%p,`JY2eFOL#[[(:A`>[VUW}DfF嚦]@ZdTؗ|^:f / jGkEA눫H:$U:nbBL0]E -vɠ%F'ږud4#yCG\s@C e+'cH,wp6NX"p]4LGt Ye'iysHsq.7DIV AO-;W -8;<Xu?m -xwEKB[;qĸ9 oqO^wS5d -ھaZRg=U+2zL<G -9HPVo\z,$x2&ͳ '"PhbM6xLp8T|M8d3Ѽj$:J$\{rW'`m8Ko&+ix7sm 2xj. -Z;z>#tS f:ص$gK%cn3Z}Baq?l/tm Hӊ,C{"&p&]|Q)2rKx`) \!AoI` -*UbꁧR,WFT$k RӕYo@n3!gbw42GXTѾ{YC,[͝O+v%ݒg11}n {Bje}.N*ݼ EMYYWO iUQ^6;/ -Fˎj%kJ^𰔋28%uvYEap$_xQ&#H i@-L6w3ޏ  -gG3;A!Lf~0K$xB$hw2W h!m|U./OF,*WPoq$Ci?} -liU8LYuw)Y+|c.Xt"i;M'oeRT8/ĉhT!{~PJ_XR64 מ}~xhBJ"镊(kIh+n3qV&>[9 3.ٺYDKu#R -FDree|**;)JSSZ33賖+e|/ -⼴e%#%Uo$f]0XsFlv6i9GڕSw0Zg[Edk{fWD`v䲳ͧTR@}&$<w%6K'a9 D š8ޘ{xBJ -4Kl:ͥXGԟBSrfZ|I+CCS;AK/hbM`a>s)VWڌK۳<#6gfxY6C?AuT8ah92= Xwp<+)'108 -t'vW&@ MKdr%%/ u -tJDtcL_>8" ? DV+pZN'_,mVg܄ΡMRFYX˳ܒy愐n՝N} ] EQ63Fq*B K)ޏ: yfx4uA& {=,)\H)[ oX mBqhdQb@]HݝQ\GAQNHDj .*ٿ_( -I3j S -LV$%>x -Aw O5d (wxDIG6e࢒:h}Ђjwq(lڴU0}t&ض'm]2%Uܒgy}` -M>OQДla?[QV.V%B#._|\v="zFb, -S[gl=U1Y_c rJẼTb0PR3Q00n~b,UؐvXX`䘴[ٻL4633|4E:c`Kqڢ5t ov y%=3+r˴HTX[1n4W}Yw!WG,n.Q`ŞۮC_}G?J -0'd8RGd>8 -N׏}:]V.]Lwp]}P76V#"$KR(jM>v4lpY(!iJt!!;4{%粓6'rgŮhq}X"7V2@ެ(\gZ*&ڭ,5B;Ҙ ҿv%QcyT~ Q; -0HQRGo;tMV/T?4 UȹrLP.!ӳYm W96+E&ԀpZ5%8.̮,n7/ U>Rla${ԎB霿~7Q4!փO\)oRd;yN0 OX$>]TYQ~% bW!s#ky ҹiB~qTNT6=g3*T%*8pI2p OI^-.(-p -}B{@Üe 0tu -;d+gvWl<ծʴ^Qb}#FݡdU4'@*9K*t -Qdsw(E"iFHw@L·#ZnE~ Af2Q9EzI 1/"*j ^ɧz ɓJIؓCc[G(%OI[#)'@f`Vx~CKYh+At=!JpAEAԻR)ZvLq=r)]X@PDj+{5[ӖwFc{Ʈ;˕hיQdݘr?Hݝq% |^}[~$QV௴8`:w:ݿW⁀Q/-@ڏx3؃|lJw1 ΒrN<1:Y2lh+Tbjr&:_ʉ;$^f3{Eߠ_?wroW˶XK8U&}&,zJ,Q#N7!'x߄^ѾxY"8ȝ̕hX)ɟAQ{!RGg);[{2:pqc۝Wk%6B><;YdX7xpϫpTZ>-ióLO,`j3IZO -{3gBXL3#=A3+z<9Ps&2nGa2-}\!&IQR -si!uGM'UMA.ɴY_ S⁰ʹkW3-"Y4G`8TH8&MKO.y[㒝c?%TZ{Kj!oQ O]ʠL -m4ɂ -y+wsڰC.-`viy7D7ĶhҼ+8иgY@Ov C& w˱޺ˤ[hAfx7EFaYs {}=p{y_ષ;g"YOr0b(!_>`b658#|&' yHpXcx -#gio_crTc3F $x+~#'Ƽ9b*IgCc|EZ|\CԾD@^q%OL}R!F+"j0,c&sN`- X< -rfQ.| s cmbE.: Q ?( -oYJTS5=aY*f}>x]LW"xafMRD"i\ u}dE)`@!/rå~vw -mWSS1aƂ4:.t 抙&pHhhblavB[Dyf oP!'7zPQ̝__'Ͽav +&CbFєfK'~3k%es_hvXb?[o|m0yKً&/ĪU48B=93rJ"㷐֙*kQz&?4]W&g _z}R l=(jj̻(ZxeܼJ$37l*{])'3#ٳ0?g6?.BFB /|ٟ*S<;e"V5XG:g9SEsoNhPVD?Q9n~hcR~V#Q50(8 -IQ%kŲmhߤD! ԶӿՕgrG&Өwڤ$̓sk.03( 2Eg.E2pTC6\duD>;}u_5(e!+p;hۃӡ%Igv54DW˖7ÙTiDZP 1~P:͸߈4|(2F<}TzYOr*URUl[A=Hm8[S)#!gŤA=Ԑcgor*EJmڎ~fKDXi A0{&2Q4AͰ!I4 ~mCx:Ӫ~DI3>ލt0u"LK;<5{a8LDPkaăTU/V|l?}Pv9RY&&t x$WW02>M[P-;Ş6AķEV#eʸRDn%S\^bqG AyJe\h ٞR="s565Nhj6oK|w/4QO/DW&%Ɛ&J4 /wrTK;[ޮ,7WxIrF/ߵ0 Ǯw+20>oj(yGҍ1wJז;G̗^5S*C`;@w9S}*IEvM `Jԉ3ׅ"1$4 FKPQlUH!}P\sF]\62vZz'O{0zݢ|:r[I3~Ҷ8 l%b ")nF::=x~zy xquN~Vm6=msL_ и6|:CiN0nBhcl+hGt 8DZ#tjm\!qR} =YlkORE9$Cz2TmhcJC‹S^04J&lѸJˇ(.WsX h9z%] I-J/}3uCʎʀNW:7/w#u C63Cg;"{*yGpZ␋%.]5\;mX]#Z;l9U-Y15럴IMm/'f][+y~'-#aOhm u2S6nia&ϑ4nyZuicӦ-mu# SZ'jSэ5|@4*E6*JP&S=I#&4VdyF&Y16)1R 6N<7uڤVi4V|e ~N_Tn q{y7sV1l=lr[.C􌪕kրMJS{fi3F7͏ 8PG3O̹]5[iv񦈰@3W ,DN_&20)g@HQ3J7s66۴()%qɶ$ѴS]F1j2:* = WsDoq$P. Bvh rgOE5K]weW&U0jv ^ 4,%h봅9$FH#/;- C[^ Ж~|m[1m95:BI"3I*ym} -g.DBD*#ۓ̿—B,i4يpkW4]Rw:.KɏJ..U-j|6,Oá"~hѓAS{N{&CW_f49܏(?Csȕ}i<%ka'/bD;_"'3ɧ9F􌁵MG!Nn>SR$zl iӛL##,գ83Dyq4dn՛dq["O6d Vghe0TEzxьfM;,ea0z1[4$6( />{Hҧ9c1Zv~i&܋#YT3RnFEsNO/OxUf18ƲOõyCYIÏ3ЍݟbOq؞%)MaJSHMGR"h0x& 5M鹿LHPhsgsBrvvpQ~{W);D DKjj_~IYmx -iZBm;4[dUG`} }w;JQԴM -V!\2{vw-O%|*`{"}0QEN-Kӿ9gx6Wh)t?S!~FQ'ج-RW6D OH=VZAfժz94.dpD4T讋vJCRU#akm8s37l׃K|F~ͨ=4ִ1vDu#\g y Z;sVBk IәK *7 Udpx-ޏnzޚVS-a Q񩔚+eEkaGT$c{\f ޯ"$ޮřoŰ͎L+K(rD.fWN =$W trc3Xb_@Il.贬ݸD߈ S_|-AMO];/]֏#3 gU(gz`8"*7[Bܣ}8n{ӵRtIG%/-X=jGc|C*fszNF;Y<7)qcOe@BXwލ˪tSGv e -bTY]I"d}j(R5oW <ʃ G UKFU4b1̃f?g1Mk|4vu@@9s$Z{Klb3t{{QO2]jTvP@@rij*%-9P.%Ng b1Gň^_}QW:b'EnEL82vMH(įvTIEXIP59F5i΀?ze.T(}~Sr]^[0'0n4'pâ\ߑה -ɑĤz:=gN8^ydFQ>T'mf(-׌hĪ瑼d(U^2;l;VfNxЭ/u%:h9JIBM#wBcl+ciڱ{oTwIaH7Y}4A\.4COfgRNCfHX= ,oaw׺ki&$iPO+TP)$*B -u;EeaZ=Ʀ=b;SSmJ.ڏTWY2MV[=/H({tz{  -kfe/*ET p\J+D ̾aUb]F*<bJuƫe{-"6D߳5XWBBt|Kd8& rl ģ-ݺ[ `*lYǸ __\;rrpYΠH8=|Fj₎hZ]pRHXpyqEBL.*$Ln)!@b-l#-6d7͉bd8!y)g𾧽b7$1|{=9ph% -{ klpwI*#hP <a2M>dk;E"]rIA g$e/By@7M<آf@ K2;X 1Zz2P[,Pbp XttxVagjF(d$pb8H"SlV1:RK -,`Q%εIA",wb-DYL - x$FxW;ٌ0*{_>?}J A~9 3WI9Onz5aאּ/ig|vCU~g-ZiM̾SkGl9/\;fDkl,Ä?.3ooO˟w6/hD68PBP% 8^\^A8rB=k)C2ҍ [=NWK㭿z"_ƒmOB:UD8)\AN @i,x{vjp3$^]6 4cܫyaIb)ّzLȊԧGY(HeV@=ޑ/zY@9("R*i:)71]#cWqkh e7x8,Jfavxܻ`FY/YWlCԶJShW>I/kb!R#{kB8+3!XJ=Oa)~D mSvj4ŹG핋(uq}uZ[TBwൻ {|Sgn1p$}QYv~b]+Qzds{lӫT,q=u~b{1lJJB Dg[eNWxoSIx{Ga!Fxw -w1j!:RUS,l);, fOlN"ԝ0-RirPQcet! kNj[u5)ѹ"zB9<){Ŗƺ庌_҈gWsHTf+EZ4g)4N^]]ݒf}0v⿴8JViOSGԽNYYu-6ڀT5x~FXʿmb?Z٢.h9p]z=qdḂd)J˖9YfLiar `Q; -N׽> |tDT!5eP#;cǖ\#*{h 3N-uz',{G}|wtZ($y]/2L6Lx/Sj^)5 1}%{ޡËSinV?CF1Dp=< uoj+ - |7*$_@w`.^C5fKr9PTm5A\IUPVА=GF$ϼ;fORW@÷1U5b髨5]}{`VqEu"3{#ֹs YgAQͳc$.:sʱ 1]󄼲l"#2 t?/]3=(tġ:dlC]53C#Xi'G !a&SdfQZeN!Okb (Ӈ~4g᚞SJoeT5Ы^Vz>~iz?db^;Qebg%^?>L5z Nв,~R. ?HÌKǷW'{ 0 }fX6/).:h=!ƒ!xcFF.:rcV;5S?ulW87{k+dk^b`x|+jފ8[&4"u}7Uhձ8!4kn!GhgG]O3=1Lg~4 v瑘ؙ9}70'v9C5<{Ko)DrhbqpĊD={#1N(>Cq3L6>=1;-3Yّ1ޖhg?f_ Ց =\`ߣ;K<49r7L(N,Ӗ&8=C8?Uh|o s!Ѱj 2w&#:~5X}`b|<#0p>˿xP^E+FHߊ=K6)y']NB5KAyb,X8.B[w[IsLN:=Ǔ3Âآ4b3-GKiq|Q^Q2+[ G;$K8vC7&a1㚒xT,5cPͮRYj*<>CNXO2.)F3ZLlq.uŌk˜j 6xnӔ+Wn4,w0;sI[QvY_'%5FQh} ix/:kѦTp=~E -&+\GK8מjIRej48~"G%I8v5XOrB=-0U~Ͳ]ȤVv^)I?p;((&sW;ׇ3M%71_gemYpGZW/E?؝B -p pTuI'~UB=$GdzՈ^i~Ow8cOhDI/.a:Pt4Vx6e#@,6xꜚNx:WDsc]-Ӟ#{H}֑e1 y -HPt dDwbMhzC4Bobc^Hރ]ρd<.R_gP`-$|H8RzUopr;g˝_WUiD9fXʸPp\AǠ!N1|d\l_0A*%M ȱf"8W*)Yb^߆2StagHs#2J e7; ژ3Tj.HЈ &>=R;rN;ƨ#(߲Kl0{@:4ک+5z'Ҳ_3p4MRg3R\Au 9{)GlE]eqO&[g0l As sƦ{E9m吮Yq>IG$dF˘zX hv5"~ntSӵ'i1aTGjڦ` t[l5Uj쉢Ƃ=DNǴ&>Zh91MgMAEI1EZan`nēnjeE#,θ7e89{V=UkZI~K/W'$S'oߪȿ@Ԕ曺kH̝aS -AkN2!3;jp`m*G nZ^j,[?Ƶ݉4跥Yk*z=PaȊыC2uh' -ڈfKdHЌ]٦Q/K]+%6lvj$9uH("RN-K%0!"]GҊO$ -!"֏U?.M7(qMnցWKDzW)_F7/EAm8?J 32eΥ.&WlT ]6[]|ƄuUWz$na~>$vM -k%vƖ"仅~đ׻彏+<²|-4eP/VZg Iuα_I~o%_ܣ߭ZXi{p;w{uNI*@漣4E=hσ_JkgA9XX0p -B p+62pUO'ImiGxY;P<OzT.mJA6^~r ǃsc/ )2ȼIIsfUn,v>י>ڿًzZ4('~+qFқg2xFZ4EÞx; #/cEd(S' 9$2z:1Y4w!M4!wU U~AQNL$~8c?&eI`RGGsWRQZڕvW*M}epffiϛCok.. AsT3 ؠ[ejԲH,ͫ&Jϊݏ!8f ±|E:Y2w-$ZqGHy2[O$Zg$d,f׭"5ML|XvsJq ue/)д^ pQAxtcpf'(Nkw^ -[Sp~Ci%A|I-=cx;N5F˼+OȨp2\ Ҿ}?&K$|(#l4v x1!JvMZn7_ uPfI>=Fh9I9S5Q,\g~y-ْXIPGQ2㗓A&` :Ie6װh_qt`ݻ02; Ltn&qVn(\IQ1zgi`1?E`*y\jGen?N ၴ5Pm$u+űwAAۂZXK{L~ӆBM|昫"hHjqYzwjxfܺ_z:xN - fx_I?+3ױniHz"ºjۦc'k~r'&41, 7=m RFJ5RL3{J]ѓ95 -kV*lf^Bi3zfZ13<{=)dqTfҷ 0/|T>KMRi}:S,4?ÀzFMepew4_Yԁ]ng7#*~2Ö2]l8XtsO -b]m͑4hQ{(j}0jU W7{RHѥnʁ},E;XWdv!``ؙO=UFocioZRaWWv@jzjg\ہjΈ92) <=DLAO_@! Dկ^3pʐ '> [b0Ԁ~Iv (FLЅ -&m$iFDoJnves-5dͺZYZVkyRȻZ[paq y5cK1rdU1\ev'¦J_A@=8<ct迏!g"'ifXbyE=4LRx|سĚBqyJuuwGO44|xnD}%apB;k"fp S;'By Zʢn0ZV[(/ ֻ tf> wUW \YG -( ,kdfQ˖ܣOdg;Qkݩ|ګV=OdOZ$ܘ12{5G"ȝ} E@KYjEIJ-r.%xhyp[z&<'R+^kd`+eIDVPd|pH\S1!8&-JsZvvVDZ9ü[ -{ϣ`n)ϽUEVָCl_YaE(fb'(mAڗj- wM[Z̬.3HW+~p@~~H -vq\gO3m¡CV&{ki|{wa!Joya<rJS8n,Uxt`z>:+{xOz|?1ZFܸ2 ={>mO떢 L˕9Rxn,Gc`MG2!)u0HA(YJ}Rpߐ.|;G@AG)4Eovem1ݧ]Q.8,J=t @r|7ﶕ.V~хTOw^2=_{aysKഊE,kJƦCI"H}{y&e[8+*}Bϱ e73%CMu,;H*x2 Hdd)^C+ۣL~ʌ G93W볊"S/G_o=Ԇ߻p,|mg"HKЬCHgMꞅ'oDb\1z)7К=Uf~=:6qvYϑFRŒ:ȷt3`ɦ\2"=% mmAӆ9vVls5!vhτ([Wunzlr åEWG>Ve# -/ʌ OGHR߳J1,SzvUh CRۢwMEl 2WxS*&1E-y3wb/-΍#̺NZɠE;hs׌C-b eZCz)r M*F8Q:J+pyy#-sy7fmI>ޕhKNtsI8jWIǷ>+1Q\}~-hGpU:jٽg6|80'~~ĠYypdٵ(U%\&8 tqY6ʧ^*Te$e"W6Ug[&ƝA1^U1P9j*^}j^EL3M-_]Hrv C-GwC}%gsRud!2wyk6nG|83^TV ZA_k޾QLW4Ѭo|]U'P#iJ-O_DtWZoGF U3)WZϺ)Hy> G~f:( -8$buzv㭡xiHfH/~^Zi { (lB",h舚 #U sD 9(KFUNw^]%kw%ϲ_ӎkVBNrF@FfϿPpv]i^ Dς7Uc sڑ&^\GDwIܰ aw1{}5uT{x% r=? @Qg# &2MB|BZ,JZDa v#;(ۄk:鈮hظCs#^5DQք+ټȦ0"ׁK]Z0wa) -̓Ie6 v ٬^:\ie>[60ABME %*&r-hK rԈRXh`  - NDCJ\B6MF'>fh_~ζ}fn ;/S\x/&)S7CM(^]BXm"AYnh(!Ćm -[qZ7Gg8҈Eu79Qrafrķ?QZ*co ]u)hCUڟ7G@ZV$,T9/spq - ^/N(T6zQuav$,,Q:wԊ5#+uPH&-=~LcǽRŠg0cg%(Y2؋2ːxG]5BG\gLL/JЭ]3x^ٍW=+(S-.Y YOӗj4bS$Ԫ~e/+l)'l&GXiXϪ8&^aXQs IQwF#dWh7N>ku$!"qRW!e=E"z>{ -+y>28)'LYapfYa?1 SdӃaaUBmЍ4Nr@-۳Y (:T ;-!I÷):ȯά7L -=-bV[T' WqD{yexljA7F#ʚC,#G(%D! ؏n[Ǟ4.@3M p#1ad{8QDyi5ko*.$BGaPf ֥qpZ_܁_4s˓t:iPWɔ^!!HPR[*bR'#FPh; :.كu!wxRRcC (\#nUmЦs]6 t@4Mp )`|*m QO[L i8M{7&d%W#z_tCfji(FA(ak\*·:O'Anp#E,Ft4Y)4YvTJ|Y%=GUq_+eUP`ncH^VV/ƎI_̊K+_meK}ıcg#*P!}$CTm -$J<}ij3$!*-D)XEnCq><ʾP6Zj|U#e Gݜ4N֨GDf9F@# R?"츌XzF}jg+~,niğ`gVW0yVHW΢eN~'lqqb-֥SV!V -!푧uI_iؗ~Wmzظ; *#`G)h'Mr>K2=2 gW [yΗBFUzv\v#Pe_exʳ&gH"9ա4C|R|e2) Jv:(.X;'cHsyU<W*җǜR Z{4gLgx#ZrQ7,m\ ->1)'=Aψ 3#ꇍg:@$mbŚlZZB9poV^lEoߤ9hOD")$VGZQ*Kʗl]%MgbX^"u~b#C$z ˂Zk/mtœ֌KFa&!+ tpn:Zm  ,d~NrG!ؚ"HgH^S[/$).7x,lGBϺ .BɊkAHn%VwKG̡UN2/FbY_P(=z)ЌU7oO0å_)@%]~ B{)wtqD^\{B =[N]5m:#E1XˇF֬+UWdY5bՐ}66K3q*(/Q;Wӵ{1xU -Lj~  -gC.EGl?כ99.10*J`v離 -5}>6p3dPU_)MĜe6#,^(!](b߽QovK|4B́z&CMK ZysR5]u)=*'-z% *UwQpI "9,S:TFE>T9N/B+: -bNɺbt>oŠLWK7e[,(謹Ѵ -1CtIy mัMŢuˎv"cйWZcgn_ c0S-f0Kx%< yy<Ln\Ȳ$+^}OXV9hٜ5Mvdn1МM|tw6S_b* nAZ48g0܊/{){~tKYH%Ih,>*juInW+ў(Tuۣgo=JT2^z!p\gh3~nZPTS --YvRvOP79 -96i3%8,XxmhG<؆ V8fdq2O(㾤yϧI% pGG|G$̡u}S|}}d3{kgb8[ 1A1fF]˱2y.d͉Ӥ1+X)Ř?ȱTFq{528 Qe}@cf~G*)JpH leel\ -@9nU6 -!:p=QUd@Ol,ܽbڱ"~s>JrCWh.nwQD%p?إdM⳰*ҲTDLaZ.q-R!ªxyYXUIB#5}>}S[?wFnvp ~$$]Ptt*p!hx777 3+}v8X.4{/GSS#sے -uM:+B)[<;V3 QA P -Iٮ[4=S%Lj3ŖR.o)~ҷ7 ҟ/Ko[9R\:]eST\'۶=Z Rr/K]qI*<*BȬS`{/}&ĿÝyo>}~鋨T]n2aFxGy5O]_ۯ娡o%[B<ʩj,ណ$Q90Szr3atjZ@ f*bySLN58_zRL$PoHDH/b{..͢NJtn3VB㩋ZműV˅cG -1?Y-%4 y87twhbk\pQ@20870w9WMjGipS=dTSD9'bmar=*ԣ>ʹ$zjRqfP0֌/<iT*x>r'M@?6$#@@mts0~$( -S{o )j O喙UukŝtcŨ<0g1_U#l,ηx,&mGI(_U-Hye30XA櫄\\ )ȡ5LJH/C@P#R},2>W%\d5͔ j&zys-8~znW3 -8"p%S{iW*AS5 /p~5/)c@UvZ͈kVEPDKi!4="Bq: ڵ-~iXq+D/7Aݍ1j>B2bq⸱æƑ39e*8jz V9EX#L#zg͚GO$.;ĥJ)E b: -lJC~Bt)|,?[y+ 2oj%gݮO9U1K%u>'ߺveLMKdpQ|y碄79$>IFDUha&Y#Hk|(fAX!3㜃v"r.9=9?AW9n~{IK|Bf g[%j(2< /@Z.X2@)]&~Z:ݴm%9d W#a(>b fJ3Z,ǕЭ=B.a G&\ -eNyGs Uå38 5˓d%VQn4Qz*s8TLX -CHMC@DlVqz8R!<tz=oP -|H ]CMxH-CNOK`+zH&DKUVQ]OGGH8vĪ2CVRf+zM:y/קk(d>@%ji9'9Wd;E -5*8?o]^'8TQԵAYX#e)Ivbz0(WD\K{l9]E{9><̐QK. nV.Hަ˗XK\\xK J`ՋP&P'Bu?B k\19X -l/%"XbN'p8wH)\Uuwl?'/Yl N:k:C -#4Ta<7"Fz2㑕 bnEkio - -n^9;>5yGK\Ӳ I^B,PayBq b0GSn>VhtoP|; A8pv^e-#\ -h(1yMZBuK,Х? -0!A?9Wk4O1"6_K"À~ dNe&ꔭ N4n3GsUP2W"Pg Lm3*f2(d -X9ӲpxvFV] ac֪–,XW`P۲VDb̍B]p$ἒepP"G=e-Y6*uD8|یkaUci:^SasJ])*Kp {~! -zUS~M8=b䦾뜿U,B\0hP%"ǯy/Yc)W%5cmV"(88٤eNM&t1  tHv )"-La9c'@jhZ-+2͎sN^h]V68gȋhb<8"18P0Qd-4C u bm5!#9Vc(^{7*-0gnOljdA4e[v>)chJC鰈br(0ڪ}v'&fYp󑞙 B[!R/ap%xqZ[:l:Cw|삅Qb1g^ @uiVR@a⋴(SilD2.HבyDK&No|/Ŕ9_.#fR/H4h5 =ny>{͂$V|iq}đoNT0px@:Tusiяf@ϩGh8&E pd1˟K`Z`AX43Տq*QӺL͚,m9z3AOqpt:R(Q=<.MŵޕJGʒlTh:9Y}8o.!(5$ #XٱƮI ESœX %b싃`+Ęs'٭p=ɗRMP`V#Z`bt=svЖaFG)L%(p{Qz:'t\Ñ plA -8M!sfFgpx!J.bW-e ݵD_dZIkBő>z~q!`5})洧Zsr5W{ShOUWw 1TԼk4;!Tz kU"tZ T*Z˓V9A8sy33?jHᶎ\NdZGZwg;q<r "Gw)*,keLP#\倍M6N#͓jq#vx̬*rJ1CDT<=jDϜVR+,/*G/Ȅv#\$JbdgCi@ -)">cŦZ݈>Β+x"1f[aH]p}l!R- f^*8^RnZ!-RK5[{<TT"Tu. raUW㪭Iܣ(fZv`u֛5A 0b9>qwk&^f0r!"w -1LG`mY;X@YIQi|; Sc7̟B@.z.ɝ*q{MȝCV;2}p$rf |h)5Tkr.x[ƫE7]:8č_X[հ7]c[}T|EjR57B]D& XNIC4fAԫ:ab=0"N\߫ {Ao-/wڌw4 -LUc&5gt/ǵĠUUMP Ä-`[MK_NWu Q& w+a?RQ^P'C d$LɃ'ĊӴ0po.|MC*{e{[i˷oJ{Qo<٬NL#5Ҫo-wÂ8=LI#@,`)SqxbŘ`);ٯ2VݴV}rXTWlqOeT WEKWR-l؂ev{V0@ 콚5(FNE5iPZ1Ŋ,k(CPc燒ďgOULfՇ=Eb Fٳ//IF幖0wJ]n6[b>[#*a&6#Y6qeeήrHA$VbT*a6ʕi/3qt+6sNͱ0UXC^Ő)nj6(KP7.8/g\wdSX>D`G'uşB`9gU%gqJ5dB{eų=olJ]_ 0)}K2&UEtK,O-yHThcXQg.h\6Gp$JC!h1 -mG?$jjY2n)$$Q(_q0ʫ'!wd:' 9AC]MFKD?b-8x~Bu _CީB 2 3e "D|D\-x[R^}o9Xxˍ =r<ez #ES(ZC:@9'F!~(y -X% V<=(u^yj -a%+A_cWf}K;nC=U(vRB ztp8[BQ`#*P> w,AK4Q8j6ihBC[#Fbv%tΥPnUZhHKdJ-zFm bQu{i[ESBR6iA@c#R|pѥS*A_h= =#~uNJ` = -[QI,nj$T}@R@F #/x`%ԙ4$ Ԗ(9rCFF7Kt|D,$چAd:XrP71:tM'#3G8ː-lZj޼z+n|g`Da-'F tUt!so6U.u88FX4W>EX*YvZB9FdU # DLȪ^'raSCX;nHZg-;&Aq$Sau -+G[sSN)cCȅ#Ej5$t84"6rwZfټټUqu{r~{}WACI6%nEsig(E4k@zZ_r⼕AkhWPBb:)Z!ןhA(!hf(5k b_O[t-rR[&RP@D PB/fpl`'uܰ7lp؝ZKv`/H&tɏ1Be†oj5|$A ͌&q5DWm0dmJ8Fߤ;ab`섖BMY2Ç_>jd$df0>灢0D[a9P>mq홒 OSW xpHN0xꅕT d*nrFDpE8>%O\Ta1\enQ7J Ѓt0(ctϩO&悅y 0* QD{\u6 шgN:`@EB% 7ߤCW&ET9* }>ch=3Zn,M FM.]a[)Q 9xjDy !;ppK q! $QM0g5X -XnϬ~ Ͷ!lMM -ٚUvuEv+dfekWh#mJ=*4A=7jn|BGVѨoMԖ~'SĿxaMb3l:}O\S#3pv6(q3 -:'0A'$XJ9p~ԋxvq+P"*\'Os[C>F*;Y?5ɀ ->ښQ#12P *?v:`/D 'C3PJ(HyI5=A4 qnSAהG%^8kd#I7O7$kPxHqϹZ;&MazώˈW:rTJ}KPYP7 @zVJO@M( 3P@(u0)}*Vd:zV}\&yU J-̒PA 2 :f  -FؾUoD]Gx \X:u6@ -k$"Ocg!s1[^~*qza u@42'J%4!_2.UM*/Znj9W&b8]( b[vk*u __h;6[ϋrx4׾_ͤ HOnrlow B3Bڔ"쇂jg ݫuR!)⏐yX@hU)N+`G H[UX )%-{e'MBTԧhkDh -/v:CXT&79n054 BL;]C{j6]%M{~ XتZ`:Gv"`-$w3/7A ^F) `_XR '@SgCR|{pJt]@gBxErCBgQjŢUkv6gCb^s*ySxr1B-`0M_)g;1p474 "W] Tz0%XO Pfy3s2N[Mg4(^ yD~gt*VIk)UXLW{J@(} -})G(ŒbU@:N1KݚtWE6<;w@4{rP5d%(f2qMI*%Fr N1Lup.eZbVA 0%jñ "v jQ/4q@ܺ#RҽC.NatTKj ,U"K -(# -L2/b#!o/֐FJfDoh)N̬j ʙ7(%31WLie\# !ϧTV4w1huTr "q<#+*u -J]o֘uT*!)_dEpbMl,2XRS֩O ìQq-nŋeʉJxƷKQ'gL[YOcl!{)0h"c~/X*,Ȑv͠p -#XS=+m kV>e Yü}1--> gdс R8g(KuA ,)8UfRC}zG4[jW2fa*/bY Vn'g46QЬl^s)'d|9V(X^5tB;Q0ⴔi -MdG(d'>pO,RqwEDo  I *#}`NZVɻ539G0 GU zR~H5ƥŻ ijm>K<طF E>)xd)DA=CZx8p8;)-R0j˒G( #;TנU,)(h 5E6u&f 2`m,[ :-HZAH%%{"W?3,ۭl$N`_<=* -m`:B -nz\}sXGI ϭ^( >Ov%ɁO+*dejw=|Da n-+G9cRĺ3@{ C$jx߉JPdU{(} E]^Jӌ0|/Ҽ2{9/Fdt^p͎|-0oPp껚d3J!a1~nwdP#0)D0B!&I -̶17ݠ缚XմpÈ8,38d#8q61&'G6°"Bwi>D`hgAG -7Nxƭ!nbRL˭%{#o:(Q?,Y; -My)?~ a>lg  Oɑw_!qd=<,%uvS5}!}zۺ?P`؛sAoDF(jG4g<jګA2zZu.A[IUDέ58}vX Hr0A҄JgD[Hpu3hKwTTc&WR_5_f+/..\ɣ簘*g\'k>Ą|u zny*jN r\"~S:"fZ O;'#.KiBlu+"">!eWwpðhY!|2ףEe*s(>5Mp{]O2bVժQEe9 ?PȑH)F0]bT4S/ ZF Ky&F.7Hl0BE=w\S>jyG3}"XHAhaoͳdž!= -“ K*KYA(z -n"5>AJE&%0!$$yc3aY iP>sV[8n4/)xJ( n?rX :PpKTgř8hoC~$2"C;뎂e_ӫqWHlxb `fshm@xuer֪j+0}NnZ7\bBQö|_qtTW:- V -5 z͖wߘV|45v?/-Ǿ\4twDhk:,%iﱰ1 Gl]#s т1A5 8`oDii^ EO4A=X۲M_mTčCŤ7r,qAܖ¹,w{}!Iw=,!u/uVJ&[ :bNKVa` ^?>ԘSTQ.c\63پ;) OJH\]%@zaq1QM$B"A6&5}PyΧ_h8'/.QSUU -_ ({"a#2 VuHmmFHy1$ٚ_on0fP1ʠkd }m@G\VA:,^ -oV CVNv J {3%mr]E@6gaDo;NSnXTQԩ+6tcIv7 de粅[{pxHmkgc ͷ{갩5m$ S!\*̢72Q.{a2ZS`/"0̑BUi8P~` zc /JO.&W(op.4~nlntQKT?r!a*9 2X wC8 U AV10 vT|S`ܪ|Jwu> W0 3L~ȧ)qP$ b2&[)Rp:bp*2&Nkp4t&R}2ÑI؅dH,/'@u /g481x4z"U9ߟ` `S)t{ՎU|đwx}m\SJtq'djzڰt~؂)S'_W`]Ѵ֦lPP~-{A/u \pg6rȳ6"%4o2LʶM"_&:1D[+Ueop8e,-up;8xQI[^I1}@ -rgx[)uzj[2R$+dB]HU\P3w(^I齺a2FJmp -8>;q©R̬(Lu$ P:A(2[HODQ!KiAׄRb@ QAR%z P)x$0 #*2m"J{i&ڝlauZ.Nň'bV~qLY^*'ZCǢkr֚/DS&^K m- -T[;E2>xT1^I 8a0]yXr(%gJ߅-Igb҃@k3BgTQ9-4=VW [_IAd吔@l1"4q"8^GZF*e:K{ aQiܚH}ɦ4oa|IŞ!#ztD%Ha CєEE$ݒ]Y&(^?U.s߫:$ ~z=CJ HkaOz6>VXF6lFJYhrao#Uh yN뾃BǫE"ѩ:M]AP67ϊ%G5CqF~ҟ/ 4Eγ/ x0-Jk Ba|:BWBqY=huP8CXH⽉89Uߣ9{)S!|ܪt.#gD29hY@5 -ʗL6K-ғka)&m9uyX&mT" -j݇MK1 .C}wCo.RCfq7HrvՋ֐ud1;W[~zuᓷ-wVLü7I9X%{!SMGôҰ)bk'fBRTh͹ -X/"Aofe7Kl!Ⱥ)Vڑۆ,{B>OIˢ[ǖmqv6VXd_NaOaZSHD +)tmI2[Vph:7c))M`Ե`F7%l3&vqE~bf;y3ܾdnC0ޏOSz|k{NA %7eTB"F?͔D`el,5-?Å<2,(t xa4Upj*]GY|f֔Wr -_,) ΧnKW:7ؽ % B&Y_v 0\D~B9Vw)t+YO{P2>ڷ[o9pS%/JШ%4 G޷>2ͷm6->n|6fKMR7RBIY d7мفl@hb)h;) -`2#EuFZI 4._?[8%k -ArQOD=d&K6oSM-a7n'pkr+c~CLo,?~|W N~XRQvCF;@S S/24ͥ#A"X CTAʑ]Yxd1'XӡxGXB~+Ed2pRo%`Eid`hP]^zjE< ^،G͏З*}l^İI iFS fKjJtΩ30>uil69gAOXN9QWXZU"#0Z|_kQٵ.Dbŷ+jŒWasuDrݤ$06LOSr嗌ǾU6_m4ڰ1`y<Fm'kLm7IHhEnhqf_1e -(SSV0s]̸ZƊ>U;eDFG̅.TEΟ.l"y #Z~cJb6Fl&|R9Xϵ*9.4b+nd[.%fNV(q3lnA%R吠 -ھ,/E}lݞmcܽjmAgmln8=cmȅ[#|&:Yl4mտdS^swKضSڝ.mt|xI^_iePA[R }'Ծf_Xc6zh㺷lAO=יP5g7*V0Em>N\e? mN *,8̀ݶ0OIʡrNB8smi2u2Ff - ^q^ OAM p T~Rw).p1 2&hϺ_ -1Y+{Lr) wW6w*6)7m0rշȪͰ \+oEZk|Bxl; -ڭD#}n{S/q!-Q(\`ݟ0 }O MFHy 05u||_.D=2W{C c8u= d[:!~#̡ChFN!4 Q!+iCY7dqL@.mjcݥp5k{fh{Kߔ9X~Ns^kϻjmaQ{ `%3T/eĞmz;àz)9=,VE{O `#vZ|ԦRr[B2l"t5r}]pC~( G_Jm;OerPQ0|O XZ~ .7\2>bc*"KSH]\Ɗ[S6/GIC1qՋ 6DQ?`@ ic궥Wm9 +'Ų]zqUCmqy6c%DGܿsJl=V9n|^~Q K"}pc|> O-y}u 9#njd#1|8o̐vbݡ*2#-Oغ_!)|VLF1)F"X7j=눲2A5ٯ-ANlPu#ҁ ${njyhAqDO'Wjዎ]U"&fP=@ڷ%h`Ya!ֈ2_^TuXf#Z*-3%o|o[|tQ,7IFLAΆܟ-"Q - (|Fs%YX);M!cWvC&]aP[`cialqiWVq&U]1c'DqYe3Y+G;JU:VV4/l\ev7Ds?zߩzӠs6̞f3 dlw&Ij;YemK653$h͂?=}Hp h?0 $iiqMMb9D^2m]3@@KA -5s&Ug QO`xajA82m *%HLjb:7Gd~x'IP1[d)7"e;xp endstream endobj 1930 0 obj <>stream -kV .PB) Fg|8,摮ۨYY9 џGS@(#0~>,OPJNK]}gJ'NWW Z -[hpD0+d2 -֣:PiBI+SsCey4`*araPT5`+k8P]VVLֲjkOm0T Dƫ X30ڦJQp3Rhu5 s(9ҟ}E{%WKX -jA%xkxjA_e$:@ǚM$#:h*Ȱ g: =mwZu\CMCfpOڰuU0 e 䥅Oٵi7Պ*ANJ9SIu)ރ=b}&1Mᩅ6Y1^#>RT;cӪE-Z+i 'n[Bdƭ%$'E xڲ -o [q$?6Lj&*Rk@pK1H%p)j˾jfNn .im *ez1? %Ėa-g;QA (k IfEscl6*_ )T)'AڝNJ;3o ->LyeIV+9+:ҙ5$Lb5ūIM{o0xcFt2,a=hCBTT0p1HQeZ䧭k?ll jg *B0xIv{>-Ƣ&joE$37VI;$jrM% A܁ -w,,#R6πѳ5܋X@0YQÞZGÈD?{N.RBJh/4U II)VҝO#rek*M9seûK7{r6BZJ& uYt=,ܺl,r8z@RN#H1oAK]d :E&h({+Ñq-4bv,5d-/F399QR]ts>HOmɓ~GFgEuFrlq,ǤJTr< %6JqMvi)mVUlLJT\;هGAYU#yTA,Qـ(_lm`d[%^(5"9ou kx!ЫOXq~'յ7s8TJ9Fjb) -2Gl*bf7ZG4 P5Y !CtJh:6P)Gl؁AH Zfu\DZ;K>îiiKA1pt5]} ;!# B`!6#k E P%@ҋ6"ω r:Q+HGN͘ZRH8ȫNpJ{`#+B:ɼu$߉)yWxJ͸ix)QY %P5tu%]<$@M>rx[ZJӇ/drv#Zr5]zAOU>|+)!<W>Q- -E@.E 54_ndKB\.f@qwW\o͠Mn}s/&0_ ͍Cl(uo vMyHWF=ŅeF<w0*}ay8AU`BimhS~3 ?@AX;G)RUFLJ -S$λ쪔Mf#qf6T6% F#֋j7#0Hhqΐ~*(A-稄 -:`E/u{֜ -kfv6fbćȳD ξ4@n)aEVځ|&S}C E惹T\bg W0y3rB'VM I -E\LӹvaD',r'$%stXߥ]3dR` -Gႀ"B+l.f$4'u?92KS?tp*AjW/Ǣc -Aqc%jٽY+6,|L#|Ygbqtw8: ШT -ԥ>OQpE/zXQWd l~&?NW@h`<)PLR])/LM@@Øк@P&UmBߔo$W/8ZH|51f)_5j_ gB_o*1ND35jH oMC Z #Ijϊ>_UfhEzDٴa=Ǵ@WAv8T/@!%/I_'[R64Wo—Fbw&۷=&7KT2r%296TLj}=/HKz#&ɝb<:l -l,'BZ;n*LѠ{q9~ioeS$d$̋OXc9x}BKlw UH_ A)N > ! $EZ/Ft1U#e?K}{lh9OipYGJ].Wޡb*0ESjBb!Q*<JQ1 kWJ=8I(>QfRk!{ۊX2 -n"eq҄bT=֎Ez cWN+n;);2ҝǝSh|qOU8ǻs;U*ՂJ1p,3v` -R.kfiRȝ ^RQCۯR^xKk?tSGRʝ?(f@`QXXU][j$EgiYQR -Ty2O`+_jhU·P(v*QSAP&eYnLarpi*;8]n![|nr:H(%&f_+M2 :z>ACng2ﬓ$;-i~X(wyLsy i"1{ْ-3@+.A68+Wp, - -^ܣ8) {tii k"īv16Mx]#pn">(kn$Hԛ2N,kEOQ20BY(QN*1#lUvwU܉"<$! CogEKF(ĬO \~Fe.D*.lKT_m@A%gM9-P&msd~Gm;k^”Sp5H7Vj2Rm,CQ6q 7jMFM 5*[;CAdmMwQ[?| "&v(!hуBiAm/fqi`+4$ ?=ELrؒuq -3[s|ԫEe"4u+|cĬ6Ø^,heS֎J6֗djnDHB9 -UέԞX9TBq&%a!,^NO0(deg*P -giJjCbI)PgJYZ[)XNrWr,tٓwo=.8D+~-/Tgw_7@bw/Y /gw[L)h*8"H^ ,7|ww _WOoW_2m_>y_/K_G}khĻ?iO?O??/ۿ,fPO(C።šd^4EΓTU}|ĉa) a{P WP!A'}8=ꉴ'MKw^T.#f Mkٵd6sZþ}p1jkWx UpgWtBCWDݕܢ胲}^hr\B!8EvHE`wP5(RP%Po.dmk].h&|ȪɒL]/?uRNA#,o1 - {`ul*5jntMU}T?1_`xA<@9C䯑/4H{Su5N>L@GTCE]A3}|ވh"NvBN0htiWE`ڛ"D5'7Ӿ4ט~ (|ONbI$\bD/v3s⺭FQ\ZfWMgY)PN| zacGb(YI@fY/\%@t+|A ƀXzc5ʅh!4[1Ҩjd$f ) |J&kr@%;= ԃvb\ğЋL^eq&)Ů@}) 2[١?\Wjõ28# x)OTs[tt帄JJ@&_vE f>$ -hܔJs\#bn,EJCe )#{s&Q #UU>MI/K RP{I>-+nYZȍlJ=A*JrвhU/^K[@*9BKPe9Ph,gל_Qu57\^o-c| -s,n-SL| a[P ᱝ (vek]Y^؎e47(Hyi)OIX?.yk*⁘>0o&&h(YZ'%&=݊8=(*'sQ',m_Ŷ - -.Gj߯4RxQ!;CW cYTڻ4,4Vu/łC2l -Ac)jˤ[*p&gNQDJm3fTΘMj8j0lfYLEI6Ayg[DSAa5dtŦUDZdywFLPbvmY/H-hn̶u5ׅӐJlAW3R4{pq*bZM6<2K?b0dg >+Ŗ -HV} hr]ѧ=/9-QH]0v/$sq;khI'\`0ْ -T;2)@ԙ0,pQ${f@SvozH} -ŜN˜"R1Y -GjGO1c^t.ɓ4P:z# 킙L]5fTd(*!BtpA./-E5!\RtHŎ0CpJ[c.&Q &9Dz(D1X. rbҸ5SsE%}u -jT\uYXq.\nJӋ:Iq,SȖ k'=\S͔ri`X0~|/7r@Z6N%pL'G{qy :8SBOgs^秏-Рᦼq {(c`D[dld7uTpWё_LaE?<ǧz}M\rӦMD4gw{l3"/Hh3j -$=}|!9B*î0o 3 "ڀXO ȝ4Rw?: $M&`oHr޽@ren_W˟?_Wwn0ыhSèE>Ź?4׿z/o~?g_ONOӿg6BӲ_/߷_N8 KiLPmg;?5UByB4<wLܲv9 u)4űJXT6?oR<wa GAٞ$Qq!D,~9Lq\pۓ[UYR""-M;o?>Ѹ?DOzV9jUhxxIo?>qtR>Kw4{h+vĴy=ǦnwukDv3IR`I jC6W~RxA@Q_u+QdqtDMZe J',q+u̕e7< 6rpc *I~mQ()B3Jw04,w3UVXBݲ=|`k)nu:U'd?b,d\iql2Q&i8䀯Ǟ 0[SWvB2+MgmF~; ̬hJ`F`"1!xo9H1mygn>Xkv=zj"&G -K;g(肻) -l6EЫힸSTG0L4KLyݖ7V?^UdF̹ߓ*7~k~':"ϬF ^sڝ_ y}\FTn]>? >#_2pbށF˔nLwvnq)o;W2c>ѹtMZ[IyV\֮};vDYصYOqW: r]bEǛLu@bR]<w!aaը٦  9ٲ@Hi!n )[0гǻmdI%U*$4>6R 3X.'1qӠHxI&-uZbyҋq[m{Byr]8A((d^&b^x:; /q3ٶN%"au^$⺡zC.2Y4OJz4 zFJ4},}Hz+vخ404MY'qg(Y|3]UԣtW -GC_A3]k#u~E#m|I|FJfd#\ئ#)bY -Dw]BsGRI 5o{##mh:Ҷ74(яج "X[el׋JP@"GRܛ+UMz̎^Ib%]/b&w}%z:@rY=Z6|"n5id?MGzGzfӑv1考]/#AnSR%Cz{ca vl;_2%^^.;ضdY/ `Ljv:,I:^Io"EBp2҂ gUc  -{!M&XT޺ ,i> =Kf-?zC,. Dr‘*Iu:E +XV1WTV2Na0K2#YKu* TBi\!SN0Ip5ɾ1>C^Z;])@D_JP!ݮ慴e'2ԠR8{3㤀0''pTn=ZC-'̅0/p0`V#Bƀ+zGټф vosK vH;Hؾa48(oB"7q\ҹ o@qE o.qW/q -jMz2O`o vM# |\btEu)G%^7.̃zPo P &)Xl -䰔*kpKF -">z3\(Af:r_c3u DZO#I% -ΣBTOEņ -ۂŪʊ2#,)#KEl{6.n$[8DeܴXh-Qv#mᐳ2LzÒxZB|]ѡc3kr!V&\VXW3J8w-H"~sl$TQxD IpMR&q -ȁ`*߶- b*M5:fLO~T%{8.KvXI\Fs~ ِ".9oH4>$.! d[9a)r ԰%K.{ń[ڞo"i>r;Q6%qFc+TB9 Ԙ^:F@x݃' 2_?*_eABji .Kd\%`pn"ᑂϞ^!%P!>.h$(r=/(c]flQ"Kv^-E)AUV&)KR:}^ɒT=ʋ8{/{X)H`YJ eLһ,)B) oћqؘ?C! [q2؁[KQm ўP![)%a)>؄ۊ)dnAȉ!#ZD5R1G]סTĚz7 P @bhbs>W># -VP!IcRނ"5,a,BvaI"WXZ֢\/sE1T 2d'%'ia@zpJK!@n8X-LYMT -Z\d:ד$d8) z櫊2=&*B61q UMBKSŲCa*5$<LG. -J.d rIݱ6Uٍ,GRA)ZYCCvX@iL7$#*Uclɨ,@jl!1H, vm0Ir[f@v.K`([$RgJ(ɱ!>8z4]RDD$'p['z5v荔2d!,NTdÊB0,: Nٝ VEy12=de.c[™˅ -I("]g="A`FWN\fL -0#+$ 3M\f$q۩HL')I\f$q`$3H -0bWzte|S,OJx긐䎺Q-Cr!: C&p!;] D6/9ƹnÆ䨋bߢ C_2}ZP2Et8u× ?ݢ{B[X]Z(Tt EOKRh-tzhjkC懐'{ٹjr2 {{8 ɋ,9S{6G?tQuRdDJ.rGŰtȞ/# |Wv<~:@B*$' -I[+MD{lcGDGz]71cKXGӃ;F,Ra}tk!q]* -|tQhp,O@t3͠ґ4MnqrCC~\\W8-!UGD i!K ᣇU⣛,DJ { %eP6XN#AFޑ(uj/ -VBsQil;Xת`|/8IHJ` -`u?]uj0aʊƙȼ`AQn0|5}dH -iMT zK3%=A[%kP6 'B$MJe\PeKQN/I\H8 CIP\EE|Ä>筫 .ճmzE0b5]#g*w9蒱BCWBA-ƇN+˜P㾽cfq"DazǖK|鋕 -[Ų]eM9%ePA`TDʠ/ rbIRE2rZJԙTQ򶓼.)*5)Y:U@)6SpAҠNUN HUohvCJ 9Q(u -RNUx$:Ӗ"iPNjfp%EWzO=/)5/Kf^UOR -RȦ[~ؘQrA`&J%^c{U,쐘g#XA! -Y![V`ҊzRe5] e9} Eni7> O|i7BwpHBnHkCd&TB7+5|&@+Y*# 殺XW)ΐْ"r |t!ŏE(ER8(tJzŗbԚ>K/UdbJKBv*vyG), U7W>~UW>ܒrP$EB+dRH2#7M+2CؐfV! ?BJa(@=G*ΰk4TyDVlG&:j*lWb&H`D.d : +`d!: fd 6#BY)MlFXMKCux)D\Ȩ&%WMJXb{?g>R!Jlp4Lh,W -o [P;饍<ۋUbI}[#NW9LK ;tY"Fh50AդvUM\Ԍ_e)GEHLJ|: w CioЃtDjMPWtHd%*x0[Vz@y@R}Q'=$+8N/_v<\H66y!$mc# [`G-ϐE Q/#_h46?_QJ6\4h0`rPdE܌' R"4f& -G)i쓮r#Ǜ(/ -$6Dh8(1 B@Α)c+*&D|.v8<^rU B0,eD8d9ʸ v$(C![пĄ5R{XL~wH!)I7YEz}^th"F|*$p -4X -* )ȸG$SZ~.TP:2JiT.TYT0&K2f1UxV0CPV%*$TUI.1&*B%䯒Cb GD¶&# , -(@Ba;I2c ^Ӥb*0;ޅv';O!$n@"TF+ +XW 1uEcCzEST+프A_o7#,fB! yj zQX.‘*F8ZaxƔX0ċ*FxYauy\w$\fR -gDGe)|)*CQY*r -Gv* JSeDȥX:K7۹r5QۨS mYӨ7?DjU*(L^Q;ywS.-V˵i"uN6AܡUvvUEUʵf=.pSg#v~T7krZ FM0_eAݻ{t~2N1ռkRc֫TMX.Ys)jgS{ywDju!ׇ/֏J(T RީԺvѺt":@LWCD~T>zL,!BI/4)TM:jUw~Hjr7Fu!1EDf) R8sZxKl藖7OAXH?Tz&D˳⓶;oGs&Kit -4#Epļt^k_+e!`Д //KQY)Yc -c:8c!nH?B}# -Yh\a1Gr.uRyU2R_ -Ra:N@/Y;e` VC-R 0H馍_ſݤWB iz@ aSrQ~?;GbdKQ\N q^kk.`8遀;@lOj '2Н啖*_p%4>HKƒԸ3@)6jtn )Bl.Sk7%ΑUd0}ѿ0lV۽LB嗯̇ -7S Eᘓ˯JuNi`._1mA5_AU{VG]IN1-'q|sj},['a_'A w5 -JTPTD:~#QA -JTP~ -2_SAP6?Csz恉is~XH$O'QAHY -JTP*_FD%*ר &D!4d (QA -JT/RA!J0s7J8iZS.R%5H_ty4~yfa"a:.ΊOخ: GYpʥc8A gfҥ蠂V_Df\ N,Ƃ?]2cFMu5 -NWPFw*t|-^ɮASW%`~lH -6Gs$uCֳ\'9M >σ_d;p8AĖEDa5Ol}]տMZ8-`5]sJ~\3]RZb%bŚ5mevv=5Kޯ[}¦fɻ -&PyFN^"~s^'Ul@c'˩!k0Ndp>S`h't8$B󅞛w;uzRƛ1bR6gz@ȜXs`tg+kꮥ.q)sNW76UsΉHH+5>А3 9{U06%9ZB+9oBKJ08?䕡%*W/i&?pwԅVY~$Gu#``Kg 7\UAeeb>(M] ٹHtr-9~t6x2LYPtJS(~:D%rɵiZ-X6ks\o ͎=Dt^ѕ0x Р)$Y_2SӔ< 3IH˵4<\(5ҒX'/o:mhk HZ?ȵ5)gi@lÒ圮Ǟ=OPw`Mۧ 679'nmY'lʻ(!5]z|ua,ȵ&גk&+<͔o`ZV]y~;̭شLM|YxAд?f[Gr7D`Peu8AD^x3nvV%h~B_s '\V"{Zr\K%v+I]zQeXyPXHϗh?RO狴HKDZl&ҐIh>DXۨT:rksR;gGTSYR)?4c4;1b B.=F6<CQr={TQ''r^Btnk\ըx?]MW)΋ 4ptd7U4@SW9.qxeC7)ϯXǛߨ!2רxEE:=g } DA=TC@.s~-^-.!ŚjAD%މ~VJP6L4 %JGDHƯ] Ҭ|Z[YE/^Dq 1w۩}hձj^ޜQRH15nt[m11^}Tk7z\ڀ3k~ - -_.>ً_/.WKqIؠB_*#7,:W#+Q^<ƙ{OKI~I4 /6*܂P~eBHCc"CтRh`Y 8.>6k|ތ$h62R``i{=uIkMֺ7y:uiEc6B;go~ShHЬsWk}MjzϠ(BM EGyBrm^erZ i;UGDlk~+ͺ MSeZ`&{֛5폔رkMïZoN{[sztD$'1{/G^wbk -_0^FHu3PMęz_2Yqxᕉl*_~-ZwG ,׶(cmYf.xK A\ZlZ -45Q[BM[x:TOo4 0A>ۂK3mYSZ2~(]菛7+Ľ9ğF$y^;M\:s2>nl80-bid3d׿m-/ϼ}ѿݙ[ %e`1(9xm'{KaIlcń: e.5 k`gx?2e6/37Yf?4ݝN5ڃszvX`G.OK ^89TWsT6*yGg.Yuq(љG-s$ktMf%kMk338EUv' h@'Sp-jlVlȂ8~yZUP}5ADŷ!rY  A>iB*຺ ae&\SS5pu"ewηR~g8XV9 * -yևlh{˴K2l Nt g-FCmT-芨 )$S%NqO0 1(DW FkA 5iԈ;<.H'f*_ ޲(PW:tGD?;ރ_Cvێ:jKPgۮmRIx-8Ζ%+yj-3̜bҁcڦRg V'|ɁH.bY/Aˇl@y)͌<\_ 8ԯmKDjțrR!#fĘɼ1!0qdA`Buxh+RRK%ȓ~1xk $("W! -A򃄸tm@.֥BN/]>~mҏښ`Vs (i6رtK!iDf`88tAĂxw(u07r0d^FYm0_$-ɭ{ q]$!E(N6/a W_-ՁF :¨vxs(rL%hT&a&'a%? #_L-T:yl!+9,?<[KA`.SYWgF;Cn,הHfcT ʾ$j4亶mH91>tGL.;mh4nh&i$Pc3RBӑȯk$[R4]A2<ܧ,f+1V>۠ o.rz8/r:*PRvd}u?fF?+ҹ'p4\P78M -2d6]'Ӫz1ϭԷqwR.&&4,D2lNFnߩyd%Vw"5/?Z -3?;2r/|C' sG}GPM._#Kħwba!S|,WJMVNOѹ̷ZiՅ.1K>(9ȇzVNav|'Tg:j(⣏Y^-]2vWw'# KHR޺4` agcϳ?iEz%F'o|o3č}".ذǑ'ل3NCSgCa28ysVՉغg#~u&&l"IJ|og16茸hl:3q:O&~tw^w# F#՛+ >{*&1oHJIʫ?P|1v{6rNɲpRbYa aVsoWSNӬyGxD'Np~)wlBS޿68Q~LrI)78 -<}-G+\s`؎kjkk]s>37fc89\w:z'y'y-O-nӁ&Y\;o)q __}Z:=qw83FK~\.S8&OTN0,rSnBی{4q?)uN\ ;͢ڥ=u'-7=>SwNǿwNz/wv^{C';z*vZ`6~EwJ8ӇBY~gJjj_3y_fO' <ƛClRo?˓NV}F;uɕk<_km${stQIQ>WVÂHv(Di)> 'J;QډNv%J+~}Y:މNw.ޟD{;nEDZwW~(xqwZ}v;z,߷5T3ؿ?i*?VS`*k+_~skj^iו]ͱ,-ۏ~3]y7ѯ닱Tﴺ(IGm~^ po?GuC{q 3͂l$ǛyXY'jd ,H6 9%OyOT/fAY?d-,*dj?ͭ7 ,[efAnH ?m'$ܐpCn&$sK Qp{ fFqDnx95ܐ&&&F!ڶjF=&&;މNBD{1ŷD{';މNBD{6yzNwZL&;}8޺f!LnOw !Q߉.IT&K8:lH7}/NBh@A7qrRzN,&Lq,<*H ՚Mu:V1WkJ82/mӨD0l"R%8Y jx >V;MnU MWv\QTLzѡfo=BSV5_2}lVBkϵzЬ=~ʝ*0C1T.‹HYQ ګ[EjۡZUVaqd6R`S7RN.aQfȿUw^ot*- -d~ՠJ!AjܥfC0Ȉ^QiYk{b@L]mTz@dx3%ftt٨bp讞]S+s:7H-F;dN߈k|1 f+C쇤T\SmdLG)3~T,t(FN ,| LݮW%p;uX.>7394F0^zrLuqfJ%[g -j/nnIm%?`'+ Ztߒcq/&#y T;~TRXZk]i[fY;@_iȯ4A4{PSWJ~ q: ;U& 6jJ䧾3\P+9GtEh+F2 FZ,҈/FVk_ojW+NUfYkvb,[uʵR'; UD!D2 6!U-/n H + -htu.ԙ{u #W%&W&-uՋJEXp*5}d|<[({dC( Pfnv_OxGGm4"S?!ÏlyEB2q'YW*crni>ԭGmhyFP3rUϓA0XĻud8Z8]bJBd_H7"SiB@i6&=Et+Ubd^uT.s\#agENlgͦ;V$JoGg{&RE'I"~/$m;-=O"I}VDy9RAZ/`VԊTk/k/ l^PzyB@5m|lW%fdf3\SPX/UB"@'IpGih*zX/VBk#:mz5 T+cI&ee\'lszabJheOK@*X²5[XXޑZ՛MCvs݊u{TTppKb{yL{nMll欰AW|~.ݍz% ovKoꡪd3YaV&,I;{pǺ+ݽ߽U{_鴳@^{$J\i+5c;Xbd#D֊FreIU2zblXe$.$ź[J& SQ7vUo4JAj CϨ.^)FxZ$bm@>cX+ c e5Z敥xm1]j{D X{\4rph&/ٌUp0*1Cx<]\E )c l'/7*1Gt<*4rP+.5u\&k/^u ٸQcZ!elk= z sةռrQr KKeAȹ&BuO35,:U4F&ȣQYRB뱞?zLjӋ0]ky8~W12EU*4z^|l֫~#+ -Y{ +դDK$W-VU)}ḒBכɒyEKrl =y4b9+]؂Fq—0Ex98z֊ iyxuTd=ZfDqvźMYZu@+@6qDţs}eҒA8*2&+|Þ8 j(ߨ=\\|^LOe761RGseorjlil:idVkfapz|,[f[_5~1{0r}Zou=>lwS aV' l{`$Qy+9m ,xX!0Tʄ儠,x^Klc ٴx|&\/ҁ:_mLdl!TXKCWѢ Od-{xr/YSGwJSH,shIMڹ}Mh`܉rN{0_Y(Z^ fU/sX3{Dhn1yjJ>~u5%:h뻠0:c_Pь5HD~lB?R)?E8uulVb3'#ŖZo4}x|Yq*3DA 7x\X\%~q88ADֺvɏ/}? 4M+E;S {$0w*,oW׳)ۣs>WR1TP]mG)9w7m/WjѶR<_rѯ˨[ i5=pk!>_>mZsY_O9ZUnP(bxRͅ/Zl>rl4tHxp4 N59e32+H 9 t{Gr"l-J'+?i*⁄ϋ]u:,*o%D  -\Be/9tz_`1tK/$!3x'f L.[\ n˳ -X%,CC+G4g\9793 -g0x u4"ӽ\V(ЯdRJq{& -[^2PO.[Q*S xL>hCC]lHH¦K0FV2=a_dBxtcTo2}|U+ҿp5+ ;Y4n~ \rϫg'j}2ɜlvLwbCC{Xķ@)ޖ6z:6f;yB?}X]7[Y*);jřB=unh9|qx.cMo3w92VRbٓ\7و@)mJ~OͳH_Ml>s-Dž,%dËqNzpMI QY}ũ6 NF}u3COAW-ς8ܟZXc]`r2s0r&氶1x}ln!#?][O+齓-huR ͩ\I2X#[{:XA"5^k ݹԂvIYf-}TYBÕ=z22OxGz6_qp< ,,Z)ߛbc: ٵRa2m*>),͊^p!_U FܪC9kܥc73`⼺9Ib_8{2룚ܹ׶֞ Af2WC,YXm6O`A[Y]VwR{zd4}5:.l͈rbzarbD 7ӥN#xw][=;:*#>>s$>lNRr`~W鬷 (р85˫W^g˭ -3~φz˗=D^C_nUHyEk|:KK[\NsB~ˮSe4S\=;[?ӣ1;F(*s=r0=s=.4֣;$u/#9j}I|wd4TMkڋF_ d |qJ6$j 0hnZӯT+;󬗚B\nr{ -΋GqwGnGs_e:9>1N%0xZrDJeiո\~imDA]@_(˵EօЯ$Bt?_;A&t;_RN ,\/Tt-.셪,$6PO/t$>j{r {&E-Kv!b}]fOWBv/l~]YW,BYc`l`-Z*|__Ua`3"^nKז7q<邙n^l;szzo>iȖ7js`J{v.GCtcH_{c*s-<ʆ%K s:Cb6ZH#SQF>\Ȧ -Շ/KFڝ{NWIO>ox\[ CgêhސdJLLZ!.uS{Y\Tu__, BHP[8Lit#lmvrݧg8G9 -,z{l#WS?V{o9}SF^'~j -ʼn/#'Ͽg{0/CH}=_`KiAGrA\=*Hapk9|כ1 "0v+F|bU[[Z4[֞{z@nrcX^ BJs|bpf -= -T".X4Tخ勰 N k,m x<-[zw IG{Z -Stc||hX) &̭7ulYW+dYSKvֹT/ĕ`\';X^/6LT!>1Im1قg=ZCxWbohv;7-_|qȺB`wm(/WGGdP$v~AyX8OM!^ϼToy-í/ra6}X)L6x/ݼ{Kr #xJKCٝ+/2W*T,$#ʪ!vZm=Sߜ9'ւ??ZEs>!/+䕢JNڹkZa4#w=BQs\z-<`?`@Nhza&C!̙/vn6{!uT3sd\:Jv=xYqZf+xՅdvS^DRF!}]mM|mm38w/Nm`.58VO4X(2nI&.k1btjiʑ4}T/"kz&nΗJyMg*3׋5Dv&*wUg[h6}lB "xvMTr,nOzA<Ab|>ܹϫOh3%d|{TTCk݆%ʌ-&sLڙ\ZQ7s0 /L1K12!׹H!믏'H̜TN..|waP ٙ(۵)쉇H;Ϝ;̘_~HZ-oe ȹZUx%^柗rWaD?ĭG:ڭ ZmTg!oÅ񭌃1]-!< $% Tlh%52nӵ[ns^ ^[?濞ܡf""VcbS{@GfM[W=Af*3U75q+_\^G]6'/q\~:}Xl1߲ȋSO~弶dz"vHo7Z9qdr ߏÿ ?$6njS+wscx0o`3"X{^>\),CqpX_C;8XFng:G;_s3 x880[=+{F1v "|oHtnnneg}lP0OoZp~\XJ/ud1Tvf۶MNhv E*mͅ12͙GtZN} -ϣZiT'nWZktG ]rϳs-gt/+mktl,M#p2{ӼzwYv z/·.^xى_q@*PD&W.V⁚Zvc/UyBZ#ϵl76}{tMK9:0Fp{xz=!xe\b{.(@/m{F@'C@FZC~:fk7}yuj ϥu79mg%@1}~{ ha -t?28X3P\[C[x,ӱCWGwHtkVc11rRX]\d2{z6=le+tg9s}pu jy%:קsX*tѮZxO!8lԺ"9r45/W7c:3CW38S4:=h:7 zgLŶ7Iuu|"(,;K0V}tmLf:ͻ@3*AJ2W4ێ!]ѷfBpaRMc|n -jl:|gfe`6ޡ{<߻A^~=?Ыݾ>j#kD#ћ`t*ۡg'*׸T(oݿ>zcci0zQu{ۻ_0f ֻvGOZw7d̍{}h>ͣZw*rSͮ_F_~;rmXdU Z!,_7;FnGG7MvO]n!;҃|p]/M9Ϗ/NVbxU߮Mf˜>T=V2s;R_-@*z2.1=̦{<̧cY\VfΈ&7*#<0%_s-0y[6P*YӑV܎SC~C7!{s#?bf{rCY{bJh'`Ij3q,`TEkpd3%  JMg 4[Ѥ8Z(HPU'33 fHӕ~GCoXoX zz_',zD 铙B׮zaw7qVWF1wd\0Nĩ̅2#K=QsWE{*eo -=2JۉgEpBZQȪU5x~~N*+Șt' Lz*d5_Sߪn{amQ%*}ieo1^{~[&TNde-YUw%6J_<hAUpԹަdWir[+54]^s;_n&d6TV5{sLwkm鮙ܽux+58)JhAqfaqҎe˸AK`7yF) tuڝ iE??wߦoQo({ż*[ PWum7+1,2+Սc;'cܱ:K[_&𱌀]Mg/*~\LQl$ehmT,έZkF B%?[*^a"SZM ['}>3ёj`~;N.N|Qۣߟ~f }1j$8 T,vt!@[ޟM5_mhׯoŖwmzNVo(J=[{=Z9SO{! W_&+8wG`[ -^ [aro, s(MxPoLt<0]~X;x>Sik_ob] prl/CX?,{u L5`:N~5e:}μ1w -W],KSCYYV֒ଚo`Uk TeOw0wy[/nW VY[EbZ_"LSk:+eQ)PFI( OdT߯,!YK:2va>!ZmeF#w9G;Ztheژ;?Fhz-^h$ 6BVj0i'׎vgF.xw4/Wxxqت.A߆]E #k|[Z2@} .߿Quz7io13|% x -jT{m|2yc0w% 2ݕ^F,sp$٭o}V]`Rwf^kݰc$:βۊ˴ t)b%Ŵ](]KVzeI/m׆G43n+[קE`6x1 LĨAIt[`ebn $Vj%r7n>e},pɅov&7?N.? on*N*Cts3'!AW-8`]s]6[p 7B]s].[p lr38[p 9tϹsJˠK5s3XC Hn0{:V%mg]ZLһ?pZ|W4g$}ψ5 n}l9z}U^oS|cjRU2P\+^Z @u6P(ďU n63seU i얏0ȘLg>X6y-_[~pp`jO Y-d32 Re'_c;6TY~C(J1ܷG-YUT+F fzUeZ6 ,#)]_Ѫ>5> Y.OrnRwR4ϯo |(E'rb!Ɩ!z|e 2Xocy;7M"{m~B>^!",[լԟW- -C RF🏍}j|}Z+{uc_i:紪FTNh$"M5Y ŭhFVZWBN4.O^PsɓNIj#BgVZly3R̼S*'Au3*o!m)G3ԧ#ާ/}, *QZdrS#Ӥ 揆)剹-ְOA)5GOkRvG`qXdEf(58_+׻WGd[dhІȔG `H헟 d?h.9)zDp1]ZuxyaaOXo#,_]n 9'rx:3}bzۤ _@Vo] }P-/nr*յk̫ˤ6d -7>l,Rތl\]^4|8|e1up&* -ѸFȳ )Bu|pZH-**&q Jޙw>\dč;o&Nly*&~G9@bltgMI("8c*E24hH73lj"\+ER?n [?)KmjyylG-j腡L+b 2l7 S*2gU#Gɦmc#eU էBtCfUiһrltUtY}1e cg5X-:ztUWƜMW-)!=qd)2lIʦ5l\~|+ =0_lM]M*9Z&>Cz ߯43xyݯ^hOgsSor+XD\BMsݤ -Sj -Zt;v&5P)URr}jh\2q5iA#46o̥ݲݍ.S窥H>Lr[*lsW.UDs|rhgMF+U6w}-ؘ9m$OvΣPʿJ5b/M~0V5G艵~Ĝ:ub- _t7pG3*ӡs 7K5"0X9yt:LpA۫^Q0^s-ZI߷iLmo~(j[MS~}lsMc>*CQmOLl4}[QV(.n-;BU`XQ,qk[z@#u[_U ˠj[Y/m46{Ɣ][jp@t[25CiF94YM9ذKD?dݻrZ^Z|zs/^<8_O9omھuiE.6fl\ϳuN/on?͚}خ/ݮɇO;9x]yr! ϻV^bϾK{WQZ˥~Zo^~t>l\ ~jtԴi{G/d9~{X6s.[n?|4<<^س3v>~%o>}ߢ{G7|YWk.>0ϯ\̟~sW\6x#]?>[](tg_ vk6Ijfb?pLc,&6ԧ$CjLrԦM>YZVCjԙ>bBDh.uS5R5X3RV^Zo@QZIۉq8-7Rll]u-^-LN(}au\&U8lh^n:J%^NbdPn.ZXҲ,QVՊ맵k5(@|Huwm~fml:Z ::fUuMv܏֪(|NGjz?MܪwHVg1B[LrdT_osSʄ;oujLzE*gUC*`}ίy_ -}+ 0X.:چ -h;7"wo?vl#xW3X)]J:t;6d`uHZ6DlX'60[p,9l: 0* f1v,9ر{79VfLu䡲kT+e>Uޔ}GQe -)_li'9#M:SR؛#|ٔUQe -TX;)ER^8jCת6}2KCm~) ׯP*5[5U#_cm!qk I-W#5&ncW@ھ~K+.뗛H@*5/*Bu~MUoLLZCkGx--6~f*P5kVu+W&#!}~ͣ?~NqFI|/fׯBtvgFW C//Wk/7u,}fs5==_$Wwo=oOb?2ׯߜ"_~ Ҩ7IޏMS XZ3RG3lI}T#ksjpl~]FhkׯE5wPYnY7|2|_+#+]ꪜ ׯ9p ׯ9Щ`S_Źd5ׅkoxe~y+Gc\U],kĈL5 ׯ9!#/կ$dW/ ӧU˾~deOiȮ~͗Ccb_4hO^UG|J}Bbyon̓۔L6[|r8n"M~kWŭVB>Gwo{oɗk^]Y8^K}xgD7]}sΔ5`/z}=4ǥ__(|ccs++_zlܞc:-|<+z8gϿ\~;hs;ozbs;Τcsp[s^}~r199^?!tj>}fZoėON\|Yr<$_zN|ww(i H<ݽjx3hgWq.ڵrF˛o^޺dq>_>ܟG&$9t|{jqNݵ/+?=΋ CK{I[3K#V©ʛ-n~Vxyҝų?Y<ݿ_tm۷[뇛[ '?8?a}挚jʸ΍^ݣc`݇_̭nn_ে]$΋Zl- aL8N'ӎoO{~N94boOM^\[\[Տ"72?u6s}5?hu[egy{umYXkwQ72?^YٙJ~Z^޼p?lYb_ƥ6{3?tҟ2#CёnhS؟>Lc?G>}WП-dlW[=C{2y!8D{tyYm->zէ__z6|e77{%ዩxۼ?8 C~L>4 {x#z?^8 o -ߵg@uKz͋IF[G|\ۙ}PϞMzw`zMlʛ -ĸ_iNԫ;/`#C켹tqw7:κrdƿrcAU@} -٧+AE$Οa̩obf3駯K6 b_G/9~}1»H\XNov챐wʃ?=片J?M~Mڄ9<(Y_' !㵌ʰKw/kDUw!fCZbnTgm'2ҥ[׽o+kY!$KkkS!Kf0ӻ2 LN ʖЙa+:yg93nͮޟyw`Yn/O6T|\B%1ʮy}m7nD`L~{r>NoMdR$zzUN翹[QG^{߽+ڧ5hw=0V&H7]on}nA"\f ޞHU'm&|MhڂkW2[p{q.Flhߜ$ U[_.G}}ou xCUق-t*<ZfXJCJb9FBgj!-H3ո oӠ2z`8=?_ }Yt[,ÿlf {I~՛CgvM[9!˽8p9ގ i}pfFEql!Ԩ86;:]=+Mss(.c1cÜƼ7q]Ybx UDw]bV6g>3boC%b^72Ƣ~ X^p prYA998e,0H;2mn"=WApLo;eI]nFҿzFk?[jwϳ,F_XϿh45y&;sN[2w8T]I'Y;[=Z7ӇI/__^|J͆y A3^;?f,#uoa X9sYvO9%{TGX-?[t=s={â.s(O8%7^f̝zOg1T<,콵?Z86n#.W߿>}q~7OA\/Uߟ~ 65^pg ׋5=D*$f1/0bQH|Fړx_*k8R?+)U~$kyZk+ &]hg?_}m}O~ J<>yqs{ye.][[}Δ,eYɃ#g#ٽ\`N˘a~]LOSarOF9)+q9HxC$y5Qij7? |z8gXeas ͇4jĢ-򼇦~(v/L}RW,z1ux}c3*{dOFoKxnul kc3_61߹Y{_WN^w_,߿ "2mj@w>[g&V׿޻ʟuy.L`<59vr^w:n%]Y٣,vyd|ucy rfgj;XwÕ7k/,yǂ4u7WxON?0oBk-N޹.=ۓ wG[&|;rEȵxvvc~z̜#rBϷRW\W3ϛ. - ̸>}yOoYs9 0`8hf#pCa%Tx]޽ݳ8{Ĭ}C5Uz>8͞! e~~{~<^z7'sWϧwzcp7X$cKmDļ>}6Oq&z2.]=+ohVwzWk (y Kbhfm7bxm=bRG}=;/WߜtԴ[^go2={s١n.}7^0OwOԧ[宦Oh~CG\6֢{[/C>fuQ"G?Z̮ȏn|sKd0e{9?ol=,g.:hI/mϭIRDܶׯ뀸MbVb)boO>}B?ݼ_y{~\9(8Xᘘ~bBgCFBP2’_?1Y9=a˸T||8w''<|ݳ嗟l2C/%C|*q鏬~PTN>JAҋ\l? B4[^"[ֳۙGK | :k&TMP.ܺ}=p%'dHdM`cۄ`q.a6_{[ %+y\Y>._MpJ&O,aܐ\ 9͜ѣg.! q(52Z+$)Q)rrp;6k}&{Yͬ__%C\+ۇz3Zb{V^|JR|?7ۋ^.=ᴈhFJ趝C H3}DzOՅ,&[YcM;1i϶ډ9f)>7q$u8EpH5[/CdtYrU0Dw5͢}NN{Ч\svZ+Yna6XCLśZ3^)~z凢M},i'A=+bg{VDX/#{VD˯#~_gG'~gEL:;bϊ(6Ǝس"v~gd//#J`,`,ApUB< k4`,TMչљpg~Uؙ"ȋx,|N:jBԄP:Jϛ0o|Ʒݓ:{߉RKLJ3wX{=?ߏO&ͿNtx0xbŋY8*>h2 $w:]=x] -&DAUg&'ϧw(0Tg|Ola8ld&7h{ob_~ k5 Vnd3qD@UDS]O0UeuMdb aWWrY n7>OX[ ~, oImCL3F q @EӰZ]*yy^@SLS / ʄ{bnL_ aw#%EAh^|40,y;'37J۴F/=?R*z; F05 t金`m1Rf` mlI*SÓ#w&d'('6Ӌy6raߴf:r@k@iخx?"|d,!4`\e9Ն(ɘqN˃1=x$MD`XD!* KW'ҧu qX? /{eBۓ.}5l6 ?"u &::zbi,kCU/Nybfigx6õj?zV Uz(XH׷Ҁ~C#@zwD1?"sqT9MhGPpKGCw"^#(C4&kyTAçԣ@T 1HS" H}pMdPM8b(mYYoEK@>Q@ŽW߾O|N0DϺ'VpQ1߉(MQ4ah{łD ahu(pZh0AZp)Ү*Ӵn΂[āOH:>fu㉥ IƤK"*!U@bw:(60!pwh&"݊zj3[ RIFWKp;b“D*"pIxh» -oEXxphҚکX|||`dg9(->?=|K? ɑO;w_`7yj@#r eǘ2gOC ̥I@5$}&g+yt[:B:ٛ8;59$'Fj)@fx,z^ KvdI` -I8mH<"" XI Ƀt$g(Bűv#$H$ed \6(.hX_TLc= E*#)f1>߀U "WI"fg23E2+O@Br112迬J\buLZ.ƙhMq+"YV"(w)@`uǼ"gyW:th-N{ĬW p4wkR{a> oÃ,;2$ kFCgXx:IID^\Qz~܍1_,znf̽qMt,H0 ^AҲ&@PJD?-A&))ȧ hR3'I@ʁL*?,@j;4 !Zɽ3qoIg<EpJSt̶˘ ]GF+XKlGV;A@hN$gۜ N 9 -1AĐe&((t rkHlR1]},U"*ۈ@{l(wgAuXsAfM~*a\D8ġ%8 [֑,_ -2 HHa "=Hfϱ -M/Cpjv`bqJ0ȚyHGMP@ - -5n05'. \NYUX!5 yh_Y@ɢ:]c#?y澯=$M?t> DNR1.]l~ręAJ׊+E3( # -Is(0 NTvu . vGǛz AͱZ q&_%ʈ D#3Wh:u/B@'x}qY+%`hۃX.,M# YC:ũK,U`r-^{yU4"G"V9JyT]>[r01ܫ%ZΆ[։ Wǎ#P^G鬺ESšJL̺%Wfeɯ;A"&vB>*^vH({8y aΑBx)JRp{&*b\1ew<}`|ØLo$tώ8uJ"M:/u{U#!ĉE&֌D?,-f!1$F&ú:zxYZnfYI vf@66[jvqK_ys,y$ V<;.%?Yraldkc'1M&(pW9,M gXB# reNRG[ vτԉ0d{Y%b.ü% -=WԿa`~#3|EȠ(఼=T"ֱΠPtr*Bk/tU'vK=f!zm$F.`Ky2f_g_䋴dr/g&B|@Ǩk;=k 9:F?.?`[N?w*b~wjNv-CLݳ݅Ӄ=9Qƍ3{_:?9p8X8̃gOg'q)=ݹoYMkɌ_1zܼ+JS+daiwo}7C"mk̮/'ÿ>絿e&^^!L'^^σIe&^^J]^h;,iQCФ^ǕοI>\n)`xZ1;9=8eAp| 2D Ѧ?ugxU -K|\(, -˅r\(,/](,b־B{ׅ2*,UXULWjƒ$~셅>ބ9_żr䕫yCq&"oT;91 $M`Zљy<;q#Xᑈ.R@ 2 dEQ$~ t|s?i#&6],|YnCN9Ue_B4! cUZ5K{Nk -0 J`?h["SrcMZIJ .S.%^}0?\. Å ÿ.A ~0? f~PKړHMgSl&6DwVw^(, -˅r\(, - _Di -˿Ƥu\ -` -KPOaP - WII]pĄ=/MXҴ,s(r11*?hnÓ?iY FzYy7z8C38uа`L" -Hn܄JyJ -]:'6aK@}_=iO!:htC8B<猎۴ǯH)DGBq3;?:f2ZtD8cVIϡs9w# U}n-]{^UiE;M>E-zYVTkѳ+Ft2ԲFZlы枙 6S^.h/h,ʣa: k^oZ}u/*cas+zOwx&Z@,A[3u T Z&"-[i)\ALWa J^nCRxQ!ɪTX|||`du/G~f$'GSz?ts~ n쿇;8iWAe\gPFKT:q#nd|@?߲笑:"`؍CNCͮ jׅk :j1/.<M"Ɵˆm]x [9X12\6PZV.35]ɨc Mt1FZ((G`${UgltR&$ htX}cyq2obᦑa'Ƶpxi \G'# -/:/?gQ҄eq^dmEpYT+7j ~jrvd\󶋦t<ِH+v;reTi˃1˃Ijz -Ȍܙqč  ̽\?́Fz.;Kb^ȞcxBtČH'׊f :V>gX>D=TGŠ@BnKBr"' 6H`0``漾M&HV9"<_C`2c]yiO[X(; Vv–AB|Xt@$C7"}"H+.c]Xrj -Ʈ*Ae#wi(DS++2*N=2:ReC*t5`Q%xiؙ -?AqZ3#WR!t'hN@X!K7ls(IØڗGl60u_+-LvJ0{i4`XxvfOVܸ,F`MKmuB -|"қ6wDǖOϸH=4WŊp}+΀# vWIN&Q5þǑN2^AoEI}/"}cRA5 rc׹פ {U -^ubw  lO|;] ,@ol!a#2ᝁ (0P;Qr1_ x;j 2 򝪔!LND$0(#FЉbD/LfEb dX0DL61GZL%-LHG;L-n(-<"ǵr BXT  - 7}2(h|a&.[%IB7w6]b65Q8L(xVvZk%rC͑YaN^@ .iy kB"B{Q,) *:aع<ʼnCY]4D賾 OrH೩ Nʦl̽0Xmʑ IycCsu .3?laL+v4P1s0'J}V б& Ї6VzpZC̋Xq$* ׌ƻ->__(jJAfe[ ̄bR7bCE0%̇/=R7Slva-we] 5́یn.&d q-t ٔj%A@@"$O-˫&Z QZ{Ab<Sj2<|&0MŊBOBcl,=VF H#2[@"!tgz|7D{SAU S!N{( vܳ; -/A*Afs~g hcQ yĢ9@*|gB|ȁ g8k9#rU9Hb6V'OSPddKq}V03a:q|+nSq -c]U9,atbglp nϑLD.ISOsFC%1Z"g2 ?1bt7_mdӢC( wqx/<$23+n(p%o+M:%*CxD:MUyxbP(Y%.' -qvnmD!lbY`AJ4~P `'1 ej fHɗ @8`# IF x5} E6gyCύP7: - -0$ 0+aC9a2 -cX QB| ?7)`H+ag[J@vMOKD~ di~yx0\Bb"b>dq> d -o8 E2T)-) lK}xK_3Rib@/'zQ$uTP Kt°VA-y_t-/S p G3(Tنjv =5\" -lZԂ%l; ,`VQNǾ*HM08V!p+b\A,]Y-edNC 97@ǒb-Zd< +X*1 Jb0 ̟8II"qMb4(B4&p/r" - sKgο V ?;W\̊k@V _S:2F">XZ -8@ :GÕg8U8u 9N.`Uxf+:pamG8 ~qcsSq)aAIPNĜ&T8*j/pqT/x|WEb]"Tie|aK 6 B_1iJС/|v@O\a`l$Ug;(WQh"H9"c+dt8KbF`~XV 4@/GF(أٗ A}. Fb-09"iFBd/1%!z \BҞ <$j@p4d[7D>@`R#ȅ;vk&8a> q#'E -5ir Rqsy&μx(:%FΕc1'χ$8i8IZ %q(Mx@ׁC1)bn Cq2Y ]̜}+?RFv{.R#?Pu8#KILn׬ 4)tP ;VDU('_Υ -GRVOcW|ȮD -eJ0  U,3@DiCT(GXP0V+ZpYITnHΗ=)Зo 1rjp{D]q$986FFU~݅r#_2cfB̜Qɯ PFP% "R{0**.#x66:!+ GW;=x*㎬${ B.xĴ$ZbLr̓:WkB4),B`(?qd@@lrCgž3C&dK 5kpF\RB8cq0(HO&g4;y&pi^'@ OIc`<|HSe3(ZT -cQQA&tLN -X;Sƹ!pehI#; |qrӑfϜ>`r`6+g sK<'2NJ)|E1B@[X>z-*b;zXWd Qq23 {|6vc,Rc+lFyj }!(pFĪ6IFLbھí-QΗȑAĔ`%`^,T||QI+VT?JZd*eSbOl%-2+3D %B,nEfʬ3SU1≃@w07.߰'wfP(8XbZQ kiQ9gy9I_"ıc:q G#V]8Q A u7;l\eoҿ] -((霖)A!!RpsPRr$nD -8̬{@El $II63otH417KyA n`VG C )`K͢+׃ӊTZT ]dSQa}?B։(=pxb1l[0; -^r;,F,(Ů31YEByo`Fqn[Cߴ/0}FV -pMqbg3b(>Rz-+lPD8f -F_$!WKdp;BXAJS;UĞ99AamH6Da7?0Fggڇ!8cQja0*S -`(v5{X -h#* |ˀe[GTf拖lw-敟E&8I 'XÂ}H,H^"Ē"j0E"6wDNqD˖=KRÆJY=lL(ot鹾1$4ѶVq)WD|= -mNÄ8WG8&.H7'2Aр],E>LFq%- zJ#=prB>ЙۑY^ qa`>֬MK އ` xӤ\l<ڸ|8S 3&9Hో^yDtȜҫ@5ll|K0Uq>X.5$šR7{cl)s! -|."\yrVzDžMDD RY)HKEm[$®A)Fhǐs&*@lVp^@\C iaI>iN l E89s 0#.v>J`('9#TXX@؞ŶI$H,Q(`6)#D|/ -(vq {*U.c%ϰ磮/.|~ -0`$ N*``ÂPRkR:B-$sV?fAD:D -َoxQ!pw[0A޼a+B|g>\!*$(~ʲn94+&[ALZGxqV0׸ 5¦&5eN6!,"!|{K ^8a- fsE(miclN7N|cr%w%:&kQjyCb5uу?Bh,`C-4*3GOH`kp`$]*fb 4K\xANL|ٍ7 }\ >D5 bn]"NҕnkB[g8WIHYPD$pɈ&,] leM^K_pTQ/sbE(:%h7cex/5WяKD4 -*\AGS;UĩؗH4nCOS;R(sx vu}Gbit!\k@4|/ BN՞kD%MY'ê+ms@WH#GPy!hN&8YW lMG>aKx, -0I)FeA ǔ190PDVQ -%Hh2a+FB 9k:W_n\8qe)TDž`w ´=O(39g5rBI.}dl 8mحP.^,)R-O[1AZϗ"2TJ&=3Q-hWCMsUBx~V8.T;[G \Wkol,<ܽxJ!ŇH_Y5ܸ$4BY&zVa g2SK`] ?.$"$>z8gxaŻ(TB%[9SLȵ0AcCDZzxKg΋V9޸N BzJ\ -IInga6Izpbq W˛ycS07[(k]w+ⷊ(\?N^+଑ C1_VC܆!]\:0dʇ&?cPUqa,YW^ ْ <:a|Dalɜs0TDHb۾{:0bgAu~ʌP(oBum>Hf"hL( v -q؜3:VU! 1!t13ngΑyusG,5i8 h"j3\,qziL1vzlEL  - pH l&?8\:l-5]Srت"W2 NlMiA\0H]Fo:]/QEyjCHaR$z#kA R&b1`{xQeNSc آdGft Bq+v`#u8 aia!D1ICgqtFMn^*\:0q&hb]b# -4ʹ݈AcW?t`NT! 9<RZG"XF4 LA3TB/ju!1rڛI^"}|0i.`P$!4H;EM(K$!u9'!LąJYIY 9-54tU -:~@6Dߒ{i[zHtq)H fm#"0Tb-,@ +} GK"? v&L=+r#1$|̿^KA*KH{c\`%Ȉ̺ҝC(eRX#5/1|Ah_mQ?-t"UC$IheX <ɡ$tf Ucу)%dXRaRF*+.#t -Huo -o!|ekR @@BհMѩ%@,f;I0I {'EK"dqJ5\ϻdC\\)113r!%#ݾV~lYW~rDhx|] < Fer!ùc,;r&A{Y Z<jҩDc -#$IȄ4~A'nFI*ZmtcYsG*눬W硁~/A<_&dܞFhoi)S-3  z z.Kd9؊vC9Y6r].@wnp`Ύ%~uXYԷ\6=x" ,%)]_D,݀(iW\þIٞL͎AquW?74">Sy7w|x+`sbk$(&j5rȺH8VɔC Yʋ ~F"Bl($unel -ϐՐ$3QYا{8zf=g yZi6 -g?&XmWPZFyZ V%`k^llgL}J,XVGOauA)p.)r+B.hzJzt0-QU`2*\c. *DWAT(!ey=d1"= 6\^m=@W[\JY}F)VVoZ0u 0g$uY0 VT#j:kkZdFHqE"Zϕ#Xao(HMQabuڟ\m -Y&W]Q^) dHڭh] -T""z sKxMEo.`BYVрi4zUZ@eU^- -jrsk,MNْW(b3G˨HU GgD[:lQ_Tw -7&g`Ǎ7r7N`Xln"GOn:]I @j\q*c!Tp@p}r71ɪԘOmu7%Ρ/On:A&aJ6^v!SU+.%j6jŬ^@TE31ұX0h-G bS -vu'"7_G!}feEB]U-@b< A2$}UN4RKrk,0 1xk\`"[Ʊ㪑C! 8k|jO}2K:d90tٖقb@,YDI.Z?/9F7 j."+ 7f\0Psh#1Zm#_BXdJN'r16,Y63v2:U##<@*G2{*{~G Ȱ cw3,ӞLKA> endstream endobj 1931 0 obj <>stream -bXĮ&gnGfv&k1]a\aʗ8GAM:Q#o'\3Wn;Xq RGb_xd~3^ݱ/1jX!wn_l+OR\Ԙ,z'A5/1@ߋ k01lڷV5dK{Eh`}pKh`g _mH oTPjQͦq/{3[$u|;nH`R7>Q{5f{K"ts5[DVSzk -uuaۉb C]w"=䵍D"f.4{,WYְ:LD#)Edб#hl_ݶU*ڌl(lid _ -d{S0'rF&{ri/70o/7^.~^n_kD*crK~&P(Uufq[ Vk쯊ϥKV: -3P]MvOMBF@c͟Ň$'dKХ;Iz&Ymx|gMJir$mDDic<'( v:W*N6P'IHᣡp9~=;yN0Gs+! 4b+gjܜEoe!5bSgٗW{.E], u|Z_u:C0RrX%OW**[N8]Asl 1|u`JB긖>& DJ+5^֯[UgC^hj ve+:)1gsCrJ=wIݞbB#eF`Az^g\)I`X=ޞ\t|W!dfΜM(8ިݳN -<y> ͸T%z9 uWV5=xS-FIiMYM@D(Ou@9sExj}HtwVIזJ%vDݦpn8^SYHe_X -l#o<ԯ'3g q}c9`3 0!}wR,Wi?2Dt* -6,VPI NgW?B o9ytes)m|ٮw6ޞ\Tye.Y$BgG@T4zg*!͐JWQ!}Ds<^o9h˪5'"J~ <M ͕yϛmK lA^ˍȽh@Reuw+!̘P<l4;RzҵR0ɖ3Iߜ[٤ b̊ e0x9JWPy/u%ac(m8K-˔=Am= V-Ĕ:Ϥ0K,.bYijUf'2ȄAHVJ3 kr$O[J4C ,/җR])`8"qzAர`%9 /_MsHEŲ%}~AjjUF'P.ZF Izug*H@Gĝ%6noo¬$ݲ颩*%r' p'J11כڡp~,*OȉUN(&W\#/|t=xz*sk a-@AA"MCBbڭ"qЇŸ6镨buM1ô%+^lՊWvx;M1 K_@;>H6^ >vh'EpFp&bgCx{ hMDw)b5͋*fc@m( "mwsV ]1%%pn HQ:1USD_ջ 7z EwtƒW @ъJxP)=BH#=Sq;_Ҭ2BEhJ˺t4g|SEه -okSqu%2 GpK7_Iy^=Zy+4i]3cKiJ 4F|!I0m:pFY;XPYÚp0va$<*!d;D9p"auk3GED$yh]G13ɩU=lsP%_VYR l!YKgXcb=_& }DNt!5m_+D1G ٘rTֱS"hˋc&6rn^&~ؘζ6GdC<8E9챪sDTQ2a8(]G/dFvXGDÃG~P,ȺjQ8ȍLd>zq"^"C7PS;bC OgDI**X/r.Q2>~)<vT.?GLvna' ڇ qVqB=aY0XpfVi\f9 {(N4g,fh9on"7Ǡ8YH,ѥ!A I~'DJ{ER~o}Hw>|ͧw_fx?~x~y?s, endstream endobj 5 0 obj <> endobj 65 0 obj <> endobj 193 0 obj <> endobj 345 0 obj <> endobj 433 0 obj <> endobj 609 0 obj <> endobj 857 0 obj <> endobj 1105 0 obj <> endobj 1106 0 obj <> endobj 1107 0 obj <> endobj 1108 0 obj <> endobj 1109 0 obj <> endobj 1110 0 obj <> endobj 1111 0 obj <> endobj 1296 0 obj <> endobj 1297 0 obj <> endobj 1298 0 obj <> endobj 1299 0 obj <> endobj 1300 0 obj <> endobj 1301 0 obj <> endobj 1302 0 obj <> endobj 1487 0 obj <> endobj 1488 0 obj <> endobj 1489 0 obj <> endobj 1490 0 obj <> endobj 1491 0 obj <> endobj 1492 0 obj <> endobj 1493 0 obj <> endobj 1494 0 obj <> endobj 1700 0 obj [/View/Design] endobj 1701 0 obj <>>> endobj 1698 0 obj [/View/Design] endobj 1699 0 obj <>>> endobj 1696 0 obj [/View/Design] endobj 1697 0 obj <>>> endobj 1694 0 obj [/View/Design] endobj 1695 0 obj <>>> endobj 1692 0 obj [/View/Design] endobj 1693 0 obj <>>> endobj 1690 0 obj [/View/Design] endobj 1691 0 obj <>>> endobj 1688 0 obj [/View/Design] endobj 1689 0 obj <>>> endobj 1686 0 obj [/View/Design] endobj 1687 0 obj <>>> endobj 1478 0 obj [/View/Design] endobj 1479 0 obj <>>> endobj 1476 0 obj [/View/Design] endobj 1477 0 obj <>>> endobj 1474 0 obj [/View/Design] endobj 1475 0 obj <>>> endobj 1472 0 obj [/View/Design] endobj 1473 0 obj <>>> endobj 1470 0 obj [/View/Design] endobj 1471 0 obj <>>> endobj 1468 0 obj [/View/Design] endobj 1469 0 obj <>>> endobj 1466 0 obj [/View/Design] endobj 1467 0 obj <>>> endobj 1287 0 obj [/View/Design] endobj 1288 0 obj <>>> endobj 1285 0 obj [/View/Design] endobj 1286 0 obj <>>> endobj 1283 0 obj [/View/Design] endobj 1284 0 obj <>>> endobj 1281 0 obj [/View/Design] endobj 1282 0 obj <>>> endobj 1279 0 obj [/View/Design] endobj 1280 0 obj <>>> endobj 1277 0 obj [/View/Design] endobj 1278 0 obj <>>> endobj 1275 0 obj [/View/Design] endobj 1276 0 obj <>>> endobj 1096 0 obj [/View/Design] endobj 1097 0 obj <>>> endobj 848 0 obj [/View/Design] endobj 849 0 obj <>>> endobj 600 0 obj [/View/Design] endobj 601 0 obj <>>> endobj 424 0 obj [/View/Design] endobj 425 0 obj <>>> endobj 336 0 obj [/View/Design] endobj 337 0 obj <>>> endobj 184 0 obj [/View/Design] endobj 185 0 obj <>>> endobj 54 0 obj [/View/Design] endobj 55 0 obj <>>> endobj 1718 0 obj [1717 0 R 1716 0 R 1715 0 R 1714 0 R 1713 0 R 1712 0 R 1711 0 R 1710 0 R] endobj 1932 0 obj <> endobj xref 0 1933 0000000004 65535 f -0000000016 00000 n -0000000781 00000 n -0000049471 00000 n -0000000006 00000 f -0000311989 00000 n -0000000008 00000 f -0000049522 00000 n -0000000009 00000 f -0000000010 00000 f -0000000011 00000 f -0000000012 00000 f -0000000013 00000 f -0000000014 00000 f -0000000015 00000 f -0000000016 00000 f -0000000017 00000 f -0000000018 00000 f -0000000019 00000 f -0000000020 00000 f -0000000021 00000 f -0000000022 00000 f -0000000023 00000 f -0000000024 00000 f -0000000025 00000 f -0000000026 00000 f -0000000027 00000 f -0000000028 00000 f -0000000029 00000 f -0000000030 00000 f -0000000031 00000 f -0000000032 00000 f -0000000033 00000 f -0000000034 00000 f -0000000035 00000 f -0000000036 00000 f -0000000037 00000 f -0000000038 00000 f -0000000039 00000 f -0000000040 00000 f -0000000041 00000 f -0000000042 00000 f -0000000043 00000 f -0000000044 00000 f -0000000045 00000 f -0000000046 00000 f -0000000047 00000 f -0000000048 00000 f -0000000049 00000 f -0000000050 00000 f -0000000051 00000 f -0000000052 00000 f -0000000053 00000 f -0000000056 00000 f -0000317503 00000 n -0000317534 00000 n -0000000057 00000 f -0000000058 00000 f -0000000059 00000 f -0000000060 00000 f -0000000061 00000 f -0000000062 00000 f -0000000063 00000 f -0000000064 00000 f -0000000066 00000 f -0000312059 00000 n -0000000067 00000 f -0000000068 00000 f -0000000069 00000 f -0000000070 00000 f -0000000071 00000 f -0000000072 00000 f -0000000073 00000 f -0000000074 00000 f -0000000075 00000 f -0000000076 00000 f -0000000077 00000 f -0000000078 00000 f -0000000079 00000 f -0000000080 00000 f -0000000081 00000 f -0000000082 00000 f -0000000083 00000 f -0000000084 00000 f -0000000085 00000 f -0000000086 00000 f -0000000087 00000 f -0000000088 00000 f -0000000089 00000 f -0000000090 00000 f -0000000091 00000 f -0000000092 00000 f -0000000093 00000 f -0000000094 00000 f -0000000095 00000 f -0000000096 00000 f -0000000097 00000 f -0000000098 00000 f -0000000099 00000 f -0000000100 00000 f -0000000101 00000 f -0000000102 00000 f -0000000103 00000 f -0000000104 00000 f -0000000105 00000 f -0000000106 00000 f -0000000107 00000 f -0000000108 00000 f -0000000109 00000 f -0000000110 00000 f -0000000111 00000 f -0000000112 00000 f -0000000113 00000 f -0000000114 00000 f -0000000115 00000 f -0000000116 00000 f -0000000117 00000 f -0000000118 00000 f -0000000119 00000 f -0000000120 00000 f -0000000121 00000 f -0000000122 00000 f -0000000123 00000 f -0000000124 00000 f -0000000125 00000 f -0000000126 00000 f -0000000127 00000 f -0000000128 00000 f -0000000129 00000 f -0000000130 00000 f -0000000131 00000 f -0000000132 00000 f -0000000133 00000 f -0000000134 00000 f -0000000135 00000 f -0000000136 00000 f -0000000137 00000 f -0000000138 00000 f -0000000139 00000 f -0000000140 00000 f -0000000141 00000 f -0000000142 00000 f -0000000143 00000 f -0000000144 00000 f -0000000145 00000 f -0000000146 00000 f -0000000147 00000 f -0000000148 00000 f -0000000149 00000 f -0000000150 00000 f -0000000151 00000 f -0000000152 00000 f -0000000153 00000 f -0000000154 00000 f -0000000155 00000 f -0000000156 00000 f -0000000157 00000 f -0000000158 00000 f -0000000159 00000 f -0000000160 00000 f -0000000161 00000 f -0000000162 00000 f -0000000163 00000 f -0000000164 00000 f -0000000165 00000 f -0000000166 00000 f -0000000167 00000 f -0000000168 00000 f -0000000169 00000 f -0000000170 00000 f -0000000171 00000 f -0000000172 00000 f -0000000173 00000 f -0000000174 00000 f -0000000175 00000 f -0000000176 00000 f -0000000177 00000 f -0000000178 00000 f -0000000179 00000 f -0000000180 00000 f -0000000181 00000 f -0000000182 00000 f -0000000183 00000 f -0000000186 00000 f -0000317385 00000 n -0000317417 00000 n -0000000187 00000 f -0000000188 00000 f -0000000189 00000 f -0000000190 00000 f -0000000191 00000 f -0000000192 00000 f -0000000194 00000 f -0000312132 00000 n -0000000195 00000 f -0000000196 00000 f -0000000197 00000 f -0000000198 00000 f -0000000199 00000 f -0000000200 00000 f -0000000201 00000 f -0000000202 00000 f -0000000203 00000 f -0000000204 00000 f -0000000205 00000 f -0000000206 00000 f -0000000207 00000 f -0000000208 00000 f -0000000209 00000 f -0000000210 00000 f -0000000211 00000 f -0000000212 00000 f -0000000213 00000 f -0000000214 00000 f -0000000215 00000 f -0000000216 00000 f -0000000217 00000 f -0000000218 00000 f -0000000219 00000 f -0000000220 00000 f -0000000221 00000 f -0000000222 00000 f -0000000223 00000 f -0000000224 00000 f -0000000225 00000 f -0000000226 00000 f -0000000227 00000 f -0000000228 00000 f -0000000229 00000 f -0000000230 00000 f -0000000231 00000 f -0000000232 00000 f -0000000233 00000 f -0000000234 00000 f -0000000235 00000 f -0000000236 00000 f -0000000237 00000 f -0000000238 00000 f -0000000239 00000 f -0000000240 00000 f -0000000241 00000 f -0000000242 00000 f -0000000243 00000 f -0000000244 00000 f -0000000245 00000 f -0000000246 00000 f -0000000247 00000 f -0000000248 00000 f -0000000249 00000 f -0000000250 00000 f -0000000251 00000 f -0000000252 00000 f -0000000253 00000 f -0000000254 00000 f -0000000255 00000 f -0000000256 00000 f -0000000257 00000 f -0000000258 00000 f -0000000259 00000 f -0000000260 00000 f -0000000261 00000 f -0000000262 00000 f -0000000263 00000 f -0000000264 00000 f -0000000265 00000 f -0000000266 00000 f -0000000267 00000 f -0000000268 00000 f -0000000269 00000 f -0000000270 00000 f -0000000271 00000 f -0000000272 00000 f -0000000273 00000 f -0000000274 00000 f -0000000275 00000 f -0000000276 00000 f -0000000277 00000 f -0000000278 00000 f -0000000279 00000 f -0000000280 00000 f -0000000281 00000 f -0000000282 00000 f -0000000283 00000 f -0000000284 00000 f -0000000285 00000 f -0000000286 00000 f -0000000287 00000 f -0000000288 00000 f -0000000289 00000 f -0000000290 00000 f -0000000291 00000 f -0000000292 00000 f -0000000293 00000 f -0000000294 00000 f -0000000295 00000 f -0000000296 00000 f -0000000297 00000 f -0000000298 00000 f -0000000299 00000 f -0000000300 00000 f -0000000301 00000 f -0000000302 00000 f -0000000303 00000 f -0000000304 00000 f -0000000305 00000 f -0000000306 00000 f -0000000307 00000 f -0000000308 00000 f -0000000309 00000 f -0000000310 00000 f -0000000311 00000 f -0000000312 00000 f -0000000313 00000 f -0000000314 00000 f -0000000315 00000 f -0000000316 00000 f -0000000317 00000 f -0000000318 00000 f -0000000319 00000 f -0000000320 00000 f -0000000321 00000 f -0000000322 00000 f -0000000323 00000 f -0000000324 00000 f -0000000325 00000 f -0000000326 00000 f -0000000327 00000 f -0000000328 00000 f -0000000329 00000 f -0000000330 00000 f -0000000331 00000 f -0000000332 00000 f -0000000333 00000 f -0000000334 00000 f -0000000335 00000 f -0000000338 00000 f -0000317267 00000 n -0000317299 00000 n -0000000339 00000 f -0000000340 00000 f -0000000341 00000 f -0000000342 00000 f -0000000343 00000 f -0000000344 00000 f -0000000346 00000 f -0000312206 00000 n -0000000347 00000 f -0000000348 00000 f -0000000349 00000 f -0000000350 00000 f -0000000351 00000 f -0000000352 00000 f -0000000353 00000 f -0000000354 00000 f -0000000355 00000 f -0000000356 00000 f -0000000357 00000 f -0000000358 00000 f -0000000359 00000 f -0000000360 00000 f -0000000361 00000 f -0000000362 00000 f -0000000363 00000 f -0000000364 00000 f -0000000365 00000 f -0000000366 00000 f -0000000367 00000 f -0000000368 00000 f -0000000369 00000 f -0000000370 00000 f -0000000371 00000 f -0000000372 00000 f -0000000373 00000 f -0000000374 00000 f -0000000375 00000 f -0000000376 00000 f -0000000377 00000 f -0000000378 00000 f -0000000379 00000 f -0000000380 00000 f -0000000381 00000 f -0000000382 00000 f -0000000383 00000 f -0000000384 00000 f -0000000385 00000 f -0000000386 00000 f -0000000387 00000 f -0000000388 00000 f -0000000389 00000 f -0000000390 00000 f -0000000391 00000 f -0000000392 00000 f -0000000393 00000 f -0000000394 00000 f -0000000395 00000 f -0000000396 00000 f -0000000397 00000 f -0000000398 00000 f -0000000399 00000 f -0000000400 00000 f -0000000401 00000 f -0000000402 00000 f -0000000403 00000 f -0000000404 00000 f -0000000405 00000 f -0000000406 00000 f -0000000407 00000 f -0000000408 00000 f -0000000409 00000 f -0000000410 00000 f -0000000411 00000 f -0000000412 00000 f -0000000413 00000 f -0000000414 00000 f -0000000415 00000 f -0000000416 00000 f -0000000417 00000 f -0000000418 00000 f -0000000419 00000 f -0000000420 00000 f -0000000421 00000 f -0000000422 00000 f -0000000423 00000 f -0000000426 00000 f -0000317149 00000 n -0000317181 00000 n -0000000427 00000 f -0000000428 00000 f -0000000429 00000 f -0000000430 00000 f -0000000431 00000 f -0000000432 00000 f -0000000434 00000 f -0000312280 00000 n -0000000435 00000 f -0000000436 00000 f -0000000437 00000 f -0000000438 00000 f -0000000439 00000 f -0000000440 00000 f -0000000441 00000 f -0000000442 00000 f -0000000443 00000 f -0000000444 00000 f -0000000445 00000 f -0000000446 00000 f -0000000447 00000 f -0000000448 00000 f -0000000449 00000 f -0000000450 00000 f -0000000451 00000 f -0000000452 00000 f -0000000453 00000 f -0000000454 00000 f -0000000455 00000 f -0000000456 00000 f -0000000457 00000 f -0000000458 00000 f -0000000459 00000 f -0000000460 00000 f -0000000461 00000 f -0000000462 00000 f -0000000463 00000 f -0000000464 00000 f -0000000465 00000 f -0000000466 00000 f -0000000467 00000 f -0000000468 00000 f -0000000469 00000 f -0000000470 00000 f -0000000471 00000 f -0000000472 00000 f -0000000473 00000 f -0000000474 00000 f -0000000475 00000 f -0000000476 00000 f -0000000477 00000 f -0000000478 00000 f -0000000479 00000 f -0000000480 00000 f -0000000481 00000 f -0000000482 00000 f -0000000483 00000 f -0000000484 00000 f -0000000485 00000 f -0000000486 00000 f -0000000487 00000 f -0000000488 00000 f -0000000489 00000 f -0000000490 00000 f -0000000491 00000 f -0000000492 00000 f -0000000493 00000 f -0000000494 00000 f -0000000495 00000 f -0000000496 00000 f -0000000497 00000 f -0000000498 00000 f -0000000499 00000 f -0000000500 00000 f -0000000501 00000 f -0000000502 00000 f -0000000503 00000 f -0000000504 00000 f -0000000505 00000 f -0000000506 00000 f -0000000507 00000 f -0000000508 00000 f -0000000509 00000 f -0000000510 00000 f -0000000511 00000 f -0000000512 00000 f -0000000513 00000 f -0000000514 00000 f -0000000515 00000 f -0000000516 00000 f -0000000517 00000 f -0000000518 00000 f -0000000519 00000 f -0000000520 00000 f -0000000521 00000 f -0000000522 00000 f -0000000523 00000 f -0000000524 00000 f -0000000525 00000 f -0000000526 00000 f -0000000527 00000 f -0000000528 00000 f -0000000529 00000 f -0000000530 00000 f -0000000531 00000 f -0000000532 00000 f -0000000533 00000 f -0000000534 00000 f -0000000535 00000 f -0000000536 00000 f -0000000537 00000 f -0000000538 00000 f -0000000539 00000 f -0000000540 00000 f -0000000541 00000 f -0000000542 00000 f -0000000543 00000 f -0000000544 00000 f -0000000545 00000 f -0000000546 00000 f -0000000547 00000 f -0000000548 00000 f -0000000549 00000 f -0000000550 00000 f -0000000551 00000 f -0000000552 00000 f -0000000553 00000 f -0000000554 00000 f -0000000555 00000 f -0000000556 00000 f -0000000557 00000 f -0000000558 00000 f -0000000559 00000 f -0000000560 00000 f -0000000561 00000 f -0000000562 00000 f -0000000563 00000 f -0000000564 00000 f -0000000565 00000 f -0000000566 00000 f -0000000567 00000 f -0000000568 00000 f -0000000569 00000 f -0000000570 00000 f -0000000571 00000 f -0000000572 00000 f -0000000573 00000 f -0000000574 00000 f -0000000575 00000 f -0000000576 00000 f -0000000577 00000 f -0000000578 00000 f -0000000579 00000 f -0000000580 00000 f -0000000581 00000 f -0000000582 00000 f -0000000583 00000 f -0000000584 00000 f -0000000585 00000 f -0000000586 00000 f -0000000587 00000 f -0000000588 00000 f -0000000589 00000 f -0000000590 00000 f -0000000591 00000 f -0000000592 00000 f -0000000593 00000 f -0000000594 00000 f -0000000595 00000 f -0000000596 00000 f -0000000597 00000 f -0000000598 00000 f -0000000599 00000 f -0000000602 00000 f -0000317031 00000 n -0000317063 00000 n -0000000603 00000 f -0000000604 00000 f -0000000605 00000 f -0000000606 00000 f -0000000607 00000 f -0000000608 00000 f -0000000610 00000 f -0000312354 00000 n -0000000611 00000 f -0000000612 00000 f -0000000613 00000 f -0000000614 00000 f -0000000615 00000 f -0000000616 00000 f -0000000617 00000 f -0000000618 00000 f -0000000619 00000 f -0000000620 00000 f -0000000621 00000 f -0000000622 00000 f -0000000623 00000 f -0000000624 00000 f -0000000625 00000 f -0000000626 00000 f -0000000627 00000 f -0000000628 00000 f -0000000629 00000 f -0000000630 00000 f -0000000631 00000 f -0000000632 00000 f -0000000633 00000 f -0000000634 00000 f -0000000635 00000 f -0000000636 00000 f -0000000637 00000 f -0000000638 00000 f -0000000639 00000 f -0000000640 00000 f -0000000641 00000 f -0000000642 00000 f -0000000643 00000 f -0000000644 00000 f -0000000645 00000 f -0000000646 00000 f -0000000647 00000 f -0000000648 00000 f -0000000649 00000 f -0000000650 00000 f -0000000651 00000 f -0000000652 00000 f -0000000653 00000 f -0000000654 00000 f -0000000655 00000 f -0000000656 00000 f -0000000657 00000 f -0000000658 00000 f -0000000659 00000 f -0000000660 00000 f -0000000661 00000 f -0000000662 00000 f -0000000663 00000 f -0000000664 00000 f -0000000665 00000 f -0000000666 00000 f -0000000667 00000 f -0000000668 00000 f -0000000669 00000 f -0000000670 00000 f -0000000671 00000 f -0000000672 00000 f -0000000673 00000 f -0000000674 00000 f -0000000675 00000 f -0000000676 00000 f -0000000677 00000 f -0000000678 00000 f -0000000679 00000 f -0000000680 00000 f -0000000681 00000 f -0000000682 00000 f -0000000683 00000 f -0000000684 00000 f -0000000685 00000 f -0000000686 00000 f -0000000687 00000 f -0000000688 00000 f -0000000689 00000 f -0000000690 00000 f -0000000691 00000 f -0000000692 00000 f -0000000693 00000 f -0000000694 00000 f -0000000695 00000 f -0000000696 00000 f -0000000697 00000 f -0000000698 00000 f -0000000699 00000 f -0000000700 00000 f -0000000701 00000 f -0000000702 00000 f -0000000703 00000 f -0000000704 00000 f -0000000705 00000 f -0000000706 00000 f -0000000707 00000 f -0000000708 00000 f -0000000709 00000 f -0000000710 00000 f -0000000711 00000 f -0000000712 00000 f -0000000713 00000 f -0000000714 00000 f -0000000715 00000 f -0000000716 00000 f -0000000717 00000 f -0000000718 00000 f -0000000719 00000 f -0000000720 00000 f -0000000721 00000 f -0000000722 00000 f -0000000723 00000 f -0000000724 00000 f -0000000725 00000 f -0000000726 00000 f -0000000727 00000 f -0000000728 00000 f -0000000729 00000 f -0000000730 00000 f -0000000731 00000 f -0000000732 00000 f -0000000733 00000 f -0000000734 00000 f -0000000735 00000 f -0000000736 00000 f -0000000737 00000 f -0000000738 00000 f -0000000739 00000 f -0000000740 00000 f -0000000741 00000 f -0000000742 00000 f -0000000743 00000 f -0000000744 00000 f -0000000745 00000 f -0000000746 00000 f -0000000747 00000 f -0000000748 00000 f -0000000749 00000 f -0000000750 00000 f -0000000751 00000 f -0000000752 00000 f -0000000753 00000 f -0000000754 00000 f -0000000755 00000 f -0000000756 00000 f -0000000757 00000 f -0000000758 00000 f -0000000759 00000 f -0000000760 00000 f -0000000761 00000 f -0000000762 00000 f -0000000763 00000 f -0000000764 00000 f -0000000765 00000 f -0000000766 00000 f -0000000767 00000 f -0000000768 00000 f -0000000769 00000 f -0000000770 00000 f -0000000771 00000 f -0000000772 00000 f -0000000773 00000 f -0000000774 00000 f -0000000775 00000 f -0000000776 00000 f -0000000777 00000 f -0000000778 00000 f -0000000779 00000 f -0000000780 00000 f -0000000781 00000 f -0000000782 00000 f -0000000783 00000 f -0000000784 00000 f -0000000785 00000 f -0000000786 00000 f -0000000787 00000 f -0000000788 00000 f -0000000789 00000 f -0000000790 00000 f -0000000791 00000 f -0000000792 00000 f -0000000793 00000 f -0000000794 00000 f -0000000795 00000 f -0000000796 00000 f -0000000797 00000 f -0000000798 00000 f -0000000799 00000 f -0000000800 00000 f -0000000801 00000 f -0000000802 00000 f -0000000803 00000 f -0000000804 00000 f -0000000805 00000 f -0000000806 00000 f -0000000807 00000 f -0000000808 00000 f -0000000809 00000 f -0000000810 00000 f -0000000811 00000 f -0000000812 00000 f -0000000813 00000 f -0000000814 00000 f -0000000815 00000 f -0000000816 00000 f -0000000817 00000 f -0000000818 00000 f -0000000819 00000 f -0000000820 00000 f -0000000821 00000 f -0000000822 00000 f -0000000823 00000 f -0000000824 00000 f -0000000825 00000 f -0000000826 00000 f -0000000827 00000 f -0000000828 00000 f -0000000829 00000 f -0000000830 00000 f -0000000831 00000 f -0000000832 00000 f -0000000833 00000 f -0000000834 00000 f -0000000835 00000 f -0000000836 00000 f -0000000837 00000 f -0000000838 00000 f -0000000839 00000 f -0000000840 00000 f -0000000841 00000 f -0000000842 00000 f -0000000843 00000 f -0000000844 00000 f -0000000845 00000 f -0000000846 00000 f -0000000847 00000 f -0000000850 00000 f -0000316913 00000 n -0000316945 00000 n -0000000851 00000 f -0000000852 00000 f -0000000853 00000 f -0000000854 00000 f -0000000855 00000 f -0000000856 00000 f -0000000858 00000 f -0000312428 00000 n -0000000859 00000 f -0000000860 00000 f -0000000861 00000 f -0000000862 00000 f -0000000863 00000 f -0000000864 00000 f -0000000865 00000 f -0000000866 00000 f -0000000867 00000 f -0000000868 00000 f -0000000869 00000 f -0000000870 00000 f -0000000871 00000 f -0000000872 00000 f -0000000873 00000 f -0000000874 00000 f -0000000875 00000 f -0000000876 00000 f -0000000877 00000 f -0000000878 00000 f -0000000879 00000 f -0000000880 00000 f -0000000881 00000 f -0000000882 00000 f -0000000883 00000 f -0000000884 00000 f -0000000885 00000 f -0000000886 00000 f -0000000887 00000 f -0000000888 00000 f -0000000889 00000 f -0000000890 00000 f -0000000891 00000 f -0000000892 00000 f -0000000893 00000 f -0000000894 00000 f -0000000895 00000 f -0000000896 00000 f -0000000897 00000 f -0000000898 00000 f -0000000899 00000 f -0000000900 00000 f -0000000901 00000 f -0000000902 00000 f -0000000903 00000 f -0000000904 00000 f -0000000905 00000 f -0000000906 00000 f -0000000907 00000 f -0000000908 00000 f -0000000909 00000 f -0000000910 00000 f -0000000911 00000 f -0000000912 00000 f -0000000913 00000 f -0000000914 00000 f -0000000915 00000 f -0000000916 00000 f -0000000917 00000 f -0000000918 00000 f -0000000919 00000 f -0000000920 00000 f -0000000921 00000 f -0000000922 00000 f -0000000923 00000 f -0000000924 00000 f -0000000925 00000 f -0000000926 00000 f -0000000927 00000 f -0000000928 00000 f -0000000929 00000 f -0000000930 00000 f -0000000931 00000 f -0000000932 00000 f -0000000933 00000 f -0000000934 00000 f -0000000935 00000 f -0000000936 00000 f -0000000937 00000 f -0000000938 00000 f -0000000939 00000 f -0000000940 00000 f -0000000941 00000 f -0000000942 00000 f -0000000943 00000 f -0000000944 00000 f -0000000945 00000 f -0000000946 00000 f -0000000947 00000 f -0000000948 00000 f -0000000949 00000 f -0000000950 00000 f -0000000951 00000 f -0000000952 00000 f -0000000953 00000 f -0000000954 00000 f -0000000955 00000 f -0000000956 00000 f -0000000957 00000 f -0000000958 00000 f -0000000959 00000 f -0000000960 00000 f -0000000961 00000 f -0000000962 00000 f -0000000963 00000 f -0000000964 00000 f -0000000965 00000 f -0000000966 00000 f -0000000967 00000 f -0000000968 00000 f -0000000969 00000 f -0000000970 00000 f -0000000971 00000 f -0000000972 00000 f -0000000973 00000 f -0000000974 00000 f -0000000975 00000 f -0000000976 00000 f -0000000977 00000 f -0000000978 00000 f -0000000979 00000 f -0000000980 00000 f -0000000981 00000 f -0000000982 00000 f -0000000983 00000 f -0000000984 00000 f -0000000985 00000 f -0000000986 00000 f -0000000987 00000 f -0000000988 00000 f -0000000989 00000 f -0000000990 00000 f -0000000991 00000 f -0000000992 00000 f -0000000993 00000 f -0000000994 00000 f -0000000995 00000 f -0000000996 00000 f -0000000997 00000 f -0000000998 00000 f -0000000999 00000 f -0000001000 00000 f -0000001001 00000 f -0000001002 00000 f -0000001003 00000 f -0000001004 00000 f -0000001005 00000 f -0000001006 00000 f -0000001007 00000 f -0000001008 00000 f -0000001009 00000 f -0000001010 00000 f -0000001011 00000 f -0000001012 00000 f -0000001013 00000 f -0000001014 00000 f -0000001015 00000 f -0000001016 00000 f -0000001017 00000 f -0000001018 00000 f -0000001019 00000 f -0000001020 00000 f -0000001021 00000 f -0000001022 00000 f -0000001023 00000 f -0000001024 00000 f -0000001025 00000 f -0000001026 00000 f -0000001027 00000 f -0000001028 00000 f -0000001029 00000 f -0000001030 00000 f -0000001031 00000 f -0000001032 00000 f -0000001033 00000 f -0000001034 00000 f -0000001035 00000 f -0000001036 00000 f -0000001037 00000 f -0000001038 00000 f -0000001039 00000 f -0000001040 00000 f -0000001041 00000 f -0000001042 00000 f -0000001043 00000 f -0000001044 00000 f -0000001045 00000 f -0000001046 00000 f -0000001047 00000 f -0000001048 00000 f -0000001049 00000 f -0000001050 00000 f -0000001051 00000 f -0000001052 00000 f -0000001053 00000 f -0000001054 00000 f -0000001055 00000 f -0000001056 00000 f -0000001057 00000 f -0000001058 00000 f -0000001059 00000 f -0000001060 00000 f -0000001061 00000 f -0000001062 00000 f -0000001063 00000 f -0000001064 00000 f -0000001065 00000 f -0000001066 00000 f -0000001067 00000 f -0000001068 00000 f -0000001069 00000 f -0000001070 00000 f -0000001071 00000 f -0000001072 00000 f -0000001073 00000 f -0000001074 00000 f -0000001075 00000 f -0000001076 00000 f -0000001077 00000 f -0000001078 00000 f -0000001079 00000 f -0000001080 00000 f -0000001081 00000 f -0000001082 00000 f -0000001083 00000 f -0000001084 00000 f -0000001085 00000 f -0000001086 00000 f -0000001087 00000 f -0000001088 00000 f -0000001089 00000 f -0000001090 00000 f -0000001091 00000 f -0000001092 00000 f -0000001093 00000 f -0000001094 00000 f -0000001095 00000 f -0000001098 00000 f -0000316793 00000 n -0000316826 00000 n -0000001099 00000 f -0000001100 00000 f -0000001101 00000 f -0000001102 00000 f -0000001103 00000 f -0000001104 00000 f -0000001112 00000 f -0000312504 00000 n -0000312581 00000 n -0000312655 00000 n -0000312729 00000 n -0000312803 00000 n -0000312878 00000 n -0000312953 00000 n -0000001113 00000 f -0000001114 00000 f -0000001115 00000 f -0000001116 00000 f -0000001117 00000 f -0000001118 00000 f -0000001119 00000 f -0000001120 00000 f -0000001121 00000 f -0000001122 00000 f -0000001123 00000 f -0000001124 00000 f -0000001125 00000 f -0000001126 00000 f -0000001127 00000 f -0000001128 00000 f -0000001129 00000 f -0000001130 00000 f -0000001131 00000 f -0000001132 00000 f -0000001133 00000 f -0000001134 00000 f -0000001135 00000 f -0000001136 00000 f -0000001137 00000 f -0000001138 00000 f -0000001139 00000 f -0000001140 00000 f -0000001141 00000 f -0000001142 00000 f -0000001143 00000 f -0000001144 00000 f -0000001145 00000 f -0000001146 00000 f -0000001147 00000 f -0000001148 00000 f -0000001149 00000 f -0000001150 00000 f -0000001151 00000 f -0000001152 00000 f -0000001153 00000 f -0000001154 00000 f -0000001155 00000 f -0000001156 00000 f -0000001157 00000 f -0000001158 00000 f -0000001159 00000 f -0000001160 00000 f -0000001161 00000 f -0000001162 00000 f -0000001163 00000 f -0000001164 00000 f -0000001165 00000 f -0000001166 00000 f -0000001167 00000 f -0000001168 00000 f -0000001169 00000 f -0000001170 00000 f -0000001171 00000 f -0000001172 00000 f -0000001173 00000 f -0000001174 00000 f -0000001175 00000 f -0000001176 00000 f -0000001177 00000 f -0000001178 00000 f -0000001179 00000 f -0000001180 00000 f -0000001181 00000 f -0000001182 00000 f -0000001183 00000 f -0000001184 00000 f -0000001185 00000 f -0000001186 00000 f -0000001187 00000 f -0000001188 00000 f -0000001189 00000 f -0000001190 00000 f -0000001191 00000 f -0000001192 00000 f -0000001193 00000 f -0000001194 00000 f -0000001195 00000 f -0000001196 00000 f -0000001197 00000 f -0000001198 00000 f -0000001199 00000 f -0000001200 00000 f -0000001201 00000 f -0000001202 00000 f -0000001203 00000 f -0000001204 00000 f -0000001205 00000 f -0000001206 00000 f -0000001207 00000 f -0000001208 00000 f -0000001209 00000 f -0000001210 00000 f -0000001211 00000 f -0000001212 00000 f -0000001213 00000 f -0000001214 00000 f -0000001215 00000 f -0000001216 00000 f -0000001217 00000 f -0000001218 00000 f -0000001219 00000 f -0000001220 00000 f -0000001221 00000 f -0000001222 00000 f -0000001223 00000 f -0000001224 00000 f -0000001225 00000 f -0000001226 00000 f -0000001227 00000 f -0000001228 00000 f -0000001229 00000 f -0000001230 00000 f -0000001231 00000 f -0000001232 00000 f -0000001233 00000 f -0000001234 00000 f -0000001235 00000 f -0000001236 00000 f -0000001237 00000 f -0000001238 00000 f -0000001239 00000 f -0000001240 00000 f -0000001241 00000 f -0000001242 00000 f -0000001243 00000 f -0000001244 00000 f -0000001245 00000 f -0000001246 00000 f -0000001247 00000 f -0000001248 00000 f -0000001249 00000 f -0000001250 00000 f -0000001251 00000 f -0000001252 00000 f -0000001253 00000 f -0000001254 00000 f -0000001255 00000 f -0000001256 00000 f -0000001257 00000 f -0000001258 00000 f -0000001259 00000 f -0000001260 00000 f -0000001261 00000 f -0000001262 00000 f -0000001263 00000 f -0000001264 00000 f -0000001265 00000 f -0000001266 00000 f -0000001267 00000 f -0000001268 00000 f -0000001269 00000 f -0000001270 00000 f -0000001271 00000 f -0000001272 00000 f -0000001273 00000 f -0000001274 00000 f -0000001289 00000 f -0000316673 00000 n -0000316706 00000 n -0000316553 00000 n -0000316586 00000 n -0000316433 00000 n -0000316466 00000 n -0000316313 00000 n -0000316346 00000 n -0000316193 00000 n -0000316226 00000 n -0000316073 00000 n -0000316106 00000 n -0000315953 00000 n -0000315986 00000 n -0000001290 00000 f -0000001291 00000 f -0000001292 00000 f -0000001293 00000 f -0000001294 00000 f -0000001295 00000 f -0000001303 00000 f -0000313028 00000 n -0000313105 00000 n -0000313179 00000 n -0000313253 00000 n -0000313327 00000 n -0000313402 00000 n -0000313477 00000 n -0000001304 00000 f -0000001305 00000 f -0000001306 00000 f -0000001307 00000 f -0000001308 00000 f -0000001309 00000 f -0000001310 00000 f -0000001311 00000 f -0000001312 00000 f -0000001313 00000 f -0000001314 00000 f -0000001315 00000 f -0000001316 00000 f -0000001317 00000 f -0000001318 00000 f -0000001319 00000 f -0000001320 00000 f -0000001321 00000 f -0000001322 00000 f -0000001323 00000 f -0000001324 00000 f -0000001325 00000 f -0000001326 00000 f -0000001327 00000 f -0000001328 00000 f -0000001329 00000 f -0000001330 00000 f -0000001331 00000 f -0000001332 00000 f -0000001333 00000 f -0000001334 00000 f -0000001335 00000 f -0000001336 00000 f -0000001337 00000 f -0000001338 00000 f -0000001339 00000 f -0000001340 00000 f -0000001341 00000 f -0000001342 00000 f -0000001343 00000 f -0000001344 00000 f -0000001345 00000 f -0000001346 00000 f -0000001347 00000 f -0000001348 00000 f -0000001349 00000 f -0000001350 00000 f -0000001351 00000 f -0000001352 00000 f -0000001353 00000 f -0000001354 00000 f -0000001355 00000 f -0000001356 00000 f -0000001357 00000 f -0000001358 00000 f -0000001359 00000 f -0000001360 00000 f -0000001361 00000 f -0000001362 00000 f -0000001363 00000 f -0000001364 00000 f -0000001365 00000 f -0000001366 00000 f -0000001367 00000 f -0000001368 00000 f -0000001369 00000 f -0000001370 00000 f -0000001371 00000 f -0000001372 00000 f -0000001373 00000 f -0000001374 00000 f -0000001375 00000 f -0000001376 00000 f -0000001377 00000 f -0000001378 00000 f -0000001379 00000 f -0000001380 00000 f -0000001381 00000 f -0000001382 00000 f -0000001383 00000 f -0000001384 00000 f -0000001385 00000 f -0000001386 00000 f -0000001387 00000 f -0000001388 00000 f -0000001389 00000 f -0000001390 00000 f -0000001391 00000 f -0000001392 00000 f -0000001393 00000 f -0000001394 00000 f -0000001395 00000 f -0000001396 00000 f -0000001397 00000 f -0000001398 00000 f -0000001399 00000 f -0000001400 00000 f -0000001401 00000 f -0000001402 00000 f -0000001403 00000 f -0000001404 00000 f -0000001405 00000 f -0000001406 00000 f -0000001407 00000 f -0000001408 00000 f -0000001409 00000 f -0000001410 00000 f -0000001411 00000 f -0000001412 00000 f -0000001413 00000 f -0000001414 00000 f -0000001415 00000 f -0000001416 00000 f -0000001417 00000 f -0000001418 00000 f -0000001419 00000 f -0000001420 00000 f -0000001421 00000 f -0000001422 00000 f -0000001423 00000 f -0000001424 00000 f -0000001425 00000 f -0000001426 00000 f -0000001427 00000 f -0000001428 00000 f -0000001429 00000 f -0000001430 00000 f -0000001431 00000 f -0000001432 00000 f -0000001433 00000 f -0000001434 00000 f -0000001435 00000 f -0000001436 00000 f -0000001437 00000 f -0000001438 00000 f -0000001439 00000 f -0000001440 00000 f -0000001441 00000 f -0000001442 00000 f -0000001443 00000 f -0000001444 00000 f -0000001445 00000 f -0000001446 00000 f -0000001447 00000 f -0000001448 00000 f -0000001449 00000 f -0000001450 00000 f -0000001451 00000 f -0000001452 00000 f -0000001453 00000 f -0000001454 00000 f -0000001455 00000 f -0000001456 00000 f -0000001457 00000 f -0000001458 00000 f -0000001459 00000 f -0000001460 00000 f -0000001461 00000 f -0000001462 00000 f -0000001463 00000 f -0000001464 00000 f -0000001465 00000 f -0000001480 00000 f -0000315833 00000 n -0000315866 00000 n -0000315713 00000 n -0000315746 00000 n -0000315593 00000 n -0000315626 00000 n -0000315473 00000 n -0000315506 00000 n -0000315353 00000 n -0000315386 00000 n -0000315233 00000 n -0000315266 00000 n -0000315113 00000 n -0000315146 00000 n -0000001481 00000 f -0000001482 00000 f -0000001483 00000 f -0000001484 00000 f -0000001485 00000 f -0000001486 00000 f -0000000000 00000 f -0000313552 00000 n -0000313629 00000 n -0000313706 00000 n -0000313780 00000 n -0000313854 00000 n -0000313928 00000 n -0000314003 00000 n -0000314078 00000 n -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000314993 00000 n -0000315026 00000 n -0000314873 00000 n -0000314906 00000 n -0000314753 00000 n -0000314786 00000 n -0000314633 00000 n -0000314666 00000 n -0000314513 00000 n -0000314546 00000 n -0000314393 00000 n -0000314426 00000 n -0000314273 00000 n -0000314306 00000 n -0000314153 00000 n -0000314186 00000 n -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000096804 00000 n -0000096881 00000 n -0000096958 00000 n -0000097032 00000 n -0000097106 00000 n -0000097180 00000 n -0000097255 00000 n -0000097330 00000 n -0000317619 00000 n -0000051272 00000 n -0000059450 00000 n -0000099107 00000 n -0000090251 00000 n -0000098365 00000 n -0000098482 00000 n -0000098607 00000 n -0000098732 00000 n -0000098857 00000 n -0000098982 00000 n -0000095761 00000 n -0000095918 00000 n -0000061541 00000 n -0000061855 00000 n -0000062136 00000 n -0000062471 00000 n -0000062765 00000 n -0000063075 00000 n -0000063355 00000 n -0000063774 00000 n -0000064086 00000 n -0000064388 00000 n -0000064682 00000 n -0000064962 00000 n -0000065243 00000 n -0000065667 00000 n -0000065949 00000 n -0000066552 00000 n -0000066889 00000 n -0000067182 00000 n -0000067495 00000 n -0000067775 00000 n -0000068202 00000 n -0000068521 00000 n -0000068823 00000 n -0000069120 00000 n -0000069436 00000 n -0000069716 00000 n -0000069996 00000 n -0000070277 00000 n -0000070929 00000 n -0000071264 00000 n -0000071556 00000 n -0000071870 00000 n -0000072151 00000 n -0000072573 00000 n -0000072890 00000 n -0000073192 00000 n -0000073493 00000 n -0000073790 00000 n -0000074070 00000 n -0000074349 00000 n -0000074630 00000 n -0000075298 00000 n -0000075634 00000 n -0000075928 00000 n -0000076245 00000 n -0000076528 00000 n -0000076822 00000 n -0000077255 00000 n -0000077575 00000 n -0000077878 00000 n -0000078174 00000 n -0000078454 00000 n -0000078733 00000 n -0000079018 00000 n -0000079695 00000 n -0000080033 00000 n -0000080332 00000 n -0000080610 00000 n -0000080927 00000 n -0000081211 00000 n -0000081653 00000 n -0000081973 00000 n -0000082278 00000 n -0000082579 00000 n -0000082862 00000 n -0000083146 00000 n -0000083429 00000 n -0000084128 00000 n -0000084410 00000 n -0000084749 00000 n -0000085048 00000 n -0000085371 00000 n -0000085658 00000 n -0000086107 00000 n -0000086431 00000 n -0000086736 00000 n -0000087040 00000 n -0000087324 00000 n -0000087605 00000 n -0000087886 00000 n -0000088170 00000 n -0000088886 00000 n -0000089230 00000 n -0000089530 00000 n -0000059517 00000 n -0000060974 00000 n -0000061026 00000 n -0000095696 00000 n -0000095631 00000 n -0000095566 00000 n -0000095501 00000 n -0000095436 00000 n -0000095371 00000 n -0000095306 00000 n -0000095241 00000 n -0000095176 00000 n -0000095111 00000 n -0000095046 00000 n -0000094981 00000 n -0000094916 00000 n -0000094851 00000 n -0000094786 00000 n -0000094721 00000 n -0000094656 00000 n -0000094591 00000 n -0000094526 00000 n -0000094461 00000 n -0000094396 00000 n -0000094331 00000 n -0000094266 00000 n -0000094201 00000 n -0000094136 00000 n -0000094071 00000 n -0000094006 00000 n -0000093941 00000 n -0000093876 00000 n -0000093811 00000 n -0000093746 00000 n -0000093681 00000 n -0000093616 00000 n -0000093551 00000 n -0000093486 00000 n -0000093421 00000 n -0000093356 00000 n -0000093291 00000 n -0000093226 00000 n -0000093161 00000 n -0000093096 00000 n -0000093031 00000 n -0000092966 00000 n -0000092901 00000 n -0000092836 00000 n -0000092771 00000 n -0000092706 00000 n -0000092641 00000 n -0000092576 00000 n -0000092511 00000 n -0000092446 00000 n -0000092381 00000 n -0000092316 00000 n -0000092251 00000 n -0000092186 00000 n -0000092121 00000 n -0000092056 00000 n -0000091991 00000 n -0000091926 00000 n -0000091861 00000 n -0000091796 00000 n -0000091731 00000 n -0000091666 00000 n -0000091601 00000 n -0000091536 00000 n -0000091471 00000 n -0000091406 00000 n -0000091341 00000 n -0000091276 00000 n -0000091211 00000 n -0000091146 00000 n -0000091081 00000 n -0000091016 00000 n -0000090951 00000 n -0000090886 00000 n -0000090821 00000 n -0000090756 00000 n -0000090691 00000 n -0000090626 00000 n -0000090561 00000 n -0000090496 00000 n -0000090431 00000 n -0000090366 00000 n -0000090186 00000 n -0000096067 00000 n -0000096454 00000 n -0000096096 00000 n -0000096220 00000 n -0000096337 00000 n -0000096578 00000 n -0000096688 00000 n -0000098245 00000 n -0000098278 00000 n -0000098125 00000 n -0000098158 00000 n -0000098005 00000 n -0000098038 00000 n -0000097885 00000 n -0000097918 00000 n -0000097765 00000 n -0000097798 00000 n -0000097645 00000 n -0000097678 00000 n -0000097525 00000 n -0000097558 00000 n -0000097405 00000 n -0000097438 00000 n -0000099185 00000 n -0000099439 00000 n -0000100465 00000 n -0000112137 00000 n -0000177727 00000 n -0000243317 00000 n -0000308907 00000 n -0000317711 00000 n -trailer <]>> startxref 317882 %%EOF \ No newline at end of file diff --git a/graphics/icon/open-refine-120px.png b/graphics/icon/open-refine-120px.png deleted file mode 100644 index 137614705..000000000 Binary files a/graphics/icon/open-refine-120px.png and /dev/null differ diff --git a/graphics/icon/open-refine-128px.png b/graphics/icon/open-refine-128px.png deleted file mode 100644 index 8acc69219..000000000 Binary files a/graphics/icon/open-refine-128px.png and /dev/null differ diff --git a/graphics/icon/open-refine-200px.png b/graphics/icon/open-refine-200px.png deleted file mode 100644 index 9f650e2eb..000000000 Binary files a/graphics/icon/open-refine-200px.png and /dev/null differ diff --git a/graphics/icon/open-refine-256px.png b/graphics/icon/open-refine-256px.png deleted file mode 100644 index ca1937917..000000000 Binary files a/graphics/icon/open-refine-256px.png and /dev/null differ diff --git a/graphics/icon/open-refine-320px.png b/graphics/icon/open-refine-320px.png new file mode 100644 index 000000000..c64b5bb4e Binary files /dev/null and b/graphics/icon/open-refine-320px.png differ diff --git a/graphics/icon/open-refine-512px.png b/graphics/icon/open-refine-512px.png deleted file mode 100644 index f2354c1f5..000000000 Binary files a/graphics/icon/open-refine-512px.png and /dev/null differ diff --git a/graphics/icon/open-refine-logo.psd b/graphics/icon/open-refine-logo.psd deleted file mode 100644 index 972363049..000000000 Binary files a/graphics/icon/open-refine-logo.psd and /dev/null differ diff --git a/graphics/icon/openrefine.icns b/graphics/icon/openrefine.icns index 119c4c0a1..9984c1bb2 100644 Binary files a/graphics/icon/openrefine.icns and b/graphics/icon/openrefine.icns differ diff --git a/graphics/icon/openrefine.ico b/graphics/icon/openrefine.ico index 4bbe51fd6..3a9afc4a0 100644 Binary files a/graphics/icon/openrefine.ico and b/graphics/icon/openrefine.ico differ diff --git a/main/.classpath b/main/.classpath deleted file mode 100644 index d3035ccc5..000000000 --- a/main/.classpath +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/main/.gitignore b/main/.gitignore deleted file mode 100644 index 8c0a17c3f..000000000 --- a/main/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/test-output diff --git a/main/.project b/main/.project deleted file mode 100644 index 2f5ebba12..000000000 --- a/main/.project +++ /dev/null @@ -1,29 +0,0 @@ - - - grefine - - - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.jsdt.core.jsNature - - diff --git a/main/.settings/.jsdtscope b/main/.settings/.jsdtscope deleted file mode 100644 index b13d30357..000000000 --- a/main/.settings/.jsdtscope +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/main/resources/schemas/TableSchemaValidator.json b/main/resources/schemas/TableSchemaValidator.json new file mode 100644 index 000000000..8f7111fb7 --- /dev/null +++ b/main/resources/schemas/TableSchemaValidator.json @@ -0,0 +1,213 @@ +{ + "version": "1.0.0", + "errors": { + + + "io-error": { + "name": "IO Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source returned an IO Error of type {error_type}", + "description": "Data reading error because of IO error.\n\n How it could be resolved:\n - Fix path if it's not correct." + }, + "http-error": { + "name": "HTTP Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source returned an HTTP error with a status code of {status_code}", + "description": "Data reading error because of HTTP error.\n\n How it could be resolved:\n - Fix url link if it's not correct." + }, + "source-error": { + "name": "Source Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source has not supported or has inconsistent contents; no tabular data can be extracted", + "description": "Data reading error because of not supported or inconsistent contents.\n\n How it could be resolved:\n - Fix data contents (e.g. change JSON data to array or arrays/objects).\n - Set correct source settings in {validator}." + }, + "scheme-error": { + "name": "Scheme Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source is in an unknown scheme; no tabular data can be extracted", + "description": "Data reading error because of incorrect scheme.\n\n How it could be resolved:\n - Fix data scheme (e.g. change scheme from `ftp` to `http`).\n - Set correct scheme in {validator}." + }, + "format-error": { + "name": "Format Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source is in an unknown format; no tabular data can be extracted", + "description": "Data reading error because of incorrect format.\n\n How it could be resolved:\n - Fix data format (e.g. change file extension from `txt` to `csv`).\n - Set correct format in {validator}." + }, + "encoding-error": { + "name": "Encoding Error", + "type": "source", + "context": "table", + "weight": 100, + "message": "The data source could not be successfully decoded with {encoding} encoding", + "description": "Data reading error because of an encoding problem.\n\n How it could be resolved:\n - Fix data source if it's broken.\n - Set correct encoding in {validator}." + }, + + + "blank-header": { + "name": "Blank Header", + "type": "structure", + "context": "head", + "weight": 3, + "message": "Header in column {column_number} is blank", + "description": "A column in the header row is missing a value. Column names should be provided.\n\n How it could be resolved:\n - Add the missing column name to the first row of the data source.\n - If the first row starts with, or ends with a comma, remove it.\n - If this error should be ignored disable `blank-header` check in {validator}." + }, + "duplicate-header": { + "name": "Duplicate Header", + "type": "structure", + "context": "head", + "weight": 3, + "message": "Header in column {column_number} is duplicated to header in column(s) {column_numbers}", + "description": "Two columns in the header row have the same value. Column names should be unique.\n\n How it could be resolved:\n - Add the missing column name to the first row of the data.\n - If the first row starts with, or ends with a comma, remove it.\n - If this error should be ignored disable `duplicate-header` check in {validator}." + }, + "blank-row": { + "name": "Blank Row", + "type": "structure", + "context": "body", + "weight": 9, + "message": "Row {row_number} is completely blank", + "description": "This row is empty. A row should contain at least one value.\n\n How it could be resolved:\n - Delete the row.\n - If this error should be ignored disable `blank-row` check in {validator}." + }, + "duplicate-row": { + "name": "Duplicate Row", + "type": "structure", + "context": "body", + "weight": 5, + "message": "Row {row_number} is duplicated to row(s) {row_numbers}", + "description": "The exact same data has been seen in another row.\n\n How it could be resolved:\n - If some of the data is incorrect, correct it.\n - If the whole row is an incorrect duplicate, remove it.\n - If this error should be ignored disable `duplicate-row` check in {validator}." + }, + "extra-value": { + "name": "Extra Value", + "type": "structure", + "context": "body", + "weight": 9, + "message": "Row {row_number} has an extra value in column {column_number}", + "description": "This row has more values compared to the header row (the first row in the data source). A key concept is that all the rows in tabular data must have the same number of columns.\n\n How it could be resolved:\n - Check data has an extra comma between the values in this row.\n - If this error should be ignored disable `extra-value` check in {validator}." + }, + "missing-value": { + "name": "Missing Value", + "type": "structure", + "context": "body", + "weight": 9, + "message": "Row {row_number} has a missing value in column {column_number}", + "description": "This row has less values compared to the header row (the first row in the data source). A key concept is that all the rows in tabular data must have the same number of columns.\n\n How it could be resolved:\n - Check data is not missing a comma between the values in this row.\n - If this error should be ignored disable `missing-value` check in {validator}." + }, + + + "schema-error": { + "name": "Table Schema Error", + "type": "schema", + "context": "table", + "weight": 15, + "message": "Table Schema error: {error_message}", + "description": "Provided schema is not valid.\n\n How it could be resolved:\n - Update schema descriptor to be a valid descriptor\n - If this error should be ignored disable schema checks in {validator}." + }, + "non-matching-header": { + "name": "Non-Matching Header", + "type": "schema", + "context": "head", + "weight": 9, + "message": "Header in column {column_number} doesn't match field name {field_name} in the schema", + "description": "One of the data source headers doesn't match the field name defined in the schema.\n\n How it could be resolved:\n - Rename header in the data source or field in the schema\n - If this error should be ignored disable `non-matching-header` check in {validator}." + }, + "extra-header": { + "name": "Extra Header", + "type": "schema", + "context": "head", + "weight": 9, + "message": "There is an extra header in column {column_number}", + "description": "The first row of the data source contains header that doesn't exist in the schema.\n\n How it could be resolved:\n - Remove the extra column from the data source or add the missing field to the schema\n - If this error should be ignored disable `extra-header` check in {validator}." + }, + "missing-header": { + "name": "Missing Header", + "type": "schema", + "context": "head", + "weight": 9, + "message": "There is a missing header in column {column_number}", + "description": "Based on the schema there should be a header that is missing in the first row of the data source.\n\n How it could be resolved:\n - Add the missing column to the data source or remove the extra field from the schema\n - If this error should be ignored disable `missing-header` check in {validator}." + }, + "type-or-format-error": { + "name": "Type or Format Error", + "type": "schema", + "context": "body", + "weight": 9, + "message": "The value {value} in row {row_number} and column {column_number} is not type {field_type} and format {field_format}", + "description": "The value does not match the schema type and format for this field.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If this value is correct, adjust the type and/or format.\n - To ignore the error, disable the `type-or-format-error` check in {validator}. In this case all schema checks for row values will be ignored." + }, + "required-constraint": { + "name": "Required Constraint", + "type": "schema", + "context": "body", + "weight": 9, + "message": "Column {column_number} is a required field, but row {row_number} has no value", + "description": "This field is a required field, but it contains no value.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove the `required` constraint from the schema.\n - If this error should be ignored disable `required-constraint` check in {validator}." + }, + "pattern-constraint": { + "name": "Pattern Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the pattern constraint of {constraint}", + "description": "This field value should conform to constraint pattern.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `pattern` constraint in the schema.\n - If this error should be ignored disable `pattern-constraint` check in {validator}." + }, + "unique-constraint": { + "name": "Unique Constraint", + "type": "schema", + "context": "body", + "weight": 9, + "message": "Rows {row_numbers} has unique constraint violation in column {column_number}", + "description": "This field is a unique field but it contains a value that has been used in another row.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then the values in this column are not unique. Remove the `unique` constraint from the schema.\n - If this error should be ignored disable `unique-constraint` check in {validator}." + }, + "enumerable-constraint": { + "name": "Enumerable Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the given enumeration: {constraint}", + "description": "This field value should be equal to one of the values in the enumeration constraint.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `enum` constraint in the schema.\n - If this error should be ignored disable `enumerable-constraint` check in {validator}." + }, + "minimum-constraint": { + "name": "Minimum Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the minimum constraint of {constraint}", + "description": "This field value should be greater or equal than constraint value.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `minimum` constraint in the schema.\n - If this error should be ignored disable `minimum-constraint` check in {validator}." + }, + "maximum-constraint": { + "name": "Maximum Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the maximum constraint of {constraint}", + "description": "This field value should be less or equal than constraint value.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `maximum` constraint in the schema.\n - If this error should be ignored disable `maximum-constraint` check in {validator}." + }, + "minimum-length-constraint": { + "name": "Minimum Length Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the minimum length constraint of {constraint}", + "description": "A lenght of this field value should be greater or equal than schema constraint value.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `minimumLength` constraint in the schema.\n - If this error should be ignored disable `minimum-length-constraint` check in {validator}." + }, + "maximum-length-constraint": { + "name": "Maximum Length Constraint", + "type": "schema", + "context": "body", + "weight": 7, + "message": "The value {value} in row {row_number} and column {column_number} does not conform to the maximum length constraint of {constraint}", + "description": "A lenght of this field value should be less or equal than schema constraint value.\n\n How it could be resolved:\n - If this value is not correct, update the value.\n - If value is correct, then remove or refine the `maximumLength` constraint in the schema.\n - If this error should be ignored disable `maximum-length-constraint` check in {validator}." + } + + + } +} diff --git a/main/resources/schemas/datapackage-template.json b/main/resources/schemas/datapackage-template.json new file mode 100644 index 000000000..53e5180cc --- /dev/null +++ b/main/resources/schemas/datapackage-template.json @@ -0,0 +1,16 @@ +{ + "image": "", + "license": "", + "last_updated": "", + "keywords": [], + "sources": [{ + "web": "", + "name": "", + "title": "" + }], + "name": "", + "description": "", + "resources": [], + "title": "", + "version": "" +} \ No newline at end of file diff --git a/main/src/com/google/refine/ProjectManager.java b/main/src/com/google/refine/ProjectManager.java index b01ffbae3..7373d5155 100644 --- a/main/src/com/google/refine/ProjectManager.java +++ b/main/src/com/google/refine/ProjectManager.java @@ -37,6 +37,7 @@ import java.io.IOException; import java.io.InputStream; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -45,7 +46,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.tools.tar.TarOutputStream; import org.json.JSONArray; import org.json.JSONException; @@ -55,6 +56,8 @@ import org.slf4j.LoggerFactory; import com.google.refine.history.HistoryEntryManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.preference.PreferenceStore; import com.google.refine.preference.TopList; @@ -73,7 +76,6 @@ public abstract class ProjectManager { // Don't spend more than this much time saving projects if doing a quick save static protected final int QUICK_SAVE_MAX_TIME = 1000 * 30; // 30 secs - protected Map _projectsMetadata; protected Map _projectsTags;// TagName, number of projects having that tag protected PreferenceStore _preferenceStore; @@ -99,8 +101,8 @@ public abstract class ProjectManager { transient protected Map _projects; static public ProjectManager singleton; - - protected ProjectManager(){ + + protected ProjectManager() { _projectsMetadata = new HashMap(); _preferenceStore = new PreferenceStore(); _projects = new HashMap(); @@ -191,7 +193,7 @@ public abstract class ProjectManager { } catch (Exception e) { e.printStackTrace(); } - }//FIXME what should be the behaviour if metadata is null? i.e. not found + } Project project = getProject(id); if (project != null && metadata != null && metadata.getModified().isAfter(project.getLastSave())) { @@ -200,8 +202,7 @@ public abstract class ProjectManager { } catch (Exception e) { e.printStackTrace(); } - }//FIXME what should be the behaviour if project is null? i.e. not found or loaded. - //FIXME what should happen if the metadata is found, but not the project? or vice versa? + } } } @@ -212,7 +213,7 @@ public abstract class ProjectManager { * @param projectId * @throws Exception */ - public abstract void saveMetadata(ProjectMetadata metadata, long projectId) throws Exception; + public abstract void saveMetadata(IMetadata metadata, long projectId) throws Exception; /** * Save project to the data store @@ -265,23 +266,23 @@ public abstract class ProjectManager { Project project = _projects.get(id); // don't call getProject() as that will load the project. if (project != null) { + LocalDateTime projectLastSaveTime = project.getLastSave(); boolean hasUnsavedChanges = - metadata.getModified().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() >= project.getLastSave().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + !metadata.getModified().isBefore(projectLastSaveTime); // We use >= instead of just > to avoid the case where a newly created project // has the same modified and last save times, resulting in the project not getting // saved at all. if (hasUnsavedChanges) { - long msecsOverdue = startTimeOfSave.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - project.getLastSave().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - + long msecsOverdue = ChronoUnit.MILLIS.between(projectLastSaveTime, startTimeOfSave); + records.add(new SaveRecord(project, msecsOverdue)); - } else if (!project.getProcessManager().hasPending() - && startTimeOfSave.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - project.getLastSave().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() > PROJECT_FLUSH_DELAY) { + && ChronoUnit.MILLIS.between(projectLastSaveTime, startTimeOfSave) > PROJECT_FLUSH_DELAY) { /* - * It's been a while since the project was last saved and it hasn't been - * modified. We can safely remove it from the cache to save some memory. + * It's been a while since the project was last saved and it hasn't been + * modified. We can safely remove it from the cache to save some memory. */ _projects.remove(id).dispose(); } @@ -307,13 +308,10 @@ public abstract class ProjectManager { "Saving all modified projects ..." : "Saving some modified projects ..." ); - - for (int i = 0; - i < records.size() && - (allModified || (LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - - startTimeOfSave.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() < QUICK_SAVE_MAX_TIME)); + + for (int i = 0;i < records.size() && + (allModified || (ChronoUnit.MILLIS.between(startTimeOfSave, LocalDateTime.now()) < QUICK_SAVE_MAX_TIME)); i++) { - try { saveProject(records.get(i).project); } catch (Exception e) { @@ -351,14 +349,14 @@ public abstract class ProjectManager { /** * Gets the project metadata from memory - * Requires that the metadata has already been loaded from the data store + * Requires that the metadata has already been loaded from the data store. * @param id * @return */ public ProjectMetadata getProjectMetadata(long id) { return _projectsMetadata.get(id); } - + /** * Gets the project metadata from memory * Requires that the metadata has already been loaded from the data store @@ -368,7 +366,7 @@ public abstract class ProjectManager { public ProjectMetadata getProjectMetadata(String name) { for (ProjectMetadata pm : _projectsMetadata.values()) { if (pm.getName().equals(name)) { - return pm; + return pm; } } return null; @@ -420,7 +418,7 @@ public abstract class ProjectManager { userMetadataPreference = new JSONArray(userMeta); } catch (JSONException e1) { logger.warn("wrong definition of userMetadata format. Please use form [{\"name\": \"client name\", \"display\":true}, {\"name\": \"progress\", \"display\":false}]"); - logger.error(ExceptionUtils.getFullStackTrace(e1)); + logger.error(ExceptionUtils.getStackTrace(e1)); } for (int index = 0; index < userMetadataPreference.length(); index++) { @@ -465,7 +463,7 @@ public abstract class ProjectManager { JSONObject projectMetaJsonObj = jsonObjArray.getJSONObject(index); projectMetaJsonObj.put("display", false); } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } } @@ -474,6 +472,7 @@ public abstract class ProjectManager { * Gets all the project Metadata currently held in memory. * @return */ + public Map getAllProjectMetadata() { for(Project project : _projects.values()) { mergeEmptyUserMetadata(project.getMetadata()); @@ -484,13 +483,14 @@ public abstract class ProjectManager { /** * Gets all the project tags currently held in memory - * + * * @return */ public Map getAllProjectTags() { return _projectsTags; } + /** * Gets the required project from the data store * If project does not already exist in memory, it is loaded from the data store @@ -596,8 +596,9 @@ public abstract class ProjectManager { * * @param ps */ - static protected void preparePreferenceStore(PreferenceStore ps) { + public static void preparePreferenceStore(PreferenceStore ps) { ps.put("scripting.expressions", new TopList(s_expressionHistoryMax)); ps.put("scripting.starred-expressions", new TopList(Integer.MAX_VALUE)); } + } diff --git a/main/src/com/google/refine/RefineServlet.java b/main/src/com/google/refine/RefineServlet.java index cb6922235..7081c0ac4 100644 --- a/main/src/com/google/refine/RefineServlet.java +++ b/main/src/com/google/refine/RefineServlet.java @@ -124,7 +124,8 @@ public class RefineServlet extends Butterfly { if (data == null) { throw new ServletException("can't find servlet init config 'refine.data', I have to give up initializing"); } - + logger.error("initializing FileProjectManager with dir"); + logger.error(data); s_dataDir = new File(data); FileProjectManager.initialize(s_dataDir); ImportingManager.initialize(this); diff --git a/main/src/com/google/refine/clustering/binning/FingerprintKeyer.java b/main/src/com/google/refine/clustering/binning/FingerprintKeyer.java index 5d8885996..0d9a07bc1 100644 --- a/main/src/com/google/refine/clustering/binning/FingerprintKeyer.java +++ b/main/src/com/google/refine/clustering/binning/FingerprintKeyer.java @@ -37,7 +37,7 @@ import java.util.Iterator; import java.util.TreeSet; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; public class FingerprintKeyer extends Keyer { diff --git a/main/src/com/google/refine/commands/Command.java b/main/src/com/google/refine/commands/Command.java index d4466c7f4..0ee9b1da9 100644 --- a/main/src/com/google/refine/commands/Command.java +++ b/main/src/com/google/refine/commands/Command.java @@ -52,11 +52,11 @@ import org.slf4j.LoggerFactory; import com.google.refine.Jsonizable; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.browsing.Engine; import com.google.refine.history.HistoryEntry; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.process.Process; import com.google.refine.util.ParsingUtilities; @@ -194,7 +194,7 @@ public abstract class Command { * @return * @throws ServletException */ - protected ProjectMetadata getProjectMetadata(HttpServletRequest request) throws ServletException { + protected ProjectMetadata getMetadata(HttpServletRequest request) throws ServletException { if (request == null) { throw new IllegalArgumentException("parameter 'request' should not be null"); } @@ -312,7 +312,20 @@ public abstract class Command { w.flush(); w.close(); } + + static protected void respondJSONObject( + HttpServletResponse response, JSONObject o) + throws IOException, JSONException { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + response.setHeader("Cache-Control", "no-cache"); + Writer w = response.getWriter(); + w.append(o.toString()); + w.flush(); + w.close(); + } + static protected void respondException(HttpServletResponse response, Exception e) throws IOException, ServletException { diff --git a/main/src/com/google/refine/commands/GetPreferenceCommand.java b/main/src/com/google/refine/commands/GetPreferenceCommand.java index 6780355da..0628efec7 100644 --- a/main/src/com/google/refine/commands/GetPreferenceCommand.java +++ b/main/src/com/google/refine/commands/GetPreferenceCommand.java @@ -54,9 +54,7 @@ public class GetPreferenceCommand extends Command { throws ServletException, IOException { Project project = request.getParameter("project") != null ? getProject(request) : null; - PreferenceStore ps = project != null ? - project.getMetadata().getPreferenceStore() : - ProjectManager.singleton.getPreferenceStore(); + PreferenceStore ps = ProjectManager.singleton.getPreferenceStore(); String prefName = request.getParameter("name"); Object pref = ps.get(prefName); diff --git a/main/src/com/google/refine/commands/HttpHeadersSupport.java b/main/src/com/google/refine/commands/HttpHeadersSupport.java new file mode 100644 index 000000000..6bac9db5b --- /dev/null +++ b/main/src/com/google/refine/commands/HttpHeadersSupport.java @@ -0,0 +1,79 @@ +/* + +Copyright 2017, Owen Stephens +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.commands; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.refine.RefineServlet; + +abstract public class HttpHeadersSupport { + + static final protected Map s_headers = new HashMap(); + + static public class HttpHeaderInfo { + final public String name; + final public String header; + final public String defaultValue; + + HttpHeaderInfo(String header, String defaultValue) { + this.name = header.toLowerCase(); + this.header = header; + this.defaultValue = defaultValue; + } + } + + static { + registerHttpHeader("User-Agent", RefineServlet.FULLNAME); + registerHttpHeader("Accept", "*/*"); + registerHttpHeader("Authorization", ""); + } + + /** + * @param header + * @param defaultValue + */ + static public void registerHttpHeader(String header, String defaultValue) { + s_headers.put(header.toLowerCase(), new HttpHeaderInfo(header, defaultValue)); + } + + static public HttpHeaderInfo getHttpHeaderInfo(String header) { + return s_headers.get(header.toLowerCase()); + } + + static public Set getHttpHeaderLabels() { + return s_headers.keySet(); + } +} diff --git a/main/src/com/google/refine/commands/SetPreferenceCommand.java b/main/src/com/google/refine/commands/SetPreferenceCommand.java index cd0635015..ac5d3cf74 100644 --- a/main/src/com/google/refine/commands/SetPreferenceCommand.java +++ b/main/src/com/google/refine/commands/SetPreferenceCommand.java @@ -52,9 +52,7 @@ public class SetPreferenceCommand extends Command { throws ServletException, IOException { Project project = request.getParameter("project") != null ? getProject(request) : null; - PreferenceStore ps = project != null ? - project.getMetadata().getPreferenceStore() : - ProjectManager.singleton.getPreferenceStore(); + PreferenceStore ps = ProjectManager.singleton.getPreferenceStore(); String prefName = request.getParameter("name"); String valueString = request.getParameter("value"); diff --git a/main/src/com/google/refine/commands/column/AddColumnByFetchingURLsCommand.java b/main/src/com/google/refine/commands/column/AddColumnByFetchingURLsCommand.java index 717fb100f..ebf6a89e0 100644 --- a/main/src/com/google/refine/commands/column/AddColumnByFetchingURLsCommand.java +++ b/main/src/com/google/refine/commands/column/AddColumnByFetchingURLsCommand.java @@ -36,6 +36,7 @@ package com.google.refine.commands.column; import javax.servlet.http.HttpServletRequest; import org.json.JSONObject; +import org.json.JSONArray; import com.google.refine.commands.EngineDependentCommand; import com.google.refine.model.AbstractOperation; @@ -46,7 +47,7 @@ import com.google.refine.operations.column.ColumnAdditionByFetchingURLsOperation public class AddColumnByFetchingURLsCommand extends EngineDependentCommand { @Override protected AbstractOperation createOperation(Project project, - HttpServletRequest request, JSONObject engineConfig) throws Exception { + HttpServletRequest request, JSONObject engineConfig) throws Exception { String baseColumnName = request.getParameter("baseColumnName"); String urlExpression = request.getParameter("urlExpression"); @@ -55,7 +56,8 @@ public class AddColumnByFetchingURLsCommand extends EngineDependentCommand { int delay = Integer.parseInt(request.getParameter("delay")); String onError = request.getParameter("onError"); boolean cacheResponses = Boolean.parseBoolean(request.getParameter("cacheResponses")); - + JSONArray httpHeadersJson = new JSONArray(request.getParameter("httpHeaders")); + return new ColumnAdditionByFetchingURLsOperation( engineConfig, baseColumnName, @@ -64,7 +66,8 @@ public class AddColumnByFetchingURLsCommand extends EngineDependentCommand { newColumnName, columnInsertIndex, delay, - cacheResponses + cacheResponses, + httpHeadersJson ); } diff --git a/main/src/com/google/refine/commands/expr/GetExpressionHistoryCommand.java b/main/src/com/google/refine/commands/expr/GetExpressionHistoryCommand.java index a76a37fd0..950e7eab9 100644 --- a/main/src/com/google/refine/commands/expr/GetExpressionHistoryCommand.java +++ b/main/src/com/google/refine/commands/expr/GetExpressionHistoryCommand.java @@ -63,7 +63,7 @@ public class GetExpressionHistoryCommand extends Command { try { Project project = getProject(request); - List localExpressions = toExpressionList(project.getMetadata().getPreferenceStore().get("scripting.expressions")); + List localExpressions = toExpressionList(ProjectManager.singleton.getPreferenceStore().get("scripting.expressions")); localExpressions = localExpressions.size() > 20 ? localExpressions.subList(0, 20) : localExpressions; List globalExpressions = toExpressionList(ProjectManager.singleton.getPreferenceStore().get("scripting.expressions")); diff --git a/main/src/com/google/refine/commands/expr/LogExpressionCommand.java b/main/src/com/google/refine/commands/expr/LogExpressionCommand.java index dd7df127c..3723869d8 100644 --- a/main/src/com/google/refine/commands/expr/LogExpressionCommand.java +++ b/main/src/com/google/refine/commands/expr/LogExpressionCommand.java @@ -54,7 +54,7 @@ public class LogExpressionCommand extends Command { Project project = getProject(request); String expression = request.getParameter("expression"); - ((TopList) project.getMetadata().getPreferenceStore().get("scripting.expressions")) + ((TopList) ProjectManager.singleton.getPreferenceStore().get("scripting.expressions")) .add(expression); ((TopList) ProjectManager.singleton.getPreferenceStore().get("scripting.expressions")) diff --git a/main/src/com/google/refine/commands/project/DeleteProjectCommand.java b/main/src/com/google/refine/commands/project/DeleteProjectCommand.java index 9d146a01e..f69f11d1b 100644 --- a/main/src/com/google/refine/commands/project/DeleteProjectCommand.java +++ b/main/src/com/google/refine/commands/project/DeleteProjectCommand.java @@ -41,8 +41,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; +import com.google.refine.model.medadata.ProjectMetadata; public class DeleteProjectCommand extends Command { diff --git a/main/src/com/google/refine/commands/project/GetMetadataCommand.java b/main/src/com/google/refine/commands/project/GetMetadataCommand.java new file mode 100644 index 000000000..87f797701 --- /dev/null +++ b/main/src/com/google/refine/commands/project/GetMetadataCommand.java @@ -0,0 +1,48 @@ +package com.google.refine.commands.project; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.everit.json.schema.ValidationException; +import org.json.JSONException; + +import com.google.refine.commands.Command; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.MetadataFactory; +import com.google.refine.model.medadata.MetadataFormat; + +public class GetMetadataCommand extends Command { + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project; + MetadataFormat metadataFormat; + try { + project = getProject(request); + metadataFormat = MetadataFormat.valueOf(request.getParameter("metadataFormat")); + } catch (ServletException e) { + respond(response, "error", e.getLocalizedMessage()); + return; + } + + // for now, only the data package metadata is supported. + if (metadataFormat != MetadataFormat.DATAPACKAGE_METADATA) { + respond(response, "error", "metadata format is not supported"); + return; + } + + IMetadata metadata = MetadataFactory.buildDataPackageMetadata(project); + respondJSONObject(response, metadata.getJSON()); + } catch (JSONException e) { + respondException(response, e); + } catch (ValidationException e) { + respondException(response, e); + } + } +} diff --git a/main/src/com/google/refine/commands/project/GetModelsCommand.java b/main/src/com/google/refine/commands/project/GetModelsCommand.java index e8a2a39f1..ea433fa9c 100644 --- a/main/src/com/google/refine/commands/project/GetModelsCommand.java +++ b/main/src/com/google/refine/commands/project/GetModelsCommand.java @@ -45,6 +45,9 @@ import org.json.JSONWriter; import com.google.refine.commands.Command; import com.google.refine.commands.HttpUtilities; +import com.google.refine.commands.HttpHeadersSupport; +import com.google.refine.commands.HttpHeadersSupport.HttpHeaderInfo; + import com.google.refine.expr.MetaParser; import com.google.refine.expr.MetaParser.LanguageInfo; import com.google.refine.importing.ImportingJob; @@ -116,6 +119,18 @@ public class GetModelsCommand extends Command { writer.endObject(); } writer.endObject(); + + writer.key("httpHeaders"); + writer.object(); + for (String headerLabel : HttpHeadersSupport.getHttpHeaderLabels()) { + HttpHeaderInfo info = HttpHeadersSupport.getHttpHeaderInfo(headerLabel); + writer.key(headerLabel); + writer.object(); + writer.key("header"); writer.value(info.header); + writer.key("defaultValue"); writer.value(info.defaultValue); + writer.endObject(); + } + writer.endObject(); writer.endObject(); } catch (JSONException e) { diff --git a/main/src/com/google/refine/commands/project/ImportProjectCommand.java b/main/src/com/google/refine/commands/project/ImportProjectCommand.java index 740b72c9b..15f5c9b14 100644 --- a/main/src/com/google/refine/commands/project/ImportProjectCommand.java +++ b/main/src/com/google/refine/commands/project/ImportProjectCommand.java @@ -51,9 +51,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.ParsingUtilities; public class ImportProjectCommand extends Command { diff --git a/main/src/com/google/refine/commands/project/PackageProjectCommand.java b/main/src/com/google/refine/commands/project/PackageProjectCommand.java new file mode 100644 index 000000000..2a56e6388 --- /dev/null +++ b/main/src/com/google/refine/commands/project/PackageProjectCommand.java @@ -0,0 +1,83 @@ +package com.google.refine.commands.project; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.tools.tar.TarOutputStream; + +import com.google.refine.ProjectManager; +import com.google.refine.browsing.Engine; +import com.google.refine.commands.Command; +import com.google.refine.exporters.CsvExporter; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.DataPackageMetadata; +import com.google.refine.model.medadata.PackageExtension; + +public class PackageProjectCommand extends Command { + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + ProjectManager.singleton.setBusy(true); + + try { + // get the metadata + String metadata = request.getParameter("metadata"); + InputStream in = IOUtils.toInputStream(metadata, "UTF-8"); + + Project project = getProject(request); + Engine engine = getEngine(request, project); + + // ensure project get saved + DataPackageMetadata dpm = new DataPackageMetadata(); + dpm.loadFromStream(in); + ProjectManager.singleton.ensureProjectSaved(project.id); + + // export project + CsvExporter exporter = new CsvExporter(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer outputStreamWriter = new OutputStreamWriter(baos); + exporter.export(project, null, engine, outputStreamWriter); + + OutputStream os = response.getOutputStream(); + try { + PackageExtension.saveZip(dpm.getPackage(), baos, os); + response.setHeader("Content-Type", "application/x-gzip"); + } finally { + outputStreamWriter.close(); + os.close(); + } + } catch (Exception e) { + respondException(response, e); + } finally { + ProjectManager.singleton.setBusy(false); + } + } + + protected void gzipTarToOutputStream(Project project, OutputStream os) throws IOException { + GZIPOutputStream gos = new GZIPOutputStream(os); + try { + tarToOutputStream(project, gos); + } finally { + gos.close(); + } + } + + protected void tarToOutputStream(Project project, OutputStream os) throws IOException { + TarOutputStream tos = new TarOutputStream(os); + try { + ProjectManager.singleton.exportProject(project.id, tos); + } finally { + tos.close(); + } + } +} diff --git a/main/src/com/google/refine/commands/project/RenameProjectCommand.java b/main/src/com/google/refine/commands/project/RenameProjectCommand.java index b8023df4a..ccea3cf99 100644 --- a/main/src/com/google/refine/commands/project/RenameProjectCommand.java +++ b/main/src/com/google/refine/commands/project/RenameProjectCommand.java @@ -39,8 +39,8 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; +import com.google.refine.model.medadata.ProjectMetadata; public class RenameProjectCommand extends Command { @Override @@ -49,7 +49,7 @@ public class RenameProjectCommand extends Command { try { String name = request.getParameter("name"); - ProjectMetadata pm = getProjectMetadata(request); + ProjectMetadata pm = getMetadata(request); pm.setName(name); diff --git a/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java b/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java index 28399a733..b5b14b648 100644 --- a/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java +++ b/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java @@ -9,15 +9,14 @@ import javax.servlet.http.HttpServletResponse; import org.json.JSONException; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; public class SetProjectMetadataCommand extends Command { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - Project project = request.getParameter("project") != null ? getProject(request) : null; String metaName = request.getParameter("name"); String valueString = request.getParameter("value"); @@ -33,7 +32,7 @@ public class SetProjectMetadataCommand extends Command { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "application/json"); - meta.setAnyField(metaName, valueString); + meta.setAnyStringField(metaName, valueString); ProjectManager.singleton.saveMetadata(meta, project.id); respond(response, "{ \"code\" : \"ok\" }"); diff --git a/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java b/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java index 2bffa2a9f..00d3e3c61 100644 --- a/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java +++ b/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java @@ -37,9 +37,9 @@ import javax.servlet.http.HttpServletResponse; import org.json.JSONException; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; public class SetProjectTagsCommand extends Command { @Override diff --git a/main/src/com/google/refine/commands/project/ValidateSchemaCommand.java b/main/src/com/google/refine/commands/project/ValidateSchemaCommand.java new file mode 100644 index 000000000..fbb96a327 --- /dev/null +++ b/main/src/com/google/refine/commands/project/ValidateSchemaCommand.java @@ -0,0 +1,42 @@ +package com.google.refine.commands.project; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.google.refine.ProjectManager; +import com.google.refine.commands.Command; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.validator.ValidateOperation; +import com.google.refine.util.ParsingUtilities; + +public class ValidateSchemaCommand extends Command { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + ProjectManager.singleton.setBusy(true); + try { + Project project = getProject(request); + JSONObject optionObj = ParsingUtilities.evaluateJsonStringToObject( + request.getParameter("options")); + + new ValidateOperation(project, optionObj).startProcess(); + + respond(response, "{ \"code\" : \"ok\" }"); + } catch (JSONException e) { + respondException(response, e); + } catch (ServletException e) { + respond(response, "error", e.getLocalizedMessage()); + return; + } finally { + ProjectManager.singleton.setBusy(false); + } + } +} diff --git a/main/src/com/google/refine/commands/row/GetRowsCommand.java b/main/src/com/google/refine/commands/row/GetRowsCommand.java index 32b088660..ca5cdf192 100644 --- a/main/src/com/google/refine/commands/row/GetRowsCommand.java +++ b/main/src/com/google/refine/commands/row/GetRowsCommand.java @@ -180,9 +180,9 @@ public class GetRowsCommand extends Command { } // metadata refresh for row mode and record mode - if (project.getMetadata() != null) { - project.getMetadata().setRowCount(project.rows.size()); - } + if (project.getMetadata() != null) { + project.getMetadata().setRowCount(project.rows.size()); + } } catch (Exception e) { respondException(response, e); } diff --git a/main/src/com/google/refine/commands/workspace/GetAllProjectMetadataCommand.java b/main/src/com/google/refine/commands/workspace/GetAllProjectMetadataCommand.java index b262c2cb7..786f005c0 100644 --- a/main/src/com/google/refine/commands/workspace/GetAllProjectMetadataCommand.java +++ b/main/src/com/google/refine/commands/workspace/GetAllProjectMetadataCommand.java @@ -47,8 +47,8 @@ import org.json.JSONException; import org.json.JSONWriter; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.Command; +import com.google.refine.model.medadata.ProjectMetadata; public class GetAllProjectMetadataCommand extends Command { @Override diff --git a/main/src/com/google/refine/exporters/CustomizableTabularExporterUtilities.java b/main/src/com/google/refine/exporters/CustomizableTabularExporterUtilities.java index 08c31e356..7f392fc46 100644 --- a/main/src/com/google/refine/exporters/CustomizableTabularExporterUtilities.java +++ b/main/src/com/google/refine/exporters/CustomizableTabularExporterUtilities.java @@ -44,7 +44,7 @@ import java.util.Map; import java.util.Properties; import java.util.TimeZone; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; diff --git a/main/src/com/google/refine/exporters/HtmlTableExporter.java b/main/src/com/google/refine/exporters/HtmlTableExporter.java index a4a72de4f..3cb6ee926 100644 --- a/main/src/com/google/refine/exporters/HtmlTableExporter.java +++ b/main/src/com/google/refine/exporters/HtmlTableExporter.java @@ -38,7 +38,7 @@ import java.io.Writer; import java.util.List; import java.util.Properties; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONObject; import com.google.refine.ProjectManager; @@ -103,7 +103,7 @@ public class HtmlTableExporter implements WriterExporter { if (cellData.link != null) { writer.write(""); } writer.write(StringEscapeUtils.escapeXml(cellData.text)); diff --git a/main/src/com/google/refine/expr/ExpressionUtils.java b/main/src/com/google/refine/expr/ExpressionUtils.java index 398eb78a7..2bd2b9c1d 100644 --- a/main/src/com/google/refine/expr/ExpressionUtils.java +++ b/main/src/com/google/refine/expr/ExpressionUtils.java @@ -125,11 +125,9 @@ public class ExpressionUtils { static public boolean sameValue(Object v1, Object v2) { if (v1 == null) { - return (v2 == null) - || (v2 instanceof String && ((String) v2).length() == 0); + return (v2 == null) ; } else if (v2 == null) { - return (v1 == null) - || (v1 instanceof String && ((String) v1).length() == 0); + return (v1 == null); } else { return v1.equals(v2); } diff --git a/main/src/com/google/refine/expr/functions/ToDate.java b/main/src/com/google/refine/expr/functions/ToDate.java index cc9b21f82..c748be0dc 100644 --- a/main/src/com/google/refine/expr/functions/ToDate.java +++ b/main/src/com/google/refine/expr/functions/ToDate.java @@ -42,7 +42,7 @@ import java.util.GregorianCalendar; import java.util.Locale; import java.util.Properties; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONWriter; @@ -50,29 +50,30 @@ import com.google.refine.expr.EvalError; import com.google.refine.expr.util.CalendarParser; import com.google.refine.expr.util.CalendarParserException; import com.google.refine.grel.Function; +import com.google.refine.grel.ControlFunctionRegistry; import com.google.refine.util.ParsingUtilities; public class ToDate implements Function { @Override public Object call(Properties bindings, Object[] args) { - if (args.length == 0) { - // missing value, can this happen? - return null; - } - Object arg0 = args[0]; String o1; - if (arg0 instanceof Date) { - return arg0; - } else if (arg0 instanceof Calendar) { - return ((Calendar) arg0).getTime(); - } else if (arg0 instanceof Long) { - o1 = ((Long) arg0).toString(); // treat integers as years - } else if (arg0 instanceof String) { - o1 = (String) arg0; + if (args.length == 0) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + " expects at least one argument"); } else { - // ignore cell values that aren't strings - return new EvalError("Not a String - cannot parse to date"); + Object arg0 = args[0]; + if (arg0 instanceof Date) { + return arg0; + } else if (arg0 instanceof Calendar) { + return ((Calendar) arg0).getTime(); + } else if (arg0 instanceof Long) { + o1 = ((Long) arg0).toString(); // treat integers as years + } else if (arg0 instanceof String && arg0.toString().trim().length() > 0) { + o1 = (String) arg0; + } else { + // ignore cell values that aren't Date, Calendar, Long or String + return new EvalError("Unable to parse as date"); + } } // "o, boolean month_first (optional)" @@ -98,13 +99,12 @@ public class ToDate implements Function { // } catch (DatatypeConfigurationException e2) { // } } - return new EvalError("Cannot parse to date"); + return new EvalError("Unable to parse as date"); } - } - - // "o, format1, format2 (optional), ..." - Locale locale = Locale.getDefault(); - if (args.length>=2) { + } else if (args.length>=2) { + // "o, format1, format2 (optional), ..." + Locale locale = Locale.getDefault(); + for (int i=1;i allMatches = new ArrayList(); + + if (args.length == 2) { + Object s = args[0]; + Object p = args[1]; + + if (s != null && p != null && (p instanceof String || p instanceof Pattern)) { + + Pattern pattern = (p instanceof String) ? Pattern.compile((String) p) : (Pattern) p; + + Matcher matcher = pattern.matcher(s.toString()); + + while (matcher.find()) { + allMatches.add(matcher.group()); + } + } + + return allMatches.toArray(new String[0]); + } + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + " expects a string or a regexp"); + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + + writer.object(); + writer.key("description"); writer.value("Returns all the occurances of match given regular expression"); + writer.key("params"); writer.value("string or regexp"); + writer.key("returns"); writer.value("array of strings"); + writer.endObject(); + } +} diff --git a/main/src/com/google/refine/expr/functions/strings/NGram.java b/main/src/com/google/refine/expr/functions/strings/NGram.java index e2ff69fea..4c715a938 100644 --- a/main/src/com/google/refine/expr/functions/strings/NGram.java +++ b/main/src/com/google/refine/expr/functions/strings/NGram.java @@ -35,7 +35,7 @@ package com.google.refine.expr.functions.strings; import java.util.Properties; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/expr/functions/strings/Range.java b/main/src/com/google/refine/expr/functions/strings/Range.java new file mode 100644 index 000000000..c26d45e5a --- /dev/null +++ b/main/src/com/google/refine/expr/functions/strings/Range.java @@ -0,0 +1,308 @@ +package com.google.refine.expr.functions.strings; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONWriter; + +import com.google.refine.expr.EvalError; +import com.google.refine.grel.ControlFunctionRegistry; +import com.google.refine.grel.Function; + +/** + * Implements the logic behind the range function. + * + * The range function can take in a single string of the form 'a, b, c' or three + * integers a, b, c where a and b represents the first (inclusive) and last (exclusive) + * numbers in the range respectively. If b is not given, a defaults to the range end + * and 0 becomes the range start. c is optional and represents the step (increment) + * for the generated sequence. + */ +public class Range implements Function { + + private static final String SEPARATOR = ","; + + private static final String lastCharacterCommaRegex = ",$"; + private static final Pattern lastCharacterCommaPattern = Pattern.compile(lastCharacterCommaRegex); + + private static final int DEFAULT_START = 0; + private static final int DEFAULT_STEP = 1; + + private static final Integer[] EMPTY_ARRAY = new Integer[0]; + + @Override + public Object call(Properties bindings, Object[] args) { + + if (args.length == 1) { + return createRangeWithOneGivenArgument(args); + } else if (args.length == 2) { + return createRangeWithTwoGivenArguments(args); + } else if (args.length == 3) { + return createRangeWithThreeGivenArguments(args); + } + + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + + /** + * Checks if a given string has a comma as the last character. + * + * This is primarily used to detect edge cases like doing range("1,"). + */ + private boolean hasCommaAsLastCharacter(String test) { + Matcher lastCharacterCommaMatcher = lastCharacterCommaPattern.matcher(test); + return lastCharacterCommaMatcher.find(); + } + + /** + * Processes the single argument given to determine if the argument is (i) a valid string, (ii) a + * valid integer, or (iii) an invalid argument. + * + * If the argument is a valid string, it can either be in the form 'a', or 'a, b' or 'a, b, c' + * where a and b are the start and end of the range respectively, and c is the optional + * step argument. In the case where 'a' is the only argument, 'a' becomes the range end (exclusive) + * and 0 becomes the default range start. + * + * If the argument is a valid integer, it can will default to become the range end, and 0 defaults + * to become the range start. + * + * In all other cases, the argument is considered invalid. + */ + private Object createRangeWithOneGivenArgument(Object[] args) { + Object range = args[0]; + + int rangeStart = DEFAULT_START; + int rangeEnd = 0; + int rangeStep = DEFAULT_STEP; + + // Check for valid string argument(s) + if (range != null && range instanceof String) { + String rangeString = ((String) range).trim(); + String[] rangeValues = rangeString.split(SEPARATOR); + + if (hasCommaAsLastCharacter(rangeString)) { + return new EvalError("the last character in the input string should not be a comma"); + } + + try { + if (rangeValues.length == 1) { + rangeEnd = Integer.parseInt(rangeValues[0].trim()); + return createRange(rangeStart, rangeEnd, rangeStep); + } else if (rangeValues.length == 2) { + rangeStart = Integer.parseInt(rangeValues[0].trim()); + rangeEnd = Integer.parseInt(rangeValues[1].trim()); + return createRange(rangeStart, rangeEnd, rangeStep); + } else if (rangeValues.length == 3) { + rangeStart = Integer.parseInt(rangeValues[0].trim()); + rangeEnd = Integer.parseInt(rangeValues[1].trim()); + rangeStep = Integer.parseInt(rangeValues[2].trim()); + return createRange(rangeStart, rangeEnd, rangeStep); + } + } catch (NumberFormatException nfe) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + } + + // Check for valid negative integer argument + if (range != null && range instanceof Double && (Double) range % 1 == 0) { + range = ((Double) range).intValue(); + return createRange(DEFAULT_START, rangeEnd, DEFAULT_STEP); + } + + // Check for valid positive integer argument + if (range != null) { + try { + rangeEnd = Integer.parseInt(String.valueOf(range)); + return createRange(DEFAULT_START, rangeEnd, DEFAULT_STEP); + } catch (NumberFormatException nfe) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + } + + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + + /** + * Processes the two arguments given to determine if the arguments are (i) two valid strings, + * (ii) two valid integers or (iii) a valid string and an integer or (iv) invalid arguments. + * + * If the arguments are valid strings, the strings can either be such that (i) each string contains + * single arguments (i.e. two arguments in total), or (ii) one string contains one argument and the other + * string contains two argument (i.e. three arguments in total). + * + * If the arguments are a valid string and a valid integer, the string can be such that (i) the string + * contains a single argument (i.e. two arguments in total) or (ii) the string contains two arguments + * (i.e. three arguments in total). + * + * In all other cases, the arguments are considered invalid. + */ + private Object createRangeWithTwoGivenArguments(Object[] args) { + Object firstArg = args[0]; + Object secondArg = args[1]; + + int rangeStart = DEFAULT_START; + int rangeEnd = 0; + int rangeStep = DEFAULT_STEP; + + if (firstArg == null || secondArg == null) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + + boolean hasString = false; + boolean hasTwoIntegers = true; + + if (firstArg instanceof String || secondArg instanceof String) { + hasString = true; + hasTwoIntegers = false; + } + + boolean hasTwoArguments = hasTwoIntegers; + boolean hasThreeArguments = false; + + // Deal with valid negative integers + if (firstArg instanceof Double && (Double) firstArg % 1 == 0) { + firstArg = ((Double) firstArg).intValue(); + } + + if (secondArg instanceof Double && (Double) secondArg % 1 == 0) { + secondArg = ((Double) secondArg).intValue(); + } + + String firstArgStringified = String.valueOf(firstArg).trim(); + String secondArgStringified = String.valueOf(secondArg).trim(); + String thirdArgStringified = ""; + + if (hasCommaAsLastCharacter(firstArgStringified) || hasCommaAsLastCharacter(secondArgStringified)) { + return new EvalError("the last character in the input string should not be a comma"); + } + + // Check if the strings are valid strings (e.g. range("1, 2", "3, 4") should fail but + // range("1, 2", "1") should pass) + if (hasString) { + String[] firstArgArray = firstArgStringified.split(SEPARATOR); + String[] secondArgArray = secondArgStringified.split(SEPARATOR); + + int combinedArrayLength = firstArgArray.length + secondArgArray.length; + + if (combinedArrayLength == 3) { + hasThreeArguments = true; + + if (firstArgArray.length == 1) { + secondArgStringified = secondArgArray[0].trim(); + thirdArgStringified = secondArgArray[1].trim(); + } else { + firstArgStringified = firstArgArray[0].trim(); + secondArgStringified = firstArgArray[1].trim(); + thirdArgStringified = secondArgArray[0].trim(); + } + + } else if (combinedArrayLength == 2) { + hasTwoArguments = true; + } + } + + try { + if (hasTwoArguments) { + rangeStart = Integer.parseInt(firstArgStringified); + rangeEnd = Integer.parseInt(secondArgStringified); + return createRange(rangeStart, rangeEnd, rangeStep); + } else if (hasThreeArguments) { + rangeStart = Integer.parseInt(firstArgStringified); + rangeEnd = Integer.parseInt(secondArgStringified); + rangeStep = Integer.parseInt(thirdArgStringified); + return createRange(rangeStart, rangeEnd, rangeStep); + } + } catch (NumberFormatException nfe) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + + /** + * Processes the three arguments given to determine if the arguments are (i) three valid strings, + * (ii) three valid integers, (iii) two valid strings and a valid integer, (iv) a valid string and + * two valid integers or (v) invalid arguments. + * + * In this case, all valid strings can only contain a single argument. + */ + private Object createRangeWithThreeGivenArguments(Object[] args) { + Object firstArg = args[0]; + Object secondArg = args[1]; + Object thirdArg = args[2]; + + // Deal with negative integers first + if (firstArg != null && firstArg instanceof Double && (Double) firstArg % 1 == 0) { + firstArg = ((Double) firstArg).intValue(); + } + + if (secondArg != null && secondArg instanceof Double && (Double) secondArg % 1 == 0) { + secondArg = ((Double) secondArg).intValue(); + } + + if (thirdArg != null && thirdArg instanceof Double && (Double) thirdArg % 1 == 0) { + thirdArg = ((Double) thirdArg).intValue(); + } + + try { + int rangeStart = Integer.parseInt(String.valueOf(firstArg).trim()); + int rangeEnd = Integer.parseInt(String.valueOf(secondArg).trim()); + int rangeStep = Integer.parseInt(String.valueOf(thirdArg).trim()); + return createRange(rangeStart, rangeEnd, rangeStep); + } catch (NumberFormatException nfe) { + return new EvalError(ControlFunctionRegistry.getFunctionName(this) + + " expects a string of the form 'a, b, c' or integers a, b, c where a and b " + + "are the start and the end of the range respectively and c is the step (increment)"); + } + } + + /** + * Creates a range from the given range values with the given step. + * + * The generated range is either an increasing sequence or a decreasing sequence, and + * each number in the sequence differs from the next number by the step value. + */ + private static Object createRange(int start, int stop, int step) { + if ((start > stop && step > 0) || (start < stop && step < 0) || step == 0) { + return EMPTY_ARRAY; + } + + int rangeSize = (int) (Math.ceil(((double) Math.abs(start - stop))/ Math.abs(step))); + + Integer[] generatedRange = new Integer[rangeSize]; + + for (int i = 0; i < rangeSize; i++) { + generatedRange[i] = start + step * i; + } + + return generatedRange; + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + + writer.object(); + writer.key("description"); writer.value( + "Returns an array where a and b are the start and the end of the range respectively and c is the step (increment)."); + writer.key("params"); writer.value("A single string 'a', 'a, b' or 'a, b, c' or one, two or three integers a or a, b or a, b, c"); + writer.key("returns"); writer.value("array"); + writer.endObject(); + } +} diff --git a/main/src/com/google/refine/expr/functions/strings/Reinterpret.java b/main/src/com/google/refine/expr/functions/strings/Reinterpret.java index a62b16c28..a376912e5 100644 --- a/main/src/com/google/refine/expr/functions/strings/Reinterpret.java +++ b/main/src/com/google/refine/expr/functions/strings/Reinterpret.java @@ -40,11 +40,11 @@ import org.json.JSONException; import org.json.JSONWriter; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.expr.EvalError; import com.google.refine.grel.ControlFunctionRegistry; import com.google.refine.grel.Function; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; public class Reinterpret implements Function { diff --git a/main/src/com/google/refine/expr/functions/strings/ReplaceChars.java b/main/src/com/google/refine/expr/functions/strings/ReplaceChars.java index ca4f91bce..4c32ff606 100644 --- a/main/src/com/google/refine/expr/functions/strings/ReplaceChars.java +++ b/main/src/com/google/refine/expr/functions/strings/ReplaceChars.java @@ -35,7 +35,7 @@ package com.google.refine.expr.functions.strings; import java.util.Properties; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/expr/functions/strings/Split.java b/main/src/com/google/refine/expr/functions/strings/Split.java index a1f591321..2532c9cfc 100644 --- a/main/src/com/google/refine/expr/functions/strings/Split.java +++ b/main/src/com/google/refine/expr/functions/strings/Split.java @@ -36,7 +36,7 @@ package com.google.refine.expr.functions.strings; import java.util.Properties; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/expr/functions/strings/SplitByCharType.java b/main/src/com/google/refine/expr/functions/strings/SplitByCharType.java index b7942f7ec..85e316974 100644 --- a/main/src/com/google/refine/expr/functions/strings/SplitByCharType.java +++ b/main/src/com/google/refine/expr/functions/strings/SplitByCharType.java @@ -35,7 +35,7 @@ package com.google.refine.expr.functions.strings; import java.util.Properties; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/expr/functions/strings/ToTitlecase.java b/main/src/com/google/refine/expr/functions/strings/ToTitlecase.java index 244a3cf2d..e1bb2d7d8 100644 --- a/main/src/com/google/refine/expr/functions/strings/ToTitlecase.java +++ b/main/src/com/google/refine/expr/functions/strings/ToTitlecase.java @@ -35,7 +35,7 @@ package com.google.refine.expr.functions.strings; import java.util.Properties; -import org.apache.commons.lang.WordUtils; +import org.apache.commons.lang3.text.WordUtils; import org.json.JSONException; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/expr/functions/strings/Unescape.java b/main/src/com/google/refine/expr/functions/strings/Unescape.java index e08436a22..6e901b070 100644 --- a/main/src/com/google/refine/expr/functions/strings/Unescape.java +++ b/main/src/com/google/refine/expr/functions/strings/Unescape.java @@ -37,7 +37,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Properties; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONException; import org.json.JSONWriter; @@ -56,13 +56,13 @@ public class Unescape implements Function { String s = (String) o1; String mode = ((String) o2).toLowerCase(); if ("html".equals(mode)) { - return StringEscapeUtils.unescapeHtml(s); + return StringEscapeUtils.unescapeHtml4(s); } else if ("xml".equals(mode)) { return StringEscapeUtils.unescapeXml(s); } else if ("csv".equals(mode)) { return StringEscapeUtils.unescapeCsv(s); } else if ("javascript".equals(mode)) { - return StringEscapeUtils.unescapeJavaScript(s); + return StringEscapeUtils.escapeEcmaScript(s); } else if ("url".equals(mode)) { try { return URLDecoder.decode(s,"UTF-8"); diff --git a/main/src/com/google/refine/grel/ControlFunctionRegistry.java b/main/src/com/google/refine/grel/ControlFunctionRegistry.java index 6dad3c2dd..abc343a74 100644 --- a/main/src/com/google/refine/grel/ControlFunctionRegistry.java +++ b/main/src/com/google/refine/grel/ControlFunctionRegistry.java @@ -109,11 +109,13 @@ import com.google.refine.expr.functions.strings.IndexOf; import com.google.refine.expr.functions.strings.LastIndexOf; import com.google.refine.expr.functions.strings.MD5; import com.google.refine.expr.functions.strings.Match; +import com.google.refine.expr.functions.strings.Find; import com.google.refine.expr.functions.strings.NGram; import com.google.refine.expr.functions.strings.NGramFingerprint; import com.google.refine.expr.functions.strings.ParseJson; import com.google.refine.expr.functions.strings.Partition; import com.google.refine.expr.functions.strings.Phonetic; +import com.google.refine.expr.functions.strings.Range; import com.google.refine.expr.functions.strings.RPartition; import com.google.refine.expr.functions.strings.Reinterpret; import com.google.refine.expr.functions.strings.Replace; @@ -200,6 +202,7 @@ public class ControlFunctionRegistry { registerFunction("substring", new Slice()); registerFunction("replace", new Replace()); registerFunction("replaceChars", new ReplaceChars()); + registerFunction("range", new Range()); registerFunction("split", new Split()); registerFunction("smartSplit", new SmartSplit()); registerFunction("splitByCharType", new SplitByCharType()); @@ -226,6 +229,7 @@ public class ControlFunctionRegistry { registerFunction("parseJson", new ParseJson()); registerFunction("ngram", new NGram()); registerFunction("match", new Match()); + registerFunction("find", new Find()); // HTML functions from JSoup registerFunction("parseHtml", new ParseHtml()); diff --git a/main/src/com/google/refine/grel/controls/IsNumeric.java b/main/src/com/google/refine/grel/controls/IsNumeric.java index 660022e5a..24013793d 100644 --- a/main/src/com/google/refine/grel/controls/IsNumeric.java +++ b/main/src/com/google/refine/grel/controls/IsNumeric.java @@ -33,7 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.grel.controls; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; public class IsNumeric extends IsTest { @Override diff --git a/main/src/com/google/refine/importers/ExcelImporter.java b/main/src/com/google/refine/importers/ExcelImporter.java index 2cd9cfbd2..2e49aa7c8 100644 --- a/main/src/com/google/refine/importers/ExcelImporter.java +++ b/main/src/com/google/refine/importers/ExcelImporter.java @@ -44,7 +44,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLException; import org.apache.poi.common.usermodel.Hyperlink; @@ -60,13 +60,13 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Cell; import com.google.refine.model.Project; import com.google.refine.model.Recon; import com.google.refine.model.Recon.Judgment; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.model.ReconCandidate; import com.google.refine.util.JSONUtilities; @@ -191,7 +191,7 @@ public class ExcelImporter extends TabularImportingParserBase { // value is fileName#sheetIndex fileNameAndSheetIndex = sheetObj.getString("fileNameAndSheetIndex").split("#"); } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } if (!fileNameAndSheetIndex[0].equals(fileSource)) diff --git a/main/src/com/google/refine/importers/FixedWidthImporter.java b/main/src/com/google/refine/importers/FixedWidthImporter.java index 89ca6ad0a..5eb437095 100644 --- a/main/src/com/google/refine/importers/FixedWidthImporter.java +++ b/main/src/com/google/refine/importers/FixedWidthImporter.java @@ -14,10 +14,10 @@ import java.util.List; import org.json.JSONArray; import org.json.JSONObject; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class FixedWidthImporter extends TabularImportingParserBase { diff --git a/main/src/com/google/refine/importers/ImportingParserBase.java b/main/src/com/google/refine/importers/ImportingParserBase.java index b77206a8a..17bad6063 100644 --- a/main/src/com/google/refine/importers/ImportingParserBase.java +++ b/main/src/com/google/refine/importers/ImportingParserBase.java @@ -44,7 +44,6 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.ImporterUtilities.MultiFileReadingProgress; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingParser; @@ -52,6 +51,7 @@ import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; abstract public class ImportingParserBase implements ImportingParser { diff --git a/main/src/com/google/refine/importers/JsonImporter.java b/main/src/com/google/refine/importers/JsonImporter.java index 35092e83d..93c66e1d9 100644 --- a/main/src/com/google/refine/importers/JsonImporter.java +++ b/main/src/com/google/refine/importers/JsonImporter.java @@ -49,7 +49,6 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.tree.ImportColumnGroup; import com.google.refine.importers.tree.TreeImportingParserBase; import com.google.refine.importers.tree.TreeReader; @@ -57,6 +56,7 @@ import com.google.refine.importers.tree.TreeReaderException; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class JsonImporter extends TreeImportingParserBase { diff --git a/main/src/com/google/refine/importers/LineBasedImporter.java b/main/src/com/google/refine/importers/LineBasedImporter.java index c0e561a77..7b2a2eda2 100644 --- a/main/src/com/google/refine/importers/LineBasedImporter.java +++ b/main/src/com/google/refine/importers/LineBasedImporter.java @@ -10,9 +10,9 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class LineBasedImporter extends TabularImportingParserBase { diff --git a/main/src/com/google/refine/importers/OdsImporter.java b/main/src/com/google/refine/importers/OdsImporter.java index b6e5147dd..dce702252 100644 --- a/main/src/com/google/refine/importers/OdsImporter.java +++ b/main/src/com/google/refine/importers/OdsImporter.java @@ -44,7 +44,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -55,13 +55,13 @@ import org.odftoolkit.odfdom.doc.table.OdfTableRow; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Cell; import com.google.refine.model.Project; import com.google.refine.model.Recon; import com.google.refine.model.Recon.Judgment; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.model.ReconCandidate; import com.google.refine.util.JSONUtilities; @@ -150,7 +150,7 @@ public class OdsImporter extends TabularImportingParserBase { // value is fileName#sheetIndex fileNameAndSheetIndex = sheetObj.getString("fileNameAndSheetIndex").split("#"); } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } if (!fileNameAndSheetIndex[0].equals(fileSource)) diff --git a/main/src/com/google/refine/importers/RdfTripleImporter.java b/main/src/com/google/refine/importers/RdfTripleImporter.java index bd73402bb..6df68cc01 100644 --- a/main/src/com/google/refine/importers/RdfTripleImporter.java +++ b/main/src/com/google/refine/importers/RdfTripleImporter.java @@ -50,7 +50,6 @@ import org.jrdf.parser.RdfReader; import org.jrdf.util.ClosableIterable; import org.json.JSONObject; -import com.google.refine.ProjectMetadata; import com.google.refine.expr.ExpressionUtils; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Cell; @@ -58,6 +57,7 @@ import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; public class RdfTripleImporter extends ImportingParserBase { private RdfReader rdfReader; diff --git a/main/src/com/google/refine/importers/SeparatorBasedImporter.java b/main/src/com/google/refine/importers/SeparatorBasedImporter.java index ec662486e..18cba2080 100644 --- a/main/src/com/google/refine/importers/SeparatorBasedImporter.java +++ b/main/src/com/google/refine/importers/SeparatorBasedImporter.java @@ -49,15 +49,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; + import org.json.JSONObject; import au.com.bytecode.opencsv.CSVParser; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class SeparatorBasedImporter extends TabularImportingParserBase { @@ -75,6 +77,7 @@ public class SeparatorBasedImporter extends TabularImportingParserBase { JSONUtilities.safePut(options, "guessCellValueTypes", false); JSONUtilities.safePut(options, "processQuotes", true); + JSONUtilities.safePut(options, "quoteCharacter", String.valueOf(CSVParser.DEFAULT_QUOTE_CHARACTER)); return options; } @@ -98,9 +101,15 @@ public class SeparatorBasedImporter extends TabularImportingParserBase { boolean processQuotes = JSONUtilities.getBoolean(options, "processQuotes", true); boolean strictQuotes = JSONUtilities.getBoolean(options, "strictQuotes", false); + Character quote = CSVParser.DEFAULT_QUOTE_CHARACTER; + String quoteCharacter = JSONUtilities.getString(options, "quoteCharacter", null); + if (quoteCharacter != null && quoteCharacter.trim().length() == 1) { + quote = quoteCharacter.trim().charAt(0); + } + final CSVParser parser = new CSVParser( sep, - CSVParser.DEFAULT_QUOTE_CHARACTER, + quote, (char) 0, // we don't want escape processing strictQuotes, CSVParser.DEFAULT_IGNORE_LEADING_WHITESPACE, diff --git a/main/src/com/google/refine/importers/TabularImportingParserBase.java b/main/src/com/google/refine/importers/TabularImportingParserBase.java index c7cdff217..ffb8f58ad 100644 --- a/main/src/com/google/refine/importers/TabularImportingParserBase.java +++ b/main/src/com/google/refine/importers/TabularImportingParserBase.java @@ -41,13 +41,13 @@ import java.util.List; import org.json.JSONObject; -import com.google.refine.ProjectMetadata; import com.google.refine.expr.ExpressionUtils; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Cell; import com.google.refine.model.Column; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; abstract public class TabularImportingParserBase extends ImportingParserBase { diff --git a/main/src/com/google/refine/importers/WikitextImporter.java b/main/src/com/google/refine/importers/WikitextImporter.java index 382abf3bb..ab624b946 100644 --- a/main/src/com/google/refine/importers/WikitextImporter.java +++ b/main/src/com/google/refine/importers/WikitextImporter.java @@ -29,6 +29,7 @@ import org.sweble.wikitext.parser.nodes.WtTemplateArguments; import org.sweble.wikitext.parser.nodes.WtText; import org.sweble.wikitext.parser.nodes.WtInternalLink; import org.sweble.wikitext.parser.nodes.WtExternalLink; +import org.sweble.wikitext.parser.nodes.WtImageLink; import org.sweble.wikitext.parser.nodes.WtLinkTitle; import org.sweble.wikitext.parser.nodes.WtLinkTitle.WtNoLinkTitle; import org.sweble.wikitext.parser.nodes.WtUrl; @@ -56,13 +57,13 @@ import org.sweble.wikitext.parser.preprocessor.PreprocessedWikitext; import xtc.parser.ParseException; -import com.google.refine.ProjectMetadata; import com.google.refine.importing.ImportingJob; import com.google.refine.model.Cell; import com.google.refine.model.Column; import com.google.refine.model.Project; import com.google.refine.model.Recon; import com.google.refine.model.ReconStats; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.model.recon.StandardReconConfig.ColumnDetail; import com.google.refine.util.JSONUtilities; import com.google.refine.model.recon.StandardReconConfig; @@ -129,7 +130,6 @@ public class WikitextImporter extends TabularImportingParserBase { public class WikitextTableVisitor extends AstVisitor { public String caption; - public List header; public List> rows; public List> references; public List wikilinkedCells; @@ -163,7 +163,6 @@ public class WikitextImporter extends TabularImportingParserBase { this.blankSpanningCells = blankSpanningCells; this.includeRawTemplates = includeRawTemplates; caption = null; - header = new ArrayList(); rows = new ArrayList>(); references = new ArrayList>(); wikilinkedCells = new ArrayList(); @@ -178,7 +177,7 @@ public class WikitextImporter extends TabularImportingParserBase { currentReferenceName = null; colspan = 0; rowspan = 0; - rowId = -1; + rowId = 0; spanningCellIdx = 0; internalLinksInCell = new ArrayList(); namedReferences = new HashMap(); @@ -202,77 +201,80 @@ public class WikitextImporter extends TabularImportingParserBase { iterate(e); } - public void visit(WtTableHeader e) { - String columnName = renderCellAsString(e); - header.add(columnName); - // For the header, we ignore rowspan and manually add cells for colspan - if (colspan > 1) { - for (int i = 0; i < colspan-1; i++) { - header.add(columnName); - } - } - } - public void visit(WtTableCaption e) { caption = renderCellAsString(e); } public void visit(WtTableRow e) { - if (currentRow == null) { - if (rowId == -1) { - // no header was found, start on the first row - rowId = 0; - } - currentRow = new ArrayList(); - currentRowReferences = new ArrayList(); - spanningCellIdx = 0; - addSpanningCells(); - iterate(e); - if(currentRow.size() > 0) { - rows.add(currentRow); - references.add(currentRowReferences); - rowId++; - } - currentRow = null; + if (currentRow != null) { + finishRow(); } + startRow(); + iterate(e); + finishRow(); + } + + private void startRow() { + currentRow = new ArrayList(); + currentRowReferences = new ArrayList(); + spanningCellIdx = 0; + addSpanningCells(); + } + + private void finishRow() { + if(currentRow.size() > 0) { + rows.add(currentRow); + references.add(currentRowReferences); + rowId++; + } + currentRow = null; } public void visit(WtTableCell e) { - if (currentRow != null) { - rowspan = 1; - colspan = 1; - internalLinksInCell.clear(); - currentReference = null; - currentReferenceName = null; - - String value = renderCellAsString(e); - - int colId = currentRow.size(); - - // Add the cell to the row we are currently building - currentRow.add(value); - currentRowReferences.add(currentReference); - - // Reconcile it if we found exactly one link in the cell - String reconciled = null; - if (internalLinksInCell.size() == 1) { - reconciled = internalLinksInCell.get(0); - wikilinkedCells.add(new WikilinkedCell(reconciled, rowId, colId)); - } - - // Mark it as spanning if we found the tags - if (colspan > 1 || rowspan > 1) { - SpanningCell spanningCell = new SpanningCell( - value, reconciled, currentReference, - rowId, colId, rowspan, colspan); - spanningCells.add(spanningCellIdx, spanningCell); - } - - // Add all spanning cells that need to be inserted after this one. - addSpanningCells(); + addCell(e); + } + + public void visit(WtTableHeader e) { + addCell(e); + } + + public void addCell(WtNode e) { + if (currentRow == null) { + startRow(); } + rowspan = 1; + colspan = 1; + internalLinksInCell.clear(); + currentReference = null; + currentReferenceName = null; + + String value = renderCellAsString(e); + + int colId = currentRow.size(); + + // Add the cell to the row we are currently building + currentRow.add(value); + currentRowReferences.add(currentReference); + + // Reconcile it if we found exactly one link in the cell + String reconciled = null; + if (internalLinksInCell.size() == 1) { + reconciled = internalLinksInCell.get(0); + wikilinkedCells.add(new WikilinkedCell(reconciled, rowId, colId)); + } + + // Mark it as spanning if we found the tags + if (colspan > 1 || rowspan > 1) { + SpanningCell spanningCell = new SpanningCell( + value, reconciled, currentReference, + rowId, colId, rowspan, colspan); + spanningCells.add(spanningCellIdx, spanningCell); + } + + // Add all spanning cells that need to be inserted after this one. + addSpanningCells(); } public String renderCellAsString(WtNode e) { @@ -403,28 +405,25 @@ public class WikitextImporter extends TabularImportingParserBase { } public void visit(WtXmlAttribute e) { - if (currentXmlAttr == null) { - xmlAttrStringBuilder = new StringBuilder(); - iterate(e); - try { - if ("colspan".equals(currentXmlAttr)) { - colspan = Integer.parseInt(xmlAttrStringBuilder.toString()); - } else if ("rowspan".equals(currentXmlAttr)) { - rowspan = Integer.parseInt(xmlAttrStringBuilder.toString()); - } else if ("name".equals(currentXmlAttr)) { - currentReferenceName = xmlAttrStringBuilder.toString(); - } - } catch (NumberFormatException nfe) { + xmlAttrStringBuilder = new StringBuilder(); + iterate(e); + try { + if ("colspan".equals(currentXmlAttr)) { + colspan = Integer.parseInt(xmlAttrStringBuilder.toString()); + } else if ("rowspan".equals(currentXmlAttr)) { + rowspan = Integer.parseInt(xmlAttrStringBuilder.toString()); + } else if ("name".equals(currentXmlAttr)) { + currentReferenceName = xmlAttrStringBuilder.toString(); } - currentXmlAttr = null; - xmlAttrStringBuilder = null; + } catch (NumberFormatException nfe) { } + currentXmlAttr = null; + xmlAttrStringBuilder = null; } public void visit(WtName e) { try { - currentXmlAttr = e.getAsString(); - + currentXmlAttr = e.getAsString(); } catch (UnsupportedOperationException soe) { currentXmlAttr = null; } @@ -507,6 +506,14 @@ public class WikitextImporter extends TabularImportingParserBase { iterate(e.getValue()); } + public void visit(WtImageLink e) { + if(includeRawTemplates) { + writeText("[["); + writeText(e.getTarget().getAsString()); + writeText("]]"); + } + } + /* Content blocks */ public void visit(WtParsedWikitextPage e) { @@ -537,7 +544,7 @@ public class WikitextImporter extends TabularImportingParserBase { } public class WikiTableDataReader implements TableDataReader { - private int currentRow = -1; + private int currentRow = 0; private WikitextTableVisitor visitor = null; private List> reconList = null; private List columnReconciled = null; @@ -545,7 +552,7 @@ public class WikitextImporter extends TabularImportingParserBase { public WikiTableDataReader(WikitextTableVisitor visitor, boolean references) { this.visitor = visitor; - currentRow = -1; + currentRow = 0; reconList = null; if (references) { @@ -569,9 +576,7 @@ public class WikitextImporter extends TabularImportingParserBase { List row = null; List origRow = null; List refRow = null; - if (currentRow == -1) { - origRow = this.visitor.header; - } else if(currentRow < this.visitor.rows.size()) { + if(currentRow < this.visitor.rows.size()) { origRow = this.visitor.rows.get(currentRow); refRow = this.visitor.references.get(currentRow); } @@ -583,10 +588,15 @@ public class WikitextImporter extends TabularImportingParserBase { if (currentRow >= 0 && reconList != null) { recon = reconList.get(currentRow).get(i); } - row.add(new Cell(origRow.get(i), recon)); + String value = origRow.get(i); + if (value != null) { + row.add(new Cell(value, recon)); + } else { + row.add(null); + } - // if we should add reference colums… - if (columnReferenced != null && columnReferenced.get(i)) { + // if we should add reference columns… + if (columnReferenced != null && i < columnReferenced.size() && columnReferenced.get(i)) { String refValue = null; // for headers if(currentRow == -1) { @@ -594,7 +604,11 @@ public class WikitextImporter extends TabularImportingParserBase { } else { refValue = refRow.get(i); } - row.add(new Cell(refValue, null)); + if (refValue != null) { + row.add(new Cell(refValue, null)); + } else { + row.add(null); + } } } } @@ -705,8 +719,6 @@ public class WikitextImporter extends TabularImportingParserBase { dataReader.reconcileToQids(wikiUrl, cfg); } - JSONUtilities.safePut(options, "headerLines", 1); - // Set metadata if (vs.caption != null && vs.caption.length() > 0) { metadata.setName(vs.caption); diff --git a/main/src/com/google/refine/importers/XmlImporter.java b/main/src/com/google/refine/importers/XmlImporter.java index 92cb866d5..3bef0d3ca 100644 --- a/main/src/com/google/refine/importers/XmlImporter.java +++ b/main/src/com/google/refine/importers/XmlImporter.java @@ -51,7 +51,6 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.tree.ImportColumnGroup; import com.google.refine.importers.tree.TreeImportingParserBase; import com.google.refine.importers.tree.TreeReader; @@ -59,6 +58,7 @@ import com.google.refine.importers.tree.TreeReaderException; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; public class XmlImporter extends TreeImportingParserBase { diff --git a/main/src/com/google/refine/importers/tree/ImportColumnGroup.java b/main/src/com/google/refine/importers/tree/ImportColumnGroup.java index 9b004b1b8..f87c13abe 100644 --- a/main/src/com/google/refine/importers/tree/ImportColumnGroup.java +++ b/main/src/com/google/refine/importers/tree/ImportColumnGroup.java @@ -3,7 +3,7 @@ package com.google.refine.importers.tree; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; /** * A column group describes a branch in tree structured data diff --git a/main/src/com/google/refine/importers/tree/TreeImportingParserBase.java b/main/src/com/google/refine/importers/tree/TreeImportingParserBase.java index 9fceab5b1..d54184ab7 100644 --- a/main/src/com/google/refine/importers/tree/TreeImportingParserBase.java +++ b/main/src/com/google/refine/importers/tree/TreeImportingParserBase.java @@ -39,16 +39,16 @@ import java.io.InputStream; import java.io.Reader; import java.util.List; -import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import org.json.JSONObject; -import com.google.refine.ProjectMetadata; import com.google.refine.importers.ImporterUtilities; import com.google.refine.importers.ImporterUtilities.MultiFileReadingProgress; import com.google.refine.importers.ImportingParserBase; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingUtilities; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; /** @@ -154,7 +154,7 @@ abstract public class TreeImportingParserBase extends ImportingParserBase { JSONObject options, List exceptions ) { - throw new NotImplementedException(); + throw new NotImplementedException("project ID:" + project.id); } /** diff --git a/main/src/com/google/refine/importing/DefaultImportingController.java b/main/src/com/google/refine/importing/DefaultImportingController.java index 40029be91..c226097fb 100644 --- a/main/src/com/google/refine/importing/DefaultImportingController.java +++ b/main/src/com/google/refine/importing/DefaultImportingController.java @@ -271,7 +271,15 @@ public class DefaultImportingController implements ImportingController { throw new ServletException(e); } } - + + /** + * return the job to the front end. + * @param request + * @param response + * @param job + * @throws ServletException + * @throws IOException + */ private void replyWithJobData(HttpServletRequest request, HttpServletResponse response, ImportingJob job) throws ServletException, IOException { diff --git a/main/src/com/google/refine/importing/ImportingJob.java b/main/src/com/google/refine/importing/ImportingJob.java index 0ebd55390..f901cabca 100644 --- a/main/src/com/google/refine/importing/ImportingJob.java +++ b/main/src/com/google/refine/importing/ImportingJob.java @@ -47,8 +47,8 @@ import org.json.JSONWriter; import com.google.refine.Jsonizable; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.util.JSONUtilities; @@ -139,6 +139,14 @@ public class ImportingJob implements Jsonizable { } } + /** + * TO check if the file record is a metadata file entry + * @param fileRecordObject + * @return JSONObject + */ + public boolean isMetadataFileRecord(JSONObject fileRecordObject) { + return fileRecordObject.has("metaDataFormat"); + } public List getSelectedFileRecords() { List results = new ArrayList(); @@ -208,5 +216,4 @@ public class ImportingJob implements Jsonizable { writer.endObject(); } } - } diff --git a/main/src/com/google/refine/importing/ImportingParser.java b/main/src/com/google/refine/importing/ImportingParser.java index f2d85c0ae..f6896b364 100644 --- a/main/src/com/google/refine/importing/ImportingParser.java +++ b/main/src/com/google/refine/importing/ImportingParser.java @@ -37,8 +37,8 @@ import java.util.List; import org.json.JSONObject; -import com.google.refine.ProjectMetadata; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; public interface ImportingParser { /** diff --git a/main/src/com/google/refine/importing/ImportingUtilities.java b/main/src/com/google/refine/importing/ImportingUtilities.java index ffb9e65ba..705c8e02a 100644 --- a/main/src/com/google/refine/importing/ImportingUtilities.java +++ b/main/src/com/google/refine/importing/ImportingUtilities.java @@ -42,6 +42,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.text.NumberFormat; @@ -49,9 +50,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -65,10 +68,14 @@ import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DecompressingHttpClient; import org.apache.http.impl.client.DefaultHttpClient; @@ -82,16 +89,36 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.importing.ImportingManager.Format; import com.google.refine.importing.UrlRewriter.Result; +import com.google.refine.model.Cell; +import com.google.refine.model.Column; +import com.google.refine.model.ColumnModel; import com.google.refine.model.Project; +import com.google.refine.model.Row; +import com.google.refine.model.medadata.DataPackageMetadata; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.MetadataFactory; +import com.google.refine.model.medadata.MetadataFormat; +import com.google.refine.model.medadata.PackageExtension; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.preference.PreferenceStore; import com.google.refine.util.JSONUtilities; +import io.frictionlessdata.datapackage.Package; +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.Schema; +import io.frictionlessdata.tableschema.TypeInferrer; +import io.frictionlessdata.tableschema.exceptions.TypeInferringException; + public class ImportingUtilities { final static protected Logger logger = LoggerFactory.getLogger("importing-utilities"); + private final static String METADATA_FILE_KEY = "metadataFile"; + + private static final int INFER_ROW_LIMIT = 100; + static public interface Progress { public void setProgress(String message, int percent); public boolean isCanceled(); @@ -172,11 +199,11 @@ public class ImportingUtilities { ) throws Exception { JSONArray fileRecords = new JSONArray(); JSONUtilities.safePut(retrievalRecord, "files", fileRecords); + JSONUtilities.safePut(retrievalRecord, "downloadCount", 0); + JSONUtilities.safePut(retrievalRecord, "archiveCount", 0); int clipboardCount = 0; int uploadCount = 0; - int downloadCount = 0; - int archiveCount = 0; // This tracks the total progress, which involves uploading data from the client // as well as downloading data from URLs. @@ -220,7 +247,7 @@ public class ImportingUtilities { List tempFiles = (List)upload.parseRequest(request); progress.setProgress("Uploading data ...", -1); - parts: for (FileItem fileItem : tempFiles) { + for (FileItem fileItem : tempFiles) { if (progress.isCanceled()) { break; } @@ -255,107 +282,27 @@ public class ImportingUtilities { } else if (name.equals("download")) { String urlString = Streams.asString(stream); - URL url = new URL(urlString); - - JSONObject fileRecord = new JSONObject(); - JSONUtilities.safePut(fileRecord, "origin", "download"); - JSONUtilities.safePut(fileRecord, "url", urlString); + download(rawDataDir, retrievalRecord, progress, fileRecords, update, urlString); + processDataPackage(retrievalRecord, fileRecords); + } else if (name.equals("data-package")) { + String urlString = Streams.asString(stream); + List results = null; for (UrlRewriter rewriter : ImportingManager.urlRewriters) { - Result result = rewriter.rewrite(urlString); - if (result != null) { - urlString = result.rewrittenUrl; - url = new URL(urlString); - - JSONUtilities.safePut(fileRecord, "url", urlString); - JSONUtilities.safePut(fileRecord, "format", result.format); - if (!result.download) { - downloadCount++; - JSONUtilities.append(fileRecords, fileRecord); - continue parts; + results = rewriter.rewrite(urlString); + if (results != null) { + for (Result result : results) { + download(rawDataDir, retrievalRecord, progress, fileRecords, + update, result.rewrittenUrl, result.metaDataFormat); } } } - - if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) { - DefaultHttpClient client = new DefaultHttpClient(); - DecompressingHttpClient httpclient = - new DecompressingHttpClient(client); - HttpGet httpGet = new HttpGet(url.toURI()); - httpGet.setHeader("User-Agent", RefineServlet.getUserAgent()); - if ("https".equals(url.getProtocol())) { - // HTTPS only - no sending password in the clear over HTTP - String userinfo = url.getUserInfo(); - if (userinfo != null) { - int s = userinfo.indexOf(':'); - if (s > 0) { - String user = userinfo.substring(0, s); - String pw = userinfo.substring(s + 1, userinfo.length()); - client.getCredentialsProvider().setCredentials( - new AuthScope(url.getHost(), 443), - new UsernamePasswordCredentials(user, pw)); - } - } - } - - HttpResponse response = httpclient.execute(httpGet); - - try { - response.getStatusLine(); - HttpEntity entity = response.getEntity(); - if (entity == null) { - throw new Exception("No content found in " + url.toString()); - } - InputStream stream2 = entity.getContent(); - String encoding = null; - if (entity.getContentEncoding() != null) { - encoding = entity.getContentEncoding().getValue(); - } - JSONUtilities.safePut(fileRecord, "declaredEncoding", encoding); - String contentType = null; - if (entity.getContentType() != null) { - contentType = entity.getContentType().getValue(); - } - JSONUtilities.safePut(fileRecord, "declaredMimeType", contentType); - if (saveStream(stream2, url, rawDataDir, progress, update, - fileRecord, fileRecords, - entity.getContentLength())) { - archiveCount++; - } - downloadCount++; - EntityUtils.consume(entity); - } finally { - httpGet.releaseConnection(); - } - } else { - // Fallback handling for non HTTP connections (only FTP?) - URLConnection urlConnection = url.openConnection(); - urlConnection.setConnectTimeout(5000); - urlConnection.connect(); - InputStream stream2 = urlConnection.getInputStream(); - JSONUtilities.safePut(fileRecord, "declaredEncoding", - urlConnection.getContentEncoding()); - JSONUtilities.safePut(fileRecord, "declaredMimeType", - urlConnection.getContentType()); - try { - if (saveStream(stream2, url, rawDataDir, progress, - update, fileRecord, fileRecords, - urlConnection.getContentLength())) { - archiveCount++; - } - downloadCount++; - } finally { - stream2.close(); - } - } } else { String value = Streams.asString(stream); parameters.put(name, value); // TODO: We really want to store this on the request so it's available for everyone // request.getParameterMap().put(name, value); - } - } else { // is file content String fileName = fileItem.getName(); if (fileName.length() > 0) { @@ -376,9 +323,11 @@ public class ImportingUtilities { JSONUtilities.safePut(fileRecord, "size", saveStreamToFile(stream, file, null)); if (postProcessRetrievedFile(rawDataDir, file, fileRecord, fileRecords, progress)) { - archiveCount++; + JSONUtilities.safeInc(retrievalRecord, "archiveCount"); } + processDataPackage(retrievalRecord, fileRecords); + uploadCount++; } } @@ -392,9 +341,144 @@ public class ImportingUtilities { } JSONUtilities.safePut(retrievalRecord, "uploadCount", uploadCount); - JSONUtilities.safePut(retrievalRecord, "downloadCount", downloadCount); JSONUtilities.safePut(retrievalRecord, "clipboardCount", clipboardCount); - JSONUtilities.safePut(retrievalRecord, "archiveCount", archiveCount); + } + + private static void processDataPackage(JSONObject retrievalRecord, JSONArray fileRecords) { + int dataPackageJSONFileIndex = getDataPackageJSONFile(fileRecords); + if (dataPackageJSONFileIndex >= 0) { + JSONObject dataPackageJSONFile = (JSONObject) fileRecords.get(dataPackageJSONFileIndex); + JSONUtilities.safePut(dataPackageJSONFile, "metaDataFormat", MetadataFormat.DATAPACKAGE_METADATA.name()); + JSONUtilities.safePut(retrievalRecord, METADATA_FILE_KEY, dataPackageJSONFile); + fileRecords.remove(dataPackageJSONFileIndex); + } + } + + private static int getDataPackageJSONFile(JSONArray fileRecords) { + for (int i = 0; i < fileRecords.length(); i++) { + JSONObject file = fileRecords.getJSONObject(i); + if (file.has("archiveFileName") && + file.has("fileName") && + file.get("fileName").equals(DataPackageMetadata.DEFAULT_FILE_NAME)) { + return i; + } + } + return -1; + } + + private static void download(File rawDataDir, JSONObject retrievalRecord, final Progress progress, + JSONArray fileRecords, final SavingUpdate update, String urlString) + throws URISyntaxException, IOException, ClientProtocolException, Exception { + download(rawDataDir, retrievalRecord, progress, fileRecords, update, urlString, null); + } + + /** + * @param rawDataDir + * @param retrievalRecord + * @param progress + * @param fileRecords + * @param update + * @param urlString + * @throws URISyntaxException + * @throws IOException + * @throws ClientProtocolException + * @throws Exception + */ + private static void download(File rawDataDir, JSONObject retrievalRecord, final Progress progress, + JSONArray fileRecords, final SavingUpdate update, String urlString, String metaDataFormat) + throws URISyntaxException, IOException, ClientProtocolException, Exception { + URL url = new URL(urlString); + JSONObject fileRecord = new JSONObject(); + JSONUtilities.safePut(fileRecord, "origin", "download"); + JSONUtilities.safePut(fileRecord, "url", urlString); + + if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) { + DefaultHttpClient client = new DefaultHttpClient(); + DecompressingHttpClient httpclient = + new DecompressingHttpClient(client); + HttpGet httpGet = new HttpGet(url.toURI()); + httpGet.setHeader("User-Agent", RefineServlet.getUserAgent()); + if ("https".equals(url.getProtocol())) { + // HTTPS only - no sending password in the clear over HTTP + String userinfo = url.getUserInfo(); + if (userinfo != null) { + int s = userinfo.indexOf(':'); + if (s > 0) { + String user = userinfo.substring(0, s); + String pw = userinfo.substring(s + 1, userinfo.length()); + client.getCredentialsProvider().setCredentials( + new AuthScope(url.getHost(), 443), + new UsernamePasswordCredentials(user, pw)); + } + } + } + + HttpResponse response = httpclient.execute(httpGet); + + try { + int code = response.getStatusLine().getStatusCode(); + if (code != HttpStatus.SC_OK) { + throw new Exception("HTTP response code: " + code + + " when accessing URL: "+ url.toString()); + } + + HttpEntity entity = response.getEntity(); + if (entity == null) { + throw new Exception("No content found in " + url.toString()); + } + InputStream stream2 = entity.getContent(); + String encoding = null; + if (entity.getContentEncoding() != null) { + encoding = entity.getContentEncoding().getValue(); + } + JSONUtilities.safePut(fileRecord, "declaredEncoding", encoding); + String contentType = null; + if (entity.getContentType() != null) { + contentType = entity.getContentType().getValue(); + } + JSONUtilities.safePut(fileRecord, "declaredMimeType", contentType); + + if (saveStream(stream2, url, rawDataDir, progress, update, + fileRecord, fileRecords, + entity.getContentLength())) { + JSONUtilities.safeInc(retrievalRecord, "archiveCount"); + } + + if (metaDataFormat != null) { + JSONUtilities.safePut(fileRecord, "metaDataFormat", metaDataFormat); + JSONUtilities.safePut(retrievalRecord, METADATA_FILE_KEY, fileRecord); + fileRecords.remove(0); + } + + JSONUtilities.safeInc(retrievalRecord, "downloadCount"); + EntityUtils.consume(entity); + } finally { + httpGet.releaseConnection(); + } + } else { + // Fallback handling for non HTTP connections (only FTP?) + URLConnection urlConnection = url.openConnection(); + urlConnection.setConnectTimeout(5000); + urlConnection.connect(); + InputStream stream2 = urlConnection.getInputStream(); + JSONUtilities.safePut(fileRecord, "declaredEncoding", + urlConnection.getContentEncoding()); + JSONUtilities.safePut(fileRecord, "declaredMimeType", + urlConnection.getContentType()); + try { + if (saveStream(stream2, url, rawDataDir, progress, + update, fileRecord, fileRecords, + urlConnection.getContentLength())) { + JSONUtilities.safeInc(retrievalRecord, "archiveCount"); + } + if (metaDataFormat != null) + JSONUtilities.safePut(fileRecord, "metaDataFormat", metaDataFormat); + + JSONUtilities.safeInc(retrievalRecord, "downloadCount"); + } finally { + stream2.close(); + } + } } private static boolean saveStream(InputStream stream, URL url, File rawDataDir, final Progress progress, @@ -1021,8 +1105,30 @@ public class ImportingUtilities { if (exceptions.size() == 0) { project.update(); // update all internal models, indexes, caches, etc. + boolean hasMetadataFileRecord = ((JSONObject)job.getRetrievalRecord()).has(METADATA_FILE_KEY); + + if (hasMetadataFileRecord) { + JSONObject metadataFileRecord = (JSONObject) job.getRetrievalRecord().get(METADATA_FILE_KEY); + + String metadataFormat = (String)metadataFileRecord.get("metaDataFormat"); + IMetadata metadata = MetadataFactory.buildMetadata(MetadataFormat.valueOf(metadataFormat)); + + String relativePath = metadataFileRecord.getString("location"); + File metadataFile = new File(job.getRawDataDir(), relativePath); + metadata.loadFromFile(metadataFile); + + // process the data package metadata + if (MetadataFormat.valueOf(metadataFormat) == MetadataFormat.DATAPACKAGE_METADATA) { + populateDataPackageMetadata(project, pm, (DataPackageMetadata) metadata); + } + logger.info(metadataFileRecord.get("metaDataFormat") + " metadata is set for project " + project.id); + } + ProjectManager.singleton.registerProject(project, pm); + // infer the column type + inferColumnType(project); + job.setProjectID(project.id); job.setState("created-project"); } else { @@ -1033,10 +1139,101 @@ public class ImportingUtilities { } } + public static void inferColumnType(final Project project) { + if (project.columnModel.columns.get(0).getType().isEmpty()) { + List listCells = new ArrayList(INFER_ROW_LIMIT); + List rows = project.rows + .stream() + .limit(INFER_ROW_LIMIT) + .map(Row::dup) + .collect(Collectors.toList()); + // convert the null object to prevent the NPE + for (Row row : rows) { + for (int i = 0; i < row.cells.size(); i++) { + Cell cell = row.cells.get(i); + if (cell == null) { + row.cells.set(i, new Cell(StringUtils.EMPTY, null)); + } + } + listCells.add(row.cells.toArray()); + } + + try { + JSONObject fieldsJSON = TypeInferrer.getInstance().infer(listCells, + project.columnModel.getColumnNames().toArray(new String[0]), + 100); + populateColumnTypes(project.columnModel, fieldsJSON.getJSONArray(Schema.JSON_KEY_FIELDS)); + } catch (TypeInferringException e) { + logger.error("infer column type exception.", ExceptionUtils.getStackTrace(e)); + } + } + } + + private static void populateDataPackageMetadata(Project project, ProjectMetadata pmd, DataPackageMetadata metadata) { + // project metadata + JSONObject pkg = metadata.getPackage().getJson(); + + pmd.setName(getDataPackageProperty(pkg, Package.JSON_KEY_NAME)); + pmd.setDescription(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_DESCRIPTION)); + pmd.setTitle(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_TITLE)); + pmd.setHomepage(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_HOMEPAGE)); + pmd.setImage(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_IMAGE)); + pmd.setLicense(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_LICENSE)); + pmd.setVersion(getDataPackageProperty(pkg, PackageExtension.JSON_KEY_VERSION)); + + if (pkg.has(PackageExtension.JSON_KEY_KEYWORKS)) { + String[] tags = pkg.getJSONArray(PackageExtension.JSON_KEY_KEYWORKS).toList().toArray(new String[0]); + pmd.appendTags(tags); + } + + // column model + JSONObject schema = metadata.getPackage().getResources().get(0).getSchema(); + if (schema != null) { + populateColumnTypes(project.columnModel, schema.getJSONArray(Schema.JSON_KEY_FIELDS)); + } + } + + private static String getDataPackageProperty(JSONObject pkg, String key) { + return JSONUtilities.getString(pkg, key, StringUtils.EMPTY); + } + /** + * Populate the column model + * @param columnModel + * @param fieldsJSON + */ + private static void populateColumnTypes(ColumnModel columnModel, JSONArray fieldsJSON) { + int cellIndex = 0; + Iterator iter = fieldsJSON.iterator(); + while(iter.hasNext()){ + JSONObject fieldJsonObj = (JSONObject)iter.next(); + Field field = new Field(fieldJsonObj); + + Column column = columnModel.getColumnByCellIndex(cellIndex); + column.setType(field.getType()); + column.setFormat(field.getFormat()); + column.setDescription(field.getDescription()); + column.setTitle(field.getTitle()); + column.setConstraints(field.getConstraints()); + + cellIndex++; + } + } + + /** + * Create project metadata. pull the "USER_NAME" from the PreferenceStore as the creator + * @param optionObj + * @return + */ static public ProjectMetadata createProjectMetadata(JSONObject optionObj) { ProjectMetadata pm = new ProjectMetadata(); + PreferenceStore ps = ProjectManager.singleton.getPreferenceStore(); + pm.setName(JSONUtilities.getString(optionObj, "projectName", "Untitled")); pm.setTags(JSONUtilities.getStringArray(optionObj, "projectTags")); + pm.setTitle(JSONUtilities.getString(optionObj, "title", "")); + pm.setHomepage(JSONUtilities.getString(optionObj, "homepage", "")); + pm.setImage(JSONUtilities.getString(optionObj, "image", "")); + pm.setLicense(JSONUtilities.getString(optionObj, "license", "")); String encoding = JSONUtilities.getString(optionObj, "encoding", "UTF-8"); if ("".equals(encoding)) { @@ -1044,6 +1241,12 @@ public class ImportingUtilities { encoding = "UTF-8"; } pm.setEncoding(encoding); + + if (ps.get(PreferenceStore.USER_NAME) != null) { + String creator = (String) ps.get(PreferenceStore.USER_NAME); + pm.setCreator(creator); + } + return pm; } } diff --git a/main/src/com/google/refine/importing/UrlRewriter.java b/main/src/com/google/refine/importing/UrlRewriter.java index 4e6015488..d7332750e 100644 --- a/main/src/com/google/refine/importing/UrlRewriter.java +++ b/main/src/com/google/refine/importing/UrlRewriter.java @@ -33,12 +33,45 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.importing; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.List; + +/** + * Given a URL rewrittenUrl, the interface will rewrite it into different URLS based on the rewrittenUrl + * The result will be stored in the Result and can be used for download, parsing etc. + * Typical use is to parse the data package json file. + * @see DataPackageUrlRewriter + */ public interface UrlRewriter { static public class Result { public String rewrittenUrl; public String format; public boolean download; + public String metaDataFormat; + + public Result(String rewrittenUrl, String format, boolean download) { + this.rewrittenUrl = rewrittenUrl; + this.format = format; + this.download = download; + } + + public Result(String rewrittenUrl, String format, boolean download, String metaDataFormat) { + this.rewrittenUrl = rewrittenUrl; + this.format = format; + this.download = download; + this.metaDataFormat = metaDataFormat; + } } - public Result rewrite(String url); + /** + * Parse the url and output the Result + * @param url + * @return + * @throws MalformedURLException + * @throws IOException + */ + public List rewrite(String url) throws MalformedURLException, IOException; + + public boolean filter(String url); } diff --git a/main/src/com/google/refine/io/FileProjectManager.java b/main/src/com/google/refine/io/FileProjectManager.java index 0ff27e0f5..eb5c54662 100644 --- a/main/src/com/google/refine/io/FileProjectManager.java +++ b/main/src/com/google/refine/io/FileProjectManager.java @@ -57,9 +57,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.history.HistoryEntryManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.DataPackageMetadata; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.MetadataFormat; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.preference.TopList; @@ -120,7 +123,6 @@ public class FileProjectManager extends ProjectManager { if (metadata == null) { metadata = ProjectMetadataUtilities.recover(getProjectDir(projectID), projectID); } - if (metadata != null) { _projectsMetadata.put(projectID, metadata); if (_projectsTags == null) { @@ -155,7 +157,7 @@ public class FileProjectManager extends ProjectManager { untar(destDir, inputStream); } } - + protected void untar(File destDir, InputStream inputStream) throws IOException { TarInputStream tin = new TarInputStream(inputStream); TarEntry tarEntry = null; @@ -231,9 +233,19 @@ public class FileProjectManager extends ProjectManager { } @Override - public void saveMetadata(ProjectMetadata metadata, long projectId) throws Exception { + public void saveMetadata(IMetadata metadata, long projectId) throws Exception { File projectDir = getProjectDir(projectId); - ProjectMetadataUtilities.save(metadata, projectDir); + + if (metadata.getFormatName() == MetadataFormat.PROJECT_METADATA) { + Project project = ProjectManager.singleton.getProject(projectId); + ((ProjectMetadata)metadata).setRowCount(project.rows.size()); + ProjectMetadataUtilities.save(metadata, projectDir); + } else if (metadata.getFormatName() == MetadataFormat.DATAPACKAGE_METADATA) { + DataPackageMetadata dp = (DataPackageMetadata)metadata; + dp.writeToFile(new File(projectDir, DataPackageMetadata.DEFAULT_FILE_NAME)); + } + + logger.info("metadata saved in " + metadata.getFormatName()); } @Override @@ -320,8 +332,6 @@ public class FileProjectManager extends ProjectManager { return saveWasNeeded; } - - @Override public void deleteProject(long projectID) { synchronized (this) { @@ -363,8 +373,6 @@ public class FileProjectManager extends ProjectManager { protected boolean loadFromFile(File file) { logger.info("Loading workspace: {}", file.getAbsolutePath()); - _projectsMetadata.clear(); - boolean found = false; if (file.exists() || file.canRead()) { @@ -464,4 +472,4 @@ public class FileProjectManager extends ProjectManager { public HistoryEntryManager getHistoryEntryManager(){ return new FileHistoryEntryManager(); } -} +} \ No newline at end of file diff --git a/main/src/com/google/refine/io/ProjectMetadataUtilities.java b/main/src/com/google/refine/io/ProjectMetadataUtilities.java index d6f3d8b93..6eaf81bbf 100644 --- a/main/src/com/google/refine/io/ProjectMetadataUtilities.java +++ b/main/src/com/google/refine/io/ProjectMetadataUtilities.java @@ -35,7 +35,6 @@ package com.google.refine.io; import java.io.File; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; @@ -44,27 +43,25 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; import org.json.JSONWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.refine.ProjectMetadata; import com.google.refine.model.Project; - +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.ProjectMetadata; public class ProjectMetadataUtilities { final static Logger logger = LoggerFactory.getLogger("project_metadata_utilities"); - - public static void save(ProjectMetadata projectMeta, File projectDir) throws JSONException, IOException { - File tempFile = new File(projectDir, "metadata.temp.json"); + + public static void save(IMetadata projectMeta, File projectDir) throws JSONException, IOException { + File tempFile = new File(projectDir, ProjectMetadata.TEMP_FILE_NAME); saveToFile(projectMeta, tempFile); - File file = new File(projectDir, "metadata.json"); - File oldFile = new File(projectDir, "metadata.old.json"); + File file = new File(projectDir, ProjectMetadata.DEFAULT_FILE_NAME); + File oldFile = new File(projectDir, ProjectMetadata.OLD_FILE_NAME); if (oldFile.exists()) { oldFile.delete(); @@ -76,12 +73,16 @@ public class ProjectMetadataUtilities { tempFile.renameTo(file); } + + public static void saveTableSchema(Project project, File projectDir) throws JSONException, IOException { - protected static void saveToFile(ProjectMetadata projectMeta, File metadataFile) throws JSONException, IOException { + } + + protected static void saveToFile(IMetadata projectMeta, File metadataFile) throws JSONException, IOException { Writer writer = new OutputStreamWriter(new FileOutputStream(metadataFile)); try { JSONWriter jsonWriter = new JSONWriter(writer); - projectMeta.write(jsonWriter); + projectMeta.write(jsonWriter, false); } finally { writer.close(); } @@ -89,17 +90,17 @@ public class ProjectMetadataUtilities { static public ProjectMetadata load(File projectDir) { try { - return loadFromFile(new File(projectDir, "metadata.json")); + return loadFromFile(new File(projectDir, ProjectMetadata.DEFAULT_FILE_NAME)); } catch (Exception e) { } try { - return loadFromFile(new File(projectDir, "metadata.temp.json")); + return loadFromFile(new File(projectDir, ProjectMetadata.TEMP_FILE_NAME)); } catch (Exception e) { } try { - return loadFromFile(new File(projectDir, "metadata.old.json")); + return loadFromFile(new File(projectDir, ProjectMetadata.OLD_FILE_NAME)); } catch (Exception e) { } @@ -148,14 +149,8 @@ public class ProjectMetadataUtilities { } static protected ProjectMetadata loadFromFile(File metadataFile) throws Exception { - FileReader reader = new FileReader(metadataFile); - try { - JSONTokener tokener = new JSONTokener(reader); - JSONObject obj = (JSONObject) tokener.nextValue(); - - return ProjectMetadata.loadFromJSON(obj); - } finally { - reader.close(); - } + ProjectMetadata projectMetaData = new ProjectMetadata(); + projectMetaData.loadFromFile(metadataFile); + return projectMetaData; } } diff --git a/main/src/com/google/refine/io/ProjectUtilities.java b/main/src/com/google/refine/io/ProjectUtilities.java index 4088cfeb9..f72856f46 100644 --- a/main/src/com/google/refine/io/ProjectUtilities.java +++ b/main/src/com/google/refine/io/ProjectUtilities.java @@ -36,6 +36,8 @@ package com.google.refine.io; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -45,6 +47,9 @@ import org.slf4j.LoggerFactory; import com.google.refine.ProjectManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.DataPackageMetadata; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.MetadataFormat; import com.google.refine.util.Pool; @@ -110,38 +115,63 @@ public class ProjectUtilities { out.close(); } } - - static public Project load(File dir, long id) { + + static public Project loadDataFile(File dir, String dataFile, long id) { try { - File file = new File(dir, "data.zip"); + File file = new File(dir, dataFile); if (file.exists()) { return loadFromFile(file, id); } } catch (Exception e) { e.printStackTrace(); } - - try { - File file = new File(dir, "data.temp.zip"); - if (file.exists()) { - return loadFromFile(file, id); - } - } catch (Exception e) { - e.printStackTrace(); - } - - try { - File file = new File(dir, "data.old.zip"); - if (file.exists()) { - return loadFromFile(file, id); - } - } catch (Exception e) { - e.printStackTrace(); - } - + return null; } - + + static public Project load(File dir, long id) { + Project project =null; + + if ((project = loadDataFile(dir, "data.zip", id)) == null) { + if ((project = loadDataFile(dir, "data.temp.zip", id)) == null) { + project = loadDataFile(dir, "data.old.zip", id); + } + } + return project; + } + + + /** + * scan the folder for json files and read them as metadata + * @param dir + * @param project + */ + public static Map retriveMetadata(File dir) { + // load the metadatas from data folder. + Map metadataMap = new HashMap(); + + File[] jsons = dir.listFiles( + (folder, file) -> { + return file.toLowerCase().endsWith(".json"); + } + ); + + for (File file : jsons) { + // already loaded + if (file.getName().startsWith("metadata.")) + continue; + + DataPackageMetadata metadata = new DataPackageMetadata(); + // load itself + metadata.loadFromFile(file); + + metadataMap.put(MetadataFormat.DATAPACKAGE_METADATA, metadata); + } + + return metadataMap; + + } + static protected Project loadFromFile( File file, long id diff --git a/main/src/com/google/refine/model/Cell.java b/main/src/com/google/refine/model/Cell.java index 59d91ec77..17a2f9ef1 100644 --- a/main/src/com/google/refine/model/Cell.java +++ b/main/src/com/google/refine/model/Cell.java @@ -35,8 +35,12 @@ package com.google.refine.model; import java.io.Serializable; import java.io.Writer; +import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Calendar; +import java.util.Date; import java.util.Properties; import org.json.JSONException; @@ -87,11 +91,19 @@ public class Cell implements HasFields, Jsonizable { } else { writer.key("v"); if (value != null) { - if (value instanceof LocalDateTime) { - writer.value(ParsingUtilities.localDateToString((LocalDateTime)value)); - writer.key("t"); writer.value("date"); + Instant instant = null; + if (value instanceof Calendar) { + instant = ((Calendar)value).toInstant(); + } else if (value instanceof Date) { + instant = ((Date)value).toInstant(); } else if (value instanceof OffsetDateTime) { - writer.value(ParsingUtilities.dateToString((OffsetDateTime) value)); + instant = ((OffsetDateTime)value).toInstant(); + } else if (value instanceof LocalDateTime) { + instant = ((LocalDateTime)value).toInstant(ZoneOffset.of("Z")); + } + + if (instant != null) { + writer.value(ParsingUtilities.instantToString(instant)); writer.key("t"); writer.value("date"); } else if (value instanceof Double && (((Double)value).isNaN() || ((Double)value).isInfinite())) { diff --git a/main/src/com/google/refine/model/Column.java b/main/src/com/google/refine/model/Column.java index 9a6e44bd6..3eafa45db 100644 --- a/main/src/com/google/refine/model/Column.java +++ b/main/src/com/google/refine/model/Column.java @@ -34,10 +34,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.model; import java.io.Writer; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; @@ -45,8 +47,14 @@ import org.json.JSONWriter; import com.google.refine.InterProjectModel; import com.google.refine.Jsonizable; import com.google.refine.model.recon.ReconConfig; +import com.google.refine.util.JSONUtilities; import com.google.refine.util.ParsingUtilities; +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.TypeInferrer; +import io.frictionlessdata.tableschema.exceptions.ConstraintsException; +import io.frictionlessdata.tableschema.exceptions.InvalidCastException; + public class Column implements Jsonizable { final private int _cellIndex; final private String _originalName; @@ -54,6 +62,13 @@ public class Column implements Jsonizable { private ReconConfig _reconConfig; private ReconStats _reconStats; + // from data package metadata Field.java: + private String type = ""; + private String format = Field.FIELD_FORMAT_DEFAULT; + private String title = ""; + private String description = ""; + private Map constraints = null; + transient protected Map _precomputes; public Column(int cellIndex, String originalName) { @@ -101,6 +116,11 @@ public class Column implements Jsonizable { writer.key("cellIndex"); writer.value(_cellIndex); writer.key("originalName"); writer.value(_originalName); writer.key("name"); writer.value(_name); + writer.key("type"); writer.value(type); + writer.key("format"); writer.value(format); + writer.key("title"); writer.value(title); + writer.key("description"); writer.value(description); + writer.key("constraints"); writer.value(new JSONObject(constraints).toString()); if (_reconConfig != null) { writer.key("reconConfig"); _reconConfig.write(writer, options); @@ -140,6 +160,56 @@ public class Column implements Jsonizable { _precomputes.put(key, value); } + + public String getType() { + return type; + } + + + public void setType(String type) { + this.type = type; + } + + + public String getFormat() { + return format; + } + + + public void setFormat(String format) { + this.format = format; + } + + + public String getTitle() { + return title; + } + + + public void setTitle(String title) { + this.title = title; + } + + + public String getDescription() { + return description; + } + + + public void setDescription(String description) { + this.description = description; + } + + + public Map getConstraints() { + return constraints; + } + + + public void setConstraints(Map constraints) { + this.constraints = constraints; + } + public void save(Writer writer) { JSONWriter jsonWriter = new JSONWriter(writer); try { @@ -154,6 +224,14 @@ public class Column implements Jsonizable { Column column = new Column(obj.getInt("cellIndex"), obj.getString("originalName")); column._name = obj.getString("name"); + column.type = JSONUtilities.getString(obj, Field.JSON_KEY_TYPE, StringUtils.EMPTY); + column.format = JSONUtilities.getString(obj, Field.JSON_KEY_FORMAT, StringUtils.EMPTY); + column.title = JSONUtilities.getString(obj, Field.JSON_KEY_TITLE, StringUtils.EMPTY); + column.description = JSONUtilities.getString(obj, Field.JSON_KEY_DESCRIPTION, StringUtils.EMPTY); + if (obj.has(Field.JSON_KEY_CONSTRAINTS)) { + column.constraints = new JSONObject(obj.getString(Field.JSON_KEY_CONSTRAINTS)).toMap(); + } + if (obj.has("reconConfig")) { column._reconConfig = ReconConfig.reconstruct(obj.getJSONObject("reconConfig")); } @@ -168,4 +246,23 @@ public class Column implements Jsonizable { public String toString() { return _name; } + + public Any castValue(String value) + throws InvalidCastException, ConstraintsException { + if (this.type.isEmpty()) { + throw new InvalidCastException(); + } else { + try { + // Using reflection to invoke appropriate type casting method from the + // TypeInferrer class + String castMethodName = "cast" + (this.type.substring(0, 1).toUpperCase() + this.type.substring(1)); + Method method = TypeInferrer.class.getMethod(castMethodName, String.class, String.class, Map.class); + Object castValue = method.invoke(TypeInferrer.getInstance(), this.format, value, null); + + return (Any) castValue; + } catch (Exception e) { + throw new InvalidCastException(); + } + } + } } diff --git a/main/src/com/google/refine/model/Project.java b/main/src/com/google/refine/model/Project.java index 7d35f29d2..e9a984ebc 100644 --- a/main/src/com/google/refine/model/Project.java +++ b/main/src/com/google/refine/model/Project.java @@ -55,9 +55,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.history.History; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.process.ProcessManager; import com.google.refine.util.ParsingUtilities; import com.google.refine.util.Pool; @@ -77,14 +77,13 @@ public class Project { transient private LocalDateTime _lastSave = LocalDateTime.now(); final static Logger logger = LoggerFactory.getLogger("project"); - + static public long generateID() { return System.currentTimeMillis() + Math.round(Math.random() * 1000000000000L); } public Project() { - id = generateID(); - history = new History(this); + this(generateID()); } protected Project(long id) { @@ -121,10 +120,6 @@ public class Project { this._lastSave = LocalDateTime.now(); } - public ProjectMetadata getMetadata() { - return ProjectManager.singleton.getProjectMetadata(id); - } - public void saveToOutputStream(OutputStream out, Pool pool) throws IOException { for (OverlayModel overlayModel : overlayModels.values()) { try { @@ -258,11 +253,14 @@ public class Project { columnModel.update(); recordModel.update(this); } - - + //wrapper of processManager variable to allow unit testing //TODO make the processManager variable private, and force all calls through this method public ProcessManager getProcessManager() { return this.processManager; } + + public ProjectMetadata getMetadata() { + return ProjectManager.singleton.getProjectMetadata(id); + } } diff --git a/main/src/com/google/refine/model/RecordModel.java b/main/src/com/google/refine/model/RecordModel.java index 2e259283b..47a6e24a7 100644 --- a/main/src/com/google/refine/model/RecordModel.java +++ b/main/src/com/google/refine/model/RecordModel.java @@ -1,303 +1,303 @@ -/* - -Copyright 2010, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -package com.google.refine.model; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Properties; - -import org.json.JSONException; -import org.json.JSONWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.refine.Jsonizable; -import com.google.refine.expr.ExpressionUtils; - -public class RecordModel implements Jsonizable { - final static Logger logger = LoggerFactory.getLogger("RecordModel"); - - final static public class CellDependency { - final public int rowIndex; - final public int cellIndex; - - public CellDependency(int rowIndex, int cellIndex) { - this.rowIndex = rowIndex; - this.cellIndex = cellIndex; - } - - @Override - public String toString() { - return rowIndex+","+cellIndex; - } - } - - final static public class RowDependency { - public int recordIndex; - public CellDependency[] cellDependencies; - public List contextRows; - - @Override - public String toString() { - return "Idx: "+recordIndex+" CellDeps: "+Arrays.toString(cellDependencies)+" Rows:"+contextRows; - } - } - - protected List _rowDependencies; - protected List _records; - - public RowDependency getRowDependency(int rowIndex) { - return _rowDependencies != null && rowIndex >= 0 && rowIndex < _rowDependencies.size() ? - _rowDependencies.get(rowIndex) : null; - } - - public int getRecordCount() { - return _records.size(); - } - - public Record getRecord(int recordIndex) { - return _records != null && recordIndex >= 0 && recordIndex < _records.size() ? - _records.get(recordIndex) : null; - } - - public Record getRecordOfRow(int rowIndex) { - RowDependency rd = getRowDependency(rowIndex); - if (rd != null) { - if (rd.recordIndex < 0) { - rd = getRowDependency(rd.contextRows.get(0)); - } - return getRecord(rd.recordIndex); - } - return null; - } - - @Override - synchronized public void write(JSONWriter writer, Properties options) - throws JSONException { - - writer.object(); - writer.key("hasRecords"); - writer.value( - _records != null && _rowDependencies != null && - _records.size() < _rowDependencies.size()); - writer.endObject(); - } - - static protected class KeyedGroup { - int[] cellIndices; - int keyCellIndex; - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - for (int i:cellIndices) { - sb.append(i).append(','); - } - return "key: " + keyCellIndex + " cells: " + sb.toString(); - } - } - - synchronized public void update(Project project) { - synchronized (project) { - List rows = project.rows; - int rowCount = rows.size(); - - ColumnModel columnModel = project.columnModel; - List keyedGroups = computeKeyedGroups(columnModel); - int groupCount = keyedGroups.size(); - - int[] lastNonBlankRowsByGroup = new int[keyedGroups.size()]; - for (int i = 0; i < lastNonBlankRowsByGroup.length; i++) { - lastNonBlankRowsByGroup[i] = -1; - } - - _rowDependencies = new ArrayList(rowCount); - - int recordIndex = 0; - for (int r = 0; r < rowCount; r++) { - Row row = rows.get(r); - RowDependency rowDependency = new RowDependency(); - - for (int g = 0; g < groupCount; g++) { - KeyedGroup group = keyedGroups.get(g); - - if (!ExpressionUtils.isNonBlankData(row.getCellValue(keyedGroups.get(0).keyCellIndex)) && - !ExpressionUtils.isNonBlankData(row.getCellValue(group.keyCellIndex))) { - int contextRowIndex = lastNonBlankRowsByGroup[g]; - if (contextRowIndex >= 0) { - for (int dependentCellIndex : group.cellIndices) { - if (ExpressionUtils.isNonBlankData(row.getCellValue(dependentCellIndex))) { - setRowDependency( - project, - rowDependency, - dependentCellIndex, - contextRowIndex, - group.keyCellIndex - ); - } - } - } - } else { - lastNonBlankRowsByGroup[g] = r; - } - } - - if (rowDependency.cellDependencies != null && rowDependency.cellDependencies.length > 0) { - rowDependency.recordIndex = -1; - rowDependency.contextRows = new ArrayList(); - for (CellDependency cd : rowDependency.cellDependencies) { - if (cd != null) { - rowDependency.contextRows.add(cd.rowIndex); - } - } - Collections.sort(rowDependency.contextRows); - } else { - rowDependency.recordIndex = recordIndex++; - } - - _rowDependencies.add(rowDependency); - } - - _records = new ArrayList(recordIndex); - if (recordIndex > 0) { - recordIndex = 0; - - int recordRowIndex = 0; - for (int r = 1; r < rowCount; r++) { - RowDependency rd = _rowDependencies.get(r); - if (rd.recordIndex >= 0) { - _records.add(new Record(recordRowIndex, r, recordIndex++)); - - recordIndex = rd.recordIndex; - recordRowIndex = r; - } - } - - _records.add(new Record(recordRowIndex, rowCount, recordIndex++)); - } - } - } - - protected List computeKeyedGroups(ColumnModel columnModel) { - List keyedGroups = new ArrayList(); - - addRootKeyedGroup(columnModel, keyedGroups); - - for (ColumnGroup group : columnModel.columnGroups) { - if (group.keyColumnIndex >= 0) { - KeyedGroup keyedGroup = new KeyedGroup(); - keyedGroup.keyCellIndex = columnModel.columns.get(group.keyColumnIndex).getCellIndex(); - keyedGroup.cellIndices = new int[group.columnSpan - 1]; - - int c = 0; - for (int i = 0; i < group.columnSpan; i++) { - int columnIndex = group.startColumnIndex + i; - if (columnIndex != group.keyColumnIndex && columnIndex < columnModel.columns.size()) { - int cellIndex = columnModel.columns.get(columnIndex).getCellIndex(); - keyedGroup.cellIndices[c++] = cellIndex; - } - } - - keyedGroups.add(keyedGroup); - } - } - - Collections.sort(keyedGroups, new Comparator() { - @Override - public int compare(KeyedGroup o1, KeyedGroup o2) { - return o2.cellIndices.length - o1.cellIndices.length; // larger groups first - } - }); - - dumpKeyedGroups(keyedGroups, columnModel); // for debug - - return keyedGroups; - } - - // debugging helper - private void dumpKeyedGroups(List groups, ColumnModel columnModel) { - for (KeyedGroup g : groups) { - String keyColName = columnModel.getColumnByCellIndex(g.keyCellIndex).getName(); - StringBuffer sb = new StringBuffer(); - for (int ci : g.cellIndices) { - Column col = columnModel.getColumnByCellIndex(ci); - if (col != null) { - // Old projects have col 0 slot empty - sb.append(col.getName()).append(','); - } - } - logger.trace("KeyedGroup " + keyColName + "::" + sb.toString()); - } - } - - protected void addRootKeyedGroup(ColumnModel columnModel, List keyedGroups) { - int count = columnModel.getMaxCellIndex() + 1; - if (count > 0 && columnModel.getKeyColumnIndex() < columnModel.columns.size()) { - KeyedGroup rootKeyedGroup = new KeyedGroup(); - - rootKeyedGroup.cellIndices = new int[count - 1]; - rootKeyedGroup.keyCellIndex = columnModel.columns.get(columnModel.getKeyColumnIndex()).getCellIndex(); - - for (int i = 0; i < count; i++) { - if (i < rootKeyedGroup.keyCellIndex) { - rootKeyedGroup.cellIndices[i] = i; - } else if (i > rootKeyedGroup.keyCellIndex) { - rootKeyedGroup.cellIndices[i - 1] = i; - } - } - keyedGroups.add(rootKeyedGroup); - } - } - - protected void setRowDependency( - Project project, - RowDependency rowDependency, - int cellIndex, - int contextRowIndex, - int contextCellIndex - ) { - if (rowDependency.cellDependencies == null) { - int count = project.columnModel.getMaxCellIndex() + 1; - - rowDependency.cellDependencies = new CellDependency[count]; - } - - rowDependency.cellDependencies[cellIndex] = - new CellDependency(contextRowIndex, contextCellIndex); - } - -} +/* + +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.Jsonizable; +import com.google.refine.expr.ExpressionUtils; + +public class RecordModel implements Jsonizable { + final static Logger logger = LoggerFactory.getLogger("RecordModel"); + + final static public class CellDependency { + final public int rowIndex; + final public int cellIndex; + + public CellDependency(int rowIndex, int cellIndex) { + this.rowIndex = rowIndex; + this.cellIndex = cellIndex; + } + + @Override + public String toString() { + return rowIndex+","+cellIndex; + } + } + + final static public class RowDependency { + public int recordIndex; + public CellDependency[] cellDependencies; + public List contextRows; + + @Override + public String toString() { + return "Idx: "+recordIndex+" CellDeps: "+Arrays.toString(cellDependencies)+" Rows:"+contextRows; + } + } + + protected List _rowDependencies; + protected List _records; + + public RowDependency getRowDependency(int rowIndex) { + return _rowDependencies != null && rowIndex >= 0 && rowIndex < _rowDependencies.size() ? + _rowDependencies.get(rowIndex) : null; + } + + public int getRecordCount() { + return _records.size(); + } + + public Record getRecord(int recordIndex) { + return _records != null && recordIndex >= 0 && recordIndex < _records.size() ? + _records.get(recordIndex) : null; + } + + public Record getRecordOfRow(int rowIndex) { + RowDependency rd = getRowDependency(rowIndex); + if (rd != null) { + if (rd.recordIndex < 0) { + rd = getRowDependency(rd.contextRows.get(0)); + } + return getRecord(rd.recordIndex); + } + return null; + } + + @Override + synchronized public void write(JSONWriter writer, Properties options) + throws JSONException { + + writer.object(); + writer.key("hasRecords"); + writer.value( + _records != null && _rowDependencies != null && + _records.size() < _rowDependencies.size()); + writer.endObject(); + } + + static protected class KeyedGroup { + int[] cellIndices; + int keyCellIndex; + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + for (int i:cellIndices) { + sb.append(i).append(','); + } + return "key: " + keyCellIndex + " cells: " + sb.toString(); + } + } + + synchronized public void update(Project project) { + synchronized (project) { + List rows = project.rows; + int rowCount = rows.size(); + + ColumnModel columnModel = project.columnModel; + List keyedGroups = computeKeyedGroups(columnModel); + int groupCount = keyedGroups.size(); + + int[] lastNonBlankRowsByGroup = new int[keyedGroups.size()]; + for (int i = 0; i < lastNonBlankRowsByGroup.length; i++) { + lastNonBlankRowsByGroup[i] = -1; + } + + _rowDependencies = new ArrayList(rowCount); + + int recordIndex = 0; + for (int r = 0; r < rowCount; r++) { + Row row = rows.get(r); + RowDependency rowDependency = new RowDependency(); + + for (int g = 0; g < groupCount; g++) { + KeyedGroup group = keyedGroups.get(g); + + if (!ExpressionUtils.isNonBlankData(row.getCellValue(keyedGroups.get(0).keyCellIndex)) && + !ExpressionUtils.isNonBlankData(row.getCellValue(group.keyCellIndex))) { + int contextRowIndex = lastNonBlankRowsByGroup[g]; + if (contextRowIndex >= 0) { + for (int dependentCellIndex : group.cellIndices) { + if (ExpressionUtils.isNonBlankData(row.getCellValue(dependentCellIndex))) { + setRowDependency( + project, + rowDependency, + dependentCellIndex, + contextRowIndex, + group.keyCellIndex + ); + } + } + } + } else { + lastNonBlankRowsByGroup[g] = r; + } + } + + if (rowDependency.cellDependencies != null && rowDependency.cellDependencies.length > 0) { + rowDependency.recordIndex = -1; + rowDependency.contextRows = new ArrayList(); + for (CellDependency cd : rowDependency.cellDependencies) { + if (cd != null) { + rowDependency.contextRows.add(cd.rowIndex); + } + } + Collections.sort(rowDependency.contextRows); + } else { + rowDependency.recordIndex = recordIndex++; + } + + _rowDependencies.add(rowDependency); + } + + _records = new ArrayList(recordIndex); + if (recordIndex > 0) { + recordIndex = 0; + + int recordRowIndex = 0; + for (int r = 1; r < rowCount; r++) { + RowDependency rd = _rowDependencies.get(r); + if (rd.recordIndex >= 0) { + _records.add(new Record(recordRowIndex, r, recordIndex++)); + + recordIndex = rd.recordIndex; + recordRowIndex = r; + } + } + + _records.add(new Record(recordRowIndex, rowCount, recordIndex++)); + } + } + } + + protected List computeKeyedGroups(ColumnModel columnModel) { + List keyedGroups = new ArrayList(); + + addRootKeyedGroup(columnModel, keyedGroups); + + for (ColumnGroup group : columnModel.columnGroups) { + if (group.keyColumnIndex >= 0) { + KeyedGroup keyedGroup = new KeyedGroup(); + keyedGroup.keyCellIndex = columnModel.columns.get(group.keyColumnIndex).getCellIndex(); + keyedGroup.cellIndices = new int[group.columnSpan - 1]; + + int c = 0; + for (int i = 0; i < group.columnSpan; i++) { + int columnIndex = group.startColumnIndex + i; + if (columnIndex != group.keyColumnIndex && columnIndex < columnModel.columns.size()) { + int cellIndex = columnModel.columns.get(columnIndex).getCellIndex(); + keyedGroup.cellIndices[c++] = cellIndex; + } + } + + keyedGroups.add(keyedGroup); + } + } + + Collections.sort(keyedGroups, new Comparator() { + @Override + public int compare(KeyedGroup o1, KeyedGroup o2) { + return o2.cellIndices.length - o1.cellIndices.length; // larger groups first + } + }); + + dumpKeyedGroups(keyedGroups, columnModel); // for debug + + return keyedGroups; + } + + // debugging helper + private void dumpKeyedGroups(List groups, ColumnModel columnModel) { + for (KeyedGroup g : groups) { + String keyColName = columnModel.getColumnByCellIndex(g.keyCellIndex).getName(); + StringBuffer sb = new StringBuffer(); + for (int ci : g.cellIndices) { + Column col = columnModel.getColumnByCellIndex(ci); + if (col != null) { + // Old projects have col 0 slot empty + sb.append(col.getName()).append(','); + } + } + logger.trace("KeyedGroup " + keyColName + "::" + sb.toString()); + } + } + + protected void addRootKeyedGroup(ColumnModel columnModel, List keyedGroups) { + int count = columnModel.getMaxCellIndex() + 1; + if (count > 0 && columnModel.getKeyColumnIndex() < columnModel.columns.size()) { + KeyedGroup rootKeyedGroup = new KeyedGroup(); + + rootKeyedGroup.cellIndices = new int[count - 1]; + rootKeyedGroup.keyCellIndex = columnModel.columns.get(columnModel.getKeyColumnIndex()).getCellIndex(); + + for (int i = 0; i < count; i++) { + if (i < rootKeyedGroup.keyCellIndex) { + rootKeyedGroup.cellIndices[i] = i; + } else if (i > rootKeyedGroup.keyCellIndex) { + rootKeyedGroup.cellIndices[i - 1] = i; + } + } + keyedGroups.add(rootKeyedGroup); + } + } + + protected void setRowDependency( + Project project, + RowDependency rowDependency, + int cellIndex, + int contextRowIndex, + int contextCellIndex + ) { + if (rowDependency.cellDependencies == null) { + int count = project.columnModel.getMaxCellIndex() + 1; + + rowDependency.cellDependencies = new CellDependency[count]; + } + + rowDependency.cellDependencies[cellIndex] = + new CellDependency(contextRowIndex, contextCellIndex); + } + +} diff --git a/main/src/com/google/refine/model/changes/ColumnAdditionChange.java b/main/src/com/google/refine/model/changes/ColumnAdditionChange.java index f3a6afcee..752ba9f51 100644 --- a/main/src/com/google/refine/model/changes/ColumnAdditionChange.java +++ b/main/src/com/google/refine/model/changes/ColumnAdditionChange.java @@ -62,6 +62,21 @@ public class ColumnAdditionChange extends ColumnChange { newCells.toArray(_newCells); } + + public String getColumnName() { + return _columnName; + } + + + public int getColumnIndex() { + return _columnIndex; + } + + + public int getNewCellIndex() { + return _newCellIndex; + } + @Override public void apply(Project project) { synchronized (project) { diff --git a/main/src/com/google/refine/model/changes/ColumnMoveChange.java b/main/src/com/google/refine/model/changes/ColumnMoveChange.java index de394c8a5..817e3eb36 100644 --- a/main/src/com/google/refine/model/changes/ColumnMoveChange.java +++ b/main/src/com/google/refine/model/changes/ColumnMoveChange.java @@ -58,6 +58,18 @@ public class ColumnMoveChange extends ColumnChange { _newColumnIndex = index; } + public int getOldColumnIndex() { + return _oldColumnIndex; + } + + public String getColumnName() { + return _columnName; + } + + public int getNewColumnIndex() { + return _newColumnIndex; + } + @Override public void apply(Project project) { synchronized (project) { diff --git a/main/src/com/google/refine/model/changes/ColumnRemovalChange.java b/main/src/com/google/refine/model/changes/ColumnRemovalChange.java index 70a2b0636..c7ecc1f92 100644 --- a/main/src/com/google/refine/model/changes/ColumnRemovalChange.java +++ b/main/src/com/google/refine/model/changes/ColumnRemovalChange.java @@ -54,11 +54,15 @@ public class ColumnRemovalChange extends ColumnChange { protected Column _oldColumn; protected CellAtRow[] _oldCells; protected List _oldColumnGroups; - + public ColumnRemovalChange(int index) { _oldColumnIndex = index; } + public int getOldColumnIndex() { + return _oldColumnIndex; + } + @Override public void apply(Project project) { synchronized (project) { diff --git a/main/src/com/google/refine/model/changes/ColumnReorderChange.java b/main/src/com/google/refine/model/changes/ColumnReorderChange.java index bdf714998..e29d01ade 100644 --- a/main/src/com/google/refine/model/changes/ColumnReorderChange.java +++ b/main/src/com/google/refine/model/changes/ColumnReorderChange.java @@ -57,6 +57,11 @@ public class ColumnReorderChange extends ColumnChange { _columnNames = columnNames; } + + public List getColumnNames() { + return _columnNames; + } + @Override public void apply(Project project) { synchronized (project) { diff --git a/main/src/com/google/refine/model/changes/ColumnSplitChange.java b/main/src/com/google/refine/model/changes/ColumnSplitChange.java index bb49a4c4a..adf50cc64 100644 --- a/main/src/com/google/refine/model/changes/ColumnSplitChange.java +++ b/main/src/com/google/refine/model/changes/ColumnSplitChange.java @@ -54,7 +54,7 @@ import com.google.refine.model.Project; import com.google.refine.model.Row; import com.google.refine.util.Pool; -public class ColumnSplitChange implements Change { +public class ColumnSplitChange extends ColumnChange { final protected String _columnName; final protected List _columnNames; @@ -118,6 +118,21 @@ public class ColumnSplitChange implements Change { _newRows = newRows; } + + public List getColumnNames() { + return _columnNames; + } + + + public boolean isRemoveOriginalColumn() { + return _removeOriginalColumn; + } + + + public int getColumnIndex() { + return _columnIndex; + } + @Override public void apply(Project project) { synchronized (project) { diff --git a/main/src/com/google/refine/model/medadata/AbstractMetadata.java b/main/src/com/google/refine/model/medadata/AbstractMetadata.java new file mode 100644 index 000000000..8ae7c3041 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/AbstractMetadata.java @@ -0,0 +1,73 @@ +package com.google.refine.model.medadata; + +import java.io.File; +import java.time.LocalDateTime; +import java.util.Properties; + +import org.apache.commons.beanutils.PropertyUtils; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; + +public abstract class AbstractMetadata implements IMetadata { + private MetadataFormat formatName = MetadataFormat.UNKNOWN; + + protected LocalDateTime written = null; + protected LocalDateTime _modified; + + public MetadataFormat getFormatName() { + return formatName; + } + + + public void setFormatName(MetadataFormat formatName) { + this.formatName = formatName; + } + + @Override + public abstract void loadFromJSON(JSONObject obj); + + @Override + public abstract void loadFromFile(File metadataFile); + + @Override + public abstract void writeToFile(File metadataFile); + + @Override + public boolean isDirty() { + return written == null || _modified.isAfter(written); + } + + @Override + public LocalDateTime getModified() { + return _modified; + } + + @Override + public void updateModified() { + _modified = LocalDateTime.now(); + } + + /** + * @param jsonWriter + * writer to save metadatea to + * @param onlyIfDirty + * true to not write unchanged metadata + * @throws JSONException + */ + @Override + public void write(JSONWriter jsonWriter, boolean onlyIfDirty) throws JSONException { + if (!onlyIfDirty || isDirty()) { + Properties options = new Properties(); + options.setProperty("mode", "save"); + + write(jsonWriter, options); + } + } + + protected static boolean propertyExists(Object bean, String property) { + return PropertyUtils.isReadable(bean, property) && + PropertyUtils.isWriteable(bean, property); + } + +} diff --git a/main/src/com/google/refine/model/medadata/DataPackageMetadata.java b/main/src/com/google/refine/model/medadata/DataPackageMetadata.java new file mode 100644 index 000000000..ba7b27118 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/DataPackageMetadata.java @@ -0,0 +1,130 @@ +package com.google.refine.model.medadata; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.everit.json.schema.ValidationException; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.frictionlessdata.datapackage.Package; +import io.frictionlessdata.datapackage.Resource; +import io.frictionlessdata.datapackage.exceptions.DataPackageException; + + +public class DataPackageMetadata extends AbstractMetadata { + private final static Logger logger = LoggerFactory.getLogger(DataPackageMetadata.class); + + public static final String DEFAULT_FILE_NAME = "datapackage.json"; + + private Package _pkg; + + public DataPackageMetadata() { + setFormatName(MetadataFormat.DATAPACKAGE_METADATA); + + _pkg = PackageExtension.buildPackageFromTemplate(); + } + + @Override + public void loadFromJSON(JSONObject obj) { + try { + _pkg = new Package(obj); + } catch (ValidationException | DataPackageException | IOException e) { + logger.error("Load from JSONObject failed" + obj.toString(4), + ExceptionUtils.getStackTrace(e)); + } + + logger.info("Data Package metadata loaded"); + } + + @Override + public void loadFromFile(File metadataFile) { + String jsonString = null; + try { + jsonString = FileUtils.readFileToString(metadataFile); + } catch (IOException e) { + logger.error("Load data package failed when reading from file: " + metadataFile.getAbsolutePath(), + ExceptionUtils.getStackTrace(e)); + } + + loadFromJSON(new JSONObject(jsonString)); + } + + /** + * Write the package to a json file. + */ + @Override + public void writeToFile(File metadataFile) { + try { + this._pkg.save(metadataFile.getAbsolutePath()); + } catch (IOException e) { + logger.error("IO exception when writing to file " + metadataFile.getAbsolutePath(), + ExceptionUtils.getStackTrace(e)); + } catch (DataPackageException e) { + logger.error("Data package exception when writing to file " + metadataFile.getAbsolutePath(), + ExceptionUtils.getStackTrace(e)); + } + } + + @Override + public void write(JSONWriter jsonWriter, Properties options) + throws JSONException { + StringWriter sw = new StringWriter(); + _pkg.getJson().write(sw); + jsonWriter = new JSONWriter(sw); + } + + @Override + public void loadFromStream(InputStream inputStream) { + try { + this._pkg = new Package(IOUtils.toString(inputStream)); + } catch (ValidationException e) { + logger.error("validation failed", ExceptionUtils.getStackTrace(e)); + } catch (DataPackageException e) { + logger.error("Data package excpetion when loading from stream", ExceptionUtils.getStackTrace(e)); + } catch (IOException e) { + logger.error("IO exception when loading from stream", ExceptionUtils.getStackTrace(e)); + } + } + + public List getResourcePaths() { + List listResources = new ArrayList(); + + for (Resource resource : _pkg.getResources()) { + listResources.add((String) resource.getPath()); + } + + return listResources; + } + + @Override + public JSONObject getJSON() { + return _pkg.getJson(); + } + + public Package getPackage() { + return _pkg; + } + + @Override + public List validate() { + try { + _pkg.validate(); + } catch (ValidationException | IOException | DataPackageException e) { + logger.error("validate json failed", ExceptionUtils.getStackTrace(e)); + } + + return _pkg.getErrors(); + } +} diff --git a/main/src/com/google/refine/model/medadata/DataPackageUrlRewriter.java b/main/src/com/google/refine/model/medadata/DataPackageUrlRewriter.java new file mode 100644 index 000000000..eefe1313c --- /dev/null +++ b/main/src/com/google/refine/model/medadata/DataPackageUrlRewriter.java @@ -0,0 +1,44 @@ +package com.google.refine.model.medadata; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import com.google.refine.importing.UrlRewriter; + + +public class DataPackageUrlRewriter implements UrlRewriter { + @Override + public List rewrite(String url) throws MalformedURLException, IOException { + List listResult = new ArrayList(); + + if (!filter(url)) + return listResult; + + listResult.add(new Result(url, "json", true, MetadataFormat.DATAPACKAGE_METADATA.name())); + + DataPackageMetadata meta = new DataPackageMetadata(); + meta.loadFromStream(new URL(url).openStream()); + // Import the data files. + for (String path : meta.getResourcePaths()) { + String fileURL = getBaseURL(url) + "/" + path; + listResult.add(new Result(fileURL, + "", // leave to guesser. "text/line-based/*sv" + true)); + } + + return listResult; + } + + @Override + public boolean filter(String url) { + return url.endsWith(DataPackageMetadata.DEFAULT_FILE_NAME); + } + + private String getBaseURL(String url) { + return url.replaceFirst(DataPackageMetadata.DEFAULT_FILE_NAME, ""); + } + +} diff --git a/main/src/com/google/refine/model/medadata/IMetadata.java b/main/src/com/google/refine/model/medadata/IMetadata.java new file mode 100644 index 000000000..1c759de5c --- /dev/null +++ b/main/src/com/google/refine/model/medadata/IMetadata.java @@ -0,0 +1,44 @@ +package com.google.refine.model.medadata; + +import java.io.File; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.List; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; + +import com.google.refine.Jsonizable; +/** + * Interface to import/export metadata + */ +public interface IMetadata extends Jsonizable { + public void loadFromJSON(JSONObject obj); + + public void loadFromFile(File metadataFile); + + public void loadFromStream(InputStream inputStream); + + public void writeToFile(File metadataFile); + + /** + * @param jsonWriter writer to save metadatea to + * @param onlyIfDirty true to not write unchanged metadata + * @throws JSONException + */ + public void write(JSONWriter jsonWriter, boolean onlyIfDirty); + + public MetadataFormat getFormatName(); + public void setFormatName(MetadataFormat format); + + public LocalDateTime getModified(); + + public void updateModified(); + + public boolean isDirty(); + + public JSONObject getJSON(); + + public List validate(); +} diff --git a/main/src/com/google/refine/model/medadata/MetadataFactory.java b/main/src/com/google/refine/model/medadata/MetadataFactory.java new file mode 100644 index 000000000..500b69148 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/MetadataFactory.java @@ -0,0 +1,82 @@ +package com.google.refine.model.medadata; + +import java.io.IOException; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.everit.json.schema.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.model.Project; +import com.google.refine.util.JSONUtilities; +import com.google.refine.util.ParsingUtilities; + +import io.frictionlessdata.datapackage.Package; +import io.frictionlessdata.datapackage.Resource; +import io.frictionlessdata.datapackage.exceptions.DataPackageException; + +public class MetadataFactory { + private final static Logger logger = LoggerFactory.getLogger(MetadataFactory.class); + + /** + * Build metadata based on the format + * @param format + * @return + */ + public static IMetadata buildMetadata(MetadataFormat format) { + IMetadata metadata = null; + if (format == MetadataFormat.PROJECT_METADATA) { + metadata = new ProjectMetadata(); + } else if (format == MetadataFormat.DATAPACKAGE_METADATA) { + metadata = new DataPackageMetadata(); + } + + return metadata; + } + + /** + * build an empty Data Package Metadata + * @return + */ + public static DataPackageMetadata buildDataPackageMetadata() { + return (DataPackageMetadata) buildMetadata(MetadataFormat.DATAPACKAGE_METADATA); + } + + /** + * Build an empty data package metadata, then populate the fields from the Project Metadata + * @param project + * @return + */ + public static DataPackageMetadata buildDataPackageMetadata(Project project) { + DataPackageMetadata dpm = buildDataPackageMetadata(); + ProjectMetadata pmd = project.getMetadata(); + Package pkg = dpm.getPackage(); + Resource resource = SchemaExtension.createResource(project.getMetadata().getName(), + project.columnModel); + try { + pkg.addResource(resource); + + putValue(pkg, Package.JSON_KEY_NAME, pmd.getName()); + putValue(pkg, PackageExtension.JSON_KEY_LAST_UPDATED, ParsingUtilities.localDateToString(pmd.getModified())); + putValue(pkg, PackageExtension.JSON_KEY_DESCRIPTION, pmd.getDescription()); + putValue(pkg, PackageExtension.JSON_KEY_TITLE, pmd.getTitle()); + putValue(pkg, PackageExtension.JSON_KEY_HOMEPAGE, pmd.getHomepage()); + putValue(pkg, PackageExtension.JSON_KEY_IMAGE, pmd.getImage()); + putValue(pkg, PackageExtension.JSON_KEY_LICENSE, pmd.getLicense()); + + pkg.removeProperty(PackageExtension.JSON_KEY_KEYWORKS); + pkg.addProperty(PackageExtension.JSON_KEY_KEYWORKS, JSONUtilities.arrayToJSONArray(pmd.getTags())); + } catch (ValidationException | IOException | DataPackageException e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + + return dpm; + } + + private static void putValue(Package pkg, String key, String value) throws DataPackageException { + if(pkg.getJson().has(key)) { + pkg.removeProperty(key); + } + pkg.addProperty(key, value); + } +} diff --git a/main/src/com/google/refine/model/medadata/MetadataFormat.java b/main/src/com/google/refine/model/medadata/MetadataFormat.java new file mode 100644 index 000000000..e6db73d9c --- /dev/null +++ b/main/src/com/google/refine/model/medadata/MetadataFormat.java @@ -0,0 +1,24 @@ +package com.google.refine.model.medadata; + + +/** + * A list of supported metadata format + * + */ +public enum MetadataFormat { + UNKNOWN("UNKNOWN"), + PROJECT_METADATA("PROJECT_METADATA"), + DATAPACKAGE_METADATA("DATAPACKAGE_METADATA"), + CSVW_METADATA("CSVW_METADATA"); + + private final String format; + + private MetadataFormat(final String format) { + this.format = format; + } + + @Override + public String toString() { + return format; + } +} diff --git a/main/src/com/google/refine/model/medadata/PackageExtension.java b/main/src/com/google/refine/model/medadata/PackageExtension.java new file mode 100644 index 000000000..1d8da5cc8 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/PackageExtension.java @@ -0,0 +1,88 @@ +package com.google.refine.model.medadata; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.StringUtils; +import org.everit.json.schema.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.frictionlessdata.datapackage.Package; +import io.frictionlessdata.datapackage.exceptions.DataPackageException; + +/** + * This class contains some methods which is not included in the official "Data Package" repo for now. + * Some methods can be removed after the official library provide the corresponding function. + */ +public class PackageExtension { + private final static Logger logger = LoggerFactory.getLogger(PackageExtension.class); + + private static final int JSON_INDENT_FACTOR = 4; + + public static final String JSON_KEY_LAST_UPDATED = "last_updated"; + public static final String JSON_KEY_DESCRIPTION = "description"; + public static final String JSON_KEY_KEYWORKS = "keywords"; + public static final String JSON_KEY_TITLE = "title"; + public static final String JSON_KEY_HOMEPAGE = "homepage"; + public static final String JSON_KEY_IMAGE = "image"; + public static final String JSON_KEY_LICENSE = "license"; + public static final String JSON_KEY_VERSION = "version"; + + public static String DATAPACKAGE_TEMPLATE_FILE = "schemas/datapackage-template.json"; + + /** + * Do the package since the final spec for the compression/bundle are not settled yet. + * https://github.com/frictionlessdata/datapackage-js/issues/93 + * + * @param pkg Package + * @param dataByteArrayOutputStream ByteArrayOutputStream + * @param destOs OutputStream + * @throws IOException + * @throws FileNotFoundException + * @see Package#saveZip(String outputFilePath) + */ + public static void saveZip(Package pkg, final ByteArrayOutputStream dataByteArrayOutputStream, final OutputStream destOs) throws FileNotFoundException, IOException { + try(ZipOutputStream zos = new ZipOutputStream(destOs)){ + // json file + ZipEntry entry = new ZipEntry(DataPackageMetadata.DEFAULT_FILE_NAME); + zos.putNextEntry(entry); + zos.write(pkg.getJson().toString(JSON_INDENT_FACTOR).getBytes()); + zos.closeEntry(); + // default data file to data.csv or given path(can only handle one file because files cannot be restored) + String path = (String) pkg.getResources().get(0).getPath(); + entry = new ZipEntry(StringUtils.isBlank(path) ? "data.csv" : path); + zos.putNextEntry(entry); + zos.write(dataByteArrayOutputStream.toByteArray()); + zos.closeEntry(); + } + } + + /** + * To build a Package object from a template file contains empty metadata + * + * @param templateFile + */ + public static Package buildPackageFromTemplate() { + try { + ClassLoader classLoader = PackageExtension.class.getClassLoader(); + File file = new File(classLoader.getResource(DATAPACKAGE_TEMPLATE_FILE).getFile()); + return new Package(FileUtils.readFileToString(file), false); + } catch (ValidationException e) { + logger.error("validation failed", ExceptionUtils.getStackTrace(e)); + } catch (DataPackageException e) { + logger.error("DataPackage Exception", ExceptionUtils.getStackTrace(e)); + } catch (IOException e) { + logger.error("IOException when build package from template", ExceptionUtils.getStackTrace(e)); + } + + return null; + } +} diff --git a/main/src/com/google/refine/ProjectMetadata.java b/main/src/com/google/refine/model/medadata/ProjectMetadata.java similarity index 59% rename from main/src/com/google/refine/ProjectMetadata.java rename to main/src/com/google/refine/model/medadata/ProjectMetadata.java index ba7997c54..962a9832e 100644 --- a/main/src/com/google/refine/ProjectMetadata.java +++ b/main/src/com/google/refine/model/medadata/ProjectMetadata.java @@ -31,10 +31,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.google.refine; +package com.google.refine.model.medadata; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.Serializable; -import java.lang.reflect.Field; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -43,49 +52,64 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.json.JSONTokener; import org.json.JSONWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.refine.ProjectManager; import com.google.refine.preference.PreferenceStore; import com.google.refine.preference.TopList; import com.google.refine.util.JSONUtilities; import com.google.refine.util.ParsingUtilities; -public class ProjectMetadata implements Jsonizable { +public class ProjectMetadata extends AbstractMetadata { + final public static String DEFAULT_FILE_NAME = "metadata.json"; + final public static String TEMP_FILE_NAME = "metadata.temp.json"; + final public static String OLD_FILE_NAME = "metadata.old.json"; + private final LocalDateTime _created; - private LocalDateTime _modified; - private LocalDateTime written = null; private String _name = ""; private String _password = ""; private String _encoding = ""; private int _encodingConfidence; - + private int _rowCount; + // user metadata + private JSONArray _userMetadata = new JSONArray();; + + // _tags maps to keywords of the data package metadata private String[] _tags = new String[0]; - private String _creator = ""; private String _contributors = ""; - private String _subject = ""; // Several refine projects may be linked - private String _description = ""; // free form of comment - private int _rowCount; // at the creation. Essential for cleaning old projects too heavy - + private String _subject = ""; // Several refine projects may be linked + private String _description = ""; // free form of comment + // import options is an array for 1-n data sources private JSONArray _importOptionMetadata = new JSONArray(); - - // user metadata - private JSONArray _userMetadata = new JSONArray(); + private Map _customMetadata = new HashMap(); private PreferenceStore _preferenceStore = new PreferenceStore(); + + // below 5 fields are from data package metadata: + private String title = ""; + private String homepage; + private String image = ""; + private String license = ""; + private String version = ""; private final static Logger logger = LoggerFactory.getLogger("project_metadata"); protected ProjectMetadata(LocalDateTime date) { + setFormatName(MetadataFormat.PROJECT_METADATA); _created = date; preparePreferenceStore(_preferenceStore); } @@ -100,7 +124,12 @@ public class ProjectMetadata implements Jsonizable { _modified = modified; _name = name; } - + + public void setRowCount(int rowCount) { + this._rowCount = rowCount; + updateModified(); + } + @Override public void write(JSONWriter writer, Properties options) throws JSONException { @@ -116,16 +145,16 @@ public class ProjectMetadata implements Jsonizable { writer.endArray(); writer.key("created"); writer.value(ParsingUtilities.localDateToString(_created)); writer.key("modified"); writer.value(ParsingUtilities.localDateToString(_modified)); - writer.key("creator"); - writer.value(_creator); - writer.key("contributors"); - writer.value(_contributors); - writer.key("subject"); - writer.value(_subject); - writer.key("description"); - writer.value(_description); - writer.key("rowCount"); - writer.value(_rowCount); + writer.key("creator"); writer.value(_creator); + writer.key("contributors"); writer.value(_contributors); + writer.key("subject"); writer.value(_subject); + writer.key("description"); writer.value(_description); + writer.key("rowCount"); writer.value(_rowCount); + writer.key("title"); writer.value(title); + writer.key("homepage"); writer.value(homepage); + writer.key("image"); writer.value(image); + writer.key("license"); writer.value(license); + writer.key("version"); writer.value(version); writer.key("customMetadata"); writer.object(); @@ -178,64 +207,47 @@ public class ProjectMetadata implements Jsonizable { return "save".equals(options.getProperty("mode")); } - public boolean isDirty() { - return written == null || _modified.isAfter(written); - } - public void write(JSONWriter jsonWriter) throws JSONException { write(jsonWriter, false); } - /** - * @param jsonWriter - * writer to save metadatea to - * @param onlyIfDirty - * true to not write unchanged metadata - * @throws JSONException - */ - public void write(JSONWriter jsonWriter, boolean onlyIfDirty) - throws JSONException { - if (!onlyIfDirty || isDirty()) { - Properties options = new Properties(); - options.setProperty("mode", "save"); + public void loadFromJSON(JSONObject obj) { + extractModifiedLocalTime(obj); - write(jsonWriter, options); - } - } + this._name = JSONUtilities.getString(obj, "name", ""); + this._password = JSONUtilities.getString(obj, "password", ""); - static public ProjectMetadata loadFromJSON(JSONObject obj) { - // TODO: Is this correct? It's using modified date for creation date - ProjectMetadata pm = new ProjectMetadata(JSONUtilities.getLocalDate(obj, "modified", LocalDateTime.now())); + this._encoding = JSONUtilities.getString(obj, "encoding", ""); + this._encodingConfidence = JSONUtilities.getInt(obj, "encodingConfidence", 0); - pm._modified = JSONUtilities.getLocalDate(obj, "modified", LocalDateTime.now()); - pm._name = JSONUtilities.getString(obj, "name", ""); - pm._password = JSONUtilities.getString(obj, "password", ""); + this._creator = JSONUtilities.getString(obj, "creator", ""); + this._contributors = JSONUtilities.getString(obj, "contributors", ""); + this._subject = JSONUtilities.getString(obj, "subject", ""); + this._description = JSONUtilities.getString(obj, "description", ""); + this._rowCount = JSONUtilities.getInt(obj, "rowCount", 0); + + this.title = JSONUtilities.getString(obj, "title", ""); + this.homepage = JSONUtilities.getString(obj, "homepage", ""); + this.image = JSONUtilities.getString(obj, "image", ""); + this.license = JSONUtilities.getString(obj, "license", ""); + this.version = JSONUtilities.getString(obj, "version", ""); - pm._encoding = JSONUtilities.getString(obj, "encoding", ""); - pm._encodingConfidence = JSONUtilities.getInt(obj, "encodingConfidence", 0); - - pm._creator = JSONUtilities.getString(obj, "creator", ""); - pm._contributors = JSONUtilities.getString(obj, "contributors", ""); - pm._subject = JSONUtilities.getString(obj, "subject", ""); - pm._description = JSONUtilities.getString(obj, "description", ""); - pm._rowCount = JSONUtilities.getInt(obj, "rowCount", 0); - - pm._tags = JSONUtilities.getStringArray(obj, "tags"); + this._tags = JSONUtilities.getStringArray(obj, "tags"); if (obj.has("preferences") && !obj.isNull("preferences")) { try { - pm._preferenceStore.load(obj.getJSONObject("preferences")); + this._preferenceStore.load(obj.getJSONObject("preferences")); } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } if (obj.has("expressions") && !obj.isNull("expressions")) { // backward compatibility try { - ((TopList) pm._preferenceStore.get("scripting.expressions")).load(obj.getJSONArray("expressions")); + ((TopList) this._preferenceStore.get("scripting.expressions")).load(obj.getJSONArray("expressions")); } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } @@ -243,41 +255,45 @@ public class ProjectMetadata implements Jsonizable { try { JSONObject obj2 = obj.getJSONObject("customMetadata"); - @SuppressWarnings("unchecked") Iterator keys = obj2.keys(); while (keys.hasNext()) { String key = keys.next(); Object value = obj2.get(key); if (value != null && value instanceof Serializable) { - pm._customMetadata.put(key, (Serializable) value); + this._customMetadata.put(key, (Serializable) value); } } } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } if (obj.has("importOptionMetadata") && !obj.isNull("importOptionMetadata")) { try { JSONArray jsonArray = obj.getJSONArray("importOptionMetadata"); - pm._importOptionMetadata = jsonArray; + this._importOptionMetadata = jsonArray; } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } if (obj.has(PreferenceStore.USER_METADATA_KEY) && !obj.isNull(PreferenceStore.USER_METADATA_KEY)) { try { JSONArray jsonArray = obj.getJSONArray(PreferenceStore.USER_METADATA_KEY); - pm._userMetadata = jsonArray; + this._userMetadata = jsonArray; } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } - pm.written = LocalDateTime.now(); // Mark it as not needing writing until modified + this.written = LocalDateTime.now(); // Mark it as not needing writing until modified - return pm; + } + + private void extractModifiedLocalTime(JSONObject obj) { + String modified = JSONUtilities.getString(obj, "modified", LocalDateTime.now().toString()); + + this._modified = ParsingUtilities.stringToLocalDate(modified); } static protected void preparePreferenceStore(PreferenceStore ps) { @@ -288,25 +304,7 @@ public class ProjectMetadata implements Jsonizable { public LocalDateTime getCreated() { return _created; } - - public void setName(String name) { - this._name = name; - updateModified(); - } - - public String getName() { - return _name; - } - - public void setEncoding(String encoding) { - this._encoding = encoding; - updateModified(); - } - - public String getEncoding() { - return _encoding; - } - + public void setEncodingConfidence(int confidence) { this._encodingConfidence = confidence; updateModified(); @@ -342,6 +340,11 @@ public class ProjectMetadata implements Jsonizable { updateModified(); } + public void appendTags(String[] tags) { + String[] mergedTags = (String[])ArrayUtils.addAll(this._tags, tags); + setTags(mergedTags); + } + public String[] getTags() { if (_tags == null) this._tags = new String[0]; return _tags; @@ -380,21 +383,39 @@ public class ProjectMetadata implements Jsonizable { } updateModified(); } - + public JSONArray getImportOptionMetadata() { return _importOptionMetadata; } - + public void setImportOptionMetadata(JSONArray jsonArray) { _importOptionMetadata = jsonArray; updateModified(); } - + public void appendImportOptionMetadata(JSONObject obj) { _importOptionMetadata.put(obj); updateModified(); } + + public String getEncoding() { + return _encoding; + } + public void setName(String name) { + this._name = name; + updateModified(); + } + + public String getName() { + return _name; + } + + public void setEncoding(String encoding) { + this._encoding = encoding; + updateModified(); + } + public String getCreator() { return _creator; } @@ -404,6 +425,7 @@ public class ProjectMetadata implements Jsonizable { updateModified(); } + public String getContributors() { return _contributors; } @@ -413,6 +435,7 @@ public class ProjectMetadata implements Jsonizable { updateModified(); } + public String getSubject() { return _subject; } @@ -421,7 +444,7 @@ public class ProjectMetadata implements Jsonizable { this._subject = subject; updateModified(); } - + public String getDescription() { return _description; } @@ -430,25 +453,70 @@ public class ProjectMetadata implements Jsonizable { this._description = description; updateModified(); } - - public int getRowCount() { - return _rowCount; + + + public String getTitle() { + return title; } - public void setRowCount(int rowCount) { - this._rowCount = rowCount; + + public void setTitle(String title) { + this.title = title; updateModified(); } + + public String getHomepage() { + return homepage; + } + + + public void setHomepage(String homepage) { + this.homepage = homepage; + updateModified(); + } + + + public String getImage() { + return image; + } + + + public void setImage(String image) { + this.image = image; + updateModified(); + } + + + public String getLicense() { + return license; + } + + + public void setLicense(String license) { + this.license = license; + updateModified(); + } + + public String getVersion() { + return version; + } + + + public void setVersion(String version) { + this.version = version; + updateModified(); + } + public JSONArray getUserMetadata() { return _userMetadata; } - + public void setUserMetadata(JSONArray userMetadata) { this._userMetadata = userMetadata; } - - private void updateUserMetadata(String metaName, String valueString) { + + private void updateUserMetadata(String metaName, String valueString) { for (int i = 0; i < _userMetadata.length(); i++) { try { JSONObject obj = _userMetadata.getJSONObject(i); @@ -456,25 +524,80 @@ public class ProjectMetadata implements Jsonizable { obj.put("value", valueString); } } catch (JSONException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + logger.error(ExceptionUtils.getStackTrace(e)); } } } - - public void setAnyField(String metaName, String valueString) { - Class metaClass = this.getClass(); - try { - Field metaField = metaClass.getDeclaredField("_" + metaName); - if (metaName.equals("tags")) { - metaField.set(this, valueString.split(",")); - } else { - metaField.set(this, valueString); + + public void setAnyStringField(String metaName, String valueString) { + if (propertyExists(this, metaName)) { + try { + if (metaName.equals("tags")) { + BeanUtils.setProperty(this, metaName, valueString.split(",")); + } else + BeanUtils.setProperty(this, metaName, valueString); + } catch (IllegalAccessException | InvocationTargetException ite) { + logger.error(ExceptionUtils.getStackTrace(ite)); } - } catch (NoSuchFieldException e) { + } else { updateUserMetadata(metaName, valueString); - } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { - logger.error(ExceptionUtils.getFullStackTrace(e)); + } + } + + @Override + public void loadFromFile(File metadataFile) { + InputStream targetStream = null; + try { + targetStream = FileUtils.openInputStream(metadataFile); + } catch (IOException e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + loadFromStream(targetStream); + } + + @Override + public void writeToFile(File metadataFile) { + Writer writer = null; + try { + writer = new OutputStreamWriter(new FileOutputStream(metadataFile)); + + JSONWriter jsonWriter = new JSONWriter(writer); + write(jsonWriter, false); + } catch (FileNotFoundException e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } finally { + try { + writer.close(); + } catch (IOException e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } } } + @Override + public void loadFromStream(InputStream inputStream) { + try (InputStreamReader reader = new InputStreamReader(inputStream)) { + JSONTokener tokener = new JSONTokener(reader); + JSONObject obj = (JSONObject) tokener.nextValue(); + + this.loadFromJSON(obj); + } catch (IOException e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + } + + @Override + public JSONObject getJSON() { + StringWriter writer = new StringWriter(); + JSONWriter jsonWriter = new JSONWriter(writer); + writeWithoutOption(jsonWriter); + + return new JSONObject(jsonWriter.toString()); + } + + @Override + public List validate() { + return null; + } + } diff --git a/main/src/com/google/refine/model/medadata/SchemaExtension.java b/main/src/com/google/refine/model/medadata/SchemaExtension.java new file mode 100644 index 000000000..8c3afd6bc --- /dev/null +++ b/main/src/com/google/refine/model/medadata/SchemaExtension.java @@ -0,0 +1,64 @@ +package com.google.refine.model.medadata; + +import com.google.refine.model.Column; +import com.google.refine.model.ColumnModel; + +import io.frictionlessdata.datapackage.Resource; +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.Schema; + +/** + * This class contains some methods which is not included in the official "table schema" repo for now. + * Some methods can be removed after the official library provide the corresponding function. + */ +public class SchemaExtension { + private static final String DEFAULT_RESOURCE_PATH = "data/"; + private static final String DEFAULT_RESOURCE_SUFFIX = ".csv"; + + /** + * insert the field to schema at specified position + * @param schema + * @param field + * @param position + */ + public static void insertField(Schema schema, Field field, int position) { + schema.getFields().add(position, field); + } + + /** + * Remove the filed from the schema at specified position + * @param schema + * @param index + * @return + */ + public static Field removeField(Schema schema, int index) { + return schema.getFields().remove(index); + } + + /** + * Create a resource by name, get the schema information from the ColumnModel + * @param resourceName + * @param columnModel + * @return + * @see ColumnModel + */ + public static Resource createResource(String resourceName, ColumnModel columnModel) { + // populate the data package schema from the openrefine column model + Schema schema = new Schema(); + for (Column column : columnModel.columns) { + schema.addField(new Field(column.getName(), + column.getType(), + column.getFormat(), + column.getTitle(), + column.getDescription(), + column.getConstraints())); + } + + Resource resource = new Resource(resourceName, + DEFAULT_RESOURCE_PATH + resourceName + DEFAULT_RESOURCE_SUFFIX, + schema.getJson()); + + return resource; + } + +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidateOperation.java b/main/src/com/google/refine/model/medadata/validator/ValidateOperation.java new file mode 100644 index 000000000..a7bbdcb55 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidateOperation.java @@ -0,0 +1,32 @@ +package com.google.refine.model.medadata.validator; + +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; + +import com.google.refine.model.AbstractOperation; +import com.google.refine.model.Project; + +public class ValidateOperation extends AbstractOperation { + private Project project; + private JSONObject options; + + public ValidateOperation(Project project, JSONObject options) { + this.project = project; + this.options = options; + } + + public JSONObject startProcess() { + return ValidatorInspector.inspect(project, options); + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + // TODO Auto-generated method stub + + } + +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidatorConfig.java b/main/src/com/google/refine/model/medadata/validator/ValidatorConfig.java new file mode 100644 index 000000000..7b5642aa1 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidatorConfig.java @@ -0,0 +1,6 @@ +package com.google.refine.model.medadata.validator; + + +public class ValidatorConfig { + +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidatorExceptions.java b/main/src/com/google/refine/model/medadata/validator/ValidatorExceptions.java new file mode 100644 index 000000000..c4cf5ea27 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidatorExceptions.java @@ -0,0 +1,6 @@ +package com.google.refine.model.medadata.validator; + + +public class ValidatorExceptions { + +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidatorInspector.java b/main/src/com/google/refine/model/medadata/validator/ValidatorInspector.java new file mode 100644 index 000000000..ca7972cc8 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidatorInspector.java @@ -0,0 +1,102 @@ +package com.google.refine.model.medadata.validator; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.model.Column; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.validator.checks.TypeorFormatError; +import com.google.refine.model.medadata.validator.checks.Validator; +import com.google.refine.util.JSONUtilities; + +public class ValidatorInspector { + private final static Logger logger = LoggerFactory.getLogger(ValidatorInspector.class); + + /** + * Return a report contains the validate result + * @param project + * @param options + * @return + */ + public static JSONObject inspect(Project project, JSONObject options) { + List columnNames; + String COLUMN_NAMES_KEY = "columnNames"; + Map> columnToCheckersMap = new HashMap>(); + JSONArray validateReport = new JSONArray(); + + logger.info("starting inspect with options:" + options.toString()); + columnNames = JSONUtilities.toStringList(options.getJSONArray(COLUMN_NAMES_KEY)); + + // build the check items + List validatorList = null; + for(String columnName : columnNames) { + validatorList = compileChecks(project, columnName, options); + if (validatorList.size() >= 0) + columnToCheckersMap.put(columnName, validatorList); + } + logger.info("=========================================================="); + logger.info("Inspector finished the checks compile. will do following check:"); + for (Entry> entry : columnToCheckersMap.entrySet()) { + logger.info("Column Name: " + entry.getKey()); + for (Validator v : entry.getValue()) { + logger.info("\t Validator: " + v.getClass().getSimpleName()); + } + } + logger.info("=========================================================="); + + // do the inspect in another loop: + for(String columnName : columnNames) { + List validators = columnToCheckersMap.get(columnName); + if (validators != null) { + for (Validator validator : validators) { + JSONArray result = validator.validate(); + if (result != null && result.length() > 0) + JSONUtilities.concatArray(validateReport, result); + } + } + } + logger.info("Inspector finished the validation."); + + return new JSONObject().put("validation-reports", (Object)validateReport); + } + + private static List compileChecks(Project project, String columnName, JSONObject options) { + Map constraintHandlersMap = ValidatorRegistry.getInstance().getConstraintHandlersMap(); + + Column column = project.columnModel.getColumnByName(columnName); + List validatorList = new ArrayList(); + + int columnIndex = project.columnModel.getColumnIndexByName(columnName); + + validatorList.add(new TypeorFormatError(project, columnIndex, options)); + + + if (column.getConstraints() != null) { + for (Entry entry : column.getConstraints().entrySet()) { + Class clazz = constraintHandlersMap.get(entry.getKey()); + try { + Constructor c = clazz.getConstructor(Project.class, int.class, JSONObject.class); + validatorList.add(c.newInstance(project, columnIndex, options)); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + logger.error("failed to do compileChecks:" + ExceptionUtils.getStackTrace(e)); + } + } + } + + return validatorList; + } + + +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidatorRegistry.java b/main/src/com/google/refine/model/medadata/validator/ValidatorRegistry.java new file mode 100644 index 000000000..3fe9d4e65 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidatorRegistry.java @@ -0,0 +1,41 @@ +package com.google.refine.model.medadata.validator; + +import java.util.HashMap; +import java.util.Map; + +import com.google.refine.model.medadata.validator.checks.EnumerableConstraint; +import com.google.refine.model.medadata.validator.checks.MaximumConstraint; +import com.google.refine.model.medadata.validator.checks.MaximumLengthConstraint; +import com.google.refine.model.medadata.validator.checks.MinimumConstraint; +import com.google.refine.model.medadata.validator.checks.MinimumLengthConstraint; +import com.google.refine.model.medadata.validator.checks.PatternConstraint; +import com.google.refine.model.medadata.validator.checks.RequiredConstraint; + +import io.frictionlessdata.tableschema.Field; + +public class ValidatorRegistry { + private static ValidatorRegistry instance = null; + private Map constraintHandlersMap = null; + + private ValidatorRegistry() { + constraintHandlersMap = new HashMap(); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_ENUM,EnumerableConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_MAXIMUM, MaximumConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_MAX_LENGTH, MaximumLengthConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_MINIMUM, MinimumConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_MIN_LENGTH, MinimumLengthConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_PATTERN, PatternConstraint.class); + constraintHandlersMap.put(Field.CONSTRAINT_KEY_REQUIRED, RequiredConstraint.class); + } + + public static ValidatorRegistry getInstance() { + if (instance == null) + instance = new ValidatorRegistry(); + + return instance; + } + + public Map getConstraintHandlersMap() { + return constraintHandlersMap; + } +} diff --git a/main/src/com/google/refine/model/medadata/validator/ValidatorSpec.java b/main/src/com/google/refine/model/medadata/validator/ValidatorSpec.java new file mode 100644 index 000000000..f32084615 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/ValidatorSpec.java @@ -0,0 +1,28 @@ +package com.google.refine.model.medadata.validator; + +import java.util.Locale; +import java.util.ResourceBundle; + +public class ValidatorSpec { + private static String VALIDATOR_RESOURCE_BUNDLE = "validator-resource-bundle"; + + private static ValidatorSpec instance = null; + private ResourceBundle bundle; + + private ValidatorSpec() { + Locale locale = new Locale("en", "US"); + bundle = ResourceBundle.getBundle(VALIDATOR_RESOURCE_BUNDLE, locale); + } + + public static ValidatorSpec getInstance() { + if (instance == null) + instance = new ValidatorSpec(); + + return instance; + } + + public String getMessage(String code) { + return bundle.getString(code); + } + +} diff --git a/main/src/com/google/refine/model/medadata/validator/checks/AbstractValidator.java b/main/src/com/google/refine/model/medadata/validator/checks/AbstractValidator.java new file mode 100644 index 000000000..85b587280 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/AbstractValidator.java @@ -0,0 +1,122 @@ +package com.google.refine.model.medadata.validator.checks; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.text.StrSubstitutor; +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.model.Cell; +import com.google.refine.model.Column; +import com.google.refine.model.Project; +import com.google.refine.model.Row; +import com.google.refine.model.medadata.validator.ValidatorSpec; + +public abstract class AbstractValidator implements Validator { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected Project project; + protected int cellIndex; + protected JSONObject options; + protected Column column; + protected String code; + + protected JSONArray jsonErros = null; + + protected Map lookup = new HashMap(6); + + /** + * Constructor + * @param project + * @param cellIndex + * @param options + */ + public AbstractValidator(Project project, int cellIndex, JSONObject options) { + this.project = project; + this.cellIndex = cellIndex; + this.options = options; + this.column = project.columnModel.getColumnByCellIndex(cellIndex); + } + + @Override + public JSONArray validate() { + for (int rowIndex = 0;rowIndex < project.rows.size();rowIndex++) { + Row row = project.rows.get(rowIndex); + Cell cell = row.getCell(cellIndex); + if (filter(cell)) + continue; + + boolean checkResult = checkCell(cell); + if (!checkResult) { + addError(formatErrorMessage(cell, rowIndex + 1)); + } + } + return jsonErros; + } + + @Override + public JSONObject formatErrorMessage(Cell cell, int rowIndex) { + String message = null; + message = ValidatorSpec.getInstance().getMessage(code); + String formattedMessage = format(message, cell.value.toString(), rowIndex, cellIndex, code); + JSONObject json = new JSONObject(); + json.put("code", code); + json.put("message", formattedMessage); + json.put("row-number", rowIndex); + json.put("column-number", cellIndex); + + return json; + } + + /** + * MessageFormat.format cannot take the named parameters. + * @param message + * @param value + * @param rowIndex + * @param cellIndex + * @param code + * @return + */ + private String format(String message, String value, int rowIndex, int cellIndex, String code) { + lookup.put("value", value); + lookup.put("row_number", Integer.toString(rowIndex)); + lookup.put("column_number", Integer.toString(cellIndex)); + lookup.put("constraint", code); + customizedFormat(); + + return new StrSubstitutor(lookup).replace(message); + } + + /* + * Empty body since default there is no customized Format + * @see com.google.refine.model.medadata.validator.checks.Validator#customizedFormat() + */ + @Override + public void customizedFormat() { + } + + /** + * will skip the cell if return true + */ + @Override + public boolean filter(Cell cell) { + return cell == null || cell.value == null; + } + + @Override + public boolean checkCell(Cell cell) { + return false; + } + + @Override + public void addError(JSONObject result) { + if (jsonErros == null) + jsonErros = new JSONArray(); + + jsonErros.put(result); + } + +} diff --git a/main/src/com/google/refine/model/medadata/validator/checks/BlankHeader.java b/main/src/com/google/refine/model/medadata/validator/checks/BlankHeader.java new file mode 100644 index 000000000..1f4fb39c4 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/BlankHeader.java @@ -0,0 +1,11 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class BlankHeader extends AbstractValidator { + public BlankHeader(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/BlankRow.java b/main/src/com/google/refine/model/medadata/validator/checks/BlankRow.java new file mode 100644 index 000000000..6f7262923 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/BlankRow.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class BlankRow extends AbstractValidator { + + public BlankRow(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/DuplicateHeader.java b/main/src/com/google/refine/model/medadata/validator/checks/DuplicateHeader.java new file mode 100644 index 000000000..957320423 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/DuplicateHeader.java @@ -0,0 +1,11 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class DuplicateHeader extends AbstractValidator { + public DuplicateHeader(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/DuplicateRow.java b/main/src/com/google/refine/model/medadata/validator/checks/DuplicateRow.java new file mode 100644 index 000000000..3cab2a56b --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/DuplicateRow.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class DuplicateRow extends AbstractValidator { + + public DuplicateRow(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/EnumerableConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/EnumerableConstraint.java new file mode 100644 index 000000000..e76f4e667 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/EnumerableConstraint.java @@ -0,0 +1,28 @@ +package com.google.refine.model.medadata.validator.checks; + +import java.util.List; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; + +public class EnumerableConstraint extends AbstractValidator { + private List enumList; + + @SuppressWarnings("unchecked") + public EnumerableConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "enumerable-constraint"; + + enumList = (List) column.getConstraints().get(Field.CONSTRAINT_KEY_ENUM); + } + + @Override + public boolean checkCell(Cell cell) { + // XXX: deal with recon + return enumList.contains(cell.value); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/ExtraHeader.java b/main/src/com/google/refine/model/medadata/validator/checks/ExtraHeader.java new file mode 100644 index 000000000..d32608f94 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/ExtraHeader.java @@ -0,0 +1,11 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class ExtraHeader extends AbstractValidator { + public ExtraHeader(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/ExtraValue.java b/main/src/com/google/refine/model/medadata/validator/checks/ExtraValue.java new file mode 100644 index 000000000..79e1a6751 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/ExtraValue.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class ExtraValue extends AbstractValidator { + + public ExtraValue(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MaximumConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/MaximumConstraint.java new file mode 100644 index 000000000..4b4e2df36 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MaximumConstraint.java @@ -0,0 +1,39 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.exceptions.ConstraintsException; +import io.frictionlessdata.tableschema.exceptions.InvalidCastException; + +@SuppressWarnings("rawtypes") +public class MaximumConstraint extends AbstractValidator { + private String threshold; + + public MaximumConstraint(Project project, int cellIndex, JSONObject options) throws InvalidCastException, ConstraintsException { + super(project, cellIndex, options); + this.code = "maximum-constraint"; + threshold = (String)column.getConstraints() + .get(Field.CONSTRAINT_KEY_MAXIMUM); + } + + @SuppressWarnings("unchecked") + @Override + public boolean checkCell(Cell cell) { + boolean valid = true; + + try { + Comparable value = column.castValue(cell.value.toString()); + // return this - threshold + if (value.compareTo(column.castValue(threshold)) > 0) + valid = false; + } catch (InvalidCastException | ConstraintsException e) { + valid = false; + } + + return valid; + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MaximumLengthConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/MaximumLengthConstraint.java new file mode 100644 index 000000000..076ec29ef --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MaximumLengthConstraint.java @@ -0,0 +1,26 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; + +public class MaximumLengthConstraint extends AbstractValidator { + private int maxLength; + + public MaximumLengthConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "maximum-length-constraint"; + + maxLength = (int) column.getConstraints() + .get(Field.CONSTRAINT_KEY_MAX_LENGTH); + } + + @Override + public boolean checkCell(Cell cell) { + return cell.value.toString().length() <= maxLength; + } + +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MinimumConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/MinimumConstraint.java new file mode 100644 index 000000000..c01d3bd12 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MinimumConstraint.java @@ -0,0 +1,39 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.exceptions.ConstraintsException; +import io.frictionlessdata.tableschema.exceptions.InvalidCastException; + +@SuppressWarnings("rawtypes") +public class MinimumConstraint extends AbstractValidator { +private String threshold; + + public MinimumConstraint(Project project, int cellIndex, JSONObject options) throws InvalidCastException, ConstraintsException { + super(project, cellIndex, options); + this.code = "minimum-constraint"; + threshold = (String)column.getConstraints() + .get(Field.CONSTRAINT_KEY_MINIMUM); + } + + @SuppressWarnings("unchecked") + @Override + public boolean checkCell(Cell cell) { + boolean valid = true; + + try { + Comparable value = column.castValue(cell.value.toString()); + // return this - threshold + if (value.compareTo(column.castValue(threshold)) < 0) + valid = false; + } catch (InvalidCastException | ConstraintsException e) { + valid = false; + } + + return valid; + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MinimumLengthConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/MinimumLengthConstraint.java new file mode 100644 index 000000000..f8e423f1e --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MinimumLengthConstraint.java @@ -0,0 +1,35 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; + +public class MinimumLengthConstraint extends AbstractValidator { + private int minLength; + + public MinimumLengthConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "minimum-length-constrain"; + + minLength = (int)column.getConstraints() + .get(Field.CONSTRAINT_KEY_MIN_LENGTH); + } + + @Override + public boolean filter(Cell cell) { + return true; + } + + @Override + public boolean checkCell(Cell cell) { + if (cell == null || cell.value == null) + return false; + + return cell.value.toString().length() >= minLength; + } + + +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MissingHeader.java b/main/src/com/google/refine/model/medadata/validator/checks/MissingHeader.java new file mode 100644 index 000000000..8574ebe65 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MissingHeader.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class MissingHeader extends AbstractValidator { + + public MissingHeader(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/MissingValue.java b/main/src/com/google/refine/model/medadata/validator/checks/MissingValue.java new file mode 100644 index 000000000..6e730ba8e --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/MissingValue.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class MissingValue extends AbstractValidator { + + public MissingValue(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/NonMatchingHeader.java b/main/src/com/google/refine/model/medadata/validator/checks/NonMatchingHeader.java new file mode 100644 index 000000000..110403c6e --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/NonMatchingHeader.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class NonMatchingHeader extends AbstractValidator { + + public NonMatchingHeader(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/PatternConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/PatternConstraint.java new file mode 100644 index 000000000..52798b9c6 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/PatternConstraint.java @@ -0,0 +1,30 @@ +package com.google.refine.model.medadata.validator.checks; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.Field; + +public class PatternConstraint extends AbstractValidator { + private String regexPattern; + + public PatternConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "pattern-constraint"; + + this.regexPattern = (String)column.getConstraints().get(Field.CONSTRAINT_KEY_PATTERN); + } + + @Override + public boolean checkCell(Cell cell) { + Pattern pattern = Pattern.compile(regexPattern); + Matcher matcher = pattern.matcher((String)cell.value); + + return matcher.matches(); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/RequiredConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/RequiredConstraint.java new file mode 100644 index 000000000..7813b9506 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/RequiredConstraint.java @@ -0,0 +1,26 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +public class RequiredConstraint extends AbstractValidator { + + public RequiredConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "required-constraint"; + } + + @Override + public boolean filter(Cell cell) { + // always check + return false; + } + + @Override + public boolean checkCell(Cell cell) { + return StringUtils.isNotBlank(cell.value.toString()); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/TypeorFormatError.java b/main/src/com/google/refine/model/medadata/validator/checks/TypeorFormatError.java new file mode 100644 index 000000000..6aaefcd27 --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/TypeorFormatError.java @@ -0,0 +1,49 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Cell; +import com.google.refine.model.Project; + +import io.frictionlessdata.tableschema.exceptions.ConstraintsException; +import io.frictionlessdata.tableschema.exceptions.InvalidCastException; + +public class TypeorFormatError extends AbstractValidator { + private String type; + private String format; + + public TypeorFormatError(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "type-or-format-error"; + + this.type = column.getType(); + this.format = column.getFormat(); + } + + @Override + public boolean checkCell(Cell cell) { + boolean valid = true; + + try { + column.castValue(cell.value.toString()); + } catch (InvalidCastException | ConstraintsException e) { + // patch for issue: https://github.com/frictionlessdata/tableschema-java/issues/21 + if ("number".equals(type)) { + try { + column.castValue(cell.value.toString() + ".0"); + } catch (InvalidCastException | ConstraintsException e1) { + valid = false; + } + } else + valid = false; + } + + return valid; + } + + @Override + public void customizedFormat() { + lookup.put("field_type", type); + lookup.put("field_format", format); + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/UniqueConstraint.java b/main/src/com/google/refine/model/medadata/validator/checks/UniqueConstraint.java new file mode 100644 index 000000000..1f562362d --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/UniqueConstraint.java @@ -0,0 +1,12 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONObject; + +import com.google.refine.model.Project; + +public class UniqueConstraint extends AbstractValidator { + public UniqueConstraint(Project project, int cellIndex, JSONObject options) { + super(project, cellIndex, options); + this.code = "unique-constraint"; + } +} \ No newline at end of file diff --git a/main/src/com/google/refine/model/medadata/validator/checks/Validator.java b/main/src/com/google/refine/model/medadata/validator/checks/Validator.java new file mode 100644 index 000000000..ad064657a --- /dev/null +++ b/main/src/com/google/refine/model/medadata/validator/checks/Validator.java @@ -0,0 +1,36 @@ +package com.google.refine.model.medadata.validator.checks; + +import org.json.JSONArray; +import org.json.JSONObject; + +import com.google.refine.model.Cell; + +public interface Validator { + /** + * Given the options and cell index, apply the validate operation. + * @return + */ + public JSONArray validate(); + + /** + * Skip if cell is incomplete + * @return + */ + public boolean filter(Cell cell); + + /** + * check the cell against the table schema + * @param cell + * @return false if fails the validation / check. Otherwise return true + */ + public boolean checkCell(Cell cell); + + /** + * Add error into the report for return + */ + public void addError(JSONObject result); + + public JSONObject formatErrorMessage(Cell cell, int rowIndex); + + public void customizedFormat(); +} diff --git a/main/src/com/google/refine/model/recon/ReconciledDataExtensionJob.java b/main/src/com/google/refine/model/recon/ReconciledDataExtensionJob.java index 0ddce01d3..07f994842 100644 --- a/main/src/com/google/refine/model/recon/ReconciledDataExtensionJob.java +++ b/main/src/com/google/refine/model/recon/ReconciledDataExtensionJob.java @@ -184,7 +184,7 @@ public class ReconciledDataExtensionJob { String str = val.getString("str"); storeCell(rows, rowindex, colindex, str); } else if (val.has("float")) { - float v = Float.parseFloat(val.getString("float")); + float v = val.getBigDecimal("float").floatValue(); storeCell(rows, rowindex, colindex, v); } else if (val.has("int")) { int v = Integer.parseInt(val.getString("int")); diff --git a/main/src/com/google/refine/model/recon/StandardReconConfig.java b/main/src/com/google/refine/model/recon/StandardReconConfig.java index d6f02548c..f1cb97d3b 100644 --- a/main/src/com/google/refine/model/recon/StandardReconConfig.java +++ b/main/src/com/google/refine/model/recon/StandardReconConfig.java @@ -48,7 +48,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -112,8 +112,7 @@ public class StandardReconConfig extends ReconConfig { JSONObject t = obj.has("type") && !obj.isNull("type") ? obj.getJSONObject("type") : null; - String limitString = obj.has("limit") && !obj.isNull("limit") ? obj.getString("limit") : ""; - int limit = "".equals(limitString) ? 0 : Integer.parseInt(limitString); + int limit = obj.has("limit") && !obj.isNull("limit") ? obj.getInt("limit") : 0; return new StandardReconConfig( obj.getString("service"), diff --git a/main/src/com/google/refine/operations/cell/MultiValuedCellSplitOperation.java b/main/src/com/google/refine/operations/cell/MultiValuedCellSplitOperation.java index 9b614f60e..83d2fdbca 100644 --- a/main/src/com/google/refine/operations/cell/MultiValuedCellSplitOperation.java +++ b/main/src/com/google/refine/operations/cell/MultiValuedCellSplitOperation.java @@ -38,7 +38,7 @@ import java.util.List; import java.util.Properties; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/operations/column/ColumnAdditionByFetchingURLsOperation.java b/main/src/com/google/refine/operations/column/ColumnAdditionByFetchingURLsOperation.java index f08f83887..d231fad04 100644 --- a/main/src/com/google/refine/operations/column/ColumnAdditionByFetchingURLsOperation.java +++ b/main/src/com/google/refine/operations/column/ColumnAdditionByFetchingURLsOperation.java @@ -42,12 +42,15 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.concurrent.ExecutionException; import org.json.JSONException; import org.json.JSONObject; +import org.json.JSONArray; import org.json.JSONWriter; import com.google.refine.browsing.Engine; @@ -66,6 +69,8 @@ import com.google.refine.model.Project; import com.google.refine.model.Row; import com.google.refine.model.changes.CellAtRow; import com.google.refine.model.changes.ColumnAdditionChange; +import com.google.refine.commands.HttpHeadersSupport; +import com.google.refine.commands.HttpHeadersSupport.HttpHeaderInfo; import com.google.refine.operations.EngineDependentOperation; import com.google.refine.operations.OnError; import com.google.refine.operations.OperationRegistry; @@ -77,6 +82,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; import com.google.common.cache.CacheLoader; + public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperation { final protected String _baseColumnName; final protected String _urlExpression; @@ -86,6 +92,7 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat final protected int _columnInsertIndex; final protected int _delay; final protected boolean _cacheResponses; + final protected JSONArray _httpHeadersJson; static public AbstractOperation reconstruct(Project project, JSONObject obj) throws Exception { JSONObject engineConfig = obj.getJSONObject("engineConfig"); @@ -98,7 +105,8 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat obj.getString("newColumnName"), obj.getInt("columnInsertIndex"), obj.getInt("delay"), - obj.optBoolean("cacheResponses", false) // false for retro-compatibility + obj.optBoolean("cacheResponses", false), // false for retro-compatibility + obj.optJSONArray("httpHeadersJson") // will be null if it doesn't exist for retro-compatibility ); } @@ -110,7 +118,8 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat String newColumnName, int columnInsertIndex, int delay, - boolean cacheResponses + boolean cacheResponses, + JSONArray httpHeadersJson ) { super(engineConfig); @@ -123,6 +132,7 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat _delay = delay; _cacheResponses = cacheResponses; + _httpHeadersJson = httpHeadersJson; } @Override @@ -140,6 +150,7 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat writer.key("onError"); writer.value(TextTransformOperation.onErrorToString(_onError)); writer.key("delay"); writer.value(_delay); writer.key("cacheResponses"); writer.value(_cacheResponses); + writer.key("httpHeadersJson"); writer.value(_httpHeadersJson); writer.endObject(); } @@ -171,7 +182,8 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat engine, eval, getBriefDescription(null), - _cacheResponses + _cacheResponses, + _httpHeadersJson ); } @@ -188,7 +200,8 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat Engine engine, Evaluable eval, String description, - boolean cacheResponses + boolean cacheResponses, + JSONArray httpHeadersJson ) throws JSONException { super(description); _project = project; @@ -217,13 +230,13 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat result = null; } - if (result == null) { - // the load method should not return any null value - throw new Exception("null result returned by fetch"); - } + if (result == null) { + // the load method should not return any null value + throw new Exception("null result returned by fetch"); + } return result; } - }); + }); } } @@ -324,7 +337,19 @@ public class ColumnAdditionByFetchingURLsOperation extends EngineDependentOperat try { URLConnection urlConnection = url.openConnection(); -// urlConnection.setRequestProperty(_headerKey, _headerValue); + if (_httpHeadersJson != null) { + Map httpHeaders = new HashMap<>(); + for (int i = 0; i < _httpHeadersJson.length(); i++) { + String headerLabel = _httpHeadersJson.getJSONObject(i).getString("name"); + String headerValue = _httpHeadersJson.getJSONObject(i).getString("value"); + httpHeaders.put(headerLabel, headerValue); + } + for (String headerLabel : HttpHeadersSupport.getHttpHeaderLabels()) { + HttpHeaderInfo info = HttpHeadersSupport.getHttpHeaderInfo(headerLabel); + + urlConnection.setRequestProperty(info.header, httpHeaders.get(headerLabel)); + } + } try { InputStream is = urlConnection.getInputStream(); diff --git a/main/src/com/google/refine/operations/column/ColumnSplitOperation.java b/main/src/com/google/refine/operations/column/ColumnSplitOperation.java index 63f2787af..553673798 100644 --- a/main/src/com/google/refine/operations/column/ColumnSplitOperation.java +++ b/main/src/com/google/refine/operations/column/ColumnSplitOperation.java @@ -39,7 +39,7 @@ import java.util.List; import java.util.Properties; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/operations/recon/ExtendDataOperation.java b/main/src/com/google/refine/operations/recon/ExtendDataOperation.java index 9771e52d5..3e16c9847 100644 --- a/main/src/com/google/refine/operations/recon/ExtendDataOperation.java +++ b/main/src/com/google/refine/operations/recon/ExtendDataOperation.java @@ -41,7 +41,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/operations/recon/ReconCopyAcrossColumnsOperation.java b/main/src/com/google/refine/operations/recon/ReconCopyAcrossColumnsOperation.java index 9c02e6ad7..6ad392b3e 100644 --- a/main/src/com/google/refine/operations/recon/ReconCopyAcrossColumnsOperation.java +++ b/main/src/com/google/refine/operations/recon/ReconCopyAcrossColumnsOperation.java @@ -41,7 +41,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; diff --git a/main/src/com/google/refine/preference/PreferenceStore.java b/main/src/com/google/refine/preference/PreferenceStore.java index 314bfefbf..e44656c6d 100644 --- a/main/src/com/google/refine/preference/PreferenceStore.java +++ b/main/src/com/google/refine/preference/PreferenceStore.java @@ -50,6 +50,9 @@ import com.google.refine.RefineServlet; public class PreferenceStore implements Jsonizable { public static final String USER_METADATA_KEY = "userMetadata"; + // use to populate "creator" filed in metadata. https://github.com/OpenRefine/OpenRefine/issues/1393 + public static final String USER_NAME = "username"; + private boolean dirty = false; protected Map _prefs = new HashMap(); diff --git a/main/src/com/google/refine/util/JSONUtilities.java b/main/src/com/google/refine/util/JSONUtilities.java index 0ac6f66b2..87e4c321c 100644 --- a/main/src/com/google/refine/util/JSONUtilities.java +++ b/main/src/com/google/refine/util/JSONUtilities.java @@ -37,6 +37,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -125,6 +126,10 @@ public class JSONUtilities { } } + static public JSONArray arrayToJSONArray(String[] array) { + return new JSONArray(Arrays.asList(array)); + } + static public int[] getIntArray(JSONObject obj, String key) { try { JSONArray a = obj.getJSONArray(key); @@ -276,6 +281,15 @@ public class JSONUtilities { } } + static public void safeInc(JSONObject obj, String key) { + try { + int currentValue = obj.getInt(key); + safePut(obj, key, currentValue + 1); + } catch (JSONException e) { + e.printStackTrace(); + } + } + static public void safePut(JSONObject obj, String key, long value) { try { obj.put(key, value); @@ -353,4 +367,11 @@ public class JSONUtilities { return list; } + + static public void concatArray(JSONArray destArray, JSONArray srcArray) + throws JSONException { + for (int i = 0; i < srcArray.length(); i++) { + destArray.put(srcArray.get(i)); + } + } } diff --git a/main/src/com/google/refine/util/JSObject.java b/main/src/com/google/refine/util/JSObject.java index 06bb8bd56..18d22fef1 100644 --- a/main/src/com/google/refine/util/JSObject.java +++ b/main/src/com/google/refine/util/JSObject.java @@ -39,7 +39,7 @@ import java.util.Enumeration; import java.util.Iterator; import java.util.Properties; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -157,7 +157,7 @@ public class JSObject extends Properties { writeJSONObject(writer, (JSONObject) o); } else { - writer.print("\"" + StringEscapeUtils.escapeJavaScript(o.toString()) + "\""); + writer.print("\"" + StringEscapeUtils.escapeEcmaScript(o.toString()) + "\""); } } } diff --git a/main/src/com/google/refine/util/ParsingUtilities.java b/main/src/com/google/refine/util/ParsingUtilities.java index a4f56e445..b2f107a21 100644 --- a/main/src/com/google/refine/util/ParsingUtilities.java +++ b/main/src/com/google/refine/util/ParsingUtilities.java @@ -38,11 +38,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; -import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Properties; @@ -56,16 +56,8 @@ import org.json.JSONObject; import org.json.JSONTokener; public class ParsingUtilities { - - static final private ThreadLocal ISO8601_FORMAT = new ThreadLocal() { - - @Override - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - } - - }; - + public static final DateTimeFormatter ISO8601 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + static public Properties parseUrlParameters(HttpServletRequest request) { Properties options = new Properties(); @@ -177,29 +169,39 @@ public class ParsingUtilities { * @return string with ISO_LOCAL_DATE_TIME formatted date & time */ static public String dateToString(OffsetDateTime d) { - return d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + return d.format(ISO8601); } static public String localDateToString(LocalDateTime d) { - return d.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + OffsetDateTime odt = OffsetDateTime.of(d, + OffsetDateTime.now().getOffset()); + + return odt.withOffsetSameInstant(ZoneOffset.of("Z")).format(ISO8601); } /** * Parse an ISO_LOCAL_DATE_TIME formatted string into a Java Date. - * + * For backward compatibility, to support the version <= 2.8, cannot use the DateTimeFormatter.ISO_OFFSET_DATE_TIME. Instead, use the ISO8601 below format: + * yyyy-MM-dd'T'HH:mm:ss'Z' + * * @param s the string to be parsed * @return LocalDateTime or null if the parse failed */ static public OffsetDateTime stringToDate(String s) { - return OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + Instant instant = Instant.parse(s); + return OffsetDateTime.ofInstant(instant, ZoneId.of("Z")); } static public LocalDateTime stringToLocalDate(String s) { - if (s.endsWith("Z")) { // UTC time - Instant instant = Instant.parse(s); - return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - } else { - return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } + Instant instant = Instant.parse(s); + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + static public String instantToString(Instant instant) { + return OffsetDateTime.ofInstant(instant, ZoneId.of("Z")).format(ISO8601); + } + + static public String instantToLocalDateTimeString(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).format(ISO8601); } } diff --git a/main/src/validator-resource-bundle.properties b/main/src/validator-resource-bundle.properties new file mode 100644 index 000000000..ca059b67d --- /dev/null +++ b/main/src/validator-resource-bundle.properties @@ -0,0 +1,13 @@ +schema-error=Table Schema error: ${error_message} +non-matching-header=Header in column ${column_number} doesn't match field name ${field_name} in the schema +extra-header=There is an extra header in column ${column_number} +missing-header=There is a missing header in column ${column_number} +type-or-format-error=The value ${value} in row ${row_number} and column ${column_number} is not type ${field_type} and format ${field_format} +required-constraint=Column ${column_number} is a required field, but row ${row_number} has no value +pattern-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the pattern constraint of ${constraint} +unique-constraint=Rows ${row_numbers} has unique constraint violation in column ${column_number} +enumerable-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the given enumeration: ${constraint} +minimum-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the minimum constraint of ${constraint} +maximum-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the maximum constraint of ${constraint} +minimum-length-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the minimum length constraint of ${constraint} +maximum-length-constraint=The value ${value} in row ${row_number} and column ${column_number} does not conform to the maximum length constraint of ${constraint} diff --git a/main/tests/data/datapackage-sample.json b/main/tests/data/datapackage-sample.json new file mode 100644 index 000000000..6ed88f3e7 --- /dev/null +++ b/main/tests/data/datapackage-sample.json @@ -0,0 +1,44 @@ +{ + "image": "http://assets.okfn.org/p/opendatahandbook/img/data-wrench.png", + "license": "PDDL-1.0", + "last_updated": "2011-09-21", + "keywords": [ + "GDP", + "World", + "Gross Domestic Product", + "Time series" + ], + "sources": [{ + "web": "http://data.worldbank.org/indicator/NY.GDP.MKTP.CD", + "name": "World Bank and OECD", + "title": "World Bank and OECD" + }], + "name": "gdp", + "description": "Country, regional and world GDP in current US Dollars ($). Regional means collections of countries e.g. Europe & Central Asia. Data is sourced from the World Bank and turned into a standard normalized CSV.", + "resources": [{ + "schema": {"fields": [ + { + "name": "Country Name", + "type": "string" + }, + { + "name": "Country Code", + "type": "string", + "foreignkey": "iso-3-geo-codes/id" + }, + { + "name": "Year", + "type": "year" + }, + { + "name": "Value", + "description": "GDP in current USD", + "type": "number" + } + ]}, + "path": "data/gdp.csv", + "name": "gdp" + }], + "title": "Country, Regional and World GDP (Gross Domestic Product)", + "version": "2011" +} \ No newline at end of file diff --git a/main/tests/data/gdp.csv b/main/tests/data/gdp.csv new file mode 100644 index 000000000..da7de60ba --- /dev/null +++ b/main/tests/data/gdp.csv @@ -0,0 +1,11543 @@ +Country Name,Country Code,Year,Value +Arab World,ARB,1968,25760683041.0826 +Arab World,ARB,1969,28434203615.4795 +Arab World,ARB,1970,31385499664.0635 +Arab World,ARB,1971,36426909888.3884 +Arab World,ARB,1972,43316056615.451 +Arab World,ARB,1973,55018394945.576 +Arab World,ARB,1974,105145803084.364 +Arab World,ARB,1975,116337021938.327 +Arab World,ARB,1976,144846175400.471 +Arab World,ARB,1977,167308327683.573 +Arab World,ARB,1978,183555544342.167 +Arab World,ARB,1979,248646206366.973 +Arab World,ARB,1980,338177454717.499 +Arab World,ARB,1981,348592795413.075 +Arab World,ARB,1982,324328753765.261 +Arab World,ARB,1983,303962539723.043 +Arab World,ARB,1984,307940771858.505 +Arab World,ARB,1985,303893618415.505 +Arab World,ARB,1986,289029150639.407 +Arab World,ARB,1987,312681678329.895 +Arab World,ARB,1988,307503035642.479 +Arab World,ARB,1989,322325140494.687 +Arab World,ARB,1990,446877161735.167 +Arab World,ARB,1991,439779177858.364 +Arab World,ARB,1992,471163515480.256 +Arab World,ARB,1993,476513630630.268 +Arab World,ARB,1994,487526906280.526 +Arab World,ARB,1995,523759898288.293 +Arab World,ARB,1996,578231297763.478 +Arab World,ARB,1997,613279452474.103 +Arab World,ARB,1998,591525594021.399 +Arab World,ARB,1999,643889670873.196 +Arab World,ARB,2000,735025140336.255 +Arab World,ARB,2001,723282816382.445 +Arab World,ARB,2002,729051715403.984 +Arab World,ARB,2003,823110541431.167 +Arab World,ARB,2004,963862340520.581 +Arab World,ARB,2005,1184661549595.13 +Arab World,ARB,2006,1404113530800.68 +Arab World,ARB,2007,1637573196970.62 +Arab World,ARB,2008,2077706582271.06 +Arab World,ARB,2009,1795472295052.32 +Arab World,ARB,2010,2109224213446 +Arab World,ARB,2011,2501942770389.97 +Arab World,ARB,2012,2740536188974.17 +Arab World,ARB,2013,2838294388723.79 +Arab World,ARB,2014,2904404570076.29 +Arab World,ARB,2015,2561129553776.39 +Arab World,ARB,2016,2500931669252.23 +Caribbean small states,CSS,1960,1922640775.37204 +Caribbean small states,CSS,1961,2080830143.04221 +Caribbean small states,CSS,1962,2195684581.07261 +Caribbean small states,CSS,1963,2331959438.21902 +Caribbean small states,CSS,1964,2519261152.97369 +Caribbean small states,CSS,1965,2712715412.87482 +Caribbean small states,CSS,1966,2942141442.47693 +Caribbean small states,CSS,1967,3158211274.70685 +Caribbean small states,CSS,1968,3140469906.3566 +Caribbean small states,CSS,1969,3417668655.56627 +Caribbean small states,CSS,1970,3760199658.87297 +Caribbean small states,CSS,1971,4088013918.53147 +Caribbean small states,CSS,1972,4732543297.55318 +Caribbean small states,CSS,1973,5169932102.82734 +Caribbean small states,CSS,1974,6720764011.57956 +Caribbean small states,CSS,1975,7864013356.29591 +Caribbean small states,CSS,1976,8070017845.29298 +Caribbean small states,CSS,1977,9382602731.01335 +Caribbean small states,CSS,1978,9571657513.65075 +Caribbean small states,CSS,1979,10966585159.6649 +Caribbean small states,CSS,1980,13453485464.7329 +Caribbean small states,CSS,1981,14842851376.6236 +Caribbean small states,CSS,1982,16495461075.5827 +Caribbean small states,CSS,1983,16712010846.6962 +Caribbean small states,CSS,1984,15923806275.1117 +Caribbean small states,CSS,1985,15742679785.309 +Caribbean small states,CSS,1986,14358714160.1057 +Caribbean small states,CSS,1987,15429015788.2553 +Caribbean small states,CSS,1988,16357329644.5497 +Caribbean small states,CSS,1989,16756727132.51 +Caribbean small states,CSS,1990,17873417057.5917 +Caribbean small states,CSS,1991,17682124581.2842 +Caribbean small states,CSS,1992,17403218746.3421 +Caribbean small states,CSS,1993,18271706185.3177 +Caribbean small states,CSS,1994,19289595454.1442 +Caribbean small states,CSS,1995,21134882108.6249 +Caribbean small states,CSS,1996,23041766083.4574 +Caribbean small states,CSS,1997,25732062006.8942 +Caribbean small states,CSS,1998,28249810016.98 +Caribbean small states,CSS,1999,30022260633.4195 +Caribbean small states,CSS,2000,32252030400.7358 +Caribbean small states,CSS,2001,33172149746.0103 +Caribbean small states,CSS,2002,34944596086.0711 +Caribbean small states,CSS,2003,37495408032.2944 +Caribbean small states,CSS,2004,41173138191.6472 +Caribbean small states,CSS,2005,46649069646.0953 +Caribbean small states,CSS,2006,52393433350.716 +Caribbean small states,CSS,2007,58257681979.3301 +Caribbean small states,CSS,2008,66480563576.5929 +Caribbean small states,CSS,2009,55930530970.8562 +Caribbean small states,CSS,2010,60772694299.858 +Caribbean small states,CSS,2011,65739442150.8813 +Caribbean small states,CSS,2012,67860562821.0636 +Caribbean small states,CSS,2013,68729777391.3784 +Caribbean small states,CSS,2014,68744227741.6518 +Caribbean small states,CSS,2015,66799701061.8327 +Caribbean small states,CSS,2016,63540193020.0893 +Central Europe and the Baltics,CEB,1990,256113714527.618 +Central Europe and the Baltics,CEB,1991,242631767248.709 +Central Europe and the Baltics,CEB,1992,259823093659.716 +Central Europe and the Baltics,CEB,1993,273915105741.891 +Central Europe and the Baltics,CEB,1994,310702556423.306 +Central Europe and the Baltics,CEB,1995,386114777052.74 +Central Europe and the Baltics,CEB,1996,412629652592.241 +Central Europe and the Baltics,CEB,1997,408895193163.935 +Central Europe and the Baltics,CEB,1998,447448444539.62 +Central Europe and the Baltics,CEB,1999,433914932035.509 +Central Europe and the Baltics,CEB,2000,427550058634.753 +Central Europe and the Baltics,CEB,2001,468161745109.393 +Central Europe and the Baltics,CEB,2002,527146030956.314 +Central Europe and the Baltics,CEB,2003,634284039973.081 +Central Europe and the Baltics,CEB,2004,762451562237.799 +Central Europe and the Baltics,CEB,2005,885747879466.35 +Central Europe and the Baltics,CEB,2006,1001942855597.53 +Central Europe and the Baltics,CEB,2007,1260957147846.53 +Central Europe and the Baltics,CEB,2008,1523196377519.86 +Central Europe and the Baltics,CEB,2009,1279854250277.72 +Central Europe and the Baltics,CEB,2010,1312750149554.56 +Central Europe and the Baltics,CEB,2011,1446220607413.34 +Central Europe and the Baltics,CEB,2012,1350778814230.21 +Central Europe and the Baltics,CEB,2013,1421351670726.71 +Central Europe and the Baltics,CEB,2014,1461972726743.52 +Central Europe and the Baltics,CEB,2015,1281539342884.75 +Central Europe and the Baltics,CEB,2016,1303382786630.62 +Early-demographic dividend,EAR,1960,152222021503.101 +Early-demographic dividend,EAR,1961,153174173789.089 +Early-demographic dividend,EAR,1962,158795369827.794 +Early-demographic dividend,EAR,1963,167975897835.712 +Early-demographic dividend,EAR,1964,192426156455.164 +Early-demographic dividend,EAR,1965,209993963772.447 +Early-demographic dividend,EAR,1966,207316686431.051 +Early-demographic dividend,EAR,1967,217587076275.313 +Early-demographic dividend,EAR,1968,235705718659.736 +Early-demographic dividend,EAR,1969,263417168820.022 +Early-demographic dividend,EAR,1970,278414463364.508 +Early-demographic dividend,EAR,1971,300750846441.857 +Early-demographic dividend,EAR,1972,330264782812.664 +Early-demographic dividend,EAR,1973,422538233529.836 +Early-demographic dividend,EAR,1974,592504417822.073 +Early-demographic dividend,EAR,1975,636292930399.372 +Early-demographic dividend,EAR,1976,694401920707.909 +Early-demographic dividend,EAR,1977,777069860599.993 +Early-demographic dividend,EAR,1978,860276042756.762 +Early-demographic dividend,EAR,1979,1045208440796.38 +Early-demographic dividend,EAR,1980,1289757085834.86 +Early-demographic dividend,EAR,1981,1441144663708.07 +Early-demographic dividend,EAR,1982,1371908131842.89 +Early-demographic dividend,EAR,1983,1385300584420.83 +Early-demographic dividend,EAR,1984,1365815981597.72 +Early-demographic dividend,EAR,1985,1397858695204.75 +Early-demographic dividend,EAR,1986,1418211765048.27 +Early-demographic dividend,EAR,1987,1429315977542.63 +Early-demographic dividend,EAR,1988,1535871536563.05 +Early-demographic dividend,EAR,1989,1568562659306.28 +Early-demographic dividend,EAR,1990,1806505172134.77 +Early-demographic dividend,EAR,1991,1912921517302.44 +Early-demographic dividend,EAR,1992,2098149661879.33 +Early-demographic dividend,EAR,1993,2302453002964.44 +Early-demographic dividend,EAR,1994,2408912031539.9 +Early-demographic dividend,EAR,1995,2468961701917.03 +Early-demographic dividend,EAR,1996,2694453298131.29 +Early-demographic dividend,EAR,1997,2878903970242.92 +Early-demographic dividend,EAR,1998,2839864195429.24 +Early-demographic dividend,EAR,1999,3010697292749.06 +Early-demographic dividend,EAR,2000,3265108230891.93 +Early-demographic dividend,EAR,2001,3224441922892.92 +Early-demographic dividend,EAR,2002,3131166808367.24 +Early-demographic dividend,EAR,2003,3500675725433.46 +Early-demographic dividend,EAR,2004,4066546525118.83 +Early-demographic dividend,EAR,2005,4742611342049.54 +Early-demographic dividend,EAR,2006,5428145324754.82 +Early-demographic dividend,EAR,2007,6446032068048.83 +Early-demographic dividend,EAR,2008,7272012608284.4 +Early-demographic dividend,EAR,2009,6990930969841.42 +Early-demographic dividend,EAR,2010,8490184886018.92 +Early-demographic dividend,EAR,2011,9567687630587.59 +Early-demographic dividend,EAR,2012,9956217405201.38 +Early-demographic dividend,EAR,2013,10199668619019.7 +Early-demographic dividend,EAR,2014,10514807961576.6 +Early-demographic dividend,EAR,2015,10225383730055.9 +Early-demographic dividend,EAR,2016,10378321855929.9 +East Asia & Pacific,EAS,1960,153406062445.133 +East Asia & Pacific,EAS,1961,153851906489.076 +East Asia & Pacific,EAS,1962,157422860957.598 +East Asia & Pacific,EAS,1963,175666653928.707 +East Asia & Pacific,EAS,1964,201828758065.12 +East Asia & Pacific,EAS,1965,224795305298.806 +East Asia & Pacific,EAS,1966,251316895422.914 +East Asia & Pacific,EAS,1967,272311886780.484 +East Asia & Pacific,EAS,1968,300037540836.51 +East Asia & Pacific,EAS,1969,345620520780.581 +East Asia & Pacific,EAS,1970,406892268832.737 +East Asia & Pacific,EAS,1971,451555305929.632 +East Asia & Pacific,EAS,1972,560418537433.307 +East Asia & Pacific,EAS,1973,738629607846.873 +East Asia & Pacific,EAS,1974,850486001086.155 +East Asia & Pacific,EAS,1975,931183199853.372 +East Asia & Pacific,EAS,1976,1023624295308.86 +East Asia & Pacific,EAS,1977,1220099418673.47 +East Asia & Pacific,EAS,1978,1539413405469.08 +East Asia & Pacific,EAS,1979,1671803883927.95 +East Asia & Pacific,EAS,1980,1806920411252.29 +East Asia & Pacific,EAS,1981,1994776943312.9 +East Asia & Pacific,EAS,1982,1953473277429.83 +East Asia & Pacific,EAS,1983,2073066401462.35 +East Asia & Pacific,EAS,1984,2223992929693.57 +East Asia & Pacific,EAS,1985,2358172144946.55 +East Asia & Pacific,EAS,1986,3076109459284.79 +East Asia & Pacific,EAS,1987,3598365917263.22 +East Asia & Pacific,EAS,1988,4330848600314.32 +East Asia & Pacific,EAS,1989,4529251580451.43 +East Asia & Pacific,EAS,1990,4740125610961.6 +East Asia & Pacific,EAS,1991,5337495034346.36 +East Asia & Pacific,EAS,1992,5827466927803.02 +East Asia & Pacific,EAS,1993,6542241886332.86 +East Asia & Pacific,EAS,1994,7302165972072.7 +East Asia & Pacific,EAS,1995,8297217618291.42 +East Asia & Pacific,EAS,1996,7993854682092.77 +East Asia & Pacific,EAS,1997,7649008381580.38 +East Asia & Pacific,EAS,1998,6857782846050.01 +East Asia & Pacific,EAS,1999,7656256420658.01 +East Asia & Pacific,EAS,2000,8283672555565.95 +East Asia & Pacific,EAS,2001,7705159083109.51 +East Asia & Pacific,EAS,2002,7827298972846.93 +East Asia & Pacific,EAS,2003,8605351856004.54 +East Asia & Pacific,EAS,2004,9651082222195.5 +East Asia & Pacific,EAS,2005,10292825485348.7 +East Asia & Pacific,EAS,2006,10913330711044.6 +East Asia & Pacific,EAS,2007,12205786067031.7 +East Asia & Pacific,EAS,2008,14097329803496.3 +East Asia & Pacific,EAS,2009,14526049741278.9 +East Asia & Pacific,EAS,2010,16934259897815.9 +East Asia & Pacific,EAS,2011,19633645544446.8 +East Asia & Pacific,EAS,2012,21013112141456.7 +East Asia & Pacific,EAS,2013,21256469783768.7 +East Asia & Pacific,EAS,2014,21894085979174.9 +East Asia & Pacific,EAS,2015,21765291253881.4 +East Asia & Pacific,EAS,2016,22477425046152.2 +East Asia & Pacific (excluding high income),EAP,1960,80259949708.256 +East Asia & Pacific (excluding high income),EAP,1961,70454320784.1637 +East Asia & Pacific (excluding high income),EAP,1962,64561374187.0283 +East Asia & Pacific (excluding high income),EAP,1963,69911007473.5442 +East Asia & Pacific (excluding high income),EAP,1964,81054694801.5609 +East Asia & Pacific (excluding high income),EAP,1965,94592399250.3341 +East Asia & Pacific (excluding high income),EAP,1966,103552673128.584 +East Asia & Pacific (excluding high income),EAP,1967,100339273081.733 +East Asia & Pacific (excluding high income),EAP,1968,101275421779.252 +East Asia & Pacific (excluding high income),EAP,1969,113721619713.104 +East Asia & Pacific (excluding high income),EAP,1970,126769627751.232 +East Asia & Pacific (excluding high income),EAP,1971,136328570880.601 +East Asia & Pacific (excluding high income),EAP,1972,154659093609.397 +East Asia & Pacific (excluding high income),EAP,1973,194723705741.74 +East Asia & Pacific (excluding high income),EAP,1974,219684982687.941 +East Asia & Pacific (excluding high income),EAP,1975,246972833579.966 +East Asia & Pacific (excluding high income),EAP,1976,250679070877.618 +East Asia & Pacific (excluding high income),EAP,1977,289679258049.107 +East Asia & Pacific (excluding high income),EAP,1978,280440164773.063 +East Asia & Pacific (excluding high income),EAP,1979,324841171790.876 +East Asia & Pacific (excluding high income),EAP,1980,374815806190.863 +East Asia & Pacific (excluding high income),EAP,1981,399965066634.494 +East Asia & Pacific (excluding high income),EAP,1982,419766288759.248 +East Asia & Pacific (excluding high income),EAP,1983,440469714741.303 +East Asia & Pacific (excluding high income),EAP,1984,478158198678.127 +East Asia & Pacific (excluding high income),EAP,1985,524014062044.045 +East Asia & Pacific (excluding high income),EAP,1986,522534774843.578 +East Asia & Pacific (excluding high income),EAP,1987,516086961052.224 +East Asia & Pacific (excluding high income),EAP,1988,572406468163.312 +East Asia & Pacific (excluding high income),EAP,1989,618643709376.269 +East Asia & Pacific (excluding high income),EAP,1990,663045725610.49 +East Asia & Pacific (excluding high income),EAP,1991,719584447233.343 +East Asia & Pacific (excluding high income),EAP,1992,806358557827.083 +East Asia & Pacific (excluding high income),EAP,1993,885238781446.321 +East Asia & Pacific (excluding high income),EAP,1994,1064868940113.18 +East Asia & Pacific (excluding high income),EAP,1995,1314307625271.26 +East Asia & Pacific (excluding high income),EAP,1996,1509620351019.71 +East Asia & Pacific (excluding high income),EAP,1997,1563661684670.21 +East Asia & Pacific (excluding high income),EAP,1998,1433164072949.29 +East Asia & Pacific (excluding high income),EAP,1999,1576476091414.92 +East Asia & Pacific (excluding high income),EAP,2000,1737704956830.78 +East Asia & Pacific (excluding high income),EAP,2001,1848846696904.54 +East Asia & Pacific (excluding high income),EAP,2002,2046255878039.6 +East Asia & Pacific (excluding high income),EAP,2003,2315970352098 +East Asia & Pacific (excluding high income),EAP,2004,2685679579693.63 +East Asia & Pacific (excluding high income),EAP,2005,3105942817285.98 +East Asia & Pacific (excluding high income),EAP,2006,3737485223150.79 +East Asia & Pacific (excluding high income),EAP,2007,4727397727396.91 +East Asia & Pacific (excluding high income),EAP,2008,5984384402421.37 +East Asia & Pacific (excluding high income),EAP,2009,6493261436292.97 +East Asia & Pacific (excluding high income),EAP,2010,7875013124582.93 +East Asia & Pacific (excluding high income),EAP,2011,9625870808891.42 +East Asia & Pacific (excluding high income),EAP,2012,10738363479428.9 +East Asia & Pacific (excluding high income),EAP,2013,11854913554643.5 +East Asia & Pacific (excluding high income),EAP,2014,12748307082155.9 +East Asia & Pacific (excluding high income),EAP,2015,13267645915189.5 +East Asia & Pacific (excluding high income),EAP,2016,13511178026303.6 +East Asia & Pacific (IDA & IBRD countries),TEA,1960,80087763368.4641 +East Asia & Pacific (IDA & IBRD countries),TEA,1961,70303171030.6136 +East Asia & Pacific (IDA & IBRD countries),TEA,1962,64422866914.6763 +East Asia & Pacific (IDA & IBRD countries),TEA,1963,69761023321.6503 +East Asia & Pacific (IDA & IBRD countries),TEA,1964,80880803448.8805 +East Asia & Pacific (IDA & IBRD countries),TEA,1965,94389464672.6492 +East Asia & Pacific (IDA & IBRD countries),TEA,1966,103330515554.021 +East Asia & Pacific (IDA & IBRD countries),TEA,1967,100124009401.253 +East Asia & Pacific (IDA & IBRD countries),TEA,1968,101058149724.505 +East Asia & Pacific (IDA & IBRD countries),TEA,1969,113477646105.785 +East Asia & Pacific (IDA & IBRD countries),TEA,1970,126497661493.109 +East Asia & Pacific (IDA & IBRD countries),TEA,1971,136036097265.624 +East Asia & Pacific (IDA & IBRD countries),TEA,1972,154327294457.505 +East Asia & Pacific (IDA & IBRD countries),TEA,1973,194305953646.402 +East Asia & Pacific (IDA & IBRD countries),TEA,1974,219213679712.873 +East Asia & Pacific (IDA & IBRD countries),TEA,1975,246442988390.718 +East Asia & Pacific (IDA & IBRD countries),TEA,1976,250141274481.86 +East Asia & Pacific (IDA & IBRD countries),TEA,1977,289057792282.702 +East Asia & Pacific (IDA & IBRD countries),TEA,1978,279838520171.011 +East Asia & Pacific (IDA & IBRD countries),TEA,1979,324144271125.13 +East Asia & Pacific (IDA & IBRD countries),TEA,1980,374011692034.316 +East Asia & Pacific (IDA & IBRD countries),TEA,1981,399106998306.283 +East Asia & Pacific (IDA & IBRD countries),TEA,1982,418865739717.139 +East Asia & Pacific (IDA & IBRD countries),TEA,1983,439524749434.869 +East Asia & Pacific (IDA & IBRD countries),TEA,1984,477132378074.312 +East Asia & Pacific (IDA & IBRD countries),TEA,1985,522889864188.566 +East Asia & Pacific (IDA & IBRD countries),TEA,1986,521413750588.998 +East Asia & Pacific (IDA & IBRD countries),TEA,1987,514979769667.717 +East Asia & Pacific (IDA & IBRD countries),TEA,1988,571178451263.419 +East Asia & Pacific (IDA & IBRD countries),TEA,1989,617316497032.628 +East Asia & Pacific (IDA & IBRD countries),TEA,1990,661623255038.025 +East Asia & Pacific (IDA & IBRD countries),TEA,1991,718040680851.845 +East Asia & Pacific (IDA & IBRD countries),TEA,1992,804628629897.439 +East Asia & Pacific (IDA & IBRD countries),TEA,1993,883339627183.539 +East Asia & Pacific (IDA & IBRD countries),TEA,1994,1062584414819.77 +East Asia & Pacific (IDA & IBRD countries),TEA,1995,1311487964653.74 +East Asia & Pacific (IDA & IBRD countries),TEA,1996,1506381674647.97 +East Asia & Pacific (IDA & IBRD countries),TEA,1997,1560307070281.15 +East Asia & Pacific (IDA & IBRD countries),TEA,1998,1430089422679.27 +East Asia & Pacific (IDA & IBRD countries),TEA,1999,1573093985533.5 +East Asia & Pacific (IDA & IBRD countries),TEA,2000,1733976957283.78 +East Asia & Pacific (IDA & IBRD countries),TEA,2001,1844880258516.11 +East Asia & Pacific (IDA & IBRD countries),TEA,2002,2041865926249.22 +East Asia & Pacific (IDA & IBRD countries),TEA,2003,2311056516099.72 +East Asia & Pacific (IDA & IBRD countries),TEA,2004,2680080452885.16 +East Asia & Pacific (IDA & IBRD countries),TEA,2005,3099556641923.49 +East Asia & Pacific (IDA & IBRD countries),TEA,2006,3729909801279.21 +East Asia & Pacific (IDA & IBRD countries),TEA,2007,4717923246206.8 +East Asia & Pacific (IDA & IBRD countries),TEA,2008,5972485978440.58 +East Asia & Pacific (IDA & IBRD countries),TEA,2009,6480284114182.45 +East Asia & Pacific (IDA & IBRD countries),TEA,2010,7859520533899.79 +East Asia & Pacific (IDA & IBRD countries),TEA,2011,9607063801205.78 +East Asia & Pacific (IDA & IBRD countries),TEA,2012,10717379224740.4 +East Asia & Pacific (IDA & IBRD countries),TEA,2013,11831819367549.7 +East Asia & Pacific (IDA & IBRD countries),TEA,2014,12723521658445.1 +East Asia & Pacific (IDA & IBRD countries),TEA,2015,13241873807583.3 +East Asia & Pacific (IDA & IBRD countries),TEA,2016,13484932862978.8 +Euro area,EMU,1960,245389523535.21 +Euro area,EMU,1961,270108736771.728 +Euro area,EMU,1962,299749528685.376 +Euro area,EMU,1963,336271770003.717 +Euro area,EMU,1964,374010191933.27 +Euro area,EMU,1965,408119028178.097 +Euro area,EMU,1966,445040235612.184 +Euro area,EMU,1967,483441345246.615 +Euro area,EMU,1968,518263505475.995 +Euro area,EMU,1969,572532268216.495 +Euro area,EMU,1970,641460637395.971 +Euro area,EMU,1971,727045243334.751 +Euro area,EMU,1972,878371965947.911 +Euro area,EMU,1973,1140215331077.93 +Euro area,EMU,1974,1292913189870.35 +Euro area,EMU,1975,1499292682301.1 +Euro area,EMU,1976,1564518129278.76 +Euro area,EMU,1977,1779460900157.27 +Euro area,EMU,1978,2179044584488.07 +Euro area,EMU,1979,2639765077757.45 +Euro area,EMU,1980,2956735717812.24 +Euro area,EMU,1981,2569819070205.93 +Euro area,EMU,1982,2487970498872.87 +Euro area,EMU,1983,2427189312535.1 +Euro area,EMU,1984,2328259732922.51 +Euro area,EMU,1985,2392054029846.51 +Euro area,EMU,1986,3357238823395.49 +Euro area,EMU,1987,4152743228203.74 +Euro area,EMU,1988,4567318804504.65 +Euro area,EMU,1989,4665607905988.66 +Euro area,EMU,1990,5871693989817.08 +Euro area,EMU,1991,6104530124284.34 +Euro area,EMU,1992,6734901870233.99 +Euro area,EMU,1993,6168663044953.83 +Euro area,EMU,1994,6515506760178.52 +Euro area,EMU,1995,7520106864481.86 +Euro area,EMU,1996,7611726452029.66 +Euro area,EMU,1997,6959484673611.01 +Euro area,EMU,1998,7151028055866.01 +Euro area,EMU,1999,7119223020204.08 +Euro area,EMU,2000,6486948965622.98 +Euro area,EMU,2001,6593233923750.33 +Euro area,EMU,2002,7173603214645.71 +Euro area,EMU,2003,8850599071571.01 +Euro area,EMU,2004,10151814034876.7 +Euro area,EMU,2005,10535328496601.9 +Euro area,EMU,2006,11184406192078.3 +Euro area,EMU,2007,12877397931377.4 +Euro area,EMU,2008,14115366559756.8 +Euro area,EMU,2009,12905452856781.1 +Euro area,EMU,2010,12642935603562.1 +Euro area,EMU,2011,13622723195115.4 +Euro area,EMU,2012,12637588983379.4 +Euro area,EMU,2013,13187495064380.7 +Euro area,EMU,2014,13457720442907.5 +Euro area,EMU,2015,11616144612366.3 +Euro area,EMU,2016,11885664082849.1 +Europe & Central Asia,ECS,1960,432763685324.509 +Europe & Central Asia,ECS,1961,460668876592.491 +Europe & Central Asia,ECS,1962,503638735647.907 +Europe & Central Asia,ECS,1963,555368983066.662 +Europe & Central Asia,ECS,1964,615028489294.961 +Europe & Central Asia,ECS,1965,669257339022.057 +Europe & Central Asia,ECS,1966,727080705610.445 +Europe & Central Asia,ECS,1967,782273534888.326 +Europe & Central Asia,ECS,1968,815868539007.526 +Europe & Central Asia,ECS,1969,895741081122.038 +Europe & Central Asia,ECS,1970,1007609884842.09 +Europe & Central Asia,ECS,1971,1136379130354.99 +Europe & Central Asia,ECS,1972,1361730567491.87 +Europe & Central Asia,ECS,1973,1729501999343.51 +Europe & Central Asia,ECS,1974,1954079227218.94 +Europe & Central Asia,ECS,1975,2282879504237.62 +Europe & Central Asia,ECS,1976,2372796286718.28 +Europe & Central Asia,ECS,1977,2689034117521.4 +Europe & Central Asia,ECS,1978,3282122941201.44 +Europe & Central Asia,ECS,1979,4015364706047.03 +Europe & Central Asia,ECS,1980,4548343163223.59 +Europe & Central Asia,ECS,1981,4048271618267.42 +Europe & Central Asia,ECS,1982,3900963906539.6 +Europe & Central Asia,ECS,1983,3784300665660.48 +Europe & Central Asia,ECS,1984,3637246299961.42 +Europe & Central Asia,ECS,1985,3764269446867.66 +Europe & Central Asia,ECS,1986,5137673243594.37 +Europe & Central Asia,ECS,1987,6348859470242.99 +Europe & Central Asia,ECS,1988,7067497880251.27 +Europe & Central Asia,ECS,1989,7224451847727.19 +Europe & Central Asia,ECS,1990,8837798957217.56 +Europe & Central Asia,ECS,1991,9118254483722.14 +Europe & Central Asia,ECS,1992,9780068883431.27 +Europe & Central Asia,ECS,1993,8987910993879.63 +Europe & Central Asia,ECS,1994,9400417935058.64 +Europe & Central Asia,ECS,1995,10827903895370.2 +Europe & Central Asia,ECS,1996,11055076580949 +Europe & Central Asia,ECS,1997,10489769869606.2 +Europe & Central Asia,ECS,1998,10749669669893.6 +Europe & Central Asia,ECS,1999,10628573427395.1 +Europe & Central Asia,ECS,2000,10011304103592.3 +Europe & Central Asia,ECS,2001,10112904613806.8 +Europe & Central Asia,ECS,2002,11061551679319.4 +Europe & Central Asia,ECS,2003,13475936343306.8 +Europe & Central Asia,ECS,2004,15710079754567.4 +Europe & Central Asia,ECS,2005,16731221185836.3 +Europe & Central Asia,ECS,2006,18108863489937 +Europe & Central Asia,ECS,2007,21160525702403.2 +Europe & Central Asia,ECS,2008,23218568155419.7 +Europe & Central Asia,ECS,2009,20419024790312.5 +Europe & Central Asia,ECS,2010,20905471026131.4 +Europe & Central Asia,ECS,2011,23146206945497.4 +Europe & Central Asia,ECS,2012,22278308473166.2 +Europe & Central Asia,ECS,2013,23253753489878.5 +Europe & Central Asia,ECS,2014,23602629398500.7 +Europe & Central Asia,ECS,2015,20278407939904.2 +Europe & Central Asia,ECS,2016,20162858489508.1 +Europe & Central Asia (excluding high income),ECA,1989,903702469910.52 +Europe & Central Asia (excluding high income),ECA,1990,960438672723.367 +Europe & Central Asia (excluding high income),ECA,1991,926765119090.884 +Europe & Central Asia (excluding high income),ECA,1992,853278424758.789 +Europe & Central Asia (excluding high income),ECA,1993,838260559328.055 +Europe & Central Asia (excluding high income),ECA,1994,727687093069.718 +Europe & Central Asia (excluding high income),ECA,1995,776931366529.155 +Europe & Central Asia (excluding high income),ECA,1996,787851671889.667 +Europe & Central Asia (excluding high income),ECA,1997,820131784781.875 +Europe & Central Asia (excluding high income),ECA,1998,771871770861.159 +Europe & Central Asia (excluding high income),ECA,1999,650450056519.407 +Europe & Central Asia (excluding high income),ECA,2000,720049109208.474 +Europe & Central Asia (excluding high income),ECA,2001,717065633312.417 +Europe & Central Asia (excluding high income),ECA,2002,820427017878.696 +Europe & Central Asia (excluding high income),ECA,2003,1037515255499.23 +Europe & Central Asia (excluding high income),ECA,2004,1367586660092.33 +Europe & Central Asia (excluding high income),ECA,2005,1726468996323.24 +Europe & Central Asia (excluding high income),ECA,2006,2114341890534.89 +Europe & Central Asia (excluding high income),ECA,2007,2727633767388.62 +Europe & Central Asia (excluding high income),ECA,2008,3372528990767.71 +Europe & Central Asia (excluding high income),ECA,2009,2655132038891.56 +Europe & Central Asia (excluding high income),ECA,2010,3156252483058.18 +Europe & Central Asia (excluding high income),ECA,2011,3871963192958.71 +Europe & Central Asia (excluding high income),ECA,2012,4070879223563.86 +Europe & Central Asia (excluding high income),ECA,2013,4302720557394.26 +Europe & Central Asia (excluding high income),ECA,2014,4078799138447.73 +Europe & Central Asia (excluding high income),ECA,2015,3120102822297.1 +Europe & Central Asia (excluding high income),ECA,2016,2979118476324.24 +Europe & Central Asia (IDA & IBRD countries),TEC,1989,960328174371.057 +Europe & Central Asia (IDA & IBRD countries),TEC,1990,1020619449300.74 +Europe & Central Asia (IDA & IBRD countries),TEC,1991,1007801660268.39 +Europe & Central Asia (IDA & IBRD countries),TEC,1992,944312695547.955 +Europe & Central Asia (IDA & IBRD countries),TEC,1993,931235788654.179 +Europe & Central Asia (IDA & IBRD countries),TEC,1994,837243545767.996 +Europe & Central Asia (IDA & IBRD countries),TEC,1995,918930607816.217 +Europe & Central Asia (IDA & IBRD countries),TEC,1996,947704910874.353 +Europe & Central Asia (IDA & IBRD countries),TEC,1997,979132756086.026 +Europe & Central Asia (IDA & IBRD countries),TEC,1998,946228594056.486 +Europe & Central Asia (IDA & IBRD countries),TEC,1999,820213773405.425 +Europe & Central Asia (IDA & IBRD countries),TEC,2000,891934707791.111 +Europe & Central Asia (IDA & IBRD countries),TEC,2001,907586896655.439 +Europe & Central Asia (IDA & IBRD countries),TEC,2002,1019107655133.6 +Europe & Central Asia (IDA & IBRD countries),TEC,2003,1255033897823.74 +Europe & Central Asia (IDA & IBRD countries),TEC,2004,1622688912935.72 +Europe & Central Asia (IDA & IBRD countries),TEC,2005,2032603631916.98 +Europe & Central Asia (IDA & IBRD countries),TEC,2006,2459168320833.04 +Europe & Central Asia (IDA & IBRD countries),TEC,2007,3156883414983.23 +Europe & Central Asia (IDA & IBRD countries),TEC,2008,3906344780241.39 +Europe & Central Asia (IDA & IBRD countries),TEC,2009,3094928199271.03 +Europe & Central Asia (IDA & IBRD countries),TEC,2010,3635573611967.41 +Europe & Central Asia (IDA & IBRD countries),TEC,2011,4400783138334.08 +Europe & Central Asia (IDA & IBRD countries),TEC,2012,4571223504946.21 +Europe & Central Asia (IDA & IBRD countries),TEC,2013,4826935346702.26 +Europe & Central Asia (IDA & IBRD countries),TEC,2014,4623950916968.12 +Europe & Central Asia (IDA & IBRD countries),TEC,2015,3597439604363.69 +Europe & Central Asia (IDA & IBRD countries),TEC,2016,3448627156740.36 +European Union,EUU,1960,358939666615.47 +European Union,EUU,1961,390789544218.134 +European Union,EUU,1962,426908194104.211 +European Union,EUU,1963,470297095519.743 +European Union,EUU,1964,521197684985.034 +European Union,EUU,1965,567590518505.608 +European Union,EUU,1966,615283909698.108 +European Union,EUU,1967,661166979382.423 +European Union,EUU,1968,687213740362.304 +European Union,EUU,1969,754796434973.419 +European Union,EUU,1970,855234352054.266 +European Union,EUU,1971,967419657510.242 +European Union,EUU,1972,1158320102742.09 +European Union,EUU,1973,1470725772176.78 +European Union,EUU,1974,1652902769120.73 +European Union,EUU,1975,1926973312297.35 +European Union,EUU,1976,1995730917312.52 +European Union,EUU,1977,2260041856810.23 +European Union,EUU,1978,2769687171486.68 +European Union,EUU,1979,3382589603382.52 +European Union,EUU,1980,3860946303423.78 +European Union,EUU,1981,3416278760871.41 +European Union,EUU,1982,3287910701007.99 +European Union,EUU,1983,3186966976981.42 +European Union,EUU,1984,3059979571007.95 +European Union,EUU,1985,3162521224924.48 +European Union,EUU,1986,4336668252551.86 +European Union,EUU,1987,5365914989685.94 +European Union,EUU,1988,5985449350585.64 +European Union,EUU,1989,6111509859371.48 +European Union,EUU,1990,7578533627539.12 +European Union,EUU,1991,7865147971074.68 +European Union,EUU,1992,8570482000033.34 +European Union,EUU,1993,7814819013689.57 +European Union,EUU,1994,8298433649328.58 +European Union,EUU,1995,9610497311406.19 +European Union,EUU,1996,9824668960664.23 +European Union,EUU,1997,9273364401458.35 +European Union,EUU,1998,9589846268503.63 +European Union,EUU,1999,9576780993379.78 +European Union,EUU,2000,8899281080301.74 +European Union,EUU,2001,9000712583377.4 +European Union,EUU,2002,9811088515429.67 +European Union,EUU,2003,11945762088043.2 +European Union,EUU,2004,13795554923350 +European Union,EUU,2005,14426590936518.2 +European Union,EUU,2006,15388732742870.8 +European Union,EUU,2007,17781304404686.5 +European Union,EUU,2008,19117797795887.7 +European Union,EUU,2009,17080879375428 +European Union,EUU,2010,16977855795546.3 +European Union,EUU,2011,18340538607136.1 +European Union,EUU,2012,17271715977529.1 +European Union,EUU,2013,18002706275463.5 +European Union,EUU,2014,18588239232261 +European Union,EUU,2015,16334844026788 +European Union,EUU,2016,16397979816576.1 +Fragile and conflict affected situations,FCS,2000,212661618000.573 +Fragile and conflict affected situations,FCS,2001,196781933108.681 +Fragile and conflict affected situations,FCS,2002,190816468796.57 +Fragile and conflict affected situations,FCS,2003,217472506931.578 +Fragile and conflict affected situations,FCS,2004,247331719330.617 +Fragile and conflict affected situations,FCS,2005,300124282952.738 +Fragile and conflict affected situations,FCS,2006,351606441448.304 +Fragile and conflict affected situations,FCS,2007,435172555818.384 +Fragile and conflict affected situations,FCS,2008,561422238512.442 +Fragile and conflict affected situations,FCS,2009,523959040574.02 +Fragile and conflict affected situations,FCS,2010,624219448362.822 +Fragile and conflict affected situations,FCS,2011,676660621845.22 +Fragile and conflict affected situations,FCS,2012,730528738068.19 +Fragile and conflict affected situations,FCS,2013,781048126926.108 +Fragile and conflict affected situations,FCS,2014,821766296573.443 +Fragile and conflict affected situations,FCS,2015,750921360242.507 +Fragile and conflict affected situations,FCS,2016,736492620797.34 +Heavily indebted poor countries (HIPC),HPC,1960,17566642115.4168 +Heavily indebted poor countries (HIPC),HPC,1961,18033526303.1068 +Heavily indebted poor countries (HIPC),HPC,1962,19660722938.5933 +Heavily indebted poor countries (HIPC),HPC,1963,23797854828.0912 +Heavily indebted poor countries (HIPC),HPC,1964,21181173056.3962 +Heavily indebted poor countries (HIPC),HPC,1965,24791433536.9807 +Heavily indebted poor countries (HIPC),HPC,1966,27037002542.0683 +Heavily indebted poor countries (HIPC),HPC,1967,26469517421.1298 +Heavily indebted poor countries (HIPC),HPC,1968,27687706183.1679 +Heavily indebted poor countries (HIPC),HPC,1969,30902141055.3267 +Heavily indebted poor countries (HIPC),HPC,1970,32389167090.0469 +Heavily indebted poor countries (HIPC),HPC,1971,34920433870.4235 +Heavily indebted poor countries (HIPC),HPC,1972,37813701276.9785 +Heavily indebted poor countries (HIPC),HPC,1973,45584823246.267 +Heavily indebted poor countries (HIPC),HPC,1974,56142116082.6774 +Heavily indebted poor countries (HIPC),HPC,1975,63535307034.3364 +Heavily indebted poor countries (HIPC),HPC,1976,67559791246.943 +Heavily indebted poor countries (HIPC),HPC,1977,79909125894.0715 +Heavily indebted poor countries (HIPC),HPC,1978,90789385371.85 +Heavily indebted poor countries (HIPC),HPC,1979,101664820366.851 +Heavily indebted poor countries (HIPC),HPC,1980,106677929490.771 +Heavily indebted poor countries (HIPC),HPC,1981,106633807023.73 +Heavily indebted poor countries (HIPC),HPC,1982,106403185666.529 +Heavily indebted poor countries (HIPC),HPC,1983,100261185483.72 +Heavily indebted poor countries (HIPC),HPC,1984,100078815923.674 +Heavily indebted poor countries (HIPC),HPC,1985,104994491996.922 +Heavily indebted poor countries (HIPC),HPC,1986,120358284739.616 +Heavily indebted poor countries (HIPC),HPC,1987,132550820434.308 +Heavily indebted poor countries (HIPC),HPC,1988,132065171231.102 +Heavily indebted poor countries (HIPC),HPC,1989,127152957339.515 +Heavily indebted poor countries (HIPC),HPC,1990,129590021510.749 +Heavily indebted poor countries (HIPC),HPC,1991,133299444700.789 +Heavily indebted poor countries (HIPC),HPC,1992,121796046071.449 +Heavily indebted poor countries (HIPC),HPC,1993,123961926712.477 +Heavily indebted poor countries (HIPC),HPC,1994,110357996127.295 +Heavily indebted poor countries (HIPC),HPC,1995,126157292697.297 +Heavily indebted poor countries (HIPC),HPC,1996,132123113216.448 +Heavily indebted poor countries (HIPC),HPC,1997,138643799979.895 +Heavily indebted poor countries (HIPC),HPC,1998,142823908564.672 +Heavily indebted poor countries (HIPC),HPC,1999,143030868524.954 +Heavily indebted poor countries (HIPC),HPC,2000,155061199539.282 +Heavily indebted poor countries (HIPC),HPC,2001,147407334553.846 +Heavily indebted poor countries (HIPC),HPC,2002,160216529979.782 +Heavily indebted poor countries (HIPC),HPC,2003,182328184670.128 +Heavily indebted poor countries (HIPC),HPC,2004,209456626079.117 +Heavily indebted poor countries (HIPC),HPC,2005,242142895089.303 +Heavily indebted poor countries (HIPC),HPC,2006,289296463304.802 +Heavily indebted poor countries (HIPC),HPC,2007,344296287547.737 +Heavily indebted poor countries (HIPC),HPC,2008,414897655686.46 +Heavily indebted poor countries (HIPC),HPC,2009,421736724661.502 +Heavily indebted poor countries (HIPC),HPC,2010,463157748195.383 +Heavily indebted poor countries (HIPC),HPC,2011,518080704387.585 +Heavily indebted poor countries (HIPC),HPC,2012,558428816750.537 +Heavily indebted poor countries (HIPC),HPC,2013,604887195078.331 +Heavily indebted poor countries (HIPC),HPC,2014,643085647289.931 +Heavily indebted poor countries (HIPC),HPC,2015,635445735588.403 +Heavily indebted poor countries (HIPC),HPC,2016,641483025657.911 +High income,HIC,1960,1060243880168.7 +High income,HIC,1961,1121981243040.71 +High income,HIC,1962,1211776607405.32 +High income,HIC,1963,1304603063142.08 +High income,HIC,1964,1424584016309.32 +High income,HIC,1965,1546689929989.83 +High income,HIC,1966,1696346036809.99 +High income,HIC,1967,1819327353188.27 +High income,HIC,1968,1971265014235.61 +High income,HIC,1969,2163798867287.21 +High income,HIC,1970,2379635398065.42 +High income,HIC,1971,2640680118163.63 +High income,HIC,1972,3063716011733.77 +High income,HIC,1973,3706329864579 +High income,HIC,1974,4181857989922.81 +High income,HIC,1975,4663456510499.43 +High income,HIC,1976,5079507752415.01 +High income,HIC,1977,5751828019898.84 +High income,HIC,1978,6888147385510.59 +High income,HIC,1979,7965334539289.59 +High income,HIC,1980,8898498285406.06 +High income,HIC,1981,9036558177457.95 +High income,HIC,1982,8949612375739.48 +High income,HIC,1983,9239819279588.91 +High income,HIC,1984,9637983479337.74 +High income,HIC,1985,10132406784514.8 +High income,HIC,1986,12297564072616.7 +High income,HIC,1987,14242430872936.9 +High income,HIC,1988,16042589882344.5 +High income,HIC,1989,16805389434609.4 +High income,HIC,1990,18896462178031 +High income,HIC,1991,19983111824868.1 +High income,HIC,1992,21499618272513.5 +High income,HIC,1993,21696143760634 +High income,HIC,1994,23254026236811.5 +High income,HIC,1995,25821731647568.2 +High income,HIC,1996,26052438147494.1 +High income,HIC,1997,25624851684656.3 +High income,HIC,1998,25710064575645.4 +High income,HIC,1999,27011881987743.9 +High income,HIC,2000,27571498060078.4 +High income,HIC,2001,27308139048338.7 +High income,HIC,2002,28461009762104.4 +High income,HIC,2003,31909785738810.1 +High income,HIC,2004,35519515046236.5 +High income,HIC,2005,37558565439921.7 +High income,HIC,2006,39635947402724.5 +High income,HIC,2007,43320749778521.8 +High income,HIC,2008,46007526813566.5 +High income,HIC,2009,43117491018121.2 +High income,HIC,2010,45187761310741.6 +High income,HIC,2011,48762979397332.6 +High income,HIC,2012,48790077028762.4 +High income,HIC,2013,49316266632359.8 +High income,HIC,2014,50308250490810.1 +High income,HIC,2015,47409525372420.8 +High income,HIC,2016,48407640157423.4 +IBRD only,IBD,1960,294792529250.207 +IBRD only,IBD,1961,281626215408.776 +IBRD only,IBD,1962,293934441536.983 +IBRD only,IBD,1963,313396206080.027 +IBRD only,IBD,1964,354919270653.881 +IBRD only,IBD,1965,389878012188.969 +IBRD only,IBD,1966,402017322061.603 +IBRD only,IBD,1967,413014336037.993 +IBRD only,IBD,1968,436571843248.962 +IBRD only,IBD,1969,488040628514.822 +IBRD only,IBD,1970,529362750187.336 +IBRD only,IBD,1971,578582492654.098 +IBRD only,IBD,1972,651149122217.479 +IBRD only,IBD,1973,837821911282.808 +IBRD only,IBD,1974,1057251132019.3 +IBRD only,IBD,1975,1142724094139.65 +IBRD only,IBD,1976,1246289423374.27 +IBRD only,IBD,1977,1413532715597.07 +IBRD only,IBD,1978,1524704140806.03 +IBRD only,IBD,1979,1825690390932.99 +IBRD only,IBD,1980,2139762149345.92 +IBRD only,IBD,1981,2321258403803.81 +IBRD only,IBD,1982,2302549053326.87 +IBRD only,IBD,1983,2272103495406.6 +IBRD only,IBD,1984,2313687727936.92 +IBRD only,IBD,1985,2439461778036.01 +IBRD only,IBD,1986,2553168739556.33 +IBRD only,IBD,1987,2616094413959.71 +IBRD only,IBD,1988,2844737806054.1 +IBRD only,IBD,1989,3045326136048.95 +IBRD only,IBD,1990,3443776644827.72 +IBRD only,IBD,1991,3711451063868.31 +IBRD only,IBD,1992,3696285768627.5 +IBRD only,IBD,1993,3965845725257.7 +IBRD only,IBD,1994,4343949156182.07 +IBRD only,IBD,1995,4877267746552.34 +IBRD only,IBD,1996,5322354990131.63 +IBRD only,IBD,1997,5633308770924 +IBRD only,IBD,1998,5473749235787.22 +IBRD only,IBD,1999,5316743211058.67 +IBRD only,IBD,2000,5778989842384.42 +IBRD only,IBD,2001,5856302815575.35 +IBRD only,IBD,2002,5947536549262.23 +IBRD only,IBD,2003,6716225139151.19 +IBRD only,IBD,2004,7990347950856.02 +IBRD only,IBD,2005,9549735974950.61 +IBRD only,IBD,2006,11321411711668.7 +IBRD only,IBD,2007,14088931626236.9 +IBRD only,IBD,2008,16926584187094.4 +IBRD only,IBD,2009,16425341705202.5 +IBRD only,IBD,2010,19954364488950.4 +IBRD only,IBD,2011,23607619466961.8 +IBRD only,IBD,2012,25002131466488.6 +IBRD only,IBD,2013,26486061975152.6 +IBRD only,IBD,2014,27385523724212.7 +IBRD only,IBD,2015,25878328332858.1 +IBRD only,IBD,2016,25924292415039.1 +IDA & IBRD total,IBT,1960,332410539293.112 +IDA & IBRD total,IBT,1961,321985771144.331 +IDA & IBRD total,IBT,1962,337685120265.7 +IDA & IBRD total,IBT,1963,362761033613.715 +IDA & IBRD total,IBT,1964,402977587836.774 +IDA & IBRD total,IBT,1965,444428542057.831 +IDA & IBRD total,IBT,1966,461258734546.45 +IDA & IBRD total,IBT,1967,472865842237.292 +IDA & IBRD total,IBT,1968,499660160465.74 +IDA & IBRD total,IBT,1969,559287041398.569 +IDA & IBRD total,IBT,1970,612186629071.988 +IDA & IBRD total,IBT,1971,661686153516.207 +IDA & IBRD total,IBT,1972,737610959339.937 +IDA & IBRD total,IBT,1973,936838994091.702 +IDA & IBRD total,IBT,1974,1191684644882.95 +IDA & IBRD total,IBT,1975,1303667274673.25 +IDA & IBRD total,IBT,1976,1413816096264.77 +IDA & IBRD total,IBT,1977,1596341785085 +IDA & IBRD total,IBT,1978,1730854109299.48 +IDA & IBRD total,IBT,1979,2065313058061.8 +IDA & IBRD total,IBT,1980,2422283170989.76 +IDA & IBRD total,IBT,1981,2610827592142.73 +IDA & IBRD total,IBT,1982,2582656721626.52 +IDA & IBRD total,IBT,1983,2522667584587.84 +IDA & IBRD total,IBT,1984,2556908680047.68 +IDA & IBRD total,IBT,1985,2691625504690.32 +IDA & IBRD total,IBT,1986,2813180853749.35 +IDA & IBRD total,IBT,1987,2897841130486.82 +IDA & IBRD total,IBT,1988,3135683945829.85 +IDA & IBRD total,IBT,1989,3336319692781.06 +IDA & IBRD total,IBT,1990,3753855345937.29 +IDA & IBRD total,IBT,1991,4028757715172.53 +IDA & IBRD total,IBT,1992,4004458417669.02 +IDA & IBRD total,IBT,1993,4264177499395.29 +IDA & IBRD total,IBT,1994,4630909142217.15 +IDA & IBRD total,IBT,1995,5209713498214.82 +IDA & IBRD total,IBT,1996,5688958786196.36 +IDA & IBRD total,IBT,1997,6012285279839.32 +IDA & IBRD total,IBT,1998,5850703459189.88 +IDA & IBRD total,IBT,1999,5702543484501.18 +IDA & IBRD total,IBT,2000,6204623716453.03 +IDA & IBRD total,IBT,2001,6271139335707.81 +IDA & IBRD total,IBT,2002,6392053928474.98 +IDA & IBRD total,IBT,2003,7219473737910.63 +IDA & IBRD total,IBT,2004,8575492654333.95 +IDA & IBRD total,IBT,2005,10227019071857.2 +IDA & IBRD total,IBT,2006,12135265896478.6 +IDA & IBRD total,IBT,2007,15039672145151.6 +IDA & IBRD total,IBT,2008,18069265699993 +IDA & IBRD total,IBT,2009,17550676210066 +IDA & IBRD total,IBT,2010,21396210578999.2 +IDA & IBRD total,IBT,2011,25246476428292.6 +IDA & IBRD total,IBT,2012,26768123725809.4 +IDA & IBRD total,IBT,2013,28404378612941.1 +IDA & IBRD total,IBT,2014,29459720089842 +IDA & IBRD total,IBT,2015,27894376853304.5 +IDA & IBRD total,IBT,2016,27911770057428.9 +IDA blend,IDB,1960,12528091853.1585 +IDA blend,IDB,1961,13388928377.9155 +IDA blend,IDB,1962,14310570026.0649 +IDA blend,IDB,1963,15136261036.8151 +IDA blend,IDB,1964,16547083675.0911 +IDA blend,IDB,1965,18008427704.7256 +IDA blend,IDB,1966,19548468193.7723 +IDA blend,IDB,1967,19670828004.2858 +IDA blend,IDB,1968,20910846571.8878 +IDA blend,IDB,1969,23861812085.1294 +IDA blend,IDB,1970,32843393663.0869 +IDA blend,IDB,1971,30365264329.5773 +IDA blend,IDB,1972,33936248295.21 +IDA blend,IDB,1973,36073763303.9852 +IDA blend,IDB,1974,52387318527.9086 +IDA blend,IDB,1975,60251909395.5155 +IDA blend,IDB,1976,73264439498.9809 +IDA blend,IDB,1977,76793603162.5652 +IDA blend,IDB,1978,83188559519.2582 +IDA blend,IDB,1979,102363089138.532 +IDA blend,IDB,1980,131669829698.641 +IDA blend,IDB,1981,135610723887.684 +IDA blend,IDB,1982,127708517011.63 +IDA blend,IDB,1983,106285802122.516 +IDA blend,IDB,1984,99724892442.8074 +IDA blend,IDB,1985,99654429284.9044 +IDA blend,IDB,1986,96843172567.8211 +IDA blend,IDB,1987,107048715951.938 +IDA blend,IDB,1988,114678605164.99 +IDA blend,IDB,1989,117241208130.828 +IDA blend,IDB,1990,124515366242.566 +IDA blend,IDB,1991,128087577902.076 +IDA blend,IDB,1992,129464158157.523 +IDA blend,IDB,1993,117459126359.094 +IDA blend,IDB,1994,117901745281.71 +IDA blend,IDB,1995,139685272955.961 +IDA blend,IDB,1996,155752080707.91 +IDA blend,IDB,1997,157340845858.204 +IDA blend,IDB,1998,150468042836.428 +IDA blend,IDB,1999,157062060341.58 +IDA blend,IDB,2000,174891573406.934 +IDA blend,IDB,2001,168921966719.703 +IDA blend,IDB,2002,183712405328.485 +IDA blend,IDB,2003,209404819444.115 +IDA blend,IDB,2004,252474835319.475 +IDA blend,IDB,2005,297634375913.672 +IDA blend,IDB,2006,373661921221.27 +IDA blend,IDB,2007,427256953306.27 +IDA blend,IDB,2008,508512182149.602 +IDA blend,IDB,2009,473469947548.861 +IDA blend,IDB,2010,699996618179.802 +IDA blend,IDB,2011,802831759834.01 +IDA blend,IDB,2012,883071662424.879 +IDA blend,IDB,2013,960383402110.485 +IDA blend,IDB,2014,1043383926479.73 +IDA blend,IDB,2015,976614588892.231 +IDA blend,IDB,2016,914220211373.289 +IDA only,IDX,1960,26900729558.6756 +IDA only,IDX,1961,28202127747.3091 +IDA only,IDX,1962,30578919823.2805 +IDA only,IDX,1963,35608750994.9935 +IDA only,IDX,1964,32782637428.41 +IDA only,IDX,1965,38049648216.3678 +IDA only,IDX,1966,41260252648.3372 +IDA only,IDX,1967,41803467079.1778 +IDA only,IDX,1968,43857842843.3742 +IDA only,IDX,1969,49232891560.4311 +IDA only,IDX,1970,51155937980.7403 +IDA only,IDX,1971,54433112942.909 +IDA only,IDX,1972,53913727572.5046 +IDA only,IDX,1973,65130587108.3052 +IDA only,IDX,1974,84317852974.6352 +IDA only,IDX,1975,103845586044.62 +IDA only,IDX,1976,95540888530.4483 +IDA only,IDX,1977,108030645862.408 +IDA only,IDX,1978,125870174760.378 +IDA only,IDX,1979,139556026001.644 +IDA only,IDX,1980,151420555888.676 +IDA only,IDX,1981,154577110449.62 +IDA only,IDX,1982,153716825855.11 +IDA only,IDX,1983,147442512236.276 +IDA only,IDX,1984,147495091462.227 +IDA only,IDX,1985,157306474735.099 +IDA only,IDX,1986,168977242488.592 +IDA only,IDX,1987,180063158863.859 +IDA only,IDX,1988,181385498558.729 +IDA only,IDX,1989,179072082631.41 +IDA only,IDX,1990,188126607992.926 +IDA only,IDX,1991,191081249885.044 +IDA only,IDX,1992,179808280295.291 +IDA only,IDX,1993,181679334834.709 +IDA only,IDX,1994,167877722801.344 +IDA only,IDX,1995,191639298395.647 +IDA only,IDX,1996,209648120648.985 +IDA only,IDX,1997,220284365616.801 +IDA only,IDX,1998,226082116332.62 +IDA only,IDX,1999,228981957026.655 +IDA only,IDX,2000,251151822827.442 +IDA only,IDX,2001,246099126238.948 +IDA only,IDX,2002,261187520893.639 +IDA only,IDX,2003,294249141169.591 +IDA only,IDX,2004,332732438424.107 +IDA only,IDX,2005,379223704355.932 +IDA only,IDX,2006,439386921175.794 +IDA only,IDX,2007,521840470219.516 +IDA only,IDX,2008,632814903358.774 +IDA only,IDX,2009,653724047017.456 +IDA only,IDX,2010,739442152602.564 +IDA only,IDX,2011,829706713781.067 +IDA only,IDX,2012,875853724368.38 +IDA only,IDX,2013,952302996802.463 +IDA only,IDX,2014,1028027058553.17 +IDA only,IDX,2015,1040940192004.62 +IDA only,IDX,2016,1077908367748.55 +IDA total,IDA,1960,38748692260.3948 +IDA total,IDA,1961,40905486343.7645 +IDA total,IDA,1962,44123855995.0844 +IDA total,IDA,1963,49688700780.7972 +IDA total,IDA,1964,48638830926.4057 +IDA total,IDA,1965,55127127619.5494 +IDA total,IDA,1966,59801429749.3153 +IDA total,IDA,1967,60439331470.9625 +IDA total,IDA,1968,63710108686.0779 +IDA total,IDA,1969,71945084444.5374 +IDA total,IDA,1970,83611704212.3582 +IDA total,IDA,1971,83933783702.5892 +IDA total,IDA,1972,87371340568.4539 +IDA total,IDA,1973,100144499811.812 +IDA total,IDA,1974,135889407708.479 +IDA total,IDA,1975,162649110156.035 +IDA total,IDA,1976,169321323255.879 +IDA total,IDA,1977,184780649645.364 +IDA total,IDA,1978,208345111898.598 +IDA total,IDA,1979,242195946786.616 +IDA total,IDA,1980,285548847107.09 +IDA total,IDA,1981,292801265885.592 +IDA total,IDA,1982,283456512539.266 +IDA total,IDA,1983,254398227877.098 +IDA total,IDA,1984,247369742089.672 +IDA total,IDA,1985,256654502726.648 +IDA total,IDA,1986,264781775186.289 +IDA total,IDA,1987,286234708513.801 +IDA total,IDA,1988,295577043063.381 +IDA total,IDA,1989,295978940818.795 +IDA total,IDA,1990,312339707016.851 +IDA total,IDA,1991,318967140383.491 +IDA total,IDA,1992,309553959926.279 +IDA total,IDA,1993,298741566135.181 +IDA total,IDA,1994,285802386263.917 +IDA total,IDA,1995,331562738922.137 +IDA total,IDA,1996,365804847537.744 +IDA total,IDA,1997,377740987931.319 +IDA total,IDA,1998,376129203724.069 +IDA total,IDA,1999,385846241612.534 +IDA total,IDA,2000,425955016165.44 +IDA total,IDA,2001,414877779648.671 +IDA total,IDA,2002,444822239206.007 +IDA total,IDA,2003,503607352753.133 +IDA total,IDA,2004,585417780901.716 +IDA total,IDA,2005,677263856840.947 +IDA total,IDA,2006,814002488452.235 +IDA total,IDA,2007,949952688725.4 +IDA total,IDA,2008,1141761566655.15 +IDA total,IDA,2009,1125176003190.5 +IDA total,IDA,2010,1444980672769.83 +IDA total,IDA,2011,1639584122424.2 +IDA total,IDA,2012,1768118438213.61 +IDA total,IDA,2013,1922693336373.33 +IDA total,IDA,2014,2082520619431.07 +IDA total,IDA,2015,2025758505542.6 +IDA total,IDA,2016,1996430776919.24 +Late-demographic dividend,LTE,1960,170979834687.82 +Late-demographic dividend,LTE,1961,157897071090.677 +Late-demographic dividend,LTE,1962,165672797274.762 +Late-demographic dividend,LTE,1963,179542413759.862 +Late-demographic dividend,LTE,1964,198102076676.436 +Late-demographic dividend,LTE,1965,220983844815.084 +Late-demographic dividend,LTE,1966,245370863668.602 +Late-demographic dividend,LTE,1967,247715535734.439 +Late-demographic dividend,LTE,1968,253063859646.871 +Late-demographic dividend,LTE,1969,282834192565.336 +Late-demographic dividend,LTE,1970,321365344911.656 +Late-demographic dividend,LTE,1971,357842300708.806 +Late-demographic dividend,LTE,1972,410662881425.586 +Late-demographic dividend,LTE,1973,521687954591.208 +Late-demographic dividend,LTE,1974,614358488944.055 +Late-demographic dividend,LTE,1975,675062859870.315 +Late-demographic dividend,LTE,1976,736553700531.906 +Late-demographic dividend,LTE,1977,854920620708.272 +Late-demographic dividend,LTE,1978,890798665710.268 +Late-demographic dividend,LTE,1979,1064378208589.85 +Late-demographic dividend,LTE,1980,1206585272167.42 +Late-demographic dividend,LTE,1981,1279073780899.5 +Late-demographic dividend,LTE,1982,1308692614317.31 +Late-demographic dividend,LTE,1983,1210824718313.28 +Late-demographic dividend,LTE,1984,1274947279487.52 +Late-demographic dividend,LTE,1985,1362593298859.71 +Late-demographic dividend,LTE,1986,1449305917334.82 +Late-demographic dividend,LTE,1987,1527784692119.54 +Late-demographic dividend,LTE,1988,1682824251759.88 +Late-demographic dividend,LTE,1989,1907361656034.27 +Late-demographic dividend,LTE,1990,2027228504115.95 +Late-demographic dividend,LTE,1991,2230277776370.29 +Late-demographic dividend,LTE,1992,2079905732735.5 +Late-demographic dividend,LTE,1993,2155921522368 +Late-demographic dividend,LTE,1994,2468839439179.73 +Late-demographic dividend,LTE,1995,3032807636908.79 +Late-demographic dividend,LTE,1996,3325672519645.58 +Late-demographic dividend,LTE,1997,3480271526233.09 +Late-demographic dividend,LTE,1998,3351316496248.07 +Late-demographic dividend,LTE,1999,3096106154774.71 +Late-demographic dividend,LTE,2000,3403220871015.89 +Late-demographic dividend,LTE,2001,3517771351799.08 +Late-demographic dividend,LTE,2002,3726423996249.01 +Late-demographic dividend,LTE,2003,4249229634687.67 +Late-demographic dividend,LTE,2004,5107491089896.68 +Late-demographic dividend,LTE,2005,6161761848491.52 +Late-demographic dividend,LTE,2006,7436786145930.92 +Late-demographic dividend,LTE,2007,9366206761782.71 +Late-demographic dividend,LTE,2008,11644623639987.3 +Late-demographic dividend,LTE,2009,11256631749196.5 +Late-demographic dividend,LTE,2010,13512634949047.9 +Late-demographic dividend,LTE,2011,16461624017478 +Late-demographic dividend,LTE,2012,17588592847258.8 +Late-demographic dividend,LTE,2013,18943439059235.6 +Late-demographic dividend,LTE,2014,19696765765637.5 +Late-demographic dividend,LTE,2015,18442948166730.4 +Late-demographic dividend,LTE,2016,18428970585288.9 +Latin America & Caribbean,LCN,1960,81335701189.6428 +Latin America & Caribbean,LCN,1961,86401548189.3568 +Latin America & Caribbean,LCN,1962,99481669694.8621 +Latin America & Caribbean,LCN,1963,100098935911.675 +Latin America & Caribbean,LCN,1964,111187567574.716 +Latin America & Caribbean,LCN,1965,119770844951.528 +Latin America & Caribbean,LCN,1966,131441042298.551 +Latin America & Caribbean,LCN,1967,134656950399.366 +Latin America & Caribbean,LCN,1968,145003712535.205 +Latin America & Caribbean,LCN,1969,162478343090.837 +Latin America & Caribbean,LCN,1970,176599027868.457 +Latin America & Caribbean,LCN,1971,197516005457.39 +Latin America & Caribbean,LCN,1972,222012459414.042 +Latin America & Caribbean,LCN,1973,290635423997.013 +Latin America & Caribbean,LCN,1974,377414609202.896 +Latin America & Caribbean,LCN,1975,395907967425.139 +Latin America & Caribbean,LCN,1976,440234859237.179 +Latin America & Caribbean,LCN,1977,484661330903.231 +Latin America & Caribbean,LCN,1978,549446058073.142 +Latin America & Caribbean,LCN,1979,653954905333.575 +Latin America & Caribbean,LCN,1980,775694200782.059 +Latin America & Caribbean,LCN,1981,893319180188.671 +Latin America & Caribbean,LCN,1982,835598635135.828 +Latin America & Caribbean,LCN,1983,742131362504.011 +Latin America & Caribbean,LCN,1984,732119681478.353 +Latin America & Caribbean,LCN,1985,753457210178.004 +Latin America & Caribbean,LCN,1986,766884114945.657 +Latin America & Caribbean,LCN,1987,807221928641.526 +Latin America & Caribbean,LCN,1988,921057504142.402 +Latin America & Caribbean,LCN,1989,1006663109065.92 +Latin America & Caribbean,LCN,1990,1170492068804.72 +Latin America & Caribbean,LCN,1991,1440178343364.67 +Latin America & Caribbean,LCN,1992,1358408884243.74 +Latin America & Caribbean,LCN,1993,1564958746610.74 +Latin America & Caribbean,LCN,1994,1797150601266.59 +Latin America & Caribbean,LCN,1995,1916099987242.12 +Latin America & Caribbean,LCN,1996,2060739339318.38 +Latin America & Caribbean,LCN,1997,2255377930705.67 +Latin America & Caribbean,LCN,1998,2271734532596.57 +Latin America & Caribbean,LCN,1999,2050793762812.47 +Latin America & Caribbean,LCN,2000,2262087168956.71 +Latin America & Caribbean,LCN,2001,2204812692751.2 +Latin America & Caribbean,LCN,2002,1976793288246.23 +Latin America & Caribbean,LCN,2003,2035297413712.73 +Latin America & Caribbean,LCN,2004,2350652052445.82 +Latin America & Caribbean,LCN,2005,2845502363895.91 +Latin America & Caribbean,LCN,2006,3337221449300.33 +Latin America & Caribbean,LCN,2007,3936233657945.05 +Latin America & Caribbean,LCN,2008,4575918476342.71 +Latin America & Caribbean,LCN,2009,4302206310681.48 +Latin America & Caribbean,LCN,2010,5334359905744.97 +Latin America & Caribbean,LCN,2011,6063818698406.67 +Latin America & Caribbean,LCN,2012,6118412885788.98 +Latin America & Caribbean,LCN,2013,6270431817071.43 +Latin America & Caribbean,LCN,2014,6389407132290.57 +Latin America & Caribbean,LCN,2015,5456024297600.86 +Latin America & Caribbean,LCN,2016,5294928047201.73 +Latin America & Caribbean (excluding high income),LAC,1960,73395468426.688 +Latin America & Caribbean (excluding high income),LAC,1961,77129609859.8088 +Latin America & Caribbean (excluding high income),LAC,1962,88911888348.821 +Latin America & Caribbean (excluding high income),LAC,1963,89097573498.6413 +Latin America & Caribbean (excluding high income),LAC,1964,99074812474.4309 +Latin America & Caribbean (excluding high income),LAC,1965,107304593077.581 +Latin America & Caribbean (excluding high income),LAC,1966,117616850185.479 +Latin America & Caribbean (excluding high income),LAC,1967,120609010516.58 +Latin America & Caribbean (excluding high income),LAC,1968,130256250949.786 +Latin America & Caribbean (excluding high income),LAC,1969,145346421052.827 +Latin America & Caribbean (excluding high income),LAC,1970,157880701506.35 +Latin America & Caribbean (excluding high income),LAC,1971,175505487290.549 +Latin America & Caribbean (excluding high income),LAC,1972,198599650385.719 +Latin America & Caribbean (excluding high income),LAC,1973,259117670925.781 +Latin America & Caribbean (excluding high income),LAC,1974,344587170087.191 +Latin America & Caribbean (excluding high income),LAC,1975,371280813794.006 +Latin America & Caribbean (excluding high income),LAC,1976,411643476107.596 +Latin America & Caribbean (excluding high income),LAC,1977,450102375279.173 +Latin America & Caribbean (excluding high income),LAC,1978,509915128707.522 +Latin America & Caribbean (excluding high income),LAC,1979,602811483644.084 +Latin America & Caribbean (excluding high income),LAC,1980,710116907993.984 +Latin America & Caribbean (excluding high income),LAC,1981,818425766809.108 +Latin America & Caribbean (excluding high income),LAC,1982,769759179392.8 +Latin America & Caribbean (excluding high income),LAC,1983,685169082243.003 +Latin America & Caribbean (excluding high income),LAC,1984,673764033131.398 +Latin America & Caribbean (excluding high income),LAC,1985,695943952938.005 +Latin America & Caribbean (excluding high income),LAC,1986,707488075055.622 +Latin America & Caribbean (excluding high income),LAC,1987,740352753238.472 +Latin America & Caribbean (excluding high income),LAC,1988,846851503909.44 +Latin America & Caribbean (excluding high income),LAC,1989,925567803838.453 +Latin America & Caribbean (excluding high income),LAC,1990,1081353158707.23 +Latin America & Caribbean (excluding high income),LAC,1991,1341594021709.41 +Latin America & Caribbean (excluding high income),LAC,1992,1247729796135 +Latin America & Caribbean (excluding high income),LAC,1993,1446291182393.37 +Latin America & Caribbean (excluding high income),LAC,1994,1663902703398.57 +Latin America & Caribbean (excluding high income),LAC,1995,1760538940825.78 +Latin America & Caribbean (excluding high income),LAC,1996,1895254878110.68 +Latin America & Caribbean (excluding high income),LAC,1997,2074269012884.32 +Latin America & Caribbean (excluding high income),LAC,1998,2085413655025.19 +Latin America & Caribbean (excluding high income),LAC,1999,1867635348455.59 +Latin America & Caribbean (excluding high income),LAC,2000,2070772006620.39 +Latin America & Caribbean (excluding high income),LAC,2001,2013631181344.99 +Latin America & Caribbean (excluding high income),LAC,2002,1791362291806.72 +Latin America & Caribbean (excluding high income),LAC,2003,1839436251262.6 +Latin America & Caribbean (excluding high income),LAC,2004,2121459944550.01 +Latin America & Caribbean (excluding high income),LAC,2005,2579728858022.5 +Latin America & Caribbean (excluding high income),LAC,2006,3029641277365.53 +Latin America & Caribbean (excluding high income),LAC,2007,3598012077005.43 +Latin America & Caribbean (excluding high income),LAC,2008,4213388169678.88 +Latin America & Caribbean (excluding high income),LAC,2009,3953038143139.52 +Latin America & Caribbean (excluding high income),LAC,2010,4923451838328.67 +Latin America & Caribbean (excluding high income),LAC,2011,5604881793369.78 +Latin America & Caribbean (excluding high income),LAC,2012,5639788774858.59 +Latin America & Caribbean (excluding high income),LAC,2013,5771253261929.29 +Latin America & Caribbean (excluding high income),LAC,2014,5905860152291 +Latin America & Caribbean (excluding high income),LAC,2015,5012025091739.32 +Latin America & Caribbean (excluding high income),LAC,2016,4851312777942.51 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1960,76482111077.6185 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1961,81138563371.9225 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1962,93494266400.9791 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1963,93815979997.1996 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1964,104219981388.786 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1965,112129154406.885 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1966,123024977802.299 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1967,125678109114.536 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1968,135122479528.732 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1969,151315870034.114 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1970,164325954209.404 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1971,183241160615.466 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1972,205656941379.028 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1973,271328852988.129 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1974,355626000642.165 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1975,371930862648.18 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1976,414445379589.903 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1977,457226562193.466 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1978,516657430530.222 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1979,616982642629.746 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1980,735862731970.81 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1981,851087909516.137 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1982,791710474105.973 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1983,696442194165.987 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1984,682241174471.374 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1985,703159186334.514 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1986,713216307476.36 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1987,750018929385.015 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1988,858688357375.556 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1989,941803363255.407 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1990,1100843309813.08 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1991,1372317945905.81 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1992,1290654691364.3 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1993,1493678182537.17 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1994,1715751583151.98 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1995,1829060058160.92 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1996,1975504627485.21 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1997,2164600526781.61 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1998,2173777212537.54 +Latin America & the Caribbean (IDA & IBRD countries),TLA,1999,1946393638777.13 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2000,2150291053438.54 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2001,2083909607913.16 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2002,1851416583657.79 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2003,1903845944839.22 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2004,2210850350066.21 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2005,2694953121481.19 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2006,3171420273729.5 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2007,3760043430335.24 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2008,4392555500737.54 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2009,4116124679683.7 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2010,5141956791475.98 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2011,5863434327505.38 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2012,5912679156108.39 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2013,6058441110940.89 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2014,6171728371691.76 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2015,5247223786180.95 +Latin America & the Caribbean (IDA & IBRD countries),TLA,2016,5091478259237.56 +Least developed countries: UN classification,LDC,1985,121227419694.536 +Least developed countries: UN classification,LDC,1986,131938238081.808 +Least developed countries: UN classification,LDC,1987,143578647017.282 +Least developed countries: UN classification,LDC,1988,147074754062.361 +Least developed countries: UN classification,LDC,1989,152628715228.138 +Least developed countries: UN classification,LDC,1990,157170715256.827 +Least developed countries: UN classification,LDC,1991,160976404765.403 +Least developed countries: UN classification,LDC,1992,141665506654.277 +Least developed countries: UN classification,LDC,1993,143654148545.13 +Least developed countries: UN classification,LDC,1994,134056722134.952 +Least developed countries: UN classification,LDC,1995,152708087751.642 +Least developed countries: UN classification,LDC,1996,168685657996.561 +Least developed countries: UN classification,LDC,1997,178534049893.884 +Least developed countries: UN classification,LDC,1998,179530418610.067 +Least developed countries: UN classification,LDC,1999,181622928394.098 +Least developed countries: UN classification,LDC,2000,206064992890.969 +Least developed countries: UN classification,LDC,2001,196139081536.109 +Least developed countries: UN classification,LDC,2002,211734892493.178 +Least developed countries: UN classification,LDC,2003,239687925539.993 +Least developed countries: UN classification,LDC,2004,275300002656.28 +Least developed countries: UN classification,LDC,2005,322233431675.558 +Least developed countries: UN classification,LDC,2006,377006716127.018 +Least developed countries: UN classification,LDC,2007,458953974129.743 +Least developed countries: UN classification,LDC,2008,570703787280.472 +Least developed countries: UN classification,LDC,2009,583900864058.256 +Least developed countries: UN classification,LDC,2010,659839922368.982 +Least developed countries: UN classification,LDC,2011,750266890616.884 +Least developed countries: UN classification,LDC,2012,796749375366.047 +Least developed countries: UN classification,LDC,2013,863502921391.032 +Least developed countries: UN classification,LDC,2014,936514097645.983 +Least developed countries: UN classification,LDC,2015,928999138028.508 +Least developed countries: UN classification,LDC,2016,940285148246.681 +Low & middle income,LMY,1960,322880916332.716 +Low & middle income,LMY,1961,311231715583.693 +Low & middle income,LMY,1962,325417665734.193 +Low & middle income,LMY,1963,350156888666 +Low & middle income,LMY,1964,389096758803.45 +Low & middle income,LMY,1965,430331569299.832 +Low & middle income,LMY,1966,445816749394.908 +Low & middle income,LMY,1967,457649154436.817 +Low & middle income,LMY,1968,484077925262.186 +Low & middle income,LMY,1969,541207794796.467 +Low & middle income,LMY,1970,592564334146.401 +Low & middle income,LMY,1971,639515242844.186 +Low & middle income,LMY,1972,714843001925.87 +Low & middle income,LMY,1973,903871947953.725 +Low & middle income,LMY,1974,1155364728140.96 +Low & middle income,LMY,1975,1278200898264.97 +Low & middle income,LMY,1976,1383584014217.24 +Low & middle income,LMY,1977,1557251333561.92 +Low & middle income,LMY,1978,1689674986849.66 +Low & middle income,LMY,1979,2008497065323.04 +Low & middle income,LMY,1980,2344256743828.1 +Low & middle income,LMY,1981,2520680956567.89 +Low & middle income,LMY,1982,2506206543210.46 +Low & middle income,LMY,1983,2460475457385.26 +Low & middle income,LMY,1984,2497490161411.77 +Low & middle income,LMY,1985,2631158758229.95 +Low & middle income,LMY,1986,2752146346147.57 +Low & middle income,LMY,1987,2830361348685.47 +Low & middle income,LMY,1988,3061048683744.94 +Low & middle income,LMY,1989,3252693889005.75 +Low & middle income,LMY,1990,3658538915672.86 +Low & middle income,LMY,1991,3901223943739.81 +Low & middle income,LMY,1992,3855226625179.94 +Low & middle income,LMY,1993,4108081847781.04 +Low & middle income,LMY,1994,4454438133064.89 +Low & middle income,LMY,1995,4983280097683.29 +Low & middle income,LMY,1996,5432096474657.18 +Low & middle income,LMY,1997,5746597952135 +Low & middle income,LMY,1998,5570045115899.25 +Low & middle income,LMY,1999,5438376046486.8 +Low & middle income,LMY,2000,5939720920632.49 +Low & middle income,LMY,2001,5994991423437.34 +Low & middle income,LMY,2002,6113967979982.54 +Low & middle income,LMY,2003,6911432587348.75 +Low & middle income,LMY,2004,8199024522261.18 +Low & middle income,LMY,2005,9771338254565.71 +Low & middle income,LMY,2006,11610585486988 +Low & middle income,LMY,2007,14403262097266.8 +Low & middle income,LMY,2008,17304070901099.9 +Low & middle income,LMY,2009,16904208788743.9 +Low & middle income,LMY,2010,20662142675904.1 +Low & middle income,LMY,2011,24424596002573.7 +Low & middle income,LMY,2012,25968368047735.1 +Low & middle income,LMY,2013,27567318674507.7 +Low & middle income,LMY,2014,28624869169602 +Low & middle income,LMY,2015,27167365722548.3 +Low & middle income,LMY,2016,27190673692762.9 +Low income,LIC,1980,80209493052.731 +Low income,LIC,1981,77218047173.3983 +Low income,LIC,1982,80698662025.246 +Low income,LIC,1983,75225831144.4383 +Low income,LIC,1984,69082552304.0881 +Low income,LIC,1985,71599175719.0479 +Low income,LIC,1986,81243884335.6357 +Low income,LIC,1987,84768074082.989 +Low income,LIC,1988,91473322897.3124 +Low income,LIC,1989,90496828327.9022 +Low income,LIC,1990,96263982212.309 +Low income,LIC,1991,98290478926.7244 +Low income,LIC,1992,86879256494.9218 +Low income,LIC,1993,86545417718.2698 +Low income,LIC,1994,73004355880.147 +Low income,LIC,1995,84186096134.2208 +Low income,LIC,1996,93849664244.7633 +Low income,LIC,1997,97016681510.3045 +Low income,LIC,1998,98076470573.4088 +Low income,LIC,1999,98178378552.344 +Low income,LIC,2000,113334918600.364 +Low income,LIC,2001,102344707240.841 +Low income,LIC,2002,110542725688.212 +Low income,LIC,2003,121412373512.426 +Low income,LIC,2004,138406056360.832 +Low income,LIC,2005,159876776422.899 +Low income,LIC,2006,177405351695.882 +Low income,LIC,2007,211564340820.323 +Low income,LIC,2008,254864815001.772 +Low income,LIC,2009,271114296708.762 +Low income,LIC,2010,289973428214.635 +Low income,LIC,2011,327276455418.728 +Low income,LIC,2012,352304788203.414 +Low income,LIC,2013,379841277359.31 +Low income,LIC,2014,409946364477.957 +Low income,LIC,2015,401557904349.121 +Low income,LIC,2016,405500691890.706 +Lower middle income,LMC,1965,137461884746.768 +Lower middle income,LMC,1966,124653071069.42 +Lower middle income,LMC,1967,132904891803.828 +Lower middle income,LMC,1968,142418186233.418 +Lower middle income,LMC,1969,158951711186.14 +Lower middle income,LMC,1970,174828599744.636 +Lower middle income,LMC,1971,181895531247.864 +Lower middle income,LMC,1972,193226951288.583 +Lower middle income,LMC,1973,230257570746.104 +Lower middle income,LMC,1974,295746915764.238 +Lower middle income,LMC,1975,328315078089.788 +Lower middle income,LMC,1976,355970733034.943 +Lower middle income,LMC,1977,407693469545.836 +Lower middle income,LMC,1978,456337424996.254 +Lower middle income,LMC,1979,519598733097.624 +Lower middle income,LMC,1980,645574198150.025 +Lower middle income,LMC,1981,681873952068.244 +Lower middle income,LMC,1982,688236725783.1 +Lower middle income,LMC,1983,672506305328.491 +Lower middle income,LMC,1984,669999684572.239 +Lower middle income,LMC,1985,705033625730.709 +Lower middle income,LMC,1986,734922204323.85 +Lower middle income,LMC,1987,807849540707.224 +Lower middle income,LMC,1988,841860056894.847 +Lower middle income,LMC,1989,854143629643.268 +Lower middle income,LMC,1990,914031845671.275 +Lower middle income,LMC,1991,879585053610.512 +Lower middle income,LMC,1992,917120105052.312 +Lower middle income,LMC,1993,928250234223.785 +Lower middle income,LMC,1994,1008393895038.79 +Lower middle income,LMC,1995,1132835750025.1 +Lower middle income,LMC,1996,1244059564531.71 +Lower middle income,LMC,1997,1285159992957.23 +Lower middle income,LMC,1998,1162403879271.33 +Lower middle income,LMC,1999,1260946586435.45 +Lower middle income,LMC,2000,1334823902218.48 +Lower middle income,LMC,2001,1347552601226.17 +Lower middle income,LMC,2002,1447377162059.63 +Lower middle income,LMC,2003,1659139779548.2 +Lower middle income,LMC,2004,1897336556896.53 +Lower middle income,LMC,2005,2185062024100.34 +Lower middle income,LMC,2006,2598144857845.71 +Lower middle income,LMC,2007,3198643502815.83 +Lower middle income,LMC,2008,3597614231170.5 +Lower middle income,LMC,2009,3686218059205.86 +Lower middle income,LMC,2010,4659868433805.74 +Lower middle income,LMC,2011,5271066216374.76 +Lower middle income,LMC,2012,5526562820088.11 +Lower middle income,LMC,2013,5765503690093.18 +Lower middle income,LMC,2014,6067382503069.23 +Lower middle income,LMC,2015,6034684295981.41 +Lower middle income,LMC,2016,6252243979358.16 +Middle East & North Africa,MEA,1968,32544887124.715 +Middle East & North Africa,MEA,1969,36324978697.3221 +Middle East & North Africa,MEA,1970,40541253687.1512 +Middle East & North Africa,MEA,1971,46837776480.6965 +Middle East & North Africa,MEA,1972,57004801533.1691 +Middle East & North Africa,MEA,1973,76859486837.6555 +Middle East & North Africa,MEA,1974,140509090225.561 +Middle East & North Africa,MEA,1975,153634644912.125 +Middle East & North Africa,MEA,1976,191078602015.36 +Middle East & North Africa,MEA,1977,221864632728.163 +Middle East & North Africa,MEA,1978,235765780535.398 +Middle East & North Africa,MEA,1979,308143991726.14 +Middle East & North Africa,MEA,1980,399635706600.82 +Middle East & North Africa,MEA,1981,413100094747.124 +Middle East & North Africa,MEA,1982,410711932256.055 +Middle East & North Africa,MEA,1983,416988855437.435 +Middle East & North Africa,MEA,1984,422997945987.704 +Middle East & North Africa,MEA,1985,429106552157.991 +Middle East & North Africa,MEA,1986,438374928771.13 +Middle East & North Africa,MEA,1987,405957775183.96 +Middle East & North Africa,MEA,1988,402886867149.289 +Middle East & North Africa,MEA,1989,415650066556.61 +Middle East & North Africa,MEA,1990,544628789841.346 +Middle East & North Africa,MEA,1991,545480411372.983 +Middle East & North Africa,MEA,1992,593377600004.871 +Middle East & North Africa,MEA,1993,597234481572.268 +Middle East & North Africa,MEA,1994,621835421140.165 +Middle East & North Africa,MEA,1995,708396338042.244 +Middle East & North Africa,MEA,1996,802232077700.633 +Middle East & North Africa,MEA,1997,832583629107.313 +Middle East & North Africa,MEA,1998,809335180977.759 +Middle East & North Africa,MEA,1999,866997519336.959 +Middle East & North Africa,MEA,2000,967123025893.847 +Middle East & North Africa,MEA,2001,970705165852.075 +Middle East & North Africa,MEA,2002,966886322034.273 +Middle East & North Africa,MEA,2003,1089026969409.72 +Middle East & North Africa,MEA,2004,1264692501508.67 +Middle East & North Africa,MEA,2005,1523735247225.23 +Middle East & North Africa,MEA,2006,1783624049835.17 +Middle East & North Africa,MEA,2007,2111520707885.6 +Middle East & North Africa,MEA,2008,2639163398129.61 +Middle East & North Africa,MEA,2009,2353140017976.63 +Middle East & North Africa,MEA,2010,2747991985923.82 +Middle East & North Africa,MEA,2011,3291238768813.27 +Middle East & North Africa,MEA,2012,3517290042987.4 +Middle East & North Africa,MEA,2013,3569108760902.47 +Middle East & North Africa,MEA,2014,3552155105906.59 +Middle East & North Africa,MEA,2015,3153183676492.12 +Middle East & North Africa,MEA,2016,3111499137793.74 +Middle East & North Africa (excluding high income),MNA,1993,291596156344.835 +Middle East & North Africa (excluding high income),MNA,1994,299033433705.304 +Middle East & North Africa (excluding high income),MNA,1995,341005087325.937 +Middle East & North Africa (excluding high income),MNA,1996,395419455083.866 +Middle East & North Africa (excluding high income),MNA,1997,406142008337.3 +Middle East & North Africa (excluding high income),MNA,1998,412226941363.17 +Middle East & North Africa (excluding high income),MNA,1999,435964657109.841 +Middle East & North Africa (excluding high income),MNA,2000,451745270165.348 +Middle East & North Africa (excluding high income),MNA,2001,467443771526.278 +Middle East & North Africa (excluding high income),MNA,2002,453763567498.89 +Middle East & North Africa (excluding high income),MNA,2003,511876924084.121 +Middle East & North Africa (excluding high income),MNA,2004,586243634956.355 +Middle East & North Africa (excluding high income),MNA,2005,691427125900.788 +Middle East & North Africa (excluding high income),MNA,2006,803273276926.291 +Middle East & North Africa (excluding high income),MNA,2007,989862700047.422 +Middle East & North Africa (excluding high income),MNA,2008,1225666634970.85 +Middle East & North Africa (excluding high income),MNA,2009,1179785595912.43 +Middle East & North Africa (excluding high income),MNA,2010,1362935523406.7 +Middle East & North Africa (excluding high income),MNA,2011,1577443963809.85 +Middle East & North Africa (excluding high income),MNA,2012,1668157593637.49 +Middle East & North Africa (excluding high income),MNA,2013,1637016759700.7 +Middle East & North Africa (excluding high income),MNA,2014,1579293076205.83 +Middle East & North Africa (excluding high income),MNA,2015,1445527236068.07 +Middle East & North Africa (IDA & IBRD countries),TMN,1993,288793981432.888 +Middle East & North Africa (IDA & IBRD countries),TMN,1994,296159788194.109 +Middle East & North Africa (IDA & IBRD countries),TMN,1995,337684846816.424 +Middle East & North Africa (IDA & IBRD countries),TMN,1996,391994294829.679 +Middle East & North Africa (IDA & IBRD countries),TMN,1997,402348143945.757 +Middle East & North Africa (IDA & IBRD countries),TMN,1998,408106910445.414 +Middle East & North Africa (IDA & IBRD countries),TMN,1999,431640382177.662 +Middle East & North Africa (IDA & IBRD countries),TMN,2000,447384545921.062 +Middle East & North Africa (IDA & IBRD countries),TMN,2001,463423567528.959 +Middle East & North Africa (IDA & IBRD countries),TMN,2002,450214949083.257 +Middle East & North Africa (IDA & IBRD countries),TMN,2003,507920055467.194 +Middle East & North Africa (IDA & IBRD countries),TMN,2004,581942286783.674 +Middle East & North Africa (IDA & IBRD countries),TMN,2005,686628893957.615 +Middle East & North Africa (IDA & IBRD countries),TMN,2006,798404019734.187 +Middle East & North Africa (IDA & IBRD countries),TMN,2007,984408659212.038 +Middle East & North Africa (IDA & IBRD countries),TMN,2008,1219063793136.09 +Middle East & North Africa (IDA & IBRD countries),TMN,2009,1172546853378.41 +Middle East & North Africa (IDA & IBRD countries),TMN,2010,1354032869496.32 +Middle East & North Africa (IDA & IBRD countries),TMN,2011,1566983828956.61 +Middle East & North Africa (IDA & IBRD countries),TMN,2012,1656868816266.87 +Middle East & North Africa (IDA & IBRD countries),TMN,2013,1624432436239.87 +Middle East & North Africa (IDA & IBRD countries),TMN,2014,1566425106492.08 +Middle East & North Africa (IDA & IBRD countries),TMN,2015,1432641907767.45 +Middle income,MIC,1960,310379641287.339 +Middle income,MIC,1961,298629856589.636 +Middle income,MIC,1962,311470336452.733 +Middle income,MIC,1963,332436061824.552 +Middle income,MIC,1964,375258292135.611 +Middle income,MIC,1965,413664176294.508 +Middle income,MIC,1966,427456737432.456 +Middle income,MIC,1967,440150321992.544 +Middle income,MIC,1968,465920438850.911 +Middle income,MIC,1969,520867515611.093 +Middle income,MIC,1970,571296418063.845 +Middle income,MIC,1971,616131606095.362 +Middle income,MIC,1972,688973366920.011 +Middle income,MIC,1973,872977889163.73 +Middle income,MIC,1974,1118745885064.35 +Middle income,MIC,1975,1236174910742.05 +Middle income,MIC,1976,1341860828067.38 +Middle income,MIC,1977,1509117019025.73 +Middle income,MIC,1978,1634224303394.68 +Middle income,MIC,1979,1947681001919.87 +Middle income,MIC,1980,2280187292914.43 +Middle income,MIC,1981,2459499139801.4 +Middle income,MIC,1982,2441787641412.67 +Middle income,MIC,1983,2400889964155.46 +Middle income,MIC,1984,2443660614880.83 +Middle income,MIC,1985,2575525887782.12 +Middle income,MIC,1986,2688214490798.85 +Middle income,MIC,1987,2763479253508.59 +Middle income,MIC,1988,2988896953383.47 +Middle income,MIC,1989,3182426401658.11 +Middle income,MIC,1990,3583954413081.7 +Middle income,MIC,1991,3825385475042.01 +Middle income,MIC,1992,3789343789199.26 +Middle income,MIC,1993,4043243535797.25 +Middle income,MIC,1994,4402360060455.03 +Middle income,MIC,1995,4922821848492.25 +Middle income,MIC,1996,5363658537833.46 +Middle income,MIC,1997,5674926531226.03 +Middle income,MIC,1998,5499432482324.87 +Middle income,MIC,1999,5365881260373.12 +Middle income,MIC,2000,5851457342169 +Middle income,MIC,2001,5917641548115.5 +Middle income,MIC,2002,6032742784414.22 +Middle income,MIC,2003,6827061176773.63 +Middle income,MIC,2004,8104396652437.36 +Middle income,MIC,2005,9658919014552.3 +Middle income,MIC,2006,11484611685884.1 +Middle income,MIC,2007,14252023316865.7 +Middle income,MIC,2008,17120054313779.3 +Middle income,MIC,2009,16697282511085.5 +Middle income,MIC,2010,20431462486141.4 +Middle income,MIC,2011,24158308272825.3 +Middle income,MIC,2012,25671457284263.6 +Middle income,MIC,2013,27244416912947.4 +Middle income,MIC,2014,28271814853457.7 +Middle income,MIC,2015,26814503372731.8 +Middle income,MIC,2016,26835804840473.3 +North America,NAC,1960,584477920198.99 +North America,NAC,1961,604157219440.397 +North America,NAC,1962,647173002027.413 +North America,NAC,1963,683353535761.531 +North America,NAC,1964,734790505460.858 +North America,NAC,1965,797723909391.131 +North America,NAC,1966,875492805408.936 +North America,NAC,1967,926623934246.798 +North America,NAC,1968,1013409031841.72 +North America,NAC,1969,1097952410241.71 +North America,NAC,1970,1163966395224.42 +North America,NAC,1971,1267253061477.52 +North America,NAC,1972,1395767220992.02 +North America,NAC,1973,1560140359214.08 +North America,NAC,1974,1709546297648.26 +North America,NAC,1975,1863102029787.65 +North America,NAC,1976,2084548864401.62 +North America,NAC,1977,2298010156934.65 +North America,NAC,1978,2575679667449.81 +North America,NAC,1979,2875732302185.42 +North America,NAC,1980,3136972126345.01 +North America,NAC,1981,3517909963656.99 +North America,NAC,1982,3659283025119.14 +North America,NAC,1983,3979574111781.89 +North America,NAC,1984,4397051258071.62 +North America,NAC,1985,4712529999482.75 +North America,NAC,1986,4968766427343.98 +North America,NAC,1987,5302830242049.45 +North America,NAC,1988,5761398451214.26 +North America,NAC,1989,6224250243275.25 +North America,NAC,1990,6575110950908.47 +North America,NAC,1991,6786006083611.19 +North America,NAC,1992,7133366589284.92 +North America,NAC,1993,7457709121892.44 +North America,NAC,1994,7888761439501.61 +North America,NAC,1995,8270122373385.4 +North America,NAC,1996,8731442777972.13 +North America,NAC,1997,9264273191726.28 +North America,NAC,1998,9724112027406.81 +North America,NAC,1999,10340031087640.9 +North America,NAC,2000,11030552667252.6 +North America,NAC,2001,11361884260892.6 +North America,NAC,2002,11739401906646.5 +North America,NAC,2003,12407237511367.9 +North America,NAC,2004,13302608706074.6 +North America,NAC,2005,14267952115864.7 +North America,NAC,2006,15176717496461.2 +North America,NAC,2007,15948507238205.8 +North America,NAC,2008,16273823136997.2 +North America,NAC,2009,15795698382986.4 +North America,NAC,2010,16583580836811.1 +North America,NAC,2011,17312124677047.8 +North America,NAC,2012,17985081294447.6 +North America,NAC,2013,18539718715830.2 +North America,NAC,2014,19191755961783.7 +North America,NAC,2015,19595346721974.5 +North America,NAC,2016,20104904753734.8 +OECD members,OED,1960,1071148468561.41 +OECD members,OED,1961,1125832842998.51 +OECD members,OED,1962,1215841389136.37 +OECD members,OED,1963,1310283723048.87 +OECD members,OED,1964,1431887112561.98 +OECD members,OED,1965,1554546204085.41 +OECD members,OED,1966,1706871950732.13 +OECD members,OED,1967,1831600828348.03 +OECD members,OED,1968,1985484758150.99 +OECD members,OED,1969,2179226855795.61 +OECD members,OED,1970,2390390686111.07 +OECD members,OED,1971,2646317574763.35 +OECD members,OED,1972,3070546015009.47 +OECD members,OED,1973,3709185177074.73 +OECD members,OED,1974,4158280828971.97 +OECD members,OED,1975,4658825077597.2 +OECD members,OED,1976,5049009543873.49 +OECD members,OED,1977,5693077367140.28 +OECD members,OED,1978,6835772209740.3 +OECD members,OED,1979,7896142494077.98 +OECD members,OED,1980,8764338498877.9 +OECD members,OED,1981,8922436064860.69 +OECD members,OED,1982,8786255124380.75 +OECD members,OED,1983,9077887021838.69 +OECD members,OED,1984,9494477176460.86 +OECD members,OED,1985,10015967458690.2 +OECD members,OED,1986,12140888703533 +OECD members,OED,1987,14052574571754.2 +OECD members,OED,1988,15854831419998 +OECD members,OED,1989,16610824977450.6 +OECD members,OED,1990,18717131834846.2 +OECD members,OED,1991,19798533698156.4 +OECD members,OED,1992,21286212823936.3 +OECD members,OED,1993,21602686285516.6 +OECD members,OED,1994,23066746233125.3 +OECD members,OED,1995,25409558844503.5 +OECD members,OED,1996,25631695317896.3 +OECD members,OED,1997,25240277081015.1 +OECD members,OED,1998,25498833540302.8 +OECD members,OED,1999,26798774923570.7 +OECD members,OED,2000,27363992604547.7 +OECD members,OED,2001,27110458919332 +OECD members,OED,2002,28288512785831.6 +OECD members,OED,2003,31695202048895.5 +OECD members,OED,2004,35282198913762.3 +OECD members,OED,2005,37292067332552.4 +OECD members,OED,2006,39314471295987.4 +OECD members,OED,2007,42976830154645.7 +OECD members,OED,2008,45489387299735.7 +OECD members,OED,2009,42557636517549.5 +OECD members,OED,2010,44588338330918.9 +OECD members,OED,2011,47907103635329.9 +OECD members,OED,2012,47806877198116 +OECD members,OED,2013,48377472404230.7 +OECD members,OED,2014,49318789250850.1 +OECD members,OED,2015,46489946919891.2 +OECD members,OED,2016,47394052796780.3 +Other small states,OSS,1970,3120000513.63038 +Other small states,OSS,1971,3703668497.25236 +Other small states,OSS,1972,4485075013.76688 +Other small states,OSS,1973,6495784978.63854 +Other small states,OSS,1974,11967802921.9622 +Other small states,OSS,1975,13355843219.8957 +Other small states,OSS,1976,16602401120.5883 +Other small states,OSS,1977,18699272186.2965 +Other small states,OSS,1978,20631386856.0129 +Other small states,OSS,1979,26785098967.4081 +Other small states,OSS,1980,37183269997.956 +Other small states,OSS,1981,37385918037.1978 +Other small states,OSS,1982,35362618281.9374 +Other small states,OSS,1983,33399704521.5294 +Other small states,OSS,1984,33642964084.1968 +Other small states,OSS,1985,31964598499.4935 +Other small states,OSS,1986,32342044549.6327 +Other small states,OSS,1987,38389402844.2948 +Other small states,OSS,1988,43139712814.829 +Other small states,OSS,1989,45177490212.0309 +Other small states,OSS,1990,54042037641.2318 +Other small states,OSS,1991,55656865129.0129 +Other small states,OSS,1992,60712295552.4811 +Other small states,OSS,1993,57746555835.7855 +Other small states,OSS,1994,60482828187.5356 +Other small states,OSS,1995,69298946725.52 +Other small states,OSS,1996,73172701870.202 +Other small states,OSS,1997,76266859217.9987 +Other small states,OSS,1998,74735365660.7398 +Other small states,OSS,1999,80433140076.8188 +Other small states,OSS,2000,91230480401.4556 +Other small states,OSS,2001,90672611826.8333 +Other small states,OSS,2002,97772578664.9743 +Other small states,OSS,2003,121118378940.561 +Other small states,OSS,2004,150081419957.446 +Other small states,OSS,2005,182102127175.268 +Other small states,OSS,2006,214036484985.338 +Other small states,OSS,2007,261998871872.612 +Other small states,OSS,2008,320442487440.3 +Other small states,OSS,2009,275173743167.462 +Other small states,OSS,2010,320128712509.16 +Other small states,OSS,2011,396019011152.095 +Other small states,OSS,2012,413403874820.602 +Other small states,OSS,2013,431086487370.448 +Other small states,OSS,2014,444405144277.814 +Other small states,OSS,2015,368813590036.302 +Other small states,OSS,2016,358773092768.133 +Pacific island small states,PSS,1975,1119628254.45097 +Pacific island small states,PSS,1976,1122701696.34762 +Pacific island small states,PSS,1977,1171337344.9078 +Pacific island small states,PSS,1978,1358461165.8138 +Pacific island small states,PSS,1979,1664350939.8496 +Pacific island small states,PSS,1980,1904926911.85166 +Pacific island small states,PSS,1981,1963994945.57172 +Pacific island small states,PSS,1982,1918437387.41573 +Pacific island small states,PSS,1983,1837236806.42751 +Pacific island small states,PSS,1984,2014185046.11569 +Pacific island small states,PSS,1985,1906817279.10052 +Pacific island small states,PSS,1986,2070080622.74654 +Pacific island small states,PSS,1987,2033588088.5576 +Pacific island small states,PSS,1988,2130602489.94617 +Pacific island small states,PSS,1989,2224856985.74429 +Pacific island small states,PSS,1990,2396519647.95393 +Pacific island small states,PSS,1991,2548876044.46992 +Pacific island small states,PSS,1992,2794580111.5317 +Pacific island small states,PSS,1993,2946262871.07794 +Pacific island small states,PSS,1994,3395405058.97482 +Pacific island small states,PSS,1995,3668259744.87741 +Pacific island small states,PSS,1996,3939096312.4053 +Pacific island small states,PSS,1997,3935301807.10212 +Pacific island small states,PSS,1998,3383564007.07114 +Pacific island small states,PSS,1999,3685133762.02162 +Pacific island small states,PSS,2000,3448662181.8232 +Pacific island small states,PSS,2001,3375672793.3588 +Pacific island small states,PSS,2002,3547120140.79495 +Pacific island small states,PSS,2003,4158737438.11844 +Pacific island small states,PSS,2004,4803309271.01673 +Pacific island small states,PSS,2005,5273003192.09283 +Pacific island small states,PSS,2006,5542891987.75737 +Pacific island small states,PSS,2007,6079623933.05137 +Pacific island small states,PSS,2008,6554131285.08651 +Pacific island small states,PSS,2009,5775975880.54319 +Pacific island small states,PSS,2010,6402407316.26645 +Pacific island small states,PSS,2011,7588040251.82748 +Pacific island small states,PSS,2012,8109258573.53231 +Pacific island small states,PSS,2013,8375325157.08132 +Pacific island small states,PSS,2014,8780810605.05193 +Pacific island small states,PSS,2015,8577127156.94718 +Pacific island small states,PSS,2016,8888374720.40864 +Post-demographic dividend,PST,1960,1044656471320.34 +Post-demographic dividend,PST,1961,1104232973387.34 +Post-demographic dividend,PST,1962,1192909023772.47 +Post-demographic dividend,PST,1963,1284222177741.77 +Post-demographic dividend,PST,1964,1401673751394.55 +Post-demographic dividend,PST,1965,1522536171657.24 +Post-demographic dividend,PST,1966,1669615615246.75 +Post-demographic dividend,PST,1967,1791607216531.48 +Post-demographic dividend,PST,1968,1941588054277.74 +Post-demographic dividend,PST,1969,2129810291257.2 +Post-demographic dividend,PST,1970,2340888132430.83 +Post-demographic dividend,PST,1971,2595435794745.15 +Post-demographic dividend,PST,1972,3010533039073.01 +Post-demographic dividend,PST,1973,3633402675461.9 +Post-demographic dividend,PST,1974,4057572289102.56 +Post-demographic dividend,PST,1975,4546348912848.59 +Post-demographic dividend,PST,1976,4932972042226.7 +Post-demographic dividend,PST,1977,5576027961708.64 +Post-demographic dividend,PST,1978,6699303304490.5 +Post-demographic dividend,PST,1979,7701902775121.56 +Post-demographic dividend,PST,1980,8528744893965.75 +Post-demographic dividend,PST,1981,8627548222470.53 +Post-demographic dividend,PST,1982,8585856196665.95 +Post-demographic dividend,PST,1983,8907329214532.46 +Post-demographic dividend,PST,1984,9311124585501.98 +Post-demographic dividend,PST,1985,9820475618013.04 +Post-demographic dividend,PST,1986,11992583228317.2 +Post-demographic dividend,PST,1987,13892518055017.9 +Post-demographic dividend,PST,1988,15649138703182.9 +Post-demographic dividend,PST,1989,16363490315438.2 +Post-demographic dividend,PST,1990,18373377012979.3 +Post-demographic dividend,PST,1991,19370853403893.8 +Post-demographic dividend,PST,1992,20787746843384.1 +Post-demographic dividend,PST,1993,20957060229668.8 +Post-demographic dividend,PST,1994,22429029383135.7 +Post-demographic dividend,PST,1995,24852064655451.6 +Post-demographic dividend,PST,1996,24982304258549.2 +Post-demographic dividend,PST,1997,24511726798084.9 +Post-demographic dividend,PST,1998,24615173781504.8 +Post-demographic dividend,PST,1999,25841010690140.8 +Post-demographic dividend,PST,2000,26282689637675.3 +Post-demographic dividend,PST,2001,26041902978856 +Post-demographic dividend,PST,2002,27158954468171.9 +Post-demographic dividend,PST,2003,30469203654916.5 +Post-demographic dividend,PST,2004,33857110810844.5 +Post-demographic dividend,PST,2005,35634331213681.7 +Post-demographic dividend,PST,2006,37477971990381.3 +Post-demographic dividend,PST,2007,40875024376117.4 +Post-demographic dividend,PST,2008,43181906552198.2 +Post-demographic dividend,PST,2009,40654677510668 +Post-demographic dividend,PST,2010,42396023961852.1 +Post-demographic dividend,PST,2011,45507579610296.7 +Post-demographic dividend,PST,2012,45431195222279.9 +Post-demographic dividend,PST,2013,45820016966895.3 +Post-demographic dividend,PST,2014,46685151869819.2 +Post-demographic dividend,PST,2015,44081796060319.5 +Post-demographic dividend,PST,2016,45072138207006.3 +Pre-demographic dividend,PRE,1960,10444792557.8843 +Pre-demographic dividend,PRE,1961,10792191176.9913 +Pre-demographic dividend,PRE,1962,11794111161.167 +Pre-demographic dividend,PRE,1963,13764637075.142 +Pre-demographic dividend,PRE,1964,12795571686.9276 +Pre-demographic dividend,PRE,1965,14432973775.8933 +Pre-demographic dividend,PRE,1966,15869568127.6857 +Pre-demographic dividend,PRE,1967,14966394983.6694 +Pre-demographic dividend,PRE,1968,15779012377.2635 +Pre-demographic dividend,PRE,1969,17940990058.5991 +Pre-demographic dividend,PRE,1970,21951712164.311 +Pre-demographic dividend,PRE,1971,21460771419.442 +Pre-demographic dividend,PRE,1972,24863758794.081 +Pre-demographic dividend,PRE,1973,30483369632.1883 +Pre-demographic dividend,PRE,1974,43595337802.2346 +Pre-demographic dividend,PRE,1975,49542343893.6683 +Pre-demographic dividend,PRE,1976,58304074038.3621 +Pre-demographic dividend,PRE,1977,64654895975.3614 +Pre-demographic dividend,PRE,1978,71877526055.5515 +Pre-demographic dividend,PRE,1979,90876577931.6188 +Pre-demographic dividend,PRE,1980,111128692579.225 +Pre-demographic dividend,PRE,1981,99890611243.0475 +Pre-demographic dividend,PRE,1982,96485338012.3114 +Pre-demographic dividend,PRE,1983,82315486748.1406 +Pre-demographic dividend,PRE,1984,81200162508.3063 +Pre-demographic dividend,PRE,1985,84292961016.2471 +Pre-demographic dividend,PRE,1986,86854873100.4704 +Pre-demographic dividend,PRE,1987,99081060277.1037 +Pre-demographic dividend,PRE,1988,101944868961.831 +Pre-demographic dividend,PRE,1989,104019371613.989 +Pre-demographic dividend,PRE,1990,167507910377.201 +Pre-demographic dividend,PRE,1991,166990503688.432 +Pre-demographic dividend,PRE,1992,152419870394.426 +Pre-demographic dividend,PRE,1993,138508748991.463 +Pre-demographic dividend,PRE,1994,126874724056.413 +Pre-demographic dividend,PRE,1995,155361079241.209 +Pre-demographic dividend,PRE,1996,173224107614.69 +Pre-demographic dividend,PRE,1997,180765049998.456 +Pre-demographic dividend,PRE,1998,178797997119.74 +Pre-demographic dividend,PRE,1999,181481700462.617 +Pre-demographic dividend,PRE,2000,211754710795.63 +Pre-demographic dividend,PRE,2001,200696505653.475 +Pre-demographic dividend,PRE,2002,237011718871.519 +Pre-demographic dividend,PRE,2003,274020284824.498 +Pre-demographic dividend,PRE,2004,332024783749.247 +Pre-demographic dividend,PRE,2005,411421770397.822 +Pre-demographic dividend,PRE,2006,513693910493.325 +Pre-demographic dividend,PRE,2007,628900837818.886 +Pre-demographic dividend,PRE,2008,802590453555.333 +Pre-demographic dividend,PRE,2009,730577224083.185 +Pre-demographic dividend,PRE,2010,1005125615291.85 +Pre-demographic dividend,PRE,2011,1162386942622.43 +Pre-demographic dividend,PRE,2012,1277869591512.9 +Pre-demographic dividend,PRE,2013,1396458716370.55 +Pre-demographic dividend,PRE,2014,1491939195061.18 +Pre-demographic dividend,PRE,2015,1296897941977.73 +Pre-demographic dividend,PRE,2016,1194739930946.8 +Small states,SST,1970,7836787195.57926 +Small states,SST,1971,8818144097.10709 +Small states,SST,1972,10407377358.8588 +Small states,SST,1973,12964753902.4681 +Small states,SST,1974,20152451259.247 +Small states,SST,1975,22936619953.578 +Small states,SST,1976,26214423633.4757 +Small states,SST,1977,29771720454.8057 +Small states,SST,1978,31993368148.3596 +Small states,SST,1979,39722899441.6698 +Small states,SST,1980,52643379201.5761 +Small states,SST,1981,54400876326.3316 +Small states,SST,1982,54173365558.0276 +Small states,SST,1983,52414910869.8285 +Small states,SST,1984,51973393619.5984 +Small states,SST,1985,50036733663.1376 +Small states,SST,1986,49068591555.2924 +Small states,SST,1987,56063766853.2931 +Small states,SST,1988,61791930551.2311 +Small states,SST,1989,64304587894.9821 +Small states,SST,1990,74325169013.8579 +Small states,SST,1991,75856511772.4776 +Small states,SST,1992,80746249830.122 +Small states,SST,1993,78946659813.3949 +Small states,SST,1994,83179982124.9322 +Small states,SST,1995,94034486010.0192 +Small states,SST,1996,100096683467.444 +Small states,SST,1997,105904285406.659 +Small states,SST,1998,106388698435.668 +Small states,SST,1999,114155580862.484 +Small states,SST,2000,126923095471.91 +Small states,SST,2001,127212731119.311 +Small states,SST,2002,136256691925.274 +Small states,SST,2003,162766467544.72 +Small states,SST,2004,196053942193.91 +Small states,SST,2005,234022949884.059 +Small states,SST,2006,271974858948.518 +Small states,SST,2007,326343861812.675 +Small states,SST,2008,393489345339.146 +Small states,SST,2009,336886290580.524 +Small states,SST,2010,387290741084.775 +Small states,SST,2011,469287734302.83 +Small states,SST,2012,489309844392.053 +Small states,SST,2013,508115793399.22 +Small states,SST,2014,521843492853.73 +Small states,SST,2015,444166579289.296 +Small states,SST,2016,431168068762.38 +South Asia,SAS,1960,46535882350.9266 +South Asia,SAS,1961,49639265058.4063 +South Asia,SAS,1962,52969959484.3953 +South Asia,SAS,1963,59564124015.8815 +South Asia,SAS,1964,68207240598.8702 +South Asia,SAS,1965,73302133554.3812 +South Asia,SAS,1966,61638106880.2155 +South Asia,SAS,1967,67859668328.6678 +South Asia,SAS,1968,71227636451.2209 +South Asia,SAS,1969,78199356804.4547 +South Asia,SAS,1970,84723556606.6577 +South Asia,SAS,1971,90042201735.933 +South Asia,SAS,1972,90429767479.7852 +South Asia,SAS,1973,103394104064.568 +South Asia,SAS,1974,125253027719.481 +South Asia,SAS,1975,134416247893.554 +South Asia,SAS,1976,131167736496.854 +South Asia,SAS,1977,151656859741.32 +South Asia,SAS,1978,172583702484.323 +South Asia,SAS,1979,193318644211.014 +South Asia,SAS,1980,233086150890.581 +South Asia,SAS,1981,247106727885.511 +South Asia,SAS,1982,255647758195.845 +South Asia,SAS,1983,270545959911.574 +South Asia,SAS,1984,269355371616.79 +South Asia,SAS,1985,292874261371.477 +South Asia,SAS,1986,310147312219.788 +South Asia,SAS,1987,344338564248.576 +South Asia,SAS,1988,370046274630.143 +South Asia,SAS,1989,373481452094.992 +South Asia,SAS,1990,402050004575.507 +South Asia,SAS,1991,357724016273.124 +South Asia,SAS,1992,379830007893.096 +South Asia,SAS,1993,376239667456.956 +South Asia,SAS,1994,426649315426.754 +South Asia,SAS,1995,474034412841.889 +South Asia,SAS,1996,518624561848.025 +South Asia,SAS,1997,544005563622.908 +South Asia,SAS,1998,551628275406.445 +South Asia,SAS,1999,590948715637.038 +South Asia,SAS,2000,614756473709.585 +South Asia,SAS,2001,630828017006.122 +South Asia,SAS,2002,663250167513.155 +South Asia,SAS,2003,774457944529.068 +South Asia,SAS,2004,897902015362.288 +South Asia,SAS,2005,1028596382537.81 +South Asia,SAS,2006,1176153232856.56 +South Asia,SAS,2007,1488571172561.54 +South Asia,SAS,2008,1515479924054.14 +South Asia,SAS,2009,1665393025481.3 +South Asia,SAS,2010,2041877081506.8 +South Asia,SAS,2011,2271681618877.26 +South Asia,SAS,2012,2297541689398.13 +South Asia,SAS,2013,2356159930348.29 +South Asia,SAS,2014,2577102437097.39 +South Asia,SAS,2015,2705001263802.98 +South Asia,SAS,2016,2896360597068.83 +South Asia (IDA & IBRD),TSA,1960,46535882350.9266 +South Asia (IDA & IBRD),TSA,1961,49639265058.4063 +South Asia (IDA & IBRD),TSA,1962,52969959484.3953 +South Asia (IDA & IBRD),TSA,1963,59564124015.8815 +South Asia (IDA & IBRD),TSA,1964,68207240598.8702 +South Asia (IDA & IBRD),TSA,1965,73302133554.3812 +South Asia (IDA & IBRD),TSA,1966,61638106880.2155 +South Asia (IDA & IBRD),TSA,1967,67859668328.6678 +South Asia (IDA & IBRD),TSA,1968,71227636451.2209 +South Asia (IDA & IBRD),TSA,1969,78199356804.4547 +South Asia (IDA & IBRD),TSA,1970,84723556606.6577 +South Asia (IDA & IBRD),TSA,1971,90042201735.933 +South Asia (IDA & IBRD),TSA,1972,90429767479.7852 +South Asia (IDA & IBRD),TSA,1973,103394104064.568 +South Asia (IDA & IBRD),TSA,1974,125253027719.481 +South Asia (IDA & IBRD),TSA,1975,134416247893.554 +South Asia (IDA & IBRD),TSA,1976,131167736496.854 +South Asia (IDA & IBRD),TSA,1977,151656859741.32 +South Asia (IDA & IBRD),TSA,1978,172583702484.323 +South Asia (IDA & IBRD),TSA,1979,193318644211.014 +South Asia (IDA & IBRD),TSA,1980,233086150890.581 +South Asia (IDA & IBRD),TSA,1981,247106727885.511 +South Asia (IDA & IBRD),TSA,1982,255647758195.845 +South Asia (IDA & IBRD),TSA,1983,270545959911.574 +South Asia (IDA & IBRD),TSA,1984,269355371616.79 +South Asia (IDA & IBRD),TSA,1985,292874261371.477 +South Asia (IDA & IBRD),TSA,1986,310147312219.788 +South Asia (IDA & IBRD),TSA,1987,344338564248.576 +South Asia (IDA & IBRD),TSA,1988,370046274630.143 +South Asia (IDA & IBRD),TSA,1989,373481452094.992 +South Asia (IDA & IBRD),TSA,1990,402050004575.507 +South Asia (IDA & IBRD),TSA,1991,357724016273.124 +South Asia (IDA & IBRD),TSA,1992,379830007893.096 +South Asia (IDA & IBRD),TSA,1993,376239667456.956 +South Asia (IDA & IBRD),TSA,1994,426649315426.754 +South Asia (IDA & IBRD),TSA,1995,474034412841.889 +South Asia (IDA & IBRD),TSA,1996,518624561848.025 +South Asia (IDA & IBRD),TSA,1997,544005563622.908 +South Asia (IDA & IBRD),TSA,1998,551628275406.445 +South Asia (IDA & IBRD),TSA,1999,590948715637.038 +South Asia (IDA & IBRD),TSA,2000,614756473709.585 +South Asia (IDA & IBRD),TSA,2001,630828017006.122 +South Asia (IDA & IBRD),TSA,2002,663250167513.155 +South Asia (IDA & IBRD),TSA,2003,774457944529.068 +South Asia (IDA & IBRD),TSA,2004,897902015362.288 +South Asia (IDA & IBRD),TSA,2005,1028596382537.81 +South Asia (IDA & IBRD),TSA,2006,1176153232856.56 +South Asia (IDA & IBRD),TSA,2007,1488571172561.54 +South Asia (IDA & IBRD),TSA,2008,1515479924054.14 +South Asia (IDA & IBRD),TSA,2009,1665393025481.3 +South Asia (IDA & IBRD),TSA,2010,2041877081506.8 +South Asia (IDA & IBRD),TSA,2011,2271681618877.26 +South Asia (IDA & IBRD),TSA,2012,2297541689398.13 +South Asia (IDA & IBRD),TSA,2013,2356159930348.29 +South Asia (IDA & IBRD),TSA,2014,2577102437097.39 +South Asia (IDA & IBRD),TSA,2015,2705001263802.98 +South Asia (IDA & IBRD),TSA,2016,2896360597068.83 +Sub-Saharan Africa,SSF,1960,30385873317.6191 +Sub-Saharan Africa,SSF,1961,31561069598.0721 +Sub-Saharan Africa,SSF,1962,34208896061.892 +Sub-Saharan Africa,SSF,1963,39225741664.7524 +Sub-Saharan Africa,SSF,1964,38271070687.048 +Sub-Saharan Africa,SSF,1965,42640958019.1558 +Sub-Saharan Africa,SSF,1966,46032013187.6583 +Sub-Saharan Africa,SSF,1967,45494160112.4203 +Sub-Saharan Africa,SSF,1968,48659688079.8348 +Sub-Saharan Africa,SSF,1969,55788782510.7981 +Sub-Saharan Africa,SSF,1970,65641311288.6349 +Sub-Saharan Africa,SSF,1971,66787674494.6591 +Sub-Saharan Africa,SSF,1972,75244560743.9655 +Sub-Saharan Africa,SSF,1973,96178659252.9924 +Sub-Saharan Africa,SSF,1974,126049492057.768 +Sub-Saharan Africa,SSF,1975,138623192435.063 +Sub-Saharan Africa,SSF,1976,150523567593.48 +Sub-Saharan Africa,SSF,1977,165924142356.056 +Sub-Saharan Africa,SSF,1978,183362414567.183 +Sub-Saharan Africa,SSF,1979,220720718807.57 +Sub-Saharan Africa,SSF,1980,277168620649.298 +Sub-Saharan Africa,SSF,1981,279597299140.561 +Sub-Saharan Africa,SSF,1982,261201722615.81 +Sub-Saharan Africa,SSF,1983,243178158509.023 +Sub-Saharan Africa,SSF,1984,231534327753.875 +Sub-Saharan Africa,SSF,1985,215767871591.388 +Sub-Saharan Africa,SSF,1986,239127684060.416 +Sub-Saharan Africa,SSF,1987,281787606420.109 +Sub-Saharan Africa,SSF,1988,296425266186.195 +Sub-Saharan Africa,SSF,1989,310125765635.045 +Sub-Saharan Africa,SSF,1990,310601465373.488 +Sub-Saharan Africa,SSF,1991,320112647218.571 +Sub-Saharan Africa,SSF,1992,314832065746.688 +Sub-Saharan Africa,SSF,1993,298956393687.87 +Sub-Saharan Africa,SSF,1994,291679005862.221 +Sub-Saharan Africa,SSF,1995,337515727715.285 +Sub-Saharan Africa,SSF,1996,349098748885.709 +Sub-Saharan Africa,SSF,1997,361099250055.323 +Sub-Saharan Africa,SSF,1998,340612558536.667 +Sub-Saharan Africa,SSF,1999,343239312535.324 +Sub-Saharan Africa,SSF,2000,367992054429.688 +Sub-Saharan Africa,SSF,2001,342553779838.8 +Sub-Saharan Africa,SSF,2002,367108332547.638 +Sub-Saharan Africa,SSF,2003,468992752558.158 +Sub-Saharan Africa,SSF,2004,583209894804.24 +Sub-Saharan Africa,SSF,2005,685920335014.857 +Sub-Saharan Africa,SSF,2006,801445697569.974 +Sub-Saharan Africa,SSF,2007,932416460747.498 +Sub-Saharan Africa,SSF,2008,1064752013079.22 +Sub-Saharan Africa,SSF,2009,1022260711828.75 +Sub-Saharan Africa,SSF,2010,1361111020590.47 +Sub-Saharan Africa,SSF,2011,1532598528787.88 +Sub-Saharan Africa,SSF,2012,1607918609335.78 +Sub-Saharan Africa,SSF,2013,1692968078930.83 +Sub-Saharan Africa,SSF,2014,1775063779086.99 +Sub-Saharan Africa,SSF,2015,1601114502934 +Sub-Saharan Africa,SSF,2016,1498001165832.29 +Sub-Saharan Africa (excluding high income),SSA,1960,30375251120.8982 +Sub-Saharan Africa (excluding high income),SSA,1961,31551059162.9924 +Sub-Saharan Africa (excluding high income),SSA,1962,34197956223.5719 +Sub-Saharan Africa (excluding high income),SSA,1963,39213859646.9823 +Sub-Saharan Africa (excluding high income),SSA,1964,38257387425.3277 +Sub-Saharan Africa (excluding high income),SSA,1965,42627500884.5051 +Sub-Saharan Africa (excluding high income),SSA,1966,46017949100.4039 +Sub-Saharan Africa (excluding high income),SSA,1967,45479820393.276 +Sub-Saharan Africa (excluding high income),SSA,1968,48646316919.2904 +Sub-Saharan Africa (excluding high income),SSA,1969,55775718701.4028 +Sub-Saharan Africa (excluding high income),SSA,1970,65627001306.2372 +Sub-Saharan Africa (excluding high income),SSA,1971,66769437518.4287 +Sub-Saharan Africa (excluding high income),SSA,1972,75217262850.8043 +Sub-Saharan Africa (excluding high income),SSA,1973,96146370804.013 +Sub-Saharan Africa (excluding high income),SSA,1974,126013152393.226 +Sub-Saharan Africa (excluding high income),SSA,1975,138582808945.702 +Sub-Saharan Africa (excluding high income),SSA,1976,150482725715.529 +Sub-Saharan Africa (excluding high income),SSA,1977,165867501154.588 +Sub-Saharan Africa (excluding high income),SSA,1978,183283589349.244 +Sub-Saharan Africa (excluding high income),SSA,1979,220598169194.501 +Sub-Saharan Africa (excluding high income),SSA,1980,277028916343.799 +Sub-Saharan Africa (excluding high income),SSA,1981,279449423286.245 +Sub-Saharan Africa (excluding high income),SSA,1982,261060121194.45 +Sub-Saharan Africa (excluding high income),SSA,1983,243036605981.087 +Sub-Saharan Africa (excluding high income),SSA,1984,231387005071.033 +Sub-Saharan Africa (excluding high income),SSA,1985,215600490241.505 +Sub-Saharan Africa (excluding high income),SSA,1986,238920584205.91 +Sub-Saharan Africa (excluding high income),SSA,1987,281539069227.019 +Sub-Saharan Africa (excluding high income),SSA,1988,296141433311.271 +Sub-Saharan Africa (excluding high income),SSA,1989,309820789988.06 +Sub-Saharan Africa (excluding high income),SSA,1990,310231626268.599 +Sub-Saharan Africa (excluding high income),SSA,1991,319737091702.347 +Sub-Saharan Africa (excluding high income),SSA,1992,314396072361.021 +Sub-Saharan Africa (excluding high income),SSA,1993,298479274571.218 +Sub-Saharan Africa (excluding high income),SSA,1994,291189045365.333 +Sub-Saharan Africa (excluding high income),SSA,1995,337004319808.346 +Sub-Saharan Africa (excluding high income),SSA,1996,348592745999.649 +Sub-Saharan Africa (excluding high income),SSA,1997,360532574706.808 +Sub-Saharan Africa (excluding high income),SSA,1998,339999446598.117 +Sub-Saharan Africa (excluding high income),SSA,1999,342611389093.949 +Sub-Saharan Africa (excluding high income),SSA,2000,367372728727.684 +Sub-Saharan Africa (excluding high income),SSA,2001,341926581367.121 +Sub-Saharan Africa (excluding high income),SSA,2002,366405040525.772 +Sub-Saharan Africa (excluding high income),SSA,2003,468282596074.19 +Sub-Saharan Africa (excluding high income),SSA,2004,582365642433.327 +Sub-Saharan Africa (excluding high income),SSA,2005,684996503798.667 +Sub-Saharan Africa (excluding high income),SSA,2006,800424661847.349 +Sub-Saharan Africa (excluding high income),SSA,2007,931379876723.897 +Sub-Saharan Africa (excluding high income),SSA,2008,1063784722921.19 +Sub-Saharan Africa (excluding high income),SSA,2009,1021413301121.37 +Sub-Saharan Africa (excluding high income),SSA,2010,1360141211429.22 +Sub-Saharan Africa (excluding high income),SSA,2011,1531532869537.52 +Sub-Saharan Africa (excluding high income),SSA,2012,1606784475519.59 +Sub-Saharan Africa (excluding high income),SSA,2013,1691556590025.3 +Sub-Saharan Africa (excluding high income),SSA,2014,1773640768624.54 +Sub-Saharan Africa (excluding high income),SSA,2015,1599676432988.51 +Sub-Saharan Africa (excluding high income),SSA,2016,1496573096431.15 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1960,30385873317.619 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1961,31561069598.0721 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1962,34208896061.8919 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1963,39225741664.7524 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1964,38271070687.0479 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1965,42640958019.1557 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1966,46032013187.6583 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1967,45494160112.4203 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1968,48659688079.8347 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1969,55788782510.798 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1970,65641311288.6349 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1971,66787674494.659 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1972,75244560743.9654 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1973,96178659252.9923 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1974,126049492057.768 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1975,138623192435.063 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1976,150523567593.48 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1977,165924142356.056 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1978,183362414567.182 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1979,220720718807.57 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1980,277168620649.297 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1981,279597299140.561 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1982,261201722615.809 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1983,243178158509.023 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1984,231534327753.875 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1985,215767871591.388 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1986,239127684060.416 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1987,281787606420.109 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1988,296425266186.195 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1989,310125765635.045 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1990,310601465373.488 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1991,320112647218.571 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1992,314832065746.688 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1993,298956393687.87 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1994,291679005862.221 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1995,337515727715.285 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1996,349098748885.709 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1997,361099250055.323 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1998,340612558536.667 +Sub-Saharan Africa (IDA & IBRD countries),TSS,1999,343239312535.324 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2000,367992054429.687 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2001,342553779838.8 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2002,367108332547.638 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2003,468992752558.158 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2004,583209894804.24 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2005,685920335014.857 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2006,801445697569.974 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2007,932416460747.498 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2008,1064752013079.22 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2009,1022260711828.75 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2010,1361111020590.47 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2011,1532598528787.88 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2012,1607918609335.78 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2013,1692968078930.83 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2014,1775063779086.99 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2015,1601114502934 +Sub-Saharan Africa (IDA & IBRD countries),TSS,2016,1498001165832.29 +Upper middle income,UMC,1960,217198647348.494 +Upper middle income,UMC,1961,198163003670.2 +Upper middle income,UMC,1962,208389042802.748 +Upper middle income,UMC,1963,218098552786.484 +Upper middle income,UMC,1964,246119837319.701 +Upper middle income,UMC,1965,274662862506.164 +Upper middle income,UMC,1966,301790005708.953 +Upper middle income,UMC,1967,306050686739.129 +Upper middle income,UMC,1968,322082966685.997 +Upper middle income,UMC,1969,360349217563.025 +Upper middle income,UMC,1970,394712327560.652 +Upper middle income,UMC,1971,432720300458.596 +Upper middle income,UMC,1972,494632159315.49 +Upper middle income,UMC,1973,642140455074.472 +Upper middle income,UMC,1974,822217878924.844 +Upper middle income,UMC,1975,906913549605.54 +Upper middle income,UMC,1976,984885444867.622 +Upper middle income,UMC,1977,1099899948045.69 +Upper middle income,UMC,1978,1175443653695.94 +Upper middle income,UMC,1979,1426467211025.1 +Upper middle income,UMC,1980,1630719856470.39 +Upper middle income,UMC,1981,1773950086597.61 +Upper middle income,UMC,1982,1749446371405.03 +Upper middle income,UMC,1983,1724517465799.49 +Upper middle income,UMC,1984,1770310443963.72 +Upper middle income,UMC,1985,1867008281184.46 +Upper middle income,UMC,1986,1949731563473.3 +Upper middle income,UMC,1987,1947828103084.37 +Upper middle income,UMC,1988,2143335758799.66 +Upper middle income,UMC,1989,2330524136589.05 +Upper middle income,UMC,1990,2668016557510.31 +Upper middle income,UMC,1991,2945883449408.78 +Upper middle income,UMC,1992,2871295456915.82 +Upper middle income,UMC,1993,3115112542376.69 +Upper middle income,UMC,1994,3394104181360.42 +Upper middle income,UMC,1995,3790108844101.48 +Upper middle income,UMC,1996,4119760606241.92 +Upper middle income,UMC,1997,4389848846126.91 +Upper middle income,UMC,1998,4336871558815.34 +Upper middle income,UMC,1999,4105143075774.15 +Upper middle income,UMC,2000,4516745939516.19 +Upper middle income,UMC,2001,4570215302631.64 +Upper middle income,UMC,2002,4585099461562.07 +Upper middle income,UMC,2003,5167508901821.26 +Upper middle income,UMC,2004,6206949763569.09 +Upper middle income,UMC,2005,7473673674144.63 +Upper middle income,UMC,2006,8886248909977.84 +Upper middle income,UMC,2007,11053091998253.6 +Upper middle income,UMC,2008,13518836281583.6 +Upper middle income,UMC,2009,13009912111056.7 +Upper middle income,UMC,2010,15772171277711.6 +Upper middle income,UMC,2011,18884745299009.3 +Upper middle income,UMC,2012,20141382977240.6 +Upper middle income,UMC,2013,21474040434046.4 +Upper middle income,UMC,2014,22200346875601.1 +Upper middle income,UMC,2015,20773804256767.4 +Upper middle income,UMC,2016,20570823016722.9 +World,WLD,1960,1366594741088.4 +World,WLD,1961,1421709987131.86 +World,WLD,1962,1526879874910.38 +World,WLD,1963,1643678995959.36 +World,WLD,1964,1800740602720.3 +World,WLD,1965,1961944483712.14 +World,WLD,1966,2128619422408.42 +World,WLD,1967,2264793675472.3 +World,WLD,1968,2443345941026.71 +World,WLD,1969,2690870797695.49 +World,WLD,1970,2956885263956.69 +World,WLD,1971,3265917075455.06 +World,WLD,1972,3766131377379.81 +World,WLD,1973,4589203445245.55 +World,WLD,1974,5292853388124.73 +World,WLD,1975,5893742092987.04 +World,WLD,1976,6412245309406.65 +World,WLD,1977,7252968306810.71 +World,WLD,1978,8537753756958.14 +World,WLD,1979,9919046089674.77 +World,WLD,1980,11166003844627.3 +World,WLD,1981,11457611762565.2 +World,WLD,1982,11355656421230.4 +World,WLD,1983,11616289845852.5 +World,WLD,1984,12058393957798 +World,WLD,1985,12681690895939.6 +World,WLD,1986,15014218229942.7 +World,WLD,1987,17083141772090.1 +World,WLD,1988,19129417553328 +World,WLD,1989,20080076891104.8 +World,WLD,1990,22579759701252.1 +World,WLD,1991,23909829105950.2 +World,WLD,1992,25389800980490.1 +World,WLD,1993,25834735037805.8 +World,WLD,1994,27740746941786.4 +World,WLD,1995,30840585358429 +World,WLD,1996,31518975357605.6 +World,WLD,1997,31403319223627.4 +World,WLD,1998,31314851252776.4 +World,WLD,1999,32486113872865.7 +World,WLD,2000,33543172517758.5 +World,WLD,2001,33335981983184.4 +World,WLD,2002,34612407886315.4 +World,WLD,2003,38867460911728.5 +World,WLD,2004,43770744134898.4 +World,WLD,2005,47385623695571.6 +World,WLD,2006,51306757202160.1 +World,WLD,2007,57793330584397.1 +World,WLD,2008,63386360623929.9 +World,WLD,2009,60086989444835.8 +World,WLD,2010,65906150734910.9 +World,WLD,2011,73241717918435.6 +World,WLD,2012,74802287628562.6 +World,WLD,2013,76924649557249.8 +World,WLD,2014,78972470867720.3 +World,WLD,2015,74606412596560.3 +World,WLD,2016,75641577475364.6 +Afghanistan,AFG,1960,537777811.111111 +Afghanistan,AFG,1961,548888895.555556 +Afghanistan,AFG,1962,546666677.777778 +Afghanistan,AFG,1963,751111191.111111 +Afghanistan,AFG,1964,800000044.444444 +Afghanistan,AFG,1965,1006666637.77778 +Afghanistan,AFG,1966,1399999966.66667 +Afghanistan,AFG,1967,1673333417.77778 +Afghanistan,AFG,1968,1373333366.66667 +Afghanistan,AFG,1969,1408888922.22222 +Afghanistan,AFG,1970,1748886595.55556 +Afghanistan,AFG,1971,1831108971.11111 +Afghanistan,AFG,1972,1595555475.55556 +Afghanistan,AFG,1973,1733333264.44444 +Afghanistan,AFG,1974,2155555497.77778 +Afghanistan,AFG,1975,2366666615.55556 +Afghanistan,AFG,1976,2555555566.66667 +Afghanistan,AFG,1977,2953333417.77778 +Afghanistan,AFG,1978,3300000108.88889 +Afghanistan,AFG,1979,3697940409.61098 +Afghanistan,AFG,1980,3641723321.99546 +Afghanistan,AFG,1981,3478787909.09091 +Afghanistan,AFG,2001,2461665937.89386 +Afghanistan,AFG,2002,4128820723.04713 +Afghanistan,AFG,2003,4583644246.48061 +Afghanistan,AFG,2004,5285465685.86423 +Afghanistan,AFG,2005,6275073571.54659 +Afghanistan,AFG,2006,7057598406.61553 +Afghanistan,AFG,2007,9843842455.48323 +Afghanistan,AFG,2008,10190529882.4878 +Afghanistan,AFG,2009,12486943505.7381 +Afghanistan,AFG,2010,15936800636.2487 +Afghanistan,AFG,2011,17930239399.8149 +Afghanistan,AFG,2012,20536542736.7297 +Afghanistan,AFG,2013,20046334303.9661 +Afghanistan,AFG,2014,20050189881.6659 +Afghanistan,AFG,2015,19702986340.5494 +Afghanistan,AFG,2016,19469022207.6357 +Albania,ALB,1984,1924242453.00793 +Albania,ALB,1985,1965384586.2409 +Albania,ALB,1986,2173750012.5 +Albania,ALB,1987,2156624900 +Albania,ALB,1988,2126000000 +Albania,ALB,1989,2335124987.5 +Albania,ALB,1990,2101624962.5 +Albania,ALB,1991,1139166645.83333 +Albania,ALB,1992,709452583.880319 +Albania,ALB,1993,1228071037.84446 +Albania,ALB,1994,1985673798.10258 +Albania,ALB,1995,2424499009.14264 +Albania,ALB,1996,3314898291.75235 +Albania,ALB,1997,2359903108.38446 +Albania,ALB,1998,2707123772.16195 +Albania,ALB,1999,3414760915.27878 +Albania,ALB,2000,3632043907.97733 +Albania,ALB,2001,4060758804.12084 +Albania,ALB,2002,4435078647.74817 +Albania,ALB,2003,5746945912.58082 +Albania,ALB,2004,7314865175.6199 +Albania,ALB,2005,8158548716.68554 +Albania,ALB,2006,8992642348.7871 +Albania,ALB,2007,10701011896.7708 +Albania,ALB,2008,12881352687.7773 +Albania,ALB,2009,12044212903.8168 +Albania,ALB,2010,11926953258.916 +Albania,ALB,2011,12890867538.5302 +Albania,ALB,2012,12319784787.2987 +Albania,ALB,2013,12781029643.5936 +Albania,ALB,2014,13219857459.1009 +Albania,ALB,2015,11390365293.8057 +Albania,ALB,2016,11926892452.8499 +Algeria,DZA,1960,2723648551.75208 +Algeria,DZA,1961,2434776645.73628 +Algeria,DZA,1962,2001468867.73344 +Algeria,DZA,1963,2703014867.32834 +Algeria,DZA,1964,2909351792.58659 +Algeria,DZA,1965,3136258896.9233 +Algeria,DZA,1966,3039834558.74906 +Algeria,DZA,1967,3370843065.76735 +Algeria,DZA,1968,3852115816.97758 +Algeria,DZA,1969,4257218772.15369 +Algeria,DZA,1970,4863487492.65763 +Algeria,DZA,1971,5077222366.97472 +Algeria,DZA,1972,6761786386.54713 +Algeria,DZA,1973,8715105930.49101 +Algeria,DZA,1974,13209713643.3219 +Algeria,DZA,1975,15557934268.4965 +Algeria,DZA,1976,17728347374.994 +Algeria,DZA,1977,20971901273.271 +Algeria,DZA,1978,26364491313.4471 +Algeria,DZA,1979,33243422157.6311 +Algeria,DZA,1980,42345277342.0195 +Algeria,DZA,1981,44348672667.8715 +Algeria,DZA,1982,45207088715.6483 +Algeria,DZA,1983,48801369800.3675 +Algeria,DZA,1984,53698278905.9678 +Algeria,DZA,1985,57937868670.1937 +Algeria,DZA,1986,63696301892.8116 +Algeria,DZA,1987,66742267773.1959 +Algeria,DZA,1988,59089067187.3943 +Algeria,DZA,1989,55631489801.5508 +Algeria,DZA,1990,62045099642.7774 +Algeria,DZA,1991,45715367087.1001 +Algeria,DZA,1992,48003298223.1178 +Algeria,DZA,1993,49946455210.966 +Algeria,DZA,1994,42542571305.5136 +Algeria,DZA,1995,41764052457.8814 +Algeria,DZA,1996,46941496779.8499 +Algeria,DZA,1997,48177862501.9495 +Algeria,DZA,1998,48187747528.899 +Algeria,DZA,1999,48640574566.6476 +Algeria,DZA,2000,54790245600.5846 +Algeria,DZA,2001,54744714396.1666 +Algeria,DZA,2002,56760288973.6703 +Algeria,DZA,2003,67863829880.4832 +Algeria,DZA,2004,85324998813.604 +Algeria,DZA,2005,103198228458.588 +Algeria,DZA,2006,117027304746.54 +Algeria,DZA,2007,134977087734.008 +Algeria,DZA,2008,171000691877.714 +Algeria,DZA,2009,137211039898.193 +Algeria,DZA,2010,161207268655.392 +Algeria,DZA,2011,200013051408.116 +Algeria,DZA,2012,209047389309.482 +Algeria,DZA,2013,209783503258.224 +Algeria,DZA,2014,213983107815.932 +Algeria,DZA,2015,164779467703.299 +Algeria,DZA,2016,156079606663.188 +American Samoa,ASM,2002,514000000 +American Samoa,ASM,2003,527000000 +American Samoa,ASM,2004,512000000 +American Samoa,ASM,2005,503000000 +American Samoa,ASM,2006,496000000 +American Samoa,ASM,2007,520000000 +American Samoa,ASM,2008,563000000 +American Samoa,ASM,2009,678000000 +American Samoa,ASM,2010,576000000 +American Samoa,ASM,2011,574000000 +American Samoa,ASM,2012,644000000 +American Samoa,ASM,2013,639000000 +American Samoa,ASM,2014,638000000 +American Samoa,ASM,2015,641000000 +Andorra,AND,1970,78619206.0850963 +Andorra,AND,1971,89409820.3592814 +Andorra,AND,1972,113408231.944085 +Andorra,AND,1973,150820102.798401 +Andorra,AND,1974,186558696.279204 +Andorra,AND,1975,220127246.376812 +Andorra,AND,1976,227281024.620741 +Andorra,AND,1977,254020153.340635 +Andorra,AND,1978,308008897.569444 +Andorra,AND,1979,411578334.159643 +Andorra,AND,1980,446416105.825017 +Andorra,AND,1981,388958731.302938 +Andorra,AND,1982,375895956.383462 +Andorra,AND,1983,327861832.946636 +Andorra,AND,1984,330070689.298282 +Andorra,AND,1985,346737964.774951 +Andorra,AND,1986,482000594.03588 +Andorra,AND,1987,611316399.407088 +Andorra,AND,1988,721425939.15155 +Andorra,AND,1989,795449332.396346 +Andorra,AND,1990,1029048481.88051 +Andorra,AND,1991,1106928582.86629 +Andorra,AND,1992,1210013651.87713 +Andorra,AND,1993,1007025755.00065 +Andorra,AND,1994,1017549124.33238 +Andorra,AND,1995,1178738991.19295 +Andorra,AND,1996,1223945356.62682 +Andorra,AND,1997,1180597272.72727 +Andorra,AND,1998,1211932397.81713 +Andorra,AND,1999,1239876305.13531 +Andorra,AND,2000,1401695227.56587 +Andorra,AND,2001,1484017897.09172 +Andorra,AND,2002,1717485413.13759 +Andorra,AND,2003,2373927765.23702 +Andorra,AND,2004,2916786689.84356 +Andorra,AND,2005,3248215396.09501 +Andorra,AND,2006,3536632793.87781 +Andorra,AND,2007,4010990966.32904 +Andorra,AND,2008,4001201113.22689 +Andorra,AND,2009,3650083356.48791 +Andorra,AND,2010,3346516556.29139 +Andorra,AND,2011,3427022518.76564 +Andorra,AND,2012,3146151869.45908 +Andorra,AND,2013,3248924588.42273 +Angola,AGO,1985,6684491978.60963 +Angola,AGO,1986,6684491978.60963 +Angola,AGO,1987,6684491978.60963 +Angola,AGO,1988,6684491978.60963 +Angola,AGO,1989,10026737967.9144 +Angola,AGO,1990,10026737967.9144 +Angola,AGO,1991,12118610904.2251 +Angola,AGO,1992,5684291718.77106 +Angola,AGO,1993,5278182970.79618 +Angola,AGO,1994,4059352153.05368 +Angola,AGO,1995,5039552482.42778 +Angola,AGO,1996,7526446605.51712 +Angola,AGO,1997,7648377412.83277 +Angola,AGO,1998,6445041824.66621 +Angola,AGO,1999,6152922942.98032 +Angola,AGO,2000,9129594818.60749 +Angola,AGO,2001,8936063723.20121 +Angola,AGO,2002,12497346669.6684 +Angola,AGO,2003,14188949190.618 +Angola,AGO,2004,19640848728.8937 +Angola,AGO,2005,28233712830.9035 +Angola,AGO,2006,41789478661.3096 +Angola,AGO,2007,60448921272.2326 +Angola,AGO,2008,84178032716.0971 +Angola,AGO,2009,75492384801.3695 +Angola,AGO,2010,82470913120.7314 +Angola,AGO,2011,104115923082.737 +Angola,AGO,2012,115398371427.673 +Angola,AGO,2013,124912063308.202 +Angola,AGO,2014,126776874216.703 +Angola,AGO,2015,102962245246.708 +Angola,AGO,2016,89633156470.3104 +Antigua and Barbuda,ATG,1977,77496740.7407407 +Antigua and Barbuda,ATG,1978,87879333.3333333 +Antigua and Barbuda,ATG,1979,109079962.962963 +Antigua and Barbuda,ATG,1980,131431037.037037 +Antigua and Barbuda,ATG,1981,147841740.740741 +Antigua and Barbuda,ATG,1982,164369296.296296 +Antigua and Barbuda,ATG,1983,182144111.111111 +Antigua and Barbuda,ATG,1984,208372851.851852 +Antigua and Barbuda,ATG,1985,240923925.925926 +Antigua and Barbuda,ATG,1986,290440148.148148 +Antigua and Barbuda,ATG,1987,337174851.851852 +Antigua and Barbuda,ATG,1988,398637740.740741 +Antigua and Barbuda,ATG,1989,438794777.777778 +Antigua and Barbuda,ATG,1990,459469074.074074 +Antigua and Barbuda,ATG,1991,481706333.333333 +Antigua and Barbuda,ATG,1992,499281148.148148 +Antigua and Barbuda,ATG,1993,535172777.777778 +Antigua and Barbuda,ATG,1994,589429592.592593 +Antigua and Barbuda,ATG,1995,577280740.740741 +Antigua and Barbuda,ATG,1996,633730629.62963 +Antigua and Barbuda,ATG,1997,680617111.111111 +Antigua and Barbuda,ATG,1998,727860592.592593 +Antigua and Barbuda,ATG,1999,766198925.925926 +Antigua and Barbuda,ATG,2000,825405518.518518 +Antigua and Barbuda,ATG,2001,795976518.518518 +Antigua and Barbuda,ATG,2002,809754481.481482 +Antigua and Barbuda,ATG,2003,850218592.592592 +Antigua and Barbuda,ATG,2004,913710370.37037 +Antigua and Barbuda,ATG,2005,1014979666.66667 +Antigua and Barbuda,ATG,2006,1149025481.48148 +Antigua and Barbuda,ATG,2007,1302388925.92593 +Antigua and Barbuda,ATG,2008,1359733555.55556 +Antigua and Barbuda,ATG,2009,1217719666.66667 +Antigua and Barbuda,ATG,2010,1147942222.22222 +Antigua and Barbuda,ATG,2011,1141865444.44444 +Antigua and Barbuda,ATG,2012,1216045777.77778 +Antigua and Barbuda,ATG,2013,1195885111.11111 +Antigua and Barbuda,ATG,2014,1274330333.33333 +Antigua and Barbuda,ATG,2015,1355645888.88889 +Antigua and Barbuda,ATG,2016,1449160185.18519 +Argentina,ARG,1962,24450604877.6081 +Argentina,ARG,1963,18272123664.4715 +Argentina,ARG,1964,25605249381.7597 +Argentina,ARG,1965,28344705966.6389 +Argentina,ARG,1966,28630474727.9023 +Argentina,ARG,1967,24256667553.2569 +Argentina,ARG,1968,26436857247.4982 +Argentina,ARG,1969,31256284543.6155 +Argentina,ARG,1970,31584210365.5447 +Argentina,ARG,1971,33293199095.4881 +Argentina,ARG,1972,34733000536.2862 +Argentina,ARG,1973,52544000116.9037 +Argentina,ARG,1974,72436777342.4554 +Argentina,ARG,1975,52438647921.9226 +Argentina,ARG,1976,51169499890.7722 +Argentina,ARG,1977,56781000100.9448 +Argentina,ARG,1978,58082870156.2634 +Argentina,ARG,1979,69252328953.3789 +Argentina,ARG,1980,76961923741.9478 +Argentina,ARG,1981,78676842366.4213 +Argentina,ARG,1982,84307486836.724 +Argentina,ARG,1983,103979106777.911 +Argentina,ARG,1984,79092001998.032 +Argentina,ARG,1985,88416668900.2596 +Argentina,ARG,1986,110934442762.694 +Argentina,ARG,1987,111106191358.197 +Argentina,ARG,1988,126206817196.091 +Argentina,ARG,1989,76636898036.4712 +Argentina,ARG,1990,141352368714.691 +Argentina,ARG,1991,189719984268.485 +Argentina,ARG,1992,228788617201.696 +Argentina,ARG,1993,236741715015.015 +Argentina,ARG,1994,257440000000 +Argentina,ARG,1995,258031750000 +Argentina,ARG,1996,272149750000 +Argentina,ARG,1997,292859000000 +Argentina,ARG,1998,298948250000 +Argentina,ARG,1999,283523000000 +Argentina,ARG,2000,284203750000 +Argentina,ARG,2001,268696750000 +Argentina,ARG,2002,97724004251.8602 +Argentina,ARG,2003,127586973492.177 +Argentina,ARG,2004,164657930452.787 +Argentina,ARG,2005,198737095012.282 +Argentina,ARG,2006,232557260817.308 +Argentina,ARG,2007,287530508430.568 +Argentina,ARG,2008,361558037110.419 +Argentina,ARG,2009,332976484577.619 +Argentina,ARG,2010,423627422092.49 +Argentina,ARG,2011,530163281574.658 +Argentina,ARG,2012,545982375701.128 +Argentina,ARG,2013,552025140252.246 +Argentina,ARG,2014,526319673731.638 +Argentina,ARG,2015,584711485367.267 +Argentina,ARG,2016,545866164478.053 +Armenia,ARM,1990,2256838858.42714 +Armenia,ARM,1991,2068526521.90299 +Armenia,ARM,1992,1272577521.7683 +Armenia,ARM,1993,1201313196.45626 +Armenia,ARM,1994,1315158670.47971 +Armenia,ARM,1995,1468317350.02343 +Armenia,ARM,1996,1596968913.19202 +Armenia,ARM,1997,1639492424.38103 +Armenia,ARM,1998,1893726437.35976 +Armenia,ARM,1999,1845482181.48539 +Armenia,ARM,2000,1911563665.39006 +Armenia,ARM,2001,2118467913.37873 +Armenia,ARM,2002,2376335048.39976 +Armenia,ARM,2003,2807061008.69084 +Armenia,ARM,2004,3576615240.41616 +Armenia,ARM,2005,4900469515.07252 +Armenia,ARM,2006,6384451606.1421 +Armenia,ARM,2007,9206301700.39619 +Armenia,ARM,2008,11662040713.8753 +Armenia,ARM,2009,8647936747.98704 +Armenia,ARM,2010,9260284937.79782 +Armenia,ARM,2011,10142111334.4961 +Armenia,ARM,2012,10619320048.5857 +Armenia,ARM,2013,11121465767.4067 +Armenia,ARM,2014,11609512939.7543 +Armenia,ARM,2015,10529182498.3475 +Armenia,ARM,2016,10547331235.1895 +Aruba,ABW,1994,1330167597.76536 +Aruba,ABW,1995,1320670391.06145 +Aruba,ABW,1996,1379888268.15642 +Aruba,ABW,1997,1531843575.41899 +Aruba,ABW,1998,1665363128.49162 +Aruba,ABW,1999,1722798882.68156 +Aruba,ABW,2000,1873452513.96648 +Aruba,ABW,2001,1920262569.8324 +Aruba,ABW,2002,1941094972.06704 +Aruba,ABW,2003,2021301675.97765 +Aruba,ABW,2004,2228279329.60894 +Aruba,ABW,2005,2331005586.59218 +Aruba,ABW,2006,2421474860.3352 +Aruba,ABW,2007,2623726256.98324 +Aruba,ABW,2008,2791960893.85475 +Aruba,ABW,2009,2498932960.89385 +Aruba,ABW,2010,2467703910.61453 +Aruba,ABW,2011,2584463687.15084 +Australia,AUS,1960,18593347519.3191 +Australia,AUS,1961,19666256019.7111 +Australia,AUS,1962,19911524246.8362 +Australia,AUS,1963,21527606674.8796 +Australia,AUS,1964,23787658192.4068 +Australia,AUS,1965,25962593795.4978 +Australia,AUS,1966,27288610146.713 +Australia,AUS,1967,30421099787.2102 +Australia,AUS,1968,32687870982.1929 +Australia,AUS,1969,36659200358.3828 +Australia,AUS,1970,41304737372.6061 +Australia,AUS,1971,45183111210.6619 +Australia,AUS,1972,52003504672.8972 +Australia,AUS,1973,63782190903.2671 +Australia,AUS,1974,88906411201.1791 +Australia,AUS,1975,97226546497.3372 +Australia,AUS,1976,104974158578.092 +Australia,AUS,1977,110266116081.67 +Australia,AUS,1978,118400631626.438 +Australia,AUS,1979,134793820288.538 +Australia,AUS,1980,149887465181.059 +Australia,AUS,1981,176804829908.278 +Australia,AUS,1982,193954540439.148 +Australia,AUS,1983,177176415535.798 +Australia,AUS,1984,193503894222.061 +Australia,AUS,1985,180470967247.066 +Australia,AUS,1986,182281757134.863 +Australia,AUS,1987,189375992588.671 +Australia,AUS,1988,236135768335.274 +Australia,AUS,1989,299941372852.374 +Australia,AUS,1990,311425933051.174 +Australia,AUS,1991,326068597441.331 +Australia,AUS,1992,325692532492.502 +Australia,AUS,1993,312372821810.006 +Australia,AUS,1994,323216879972.328 +Australia,AUS,1995,368391743391.743 +Australia,AUS,1996,401819423368.74 +Australia,AUS,1997,436097980904.68 +Australia,AUS,1998,399778878111.646 +Australia,AUS,1999,389146509587.668 +Australia,AUS,2000,415446209885.072 +Australia,AUS,2001,378899860470.108 +Australia,AUS,2002,394635830891.586 +Australia,AUS,2003,466853232382.062 +Australia,AUS,2004,613329776639.636 +Australia,AUS,2005,693764095624.718 +Australia,AUS,2006,747572626534.891 +Australia,AUS,2007,853764622752.61 +Australia,AUS,2008,1055334825425.25 +Australia,AUS,2009,927168310999.853 +Australia,AUS,2010,1142876772659.21 +Australia,AUS,2011,1390557034407.97 +Australia,AUS,2012,1538194473087.23 +Australia,AUS,2013,1567178619062.28 +Australia,AUS,2014,1459597906912.7 +Australia,AUS,2015,1345383143356.35 +Australia,AUS,2016,1204616439828.41 +Austria,AUT,1960,6592693841.18495 +Austria,AUT,1961,7311749633.36229 +Austria,AUT,1962,7756110210.11966 +Austria,AUT,1963,8374175257.73075 +Austria,AUT,1964,9169983885.71185 +Austria,AUT,1965,9994070615.85997 +Austria,AUT,1966,10887682273.1014 +Austria,AUT,1967,11579431668.9165 +Austria,AUT,1968,12440625312.8685 +Austria,AUT,1969,13582798556.2404 +Austria,AUT,1970,15335972267.7957 +Austria,AUT,1971,17815464919.0439 +Austria,AUT,1972,22006470861.3608 +Austria,AUT,1973,29444365310.2818 +Austria,AUT,1974,35104529078.3274 +Austria,AUT,1975,39962704274.3146 +Austria,AUT,1976,42856485617.8569 +Austria,AUT,1977,51421585629.8393 +Austria,AUT,1978,61902774945.5131 +Austria,AUT,1979,73759181883.685 +Austria,AUT,1980,81861232822.8037 +Austria,AUT,1981,70863106877.484 +Austria,AUT,1982,71103585383.5605 +Austria,AUT,1983,71947277233.032 +Austria,AUT,1984,67821568599.1335 +Austria,AUT,1985,69219621907.4222 +Austria,AUT,1986,98797587381.7035 +Austria,AUT,1987,123869321397.475 +Austria,AUT,1988,133018182770.534 +Austria,AUT,1989,132785154342.174 +Austria,AUT,1990,166062376739.683 +Austria,AUT,1991,173375508073.07 +Austria,AUT,1992,194608183696.469 +Austria,AUT,1993,189921096652.076 +Austria,AUT,1994,203044926876.28 +Austria,AUT,1995,240457622492.152 +Austria,AUT,1996,236720496490.772 +Austria,AUT,1997,212323463750.141 +Austria,AUT,1998,217683626056.025 +Austria,AUT,1999,216725261027.062 +Austria,AUT,2000,196421706283.398 +Austria,AUT,2001,196953628635.347 +Austria,AUT,2002,212970685111.989 +Austria,AUT,2003,260721478555.305 +Austria,AUT,2004,299857238639.185 +Austria,AUT,2005,314648986444.472 +Austria,AUT,2006,334309371471.584 +Austria,AUT,2007,386458951546.674 +Austria,AUT,2008,427611527757.434 +Austria,AUT,2009,397594276187.83 +Austria,AUT,2010,390211866490.257 +Austria,AUT,2011,429037361514.409 +Austria,AUT,2012,407451583084.239 +Austria,AUT,2013,428248420485.175 +Austria,AUT,2014,438376178526.317 +Austria,AUT,2015,376967406147.987 +Austria,AUT,2016,386427793787.215 +Azerbaijan,AZE,1990,8858006035.91566 +Azerbaijan,AZE,1991,8792365810.5094 +Azerbaijan,AZE,1992,4991350457.5425 +Azerbaijan,AZE,1993,3973027396.65195 +Azerbaijan,AZE,1994,3313739673.54738 +Azerbaijan,AZE,1995,3052467522.36104 +Azerbaijan,AZE,1996,3176749593.11788 +Azerbaijan,AZE,1997,3962710163.11167 +Azerbaijan,AZE,1998,4446396217.63265 +Azerbaijan,AZE,1999,4581222442.45783 +Azerbaijan,AZE,2000,5272617196.04517 +Azerbaijan,AZE,2001,5707720390.8515 +Azerbaijan,AZE,2002,6235795103.88809 +Azerbaijan,AZE,2003,7276013031.96905 +Azerbaijan,AZE,2004,8680472168.51531 +Azerbaijan,AZE,2005,13245716099.0057 +Azerbaijan,AZE,2006,20983019923.8863 +Azerbaijan,AZE,2007,33050343782.7759 +Azerbaijan,AZE,2008,48852482960.0779 +Azerbaijan,AZE,2009,44291490420.5026 +Azerbaijan,AZE,2010,52902703376.1056 +Azerbaijan,AZE,2011,65951627200.2026 +Azerbaijan,AZE,2012,69684317718.9409 +Azerbaijan,AZE,2013,74164435946.4627 +Azerbaijan,AZE,2014,75244166772.9185 +Azerbaijan,AZE,2015,53074370486.0433 +Azerbaijan,AZE,2016,37847715736.0406 +"Bahamas, The",BHS,1960,169803921.568627 +"Bahamas, The",BHS,1961,190098039.215686 +"Bahamas, The",BHS,1962,212254901.960784 +"Bahamas, The",BHS,1963,237745098.039216 +"Bahamas, The",BHS,1964,266666666.666667 +"Bahamas, The",BHS,1965,300392156.862745 +"Bahamas, The",BHS,1966,340000000 +"Bahamas, The",BHS,1967,390196078.431373 +"Bahamas, The",BHS,1968,444901960.784314 +"Bahamas, The",BHS,1969,528137254.901961 +"Bahamas, The",BHS,1970,538423153.692615 +"Bahamas, The",BHS,1971,573400000 +"Bahamas, The",BHS,1972,590900000 +"Bahamas, The",BHS,1973,670900000 +"Bahamas, The",BHS,1974,632400000 +"Bahamas, The",BHS,1975,596200000 +"Bahamas, The",BHS,1976,642100000 +"Bahamas, The",BHS,1977,713000000 +"Bahamas, The",BHS,1978,832400000 +"Bahamas, The",BHS,1979,1139800100 +"Bahamas, The",BHS,1980,1335300000 +"Bahamas, The",BHS,1981,1426500000 +"Bahamas, The",BHS,1982,1578300000 +"Bahamas, The",BHS,1983,1732800000 +"Bahamas, The",BHS,1984,2041100000 +"Bahamas, The",BHS,1985,2320699900 +"Bahamas, The",BHS,1986,2472500000 +"Bahamas, The",BHS,1987,2713999900 +"Bahamas, The",BHS,1988,2817900000 +"Bahamas, The",BHS,1989,3062000000 +"Bahamas, The",BHS,1990,3166000000 +"Bahamas, The",BHS,1991,3111160000 +"Bahamas, The",BHS,1992,3109000000 +"Bahamas, The",BHS,1993,3092000000 +"Bahamas, The",BHS,1994,3259000000 +"Bahamas, The",BHS,1995,3429000000 +"Bahamas, The",BHS,1996,3609000000 +"Bahamas, The",BHS,1997,4961119000 +"Bahamas, The",BHS,1998,5353524000 +"Bahamas, The",BHS,1999,6019972000 +"Bahamas, The",BHS,2000,6327552000 +"Bahamas, The",BHS,2001,6516651000 +"Bahamas, The",BHS,2002,6957996000 +"Bahamas, The",BHS,2003,6949317000 +"Bahamas, The",BHS,2004,7094413000 +"Bahamas, The",BHS,2005,7706222000 +"Bahamas, The",BHS,2006,7965588000 +"Bahamas, The",BHS,2007,8318996000 +"Bahamas, The",BHS,2008,8247000000 +"Bahamas, The",BHS,2009,7820000000 +"Bahamas, The",BHS,2010,7910000000 +"Bahamas, The",BHS,2011,7890000000 +"Bahamas, The",BHS,2012,8399000000 +"Bahamas, The",BHS,2013,8522000000 +"Bahamas, The",BHS,2014,8618000000 +"Bahamas, The",BHS,2015,8854000000 +"Bahamas, The",BHS,2016,9047000000 +Bahrain,BHR,1980,3072698328.46909 +Bahrain,BHR,1981,3467819148.93617 +Bahrain,BHR,1982,3645744680.85106 +Bahrain,BHR,1983,3735106382.97872 +Bahrain,BHR,1984,3905585106.38298 +Bahrain,BHR,1985,3651861702.12766 +Bahrain,BHR,1986,3052393617.02128 +Bahrain,BHR,1987,3392021010.6383 +Bahrain,BHR,1988,3702393617.02128 +Bahrain,BHR,1989,3863563829.78723 +Bahrain,BHR,1990,4229787234.04255 +Bahrain,BHR,1991,4616223404.25532 +Bahrain,BHR,1992,4751063829.78723 +Bahrain,BHR,1993,5200265957.44681 +Bahrain,BHR,1994,5567553457.44681 +Bahrain,BHR,1995,5849467819.14894 +Bahrain,BHR,1996,6101861436.17021 +Bahrain,BHR,1997,6349202393.61702 +Bahrain,BHR,1998,6183776595.74468 +Bahrain,BHR,1999,6621010372.34043 +Bahrain,BHR,2000,9062906914.89362 +Bahrain,BHR,2001,8976207712.76596 +Bahrain,BHR,2002,9632155053.19149 +Bahrain,BHR,2003,11074822074.4681 +Bahrain,BHR,2004,13150166755.3191 +Bahrain,BHR,2005,15968726861.7021 +Bahrain,BHR,2006,18505053191.4894 +Bahrain,BHR,2007,21730000000 +Bahrain,BHR,2008,25710877659.5745 +Bahrain,BHR,2009,22938218085.1064 +Bahrain,BHR,2010,25713271276.5957 +Bahrain,BHR,2011,29157446808.5106 +Bahrain,BHR,2012,30749308510.6383 +Bahrain,BHR,2013,32898670212.766 +Bahrain,BHR,2014,33387712765.9575 +Bahrain,BHR,2015,31125851063.8298 +Bahrain,BHR,2016,31858510638.2979 +Bangladesh,BGD,1960,4274893913.46432 +Bangladesh,BGD,1961,4817580183.56657 +Bangladesh,BGD,1962,5081413339.74945 +Bangladesh,BGD,1963,5319458351.12372 +Bangladesh,BGD,1964,5386054619.31076 +Bangladesh,BGD,1965,5906636556.95802 +Bangladesh,BGD,1966,6439687598.27648 +Bangladesh,BGD,1967,7253575399.26882 +Bangladesh,BGD,1968,7483685473.4584 +Bangladesh,BGD,1969,8471006100.89247 +Bangladesh,BGD,1970,8992721809.32801 +Bangladesh,BGD,1971,8751842839.73302 +Bangladesh,BGD,1972,6288245866.66667 +Bangladesh,BGD,1973,8086725729.3407 +Bangladesh,BGD,1974,12512460519.7088 +Bangladesh,BGD,1975,19448348073.4565 +Bangladesh,BGD,1976,10117113333.3333 +Bangladesh,BGD,1977,9651149301.8746 +Bangladesh,BGD,1978,13281767142.8571 +Bangladesh,BGD,1979,15565480321.9448 +Bangladesh,BGD,1980,18138049095.6072 +Bangladesh,BGD,1981,20249694002.448 +Bangladesh,BGD,1982,18525399201.5968 +Bangladesh,BGD,1983,17609048821.5488 +Bangladesh,BGD,1984,18920840000 +Bangladesh,BGD,1985,22278423076.9231 +Bangladesh,BGD,1986,21774033333.3333 +Bangladesh,BGD,1987,24298032258.0645 +Bangladesh,BGD,1988,26579005760.315 +Bangladesh,BGD,1989,28781714763.7823 +Bangladesh,BGD,1990,31598341233.5793 +Bangladesh,BGD,1991,30957483290.541 +Bangladesh,BGD,1992,31708873954.9405 +Bangladesh,BGD,1993,33166520084.8295 +Bangladesh,BGD,1994,33768662171.2233 +Bangladesh,BGD,1995,37939748051.3878 +Bangladesh,BGD,1996,46438482370.3942 +Bangladesh,BGD,1997,48244308274.8086 +Bangladesh,BGD,1998,49984559471.3656 +Bangladesh,BGD,1999,51270569883.5275 +Bangladesh,BGD,2000,53369787318.6245 +Bangladesh,BGD,2001,53991289844.3291 +Bangladesh,BGD,2002,54724081490.5102 +Bangladesh,BGD,2003,60158929188.2556 +Bangladesh,BGD,2004,65108544250.0425 +Bangladesh,BGD,2005,69442943089.4309 +Bangladesh,BGD,2006,71819083683.7403 +Bangladesh,BGD,2007,79611888213.148 +Bangladesh,BGD,2008,91631278239.3237 +Bangladesh,BGD,2009,102477791472.39 +Bangladesh,BGD,2010,115279077465.226 +Bangladesh,BGD,2011,128637938711.386 +Bangladesh,BGD,2012,133355749482.478 +Bangladesh,BGD,2013,149990451022.29 +Bangladesh,BGD,2014,172885454931.453 +Bangladesh,BGD,2015,195078561219.325 +Bangladesh,BGD,2016,221415280325.518 +Barbados,BRB,1980,1012264035.93834 +Barbados,BRB,1981,1114104641.92324 +Barbados,BRB,1982,1164059789.75045 +Barbados,BRB,1983,1235775210.81961 +Barbados,BRB,1984,1347033586.80082 +Barbados,BRB,1985,1409740726.1551 +Barbados,BRB,1986,1547790025.47984 +Barbados,BRB,1987,1704440882.93392 +Barbados,BRB,1988,1812891396.47311 +Barbados,BRB,1989,2006394477.47387 +Barbados,BRB,1990,2012302519.52124 +Barbados,BRB,1991,2020744653.61842 +Barbados,BRB,1992,1957168343.54374 +Barbados,BRB,1993,2063267995.73752 +Barbados,BRB,1994,2151285060.49141 +Barbados,BRB,1995,2261996995.18515 +Barbados,BRB,1996,2411846465.43502 +Barbados,BRB,1997,2549260575.07039 +Barbados,BRB,1998,2874413287.26735 +Barbados,BRB,1999,3012021852.2502 +Barbados,BRB,2000,3121619807.97687 +Barbados,BRB,2001,3116632412.98352 +Barbados,BRB,2002,3169612598.33334 +Barbados,BRB,2003,3274856571.16604 +Barbados,BRB,2004,3514370691.25563 +Barbados,BRB,2005,3897467233.85757 +Barbados,BRB,2006,4303275572.40197 +Barbados,BRB,2007,4546115388.40716 +Barbados,BRB,2008,4595264650 +Barbados,BRB,2009,4601250000 +Barbados,BRB,2010,4446800000 +Barbados,BRB,2011,4358900000 +Barbados,BRB,2012,4332150000 +Barbados,BRB,2013,4371200000 +Barbados,BRB,2014,4352700000 +Barbados,BRB,2015,4421800000 +Barbados,BRB,2016,4587550000 +Belarus,BLR,1990,21650000000 +Belarus,BLR,1991,17420000000 +Belarus,BLR,1992,17120370370.3704 +Belarus,BLR,1993,16286280991.7355 +Belarus,BLR,1994,14932192010.7419 +Belarus,BLR,1995,13972630803.5817 +Belarus,BLR,1996,14756861538.4615 +Belarus,BLR,1997,14128412417.193 +Belarus,BLR,1998,15222014828.3039 +Belarus,BLR,1999,12138485328.6267 +Belarus,BLR,2000,12736856827.9847 +Belarus,BLR,2001,12354820143.8849 +Belarus,BLR,2002,14594925392.9691 +Belarus,BLR,2003,17825436034.5366 +Belarus,BLR,2004,23141587717.7633 +Belarus,BLR,2005,30210091836.8294 +Belarus,BLR,2006,36961821893.6976 +Belarus,BLR,2007,45275747860.6442 +Belarus,BLR,2008,60752177438.8895 +Belarus,BLR,2009,49208656976.0389 +Belarus,BLR,2010,57231904542.8755 +Belarus,BLR,2011,61762341356.8996 +Belarus,BLR,2012,65685903833.0838 +Belarus,BLR,2013,75527537703.1836 +Belarus,BLR,2014,78813049849.6078 +Belarus,BLR,2015,56454775870.958 +Belarus,BLR,2016,47433442293.1858 +Belgium,BEL,1960,11658722590.99 +Belgium,BEL,1961,12400145221.595 +Belgium,BEL,1962,13264015675.3193 +Belgium,BEL,1963,14260017387.0492 +Belgium,BEL,1964,15960106680.6732 +Belgium,BEL,1965,17371457607.9374 +Belgium,BEL,1966,18651883472.4808 +Belgium,BEL,1967,19992040788.4593 +Belgium,BEL,1968,21376353113.475 +Belgium,BEL,1969,23710735894.7022 +Belgium,BEL,1970,26849173618.3945 +Belgium,BEL,1971,29981318394.8688 +Belgium,BEL,1972,37408626615.3423 +Belgium,BEL,1973,47999408507.5554 +Belgium,BEL,1974,56333063690.9693 +Belgium,BEL,1975,66029811341.45 +Belgium,BEL,1976,71494607105.5381 +Belgium,BEL,1977,83283407090.6021 +Belgium,BEL,1978,101788571282.183 +Belgium,BEL,1979,116938177352.779 +Belgium,BEL,1980,127508322941.095 +Belgium,BEL,1981,105290713602.781 +Belgium,BEL,1982,92588982518.0999 +Belgium,BEL,1983,87650998816.568 +Belgium,BEL,1984,83795759983.2449 +Belgium,BEL,1985,86730120728.3103 +Belgium,BEL,1986,120661334386.852 +Belgium,BEL,1987,150194219665.046 +Belgium,BEL,1988,163168007789.358 +Belgium,BEL,1989,165100250614.251 +Belgium,BEL,1990,206431036697.248 +Belgium,BEL,1991,211638016538.689 +Belgium,BEL,1992,236038607528.231 +Belgium,BEL,1993,225924893540.112 +Belgium,BEL,1994,246195171328.671 +Belgium,BEL,1995,289567597153.804 +Belgium,BEL,1996,281357654723.127 +Belgium,BEL,1997,254813937753.721 +Belgium,BEL,1998,260602356079.129 +Belgium,BEL,1999,260202429149.798 +Belgium,BEL,2000,237904919845.218 +Belgium,BEL,2001,237841610738.255 +Belgium,BEL,2002,258860342555.995 +Belgium,BEL,2003,319003386004.515 +Belgium,BEL,2004,370885274397.815 +Belgium,BEL,2005,387365999253.824 +Belgium,BEL,2006,409813072387.404 +Belgium,BEL,2007,471821790309.335 +Belgium,BEL,2008,518626043650.212 +Belgium,BEL,2009,484552653514.865 +Belgium,BEL,2010,483548693409.337 +Belgium,BEL,2011,527008036846.811 +Belgium,BEL,2012,497884088082.651 +Belgium,BEL,2013,520117140495.059 +Belgium,BEL,2014,531750903174.686 +Belgium,BEL,2015,454991318727.907 +Belgium,BEL,2016,466365727353.85 +Belize,BLZ,1960,28071888.5622288 +Belize,BLZ,1961,29964370.7125857 +Belize,BLZ,1962,31856922.8615428 +Belize,BLZ,1963,33749405.0118998 +Belize,BLZ,1964,36193826.1234775 +Belize,BLZ,1965,40069930.0699301 +Belize,BLZ,1966,44405594.4055944 +Belize,BLZ,1967,47379310.3448276 +Belize,BLZ,1968,44910179.6407186 +Belize,BLZ,1969,47305389.2215569 +Belize,BLZ,1970,53233532.9341317 +Belize,BLZ,1971,59207317.0731707 +Belize,BLZ,1972,66062500 +Belize,BLZ,1973,78343558.2822086 +Belize,BLZ,1974,103216374.269006 +Belize,BLZ,1975,118066298.342541 +Belize,BLZ,1976,96905829.5964126 +Belize,BLZ,1977,117650000 +Belize,BLZ,1978,136300000 +Belize,BLZ,1979,151800000 +Belize,BLZ,1980,194750000 +Belize,BLZ,1981,192900000 +Belize,BLZ,1982,179250000 +Belize,BLZ,1983,189000000 +Belize,BLZ,1984,210900000 +Belize,BLZ,1985,209150000 +Belize,BLZ,1986,227850000 +Belize,BLZ,1987,276550000 +Belize,BLZ,1988,314900000 +Belize,BLZ,1989,363150000 +Belize,BLZ,1990,413050000 +Belize,BLZ,1991,444720750 +Belize,BLZ,1992,518239100 +Belize,BLZ,1993,559858250 +Belize,BLZ,1994,580863700 +Belize,BLZ,1995,620140400 +Belize,BLZ,1996,641383800 +Belize,BLZ,1997,654314350 +Belize,BLZ,1998,688992450 +Belize,BLZ,1999,732732350 +Belize,BLZ,2000,832072450 +Belize,BLZ,2001,871860600 +Belize,BLZ,2002,932551850 +Belize,BLZ,2003,990374050 +Belize,BLZ,2004,1057845500 +Belize,BLZ,2005,1114222550 +Belize,BLZ,2006,1217467600 +Belize,BLZ,2007,1290573400 +Belize,BLZ,2008,1368625150 +Belize,BLZ,2009,1336957250 +Belize,BLZ,2010,1397113450 +Belize,BLZ,2011,1486712300 +Belize,BLZ,2012,1573618750 +Belize,BLZ,2013,1613705850 +Belize,BLZ,2014,1706497950 +Belize,BLZ,2015,1742545900 +Belize,BLZ,2016,1765256400 +Benin,BEN,1960,226195579.35701 +Benin,BEN,1961,235668222.429984 +Benin,BEN,1962,236434906.75427 +Benin,BEN,1963,253927646.475909 +Benin,BEN,1964,269818988.259263 +Benin,BEN,1965,289908720.648622 +Benin,BEN,1966,302925280.773564 +Benin,BEN,1967,306222000.407316 +Benin,BEN,1968,326323097.355964 +Benin,BEN,1969,330748211.459737 +Benin,BEN,1970,333627758.154666 +Benin,BEN,1971,335072975.215766 +Benin,BEN,1972,410331900.950531 +Benin,BEN,1973,504376035.716401 +Benin,BEN,1974,554654786.965107 +Benin,BEN,1975,676870140.341529 +Benin,BEN,1976,698408244.385343 +Benin,BEN,1977,750049739.152238 +Benin,BEN,1978,928843304.783965 +Benin,BEN,1979,1186231265.18417 +Benin,BEN,1980,1405251547.23882 +Benin,BEN,1981,1291119965.11262 +Benin,BEN,1982,1267778489.03079 +Benin,BEN,1983,1095348302.91865 +Benin,BEN,1984,1051133927.00009 +Benin,BEN,1985,1045712703.02696 +Benin,BEN,1986,1336102040.71025 +Benin,BEN,1987,1562412030.34838 +Benin,BEN,1988,1620246187.15171 +Benin,BEN,1989,1502294411.46202 +Benin,BEN,1990,1959965243.76269 +Benin,BEN,1991,1986437859.90346 +Benin,BEN,1992,1695315305.70308 +Benin,BEN,1993,2274557914.07481 +Benin,BEN,1994,1598075932.35432 +Benin,BEN,1995,2169627250.93379 +Benin,BEN,1996,2361116587.86079 +Benin,BEN,1997,2268301537.65128 +Benin,BEN,1998,2455092582.30927 +Benin,BEN,1999,2689787917.50711 +Benin,BEN,2000,2569186642.86999 +Benin,BEN,2001,2680213931.46472 +Benin,BEN,2002,3054571081.6912 +Benin,BEN,2003,3905366187.87017 +Benin,BEN,2004,4521424807.22519 +Benin,BEN,2005,4803702821.08055 +Benin,BEN,2006,5142380779.44103 +Benin,BEN,2007,5969535131.58016 +Benin,BEN,2008,7132787396.66547 +Benin,BEN,2009,7097198711.61023 +Benin,BEN,2010,6970240895.49888 +Benin,BEN,2011,7814081155.64988 +Benin,BEN,2012,8152554487.31321 +Benin,BEN,2013,9156748441.42175 +Benin,BEN,2014,9707432015.61441 +Benin,BEN,2015,8290986804.45245 +Benin,BEN,2016,8583031398.21675 +Bermuda,BMU,1960,84466654.0801544 +Bermuda,BMU,1961,89249986.7007156 +Bermuda,BMU,1962,94149985.9705588 +Bermuda,BMU,1963,96366652.3069165 +Bermuda,BMU,1964,107566650.637987 +Bermuda,BMU,1965,114339048.962736 +Bermuda,BMU,1966,134173373.782802 +Bermuda,BMU,1967,155102984.621576 +Bermuda,BMU,1968,150000000 +Bermuda,BMU,1969,164900000 +Bermuda,BMU,1970,186300000 +Bermuda,BMU,1971,211100000 +Bermuda,BMU,1972,235400000 +Bermuda,BMU,1973,269500000 +Bermuda,BMU,1974,312600000 +Bermuda,BMU,1975,345000000 +Bermuda,BMU,1976,386300000 +Bermuda,BMU,1977,447000000 +Bermuda,BMU,1978,475800000 +Bermuda,BMU,1979,517200000 +Bermuda,BMU,1980,613299968 +Bermuda,BMU,1981,739100032 +Bermuda,BMU,1982,785500032 +Bermuda,BMU,1983,889400000 +Bermuda,BMU,1984,985699968 +Bermuda,BMU,1985,1039500032 +Bermuda,BMU,1986,1173500032 +Bermuda,BMU,1987,1296499968 +Bermuda,BMU,1988,1415100032 +Bermuda,BMU,1989,1501500032 +Bermuda,BMU,1990,1592400000 +Bermuda,BMU,1991,1634899968 +Bermuda,BMU,1992,1679900032 +Bermuda,BMU,1993,1820359936 +Bermuda,BMU,1994,1867160064 +Bermuda,BMU,1995,2030749952 +Bermuda,BMU,1996,2695390000 +Bermuda,BMU,1997,2932827000 +Bermuda,BMU,1998,3130748000 +Bermuda,BMU,1999,3324433000 +Bermuda,BMU,2000,3480219000 +Bermuda,BMU,2001,3680483000 +Bermuda,BMU,2002,3937228000 +Bermuda,BMU,2003,4186525000 +Bermuda,BMU,2004,4484703000 +Bermuda,BMU,2005,4868136000 +Bermuda,BMU,2006,5414299000 +Bermuda,BMU,2007,5895048000 +Bermuda,BMU,2008,6109928000 +Bermuda,BMU,2009,5806378000 +Bermuda,BMU,2010,5744414000 +Bermuda,BMU,2011,5550771000 +Bermuda,BMU,2012,5537537000 +Bermuda,BMU,2013,5573710000 +Bhutan,BTN,1980,135653295.165394 +Bhutan,BTN,1981,146391639.722864 +Bhutan,BTN,1982,148934334.038055 +Bhutan,BTN,1983,165585940.594059 +Bhutan,BTN,1984,169264991.197183 +Bhutan,BTN,1985,172217502.021019 +Bhutan,BTN,1986,201375725.614592 +Bhutan,BTN,1987,253182453.703704 +Bhutan,BTN,1988,283855833.333333 +Bhutan,BTN,1989,275949889.09427 +Bhutan,BTN,1990,299787275.842376 +Bhutan,BTN,1991,250045839.929639 +Bhutan,BTN,1992,250794359.567901 +Bhutan,BTN,1993,235239570.350935 +Bhutan,BTN,1994,270801565.189672 +Bhutan,BTN,1995,303053462.843047 +Bhutan,BTN,1996,316420860.852385 +Bhutan,BTN,1997,365964500.137703 +Bhutan,BTN,1998,376955087.251575 +Bhutan,BTN,1999,419035810.496981 +Bhutan,BTN,2000,439158233.199822 +Bhutan,BTN,2001,476360697.181606 +Bhutan,BTN,2002,537050133.717342 +Bhutan,BTN,2003,622026107.771576 +Bhutan,BTN,2004,702682018.976169 +Bhutan,BTN,2005,818869145.124717 +Bhutan,BTN,2006,897731524.929922 +Bhutan,BTN,2007,1196091805.02316 +Bhutan,BTN,2008,1258332337.28382 +Bhutan,BTN,2009,1264758197.96593 +Bhutan,BTN,2010,1585472534.10547 +Bhutan,BTN,2011,1820207625.80217 +Bhutan,BTN,2012,1823692109.61652 +Bhutan,BTN,2013,1798333725.83954 +Bhutan,BTN,2014,1958819914.95916 +Bhutan,BTN,2015,2057947621.19283 +Bhutan,BTN,2016,2236933237.89015 +Bolivia,BOL,1960,563110051.920733 +Bolivia,BOL,1961,612518906.826491 +Bolivia,BOL,1962,669722541.277818 +Bolivia,BOL,1963,721142957.311474 +Bolivia,BOL,1964,812543072.505384 +Bolivia,BOL,1965,908874537.037037 +Bolivia,BOL,1966,994044553.872054 +Bolivia,BOL,1967,1084059814.81481 +Bolivia,BOL,1968,908874537.037037 +Bolivia,BOL,1969,964615698.653199 +Bolivia,BOL,1970,1017171717.17172 +Bolivia,BOL,1971,1095454545.45455 +Bolivia,BOL,1972,1257615644.97932 +Bolivia,BOL,1973,1263018490.75462 +Bolivia,BOL,1974,2100249875.06247 +Bolivia,BOL,1975,2404697651.17441 +Bolivia,BOL,1976,2732083958.02099 +Bolivia,BOL,1977,3227436281.85907 +Bolivia,BOL,1978,3758220889.55522 +Bolivia,BOL,1979,4421343606.18135 +Bolivia,BOL,1980,4537487842.57749 +Bolivia,BOL,1981,5891606676.18271 +Bolivia,BOL,1982,5594118400.16731 +Bolivia,BOL,1983,5422656261.71049 +Bolivia,BOL,1984,6169481549.37482 +Bolivia,BOL,1985,5377277406.71638 +Bolivia,BOL,1986,3959379487.6064 +Bolivia,BOL,1987,4347956298.51327 +Bolivia,BOL,1988,4597615562.66594 +Bolivia,BOL,1989,4715978868.21613 +Bolivia,BOL,1990,4867582620.20708 +Bolivia,BOL,1991,5343274311.56789 +Bolivia,BOL,1992,5643893347.00679 +Bolivia,BOL,1993,5734676560.92471 +Bolivia,BOL,1994,5981244886.917 +Bolivia,BOL,1995,6715220507.05164 +Bolivia,BOL,1996,7396966657.47054 +Bolivia,BOL,1997,7925673448.41368 +Bolivia,BOL,1998,8497545598.08352 +Bolivia,BOL,1999,8285075872.27307 +Bolivia,BOL,2000,8397912509.09679 +Bolivia,BOL,2001,8141537937.61068 +Bolivia,BOL,2002,7905485216.17852 +Bolivia,BOL,2003,8082364868.39357 +Bolivia,BOL,2004,8773451738.91129 +Bolivia,BOL,2005,9549077869.10651 +Bolivia,BOL,2006,11451869164.7112 +Bolivia,BOL,2007,13120159975.5451 +Bolivia,BOL,2008,16674324634.2373 +Bolivia,BOL,2009,17339992165.2422 +Bolivia,BOL,2010,19649631308.1648 +Bolivia,BOL,2011,23963033443.8518 +Bolivia,BOL,2012,27084497539.7974 +Bolivia,BOL,2013,30659338929.0883 +Bolivia,BOL,2014,32996187988.4226 +Bolivia,BOL,2015,33000198263.3864 +Bolivia,BOL,2016,33806395513.7482 +Bosnia and Herzegovina,BIH,1994,1255802469.1358 +Bosnia and Herzegovina,BIH,1995,1866572953.73666 +Bosnia and Herzegovina,BIH,1996,2786045321.63743 +Bosnia and Herzegovina,BIH,1997,3671816504.23851 +Bosnia and Herzegovina,BIH,1998,4116699437.4041 +Bosnia and Herzegovina,BIH,1999,4685729738.56209 +Bosnia and Herzegovina,BIH,2000,5505984455.95855 +Bosnia and Herzegovina,BIH,2001,5748990666.17862 +Bosnia and Herzegovina,BIH,2002,6651226179.01829 +Bosnia and Herzegovina,BIH,2003,8370020196.19158 +Bosnia and Herzegovina,BIH,2004,10022840634.9206 +Bosnia and Herzegovina,BIH,2005,11225138297.1959 +Bosnia and Herzegovina,BIH,2006,12866524918.2221 +Bosnia and Herzegovina,BIH,2007,15776422673.198 +Bosnia and Herzegovina,BIH,2008,19101454463.7507 +Bosnia and Herzegovina,BIH,2009,17600630726.6141 +Bosnia and Herzegovina,BIH,2010,17164279813.0968 +Bosnia and Herzegovina,BIH,2011,18629346790.8167 +Bosnia and Herzegovina,BIH,2012,17207367625.8048 +Bosnia and Herzegovina,BIH,2013,18154290272.2151 +Bosnia and Herzegovina,BIH,2014,18521475376.4754 +Bosnia and Herzegovina,BIH,2015,16173806634.5336 +Bosnia and Herzegovina,BIH,2016,16559695718.568 +Botswana,BWA,1960,30412308.9864012 +Botswana,BWA,1961,32902336.644746 +Botswana,BWA,1962,35643207.6265246 +Botswana,BWA,1963,38091150.566196 +Botswana,BWA,1964,41613969.0506064 +Botswana,BWA,1965,45790869.7473126 +Botswana,BWA,1966,51464435.1464435 +Botswana,BWA,1967,58646443.5146444 +Botswana,BWA,1968,66248256.6248257 +Botswana,BWA,1969,77356914.0788191 +Botswana,BWA,1970,96245114.4611949 +Botswana,BWA,1971,126957494.407159 +Botswana,BWA,1972,165258093.875959 +Botswana,BWA,1973,244129088.027662 +Botswana,BWA,1974,306033848.417954 +Botswana,BWA,1975,355172413.793103 +Botswana,BWA,1976,372010119.595216 +Botswana,BWA,1977,451603325.415677 +Botswana,BWA,1978,590376720.598889 +Botswana,BWA,1979,819877300.613497 +Botswana,BWA,1980,1060923829.13021 +Botswana,BWA,1981,1073861599.13948 +Botswana,BWA,1982,1014907254.54016 +Botswana,BWA,1983,1172258182.14969 +Botswana,BWA,1984,1240796364.75662 +Botswana,BWA,1985,1114764007.14811 +Botswana,BWA,1986,1392634771.9653 +Botswana,BWA,1987,1965274882.36345 +Botswana,BWA,1988,2644536804.11244 +Botswana,BWA,1989,3083800684.89751 +Botswana,BWA,1990,3790567051.86778 +Botswana,BWA,1991,3942792837.35655 +Botswana,BWA,1992,4146513722.33019 +Botswana,BWA,1993,4160086253.1468 +Botswana,BWA,1994,4259330999.03151 +Botswana,BWA,1995,4730611067.02258 +Botswana,BWA,1996,4847752842.78924 +Botswana,BWA,1997,5020214747.45261 +Botswana,BWA,1998,4790458837.17078 +Botswana,BWA,1999,5484257417.17844 +Botswana,BWA,2000,5788329609.15755 +Botswana,BWA,2001,5489608299.66445 +Botswana,BWA,2002,5438857106.73536 +Botswana,BWA,2003,7511582173.37724 +Botswana,BWA,2004,8957467706.5354 +Botswana,BWA,2005,9931134940.51346 +Botswana,BWA,2006,10126940513.3125 +Botswana,BWA,2007,10939053367.1521 +Botswana,BWA,2008,10945070441.9283 +Botswana,BWA,2009,10267133177.7334 +Botswana,BWA,2010,12786654365.8738 +Botswana,BWA,2011,15682926890.0326 +Botswana,BWA,2012,14686278713.5189 +Botswana,BWA,2013,14915780533.086 +Botswana,BWA,2014,16259445049.7698 +Botswana,BWA,2015,14430608463.4399 +Botswana,BWA,2016,15274861068.209 +Brazil,BRA,1960,15165569912.5199 +Brazil,BRA,1961,15236854859.469 +Brazil,BRA,1962,19926293839.0163 +Brazil,BRA,1963,23021477292.2093 +Brazil,BRA,1964,21211892259.9904 +Brazil,BRA,1965,21790035117.19 +Brazil,BRA,1966,27062716577.9111 +Brazil,BRA,1967,30591834053.9653 +Brazil,BRA,1968,33875881876.3672 +Brazil,BRA,1969,37458898243.8609 +Brazil,BRA,1970,42327600098.2412 +Brazil,BRA,1971,49204456700.4516 +Brazil,BRA,1972,58539008786.3684 +Brazil,BRA,1973,79279057730.829 +Brazil,BRA,1974,105136007528.76 +Brazil,BRA,1975,123709376567.89 +Brazil,BRA,1976,152678020452.829 +Brazil,BRA,1977,176171284311.761 +Brazil,BRA,1978,200800891870.164 +Brazil,BRA,1979,224969488835.181 +Brazil,BRA,1980,235024598983.261 +Brazil,BRA,1981,263561088977.129 +Brazil,BRA,1982,281682304161.041 +Brazil,BRA,1983,203304515490.795 +Brazil,BRA,1984,209023912696.839 +Brazil,BRA,1985,222942790435.299 +Brazil,BRA,1986,268137224729.722 +Brazil,BRA,1987,294084112392.66 +Brazil,BRA,1988,330397381998.489 +Brazil,BRA,1989,425595310000 +Brazil,BRA,1990,461951782000 +Brazil,BRA,1991,602860000000 +Brazil,BRA,1992,400599250000 +Brazil,BRA,1993,437798577639.752 +Brazil,BRA,1994,558111997497.263 +Brazil,BRA,1995,785643456467.255 +Brazil,BRA,1996,850425828275.793 +Brazil,BRA,1997,883199443413.729 +Brazil,BRA,1998,863723395088.324 +Brazil,BRA,1999,599388879704.634 +Brazil,BRA,2000,655421153320.579 +Brazil,BRA,2001,559372502338.237 +Brazil,BRA,2002,507962741819.919 +Brazil,BRA,2003,558320116997.075 +Brazil,BRA,2004,669316239316.239 +Brazil,BRA,2005,891629970423.924 +Brazil,BRA,2006,1107640325472.35 +Brazil,BRA,2007,1397084381901.29 +Brazil,BRA,2008,1695824517395.57 +Brazil,BRA,2009,1667019605881.76 +Brazil,BRA,2010,2208871646202.82 +Brazil,BRA,2011,2616201578192.25 +Brazil,BRA,2012,2465188674415.03 +Brazil,BRA,2013,2472806919901.67 +Brazil,BRA,2014,2455993200170 +Brazil,BRA,2015,1803652649613.75 +Brazil,BRA,2016,1796186586414.45 +Brunei Darussalam,BRN,1965,114040245.655299 +Brunei Darussalam,BRN,1966,132758395.400497 +Brunei Darussalam,BRN,1967,139030445.576898 +Brunei Darussalam,BRN,1968,160819286.554292 +Brunei Darussalam,BRN,1969,161211289.690318 +Brunei Darussalam,BRN,1970,179080099.307461 +Brunei Darussalam,BRN,1971,197523179.241883 +Brunei Darussalam,BRN,1972,270818555.823521 +Brunei Darussalam,BRN,1973,433092003.579273 +Brunei Darussalam,BRN,1974,1073577085.64159 +Brunei Darussalam,BRN,1975,1168304305.65513 +Brunei Darussalam,BRN,1976,1423061356.64562 +Brunei Darussalam,BRN,1977,1732721160.94122 +Brunei Darussalam,BRN,1978,1941600703.60598 +Brunei Darussalam,BRN,1979,2803780005.51826 +Brunei Darussalam,BRN,1980,4928824957.9675 +Brunei Darussalam,BRN,1981,4366213849.57637 +Brunei Darussalam,BRN,1982,4264252336.4486 +Brunei Darussalam,BRN,1983,3844723142.45149 +Brunei Darussalam,BRN,1984,3782523088.4628 +Brunei Darussalam,BRN,1985,3523612563.06532 +Brunei Darussalam,BRN,1986,2358592817.12134 +Brunei Darussalam,BRN,1987,2754463437.79677 +Brunei Darussalam,BRN,1988,2690717551.18267 +Brunei Darussalam,BRN,1989,2985467979.28524 +Brunei Darussalam,BRN,1990,3520551724.13793 +Brunei Darussalam,BRN,1991,3701667052.55846 +Brunei Darussalam,BRN,1992,4183548189.07305 +Brunei Darussalam,BRN,1993,4105706151.75145 +Brunei Darussalam,BRN,1994,4087337959.93191 +Brunei Darussalam,BRN,1995,4734020036.68689 +Brunei Darussalam,BRN,1996,5115602836.87943 +Brunei Darussalam,BRN,1997,5197332974.13793 +Brunei Darussalam,BRN,1998,4051147227.53346 +Brunei Darussalam,BRN,1999,4600000000 +Brunei Darussalam,BRN,2000,6001153306.2645 +Brunei Darussalam,BRN,2001,5601090584.36122 +Brunei Darussalam,BRN,2002,5843329107.56171 +Brunei Darussalam,BRN,2003,6557333084.60567 +Brunei Darussalam,BRN,2004,7872333215.00414 +Brunei Darussalam,BRN,2005,9531402847.87311 +Brunei Darussalam,BRN,2006,11470703002.0769 +Brunei Darussalam,BRN,2007,12247694247.2298 +Brunei Darussalam,BRN,2008,14393099068.5859 +Brunei Darussalam,BRN,2009,10732366286.2643 +Brunei Darussalam,BRN,2010,13707370737.0737 +Brunei Darussalam,BRN,2011,18525319977.7407 +Brunei Darussalam,BRN,2012,19048495518.5659 +Brunei Darussalam,BRN,2013,18093829923.2737 +Brunei Darussalam,BRN,2014,17123125493.2912 +Brunei Darussalam,BRN,2015,12930394937.8137 +Brunei Darussalam,BRN,2016,11400266877.3769 +Bulgaria,BGR,1980,19839230769.2308 +Bulgaria,BGR,1981,19870000000 +Bulgaria,BGR,1982,19342000000 +Bulgaria,BGR,1983,16563666666.6667 +Bulgaria,BGR,1984,17594944444.4444 +Bulgaria,BGR,1985,17155421052.6316 +Bulgaria,BGR,1986,20249294117.6471 +Bulgaria,BGR,1987,28101000000 +Bulgaria,BGR,1988,22555941176.4706 +Bulgaria,BGR,1989,21988444444.4444 +Bulgaria,BGR,1990,20632090909.0909 +Bulgaria,BGR,1991,10943548387.0968 +Bulgaria,BGR,1992,10350515463.9175 +Bulgaria,BGR,1993,10829710144.9275 +Bulgaria,BGR,1994,9697416974.16974 +Bulgaria,BGR,1995,13063422619.0476 +Bulgaria,BGR,1996,10109404159.6402 +Bulgaria,BGR,1997,11195630536.8928 +Bulgaria,BGR,1998,14630974778.4594 +Bulgaria,BGR,1999,13495062850.302 +Bulgaria,BGR,2000,13148099185.2305 +Bulgaria,BGR,2001,14135393875.5893 +Bulgaria,BGR,2002,16360346653.8276 +Bulgaria,BGR,2003,21074775206.3254 +Bulgaria,BGR,2004,26094622563.6468 +Bulgaria,BGR,2005,29821662537.3229 +Bulgaria,BGR,2006,34304448149.8108 +Bulgaria,BGR,2007,44765733379.986 +Bulgaria,BGR,2008,54666642734.2757 +Bulgaria,BGR,2009,51783454183.5501 +Bulgaria,BGR,2010,50610031135.7791 +Bulgaria,BGR,2011,57418391041.5926 +Bulgaria,BGR,2012,53903028252.2996 +Bulgaria,BGR,2013,55758744571.1183 +Bulgaria,BGR,2014,56732006512.0065 +Bulgaria,BGR,2015,50199117547.0415 +Bulgaria,BGR,2016,52395164027.1493 +Burkina Faso,BFA,1960,330442817.168859 +Burkina Faso,BFA,1961,350247237.11684 +Burkina Faso,BFA,1962,379567178.256898 +Burkina Faso,BFA,1963,394040749.12567 +Burkina Faso,BFA,1964,410321785.631059 +Burkina Faso,BFA,1965,422916848.424208 +Burkina Faso,BFA,1966,433889831.584706 +Burkina Faso,BFA,1967,450753993.176448 +Burkina Faso,BFA,1968,460442864.205949 +Burkina Faso,BFA,1969,478298781.545658 +Burkina Faso,BFA,1970,458404330.125096 +Burkina Faso,BFA,1971,482411278.982439 +Burkina Faso,BFA,1972,578595583.975723 +Burkina Faso,BFA,1973,674773821.151416 +Burkina Faso,BFA,1974,751133642.647461 +Burkina Faso,BFA,1975,939972703.463021 +Burkina Faso,BFA,1976,976547572.215824 +Burkina Faso,BFA,1977,1131225278.77773 +Burkina Faso,BFA,1978,1475584037.28156 +Burkina Faso,BFA,1979,1748480982.18517 +Burkina Faso,BFA,1980,1928720390.28869 +Burkina Faso,BFA,1981,1775842679.94056 +Burkina Faso,BFA,1982,1754450379.2077 +Burkina Faso,BFA,1983,1600278756.43589 +Burkina Faso,BFA,1984,1459880352.6483 +Burkina Faso,BFA,1985,1552493413.98989 +Burkina Faso,BFA,1986,2036303381.20142 +Burkina Faso,BFA,1987,2369835438.62393 +Burkina Faso,BFA,1988,2616040645.87263 +Burkina Faso,BFA,1989,2615588545.68629 +Burkina Faso,BFA,1990,3101301780.95067 +Burkina Faso,BFA,1991,3135045684.1006 +Burkina Faso,BFA,1992,2240264711.54816 +Burkina Faso,BFA,1993,2332018010.55341 +Burkina Faso,BFA,1994,1895290964.80829 +Burkina Faso,BFA,1995,2379518099.2266 +Burkina Faso,BFA,1996,2586550747.09844 +Burkina Faso,BFA,1997,2447669403.89018 +Burkina Faso,BFA,1998,2804902723.73145 +Burkina Faso,BFA,1999,2993753187.09677 +Burkina Faso,BFA,2000,2628920056.10098 +Burkina Faso,BFA,2001,2812845513.57125 +Burkina Faso,BFA,2002,3205592289.79773 +Burkina Faso,BFA,2003,4205691222.11396 +Burkina Faso,BFA,2004,4838551099.70985 +Burkina Faso,BFA,2005,5462709498.45119 +Burkina Faso,BFA,2006,5844669845.53733 +Burkina Faso,BFA,2007,6771277870.96412 +Burkina Faso,BFA,2008,8369637065.40255 +Burkina Faso,BFA,2009,8369175126.25316 +Burkina Faso,BFA,2010,8979966766.07232 +Burkina Faso,BFA,2011,10724061338.5874 +Burkina Faso,BFA,2012,11166061507.8024 +Burkina Faso,BFA,2013,11934606509.5944 +Burkina Faso,BFA,2014,12400688571.1396 +Burkina Faso,BFA,2015,11148759324.507 +Burkina Faso,BFA,2016,12115155931.5461 +Burundi,BDI,1960,195999990 +Burundi,BDI,1961,202999992 +Burundi,BDI,1962,213500006 +Burundi,BDI,1963,232749998 +Burundi,BDI,1964,260750008 +Burundi,BDI,1965,158994962.962963 +Burundi,BDI,1966,165444571.428571 +Burundi,BDI,1967,178297142.857143 +Burundi,BDI,1968,183200000 +Burundi,BDI,1969,190205714.285714 +Burundi,BDI,1970,242732571.428571 +Burundi,BDI,1971,252842285.714286 +Burundi,BDI,1972,246804571.428571 +Burundi,BDI,1973,304339839.552146 +Burundi,BDI,1974,345263492.063492 +Burundi,BDI,1975,420986666.666667 +Burundi,BDI,1976,448412753.623188 +Burundi,BDI,1977,547535555.555556 +Burundi,BDI,1978,610225555.555556 +Burundi,BDI,1979,782496666.666667 +Burundi,BDI,1980,919726666.666667 +Burundi,BDI,1981,969046666.666667 +Burundi,BDI,1982,1013222222.22222 +Burundi,BDI,1983,1082926304.46477 +Burundi,BDI,1984,987143931.166987 +Burundi,BDI,1985,1149979285.77347 +Burundi,BDI,1986,1201725497.06578 +Burundi,BDI,1987,1131466494.01101 +Burundi,BDI,1988,1082403219.48787 +Burundi,BDI,1989,1113924130.41149 +Burundi,BDI,1990,1132101252.51817 +Burundi,BDI,1991,1167398478.3459 +Burundi,BDI,1992,1083037670.60484 +Burundi,BDI,1993,938632612.026359 +Burundi,BDI,1994,925030590.153683 +Burundi,BDI,1995,1000428393.88528 +Burundi,BDI,1996,869033856.317093 +Burundi,BDI,1997,972896267.915425 +Burundi,BDI,1998,893770806.077641 +Burundi,BDI,1999,808077223.365746 +Burundi,BDI,2000,870486065.883137 +Burundi,BDI,2001,876794723.068586 +Burundi,BDI,2002,825394490.159111 +Burundi,BDI,2003,784654423.620476 +Burundi,BDI,2004,915257323.3961 +Burundi,BDI,2005,1117257279.46188 +Burundi,BDI,2006,1273180597.02711 +Burundi,BDI,2007,1356078278.18821 +Burundi,BDI,2008,1611634331.64869 +Burundi,BDI,2009,1739781488.7457 +Burundi,BDI,2010,2026864469.36388 +Burundi,BDI,2011,2355652125.85184 +Burundi,BDI,2012,2472384906.99794 +Burundi,BDI,2013,2714505634.52629 +Burundi,BDI,2014,3093647226.8107 +Burundi,BDI,2015,3097324739.8493 +Burundi,BDI,2016,3007029030.39976 +Cabo Verde,CPV,1980,142246875.536716 +Cabo Verde,CPV,1981,139468114.599741 +Cabo Verde,CPV,1982,140630758.594899 +Cabo Verde,CPV,1983,138476239.366792 +Cabo Verde,CPV,1984,132019065.033419 +Cabo Verde,CPV,1985,137728155.212661 +Cabo Verde,CPV,1986,190651207.999511 +Cabo Verde,CPV,1987,235253171.841062 +Cabo Verde,CPV,1988,264308140.285149 +Cabo Verde,CPV,1989,267448513.108168 +Cabo Verde,CPV,1990,306891107.262039 +Cabo Verde,CPV,1991,319827058.592875 +Cabo Verde,CPV,1992,357160985.327413 +Cabo Verde,CPV,1993,490417389.682569 +Cabo Verde,CPV,1994,406580652.330537 +Cabo Verde,CPV,1995,487148993.533109 +Cabo Verde,CPV,1996,501979069.274683 +Cabo Verde,CPV,1997,490608657.924976 +Cabo Verde,CPV,1998,521910560.524868 +Cabo Verde,CPV,1999,592416703.058878 +Cabo Verde,CPV,2000,539227277.626411 +Cabo Verde,CPV,2001,563024383.296626 +Cabo Verde,CPV,2002,620974660.230303 +Cabo Verde,CPV,2003,813963830.179217 +Cabo Verde,CPV,2004,924318490.7598 +Cabo Verde,CPV,2005,971977088.156914 +Cabo Verde,CPV,2006,1107891063.43863 +Cabo Verde,CPV,2007,1513934037.24782 +Cabo Verde,CPV,2008,1789333748.6799 +Cabo Verde,CPV,2009,1711817181.52969 +Cabo Verde,CPV,2010,1664310769.55229 +Cabo Verde,CPV,2011,1864824080.69256 +Cabo Verde,CPV,2012,1751888561.72747 +Cabo Verde,CPV,2013,1850951315.45564 +Cabo Verde,CPV,2014,1858121723.24993 +Cabo Verde,CPV,2015,1574288667.69981 +Cabo Verde,CPV,2016,1617467435.77007 +Cambodia,KHM,1960,637142865.714286 +Cambodia,KHM,1961,642857134.285714 +Cambodia,KHM,1962,660000008.571429 +Cambodia,KHM,1963,728571437.142857 +Cambodia,KHM,1964,782857128.571429 +Cambodia,KHM,1965,868571428.571429 +Cambodia,KHM,1966,914285714.285714 +Cambodia,KHM,1967,962857134.285714 +Cambodia,KHM,1968,1065714248.57143 +Cambodia,KHM,1969,978873232.394366 +Cambodia,KHM,1970,718401157.724163 +Cambodia,KHM,1971,969911421.394181 +Cambodia,KHM,1972,505549441.375077 +Cambodia,KHM,1973,702899155.982033 +Cambodia,KHM,1974,588443893.689773 +Cambodia,KHM,1993,2533727592.02872 +Cambodia,KHM,1994,2791435272.25765 +Cambodia,KHM,1995,3441205692.90388 +Cambodia,KHM,1996,3506695719.5716 +Cambodia,KHM,1997,3443413388.70675 +Cambodia,KHM,1998,3120425502.56968 +Cambodia,KHM,1999,3517242477.23368 +Cambodia,KHM,2000,3654031716.27649 +Cambodia,KHM,2001,3984000517.01899 +Cambodia,KHM,2002,4284028482.54971 +Cambodia,KHM,2003,4658246918.26014 +Cambodia,KHM,2004,5337833248.02828 +Cambodia,KHM,2005,6293046161.82646 +Cambodia,KHM,2006,7274595706.66355 +Cambodia,KHM,2007,8639235842.17756 +Cambodia,KHM,2008,10351914093.1626 +Cambodia,KHM,2009,10401851850.6062 +Cambodia,KHM,2010,11242275198.9706 +Cambodia,KHM,2011,12829541141.0175 +Cambodia,KHM,2012,14038383450.1945 +Cambodia,KHM,2013,15449630418.5461 +Cambodia,KHM,2014,16777820332.6952 +Cambodia,KHM,2015,18049954289.4301 +Cambodia,KHM,2016,20016747754.0153 +Cameroon,CMR,1960,618740988.011405 +Cameroon,CMR,1961,657597382.759152 +Cameroon,CMR,1962,699373701.217138 +Cameroon,CMR,1963,723624365.288138 +Cameroon,CMR,1964,782384527.813649 +Cameroon,CMR,1965,814139855.756458 +Cameroon,CMR,1966,853268771.097081 +Cameroon,CMR,1967,934079050.346173 +Cameroon,CMR,1968,1053077155.17925 +Cameroon,CMR,1969,1152418514.82616 +Cameroon,CMR,1970,1160002260.94729 +Cameroon,CMR,1971,1233991075.11626 +Cameroon,CMR,1972,1430951331.85034 +Cameroon,CMR,1973,1758727395.18703 +Cameroon,CMR,1974,2255496995.49378 +Cameroon,CMR,1975,2752771043.88609 +Cameroon,CMR,1976,3076592431.27204 +Cameroon,CMR,1977,3366368664.59706 +Cameroon,CMR,1978,4409920643.6422 +Cameroon,CMR,1979,5811444660.65752 +Cameroon,CMR,1980,6740756568.91566 +Cameroon,CMR,1981,7636345827.34308 +Cameroon,CMR,1982,7322914570.15588 +Cameroon,CMR,1983,7381854746.91629 +Cameroon,CMR,1984,7801858825.18416 +Cameroon,CMR,1985,8148223603.58399 +Cameroon,CMR,1986,10621158532.5193 +Cameroon,CMR,1987,12302471429.4318 +Cameroon,CMR,1988,12493286761.7341 +Cameroon,CMR,1989,11140055364.1502 +Cameroon,CMR,1990,11151578050.7356 +Cameroon,CMR,1991,12434370004.9586 +Cameroon,CMR,1992,11396310990.2197 +Cameroon,CMR,1993,13532137227.585 +Cameroon,CMR,1994,9220470913.32766 +Cameroon,CMR,1995,8733231184.34755 +Cameroon,CMR,1996,9732328115.73674 +Cameroon,CMR,1997,9840553235.89425 +Cameroon,CMR,1998,9629649416.89037 +Cameroon,CMR,1999,10486451144.4061 +Cameroon,CMR,2000,9287367235.25769 +Cameroon,CMR,2001,9633109349.64535 +Cameroon,CMR,2002,10879778384.1965 +Cameroon,CMR,2003,13621738837.1961 +Cameroon,CMR,2004,15775357014.6254 +Cameroon,CMR,2005,16587858856.6778 +Cameroon,CMR,2006,17953066721.0949 +Cameroon,CMR,2007,20431780377.8605 +Cameroon,CMR,2008,23322254113.5623 +Cameroon,CMR,2009,23381142146.6485 +Cameroon,CMR,2010,23622483983.7101 +Cameroon,CMR,2011,26587311527.5711 +Cameroon,CMR,2012,26472056037.7696 +Cameroon,CMR,2013,29567504655.4935 +Cameroon,CMR,2014,32050817632.9602 +Cameroon,CMR,2015,28415950981.4447 +Cameroon,CMR,2016,24204448566.6105 +Canada,CAN,1960,41093453544.9096 +Canada,CAN,1961,40767969453.696 +Canada,CAN,1962,41978852041.4426 +Canada,CAN,1963,44657169109.224 +Canada,CAN,1964,48882938810.2204 +Canada,CAN,1965,53909570342.169 +Canada,CAN,1966,60358632035.1532 +Canada,CAN,1967,64768831262.1761 +Canada,CAN,1968,70759031841.7237 +Canada,CAN,1969,77887510241.7083 +Canada,CAN,1970,87896095224.4234 +Canada,CAN,1971,99271961477.5203 +Canada,CAN,1972,113082820992.019 +Canada,CAN,1973,131321859214.079 +Canada,CAN,1974,160408697648.262 +Canada,CAN,1975,173834029787.652 +Canada,CAN,1976,206575564401.623 +Canada,CAN,1977,211612156934.65 +Canada,CAN,1978,218632867449.812 +Canada,CAN,1979,243072102185.419 +Canada,CAN,1980,273853826377.01 +Canada,CAN,1981,306214863624.99 +Canada,CAN,1982,313506525087.136 +Canada,CAN,1983,340547711781.889 +Canada,CAN,1984,355372558103.621 +Canada,CAN,1985,364756499450.751 +Canada,CAN,1986,377437927311.983 +Canada,CAN,1987,431316742081.448 +Canada,CAN,1988,507354351182.254 +Canada,CAN,1989,565055743243.243 +Canada,CAN,1990,593929550908.468 +Canada,CAN,1991,610328183643.188 +Canada,CAN,1992,592387689252.916 +Canada,CAN,1993,577170761956.438 +Canada,CAN,1994,578139279437.61 +Canada,CAN,1995,604031623433.401 +Canada,CAN,1996,628546387972.131 +Canada,CAN,1997,652825364726.275 +Canada,CAN,1998,631813279406.808 +Canada,CAN,1999,676082654640.91 +Canada,CAN,2000,742293448252.643 +Canada,CAN,2001,736379777892.562 +Canada,CAN,2002,757950678646.53 +Canada,CAN,2003,892380986367.854 +Canada,CAN,2004,1023196003074.56 +Canada,CAN,2005,1169357979864.66 +Canada,CAN,2006,1315415197461.21 +Canada,CAN,2007,1464977190205.75 +Canada,CAN,2008,1549131208997.19 +Canada,CAN,2009,1371153004986.44 +Canada,CAN,2010,1613464422811.13 +Canada,CAN,2011,1788647906047.76 +Canada,CAN,2012,1824288757447.57 +Canada,CAN,2013,1842628005830.18 +Canada,CAN,2014,1792883225804.38 +Canada,CAN,2015,1552807652015.37 +Canada,CAN,2016,1529760492201.35 +Cayman Islands,CYM,1996,1012444074.07495 +Cayman Islands,CYM,2006,3207032512.94205 +Central African Republic,CAF,1960,112155598.949571 +Central African Republic,CAF,1961,123134584.467673 +Central African Republic,CAF,1962,124482748.937917 +Central African Republic,CAF,1963,129379097.888958 +Central African Republic,CAF,1964,142025069.461676 +Central African Republic,CAF,1965,150574816.300764 +Central African Republic,CAF,1966,157930041.875883 +Central African Republic,CAF,1967,163820538.867947 +Central African Republic,CAF,1968,191767436.956884 +Central African Republic,CAF,1969,188039191.323608 +Central African Republic,CAF,1970,189106554.521277 +Central African Republic,CAF,1971,201450768.367553 +Central African Republic,CAF,1972,230317908.038643 +Central African Republic,CAF,1973,271183061.359635 +Central African Republic,CAF,1974,281398668.160613 +Central African Republic,CAF,1975,378660016.265936 +Central African Republic,CAF,1976,451152449.984411 +Central African Republic,CAF,1977,507298120.68315 +Central African Republic,CAF,1978,610578523.761178 +Central African Republic,CAF,1979,700764892.704831 +Central African Republic,CAF,1980,797048028.773247 +Central African Republic,CAF,1981,694803502.722356 +Central African Republic,CAF,1982,748312283.726758 +Central African Republic,CAF,1983,658679394.907969 +Central African Republic,CAF,1984,637820620.670195 +Central African Republic,CAF,1985,864849765.059665 +Central African Republic,CAF,1986,1122265026.38274 +Central African Republic,CAF,1987,1200991825.95398 +Central African Republic,CAF,1988,1264899368.20165 +Central African Republic,CAF,1989,1233930277.04922 +Central African Republic,CAF,1990,1440711395.67069 +Central African Republic,CAF,1991,1377375030.52921 +Central African Republic,CAF,1992,1411917558.45855 +Central African Republic,CAF,1993,1278781166.72188 +Central African Republic,CAF,1994,851174350.649409 +Central African Republic,CAF,1995,1115389731.79119 +Central African Republic,CAF,1996,1007791186.20106 +Central African Republic,CAF,1997,937741468.029676 +Central African Republic,CAF,1998,967338348.658314 +Central African Republic,CAF,1999,999477510.686632 +Central African Republic,CAF,2000,914500299.097034 +Central African Republic,CAF,2001,931833302.752857 +Central African Republic,CAF,2002,991387870.12463 +Central African Republic,CAF,2003,1139754799.16304 +Central African Republic,CAF,2004,1270080250.65268 +Central African Republic,CAF,2005,1350301057.06866 +Central African Republic,CAF,2006,1460562038.37097 +Central African Republic,CAF,2007,1698125617.92304 +Central African Republic,CAF,2008,1985239276.62611 +Central African Republic,CAF,2009,1981728140.77833 +Central African Republic,CAF,2010,1986014845.63184 +Central African Republic,CAF,2011,2212699746.81377 +Central African Republic,CAF,2012,2184183758.31567 +Central African Republic,CAF,2013,1518565219.01061 +Central African Republic,CAF,2014,1702898939.55483 +Central African Republic,CAF,2015,1583776759.97697 +Central African Republic,CAF,2016,1756124677.19671 +Chad,TCD,1960,313582727.638108 +Chad,TCD,1961,333975336.626596 +Chad,TCD,1962,357635713.876856 +Chad,TCD,1963,371767002.656036 +Chad,TCD,1964,392247517.601949 +Chad,TCD,1965,416926302.963497 +Chad,TCD,1966,432794922.459759 +Chad,TCD,1967,449826322.995107 +Chad,TCD,1968,453980096.654412 +Chad,TCD,1969,471635620.924368 +Chad,TCD,1970,469266736.605101 +Chad,TCD,1971,501866730.722503 +Chad,TCD,1972,585427545.723598 +Chad,TCD,1973,647199482.827982 +Chad,TCD,1974,652532796.06664 +Chad,TCD,1975,864602103.303074 +Chad,TCD,1976,866044961.048354 +Chad,TCD,1977,935360466.351488 +Chad,TCD,1978,1113920122.61232 +Chad,TCD,1979,1004316495.11176 +Chad,TCD,1980,1033002401.82543 +Chad,TCD,1981,876937559.724954 +Chad,TCD,1982,834369860.427317 +Chad,TCD,1983,832415805.956265 +Chad,TCD,1984,919103735.322906 +Chad,TCD,1985,1033069709.99506 +Chad,TCD,1986,1067828247.23579 +Chad,TCD,1987,1163426850.65024 +Chad,TCD,1988,1482597298.88729 +Chad,TCD,1989,1433686309.83641 +Chad,TCD,1990,1738605558.05436 +Chad,TCD,1991,1877138041.64308 +Chad,TCD,1992,1881847676.80752 +Chad,TCD,1993,1463251055.40068 +Chad,TCD,1994,1179837954.72193 +Chad,TCD,1995,1445919969.89272 +Chad,TCD,1996,1607345450.04578 +Chad,TCD,1997,1544689502.82472 +Chad,TCD,1998,1744794457.276 +Chad,TCD,1999,1534673583.2487 +Chad,TCD,2000,1385058161.76746 +Chad,TCD,2001,1709347793.32873 +Chad,TCD,2002,1987622279.11463 +Chad,TCD,2003,2736666515.8294 +Chad,TCD,2004,4414929219.99649 +Chad,TCD,2005,6646663561.2656 +Chad,TCD,2006,7422102655.98832 +Chad,TCD,2007,8638711442.7705 +Chad,TCD,2008,10351932604.4154 +Chad,TCD,2009,9253484108.49701 +Chad,TCD,2010,10657705536.4978 +Chad,TCD,2011,12156380425.0825 +Chad,TCD,2012,12368071038.7362 +Chad,TCD,2013,12949854262.8127 +Chad,TCD,2014,13922223233.5184 +Chad,TCD,2015,10888798113.7866 +Chad,TCD,2016,9600761473.78738 +Channel Islands,CHI,1998,5945677376.61477 +Channel Islands,CHI,1999,6262740656.85164 +Channel Islands,CHI,2000,6439703434.71024 +Channel Islands,CHI,2001,6232906290.4851 +Channel Islands,CHI,2002,6663669064.7482 +Channel Islands,CHI,2003,7332244897.95918 +Channel Islands,CHI,2004,8553643354.08275 +Channel Islands,CHI,2005,8827272727.27273 +Channel Islands,CHI,2006,9676172953.08188 +Channel Islands,CHI,2007,11514605842.3369 +Chile,CHL,1960,4109993820.85867 +Chile,CHL,1961,4609700367.43769 +Chile,CHL,1962,5416266783.44725 +Chile,CHL,1963,5668205957.19525 +Chile,CHL,1964,5982343871.07657 +Chile,CHL,1965,6026583778.99913 +Chile,CHL,1966,7072636623.49356 +Chile,CHL,1967,7013192291.50469 +Chile,CHL,1968,7167084577.79975 +Chile,CHL,1969,8377098357.87508 +Chile,CHL,1970,9126311883.24115 +Chile,CHL,1971,10884112885.7799 +Chile,CHL,1972,11853818445.9512 +Chile,CHL,1973,16836261234.4254 +Chile,CHL,1974,16210404218.7163 +Chile,CHL,1975,7622217357.00739 +Chile,CHL,1976,10341925245.9606 +Chile,CHL,1977,13962893421.8332 +Chile,CHL,1978,15989933709.6354 +Chile,CHL,1979,21803696986.4362 +Chile,CHL,1980,29036709873.0313 +Chile,CHL,1981,34509878043.8003 +Chile,CHL,1982,25325893204.9564 +Chile,CHL,1983,20355959236.7512 +Chile,CHL,1984,19622527479.8718 +Chile,CHL,1985,17702885393.2705 +Chile,CHL,1986,18891048818.9936 +Chile,CHL,1987,22255407684.5828 +Chile,CHL,1988,26040229792.9356 +Chile,CHL,1989,29885685143.0648 +Chile,CHL,1990,33113887817.8869 +Chile,CHL,1991,37834793730.368 +Chile,CHL,1992,45964327558.9522 +Chile,CHL,1993,49297773130.1452 +Chile,CHL,1994,57008425295.862 +Chile,CHL,1995,73447063319.1781 +Chile,CHL,1996,78039572221.6062 +Chile,CHL,1997,84952360922.4958 +Chile,CHL,1998,81577430181.4606 +Chile,CHL,1999,75173794497.0496 +Chile,CHL,2000,77860932151.9303 +Chile,CHL,2001,70980110277.4532 +Chile,CHL,2002,69737148847.9119 +Chile,CHL,2003,75643733356.097 +Chile,CHL,2004,99210528495.6404 +Chile,CHL,2005,122964812046.14 +Chile,CHL,2006,154788024805.866 +Chile,CHL,2007,173605979255.357 +Chile,CHL,2008,179638483957.885 +Chile,CHL,2009,172389500030.292 +Chile,CHL,2010,218537565496.634 +Chile,CHL,2011,252251992029.518 +Chile,CHL,2012,267122318194.715 +Chile,CHL,2013,278384345259.391 +Chile,CHL,2014,260990291671.248 +Chile,CHL,2015,242517910999.453 +Chile,CHL,2016,247027899421.318 +China,CHN,1960,59716467625.3148 +China,CHN,1961,50056868957.6732 +China,CHN,1962,47209359005.6056 +China,CHN,1963,50706799902.5103 +China,CHN,1964,59708343488.5043 +China,CHN,1965,70436266146.7219 +China,CHN,1966,76720285969.6157 +China,CHN,1967,72881631326.6715 +China,CHN,1968,70846535055.6503 +China,CHN,1969,79705906247.4612 +China,CHN,1970,92602973434.0726 +China,CHN,1971,99800958648.1436 +China,CHN,1972,113687586299.051 +China,CHN,1973,138544284708.957 +China,CHN,1974,144182133387.722 +China,CHN,1975,163431551779.761 +China,CHN,1976,153940455341.506 +China,CHN,1977,174938098826.569 +China,CHN,1978,149540650406.504 +China,CHN,1979,178282608695.652 +China,CHN,1980,191150000000 +China,CHN,1981,195865079365.079 +China,CHN,1982,205091603053.435 +China,CHN,1983,230685823754.789 +China,CHN,1984,259946428571.429 +China,CHN,1985,309486394557.823 +China,CHN,1986,300759420289.855 +China,CHN,1987,272973094170.404 +China,CHN,1988,312353909465.021 +China,CHN,1989,347767206477.733 +China,CHN,1990,360858508604.206 +China,CHN,1991,383372822299.652 +China,CHN,1992,426915227629.513 +China,CHN,1993,444730903968.185 +China,CHN,1994,564325246266.838 +China,CHN,1995,734548001963.907 +China,CHN,1996,863746361646.34 +China,CHN,1997,961603416246.472 +China,CHN,1998,1029043011921.59 +China,CHN,1999,1093997559885.48 +China,CHN,2000,1211346395438.73 +China,CHN,2001,1339395440432.04 +China,CHN,2002,1470549716080.71 +China,CHN,2003,1660287543796.06 +China,CHN,2004,1955347477285.91 +China,CHN,2005,2285965854313.36 +China,CHN,2006,2752132089196.58 +China,CHN,2007,3552182714426.55 +China,CHN,2008,4598205419718.8 +China,CHN,2009,5109954035775.98 +China,CHN,2010,6100620356557.32 +China,CHN,2011,7572554360442.62 +China,CHN,2012,8560546868811.69 +China,CHN,2013,9607224248684.59 +China,CHN,2014,10482371325324.7 +China,CHN,2015,11064664793255.7 +China,CHN,2016,11199145157649.2 +Colombia,COL,1960,4040948153.73022 +Colombia,COL,1961,4552914000 +Colombia,COL,1962,4968603735.58222 +Colombia,COL,1963,4838841455.55556 +Colombia,COL,1964,5992169466.66667 +Colombia,COL,1965,5790247619.04762 +Colombia,COL,1966,5452762962.96296 +Colombia,COL,1967,5727195020.23203 +Colombia,COL,1968,5918455409.8099 +Colombia,COL,1969,6405427873.70755 +Colombia,COL,1970,7198360460.19887 +Colombia,COL,1971,7820380970.53674 +Colombia,COL,1972,8671358732.68486 +Colombia,COL,1973,10315760000.3394 +Colombia,COL,1974,12370029583.6419 +Colombia,COL,1975,13098633901.8673 +Colombia,COL,1976,15341403660.4698 +Colombia,COL,1977,19470960619.1297 +Colombia,COL,1978,23263511958.0509 +Colombia,COL,1979,27940411250.2732 +Colombia,COL,1980,33400735644.0481 +Colombia,COL,1981,36388366869.0309 +Colombia,COL,1982,38968039721.748 +Colombia,COL,1983,38729822781.5997 +Colombia,COL,1984,38253120737.9671 +Colombia,COL,1985,34894411351.983 +Colombia,COL,1986,34942489683.9712 +Colombia,COL,1987,36373307085.0887 +Colombia,COL,1988,39212550050.4223 +Colombia,COL,1989,39540080200.3938 +Colombia,COL,1990,40274204595.2296 +Colombia,COL,1991,41239551378.2482 +Colombia,COL,1992,49279585355.0948 +Colombia,COL,1993,55802538219.0264 +Colombia,COL,1994,81703500846.0364 +Colombia,COL,1995,92507279383.0387 +Colombia,COL,1996,97160109277.8087 +Colombia,COL,1997,106659508271.255 +Colombia,COL,1998,98443739941.1664 +Colombia,COL,1999,86186158684.7685 +Colombia,COL,2000,99886577330.7271 +Colombia,COL,2001,98203546156.3102 +Colombia,COL,2002,97933391976.083 +Colombia,COL,2003,94684584162.773 +Colombia,COL,2004,117074863821.85 +Colombia,COL,2005,146566264837.014 +Colombia,COL,2006,162590146096.414 +Colombia,COL,2007,207416494642.379 +Colombia,COL,2008,243982437870.84 +Colombia,COL,2009,233821670544.258 +Colombia,COL,2010,287018184637.529 +Colombia,COL,2011,335415156702.186 +Colombia,COL,2012,369659700375.52 +Colombia,COL,2013,380191881860.372 +Colombia,COL,2014,378195716714.266 +Colombia,COL,2015,291519591532.951 +Colombia,COL,2016,282462548889.26 +Comoros,COM,1980,123505640.914474 +Comoros,COM,1981,114271897.268272 +Comoros,COM,1982,107089552.302395 +Comoros,COM,1983,111519676.021905 +Comoros,COM,1984,107489822.704044 +Comoros,COM,1985,114490697.57503 +Comoros,COM,1986,162487763.894624 +Comoros,COM,1987,196433684.042405 +Comoros,COM,1988,207476554.806734 +Comoros,COM,1989,198733185.875107 +Comoros,COM,1990,250030760.754786 +Comoros,COM,1991,246823428.906664 +Comoros,COM,1992,266191040.373328 +Comoros,COM,1993,263568114.445462 +Comoros,COM,1994,185761822.560488 +Comoros,COM,1995,231896229.562629 +Comoros,COM,1996,230495751.446593 +Comoros,COM,1997,212099634.697751 +Comoros,COM,1998,215394066.068976 +Comoros,COM,1999,222580453.753844 +Comoros,COM,2000,203846427.738737 +Comoros,COM,2001,220093812.206791 +Comoros,COM,2002,246737679.472106 +Comoros,COM,2003,317562269.371106 +Comoros,COM,2004,368143118.68996 +Comoros,COM,2005,380372892.606774 +Comoros,COM,2006,406111873.539847 +Comoros,COM,2007,462453582.873627 +Comoros,COM,2008,523134896.968654 +Comoros,COM,2009,524157261.014644 +Comoros,COM,2010,530493353.218937 +Comoros,COM,2011,586281766.75997 +Comoros,COM,2012,570865941.229395 +Comoros,COM,2013,618663921.86116 +Comoros,COM,2014,647720707.076427 +Comoros,COM,2015,565689764.63263 +Comoros,COM,2016,616654490.471188 +"Congo, Dem. Rep.",COD,1960,3359404132.31797 +"Congo, Dem. Rep.",COD,1961,3086746936.39789 +"Congo, Dem. Rep.",COD,1962,3779841409.55267 +"Congo, Dem. Rep.",COD,1963,6213185773.92265 +"Congo, Dem. Rep.",COD,1964,2881545277.14307 +"Congo, Dem. Rep.",COD,1965,4043901724.13762 +"Congo, Dem. Rep.",COD,1966,4532660145.32842 +"Congo, Dem. Rep.",COD,1967,3384063359.82333 +"Congo, Dem. Rep.",COD,1968,3909780525.37375 +"Congo, Dem. Rep.",COD,1969,5032434970.84174 +"Congo, Dem. Rep.",COD,1970,4877684933.12505 +"Congo, Dem. Rep.",COD,1971,5594770351.56981 +"Congo, Dem. Rep.",COD,1972,6173712809.28569 +"Congo, Dem. Rep.",COD,1973,7870239463.58461 +"Congo, Dem. Rep.",COD,1974,9596960174.34365 +"Congo, Dem. Rep.",COD,1975,10237343153.4311 +"Congo, Dem. Rep.",COD,1976,9648583220.47256 +"Congo, Dem. Rep.",COD,1977,12344424775.9239 +"Congo, Dem. Rep.",COD,1978,15372607995.4061 +"Congo, Dem. Rep.",COD,1979,15068422242.0571 +"Congo, Dem. Rep.",COD,1980,14394927492.9647 +"Congo, Dem. Rep.",COD,1981,12537821039.8252 +"Congo, Dem. Rep.",COD,1982,13651667370.5466 +"Congo, Dem. Rep.",COD,1983,11006712649.2175 +"Congo, Dem. Rep.",COD,1984,7857729192.85629 +"Congo, Dem. Rep.",COD,1985,7195042615.84176 +"Congo, Dem. Rep.",COD,1986,8095367168.16178 +"Congo, Dem. Rep.",COD,1987,7661625472.51189 +"Congo, Dem. Rep.",COD,1988,8861299976.68858 +"Congo, Dem. Rep.",COD,1989,9021862775.25443 +"Congo, Dem. Rep.",COD,1990,9349764580.35198 +"Congo, Dem. Rep.",COD,1991,9087965281.52807 +"Congo, Dem. Rep.",COD,1992,8206227134.00857 +"Congo, Dem. Rep.",COD,1993,10707792340.0276 +"Congo, Dem. Rep.",COD,1994,5820383305.54668 +"Congo, Dem. Rep.",COD,1995,5643439262.21735 +"Congo, Dem. Rep.",COD,1996,5771454939.62404 +"Congo, Dem. Rep.",COD,1997,6090840548.18784 +"Congo, Dem. Rep.",COD,1998,6215591269.89747 +"Congo, Dem. Rep.",COD,1999,4711259427.27273 +"Congo, Dem. Rep.",COD,2000,19088046305.7971 +"Congo, Dem. Rep.",COD,2001,7438189100.33333 +"Congo, Dem. Rep.",COD,2002,8728038525.14034 +"Congo, Dem. Rep.",COD,2003,8937567059.87754 +"Congo, Dem. Rep.",COD,2004,10297483481.223 +"Congo, Dem. Rep.",COD,2005,11964484667.9102 +"Congo, Dem. Rep.",COD,2006,14296507096.4135 +"Congo, Dem. Rep.",COD,2007,16364029327.3457 +"Congo, Dem. Rep.",COD,2008,19206060270.2521 +"Congo, Dem. Rep.",COD,2009,18262773820.8055 +"Congo, Dem. Rep.",COD,2010,20523285374.187 +"Congo, Dem. Rep.",COD,2011,23849009737.6669 +"Congo, Dem. Rep.",COD,2012,27463220380.0054 +"Congo, Dem. Rep.",COD,2013,30014813755.772 +"Congo, Dem. Rep.",COD,2014,34028119332.6087 +"Congo, Dem. Rep.",COD,2015,36188521106.9294 +"Congo, Dem. Rep.",COD,2016,34998638634.1 +"Congo, Rep.",COG,1960,131731862.568997 +"Congo, Rep.",COG,1961,151675739.160627 +"Congo, Rep.",COG,1962,166521239.863281 +"Congo, Rep.",COG,1963,172233430.871502 +"Congo, Rep.",COG,1964,185693724.845331 +"Congo, Rep.",COG,1965,198318063.860835 +"Congo, Rep.",COG,1966,220613582.369827 +"Congo, Rep.",COG,1967,237397428.336429 +"Congo, Rep.",COG,1968,251247458.012189 +"Congo, Rep.",COG,1969,265040036.059116 +"Congo, Rep.",COG,1970,274960699.85855 +"Congo, Rep.",COG,1971,322128019.323561 +"Congo, Rep.",COG,1972,410669262.897929 +"Congo, Rep.",COG,1973,541973362.480998 +"Congo, Rep.",COG,1974,585364635.354748 +"Congo, Rep.",COG,1975,767102679.018622 +"Congo, Rep.",COG,1976,754549600.548182 +"Congo, Rep.",COG,1977,765224030.636552 +"Congo, Rep.",COG,1978,878771771.29105 +"Congo, Rep.",COG,1979,1198749665.95066 +"Congo, Rep.",COG,1980,1705796849.54655 +"Congo, Rep.",COG,1981,1993512325.92286 +"Congo, Rep.",COG,1982,2160640566.5396 +"Congo, Rep.",COG,1983,2097274289.61512 +"Congo, Rep.",COG,1984,2193581366.40722 +"Congo, Rep.",COG,1985,2160872541.41887 +"Congo, Rep.",COG,1986,1849268214.68184 +"Congo, Rep.",COG,1987,2297753649.2798 +"Congo, Rep.",COG,1988,2212536313.33492 +"Congo, Rep.",COG,1989,2389593021.94866 +"Congo, Rep.",COG,1990,2798746050.58236 +"Congo, Rep.",COG,1991,2724853507.63856 +"Congo, Rep.",COG,1992,2933222705.80382 +"Congo, Rep.",COG,1993,1918970177.74925 +"Congo, Rep.",COG,1994,1769365438.87155 +"Congo, Rep.",COG,1995,2116003868.17928 +"Congo, Rep.",COG,1996,2540697537.71673 +"Congo, Rep.",COG,1997,2322719101.29807 +"Congo, Rep.",COG,1998,1949481380.64044 +"Congo, Rep.",COG,1999,2353909441.71514 +"Congo, Rep.",COG,2000,3219910666.03357 +"Congo, Rep.",COG,2001,2794259783.08097 +"Congo, Rep.",COG,2002,3019993738.77492 +"Congo, Rep.",COG,2003,3495868808.0512 +"Congo, Rep.",COG,2004,4648628921.36969 +"Congo, Rep.",COG,2005,6087003176.11624 +"Congo, Rep.",COG,2006,7731261310.93322 +"Congo, Rep.",COG,2007,8394688284.06224 +"Congo, Rep.",COG,2008,11859014004.0772 +"Congo, Rep.",COG,2009,9593536531.23778 +"Congo, Rep.",COG,2010,12007880590.4575 +"Congo, Rep.",COG,2011,14425607224.168 +"Congo, Rep.",COG,2012,13677930123.5919 +"Congo, Rep.",COG,2013,14085852120.4761 +"Congo, Rep.",COG,2014,14177437627.2969 +"Congo, Rep.",COG,2015,8553154505.83693 +"Congo, Rep.",COG,2016,7833509443.39395 +Costa Rica,CRI,1960,507513829.994855 +Costa Rica,CRI,1961,490325181.614275 +Costa Rica,CRI,1962,479180824.348506 +Costa Rica,CRI,1963,511902136.809973 +Costa Rica,CRI,1964,542578367.242598 +Costa Rica,CRI,1965,592981162.264151 +Costa Rica,CRI,1966,647305630.188679 +Costa Rica,CRI,1967,699456618.867925 +Costa Rica,CRI,1968,773841494.339623 +Costa Rica,CRI,1969,853630203.773585 +Costa Rica,CRI,1970,984830158.490566 +Costa Rica,CRI,1971,1077152902.29104 +Costa Rica,CRI,1972,1238251695.55388 +Costa Rica,CRI,1973,1528916185.23199 +Costa Rica,CRI,1974,1666544754.09836 +Costa Rica,CRI,1975,1960863465.5776 +Costa Rica,CRI,1976,2412555425.90432 +Costa Rica,CRI,1977,3072427012.83547 +Costa Rica,CRI,1978,3523208809.80163 +Costa Rica,CRI,1979,4035519323.22054 +Costa Rica,CRI,1980,4831447001.16686 +Costa Rica,CRI,1981,2623807074.2948 +Costa Rica,CRI,1982,2606621255.01581 +Costa Rica,CRI,1983,3976453966.73983 +Costa Rica,CRI,1984,4593908718.76172 +Costa Rica,CRI,1985,4796628461.38614 +Costa Rica,CRI,1986,5477895474.91039 +Costa Rica,CRI,1987,5841132961.60586 +Costa Rica,CRI,1988,6063759370.62937 +Costa Rica,CRI,1989,6866402028.10997 +Costa Rica,CRI,1990,7403457319.20472 +Costa Rica,CRI,1991,7168999428.24471 +Costa Rica,CRI,1992,8528593084.48749 +Costa Rica,CRI,1993,9537297507.16915 +Costa Rica,CRI,1994,10432619390.3609 +Costa Rica,CRI,1995,11513472693.8707 +Costa Rica,CRI,1996,11618286553.3677 +Costa Rica,CRI,1997,12552071367.1539 +Costa Rica,CRI,1998,13617405420.1174 +Costa Rica,CRI,1999,14195623424.811 +Costa Rica,CRI,2000,14949514585.1585 +Costa Rica,CRI,2001,15913363335.0564 +Costa Rica,CRI,2002,16504795453.2822 +Costa Rica,CRI,2003,17195867540.353 +Costa Rica,CRI,2004,18529767934.4743 +Costa Rica,CRI,2005,19952156474.8454 +Costa Rica,CRI,2006,22600431878.0024 +Costa Rica,CRI,2007,26743874286.8514 +Costa Rica,CRI,2008,30612932802.8991 +Costa Rica,CRI,2009,30562361123.0307 +Costa Rica,CRI,2010,37268635287.0856 +Costa Rica,CRI,2011,42262697840.385 +Costa Rica,CRI,2012,46473128285.5589 +Costa Rica,CRI,2013,49745088111.6953 +Costa Rica,CRI,2014,50656002895.8763 +Costa Rica,CRI,2015,54840103850.265 +Costa Rica,CRI,2016,57435507212.256 +Cote d'Ivoire,CIV,1960,546203561.571989 +Cote d'Ivoire,CIV,1961,618245639.221382 +Cote d'Ivoire,CIV,1962,645284344.684118 +Cote d'Ivoire,CIV,1963,761047045.830402 +Cote d'Ivoire,CIV,1964,921063266.445521 +Cote d'Ivoire,CIV,1965,919771356.426097 +Cote d'Ivoire,CIV,1966,1024103034.29198 +Cote d'Ivoire,CIV,1967,1082922892.15202 +Cote d'Ivoire,CIV,1968,1281281245.67032 +Cote d'Ivoire,CIV,1969,1361360157.26999 +Cote d'Ivoire,CIV,1970,1455482990.24143 +Cote d'Ivoire,CIV,1971,1584128262.08933 +Cote d'Ivoire,CIV,1972,1849400599.77558 +Cote d'Ivoire,CIV,1973,2508421234.8557 +Cote d'Ivoire,CIV,1974,3070151901.06383 +Cote d'Ivoire,CIV,1975,3893839190.26806 +Cote d'Ivoire,CIV,1976,4662053707.7763 +Cote d'Ivoire,CIV,1977,6265067857.86534 +Cote d'Ivoire,CIV,1978,7900524897.8644 +Cote d'Ivoire,CIV,1979,9142935857.94766 +Cote d'Ivoire,CIV,1980,10175615441.8127 +Cote d'Ivoire,CIV,1981,8432588483.85263 +Cote d'Ivoire,CIV,1982,7567109766.61129 +Cote d'Ivoire,CIV,1983,6838185418.53642 +Cote d'Ivoire,CIV,1984,6841638714.5454 +Cote d'Ivoire,CIV,1985,6977650069.33578 +Cote d'Ivoire,CIV,1986,9158302205.36237 +Cote d'Ivoire,CIV,1987,10087653189.3287 +Cote d'Ivoire,CIV,1988,10255170459.986 +Cote d'Ivoire,CIV,1989,9757410614.0812 +Cote d'Ivoire,CIV,1990,10795850106.9547 +Cote d'Ivoire,CIV,1991,10492628915.4927 +Cote d'Ivoire,CIV,1992,11152971316.074 +Cote d'Ivoire,CIV,1993,11045759468.9412 +Cote d'Ivoire,CIV,1994,8313557450.25213 +Cote d'Ivoire,CIV,1995,11000146839.497 +Cote d'Ivoire,CIV,1996,12139234938.7863 +Cote d'Ivoire,CIV,1997,11722142706.1278 +Cote d'Ivoire,CIV,1998,12612033728.8572 +Cote d'Ivoire,CIV,1999,12376639822.9265 +Cote d'Ivoire,CIV,2000,10717022462.6859 +Cote d'Ivoire,CIV,2001,11192560827.2962 +Cote d'Ivoire,CIV,2002,12346919216.1359 +Cote d'Ivoire,CIV,2003,15306602560.2533 +Cote d'Ivoire,CIV,2004,16554441846.5192 +Cote d'Ivoire,CIV,2005,17084928927.4555 +Cote d'Ivoire,CIV,2006,17800887796.4987 +Cote d'Ivoire,CIV,2007,20343635319.6174 +Cote d'Ivoire,CIV,2008,24224903099.6283 +Cote d'Ivoire,CIV,2009,24277493862.0625 +Cote d'Ivoire,CIV,2010,24884505034.5564 +Cote d'Ivoire,CIV,2011,25381616734.0693 +Cote d'Ivoire,CIV,2012,27040562587.1771 +Cote d'Ivoire,CIV,2013,31273049200.243 +Cote d'Ivoire,CIV,2014,35372603446.2605 +Cote d'Ivoire,CIV,2015,32828516580.3451 +Cote d'Ivoire,CIV,2016,36164644633.0972 +Croatia,HRV,1995,22387561845.2244 +Croatia,HRV,1996,23678012697.3612 +Croatia,HRV,1997,23822087053.2091 +Croatia,HRV,1998,25432144406.2043 +Croatia,HRV,1999,23386945596.6928 +Croatia,HRV,2000,21774273832.1031 +Croatia,HRV,2001,23289671102.3197 +Croatia,HRV,2002,26878499206.0165 +Croatia,HRV,2003,34658113497.39 +Croatia,HRV,2004,41574530815.5047 +Croatia,HRV,2005,45416076680.8707 +Croatia,HRV,2006,50453577898.4886 +Croatia,HRV,2007,60093155532.7678 +Croatia,HRV,2008,70481451814.3118 +Croatia,HRV,2009,62703095750.5257 +Croatia,HRV,2010,59665427464.5326 +Croatia,HRV,2011,62236751773.0497 +Croatia,HRV,2012,56485301967.4205 +Croatia,HRV,2013,57769872074.883 +Croatia,HRV,2014,57080369367.8021 +Croatia,HRV,2015,48676334689.3545 +Croatia,HRV,2016,50425333970.0264 +Cuba,CUB,1970,5693005200 +Cuba,CUB,1971,6914658400 +Cuba,CUB,1972,8135150891.92025 +Cuba,CUB,1973,9987709650.18094 +Cuba,CUB,1974,11405957317.0732 +Cuba,CUB,1975,13027415243.9024 +Cuba,CUB,1976,13789579902.5579 +Cuba,CUB,1977,14206158674.6988 +Cuba,CUB,1978,17844705324.6753 +Cuba,CUB,1979,19584443287.6712 +Cuba,CUB,1980,19912889861.1111 +Cuba,CUB,1981,20150254096.3855 +Cuba,CUB,1982,20953510235.2941 +Cuba,CUB,1983,22204940512.2235 +Cuba,CUB,1984,24039383608.4235 +Cuba,CUB,1985,22920490774.102 +Cuba,CUB,1986,24226574634.0293 +Cuba,CUB,1987,25213935012.0819 +Cuba,CUB,1988,27458999472.2955 +Cuba,CUB,1989,27023468665.8977 +Cuba,CUB,1990,28645436569.1489 +Cuba,CUB,1991,24316556025.6585 +Cuba,CUB,1992,22085858243.2432 +Cuba,CUB,1993,22367254864.8649 +Cuba,CUB,1994,28448326756.7568 +Cuba,CUB,1995,30429803651.2192 +Cuba,CUB,1996,25017300000 +Cuba,CUB,1997,25366200000 +Cuba,CUB,1998,25736331247.1786 +Cuba,CUB,1999,28364615241.6894 +Cuba,CUB,2000,30565400000 +Cuba,CUB,2001,31682400000 +Cuba,CUB,2002,33590500000 +Cuba,CUB,2003,35901200000 +Cuba,CUB,2004,38203000000 +Cuba,CUB,2005,42643836074.8 +Cuba,CUB,2006,52742800000 +Cuba,CUB,2007,58603900000 +Cuba,CUB,2008,60806300000 +Cuba,CUB,2009,62080000000 +Cuba,CUB,2010,64328000000 +Cuba,CUB,2011,68990000000 +Cuba,CUB,2012,73141000000 +Cuba,CUB,2013,77148000000 +Cuba,CUB,2014,80656100000 +Cuba,CUB,2015,87132800000 +Cyprus,CYP,1975,489914760.682807 +Cyprus,CYP,1976,576090073.715036 +Cyprus,CYP,1977,734887973.975806 +Cyprus,CYP,1978,964026512.197839 +Cyprus,CYP,1979,1288715209.58084 +Cyprus,CYP,1980,2154311276.94859 +Cyprus,CYP,1981,2087496373.77964 +Cyprus,CYP,1982,2159242416.76942 +Cyprus,CYP,1983,2160364071.19021 +Cyprus,CYP,1984,2278248953.14058 +Cyprus,CYP,1985,2430411900.19194 +Cyprus,CYP,1986,3090734463.27684 +Cyprus,CYP,1987,3704813885.50548 +Cyprus,CYP,1988,4278792597.23965 +Cyprus,CYP,1989,4563482603.5503 +Cyprus,CYP,1990,5591130217.66965 +Cyprus,CYP,1991,5770197348.48485 +Cyprus,CYP,1992,6912150456.32334 +Cyprus,CYP,1993,6590291048.29211 +Cyprus,CYP,1994,7425703928.57143 +Cyprus,CYP,1995,9826778783.9586 +Cyprus,CYP,1996,9899623588.45671 +Cyprus,CYP,1997,9594298745.72406 +Cyprus,CYP,1998,10353506787.3303 +Cyprus,CYP,1999,10614455231.931 +Cyprus,CYP,2000,10183317624.8822 +Cyprus,CYP,2001,10567304189.4353 +Cyprus,CYP,2002,11618269230.7692 +Cyprus,CYP,2003,14576896942.2424 +Cyprus,CYP,2004,17422375000 +Cyprus,CYP,2005,18703146374.829 +Cyprus,CYP,2006,20403713461.2972 +Cyprus,CYP,2007,24077470572.1325 +Cyprus,CYP,2008,27839460963.8201 +Cyprus,CYP,2009,25942622950.8197 +Cyprus,CYP,2010,25562251655.6291 +Cyprus,CYP,2011,27427161523.4918 +Cyprus,CYP,2012,25012206090.1966 +Cyprus,CYP,2013,24054965480.616 +Cyprus,CYP,2014,23308212816.7706 +Cyprus,CYP,2015,19559942331.1523 +Cyprus,CYP,2016,19801664168.2788 +Czech Republic,CZE,1990,40315847383.7197 +Czech Republic,CZE,1991,29557058174.1697 +Czech Republic,CZE,1992,34451993226.0347 +Czech Republic,CZE,1993,40452245779.1651 +Czech Republic,CZE,1994,47364869195.7617 +Czech Republic,CZE,1995,59537113790.5049 +Czech Republic,CZE,1996,66775046785.4354 +Czech Republic,CZE,1997,61621364981.4187 +Czech Republic,CZE,1998,66372594575.1707 +Czech Republic,CZE,1999,64719461254.5271 +Czech Republic,CZE,2000,61474265134.5364 +Czech Republic,CZE,2001,67375623427.4642 +Czech Republic,CZE,2002,81696651658.8981 +Czech Republic,CZE,2003,99300329682.0164 +Czech Republic,CZE,2004,118976023159.713 +Czech Republic,CZE,2005,135990215966.674 +Czech Republic,CZE,2006,155213006071.979 +Czech Republic,CZE,2007,188818155388.125 +Czech Republic,CZE,2008,235204812643.146 +Czech Republic,CZE,2009,205729790694.015 +Czech Republic,CZE,2010,207016402026.364 +Czech Republic,CZE,2011,227948349666.354 +Czech Republic,CZE,2012,207376427020.815 +Czech Republic,CZE,2013,209402444996.104 +Czech Republic,CZE,2014,207818330723.835 +Czech Republic,CZE,2015,185156359571.116 +Czech Republic,CZE,2016,192924593987.295 +Denmark,DNK,1960,6248946880.2777 +Denmark,DNK,1961,6933842098.84548 +Denmark,DNK,1962,7812968114.40012 +Denmark,DNK,1963,8316692385.77386 +Denmark,DNK,1964,9506678762.77765 +Denmark,DNK,1965,10678897387.0006 +Denmark,DNK,1966,11721248101.0874 +Denmark,DNK,1967,12788479692.1939 +Denmark,DNK,1968,13196541952 +Denmark,DNK,1969,15009384584.5333 +Denmark,DNK,1970,17075466666.6667 +Denmark,DNK,1971,19085681968.1403 +Denmark,DNK,1972,23232411897.6012 +Denmark,DNK,1973,30730638895.7765 +Denmark,DNK,1974,34160363582.6675 +Denmark,DNK,1975,40474400473.3563 +Denmark,DNK,1976,44575847808.1059 +Denmark,DNK,1977,49784281716.4179 +Denmark,DNK,1978,60362854966.8154 +Denmark,DNK,1979,70366280174.8717 +Denmark,DNK,1980,71127592753.5975 +Denmark,DNK,1981,61877755004.6326 +Denmark,DNK,1982,60412846238.7787 +Denmark,DNK,1983,60644833242.2089 +Denmark,DNK,1984,59105208272.9853 +Denmark,DNK,1985,62658544411.3095 +Denmark,DNK,1986,88078729452.4781 +Denmark,DNK,1987,109414353171.645 +Denmark,DNK,1988,115552848547.872 +Denmark,DNK,1989,112409236409.401 +Denmark,DNK,1990,138247261092.977 +Denmark,DNK,1991,139224732275.463 +Denmark,DNK,1992,152915624326.966 +Denmark,DNK,1993,143195607581.857 +Denmark,DNK,1994,156162311731.598 +Denmark,DNK,1995,185006961302.299 +Denmark,DNK,1996,187632400365.599 +Denmark,DNK,1997,173537588008.176 +Denmark,DNK,1998,176992000955.11 +Denmark,DNK,1999,177965224620.854 +Denmark,DNK,2000,164158800460.219 +Denmark,DNK,2001,164791416350.267 +Denmark,DNK,2002,178635160297.415 +Denmark,DNK,2003,218095997085.477 +Denmark,DNK,2004,251373036671.062 +Denmark,DNK,2005,264467308109.19 +Denmark,DNK,2006,282884912894.33 +Denmark,DNK,2007,319423370134.284 +Denmark,DNK,2008,353361056079.716 +Denmark,DNK,2009,321241396034.248 +Denmark,DNK,2010,321995350346.501 +Denmark,DNK,2011,344003209695.606 +Denmark,DNK,2012,327148899962.146 +Denmark,DNK,2013,343584385594.132 +Denmark,DNK,2014,352296969840.949 +Denmark,DNK,2015,301307828843.613 +Denmark,DNK,2016,306142937248.09 +Djibouti,DJI,1985,340989527.967995 +Djibouti,DJI,1987,373371738.286415 +Djibouti,DJI,1988,395794538.630775 +Djibouti,DJI,1989,409220087.102818 +Djibouti,DJI,1990,452328087.282876 +Djibouti,DJI,1991,462421998.525779 +Djibouti,DJI,1992,478058304.871118 +Djibouti,DJI,1993,466048469.22986 +Djibouti,DJI,1994,491689220.744875 +Djibouti,DJI,1995,497723960.589913 +Djibouti,DJI,1996,494004647.73437 +Djibouti,DJI,1997,502675542.001227 +Djibouti,DJI,1998,514267869.300758 +Djibouti,DJI,1999,536080148.097299 +Djibouti,DJI,2000,551230861.856505 +Djibouti,DJI,2001,572417440.820162 +Djibouti,DJI,2002,591122039.601398 +Djibouti,DJI,2003,622044665.515049 +Djibouti,DJI,2004,666072101.777505 +Djibouti,DJI,2005,708633194.726566 +Djibouti,DJI,2006,768873684.032838 +Djibouti,DJI,2007,847918929.107984 +Djibouti,DJI,2008,999105339.267729 +Djibouti,DJI,2009,1049110684.72493 +Djibouti,DJI,2010,1128611700.3618 +Djibouti,DJI,2011,1239144501.77525 +Djibouti,DJI,2012,1353632941.5207 +Djibouti,DJI,2013,1455000000 +Djibouti,DJI,2014,1588000000 +Djibouti,DJI,2015,1727000000 +Dominica,DMA,1977,36370370.3703704 +Dominica,DMA,1978,45170370.3703704 +Dominica,DMA,1979,44296296.2962963 +Dominica,DMA,1980,59100000 +Dominica,DMA,1981,66218518.5185185 +Dominica,DMA,1982,72051851.8518518 +Dominica,DMA,1983,79925925.9259259 +Dominica,DMA,1984,89848148.1481481 +Dominica,DMA,1985,98585185.1851852 +Dominica,DMA,1986,112074074.074074 +Dominica,DMA,1987,126348148.148148 +Dominica,DMA,1988,143766666.666667 +Dominica,DMA,1989,153374074.074074 +Dominica,DMA,1990,166322222.222222 +Dominica,DMA,1991,180437037.037037 +Dominica,DMA,1992,191759259.259259 +Dominica,DMA,1993,200418518.518519 +Dominica,DMA,1994,215459259.259259 +Dominica,DMA,1995,224037037.037037 +Dominica,DMA,1996,236444444.444444 +Dominica,DMA,1997,245781481.481481 +Dominica,DMA,1998,258440740.740741 +Dominica,DMA,1999,267740740.740741 +Dominica,DMA,2000,335845814.814815 +Dominica,DMA,2001,343119370.37037 +Dominica,DMA,2002,337695740.740741 +Dominica,DMA,2003,350091222.222222 +Dominica,DMA,2004,374771481.481481 +Dominica,DMA,2005,370370370.37037 +Dominica,DMA,2006,390370370.37037 +Dominica,DMA,2007,421375851.851852 +Dominica,DMA,2008,458190185.185185 +Dominica,DMA,2009,489074333.333333 +Dominica,DMA,2010,493824407.407407 +Dominica,DMA,2011,500988407.407407 +Dominica,DMA,2012,485905592.592593 +Dominica,DMA,2013,508447148.148148 +Dominica,DMA,2014,528178703.703704 +Dominica,DMA,2015,517218962.962963 +Dominica,DMA,2016,525424630 +Dominican Republic,DOM,1960,672399700 +Dominican Republic,DOM,1961,654100200 +Dominican Republic,DOM,1962,824100000 +Dominican Republic,DOM,1963,940799900 +Dominican Republic,DOM,1964,1025599900 +Dominican Republic,DOM,1965,888100000 +Dominican Republic,DOM,1966,983900000 +Dominican Republic,DOM,1967,1034800000 +Dominican Republic,DOM,1968,1079100000 +Dominican Republic,DOM,1969,1230500000 +Dominican Republic,DOM,1970,1485400100 +Dominican Republic,DOM,1971,1666400000 +Dominican Republic,DOM,1972,1987300000 +Dominican Republic,DOM,1973,2344699900 +Dominican Republic,DOM,1974,2925600000 +Dominican Republic,DOM,1975,3599300100 +Dominican Republic,DOM,1976,3951399900 +Dominican Republic,DOM,1977,4587100200 +Dominican Republic,DOM,1978,4774400000 +Dominican Republic,DOM,1979,5498800100 +Dominican Republic,DOM,1980,6631000100 +Dominican Republic,DOM,1981,7266999800 +Dominican Republic,DOM,1982,7964000300 +Dominican Republic,DOM,1983,8622000100 +Dominican Republic,DOM,1984,10330399700 +Dominican Republic,DOM,1985,5044592912.6775 +Dominican Republic,DOM,1986,6122197810.14358 +Dominican Republic,DOM,1987,5826987203.49563 +Dominican Republic,DOM,1988,5374315190.18405 +Dominican Republic,DOM,1989,6686592728.70663 +Dominican Republic,DOM,1990,7073674721.12418 +Dominican Republic,DOM,1991,9724402004.34906 +Dominican Republic,DOM,1992,11277676879.9612 +Dominican Republic,DOM,1993,12976408000 +Dominican Republic,DOM,1994,14511134920.6349 +Dominican Republic,DOM,1995,16358496124.031 +Dominican Republic,DOM,1996,18131813000.6281 +Dominican Republic,DOM,1997,19593869595.0191 +Dominican Republic,DOM,1998,21171523985.0642 +Dominican Republic,DOM,1999,21709726722.118 +Dominican Republic,DOM,2000,23996063503.0497 +Dominican Republic,DOM,2001,24892521236.9553 +Dominican Republic,DOM,2002,26571620978.7885 +Dominican Republic,DOM,2003,21277165885.6865 +Dominican Republic,DOM,2004,22039232609.9553 +Dominican Republic,DOM,2005,34004033803.9438 +Dominican Republic,DOM,2006,35952845582.5029 +Dominican Republic,DOM,2007,44169678153.1566 +Dominican Republic,DOM,2008,48288967303.4896 +Dominican Republic,DOM,2009,48376555305.6902 +Dominican Republic,DOM,2010,53954579003.5277 +Dominican Republic,DOM,2011,57746684847.0898 +Dominican Republic,DOM,2012,60613645121.3529 +Dominican Republic,DOM,2013,61965942056.6828 +Dominican Republic,DOM,2014,65231032303.2418 +Dominican Republic,DOM,2015,68102618092.1031 +Dominican Republic,DOM,2016,71583553488.0804 +Ecuador,ECU,1960,1010325138.03016 +Ecuador,ECU,1961,979108806.848646 +Ecuador,ECU,1962,958598195.033967 +Ecuador,ECU,1963,1038389642.31418 +Ecuador,ECU,1964,1156150890.06133 +Ecuador,ECU,1965,2387048255.45173 +Ecuador,ECU,1966,2429309513.80854 +Ecuador,ECU,1967,2553596091.82258 +Ecuador,ECU,1968,2582180794.1855 +Ecuador,ECU,1969,3112166848.3004 +Ecuador,ECU,1970,2862504169.99893 +Ecuador,ECU,1971,2754220263.02528 +Ecuador,ECU,1972,3185987234.84089 +Ecuador,ECU,1973,3891755551.94138 +Ecuador,ECU,1974,6599259420.99605 +Ecuador,ECU,1975,7731677256.80982 +Ecuador,ECU,1976,9091924304.83477 +Ecuador,ECU,1977,11026346589.5011 +Ecuador,ECU,1978,11922502170.6405 +Ecuador,ECU,1979,14175166007.5774 +Ecuador,ECU,1980,17881514682.8784 +Ecuador,ECU,1981,21810767209.3695 +Ecuador,ECU,1982,19929853574.6095 +Ecuador,ECU,1983,17152483214.3536 +Ecuador,ECU,1984,16912515183.2783 +Ecuador,ECU,1985,17149094589.9827 +Ecuador,ECU,1986,15314143988.0621 +Ecuador,ECU,1987,13945431882.2271 +Ecuador,ECU,1988,13051886552.3377 +Ecuador,ECU,1989,13890828707.6493 +Ecuador,ECU,1990,15239278100.3502 +Ecuador,ECU,1991,16988535267.6338 +Ecuador,ECU,1992,18094238119.0595 +Ecuador,ECU,1993,18938717358.6793 +Ecuador,ECU,1994,22708673336.6683 +Ecuador,ECU,1995,24432884442.2211 +Ecuador,ECU,1996,25226393196.5983 +Ecuador,ECU,1997,28162053026.5133 +Ecuador,ECU,1998,27981896948.4742 +Ecuador,ECU,1999,19645272636.3182 +Ecuador,ECU,2000,18327764882.4412 +Ecuador,ECU,2001,24468324000 +Ecuador,ECU,2002,28548945000 +Ecuador,ECU,2003,32432859000 +Ecuador,ECU,2004,36591661000 +Ecuador,ECU,2005,41507085000 +Ecuador,ECU,2006,46802044000 +Ecuador,ECU,2007,51007777000 +Ecuador,ECU,2008,61762635000 +Ecuador,ECU,2009,62519686000 +Ecuador,ECU,2010,69555367000 +Ecuador,ECU,2011,79276664000 +Ecuador,ECU,2012,87924544000 +Ecuador,ECU,2013,95129659000 +Ecuador,ECU,2014,102292260000 +Ecuador,ECU,2015,100176808000 +Ecuador,ECU,2016,97802211000 +"Egypt, Arab Rep.",EGY,1965,5111621013.54303 +"Egypt, Arab Rep.",EGY,1966,5339520612.99374 +"Egypt, Arab Rep.",EGY,1967,5579168509.50907 +"Egypt, Arab Rep.",EGY,1968,6109112149.53271 +"Egypt, Arab Rep.",EGY,1969,6861743341.40436 +"Egypt, Arab Rep.",EGY,1970,7682491836.22206 +"Egypt, Arab Rep.",EGY,1971,8266003570.51773 +"Egypt, Arab Rep.",EGY,1972,8763960703.20579 +"Egypt, Arab Rep.",EGY,1973,9616725366.34664 +"Egypt, Arab Rep.",EGY,1974,9015166839.80885 +"Egypt, Arab Rep.",EGY,1975,11437965585.2696 +"Egypt, Arab Rep.",EGY,1976,13360476861.9662 +"Egypt, Arab Rep.",EGY,1977,14636028766.883 +"Egypt, Arab Rep.",EGY,1978,14849909490.6004 +"Egypt, Arab Rep.",EGY,1979,18150000571.4286 +"Egypt, Arab Rep.",EGY,1980,22912500555.5556 +"Egypt, Arab Rep.",EGY,1981,23405404729.7297 +"Egypt, Arab Rep.",EGY,1982,25592365394.0887 +"Egypt, Arab Rep.",EGY,1983,28137369499.4179 +"Egypt, Arab Rep.",EGY,1984,30642873038.0563 +"Egypt, Arab Rep.",EGY,1985,34689560464.8728 +"Egypt, Arab Rep.",EGY,1986,35880262675.3976 +"Egypt, Arab Rep.",EGY,1987,40507934171.249 +"Egypt, Arab Rep.",EGY,1988,35044634014.7643 +"Egypt, Arab Rep.",EGY,1989,39648442534.0768 +"Egypt, Arab Rep.",EGY,1990,43130416913.4141 +"Egypt, Arab Rep.",EGY,1991,36970555898.9698 +"Egypt, Arab Rep.",EGY,1992,41855986519.4235 +"Egypt, Arab Rep.",EGY,1993,46578631452.581 +"Egypt, Arab Rep.",EGY,1994,51897983392.6453 +"Egypt, Arab Rep.",EGY,1995,60159245060.4542 +"Egypt, Arab Rep.",EGY,1996,67629716981.1321 +"Egypt, Arab Rep.",EGY,1997,78436578171.0914 +"Egypt, Arab Rep.",EGY,1998,84828807556.0803 +"Egypt, Arab Rep.",EGY,1999,90710704806.8416 +"Egypt, Arab Rep.",EGY,2000,99838543960.0763 +"Egypt, Arab Rep.",EGY,2001,97632008709.853 +"Egypt, Arab Rep.",EGY,2002,87850683978.6691 +"Egypt, Arab Rep.",EGY,2003,82924503942.6381 +"Egypt, Arab Rep.",EGY,2004,78845185293.4964 +"Egypt, Arab Rep.",EGY,2005,89685725230.2517 +"Egypt, Arab Rep.",EGY,2006,107484034870.974 +"Egypt, Arab Rep.",EGY,2007,130478960092.499 +"Egypt, Arab Rep.",EGY,2008,162818181818.182 +"Egypt, Arab Rep.",EGY,2009,188982374700.805 +"Egypt, Arab Rep.",EGY,2010,218888324504.753 +"Egypt, Arab Rep.",EGY,2011,236001858960.015 +"Egypt, Arab Rep.",EGY,2012,279372758361.832 +"Egypt, Arab Rep.",EGY,2013,288586231501.877 +"Egypt, Arab Rep.",EGY,2014,305529656458.438 +"Egypt, Arab Rep.",EGY,2015,332698041030.807 +"Egypt, Arab Rep.",EGY,2016,336296921758.782 +El Salvador,SLV,1965,877720000 +El Salvador,SLV,1966,929520000 +El Salvador,SLV,1967,976200000 +El Salvador,SLV,1968,1009760100 +El Salvador,SLV,1969,1049400000 +El Salvador,SLV,1970,1132920000 +El Salvador,SLV,1971,1186120000 +El Salvador,SLV,1972,1263720000 +El Salvador,SLV,1973,1442320000 +El Salvador,SLV,1974,1665880000 +El Salvador,SLV,1975,1884120100 +El Salvador,SLV,1976,2328280100 +El Salvador,SLV,1977,2941640100 +El Salvador,SLV,1978,3127960000 +El Salvador,SLV,1979,3463639900 +El Salvador,SLV,1980,3573959900 +El Salvador,SLV,1981,3437200200 +El Salvador,SLV,1982,3399189100 +El Salvador,SLV,1983,3506347800 +El Salvador,SLV,1984,3661683400 +El Salvador,SLV,1985,3800368600 +El Salvador,SLV,1986,3771663200 +El Salvador,SLV,1987,3958045800 +El Salvador,SLV,1988,4189880000 +El Salvador,SLV,1989,4372215300 +El Salvador,SLV,1990,4800900000 +El Salvador,SLV,1991,5311000000 +El Salvador,SLV,1992,5954700000 +El Salvador,SLV,1993,6938000000 +El Salvador,SLV,1994,8085600000 +El Salvador,SLV,1995,9500500000 +El Salvador,SLV,1996,10315500000 +El Salvador,SLV,1997,11134700000 +El Salvador,SLV,1998,12008400000 +El Salvador,SLV,1999,12464700000 +El Salvador,SLV,2000,13134100000 +El Salvador,SLV,2001,13812700000 +El Salvador,SLV,2002,14306700000 +El Salvador,SLV,2003,15046700000 +El Salvador,SLV,2004,15798300000 +El Salvador,SLV,2005,17093800000 +El Salvador,SLV,2006,18550700000 +El Salvador,SLV,2007,20104900000 +El Salvador,SLV,2008,21430950000 +El Salvador,SLV,2009,20661030000 +El Salvador,SLV,2010,21418330000 +El Salvador,SLV,2011,23139040000 +El Salvador,SLV,2012,23813600000 +El Salvador,SLV,2013,24350930000 +El Salvador,SLV,2014,25054230000 +El Salvador,SLV,2015,26052340000 +El Salvador,SLV,2016,26797470000 +Equatorial Guinea,GNQ,1962,9122751.45318345 +Equatorial Guinea,GNQ,1963,10840095.1283649 +Equatorial Guinea,GNQ,1964,12712471.3960211 +Equatorial Guinea,GNQ,1965,64748333.3333333 +Equatorial Guinea,GNQ,1966,69110000 +Equatorial Guinea,GNQ,1967,72317446.9327193 +Equatorial Guinea,GNQ,1968,67514285.7142857 +Equatorial Guinea,GNQ,1969,67225714.2857143 +Equatorial Guinea,GNQ,1970,66331428.5714286 +Equatorial Guinea,GNQ,1971,64946954.756798 +Equatorial Guinea,GNQ,1972,65429198.238708 +Equatorial Guinea,GNQ,1973,81203226.9138345 +Equatorial Guinea,GNQ,1974,94159862.7073691 +Equatorial Guinea,GNQ,1975,104295643.388437 +Equatorial Guinea,GNQ,1976,103653049.93797 +Equatorial Guinea,GNQ,1977,103987520.075827 +Equatorial Guinea,GNQ,1980,50642880.7737503 +Equatorial Guinea,GNQ,1981,36731422.8456914 +Equatorial Guinea,GNQ,1982,44294647.733479 +Equatorial Guinea,GNQ,1983,44442456.94764 +Equatorial Guinea,GNQ,1984,50320914.4065688 +Equatorial Guinea,GNQ,1985,62118564.8495425 +Equatorial Guinea,GNQ,1986,76407396.7552964 +Equatorial Guinea,GNQ,1987,93345847.7270323 +Equatorial Guinea,GNQ,1988,100534663.294927 +Equatorial Guinea,GNQ,1989,88265974.5843603 +Equatorial Guinea,GNQ,1990,112119406.548331 +Equatorial Guinea,GNQ,1991,110906032.075075 +Equatorial Guinea,GNQ,1992,134707184.355541 +Equatorial Guinea,GNQ,1993,136047896.155778 +Equatorial Guinea,GNQ,1994,100807001.813926 +Equatorial Guinea,GNQ,1995,141853368.256815 +Equatorial Guinea,GNQ,1996,232463036.435759 +Equatorial Guinea,GNQ,1997,442337849.474377 +Equatorial Guinea,GNQ,1998,370687618.717326 +Equatorial Guinea,GNQ,1999,621117885.668503 +Equatorial Guinea,GNQ,2000,1045998496.43872 +Equatorial Guinea,GNQ,2001,1461139022.02954 +Equatorial Guinea,GNQ,2002,1806742742.27311 +Equatorial Guinea,GNQ,2003,2484745935.09329 +Equatorial Guinea,GNQ,2004,4410764338.66733 +Equatorial Guinea,GNQ,2005,8217369092.65224 +Equatorial Guinea,GNQ,2006,10086528698.8604 +Equatorial Guinea,GNQ,2007,13071718758.7373 +Equatorial Guinea,GNQ,2008,19749893536.3204 +Equatorial Guinea,GNQ,2009,15027795173.2187 +Equatorial Guinea,GNQ,2010,16298542027.9965 +Equatorial Guinea,GNQ,2011,21329395900.871 +Equatorial Guinea,GNQ,2012,22389627294.4179 +Equatorial Guinea,GNQ,2013,21942597765.3631 +Equatorial Guinea,GNQ,2014,21461989483.1265 +Equatorial Guinea,GNQ,2015,12162117377.3923 +Equatorial Guinea,GNQ,2016,10178967206.0184 +Eritrea,ERI,1992,477101651.648376 +Eritrea,ERI,1993,467872714.755603 +Eritrea,ERI,1994,531688311.688312 +Eritrea,ERI,1995,578015625 +Eritrea,ERI,1996,693535954.190067 +Eritrea,ERI,1997,686490090.140141 +Eritrea,ERI,1998,745526154.93283 +Eritrea,ERI,1999,688921325.712043 +Eritrea,ERI,2000,706370815.584416 +Eritrea,ERI,2001,752368495.512622 +Eritrea,ERI,2002,729321366.651861 +Eritrea,ERI,2003,870247703.182758 +Eritrea,ERI,2004,1109054005.43971 +Eritrea,ERI,2005,1098425900.74116 +Eritrea,ERI,2006,1211161879.6748 +Eritrea,ERI,2007,1317974491.05691 +Eritrea,ERI,2008,1380188800 +Eritrea,ERI,2009,1856695551.21951 +Eritrea,ERI,2010,2117039512.19512 +Eritrea,ERI,2011,2607739837.39837 +Estonia,EST,1995,4373665145.55468 +Estonia,EST,1996,4746109767.1999 +Estonia,EST,1997,5066240419.29666 +Estonia,EST,1998,5617109244.63233 +Estonia,EST,1999,5726897998.29642 +Estonia,EST,2000,5685774808.80862 +Estonia,EST,2001,6245069734.13302 +Estonia,EST,2002,7322069511.16134 +Estonia,EST,2003,9833870709.12376 +Estonia,EST,2004,12059201242.236 +Estonia,EST,2005,14006088297.4754 +Estonia,EST,2006,16963625015.6818 +Estonia,EST,2007,22237061730.0849 +Estonia,EST,2008,24194039255.8957 +Estonia,EST,2009,19652492636.8436 +Estonia,EST,2010,19490936349.1753 +Estonia,EST,2011,23170239900.7654 +Estonia,EST,2012,23043864510.0543 +Estonia,EST,2013,25081185830.6876 +Estonia,EST,2014,26213940386.7883 +Estonia,EST,2015,22460470490.0133 +Estonia,EST,2016,23136741984.155 +Ethiopia,ETH,1981,7324903188.4058 +Ethiopia,ETH,1982,7707678019.32367 +Ethiopia,ETH,1983,8567890821.25604 +Ethiopia,ETH,1984,8096302367.14976 +Ethiopia,ETH,1985,9480840483.09179 +Ethiopia,ETH,1986,9848600869.56522 +Ethiopia,ETH,1987,10527338647.343 +Ethiopia,ETH,1988,10908935748.7923 +Ethiopia,ETH,1989,11476584879.2271 +Ethiopia,ETH,1990,12175166763.285 +Ethiopia,ETH,1991,13463868357.4879 +Ethiopia,ETH,1992,10492993077.6093 +Ethiopia,ETH,1993,8830712713.90781 +Ethiopia,ETH,1994,6927950564.55657 +Ethiopia,ETH,1995,7663984567.90123 +Ethiopia,ETH,1996,8547939730.62374 +Ethiopia,ETH,1997,8589211390.49612 +Ethiopia,ETH,1998,7818224905.55071 +Ethiopia,ETH,1999,7700833482.00615 +Ethiopia,ETH,2000,8242392103.68061 +Ethiopia,ETH,2001,8231326016.47494 +Ethiopia,ETH,2002,7850809498.16803 +Ethiopia,ETH,2003,8623691300.04079 +Ethiopia,ETH,2004,10131187261.4421 +Ethiopia,ETH,2005,12401139453.9738 +Ethiopia,ETH,2006,15280861834.6024 +Ethiopia,ETH,2007,19707616772.7996 +Ethiopia,ETH,2008,27066912635.2228 +Ethiopia,ETH,2009,32437389116.038 +Ethiopia,ETH,2010,29933790334.3418 +Ethiopia,ETH,2011,31952763089.33 +Ethiopia,ETH,2012,43310721414.0829 +Ethiopia,ETH,2013,47648211133.2183 +Ethiopia,ETH,2014,55612228233.5179 +Ethiopia,ETH,2015,64464423674.6569 +Ethiopia,ETH,2016,72374252815.3782 +Faroe Islands,FRO,1998,1105688872.97039 +Faroe Islands,FRO,1999,1125684470.05533 +Faroe Islands,FRO,2000,1062339943.83343 +Faroe Islands,FRO,2001,1154899793.33878 +Faroe Islands,FRO,2002,1268445919.41429 +Faroe Islands,FRO,2003,1486861878.95624 +Faroe Islands,FRO,2004,1683997930.26322 +Faroe Islands,FRO,2005,1730891409.07567 +Faroe Islands,FRO,2006,1970142377.91507 +Faroe Islands,FRO,2007,2278229533.05096 +Faroe Islands,FRO,2008,2413237402.14803 +Faroe Islands,FRO,2009,2257097731.55019 +Faroe Islands,FRO,2010,2301178416.00619 +Faroe Islands,FRO,2011,2468748767.9772 +Faroe Islands,FRO,2012,2356505419.09755 +Faroe Islands,FRO,2013,2613458942.48139 +Fiji,FJI,1960,112328422.113084 +Fiji,FJI,1961,116987784.913739 +Fiji,FJI,1962,122906434.957814 +Fiji,FJI,1963,129454728.623599 +Fiji,FJI,1964,140032741.468329 +Fiji,FJI,1965,147084750.031482 +Fiji,FJI,1966,150603925.515853 +Fiji,FJI,1967,162625885.863484 +Fiji,FJI,1968,166952937.135005 +Fiji,FJI,1969,182182067.703568 +Fiji,FJI,1970,219878482.173564 +Fiji,FJI,1971,247749327.721267 +Fiji,FJI,1972,316650508.967523 +Fiji,FJI,1973,425963359.355326 +Fiji,FJI,1974,558589870.903674 +Fiji,FJI,1975,684268280.812751 +Fiji,FJI,1976,694552411.718837 +Fiji,FJI,1977,719533137.126662 +Fiji,FJI,1978,829239489.844119 +Fiji,FJI,1979,1019743927.24662 +Fiji,FJI,1980,1202567359.4132 +Fiji,FJI,1981,1235899836.18067 +Fiji,FJI,1982,1194015444.01544 +Fiji,FJI,1983,1123107276.30285 +Fiji,FJI,1984,1177997413.63384 +Fiji,FJI,1985,1141210124.82663 +Fiji,FJI,1986,1290228616.82408 +Fiji,FJI,1987,1177908191.97685 +Fiji,FJI,1988,1109976927.91722 +Fiji,FJI,1989,1182686577.22645 +Fiji,FJI,1990,1337024782.22702 +Fiji,FJI,1991,1383843860.1247 +Fiji,FJI,1992,1531803060.54558 +Fiji,FJI,1993,1635426125.30808 +Fiji,FJI,1994,1825285158.11762 +Fiji,FJI,1995,1970347720.96992 +Fiji,FJI,1996,2129266728.42585 +Fiji,FJI,1997,2093994597.21549 +Fiji,FJI,1998,1656784779.545 +Fiji,FJI,1999,1942170999.18765 +Fiji,FJI,2000,1684109743.49338 +Fiji,FJI,2001,1660102345.60309 +Fiji,FJI,2002,1842691481.09196 +Fiji,FJI,2003,2315935752.71653 +Fiji,FJI,2004,2727507212.92556 +Fiji,FJI,2005,3006725014.78415 +Fiji,FJI,2006,3102741451.01664 +Fiji,FJI,2007,3405050611.68726 +Fiji,FJI,2008,3523185919.55826 +Fiji,FJI,2009,2870624635.68032 +Fiji,FJI,2010,3140508835.9485 +Fiji,FJI,2011,3774530615.65916 +Fiji,FJI,2012,3972012570.53467 +Fiji,FJI,2013,4190143206.25611 +Fiji,FJI,2014,4469810099.08335 +Fiji,FJI,2015,4391064780.77249 +Fiji,FJI,2016,4631626233.96998 +Finland,FIN,1960,5224102195.52771 +Finland,FIN,1961,5921659485.03284 +Finland,FIN,1962,6340580854.39073 +Finland,FIN,1963,6885920328.66187 +Finland,FIN,1964,7766655085.78588 +Finland,FIN,1965,8589340019.02985 +Finland,FIN,1966,9208524504.87684 +Finland,FIN,1967,9368954010.3132 +Finland,FIN,1968,8823033880.32993 +Finland,FIN,1969,10070766720.5011 +Finland,FIN,1970,11365953567.3839 +Finland,FIN,1971,12536710287.0134 +Finland,FIN,1972,14754136507.0261 +Finland,FIN,1973,19486826979.9284 +Finland,FIN,1974,24867278714.3532 +Finland,FIN,1975,29494515597.22 +Finland,FIN,1976,31873171718.726 +Finland,FIN,1977,33524682307.8058 +Finland,FIN,1978,36283091407.9422 +Finland,FIN,1979,44498283620.8213 +Finland,FIN,1980,53685049410.2646 +Finland,FIN,1981,52485533204.7396 +Finland,FIN,1982,52832120389.7866 +Finland,FIN,1983,51014090520.9223 +Finland,FIN,1984,52926394934.7052 +Finland,FIN,1985,55914236377.5902 +Finland,FIN,1986,73586676049.7302 +Finland,FIN,1987,91642093872.5822 +Finland,FIN,1988,109103056147.832 +Finland,FIN,1989,119064708327.56 +Finland,FIN,1990,141517648888.198 +Finland,FIN,1991,127866490222.026 +Finland,FIN,1992,112625431377.754 +Finland,FIN,1993,89255751014.885 +Finland,FIN,1994,103321570859.419 +Finland,FIN,1995,134199346405.229 +Finland,FIN,1996,132099404607.818 +Finland,FIN,1997,126833123353.568 +Finland,FIN,1998,133936359590.565 +Finland,FIN,1999,135225868314.511 +Finland,FIN,2000,125539893126.958 +Finland,FIN,2001,129250111856.823 +Finland,FIN,2002,139552983248.635 +Finland,FIN,2003,171071106094.808 +Finland,FIN,2004,196768065557.487 +Finland,FIN,2005,204436015420.968 +Finland,FIN,2006,216552502822.732 +Finland,FIN,2007,255384615384.615 +Finland,FIN,2008,283742493042.332 +Finland,FIN,2009,251499027507.641 +Finland,FIN,2010,247799815768.477 +Finland,FIN,2011,273674236772.815 +Finland,FIN,2012,256706466091.089 +Finland,FIN,2013,269980111642.898 +Finland,FIN,2014,272609288689.575 +Finland,FIN,2015,232361689855.142 +Finland,FIN,2016,236785046710.878 +France,FRA,1960,62651474946.6007 +France,FRA,1961,68346741504.4257 +France,FRA,1962,76313782251.6964 +France,FRA,1963,85551113767.3727 +France,FRA,1964,94906593388.3107 +France,FRA,1965,102160571409.274 +France,FRA,1966,110597467198.645 +France,FRA,1967,119466139619.589 +France,FRA,1968,129847107787.883 +France,FRA,1969,140725497222.277 +France,FRA,1970,148948860281.091 +France,FRA,1971,166564460755.298 +France,FRA,1972,204283485045.514 +France,FRA,1973,265381555686.506 +France,FRA,1974,286526186579.378 +France,FRA,1975,362000917852.226 +France,FRA,1976,373410270417.919 +France,FRA,1977,411464295266.114 +France,FRA,1978,508183139534.884 +France,FRA,1979,615834104224.484 +France,FRA,1980,703525302701.025 +France,FRA,1981,617589619794.81 +France,FRA,1982,586837009681.605 +France,FRA,1983,561852138738.274 +France,FRA,1984,532648802822.187 +France,FRA,1985,555197109067.017 +France,FRA,1986,774556302680.178 +France,FRA,1987,938368438284.405 +France,FRA,1988,1023504019381.13 +France,FRA,1989,1030122352457.33 +France,FRA,1990,1275300566196.84 +France,FRA,1991,1275563306592.26 +France,FRA,1992,1408724907063.2 +France,FRA,1993,1330094973361.13 +France,FRA,1994,1401636342155.01 +France,FRA,1995,1609892232882.11 +France,FRA,1996,1614245416078.98 +France,FRA,1997,1460709148123.17 +France,FRA,1998,1510758283299.98 +France,FRA,1999,1500275942893.67 +France,FRA,2000,1368438363736.87 +France,FRA,2001,1382218344519.02 +France,FRA,2002,1500337850555.24 +France,FRA,2003,1848124153498.87 +France,FRA,2004,2124112242364.04 +France,FRA,2005,2203678646934.46 +France,FRA,2006,2325011918203.49 +France,FRA,2007,2663112510265.54 +France,FRA,2008,2923465651091.26 +France,FRA,2009,2693827452070.02 +France,FRA,2010,2646837111794.78 +France,FRA,2011,2862680142625.14 +France,FRA,2012,2681416108537.39 +France,FRA,2013,2808511203185.39 +France,FRA,2014,2849305322684.76 +France,FRA,2015,2433562015516.21 +France,FRA,2016,2465453975282.24 +French Polynesia,PYF,1965,176534589.603389 +French Polynesia,PYF,1966,215659455.017302 +French Polynesia,PYF,1967,220984369.12915 +French Polynesia,PYF,1968,259590076.293001 +French Polynesia,PYF,1969,242943776.862299 +French Polynesia,PYF,1970,254035999.217197 +French Polynesia,PYF,1971,296613496.873269 +French Polynesia,PYF,1972,325843254.667123 +French Polynesia,PYF,1973,431254103.046477 +French Polynesia,PYF,1974,555337985.682991 +French Polynesia,PYF,1975,690319754.911192 +French Polynesia,PYF,1976,732286143.34291 +French Polynesia,PYF,1977,793193187.41557 +French Polynesia,PYF,1978,1005573294.20763 +French Polynesia,PYF,1979,1215031775.26795 +French Polynesia,PYF,1980,1362151523.68993 +French Polynesia,PYF,1981,1279972866.38172 +French Polynesia,PYF,1982,1286462642.63697 +French Polynesia,PYF,1983,1335895286.3918 +French Polynesia,PYF,1984,1378991403.37881 +French Polynesia,PYF,1985,1507230778.89921 +French Polynesia,PYF,1986,2301514717.29807 +French Polynesia,PYF,1987,2543199148.3893 +French Polynesia,PYF,1988,2687472829.62988 +French Polynesia,PYF,1989,2636461517.1052 +French Polynesia,PYF,1990,3181206304.81549 +French Polynesia,PYF,1991,3267367609.89528 +French Polynesia,PYF,1992,3558215110.24809 +French Polynesia,PYF,1993,3694600399.89225 +French Polynesia,PYF,1994,3522272321.40766 +French Polynesia,PYF,1995,3982374845.92709 +French Polynesia,PYF,1996,3954696873.74892 +French Polynesia,PYF,1997,3567062511.87293 +French Polynesia,PYF,1998,3775160797.38928 +French Polynesia,PYF,1999,3797016068.69688 +French Polynesia,PYF,2000,3447543137.9415 +Gabon,GAB,1960,141468977.570007 +Gabon,GAB,1961,167637907.381723 +Gabon,GAB,1962,182796536.499884 +Gabon,GAB,1963,154480244.246844 +Gabon,GAB,1964,215679855.272516 +Gabon,GAB,1965,226474285.587109 +Gabon,GAB,1966,245849781.7159 +Gabon,GAB,1967,271543680.279285 +Gabon,GAB,1968,294468564.534369 +Gabon,GAB,1969,318124701.048949 +Gabon,GAB,1970,323802475.480977 +Gabon,GAB,1971,381687073.058558 +Gabon,GAB,1972,430508357.723916 +Gabon,GAB,1973,722780701.123251 +Gabon,GAB,1974,1544216003.9841 +Gabon,GAB,1975,2157592936.60712 +Gabon,GAB,1976,3009409970.90514 +Gabon,GAB,1977,2809349074.17738 +Gabon,GAB,1978,2389479269.18878 +Gabon,GAB,1979,3030251116.35999 +Gabon,GAB,1980,4279637933.85126 +Gabon,GAB,1981,3862269126.92642 +Gabon,GAB,1982,3618007844.44919 +Gabon,GAB,1983,3391275731.31834 +Gabon,GAB,1984,3561451562.23569 +Gabon,GAB,1985,3339914759.37269 +Gabon,GAB,1986,3403638193.57912 +Gabon,GAB,1987,3281797038.66591 +Gabon,GAB,1988,3834503378.35525 +Gabon,GAB,1989,4186411457.4569 +Gabon,GAB,1990,5952293765.84484 +Gabon,GAB,1991,5402919956.93831 +Gabon,GAB,1992,5592390848.52648 +Gabon,GAB,1993,4378645081.01769 +Gabon,GAB,1994,4190819314.02958 +Gabon,GAB,1995,4958845906.34769 +Gabon,GAB,1996,5694040336.82571 +Gabon,GAB,1997,5326816858.99586 +Gabon,GAB,1998,4483417119.83928 +Gabon,GAB,1999,4662992036.2073 +Gabon,GAB,2000,5067865320.7979 +Gabon,GAB,2001,5018874179.18704 +Gabon,GAB,2002,5310381151.35952 +Gabon,GAB,2003,6497305662.09274 +Gabon,GAB,2004,7756293574.98077 +Gabon,GAB,2005,9458884812.18106 +Gabon,GAB,2006,10154041929.6521 +Gabon,GAB,2007,12438956756.4455 +Gabon,GAB,2008,15508574820.3516 +Gabon,GAB,2009,12065138272.7538 +Gabon,GAB,2010,14358584300.3006 +Gabon,GAB,2011,18186478119.9582 +Gabon,GAB,2012,17171447372.3334 +Gabon,GAB,2013,17590716232.4913 +Gabon,GAB,2014,18179717776.1597 +Gabon,GAB,2015,14262032470.9041 +Gabon,GAB,2016,14213558130.1817 +"Gambia, The",GMB,1966,44212353.6988296 +"Gambia, The",GMB,1967,46695363.1558873 +"Gambia, The",GMB,1968,41160658.5705371 +"Gambia, The",GMB,1969,45168722.6995632 +"Gambia, The",GMB,1970,52296836.749388 +"Gambia, The",GMB,1971,55728608.974983 +"Gambia, The",GMB,1972,59161544.9957528 +"Gambia, The",GMB,1973,75187969.924812 +"Gambia, The",GMB,1974,95797533.4619206 +"Gambia, The",GMB,1975,115182522.123894 +"Gambia, The",GMB,1976,112189468.481826 +"Gambia, The",GMB,1977,138094243.349324 +"Gambia, The",GMB,1978,171836793.402695 +"Gambia, The",GMB,1979,207114382.546071 +"Gambia, The",GMB,1980,241080708.89018 +"Gambia, The",GMB,1981,218764445.784343 +"Gambia, The",GMB,1982,216051495.959817 +"Gambia, The",GMB,1983,213446562.57106 +"Gambia, The",GMB,1984,177338801.93075 +"Gambia, The",GMB,1985,225724851.691107 +"Gambia, The",GMB,1986,185646209.386282 +"Gambia, The",GMB,1987,220626484.224811 +"Gambia, The",GMB,1988,266673126.229801 +"Gambia, The",GMB,1989,284119692.49433 +"Gambia, The",GMB,1990,317083373.524559 +"Gambia, The",GMB,1991,690314321.374999 +"Gambia, The",GMB,1992,714255460.503389 +"Gambia, The",GMB,1993,755042548.055824 +"Gambia, The",GMB,1994,746491692.583857 +"Gambia, The",GMB,1995,785996982.492168 +"Gambia, The",GMB,1996,848237108.56163 +"Gambia, The",GMB,1997,803630742.53446 +"Gambia, The",GMB,1998,840285264.631545 +"Gambia, The",GMB,1999,814723460.08372 +"Gambia, The",GMB,2000,782915402.421095 +"Gambia, The",GMB,2001,687408804.630527 +"Gambia, The",GMB,2002,578236035.104279 +"Gambia, The",GMB,2003,487038821.611959 +"Gambia, The",GMB,2004,578785278.7703 +"Gambia, The",GMB,2005,624174723.713404 +"Gambia, The",GMB,2006,655068695.952711 +"Gambia, The",GMB,2007,798870894.208271 +"Gambia, The",GMB,2008,965769128.170004 +"Gambia, The",GMB,2009,900639747.93953 +"Gambia, The",GMB,2010,952429030.415536 +"Gambia, The",GMB,2011,904256643.415984 +"Gambia, The",GMB,2012,912569686.7859 +"Gambia, The",GMB,2013,903779326.206421 +"Gambia, The",GMB,2014,849122624.781348 +"Gambia, The",GMB,2015,938794719.358588 +"Gambia, The",GMB,2016,964599177.535474 +Georgia,GEO,1990,7753501867.76095 +Georgia,GEO,1991,6357615894.03974 +Georgia,GEO,1992,3690328963.64086 +Georgia,GEO,1993,2701181331.30816 +Georgia,GEO,1994,2513870586.73344 +Georgia,GEO,1995,2693731865.97036 +Georgia,GEO,1996,3094915505.9093 +Georgia,GEO,1997,3510540809.24855 +Georgia,GEO,1998,3613500117.24928 +Georgia,GEO,1999,2800024313.95144 +Georgia,GEO,2000,3057453482.55826 +Georgia,GEO,2001,3219487824.87911 +Georgia,GEO,2002,3395778673.79183 +Georgia,GEO,2003,3991374548.50512 +Georgia,GEO,2004,5125273880.74998 +Georgia,GEO,2005,6410941012.59189 +Georgia,GEO,2006,7745406200.85374 +Georgia,GEO,2007,10172869679.7366 +Georgia,GEO,2008,12795044472.7663 +Georgia,GEO,2009,10766809099.0721 +Georgia,GEO,2010,11638536834.4274 +Georgia,GEO,2011,14434619982.2117 +Georgia,GEO,2012,15846474595.773 +Georgia,GEO,2013,16140047012.1438 +Georgia,GEO,2014,16509305827.7171 +Georgia,GEO,2015,13993546732.4726 +Georgia,GEO,2016,14332880086.196 +Germany,DEU,1970,215021806498.156 +Germany,DEU,1971,249039217364.635 +Germany,DEU,1972,298667219346.133 +Germany,DEU,1973,396866742553.97 +Germany,DEU,1974,443618642959.716 +Germany,DEU,1975,488780155338.262 +Germany,DEU,1976,517787921003.573 +Germany,DEU,1977,598226205424.071 +Germany,DEU,1978,737668356280.428 +Germany,DEU,1979,878010536975.776 +Germany,DEU,1980,946695355820.96 +Germany,DEU,1981,797443405711.813 +Germany,DEU,1982,773638200773.757 +Germany,DEU,1983,767768378016.086 +Germany,DEU,1984,722367608343.069 +Germany,DEU,1985,729763282952.432 +Germany,DEU,1986,1042300769791.95 +Germany,DEU,1987,1293264353318.82 +Germany,DEU,1988,1395931548502.06 +Germany,DEU,1989,1393674332154.37 +Germany,DEU,1990,1764967948916.6 +Germany,DEU,1991,1861873895109.02 +Germany,DEU,1992,2123130870381.97 +Germany,DEU,1993,2068555542410.98 +Germany,DEU,1994,2205966011811.5 +Germany,DEU,1995,2591620035485.19 +Germany,DEU,1996,2503665193657.4 +Germany,DEU,1997,2218689375140.99 +Germany,DEU,1998,2243225519617.65 +Germany,DEU,1999,2199957383336.88 +Germany,DEU,2000,1949953934033.54 +Germany,DEU,2001,1950648769574.94 +Germany,DEU,2002,2079136081309.99 +Germany,DEU,2003,2505733634311.51 +Germany,DEU,2004,2819245095604.67 +Germany,DEU,2005,2861410272354.18 +Germany,DEU,2006,3002446368084.31 +Germany,DEU,2007,3439953462907.2 +Germany,DEU,2008,3752365607148.09 +Germany,DEU,2009,3418005001389.27 +Germany,DEU,2010,3417094562648.95 +Germany,DEU,2011,3757698281117.55 +Germany,DEU,2012,3543983909148.01 +Germany,DEU,2013,3752513503278.41 +Germany,DEU,2014,3879276587198.91 +Germany,DEU,2015,3363599907529.78 +Germany,DEU,2016,3466756880460.62 +Ghana,GHA,1960,1217230094.97205 +Ghana,GHA,1961,1302674324.88378 +Ghana,GHA,1962,1382515654.47343 +Ghana,GHA,1963,1540797588.57221 +Ghana,GHA,1964,1731296199.52296 +Ghana,GHA,1965,2053462968.0426 +Ghana,GHA,1966,2126300672.22965 +Ghana,GHA,1967,1747187644.9031 +Ghana,GHA,1968,1666909517.65205 +Ghana,GHA,1969,1962050555.7775 +Ghana,GHA,1970,2215028588.45646 +Ghana,GHA,1971,2417108577.52739 +Ghana,GHA,1972,2112293279.98507 +Ghana,GHA,1973,2465493032.25878 +Ghana,GHA,1974,2894409912.1709 +Ghana,GHA,1975,2810106390.10618 +Ghana,GHA,1976,2765254360.20623 +Ghana,GHA,1977,3189428571.42857 +Ghana,GHA,1978,3662478172.79996 +Ghana,GHA,1979,4020227931.15097 +Ghana,GHA,1980,4445228057.45354 +Ghana,GHA,1981,4222441673.17049 +Ghana,GHA,1982,4035994383.38367 +Ghana,GHA,1983,4057275077.68156 +Ghana,GHA,1984,4412279865.40315 +Ghana,GHA,1985,4504342152.98679 +Ghana,GHA,1986,5727602648.75537 +Ghana,GHA,1987,5074829931.97279 +Ghana,GHA,1988,5197840972.6857 +Ghana,GHA,1989,5251764269.91812 +Ghana,GHA,1990,5889174833.90034 +Ghana,GHA,1991,6596546195.65217 +Ghana,GHA,1992,6413901601.83066 +Ghana,GHA,1993,5966255778.12018 +Ghana,GHA,1994,5444560669.45607 +Ghana,GHA,1995,6465137614.6789 +Ghana,GHA,1996,6934984709.48012 +Ghana,GHA,1997,6891308593.75 +Ghana,GHA,1998,7480968858.13149 +Ghana,GHA,1999,7719354838.70968 +Ghana,GHA,2000,4983024408.14828 +Ghana,GHA,2001,5314909953.92992 +Ghana,GHA,2002,6166330136.2948 +Ghana,GHA,2003,7632406552.83803 +Ghana,GHA,2004,8881368538.07671 +Ghana,GHA,2005,10731634116.7384 +Ghana,GHA,2006,20409257610.4746 +Ghana,GHA,2007,24758819717.7074 +Ghana,GHA,2008,28526891010.4925 +Ghana,GHA,2009,25977847813.7422 +Ghana,GHA,2010,32174772955.9748 +Ghana,GHA,2011,39566292432.8615 +Ghana,GHA,2012,41939728978.7281 +Ghana,GHA,2013,47805069494.9082 +Ghana,GHA,2014,38616536131.648 +Ghana,GHA,2015,37543361203.5609 +Ghana,GHA,2016,42689783733.873 +Greece,GRC,1960,4446528164.67559 +Greece,GRC,1961,5016048786.22753 +Greece,GRC,1962,5327573509.09843 +Greece,GRC,1963,5949478034.88751 +Greece,GRC,1964,6680298250.57961 +Greece,GRC,1965,7600579093.1158 +Greece,GRC,1966,8455611129.27936 +Greece,GRC,1967,9136711287.82434 +Greece,GRC,1968,9915140546.35072 +Greece,GRC,1969,11266091570.5718 +Greece,GRC,1970,13139862500 +Greece,GRC,1971,14591755681.8182 +Greece,GRC,1972,16885506818.1818 +Greece,GRC,1973,22347844649.0219 +Greece,GRC,1974,25351305681.8182 +Greece,GRC,1975,28525872476.0893 +Greece,GRC,1976,31152840485.0746 +Greece,GRC,1977,36176233117.4838 +Greece,GRC,1978,44270203153.9889 +Greece,GRC,1979,54481875804.9678 +Greece,GRC,1980,56829663469.2246 +Greece,GRC,1981,52346507380.0738 +Greece,GRC,1982,54617991326.5306 +Greece,GRC,1983,49428872678.0186 +Greece,GRC,1984,48020024788.3918 +Greece,GRC,1985,47820850974.5867 +Greece,GRC,1986,56379593719.5716 +Greece,GRC,1987,65652751132.3603 +Greece,GRC,1988,76261278404.9964 +Greece,GRC,1989,79169043642.4675 +Greece,GRC,1990,97891090928.6328 +Greece,GRC,1991,105143232379.884 +Greece,GRC,1992,116224673042.546 +Greece,GRC,1993,108809058858.502 +Greece,GRC,1994,116601802106.742 +Greece,GRC,1995,136878366230.328 +Greece,GRC,1996,145861612825.595 +Greece,GRC,1997,143157600024.959 +Greece,GRC,1998,144428172835.236 +Greece,GRC,1999,142540728958.023 +Greece,GRC,2000,130133845771.144 +Greece,GRC,2001,136191353467.562 +Greece,GRC,2002,153830947016.751 +Greece,GRC,2003,201924270316.027 +Greece,GRC,2004,240521260988.329 +Greece,GRC,2005,247783001865.44 +Greece,GRC,2006,273317737046.795 +Greece,GRC,2007,318497936901.177 +Greece,GRC,2008,354460802548.704 +Greece,GRC,2009,330000252153.376 +Greece,GRC,2010,299361576558.217 +Greece,GRC,2011,287797822093.178 +Greece,GRC,2012,245670666639.047 +Greece,GRC,2013,239862011450.103 +Greece,GRC,2014,236079774688.445 +Greece,GRC,2015,194860187692.451 +Greece,GRC,2016,194558651275.836 +Greenland,GRL,1970,69520026.6666667 +Greenland,GRL,1971,88570952.8688525 +Greenland,GRL,1972,106101175.65798 +Greenland,GRL,1973,140153748.243656 +Greenland,GRL,1974,169918948.629182 +Greenland,GRL,1975,211194305.702999 +Greenland,GRL,1976,240780413.56493 +Greenland,GRL,1977,282269373.001066 +Greenland,GRL,1978,355989047.256374 +Greenland,GRL,1979,420642463.409998 +Greenland,GRL,1980,476055288.418886 +Greenland,GRL,1981,435746974.759244 +Greenland,GRL,1982,402405069.367769 +Greenland,GRL,1983,416183706.943685 +Greenland,GRL,1984,379371608.442925 +Greenland,GRL,1985,412876071.118493 +Greenland,GRL,1986,603015696.452849 +Greenland,GRL,1987,787392365.831908 +Greenland,GRL,1988,898611007.947709 +Greenland,GRL,1989,929796722.387896 +Greenland,GRL,1990,1018970364.86443 +Greenland,GRL,1991,1016493394.8253 +Greenland,GRL,1992,1037921836.9477 +Greenland,GRL,1993,927219728.866886 +Greenland,GRL,1994,1005879948.43254 +Greenland,GRL,1995,1208946165.92889 +Greenland,GRL,1996,1197509786.67632 +Greenland,GRL,1997,1072147778.03013 +Greenland,GRL,1998,1149862702.96084 +Greenland,GRL,1999,1131561595.13775 +Greenland,GRL,2000,1068030829.75591 +Greenland,GRL,2001,1086172922.57413 +Greenland,GRL,2002,1169138789.31435 +Greenland,GRL,2003,1407866174.84099 +Greenland,GRL,2004,1621822369.84861 +Greenland,GRL,2005,1656172355.71712 +Greenland,GRL,2006,1818759669.06572 +Greenland,GRL,2007,2049139372.11823 +Greenland,GRL,2008,2310743218.06163 +Greenland,GRL,2009,2324870823.92882 +Greenland,GRL,2010,2306502373.71313 +Greenland,GRL,2011,2515245776.44495 +Greenland,GRL,2012,2408666378.93828 +Greenland,GRL,2013,2483396542.2075 +Greenland,GRL,2014,2551126948.77506 +Greenland,GRL,2015,2220380802.33059 +Grenada,GRD,1977,71494495.1851852 +Grenada,GRD,1978,88322386.2962963 +Grenada,GRD,1979,102244362.222222 +Grenada,GRD,1980,110900457.037037 +Grenada,GRD,1981,115651918.888889 +Grenada,GRD,1982,125435590 +Grenada,GRD,1983,131803552.222222 +Grenada,GRD,1984,145533310.740741 +Grenada,GRD,1985,167728455.185185 +Grenada,GRD,1986,187589522.592593 +Grenada,GRD,1987,215009569.62963 +Grenada,GRD,1988,236357523.703704 +Grenada,GRD,1989,267327642.222222 +Grenada,GRD,1990,278098762.962963 +Grenada,GRD,1991,300757900 +Grenada,GRD,1992,310160455.925926 +Grenada,GRD,1993,309812192.962963 +Grenada,GRD,1994,325111808.148148 +Grenada,GRD,1995,342172523.703704 +Grenada,GRD,1996,366911440.37037 +Grenada,GRD,1997,392190585.925926 +Grenada,GRD,1998,445903598.888889 +Grenada,GRD,1999,482009371.481481 +Grenada,GRD,2000,520044370.37037 +Grenada,GRD,2001,520444185.185185 +Grenada,GRD,2002,540336925.925926 +Grenada,GRD,2003,591018407.407407 +Grenada,GRD,2004,599118592.592593 +Grenada,GRD,2005,695370296.296296 +Grenada,GRD,2006,698518518.518518 +Grenada,GRD,2007,758518518.518518 +Grenada,GRD,2008,825925925.925926 +Grenada,GRD,2009,771278111.111111 +Grenada,GRD,2010,771015888.888889 +Grenada,GRD,2011,778648666.666667 +Grenada,GRD,2012,799882148.148148 +Grenada,GRD,2013,842571333.333333 +Grenada,GRD,2014,911481481.481481 +Grenada,GRD,2015,984074074.074074 +Grenada,GRD,2016,1016208194.81481 +Guam,GUM,2002,3385000000 +Guam,GUM,2003,3560000000 +Guam,GUM,2004,3857000000 +Guam,GUM,2005,4197000000 +Guam,GUM,2006,4213000000 +Guam,GUM,2007,4375000000 +Guam,GUM,2008,4621000000 +Guam,GUM,2009,4781000000 +Guam,GUM,2010,4895000000 +Guam,GUM,2011,4928000000 +Guam,GUM,2012,5199000000 +Guam,GUM,2013,5364000000 +Guam,GUM,2014,5566000000 +Guam,GUM,2015,5734000000 +Guatemala,GTM,1960,1043599900 +Guatemala,GTM,1961,1076699900 +Guatemala,GTM,1962,1143600000 +Guatemala,GTM,1963,1262800000 +Guatemala,GTM,1964,1299099900 +Guatemala,GTM,1965,1331399900 +Guatemala,GTM,1966,1390700000 +Guatemala,GTM,1967,1453500000 +Guatemala,GTM,1968,1610500000 +Guatemala,GTM,1969,1715399900 +Guatemala,GTM,1970,1904000000 +Guatemala,GTM,1971,1984800000 +Guatemala,GTM,1972,2101300000 +Guatemala,GTM,1973,2569200100 +Guatemala,GTM,1974,3161499900 +Guatemala,GTM,1975,3645900000 +Guatemala,GTM,1976,4365300200 +Guatemala,GTM,1977,5480500200 +Guatemala,GTM,1978,6070600200 +Guatemala,GTM,1979,6902600200 +Guatemala,GTM,1980,7878700000 +Guatemala,GTM,1981,8607500300 +Guatemala,GTM,1982,8716999700 +Guatemala,GTM,1983,9050000400 +Guatemala,GTM,1984,9470000100 +Guatemala,GTM,1985,9721652086.95652 +Guatemala,GTM,1986,7231963515.98173 +Guatemala,GTM,1987,7084399840 +Guatemala,GTM,1988,7841602824.42748 +Guatemala,GTM,1989,8410724360.79546 +Guatemala,GTM,1990,7650125217.35253 +Guatemala,GTM,1991,9406097735.09117 +Guatemala,GTM,1992,10440842165.3193 +Guatemala,GTM,1993,11399942453.0646 +Guatemala,GTM,1994,12983235568.2292 +Guatemala,GTM,1995,14655487455.7333 +Guatemala,GTM,1996,15674852771.1356 +Guatemala,GTM,1997,17790095900.9809 +Guatemala,GTM,1998,19395461989.5391 +Guatemala,GTM,1999,18318512501.0256 +Guatemala,GTM,2000,19288926545.3759 +Guatemala,GTM,2001,18702802394.8286 +Guatemala,GTM,2002,20776669466.6053 +Guatemala,GTM,2003,21917706490.5299 +Guatemala,GTM,2004,23965275995.7214 +Guatemala,GTM,2005,27211377225.2715 +Guatemala,GTM,2006,30231249362.0604 +Guatemala,GTM,2007,34113107085.6085 +Guatemala,GTM,2008,39136893345.1501 +Guatemala,GTM,2009,37733994976.4137 +Guatemala,GTM,2010,41338595380.8159 +Guatemala,GTM,2011,47654841112.8523 +Guatemala,GTM,2012,50388454861.1111 +Guatemala,GTM,2013,53851058955.2999 +Guatemala,GTM,2014,58722323918.1604 +Guatemala,GTM,2015,63767597193.9175 +Guatemala,GTM,2016,68763255963.8943 +Guinea,GIN,1986,1922600899.3841 +Guinea,GIN,1987,2041538057.02893 +Guinea,GIN,1988,2384295763.72549 +Guinea,GIN,1989,2432029380.43694 +Guinea,GIN,1990,2666616176.91609 +Guinea,GIN,1991,3014890569.04099 +Guinea,GIN,1992,3284625277.16186 +Guinea,GIN,1993,3279063317.63475 +Guinea,GIN,1994,3383218922.79336 +Guinea,GIN,1995,3693753379.05992 +Guinea,GIN,1996,3869032270.91633 +Guinea,GIN,1997,3783788551.0819 +Guinea,GIN,1998,3588376057.01536 +Guinea,GIN,1999,3461282293.64624 +Guinea,GIN,2000,2995360969.16199 +Guinea,GIN,2001,2833442750.43639 +Guinea,GIN,2002,2949637039.04424 +Guinea,GIN,2003,3446442218.89829 +Guinea,GIN,2004,3666349049.42641 +Guinea,GIN,2005,2937071767.25576 +Guinea,GIN,2006,2931625104.50109 +Guinea,GIN,2007,4134173275.1244 +Guinea,GIN,2008,4515824647.43939 +Guinea,GIN,2009,4609923756.18485 +Guinea,GIN,2010,4735956493.06479 +Guinea,GIN,2011,5067360009.39196 +Guinea,GIN,2012,5667229758.9878 +Guinea,GIN,2013,6231725484.55943 +Guinea,GIN,2014,6624068015.50039 +Guinea,GIN,2015,6699203543.29047 +Guinea,GIN,2016,6298927773.1587 +Guinea-Bissau,GNB,1970,78733594.8444943 +Guinea-Bissau,GNB,1971,78540057.1428572 +Guinea-Bissau,GNB,1972,87702828.5714286 +Guinea-Bissau,GNB,1973,89374237.2881356 +Guinea-Bissau,GNB,1974,98775328.9473684 +Guinea-Bissau,GNB,1975,108985740.159382 +Guinea-Bissau,GNB,1976,112386489.013558 +Guinea-Bissau,GNB,1977,114971207.198201 +Guinea-Bissau,GNB,1978,122666858.789625 +Guinea-Bissau,GNB,1979,118537875.125881 +Guinea-Bissau,GNB,1980,110653830.726033 +Guinea-Bissau,GNB,1981,154731969.69697 +Guinea-Bissau,GNB,1982,165523634.49692 +Guinea-Bissau,GNB,1983,163577538.330494 +Guinea-Bissau,GNB,1984,138478900.626886 +Guinea-Bissau,GNB,1985,143856253.125 +Guinea-Bissau,GNB,1986,130225018.751674 +Guinea-Bissau,GNB,1987,173836362.010107 +Guinea-Bissau,GNB,1988,164458120.314078 +Guinea-Bissau,GNB,1989,213143016.443434 +Guinea-Bissau,GNB,1990,243961995.509711 +Guinea-Bissau,GNB,1991,257150573.215747 +Guinea-Bissau,GNB,1992,226313492.713567 +Guinea-Bissau,GNB,1993,236880813.821765 +Guinea-Bissau,GNB,1994,235619994.759074 +Guinea-Bissau,GNB,1995,253966919.939838 +Guinea-Bissau,GNB,1996,270419779.418107 +Guinea-Bissau,GNB,1997,268551010.941186 +Guinea-Bissau,GNB,1998,206457553.397101 +Guinea-Bissau,GNB,1999,224446652.14622 +Guinea-Bissau,GNB,2000,370173851.946119 +Guinea-Bissau,GNB,2001,392278168.199897 +Guinea-Bissau,GNB,2002,415843481.998691 +Guinea-Bissau,GNB,2003,476388260.639232 +Guinea-Bissau,GNB,2004,531109356.165462 +Guinea-Bissau,GNB,2005,586791883.717707 +Guinea-Bissau,GNB,2006,591829908.426264 +Guinea-Bissau,GNB,2007,695606288.605845 +Guinea-Bissau,GNB,2008,864136712.986648 +Guinea-Bissau,GNB,2009,825828703.628208 +Guinea-Bissau,GNB,2010,846332456.382994 +Guinea-Bissau,GNB,2011,1105497854.79398 +Guinea-Bissau,GNB,2012,995582062.343018 +Guinea-Bissau,GNB,2013,1026664188.52724 +Guinea-Bissau,GNB,2014,1109007299.94033 +Guinea-Bissau,GNB,2015,1056776896.92865 +Guinea-Bissau,GNB,2016,1126122707.91534 +Guyana,GUY,1960,170215248.206265 +Guyana,GUY,1961,185848451.262906 +Guyana,GUY,1962,194948375.430205 +Guyana,GUY,1963,175756868.692761 +Guyana,GUY,1964,194773376.888526 +Guyana,GUY,1965,213235294.117647 +Guyana,GUY,1966,228705882.352941 +Guyana,GUY,1967,250176470.588235 +Guyana,GUY,1968,229750000 +Guyana,GUY,1969,249300000 +Guyana,GUY,1970,267800000 +Guyana,GUY,1971,282050000 +Guyana,GUY,1972,285380952.380952 +Guyana,GUY,1973,307047619.047619 +Guyana,GUY,1974,433954545.454545 +Guyana,GUY,1975,494791666.666667 +Guyana,GUY,1976,454440000 +Guyana,GUY,1977,449880000 +Guyana,GUY,1978,507080000 +Guyana,GUY,1979,530440000 +Guyana,GUY,1980,603200000 +Guyana,GUY,1981,570357107.142857 +Guyana,GUY,1982,482000000 +Guyana,GUY,1983,489333333.333333 +Guyana,GUY,1984,437631605.263158 +Guyana,GUY,1985,453488372.093023 +Guyana,GUY,1986,504651139.534884 +Guyana,GUY,1987,354591846.938775 +Guyana,GUY,1988,413799990 +Guyana,GUY,1989,379779389.705882 +Guyana,GUY,1990,396582263.291139 +Guyana,GUY,1991,336708419.499106 +Guyana,GUY,1992,368281378.896883 +Guyana,GUY,1993,442273433.179724 +Guyana,GUY,1994,540874934.201012 +Guyana,GUY,1995,621626785.915493 +Guyana,GUY,1996,705406001.424501 +Guyana,GUY,1997,749138009.564539 +Guyana,GUY,1998,717530683.169567 +Guyana,GUY,1999,694754988.258295 +Guyana,GUY,2000,712667896.727512 +Guyana,GUY,2001,696281471.678532 +Guyana,GUY,2002,722460886.371384 +Guyana,GUY,2003,741929342.788749 +Guyana,GUY,2004,785918769.587635 +Guyana,GUY,2005,824880550.343965 +Guyana,GUY,2006,1458446872.26976 +Guyana,GUY,2007,1740334781.83731 +Guyana,GUY,2008,1922598121.23066 +Guyana,GUY,2009,2025565089.48272 +Guyana,GUY,2010,2259288396.24467 +Guyana,GUY,2011,2576602497.33479 +Guyana,GUY,2012,2851154075.95385 +Guyana,GUY,2013,2990006533.77749 +Guyana,GUY,2014,3077086275.94585 +Guyana,GUY,2015,3179104116.22276 +Guyana,GUY,2016,3446266343.82567 +Haiti,HTI,1991,3473562850.25798 +Haiti,HTI,1992,2257129873.66261 +Haiti,HTI,1993,1878253818.07845 +Haiti,HTI,1994,2167569095.11746 +Haiti,HTI,1995,2813313281.80715 +Haiti,HTI,1996,2907517543.3645 +Haiti,HTI,1997,3338949151.59927 +Haiti,HTI,1998,3723903723.63772 +Haiti,HTI,1999,4153725966.87677 +Haiti,HTI,2000,3953839593.78427 +Haiti,HTI,2001,3596448035.44747 +Haiti,HTI,2002,3472194099.40131 +Haiti,HTI,2003,2960306120.93557 +Haiti,HTI,2004,3537721020.41158 +Haiti,HTI,2005,4310356252.83669 +Haiti,HTI,2006,4756204069.61876 +Haiti,HTI,2007,5885321655.91684 +Haiti,HTI,2008,6548530572.35291 +Haiti,HTI,2009,6584649419.28348 +Haiti,HTI,2010,6622541528.56888 +Haiti,HTI,2011,7516834160.25277 +Haiti,HTI,2012,7890216507.68913 +Haiti,HTI,2013,8452509315.87722 +Haiti,HTI,2014,8776360623.29898 +Haiti,HTI,2015,8724656126.49849 +Haiti,HTI,2016,8022638721.92013 +Honduras,HND,1960,335650000 +Honduras,HND,1961,356200000 +Honduras,HND,1962,387750000 +Honduras,HND,1963,410200000 +Honduras,HND,1964,457000000 +Honduras,HND,1965,508650000 +Honduras,HND,1966,549950000 +Honduras,HND,1967,598100000 +Honduras,HND,1968,646800000 +Honduras,HND,1969,668000050 +Honduras,HND,1970,723000000 +Honduras,HND,1971,731000000 +Honduras,HND,1972,802999950 +Honduras,HND,1973,912499950 +Honduras,HND,1974,1034500000 +Honduras,HND,1975,1124000000 +Honduras,HND,1976,1347999950 +Honduras,HND,1977,1669499950 +Honduras,HND,1978,1929499950 +Honduras,HND,1979,2251499950 +Honduras,HND,1980,2566000050 +Honduras,HND,1981,2819500000 +Honduras,HND,1982,2903500050 +Honduras,HND,1983,3076999950 +Honduras,HND,1984,3319000000 +Honduras,HND,1985,3639499950 +Honduras,HND,1986,3808500050 +Honduras,HND,1987,4152499950 +Honduras,HND,1988,3970386266.09442 +Honduras,HND,1989,3563448310.34483 +Honduras,HND,1990,3048881322.9572 +Honduras,HND,1991,3068444711.94538 +Honduras,HND,1992,3419487440.65916 +Honduras,HND,1993,3481990761.34498 +Honduras,HND,1994,3432356578.82219 +Honduras,HND,1995,3911053180.39625 +Honduras,HND,1996,4034037162.16216 +Honduras,HND,1997,4663193916.34981 +Honduras,HND,1998,5202215657.31167 +Honduras,HND,1999,5372543554.00697 +Honduras,HND,2000,7103529494.37412 +Honduras,HND,2001,7565869927.73763 +Honduras,HND,2002,7775078402.92785 +Honduras,HND,2003,8140271080.5604 +Honduras,HND,2004,8772194250.27021 +Honduras,HND,2005,9672035709.39793 +Honduras,HND,2006,10841742347.7968 +Honduras,HND,2007,12275501784.2971 +Honduras,HND,2008,13789715132.502 +Honduras,HND,2009,14587496229.1811 +Honduras,HND,2010,15839344591.9842 +Honduras,HND,2011,17710275997.4437 +Honduras,HND,2012,18528554861.015 +Honduras,HND,2013,18499729063.3889 +Honduras,HND,2014,19756533658.1136 +Honduras,HND,2015,20844308600.8656 +Honduras,HND,2016,21516915352.6217 +"Hong Kong SAR, China",HKG,1960,1320796651.69457 +"Hong Kong SAR, China",HKG,1961,1383681651.13776 +"Hong Kong SAR, China",HKG,1962,1612346412.26475 +"Hong Kong SAR, China",HKG,1963,1935298266.45384 +"Hong Kong SAR, China",HKG,1964,2206466461.26434 +"Hong Kong SAR, China",HKG,1965,2435078534.03141 +"Hong Kong SAR, China",HKG,1966,2489845016.64894 +"Hong Kong SAR, China",HKG,1967,2692474989.12571 +"Hong Kong SAR, China",HKG,1968,2716964388.42418 +"Hong Kong SAR, China",HKG,1969,3189740055.13982 +"Hong Kong SAR, China",HKG,1970,3800766535.62088 +"Hong Kong SAR, China",HKG,1971,4476001946.01486 +"Hong Kong SAR, China",HKG,1972,5710107420.14394 +"Hong Kong SAR, China",HKG,1973,8030117555.62033 +"Hong Kong SAR, China",HKG,1974,9388663645.7588 +"Hong Kong SAR, China",HKG,1975,10048022369.9141 +"Hong Kong SAR, China",HKG,1976,12876366008.8077 +"Hong Kong SAR, China",HKG,1977,15719433719.4337 +"Hong Kong SAR, China",HKG,1978,18315007365.9713 +"Hong Kong SAR, China",HKG,1979,22526035940.5921 +"Hong Kong SAR, China",HKG,1980,28861759209.0191 +"Hong Kong SAR, China",HKG,1981,31055409443.043 +"Hong Kong SAR, China",HKG,1982,32291306281.8168 +"Hong Kong SAR, China",HKG,1983,29907091339.5364 +"Hong Kong SAR, China",HKG,1984,33511383985.6741 +"Hong Kong SAR, China",HKG,1985,35699543050.7778 +"Hong Kong SAR, China",HKG,1986,41075570591.9291 +"Hong Kong SAR, China",HKG,1987,50622571586.1149 +"Hong Kong SAR, China",HKG,1988,59707404560.5944 +"Hong Kong SAR, China",HKG,1989,68790369107.2962 +"Hong Kong SAR, China",HKG,1990,76928290841.8701 +"Hong Kong SAR, China",HKG,1991,88959620135.8864 +"Hong Kong SAR, China",HKG,1992,104272278634.731 +"Hong Kong SAR, China",HKG,1993,120353947980.764 +"Hong Kong SAR, China",HKG,1994,135812069768.646 +"Hong Kong SAR, China",HKG,1995,144652912433.103 +"Hong Kong SAR, China",HKG,1996,159717233621.659 +"Hong Kong SAR, China",HKG,1997,177352785419.977 +"Hong Kong SAR, China",HKG,1998,168886163221.567 +"Hong Kong SAR, China",HKG,1999,165768095391.557 +"Hong Kong SAR, China",HKG,2000,171668164082.555 +"Hong Kong SAR, China",HKG,2001,169403241524.337 +"Hong Kong SAR, China",HKG,2002,166349228737.386 +"Hong Kong SAR, China",HKG,2003,161384522525.299 +"Hong Kong SAR, China",HKG,2004,169099768875.193 +"Hong Kong SAR, China",HKG,2005,181570082162.19 +"Hong Kong SAR, China",HKG,2006,193536265094.364 +"Hong Kong SAR, China",HKG,2007,211597405593.868 +"Hong Kong SAR, China",HKG,2008,219279678430.164 +"Hong Kong SAR, China",HKG,2009,214046415026.187 +"Hong Kong SAR, China",HKG,2010,228637697575.04 +"Hong Kong SAR, China",HKG,2011,248513617677.287 +"Hong Kong SAR, China",HKG,2012,262629441493.476 +"Hong Kong SAR, China",HKG,2013,275696879834.966 +"Hong Kong SAR, China",HKG,2014,291459356985.337 +"Hong Kong SAR, China",HKG,2015,309403880389.071 +"Hong Kong SAR, China",HKG,2016,320912235498.728 +Hungary,HUN,1991,34650782427.2429 +Hungary,HUN,1992,38616036221.8202 +Hungary,HUN,1993,40006243367.539 +Hungary,HUN,1994,43039008829.7659 +Hungary,HUN,1995,46288369007.4588 +Hungary,HUN,1996,46538169511.6894 +Hungary,HUN,1997,47178241568.5704 +Hungary,HUN,1998,48661666395.3691 +Hungary,HUN,1999,49074725337.7458 +Hungary,HUN,2000,47209471853.3471 +Hungary,HUN,2001,53696730223.8318 +Hungary,HUN,2002,67561285378.7259 +Hungary,HUN,2003,85050281601.04 +Hungary,HUN,2004,103694527827.457 +Hungary,HUN,2005,112589039620.207 +Hungary,HUN,2006,114801188269.404 +Hungary,HUN,2007,139198195460.551 +Hungary,HUN,2008,157290970540.917 +Hungary,HUN,2009,129965360575.699 +Hungary,HUN,2010,130255637530.908 +Hungary,HUN,2011,140091591852.976 +Hungary,HUN,2012,127321135030.078 +Hungary,HUN,2013,134680475647.645 +Hungary,HUN,2014,139294565100.565 +Hungary,HUN,2015,121715203207.647 +Hungary,HUN,2016,124342940194.419 +Iceland,ISL,1960,248434096.968726 +Iceland,ISL,1961,253885656.329253 +Iceland,ISL,1962,284916516.159537 +Iceland,ISL,1963,340061650.119898 +Iceland,ISL,1964,434267936.914583 +Iceland,ISL,1965,523694949.370689 +Iceland,ISL,1966,628893310.399926 +Iceland,ISL,1967,621225962.154708 +Iceland,ISL,1968,474399471.622359 +Iceland,ISL,1969,414709311.35296 +Iceland,ISL,1970,531004659.090909 +Iceland,ISL,1971,675722954.545455 +Iceland,ISL,1972,846506911.398142 +Iceland,ISL,1973,1163864529.01365 +Iceland,ISL,1974,1527559579.78989 +Iceland,ISL,1975,1418360312.29668 +Iceland,ISL,1976,1683117417.79656 +Iceland,ISL,1977,2226538693.61895 +Iceland,ISL,1978,2532331673.49047 +Iceland,ISL,1979,2876729410.09643 +Iceland,ISL,1980,3409023699.34967 +Iceland,ISL,1981,3521512388.91504 +Iceland,ISL,1982,3232804218.11116 +Iceland,ISL,1983,2788530415.2511 +Iceland,ISL,1984,2887825523.68452 +Iceland,ISL,1985,3008412959.52317 +Iceland,ISL,1986,4022192403.69597 +Iceland,ISL,1987,5565384032.45323 +Iceland,ISL,1988,6156487920.21202 +Iceland,ISL,1989,5718878001.74609 +Iceland,ISL,1990,6521544489.20626 +Iceland,ISL,1991,6966138525.63635 +Iceland,ISL,1992,7138787995.32199 +Iceland,ISL,1993,6269347502.78093 +Iceland,ISL,1994,6441621297.77366 +Iceland,ISL,1995,7181787873.86945 +Iceland,ISL,1996,7501950115.78947 +Iceland,ISL,1997,7596126045.95208 +Iceland,ISL,1998,8468339855.94356 +Iceland,ISL,1999,8931365460.57043 +Iceland,ISL,2000,8946079678.43696 +Iceland,ISL,2001,8146073989.52626 +Iceland,ISL,2002,9199643907.97902 +Iceland,ISL,2003,11304084266.5137 +Iceland,ISL,2004,13703315349.2507 +Iceland,ISL,2005,16691213479.471 +Iceland,ISL,2006,17043245939.014 +Iceland,ISL,2007,21295012098.9774 +Iceland,ISL,2008,17640375722.4448 +Iceland,ISL,2009,12887072301.1621 +Iceland,ISL,2010,13254818331.1893 +Iceland,ISL,2011,14674650434.6757 +Iceland,ISL,2012,14218575093.024 +Iceland,ISL,2013,15479256844.8708 +Iceland,ISL,2014,17178962757.4324 +Iceland,ISL,2015,16783714958.4498 +Iceland,ISL,2016,20047413006.3471 +India,IND,1960,36535925031.34 +India,IND,1961,38709096075.6305 +India,IND,1962,41599070242.3094 +India,IND,1963,47776000900.0302 +India,IND,1964,55726873083.5543 +India,IND,1965,58760424669.8482 +India,IND,1966,45253641303.1897 +India,IND,1967,49466168890.9507 +India,IND,1968,52377324284.1951 +India,IND,1969,57668330026.3629 +India,IND,1970,61589800519.5084 +India,IND,1971,66452561865.8332 +India,IND,1972,70509913049.4003 +India,IND,1973,84374541630.2062 +India,IND,1974,98198276856.6209 +India,IND,1975,97159222024.1364 +India,IND,1976,101346972433.934 +India,IND,1977,119866746574.408 +India,IND,1978,135468782808.69 +India,IND,1979,150950826964.424 +India,IND,1980,183839864649.15 +India,IND,1981,190909548789.769 +India,IND,1982,198037712681.605 +India,IND,1983,215350771428.331 +India,IND,1984,209328156800.867 +India,IND,1985,229410293759.071 +India,IND,1986,245664654062.873 +India,IND,1987,275311425331.64 +India,IND,1988,292632656262.687 +India,IND,1989,292093308319.642 +India,IND,1990,316697337894.513 +India,IND,1991,266502281094.117 +India,IND,1992,284363884080.101 +India,IND,1993,275570363431.902 +India,IND,1994,322909902308.892 +India,IND,1995,355475984177.451 +India,IND,1996,387656017798.596 +India,IND,1997,410320300470.283 +India,IND,1998,415730874171.13 +India,IND,1999,452699998386.914 +India,IND,2000,462146799337.698 +India,IND,2001,478965491060.771 +India,IND,2002,508068952065.901 +India,IND,2003,599592902016.345 +India,IND,2004,699688852930.276 +India,IND,2005,808901077222.839 +India,IND,2006,920316529729.747 +India,IND,2007,1201111768410.27 +India,IND,2008,1186952757636.11 +India,IND,2009,1323940295874.06 +India,IND,2010,1656617073124.71 +India,IND,2011,1823049927772.05 +India,IND,2012,1827637859136.23 +India,IND,2013,1856722121394.42 +India,IND,2014,2035393459980.06 +India,IND,2015,2111751098184.5 +India,IND,2016,2263522518124.03 +Indonesia,IDN,1967,5667756644.83099 +Indonesia,IDN,1968,7076465295.33327 +Indonesia,IDN,1969,8337423312.88344 +Indonesia,IDN,1970,9150684931.50685 +Indonesia,IDN,1971,9333536359.79807 +Indonesia,IDN,1972,10997590361.4458 +Indonesia,IDN,1973,16273253012.0482 +Indonesia,IDN,1974,25802409638.5542 +Indonesia,IDN,1975,30463855421.6867 +Indonesia,IDN,1976,37269156626.506 +Indonesia,IDN,1977,45808915662.6506 +Indonesia,IDN,1978,51455719099.9244 +Indonesia,IDN,1979,51400186379.3033 +Indonesia,IDN,1980,72482337370.3493 +Indonesia,IDN,1981,85518233450.774 +Indonesia,IDN,1982,90158449307.2387 +Indonesia,IDN,1983,81052283404.6079 +Indonesia,IDN,1984,84853699994.0508 +Indonesia,IDN,1985,85289491750.3225 +Indonesia,IDN,1986,79954072569.8533 +Indonesia,IDN,1987,75929617576.8776 +Indonesia,IDN,1988,84300174477.2018 +Indonesia,IDN,1989,94451427898.3405 +Indonesia,IDN,1990,106140727357.035 +Indonesia,IDN,1991,116621996217.133 +Indonesia,IDN,1992,128026966579.964 +Indonesia,IDN,1993,158006700301.533 +Indonesia,IDN,1994,176892143931.505 +Indonesia,IDN,1995,202132028723.115 +Indonesia,IDN,1996,227369679374.973 +Indonesia,IDN,1997,215748998609.635 +Indonesia,IDN,1998,95445547872.715 +Indonesia,IDN,1999,140001351215.462 +Indonesia,IDN,2000,165021012077.81 +Indonesia,IDN,2001,160446947784.909 +Indonesia,IDN,2002,195660611165.183 +Indonesia,IDN,2003,234772463823.808 +Indonesia,IDN,2004,256836875295.452 +Indonesia,IDN,2005,285868618224.017 +Indonesia,IDN,2006,364570514304.85 +Indonesia,IDN,2007,432216737774.861 +Indonesia,IDN,2008,510228635279.289 +Indonesia,IDN,2009,539580088908.138 +Indonesia,IDN,2010,755094160363.071 +Indonesia,IDN,2011,892969107923.094 +Indonesia,IDN,2012,917869910105.749 +Indonesia,IDN,2013,912524136718.018 +Indonesia,IDN,2014,890814755233.225 +Indonesia,IDN,2015,861256351277.359 +Indonesia,IDN,2016,932259177765.307 +"Iran, Islamic Rep.",IRN,1960,4199134389.43894 +"Iran, Islamic Rep.",IRN,1961,4426949094.38944 +"Iran, Islamic Rep.",IRN,1962,4693566415.84158 +"Iran, Islamic Rep.",IRN,1963,4928628018.48185 +"Iran, Islamic Rep.",IRN,1964,5379845647.52475 +"Iran, Islamic Rep.",IRN,1965,6197319928.71287 +"Iran, Islamic Rep.",IRN,1966,6789938671.9472 +"Iran, Islamic Rep.",IRN,1967,7555383689.76898 +"Iran, Islamic Rep.",IRN,1968,8623172959.73597 +"Iran, Islamic Rep.",IRN,1969,9743089607.92079 +"Iran, Islamic Rep.",IRN,1970,10976245153.7954 +"Iran, Islamic Rep.",IRN,1971,13731801564.3564 +"Iran, Islamic Rep.",IRN,1972,17153463263.3663 +"Iran, Islamic Rep.",IRN,1973,27081698249.1798 +"Iran, Islamic Rep.",IRN,1974,46209092072.4584 +"Iran, Islamic Rep.",IRN,1975,51776222349.8518 +"Iran, Islamic Rep.",IRN,1976,68055295080.4565 +"Iran, Islamic Rep.",IRN,1977,80600122702.2202 +"Iran, Islamic Rep.",IRN,1978,77994316621.497 +"Iran, Islamic Rep.",IRN,1979,90391877325.2927 +"Iran, Islamic Rep.",IRN,1980,94362275579.4621 +"Iran, Islamic Rep.",IRN,1981,100499312749.447 +"Iran, Islamic Rep.",IRN,1982,125948756439.161 +"Iran, Islamic Rep.",IRN,1983,156365156618.288 +"Iran, Islamic Rep.",IRN,1984,162276728619.617 +"Iran, Islamic Rep.",IRN,1985,180183629600.036 +"Iran, Islamic Rep.",IRN,1986,209094561834.095 +"Iran, Islamic Rep.",IRN,1987,134009995922.987 +"Iran, Islamic Rep.",IRN,1988,123057861333.933 +"Iran, Islamic Rep.",IRN,1989,120496362916.372 +"Iran, Islamic Rep.",IRN,1990,124813263926.374 +"Iran, Islamic Rep.",IRN,1993,63743623232.012 +"Iran, Islamic Rep.",IRN,1994,71841461172.5964 +"Iran, Islamic Rep.",IRN,1995,96419225743.6737 +"Iran, Islamic Rep.",IRN,1996,120403931885.441 +"Iran, Islamic Rep.",IRN,1997,113919163421.155 +"Iran, Islamic Rep.",IRN,1998,110276913362.508 +"Iran, Islamic Rep.",IRN,1999,113848450088.351 +"Iran, Islamic Rep.",IRN,2000,109591707802.216 +"Iran, Islamic Rep.",IRN,2001,126878750295.944 +"Iran, Islamic Rep.",IRN,2002,128626917503.72 +"Iran, Islamic Rep.",IRN,2003,153544751395.43 +"Iran, Islamic Rep.",IRN,2004,183697185041.72 +"Iran, Islamic Rep.",IRN,2005,219845971945.264 +"Iran, Islamic Rep.",IRN,2006,258645743978.386 +"Iran, Islamic Rep.",IRN,2007,337474485087.271 +"Iran, Islamic Rep.",IRN,2008,397189565318.895 +"Iran, Islamic Rep.",IRN,2009,398978104575.331 +"Iran, Islamic Rep.",IRN,2010,467790215915.476 +"Iran, Islamic Rep.",IRN,2011,592037800186.865 +"Iran, Islamic Rep.",IRN,2012,587209369682.67 +"Iran, Islamic Rep.",IRN,2013,511620875086.78 +"Iran, Islamic Rep.",IRN,2014,425326068422.881 +"Iran, Islamic Rep.",IRN,2015,393436064441.692 +Iraq,IRQ,1960,1684121534.58415 +Iraq,IRQ,1961,1831700364.04369 +Iraq,IRQ,1962,1954634836.18034 +Iraq,IRQ,1963,1978437692.5231 +Iraq,IRQ,1964,2340521142.5371 +Iraq,IRQ,1968,2896947633.71605 +Iraq,IRQ,1969,3008120974.51694 +Iraq,IRQ,1970,3281713805.65668 +Iraq,IRQ,1971,3865346534.65347 +Iraq,IRQ,1972,4113848002.40312 +Iraq,IRQ,1973,5134367778.1446 +Iraq,IRQ,1974,11516762614.2906 +Iraq,IRQ,1975,13458516762.6143 +Iraq,IRQ,1976,17754825601.0836 +Iraq,IRQ,1977,19838130714.5276 +Iraq,IRQ,1978,23762275651.8794 +Iraq,IRQ,1979,37816457839.4853 +Iraq,IRQ,1980,53405689129.6986 +Iraq,IRQ,1981,38424991534.0332 +Iraq,IRQ,1982,42595309882.7471 +Iraq,IRQ,1983,40595046638.7906 +Iraq,IRQ,1984,46802508845.2879 +Iraq,IRQ,1985,48284979092.9559 +Iraq,IRQ,1986,47127693792.2161 +Iraq,IRQ,1987,56609842393.0524 +Iraq,IRQ,1988,62503055644.9019 +Iraq,IRQ,1989,65641363782.5668 +Iraq,IRQ,1990,179885815374.719 +Iraq,IRQ,2004,36627901762.063 +Iraq,IRQ,2005,49954890353.2609 +Iraq,IRQ,2006,65140293687.5395 +Iraq,IRQ,2007,88840050497.0957 +Iraq,IRQ,2008,131613661510.475 +Iraq,IRQ,2009,111660855042.735 +Iraq,IRQ,2010,138516722649.573 +Iraq,IRQ,2011,185749664444.444 +Iraq,IRQ,2012,218000986222.639 +Iraq,IRQ,2013,234648370497.427 +Iraq,IRQ,2014,234648370497.427 +Iraq,IRQ,2015,179640210726.448 +Iraq,IRQ,2016,171489001692.047 +Ireland,IRL,1960,1939329775.43739 +Ireland,IRL,1961,2088012282.35667 +Ireland,IRL,1962,2260349684.08625 +Ireland,IRL,1963,2430843768.44553 +Ireland,IRL,1964,2766608945.87402 +Ireland,IRL,1965,2945704142.99765 +Ireland,IRL,1966,3104034393.23162 +Ireland,IRL,1967,3343636773.36759 +Ireland,IRL,1968,3278584478.33023 +Ireland,IRL,1969,3787077343.72783 +Ireland,IRL,1970,4400344547.34455 +Ireland,IRL,1971,5103293982.36872 +Ireland,IRL,1972,6324312352.47836 +Ireland,IRL,1973,7488574956.58885 +Ireland,IRL,1974,7904673172.52808 +Ireland,IRL,1975,9493191637.63066 +Ireland,IRL,1976,9463110246.24965 +Ireland,IRL,1977,11259469295.2329 +Ireland,IRL,1978,14662488976.1401 +Ireland,IRL,1979,18337460025.7898 +Ireland,IRL,1980,21769374170.5778 +Ireland,IRL,1981,20690641399.4169 +Ireland,IRL,1982,21496001229.5998 +Ireland,IRL,1983,20786594499.3638 +Ireland,IRL,1984,20126542854.7038 +Ireland,IRL,1985,21291058299.3254 +Ireland,IRL,1986,28742982513.777 +Ireland,IRL,1987,33954080524.3446 +Ireland,IRL,1988,37810270545.8908 +Ireland,IRL,1989,39277216207.166 +Ireland,IRL,1990,49354416568.9723 +Ireland,IRL,1991,49836763468.1202 +Ireland,IRL,1992,55973865067.6672 +Ireland,IRL,1993,52469340621.0024 +Ireland,IRL,1994,57154150176.6784 +Ireland,IRL,1995,69208232323.2323 +Ireland,IRL,1996,75864982358.871 +Ireland,IRL,1997,82816774116.5234 +Ireland,IRL,1998,90111618257.2614 +Ireland,IRL,1999,98781557639.0369 +Ireland,IRL,2000,99855067256.3111 +Ireland,IRL,2001,109119123042.506 +Ireland,IRL,2002,127941699604.743 +Ireland,IRL,2003,164285609480.813 +Ireland,IRL,2004,193870908865.16 +Ireland,IRL,2005,211684827757.742 +Ireland,IRL,2006,232167519759.127 +Ireland,IRL,2007,270043019436.08 +Ireland,IRL,2008,274919027391.241 +Ireland,IRL,2009,235765976660.183 +Ireland,IRL,2010,221343483668.837 +Ireland,IRL,2011,240590736123.315 +Ireland,IRL,2012,225819189160.686 +Ireland,IRL,2013,239271158532.518 +Ireland,IRL,2014,256271345442.586 +Ireland,IRL,2015,283716006697.636 +Ireland,IRL,2016,294053595685.717 +Isle of Man,IMN,1995,914727080.643268 +Isle of Man,IMN,1996,1023086918.62768 +Isle of Man,IMN,1997,1180919719.40764 +Isle of Man,IMN,1998,1382548249.78303 +Isle of Man,IMN,1999,1567465656.8505 +Isle of Man,IMN,2000,1563667799.61578 +Isle of Man,IMN,2001,1614595290.9182 +Isle of Man,IMN,2002,1897606791.43353 +Isle of Man,IMN,2003,2264911806.90354 +Isle of Man,IMN,2004,2758117365.04863 +Isle of Man,IMN,2005,2971167185.35511 +Isle of Man,IMN,2006,3344402193.24608 +Isle of Man,IMN,2007,5685988395.35814 +Isle of Man,IMN,2008,5827468750 +Isle of Man,IMN,2009,5047909331.6716 +Isle of Man,IMN,2010,5420118974.04203 +Isle of Man,IMN,2011,6066454093.89521 +Isle of Man,IMN,2012,6433357030.0158 +Isle of Man,IMN,2013,6754330154.76004 +Isle of Man,IMN,2014,7428280401.5139 +Israel,ISR,1960,2598500000 +Israel,ISR,1961,3138500000 +Israel,ISR,1962,2510000000 +Israel,ISR,1963,2992333333.33333 +Israel,ISR,1964,3405333333.33333 +Israel,ISR,1965,3663333333.33333 +Israel,ISR,1966,3980000000 +Israel,ISR,1967,4030000000 +Israel,ISR,1968,4619000000 +Israel,ISR,1969,5329333333.33333 +Israel,ISR,1970,6267666666.66667 +Israel,ISR,1971,5851750000 +Israel,ISR,1972,7496250000 +Israel,ISR,1973,9692000000 +Israel,ISR,1974,13986250000 +Israel,ISR,1975,13028166666.6667 +Israel,ISR,1976,12359875000 +Israel,ISR,1977,14390000000 +Israel,ISR,1978,13967647058.8235 +Israel,ISR,1979,17796000000 +Israel,ISR,1980,21884705882.3529 +Israel,ISR,1981,23258596491.2281 +Israel,ISR,1982,24539341563.786 +Israel,ISR,1983,27442580071.1744 +Israel,ISR,1984,26043655184.1746 +Israel,ISR,1985,24121638985.495 +Israel,ISR,1986,29702243917.1932 +Israel,ISR,1987,35477234541.5778 +Israel,ISR,1988,43893303708.7998 +Israel,ISR,1989,44599771759.5491 +Israel,ISR,1990,52490327348.4773 +Israel,ISR,1991,59170286560.4844 +Israel,ISR,1992,65771216420.6417 +Israel,ISR,1993,65925583866.2945 +Israel,ISR,1994,74669719504.5 +Israel,ISR,1995,99968784246.0067 +Israel,ISR,1996,109774101575.963 +Israel,ISR,1997,114543978662.956 +Israel,ISR,1998,115838004263.046 +Israel,ISR,1999,117212358383.458 +Israel,ISR,2000,132396684080.151 +Israel,ISR,2001,130751599020.377 +Israel,ISR,2002,121092701253.746 +Israel,ISR,2003,126749741990.734 +Israel,ISR,2004,135418563141.455 +Israel,ISR,2005,142837533703.233 +Israel,ISR,2006,154511423313.434 +Israel,ISR,2007,179564275455.807 +Israel,ISR,2008,216760312151.616 +Israel,ISR,2009,208068814688.605 +Israel,ISR,2010,233754747258.625 +Israel,ISR,2011,261374751963.332 +Israel,ISR,2012,257641795689.722 +Israel,ISR,2013,293314777888.557 +Israel,ISR,2014,308769389865.564 +Israel,ISR,2015,299415714726.768 +Israel,ISR,2016,318743685882.414 +Italy,ITA,1960,40385288344.1911 +Italy,ITA,1961,44842760293.1924 +Italy,ITA,1962,50383891898.9911 +Italy,ITA,1963,57710743059.8341 +Italy,ITA,1964,63175417019.0094 +Italy,ITA,1965,67978153850.5191 +Italy,ITA,1966,73654870011.2757 +Italy,ITA,1967,81133120065.4202 +Italy,ITA,1968,87942231678.3505 +Italy,ITA,1969,97085082807.3751 +Italy,ITA,1970,113021272614.622 +Italy,ITA,1971,124261125780.275 +Italy,ITA,1972,144780888114.21 +Italy,ITA,1973,174913182995.683 +Italy,ITA,1974,198906211372.432 +Italy,ITA,1975,226944778173.191 +Italy,ITA,1976,223976031867.876 +Italy,ITA,1977,256746611586.57 +Italy,ITA,1978,314019079397.673 +Italy,ITA,1979,392378586343.51 +Italy,ITA,1980,475682508026.227 +Italy,ITA,1981,429282144779.424 +Italy,ITA,1982,425863253400.143 +Italy,ITA,1983,441580964558.899 +Italy,ITA,1984,436443282455.367 +Italy,ITA,1985,450725817665.551 +Italy,ITA,1986,638273988310.17 +Italy,ITA,1987,803055421720.944 +Italy,ITA,1988,888667916542.696 +Italy,ITA,1989,925598071408.411 +Italy,ITA,1990,1177326298642.53 +Italy,ITA,1991,1242109402060.25 +Italy,ITA,1992,1315806990573.45 +Italy,ITA,1993,1061445229481.97 +Italy,ITA,1994,1095590837536.02 +Italy,ITA,1995,1170787357066.44 +Italy,ITA,1996,1308929305684.53 +Italy,ITA,1997,1239050936221.01 +Italy,ITA,1998,1266309223820.68 +Italy,ITA,1999,1248563148945.24 +Italy,ITA,2000,1141760021927.4 +Italy,ITA,2001,1162317840447.43 +Italy,ITA,2002,1266510668642.95 +Italy,ITA,2003,1569649631715.58 +Italy,ITA,2004,1798314755525.2 +Italy,ITA,2005,1852661936077.6 +Italy,ITA,2006,1942633841801.53 +Italy,ITA,2007,2203053327128.39 +Italy,ITA,2008,2390729210487.77 +Italy,ITA,2009,2185160158794.11 +Italy,ITA,2010,2125058270201.64 +Italy,ITA,2011,2276292459232.78 +Italy,ITA,2012,2072823111961.1 +Italy,ITA,2013,2130491269673.44 +Italy,ITA,2014,2151732834411.5 +Italy,ITA,2015,1824902219021.73 +Italy,ITA,2016,1849970464191.78 +Jamaica,JAM,1960,699050678.98642 +Jamaica,JAM,1961,748028839.423211 +Jamaica,JAM,1962,777712445.751085 +Jamaica,JAM,1963,826690466.190676 +Jamaica,JAM,1964,897931401.371972 +Jamaica,JAM,1965,972140557.188856 +Jamaica,JAM,1966,1096738065.2387 +Jamaica,JAM,1967,1148025407.34604 +Jamaica,JAM,1968,1083883355.33421 +Jamaica,JAM,1969,1191287651.50606 +Jamaica,JAM,1970,1404776071.04284 +Jamaica,JAM,1971,1539865513.92891 +Jamaica,JAM,1972,1875048859.93485 +Jamaica,JAM,1973,1905917553.19149 +Jamaica,JAM,1974,2375096249.03751 +Jamaica,JAM,1975,2860411285.88714 +Jamaica,JAM,1976,2966010229.8977 +Jamaica,JAM,1977,3249697393.02607 +Jamaica,JAM,1978,2644449232.29321 +Jamaica,JAM,1979,2425033998.18676 +Jamaica,JAM,1980,2679409453.23903 +Jamaica,JAM,1981,2979061412.37229 +Jamaica,JAM,1982,3293533288.42483 +Jamaica,JAM,1983,3619294120.69144 +Jamaica,JAM,1984,2373566957.49214 +Jamaica,JAM,1985,2100223149.71396 +Jamaica,JAM,1986,2754566176.20212 +Jamaica,JAM,1987,3286987551.71597 +Jamaica,JAM,1988,3828310734.97795 +Jamaica,JAM,1989,4404970058.83786 +Jamaica,JAM,1990,4592224067.37194 +Jamaica,JAM,1991,4071219198.03601 +Jamaica,JAM,1992,3530892749.02131 +Jamaica,JAM,1993,4859766160.89207 +Jamaica,JAM,1994,4907861405.82758 +Jamaica,JAM,1995,5779285207.92079 +Jamaica,JAM,1996,6504445801.34228 +Jamaica,JAM,1997,7450324787.38384 +Jamaica,JAM,1998,8718300136.42565 +Jamaica,JAM,1999,8795765306.12245 +Jamaica,JAM,2000,8929375580.31569 +Jamaica,JAM,2001,9087918836.80556 +Jamaica,JAM,2002,9694161516.27524 +Jamaica,JAM,2003,9399452787.8474 +Jamaica,JAM,2004,10150978154.5484 +Jamaica,JAM,2005,11204416000 +Jamaica,JAM,2006,11905525197.3285 +Jamaica,JAM,2007,12824094989.8639 +Jamaica,JAM,2008,13678551837.6303 +Jamaica,JAM,2009,12038998756.9217 +Jamaica,JAM,2010,13192229343.0991 +Jamaica,JAM,2011,14440457548.68 +Jamaica,JAM,2012,14802430172.5391 +Jamaica,JAM,2013,14276559215.7309 +Jamaica,JAM,2014,13897561173.4853 +Jamaica,JAM,2015,14261995113.6721 +Jamaica,JAM,2016,14027151472.7053 +Japan,JPN,1960,44307342950.4 +Japan,JPN,1961,53508617739.3778 +Japan,JPN,1962,60723018683.7333 +Japan,JPN,1963,69498131797.3333 +Japan,JPN,1964,81749006381.5111 +Japan,JPN,1965,90950278257.7778 +Japan,JPN,1966,105628070343.111 +Japan,JPN,1967,123781880217.6 +Japan,JPN,1968,146601072685.511 +Japan,JPN,1969,172204199480.889 +Japan,JPN,1970,211514189326.389 +Japan,JPN,1971,238914956436.922 +Japan,JPN,1972,316393344649.993 +Japan,JPN,1973,429857320166.565 +Japan,JPN,1974,477155786487.722 +Japan,JPN,1975,518855814542.728 +Japan,JPN,1976,583142956487.974 +Japan,JPN,1977,717696307916.651 +Japan,JPN,1978,1008391778690.25 +Japan,JPN,1979,1049578503045.09 +Japan,JPN,1980,1099692917412.75 +Japan,JPN,1981,1215508719880.85 +Japan,JPN,1982,1129894523635.09 +Japan,JPN,1983,1232343804284.17 +Japan,JPN,1984,1309740021609.74 +Japan,JPN,1985,1400714806812.65 +Japan,JPN,1986,2075034242192.31 +Japan,JPN,1987,2514283894261.86 +Japan,JPN,1988,3050637784817.52 +Japan,JPN,1989,3052315661954.82 +Japan,JPN,1990,3139974443543 +Japan,JPN,1991,3578139437172.02 +Japan,JPN,1992,3897826229662.86 +Japan,JPN,1993,4466565327401.26 +Japan,JPN,1994,4907039384469.68 +Japan,JPN,1995,5449116304981.1 +Japan,JPN,1996,4833712542207.1 +Japan,JPN,1997,4414732843544.43 +Japan,JPN,1998,4032509760872.94 +Japan,JPN,1999,4562078822335.45 +Japan,JPN,2000,4887519660744.86 +Japan,JPN,2001,4303544259842.72 +Japan,JPN,2002,4115116279069.77 +Japan,JPN,2003,4445658071221.86 +Japan,JPN,2004,4815148854362.11 +Japan,JPN,2005,4755410630912.14 +Japan,JPN,2006,4530377224970.4 +Japan,JPN,2007,4515264514430.57 +Japan,JPN,2008,5037908465114.48 +Japan,JPN,2009,5231382674593.7 +Japan,JPN,2010,5700098114744.41 +Japan,JPN,2011,6157459594823.72 +Japan,JPN,2012,6203213121334.12 +Japan,JPN,2013,5155717056270.83 +Japan,JPN,2014,4848733415523.53 +Japan,JPN,2015,4383076298081.86 +Japan,JPN,2016,4939383909875.27 +Jordan,JOR,1965,599831979.837581 +Jordan,JOR,1966,658078969.476337 +Jordan,JOR,1967,631755810.697284 +Jordan,JOR,1968,561187342.481098 +Jordan,JOR,1969,698963875.66508 +Jordan,JOR,1970,639596751.610193 +Jordan,JOR,1971,678241388.966676 +Jordan,JOR,1972,788574628.955475 +Jordan,JOR,1973,943700547.778454 +Jordan,JOR,1974,1197454206.76808 +Jordan,JOR,1975,1363039399.62477 +Jordan,JOR,1976,1708734939.75904 +Jordan,JOR,1977,2096568478.59095 +Jordan,JOR,1978,2602748691.09948 +Jordan,JOR,1979,3271728271.72827 +Jordan,JOR,1980,3910036925.14267 +Jordan,JOR,1981,4384685230.02421 +Jordan,JOR,1982,4680567375.88653 +Jordan,JOR,1983,4920407601.21179 +Jordan,JOR,1984,4966710013.0039 +Jordan,JOR,1985,4993829194.12063 +Jordan,JOR,1986,6401380000 +Jordan,JOR,1987,6755599113.73707 +Jordan,JOR,1988,6277197435.2124 +Jordan,JOR,1989,4220945005.22102 +Jordan,JOR,1990,4160003917.43258 +Jordan,JOR,1991,4344250257.01278 +Jordan,JOR,1992,5311329067.37276 +Jordan,JOR,1993,5605841535.57512 +Jordan,JOR,1994,6237739516.24445 +Jordan,JOR,1995,6727446632.42009 +Jordan,JOR,1996,6928359238.36389 +Jordan,JOR,1997,7246188575.45839 +Jordan,JOR,1998,7912327362.48237 +Jordan,JOR,1999,8149106064.88011 +Jordan,JOR,2000,8460424400.56418 +Jordan,JOR,2001,8975689844.8519 +Jordan,JOR,2002,9582453032.44006 +Jordan,JOR,2003,10195660789.8449 +Jordan,JOR,2004,11411390409.0268 +Jordan,JOR,2005,12588665303.244 +Jordan,JOR,2006,15056929760.2257 +Jordan,JOR,2007,17110587447.1086 +Jordan,JOR,2008,21972004086.2336 +Jordan,JOR,2009,23820230000 +Jordan,JOR,2010,26425379436.6197 +Jordan,JOR,2011,28840263380.2817 +Jordan,JOR,2012,30937277605.6338 +Jordan,JOR,2013,33593843661.9718 +Jordan,JOR,2014,35826925774.6479 +Jordan,JOR,2015,37517410281.6901 +Jordan,JOR,2016,38654727746.4789 +Kazakhstan,KAZ,1990,26932728898.8146 +Kazakhstan,KAZ,1991,24881135586.399 +Kazakhstan,KAZ,1992,24906939560.1098 +Kazakhstan,KAZ,1993,23409027475.6879 +Kazakhstan,KAZ,1994,21250839258.0901 +Kazakhstan,KAZ,1995,20374307047.115 +Kazakhstan,KAZ,1996,21035357832.8019 +Kazakhstan,KAZ,1997,22165932062.966 +Kazakhstan,KAZ,1998,22135245413.2312 +Kazakhstan,KAZ,1999,16870817134.7767 +Kazakhstan,KAZ,2000,18291990619.137 +Kazakhstan,KAZ,2001,22152689129.5583 +Kazakhstan,KAZ,2002,24636598581.0204 +Kazakhstan,KAZ,2003,30833692831.3955 +Kazakhstan,KAZ,2004,43151647002.6096 +Kazakhstan,KAZ,2005,57123671733.8952 +Kazakhstan,KAZ,2006,81003884545.4099 +Kazakhstan,KAZ,2007,104849886825.584 +Kazakhstan,KAZ,2008,133441612246.798 +Kazakhstan,KAZ,2009,115308661142.927 +Kazakhstan,KAZ,2010,148047348240.643 +Kazakhstan,KAZ,2011,192626507971.584 +Kazakhstan,KAZ,2012,207998568865.789 +Kazakhstan,KAZ,2013,236634552078.102 +Kazakhstan,KAZ,2014,221415572819.5 +Kazakhstan,KAZ,2015,184388432148.715 +Kazakhstan,KAZ,2016,133657084404.957 +Kenya,KEN,1960,791265458.813328 +Kenya,KEN,1961,792959472.134266 +Kenya,KEN,1962,868111400.008864 +Kenya,KEN,1963,926589348.567393 +Kenya,KEN,1964,998759333.637333 +Kenya,KEN,1965,997919319.974061 +Kenya,KEN,1966,1164519673.19064 +Kenya,KEN,1967,1232559505.9162 +Kenya,KEN,1968,1353295457.51798 +Kenya,KEN,1969,1458379415.39403 +Kenya,KEN,1970,1603447357.24209 +Kenya,KEN,1971,1778391289.20054 +Kenya,KEN,1972,2107279157.38336 +Kenya,KEN,1973,2502142444.14182 +Kenya,KEN,1974,2973309272.02986 +Kenya,KEN,1975,3259344935.75357 +Kenya,KEN,1976,3474542392.04597 +Kenya,KEN,1977,4494378855.32203 +Kenya,KEN,1978,5303734882.51159 +Kenya,KEN,1979,6234390975.29871 +Kenya,KEN,1980,7265315331.62273 +Kenya,KEN,1981,6854491453.91471 +Kenya,KEN,1982,6431579357.30273 +Kenya,KEN,1983,5979198463.82274 +Kenya,KEN,1984,6191437070.43467 +Kenya,KEN,1985,6135034338.30431 +Kenya,KEN,1986,7239126716.93219 +Kenya,KEN,1987,7970820530.76691 +Kenya,KEN,1988,8355380879.12955 +Kenya,KEN,1989,8283114648.38116 +Kenya,KEN,1990,8572359162.86876 +Kenya,KEN,1991,8151479004.21334 +Kenya,KEN,1992,8209129171.73649 +Kenya,KEN,1993,5751789915.05363 +Kenya,KEN,1994,7148145375.78545 +Kenya,KEN,1995,9046326059.98857 +Kenya,KEN,1996,12045858436.2399 +Kenya,KEN,1997,13115773737.5664 +Kenya,KEN,1998,14093998843.7334 +Kenya,KEN,1999,12896013576.7324 +Kenya,KEN,2000,12705357103.0056 +Kenya,KEN,2001,12986007425.8781 +Kenya,KEN,2002,13147743910.7241 +Kenya,KEN,2003,14904517649.8476 +Kenya,KEN,2004,16095337093.8366 +Kenya,KEN,2005,18737897744.7948 +Kenya,KEN,2006,25825524820.8064 +Kenya,KEN,2007,31958195182.2406 +Kenya,KEN,2008,35895153327.8497 +Kenya,KEN,2009,37021512048.8158 +Kenya,KEN,2010,39999659233.7555 +Kenya,KEN,2011,41953433591.4101 +Kenya,KEN,2012,50412754821.9795 +Kenya,KEN,2013,55097343447.5616 +Kenya,KEN,2014,61445345999.0765 +Kenya,KEN,2015,63767539356.6051 +Kenya,KEN,2016,70529014778.3251 +Kiribati,KIR,1970,14295279.5446937 +Kiribati,KIR,1971,15278632.4786325 +Kiribati,KIR,1972,18936526.9461078 +Kiribati,KIR,1973,31710657.7257811 +Kiribati,KIR,1974,85637174.3722131 +Kiribati,KIR,1975,55081816.9917528 +Kiribati,KIR,1976,41109617.4996945 +Kiribati,KIR,1977,38748059.4366822 +Kiribati,KIR,1978,45210026.3248255 +Kiribati,KIR,1979,42620165.4370668 +Kiribati,KIR,1980,38715554.5433842 +Kiribati,KIR,1981,41369800.0459664 +Kiribati,KIR,1982,40572066.1324678 +Kiribati,KIR,1983,37837837.8378378 +Kiribati,KIR,1984,41246160.596753 +Kiribati,KIR,1985,32125148.4042182 +Kiribati,KIR,1986,32085561.4973262 +Kiribati,KIR,1987,33608738.2719507 +Kiribati,KIR,1988,42972107.1958747 +Kiribati,KIR,1989,41119721.651115 +Kiribati,KIR,1990,39809538.6776989 +Kiribati,KIR,1991,47515189.2818196 +Kiribati,KIR,1992,47737955.346651 +Kiribati,KIR,1993,46919624.6430029 +Kiribati,KIR,1994,54832577.8622606 +Kiribati,KIR,1995,56338028.1690141 +Kiribati,KIR,1996,66515376.7900462 +Kiribati,KIR,1997,67537479.5903221 +Kiribati,KIR,1998,65334841.0604347 +Kiribati,KIR,1999,69032258.0645161 +Kiribati,KIR,2000,67254174.3970315 +Kiribati,KIR,2001,63101272.3699183 +Kiribati,KIR,2002,72196457.6768445 +Kiribati,KIR,2003,90231856.8000519 +Kiribati,KIR,2004,102367039.270481 +Kiribati,KIR,2005,112133944.253532 +Kiribati,KIR,2006,108545632.53012 +Kiribati,KIR,2007,130754915.906619 +Kiribati,KIR,2008,139125482.301627 +Kiribati,KIR,2009,130465372.016846 +Kiribati,KIR,2010,153275912.676573 +Kiribati,KIR,2011,177142135.121196 +Kiribati,KIR,2012,188045661.627666 +Kiribati,KIR,2013,187153601.08129 +Kiribati,KIR,2014,186066973.138634 +Kiribati,KIR,2015,160121929.231463 +Kiribati,KIR,2016,165765016.354445 +"Korea, Rep.",KOR,1960,3957873925.93719 +"Korea, Rep.",KOR,1961,2417237753.94419 +"Korea, Rep.",KOR,1962,2813933899.74577 +"Korea, Rep.",KOR,1963,3988246108.63888 +"Korea, Rep.",KOR,1964,3458518493.92483 +"Korea, Rep.",KOR,1965,3120307807.80781 +"Korea, Rep.",KOR,1966,3928171298.00251 +"Korea, Rep.",KOR,1967,4854576371.4328 +"Korea, Rep.",KOR,1968,6117260075.90819 +"Korea, Rep.",KOR,1969,7675805108.27318 +"Korea, Rep.",KOR,1970,8999227202.47295 +"Korea, Rep.",KOR,1971,9889961111.91128 +"Korea, Rep.",KOR,1972,10842220468.8335 +"Korea, Rep.",KOR,1973,13841885920.8676 +"Korea, Rep.",KOR,1974,19482038222.8595 +"Korea, Rep.",KOR,1975,21704752066.1157 +"Korea, Rep.",KOR,1976,29779338842.9752 +"Korea, Rep.",KOR,1977,38265082644.6281 +"Korea, Rep.",KOR,1978,51700619834.7107 +"Korea, Rep.",KOR,1979,66567975206.6116 +"Korea, Rep.",KOR,1980,64980820835.3226 +"Korea, Rep.",KOR,1981,72425590649.4574 +"Korea, Rep.",KOR,1982,77773431088.253 +"Korea, Rep.",KOR,1983,87024427972.9294 +"Korea, Rep.",KOR,1984,96597434179.5082 +"Korea, Rep.",KOR,1985,100273097170.18 +"Korea, Rep.",KOR,1986,115537126325.94 +"Korea, Rep.",KOR,1987,146133338196.141 +"Korea, Rep.",KOR,1988,196964195387.371 +"Korea, Rep.",KOR,1989,243526047716.915 +"Korea, Rep.",KOR,1990,279349355713.801 +"Korea, Rep.",KOR,1991,325734233312.879 +"Korea, Rep.",KOR,1992,350051111253.443 +"Korea, Rep.",KOR,1993,386302839273.923 +"Korea, Rep.",KOR,1994,455602962225.403 +"Korea, Rep.",KOR,1995,556130926912.754 +"Korea, Rep.",KOR,1996,598099073901.423 +"Korea, Rep.",KOR,1997,557503074772.151 +"Korea, Rep.",KOR,1998,374241351752.483 +"Korea, Rep.",KOR,1999,485248229336.653 +"Korea, Rep.",KOR,2000,561633125839.994 +"Korea, Rep.",KOR,2001,533052076313.527 +"Korea, Rep.",KOR,2002,609020054512.465 +"Korea, Rep.",KOR,2003,680520724062.403 +"Korea, Rep.",KOR,2004,764880644710.649 +"Korea, Rep.",KOR,2005,898137194716.188 +"Korea, Rep.",KOR,2006,1011797457138.5 +"Korea, Rep.",KOR,2007,1122679154632.41 +"Korea, Rep.",KOR,2008,1002219052967.54 +"Korea, Rep.",KOR,2009,901934953364.711 +"Korea, Rep.",KOR,2010,1094499338702.72 +"Korea, Rep.",KOR,2011,1202463682633.85 +"Korea, Rep.",KOR,2012,1222807284485.31 +"Korea, Rep.",KOR,2013,1305604981271.91 +"Korea, Rep.",KOR,2014,1411333926201.24 +"Korea, Rep.",KOR,2015,1382764027113.82 +"Korea, Rep.",KOR,2016,1411245589976.63 +Kosovo,XKX,2000,1849196082.05507 +Kosovo,XKX,2001,2535333631.88536 +Kosovo,XKX,2002,2702427046.9355 +Kosovo,XKX,2003,3355083116.58939 +Kosovo,XKX,2004,3639935347.50715 +Kosovo,XKX,2005,3736599925.38241 +Kosovo,XKX,2006,4078158323.92423 +Kosovo,XKX,2007,4833561456.33726 +Kosovo,XKX,2008,5687488208.58356 +Kosovo,XKX,2009,5653792720.20006 +Kosovo,XKX,2010,5829933774.83444 +Kosovo,XKX,2011,6649291075.89658 +Kosovo,XKX,2012,6473724784.78736 +Kosovo,XKX,2013,7072092405.73553 +Kosovo,XKX,2014,7386891336.07536 +Kosovo,XKX,2015,6440501275.36875 +Kosovo,XKX,2016,6649888888.88889 +Kuwait,KWT,1965,2097451694.2033 +Kuwait,KWT,1966,2391486978.43741 +Kuwait,KWT,1967,2441893027.16326 +Kuwait,KWT,1968,2663119574.34892 +Kuwait,KWT,1969,2769532343.88127 +Kuwait,KWT,1970,2873984878.18538 +Kuwait,KWT,1971,3880370401.57259 +Kuwait,KWT,1972,4451200972.9401 +Kuwait,KWT,1973,5408293998.65138 +Kuwait,KWT,1974,13004774556.6166 +Kuwait,KWT,1975,12024138275.8621 +Kuwait,KWT,1976,13131668946.6484 +Kuwait,KWT,1977,14135729588.2763 +Kuwait,KWT,1978,15500908760.4507 +Kuwait,KWT,1979,24746019536.903 +Kuwait,KWT,1980,28638550499.4451 +Kuwait,KWT,1981,25056672166.4275 +Kuwait,KWT,1982,21577977770.059 +Kuwait,KWT,1983,20869434305.3173 +Kuwait,KWT,1984,21697297872.3404 +Kuwait,KWT,1985,21442619680.8511 +Kuwait,KWT,1986,17903681693.0489 +Kuwait,KWT,1987,22365734481.5213 +Kuwait,KWT,1988,20692472759.8566 +Kuwait,KWT,1989,24312117767.1886 +Kuwait,KWT,1990,18427777777.7778 +Kuwait,KWT,1991,11008793176.2223 +Kuwait,KWT,1992,19858555214.7239 +Kuwait,KWT,1993,23941391390.7285 +Kuwait,KWT,1994,24848483838.3838 +Kuwait,KWT,1995,27191689008.0429 +Kuwait,KWT,1996,31493987641.9506 +Kuwait,KWT,1997,30354434553.2476 +Kuwait,KWT,1998,25941929461.9423 +Kuwait,KWT,1999,30121879434.954 +Kuwait,KWT,2000,37711864406.7797 +Kuwait,KWT,2001,34890772742.0933 +Kuwait,KWT,2002,38137545245.1464 +Kuwait,KWT,2003,47875838926.1745 +Kuwait,KWT,2004,59440108585.0017 +Kuwait,KWT,2005,80797945205.4795 +Kuwait,KWT,2006,101550654720.882 +Kuwait,KWT,2007,114641097818.438 +Kuwait,KWT,2008,147395833333.333 +Kuwait,KWT,2009,105899930507.297 +Kuwait,KWT,2010,115419050942.08 +Kuwait,KWT,2011,154027536231.884 +Kuwait,KWT,2012,174070025008.932 +Kuwait,KWT,2013,174161495063.47 +Kuwait,KWT,2014,162631763879.129 +Kuwait,KWT,2015,114041209704.221 +Kyrgyz Republic,KGZ,1990,2674000000 +Kyrgyz Republic,KGZ,1991,2570833333.33333 +Kyrgyz Republic,KGZ,1992,2316562500 +Kyrgyz Republic,KGZ,1993,2028295454.54545 +Kyrgyz Republic,KGZ,1994,1681006993.00699 +Kyrgyz Republic,KGZ,1995,1661018518.51852 +Kyrgyz Republic,KGZ,1996,1827570586.16784 +Kyrgyz Republic,KGZ,1997,1767864035.71943 +Kyrgyz Republic,KGZ,1998,1645963749.83146 +Kyrgyz Republic,KGZ,1999,1249062025.13805 +Kyrgyz Republic,KGZ,2000,1369693171.43504 +Kyrgyz Republic,KGZ,2001,1525112241.84376 +Kyrgyz Republic,KGZ,2002,1605640633.42189 +Kyrgyz Republic,KGZ,2003,1919012780.97086 +Kyrgyz Republic,KGZ,2004,2211535311.62834 +Kyrgyz Republic,KGZ,2005,2460248026.17783 +Kyrgyz Republic,KGZ,2006,2834168889.42019 +Kyrgyz Republic,KGZ,2007,3802566170.81543 +Kyrgyz Republic,KGZ,2008,5139957784.91084 +Kyrgyz Republic,KGZ,2009,4690062255.12247 +Kyrgyz Republic,KGZ,2010,4794357795.07139 +Kyrgyz Republic,KGZ,2011,6197766118.59856 +Kyrgyz Republic,KGZ,2012,6605139933.41063 +Kyrgyz Republic,KGZ,2013,7335027591.91628 +Kyrgyz Republic,KGZ,2014,7468096566.71158 +Kyrgyz Republic,KGZ,2015,6678178340.45121 +Kyrgyz Republic,KGZ,2016,6551287937.62632 +Lao PDR,LAO,1984,1757142805.71429 +Lao PDR,LAO,1985,2366666615.55556 +Lao PDR,LAO,1986,1776842041.05263 +Lao PDR,LAO,1987,1087273103.69639 +Lao PDR,LAO,1988,598961269.297879 +Lao PDR,LAO,1989,714046821.093797 +Lao PDR,LAO,1990,865559856.1639 +Lao PDR,LAO,1991,1028087972.31085 +Lao PDR,LAO,1992,1127806944.61513 +Lao PDR,LAO,1993,1327748654.65969 +Lao PDR,LAO,1994,1543606345.11684 +Lao PDR,LAO,1995,1763536304.53964 +Lao PDR,LAO,1996,1873671550.34636 +Lao PDR,LAO,1997,1747011857.33107 +Lao PDR,LAO,1998,1280177838.71905 +Lao PDR,LAO,1999,1454430642.49183 +Lao PDR,LAO,2000,1731198022.45494 +Lao PDR,LAO,2001,1768619058.34647 +Lao PDR,LAO,2002,1758176653.07746 +Lao PDR,LAO,2003,2023324407.30316 +Lao PDR,LAO,2004,2366398119.8821 +Lao PDR,LAO,2005,2735558726.25625 +Lao PDR,LAO,2006,3452882514.00166 +Lao PDR,LAO,2007,4222962987.53859 +Lao PDR,LAO,2008,5443915120.50795 +Lao PDR,LAO,2009,5832915387.08908 +Lao PDR,LAO,2010,7127792629.58294 +Lao PDR,LAO,2011,8261299199.6817 +Lao PDR,LAO,2012,10191367558.2708 +Lao PDR,LAO,2013,11942157500.4254 +Lao PDR,LAO,2014,13268297521.6066 +Lao PDR,LAO,2015,14390323462.0115 +Lao PDR,LAO,2016,15903437789.8552 +Latvia,LVA,1995,5788368511.12379 +Latvia,LVA,1996,5970044665.64574 +Latvia,LVA,1997,6525676264.21486 +Latvia,LVA,1998,7174985106.63648 +Latvia,LVA,1999,7533187605.09248 +Latvia,LVA,2000,7937758980.30127 +Latvia,LVA,2001,8350252966.19655 +Latvia,LVA,2002,9546441564.34743 +Latvia,LVA,2003,11748433157.0533 +Latvia,LVA,2004,14373269155.7174 +Latvia,LVA,2005,16922504044.804 +Latvia,LVA,2006,21447021570.1028 +Latvia,LVA,2007,30901399261.387 +Latvia,LVA,2008,35596016664.2304 +Latvia,LVA,2009,26169854045.0375 +Latvia,LVA,2010,23757368290.0955 +Latvia,LVA,2011,28223552824.7508 +Latvia,LVA,2012,28119996053.2511 +Latvia,LVA,2013,30254571077.6726 +Latvia,LVA,2014,31352249349.5712 +Latvia,LVA,2015,27026037600.3392 +Latvia,LVA,2016,27677391316.3405 +Lebanon,LBN,1988,3313540067.93246 +Lebanon,LBN,1989,2717998687.71002 +Lebanon,LBN,1990,2838485353.96187 +Lebanon,LBN,1991,4451497288.27108 +Lebanon,LBN,1992,5545921947.46583 +Lebanon,LBN,1993,7535259851.03597 +Lebanon,LBN,1994,9599127049.9375 +Lebanon,LBN,1995,11718795528.4939 +Lebanon,LBN,1996,13690217333.2697 +Lebanon,LBN,1997,15751867489.4446 +Lebanon,LBN,1998,17247179005.5219 +Lebanon,LBN,1999,17391056369.2265 +Lebanon,LBN,2000,17260364842.4544 +Lebanon,LBN,2001,17649751243.7811 +Lebanon,LBN,2002,19152238805.9701 +Lebanon,LBN,2003,20082918739.6352 +Lebanon,LBN,2004,20955223880.597 +Lebanon,LBN,2005,21287562189.0547 +Lebanon,LBN,2006,21796351575.4561 +Lebanon,LBN,2007,24577114427.8607 +Lebanon,LBN,2008,28829850746.2687 +Lebanon,LBN,2009,35139635157.5456 +Lebanon,LBN,2010,38009950248.7562 +Lebanon,LBN,2011,40078938640.1327 +Lebanon,LBN,2012,43205095854.063 +Lebanon,LBN,2013,44352417910.4478 +Lebanon,LBN,2014,45730945273.6318 +Lebanon,LBN,2015,47084703150.9121 +Lebanon,LBN,2016,47536798648.0929 +Lesotho,LSO,1960,34579308.4138317 +Lesotho,LSO,1961,35699286.0142797 +Lesotho,LSO,1962,41859162.8167437 +Lesotho,LSO,1963,47039059.2188156 +Lesotho,LSO,1964,51938961.2207756 +Lesotho,LSO,1965,54878902.4219516 +Lesotho,LSO,1966,56698866.0226795 +Lesotho,LSO,1967,59260814.7837043 +Lesotho,LSO,1968,61444771.1045779 +Lesotho,LSO,1969,65966680.6663867 +Lesotho,LSO,1970,68738625.2274955 +Lesotho,LSO,1971,76482102.9082774 +Lesotho,LSO,1972,80915831.9240276 +Lesotho,LSO,1973,121181556.195965 +Lesotho,LSO,1974,150846210.448859 +Lesotho,LSO,1975,149560513.860717 +Lesotho,LSO,1976,147654093.836247 +Lesotho,LSO,1977,193307267.709292 +Lesotho,LSO,1978,266559337.626495 +Lesotho,LSO,1979,290142517.814727 +Lesotho,LSO,1980,431561376.476631 +Lesotho,LSO,1981,434188034.188034 +Lesotho,LSO,1982,348746792.515574 +Lesotho,LSO,1983,386699344.059023 +Lesotho,LSO,1984,333158502.306626 +Lesotho,LSO,1985,268626893.690796 +Lesotho,LSO,1986,318862870.333317 +Lesotho,LSO,1987,402774850.256341 +Lesotho,LSO,1988,470389190.094405 +Lesotho,LSO,1989,495404890.865006 +Lesotho,LSO,1990,596415106.633185 +Lesotho,LSO,1991,704329193.303687 +Lesotho,LSO,1992,831033946.369022 +Lesotho,LSO,1993,835592816.316424 +Lesotho,LSO,1994,878250452.815718 +Lesotho,LSO,1995,1001889845.5807 +Lesotho,LSO,1996,946123280.510758 +Lesotho,LSO,1997,997996034.86775 +Lesotho,LSO,1998,928458201.135383 +Lesotho,LSO,1999,912771283.593936 +Lesotho,LSO,2000,887295262.184165 +Lesotho,LSO,2001,825706966.802027 +Lesotho,LSO,2002,775780697.302945 +Lesotho,LSO,2003,1157832935.83899 +Lesotho,LSO,2004,1511236652.019 +Lesotho,LSO,2005,1682350941.54943 +Lesotho,LSO,2006,1800105593.03747 +Lesotho,LSO,2007,1820597204.99096 +Lesotho,LSO,2008,1868776303.14958 +Lesotho,LSO,2009,1864004707.45587 +Lesotho,LSO,2010,2394097298.85591 +Lesotho,LSO,2011,2791545696.37485 +Lesotho,LSO,2012,2678243067.38507 +Lesotho,LSO,2013,2532392022.47022 +Lesotho,LSO,2014,2520952363.38624 +Lesotho,LSO,2015,2335194894.3494 +Lesotho,LSO,2016,2199709488.86039 +Liberia,LBR,1960,190495600 +Liberia,LBR,1961,183920900 +Liberia,LBR,1962,191861800 +Liberia,LBR,1963,200229600 +Liberia,LBR,1964,218929100 +Liberia,LBR,1965,229260800 +Liberia,LBR,1966,244459500 +Liberia,LBR,1967,261024300 +Liberia,LBR,1968,276820700 +Liberia,LBR,1969,306961800 +Liberia,LBR,1970,323099700 +Liberia,LBR,1971,341543100 +Liberia,LBR,1972,368098000 +Liberia,LBR,1973,386968300 +Liberia,LBR,1974,486955000 +Liberia,LBR,1975,577549300 +Liberia,LBR,1976,596675700 +Liberia,LBR,1977,673010600 +Liberia,LBR,1978,717240400 +Liberia,LBR,1979,814067900 +Liberia,LBR,1980,854711500 +Liberia,LBR,1981,846514500 +Liberia,LBR,1982,863933200 +Liberia,LBR,1983,823374900 +Liberia,LBR,1984,848478300 +Liberia,LBR,1985,851296100 +Liberia,LBR,1986,840964400 +Liberia,LBR,1987,972800000 +Liberia,LBR,1988,1038300000 +Liberia,LBR,1989,786300000 +Liberia,LBR,1990,384400000 +Liberia,LBR,1991,348000000 +Liberia,LBR,1992,223500000 +Liberia,LBR,1993,160400000 +Liberia,LBR,1994,132200000 +Liberia,LBR,1995,134800000 +Liberia,LBR,1996,159400000 +Liberia,LBR,1997,295900000 +Liberia,LBR,1998,359600000 +Liberia,LBR,1999,441800000 +Liberia,LBR,2000,529064600 +Liberia,LBR,2001,521000000 +Liberia,LBR,2002,543000000 +Liberia,LBR,2003,416000000 +Liberia,LBR,2004,474700000 +Liberia,LBR,2005,550000000 +Liberia,LBR,2006,604028900 +Liberia,LBR,2007,739027200 +Liberia,LBR,2008,850040500 +Liberia,LBR,2009,1155147400 +Liberia,LBR,2010,1292697100 +Liberia,LBR,2011,1545400000 +Liberia,LBR,2012,1735500000 +Liberia,LBR,2013,1946500000 +Liberia,LBR,2014,2013000000 +Liberia,LBR,2015,2034000000 +Liberia,LBR,2016,2101000000 +Libya,LBY,1990,28901836158.1921 +Libya,LBY,1991,31995012468.8279 +Libya,LBY,1992,33881392045.4545 +Libya,LBY,1993,30657030223.3903 +Libya,LBY,1994,28607921928.8175 +Libya,LBY,1995,25544128198.9955 +Libya,LBY,1996,27884615384.6154 +Libya,LBY,1997,30698633109.1343 +Libya,LBY,1998,27249786142.0017 +Libya,LBY,1999,35976714100.9056 +Libya,LBY,2000,38270206950.41 +Libya,LBY,2001,34110064452.1567 +Libya,LBY,2002,20481889763.7795 +Libya,LBY,2003,26265625000 +Libya,LBY,2004,33122307692.3077 +Libya,LBY,2005,47334148578.4164 +Libya,LBY,2006,54961936662.6066 +Libya,LBY,2007,67516236337.7158 +Libya,LBY,2008,87140405361.2292 +Libya,LBY,2009,63028320702.0343 +Libya,LBY,2010,74773444900.5368 +Libya,LBY,2011,34699395523.6073 +Liechtenstein,LIE,1970,90098330.6654471 +Liechtenstein,LIE,1971,104888628.171944 +Liechtenstein,LIE,1972,124941925.010473 +Liechtenstein,LIE,1973,165930611.729019 +Liechtenstein,LIE,1974,193983720.461869 +Liechtenstein,LIE,1975,246387479.177159 +Liechtenstein,LIE,1976,272493879.020643 +Liechtenstein,LIE,1977,303496276.263782 +Liechtenstein,LIE,1978,436918176.733781 +Liechtenstein,LIE,1979,503180669.994587 +Liechtenstein,LIE,1980,534701915.617354 +Liechtenstein,LIE,1981,511658690.561043 +Liechtenstein,LIE,1982,522090331.478107 +Liechtenstein,LIE,1983,524034109.856605 +Liechtenstein,LIE,1984,502617355.407073 +Liechtenstein,LIE,1985,529078995.563876 +Liechtenstein,LIE,1986,779365167.602424 +Liechtenstein,LIE,1987,1052843347.63948 +Liechtenstein,LIE,1988,1161757671.01756 +Liechtenstein,LIE,1989,1120000916.92646 +Liechtenstein,LIE,1990,1421466239.56234 +Liechtenstein,LIE,1991,1484152022.3152 +Liechtenstein,LIE,1992,1631197909.259 +Liechtenstein,LIE,1993,1673104493.77369 +Liechtenstein,LIE,1994,1948118227.68151 +Liechtenstein,LIE,1995,2428461395.34884 +Liechtenstein,LIE,1996,2504033252.42718 +Liechtenstein,LIE,1997,2298410390.68421 +Liechtenstein,LIE,1998,2479721340.8746 +Liechtenstein,LIE,1999,2664026095.06058 +Liechtenstein,LIE,2000,2483953102.79488 +Liechtenstein,LIE,2001,2491822706.80256 +Liechtenstein,LIE,2002,2688630822.53304 +Liechtenstein,LIE,2003,3070691319.52179 +Liechtenstein,LIE,2004,3454362685.96703 +Liechtenstein,LIE,2005,3659251525.8593 +Liechtenstein,LIE,2006,4000239272.61126 +Liechtenstein,LIE,2007,4601299566.81106 +Liechtenstein,LIE,2008,5081432924.0144 +Liechtenstein,LIE,2009,4504549214.22663 +Liechtenstein,LIE,2010,5082366478.08994 +Liechtenstein,LIE,2011,5739977477.47748 +Liechtenstein,LIE,2012,5456009384.66461 +Liechtenstein,LIE,2013,6391735893.83968 +Liechtenstein,LIE,2014,6663501418.90417 +Lithuania,LTU,1995,7870782260.51698 +Lithuania,LTU,1996,8385109020.28485 +Lithuania,LTU,1997,10120274492.8787 +Lithuania,LTU,1998,11240360897.7126 +Lithuania,LTU,1999,10972878636.1675 +Lithuania,LTU,2000,11539211480.3625 +Lithuania,LTU,2001,12252498921.0186 +Lithuania,LTU,2002,14278357283.7419 +Lithuania,LTU,2003,18802576988.1557 +Lithuania,LTU,2004,22649930576.2543 +Lithuania,LTU,2005,26125575942.2814 +Lithuania,LTU,2006,30216060233.4044 +Lithuania,LTU,2007,39738180076.6283 +Lithuania,LTU,2008,47850551148.8365 +Lithuania,LTU,2009,37440673477.8982 +Lithuania,LTU,2010,37120517693.8622 +Lithuania,LTU,2011,43476878139.2577 +Lithuania,LTU,2012,42847900765.8065 +Lithuania,LTU,2013,46473646001.5672 +Lithuania,LTU,2014,48545251795.561 +Lithuania,LTU,2015,41402022148.2053 +Lithuania,LTU,2016,42738875963.3703 +Luxembourg,LUX,1960,703925705.942958 +Luxembourg,LUX,1961,704145671.350213 +Luxembourg,LUX,1962,741509480.796284 +Luxembourg,LUX,1963,791140595.772755 +Luxembourg,LUX,1964,903158753.943622 +Luxembourg,LUX,1965,921600736.304026 +Luxembourg,LUX,1966,968440149.470951 +Luxembourg,LUX,1967,974721762.535327 +Luxembourg,LUX,1968,1066447130.82052 +Luxembourg,LUX,1969,1234878980.502 +Luxembourg,LUX,1970,1509155062.52521 +Luxembourg,LUX,1971,1572310771.77053 +Luxembourg,LUX,1972,1968733021.7212 +Luxembourg,LUX,1973,2701874663.63072 +Luxembourg,LUX,1974,3295861019.05551 +Luxembourg,LUX,1975,3233431611.27564 +Luxembourg,LUX,1976,3544268025.07837 +Luxembourg,LUX,1977,3922895891.95273 +Luxembourg,LUX,1978,4884869091.84066 +Luxembourg,LUX,1979,5711457760.04403 +Luxembourg,LUX,1980,6232005655.95255 +Luxembourg,LUX,1981,5231808670.14342 +Luxembourg,LUX,1982,4764549532.05015 +Luxembourg,LUX,1983,4683697830.37475 +Luxembourg,LUX,1984,4594891580.56409 +Luxembourg,LUX,1985,4738559684.76119 +Luxembourg,LUX,1986,6921264132.20155 +Luxembourg,LUX,1987,8614215559.15721 +Luxembourg,LUX,1988,9750161053.209 +Luxembourg,LUX,1989,10391504709.2547 +Luxembourg,LUX,1990,13229247947.8513 +Luxembourg,LUX,1991,14321878795.0384 +Luxembourg,LUX,1992,16065740777.9172 +Luxembourg,LUX,1993,16486900186.5672 +Luxembourg,LUX,1994,18325791415.4811 +Luxembourg,LUX,1995,21588170498.0843 +Luxembourg,LUX,1996,21776609771.987 +Luxembourg,LUX,1997,19731912494.3617 +Luxembourg,LUX,1998,20209122027.1171 +Luxembourg,LUX,1999,22235929043.2559 +Luxembourg,LUX,2000,21263514833.2412 +Luxembourg,LUX,2001,21272418791.9463 +Luxembourg,LUX,2002,23616328816.1114 +Luxembourg,LUX,2003,29557325056.4334 +Luxembourg,LUX,2004,34685281847.5292 +Luxembourg,LUX,2005,37347394602.6614 +Luxembourg,LUX,2006,42414308116.9239 +Luxembourg,LUX,2007,50888134410.0739 +Luxembourg,LUX,2008,55849686538.7432 +Luxembourg,LUX,2009,51370543206.4462 +Luxembourg,LUX,2010,53212476812.2957 +Luxembourg,LUX,2011,60004630234.4135 +Luxembourg,LUX,2012,56677961787.0717 +Luxembourg,LUX,2013,61808178300.1352 +Luxembourg,LUX,2014,66298060521.7586 +Luxembourg,LUX,2015,58048242436.0611 +Luxembourg,LUX,2016,59947781147.4776 +"Macao SAR, China",MAC,1982,1130457130.00739 +"Macao SAR, China",MAC,1983,1121486562.52512 +"Macao SAR, China",MAC,1984,1292297736.23885 +"Macao SAR, China",MAC,1985,1348827324.09861 +"Macao SAR, China",MAC,1986,1517496131.38322 +"Macao SAR, China",MAC,1987,1941246972.45874 +"Macao SAR, China",MAC,1988,2269233639.28065 +"Macao SAR, China",MAC,1989,2683254708.17411 +"Macao SAR, China",MAC,1990,3220920084.77746 +"Macao SAR, China",MAC,1991,3735117374.41125 +"Macao SAR, China",MAC,1992,4879018601.90911 +"Macao SAR, China",MAC,1993,5625533410.31176 +"Macao SAR, China",MAC,1994,6265844252.10105 +"Macao SAR, China",MAC,1995,6996034036.99892 +"Macao SAR, China",MAC,1996,7122539666.59972 +"Macao SAR, China",MAC,1997,7211264780.00828 +"Macao SAR, China",MAC,1998,6742367273.27418 +"Macao SAR, China",MAC,1999,6490571703.85015 +"Macao SAR, China",MAC,2000,6720492405.83611 +"Macao SAR, China",MAC,2001,6811227982.82193 +"Macao SAR, China",MAC,2002,7322677820.10108 +"Macao SAR, China",MAC,2003,8195033162.12038 +"Macao SAR, China",MAC,2004,10585624890.9277 +"Macao SAR, China",MAC,2005,12092222041.9168 +"Macao SAR, China",MAC,2006,14789661809.1834 +"Macao SAR, China",MAC,2007,18340447242.9971 +"Macao SAR, China",MAC,2008,20917444919.6394 +"Macao SAR, China",MAC,2009,21475520709.3922 +"Macao SAR, China",MAC,2010,28123640998.7253 +"Macao SAR, China",MAC,2011,36709860068.3445 +"Macao SAR, China",MAC,2012,43031577366.4251 +"Macao SAR, China",MAC,2013,51552075901.5183 +"Macao SAR, China",MAC,2014,55347998647.8196 +"Macao SAR, China",MAC,2015,45415278647.464 +"Macao SAR, China",MAC,2016,44802641829.9265 +"Macedonia, FYR",MKD,1990,4471828621.90813 +"Macedonia, FYR",MKD,1991,4694744897.95918 +"Macedonia, FYR",MKD,1992,2316618542.52603 +"Macedonia, FYR",MKD,1993,2550195043.10345 +"Macedonia, FYR",MKD,1994,3381270207.85219 +"Macedonia, FYR",MKD,1995,4449375346.45669 +"Macedonia, FYR",MKD,1996,4422160017.54386 +"Macedonia, FYR",MKD,1997,3735312142.57028 +"Macedonia, FYR",MKD,1998,3571043102.5641 +"Macedonia, FYR",MKD,1999,3673288263.62039 +"Macedonia, FYR",MKD,2000,3772851420.24763 +"Macedonia, FYR",MKD,2001,3709637829.94866 +"Macedonia, FYR",MKD,2002,4018365247.44444 +"Macedonia, FYR",MKD,2003,4946292774.79046 +"Macedonia, FYR",MKD,2004,5682719260.0763 +"Macedonia, FYR",MKD,2005,6258600713.82627 +"Macedonia, FYR",MKD,2006,6861222331.96317 +"Macedonia, FYR",MKD,2007,8336478142.08872 +"Macedonia, FYR",MKD,2008,9909548410.82744 +"Macedonia, FYR",MKD,2009,9401731495.71661 +"Macedonia, FYR",MKD,2010,9407168702.4313 +"Macedonia, FYR",MKD,2011,10494632699.3859 +"Macedonia, FYR",MKD,2012,9745251126.0109 +"Macedonia, FYR",MKD,2013,10817712138.9451 +"Macedonia, FYR",MKD,2014,11362272837.8818 +"Macedonia, FYR",MKD,2015,10051659161.1733 +"Macedonia, FYR",MKD,2016,10899583154.6499 +Madagascar,MDG,1960,673081724.075966 +Madagascar,MDG,1961,699161943.856733 +Madagascar,MDG,1962,739286906.851163 +Madagascar,MDG,1963,759345862.970929 +Madagascar,MDG,1964,802482182.923768 +Madagascar,MDG,1965,833563472.161911 +Madagascar,MDG,1966,900264583.687729 +Madagascar,MDG,1967,956436931.141842 +Madagascar,MDG,1968,1031669636.36062 +Madagascar,MDG,1969,1056391054.53794 +Madagascar,MDG,1970,1111859569.77066 +Madagascar,MDG,1971,1199507629.99222 +Madagascar,MDG,1972,1341590681.586 +Madagascar,MDG,1973,1653062347.36378 +Madagascar,MDG,1974,1917508190.04689 +Madagascar,MDG,1975,2283049233.28581 +Madagascar,MDG,1976,2181844193.92388 +Madagascar,MDG,1977,2358930406.42896 +Madagascar,MDG,1978,2669755115.50372 +Madagascar,MDG,1979,3463565881.42215 +Madagascar,MDG,1980,4042139901.36379 +Madagascar,MDG,1981,3594868208.41664 +Madagascar,MDG,1982,3526198070.09789 +Madagascar,MDG,1983,3511573991.89606 +Madagascar,MDG,1984,2939485471.50097 +Madagascar,MDG,1985,2857889712.4808 +Madagascar,MDG,1986,3258288890.58647 +Madagascar,MDG,1987,2565634382.28729 +Madagascar,MDG,1988,2442507588.38468 +Madagascar,MDG,1989,2498059014.77295 +Madagascar,MDG,1990,3081479800.28735 +Madagascar,MDG,1991,2653141958.52585 +Madagascar,MDG,1992,3024459564.32157 +Madagascar,MDG,1993,3370842210.90955 +Madagascar,MDG,1994,2977040722.47057 +Madagascar,MDG,1995,3159901231.97468 +Madagascar,MDG,1996,3995028592.78722 +Madagascar,MDG,1997,3545776697.12109 +Madagascar,MDG,1998,3738704467.51878 +Madagascar,MDG,1999,3717515282.53319 +Madagascar,MDG,2000,3877673539.09084 +Madagascar,MDG,2001,4529575347.56805 +Madagascar,MDG,2002,4397254607.61164 +Madagascar,MDG,2003,5474030080.24451 +Madagascar,MDG,2004,4363934494.37405 +Madagascar,MDG,2005,5039293030.82367 +Madagascar,MDG,2006,5515884348.54904 +Madagascar,MDG,2007,7342923489.09616 +Madagascar,MDG,2008,9413002920.97008 +Madagascar,MDG,2009,8550363974.79243 +Madagascar,MDG,2010,8729936135.74487 +Madagascar,MDG,2011,9892702357.56691 +Madagascar,MDG,2012,9919780071.28764 +Madagascar,MDG,2013,10601690871.7611 +Madagascar,MDG,2014,10673516672.6664 +Madagascar,MDG,2015,9738652322.17001 +Madagascar,MDG,2016,9990653078.19497 +Malawi,MWI,1960,162960130.465802 +Malawi,MWI,1961,174580139.768776 +Malawi,MWI,1962,183120146.605902 +Malawi,MWI,1963,190820152.770523 +Malawi,MWI,1964,194740155.908875 +Malawi,MWI,1965,229460183.705713 +Malawi,MWI,1966,260400208.476282 +Malawi,MWI,1967,269812781.79847 +Malawi,MWI,1968,245160098.113071 +Malawi,MWI,1969,265800106.373203 +Malawi,MWI,1970,290520116.266151 +Malawi,MWI,1971,365389567.21636 +Malawi,MWI,1972,406084197.385471 +Malawi,MWI,1973,444302221.260027 +Malawi,MWI,1974,548618789.94423 +Malawi,MWI,1975,613196872.560739 +Malawi,MWI,1976,670309252.98248 +Malawi,MWI,1977,806265763.845459 +Malawi,MWI,1978,948983308.780545 +Malawi,MWI,1979,1058297676.33572 +Malawi,MWI,1980,1237662066.78974 +Malawi,MWI,1981,1237686960.55408 +Malawi,MWI,1982,1180094061.48178 +Malawi,MWI,1983,1223225019.32697 +Malawi,MWI,1984,1208026079.75492 +Malawi,MWI,1985,1131349992.22735 +Malawi,MWI,1986,1183671788.12366 +Malawi,MWI,1987,1183071363.00406 +Malawi,MWI,1988,1379923808.24718 +Malawi,MWI,1989,1590201656.14301 +Malawi,MWI,1990,1880784191.81484 +Malawi,MWI,1991,2203536031.03471 +Malawi,MWI,1992,1799529357.0981 +Malawi,MWI,1993,2070647127.03808 +Malawi,MWI,1994,1181801919.66833 +Malawi,MWI,1995,1397454122.24047 +Malawi,MWI,1996,2281039097.69299 +Malawi,MWI,1997,2663238982.8009 +Malawi,MWI,1998,1750585204.44813 +Malawi,MWI,1999,1775920039.58919 +Malawi,MWI,2000,1743506287.41519 +Malawi,MWI,2001,1716502862.2954 +Malawi,MWI,2002,3495748397.63025 +Malawi,MWI,2003,3208837077.25069 +Malawi,MWI,2004,3476094498.87517 +Malawi,MWI,2005,3655909664.1423 +Malawi,MWI,2006,3997852636.24547 +Malawi,MWI,2007,4432192843.58998 +Malawi,MWI,2008,5320925102.29496 +Malawi,MWI,2009,6190991712.12014 +Malawi,MWI,2010,6959697194.02096 +Malawi,MWI,2011,8003300198.30166 +Malawi,MWI,2012,6028470988.53624 +Malawi,MWI,2013,5518901971.40057 +Malawi,MWI,2014,6054750320.3278 +Malawi,MWI,2015,6373201160.03248 +Malawi,MWI,2016,5441852138.14605 +Malaysia,MYS,1960,1916241996.60264 +Malaysia,MYS,1961,1901868548.28172 +Malaysia,MYS,1962,2001502678.6881 +Malaysia,MYS,1963,2510126747.68065 +Malaysia,MYS,1964,2674441395.53116 +Malaysia,MYS,1965,2956356984.18921 +Malaysia,MYS,1966,3143538481.64119 +Malaysia,MYS,1967,3188945511.56409 +Malaysia,MYS,1968,3330393309.81315 +Malaysia,MYS,1969,3664575983.27453 +Malaysia,MYS,1970,3864170913.36731 +Malaysia,MYS,1971,4244340333.51899 +Malaysia,MYS,1972,5043268548.73032 +Malaysia,MYS,1973,7662996766.66803 +Malaysia,MYS,1974,9496074114.07918 +Malaysia,MYS,1975,9298800799.46702 +Malaysia,MYS,1976,11050125904.9418 +Malaysia,MYS,1977,13139397879.1695 +Malaysia,MYS,1978,16358376511.2263 +Malaysia,MYS,1979,21213672089.1976 +Malaysia,MYS,1980,24488033442.0506 +Malaysia,MYS,1981,25004557093.8761 +Malaysia,MYS,1982,26804401815.5348 +Malaysia,MYS,1983,30346788437.5135 +Malaysia,MYS,1984,33943505717.6993 +Malaysia,MYS,1985,31200161095.4491 +Malaysia,MYS,1986,27734562640.4277 +Malaysia,MYS,1987,32181695507.2234 +Malaysia,MYS,1988,35271880250.4964 +Malaysia,MYS,1989,38848567631.4235 +Malaysia,MYS,1990,44024178343.0071 +Malaysia,MYS,1991,49142784405.0044 +Malaysia,MYS,1992,59167157497.9385 +Malaysia,MYS,1993,66894448545.1226 +Malaysia,MYS,1994,74477975918.3051 +Malaysia,MYS,1995,88704944178.6284 +Malaysia,MYS,1996,100854996422.609 +Malaysia,MYS,1997,100005323301.867 +Malaysia,MYS,1998,72167753770.8928 +Malaysia,MYS,1999,79148947368.4211 +Malaysia,MYS,2000,93789736842.1053 +Malaysia,MYS,2001,92783947368.4211 +Malaysia,MYS,2002,100845263157.895 +Malaysia,MYS,2003,110202368421.053 +Malaysia,MYS,2004,124749736842.105 +Malaysia,MYS,2005,143534102611.497 +Malaysia,MYS,2006,162690965596.205 +Malaysia,MYS,2007,193547824063.3 +Malaysia,MYS,2008,230813597937.526 +Malaysia,MYS,2009,202257586267.556 +Malaysia,MYS,2010,255016609232.871 +Malaysia,MYS,2011,297951960784.314 +Malaysia,MYS,2012,314442825692.826 +Malaysia,MYS,2013,323276841537.339 +Malaysia,MYS,2014,338068990803.263 +Malaysia,MYS,2015,296283190372.552 +Malaysia,MYS,2016,296359118754.525 +Maldives,MDV,1980,42463576.1589404 +Maldives,MDV,1981,44781456.9536424 +Maldives,MDV,1982,47935843.7935844 +Maldives,MDV,1983,57829787.2340425 +Maldives,MDV,1984,109503546.099291 +Maldives,MDV,1985,127154929.577465 +Maldives,MDV,1986,141902097.902098 +Maldives,MDV,1987,141268980.477223 +Maldives,MDV,1988,168610478.359909 +Maldives,MDV,1989,189535398.230089 +Maldives,MDV,1990,215089005.235602 +Maldives,MDV,1991,244468292.682927 +Maldives,MDV,1992,284853358.561968 +Maldives,MDV,1993,322326642.335766 +Maldives,MDV,1994,355884383.08887 +Maldives,MDV,1995,398988954.970263 +Maldives,MDV,1996,450382327.952421 +Maldives,MDV,1997,508223602.37893 +Maldives,MDV,1998,540096397.621071 +Maldives,MDV,1999,589239753.610875 +Maldives,MDV,2000,624337145.284622 +Maldives,MDV,2001,870179738.562091 +Maldives,MDV,2002,897031250 +Maldives,MDV,2003,1043403343.75 +Maldives,MDV,2004,1202240023.4375 +Maldives,MDV,2005,1119806500 +Maldives,MDV,2006,1474698125 +Maldives,MDV,2007,1745998937.5 +Maldives,MDV,2008,2109960937.5 +Maldives,MDV,2009,2149257812.5 +Maldives,MDV,2010,2323401757.8125 +Maldives,MDV,2011,2449576516.91549 +Maldives,MDV,2012,2518312129.02218 +Maldives,MDV,2013,2795147949.78753 +Maldives,MDV,2014,3094197810.2 +Maldives,MDV,2015,3435244658.76626 +Maldives,MDV,2016,3591112750.83939 +Mali,MLI,1967,275494520.141999 +Mali,MLI,1968,343771964.662167 +Mali,MLI,1969,339913833.096246 +Mali,MLI,1970,359772363.262207 +Mali,MLI,1971,430096738.369216 +Mali,MLI,1972,486617332.387405 +Mali,MLI,1973,563683660.31194 +Mali,MLI,1974,538747268.333356 +Mali,MLI,1975,830710615.179954 +Mali,MLI,1976,939227993.66396 +Mali,MLI,1977,1049838492.55759 +Mali,MLI,1978,1222702356.10946 +Mali,MLI,1979,1595423285.64659 +Mali,MLI,1980,1759690811.60699 +Mali,MLI,1981,1538972158.1782 +Mali,MLI,1982,1333754034.23489 +Mali,MLI,1983,1297765448.50498 +Mali,MLI,1984,1232932008.13719 +Mali,MLI,1985,1392195933.33971 +Mali,MLI,1986,1852163474.54664 +Mali,MLI,1987,2090629722.63611 +Mali,MLI,1988,2169040741.55896 +Mali,MLI,1989,2181821902.43953 +Mali,MLI,1990,2681912030.49384 +Mali,MLI,1991,2724131545.16958 +Mali,MLI,1992,2830673388.82429 +Mali,MLI,1993,2818280876.07615 +Mali,MLI,1994,2081846482.74771 +Mali,MLI,1995,2706425298.36818 +Mali,MLI,1996,2780422212.26995 +Mali,MLI,1997,2697105694.07956 +Mali,MLI,1998,2920358586.75234 +Mali,MLI,1999,3439463140.35541 +Mali,MLI,2000,2954129565.82965 +Mali,MLI,2001,3465305993.47783 +Mali,MLI,2002,3889758023.73699 +Mali,MLI,2003,4703504466.53245 +Mali,MLI,2004,5444474268.42491 +Mali,MLI,2005,6245031690.06808 +Mali,MLI,2006,6899799785.8441 +Mali,MLI,2007,8145694631.88354 +Mali,MLI,2008,9750822511.47988 +Mali,MLI,2009,10181021770.4326 +Mali,MLI,2010,10678749467.4697 +Mali,MLI,2011,12978107560.5982 +Mali,MLI,2012,12442747897.2223 +Mali,MLI,2013,12813248724.7996 +Mali,MLI,2014,14004067516.3577 +Mali,MLI,2015,12746688961.7795 +Mali,MLI,2016,14045098237.528 +Malta,MLT,1970,250721821.553678 +Malta,MLT,1971,264579879.784878 +Malta,MLT,1972,295118249.324932 +Malta,MLT,1973,345602025.375393 +Malta,MLT,1974,376094108.475331 +Malta,MLT,1975,474620439.58496 +Malta,MLT,1976,527936988.791275 +Malta,MLT,1977,625573345.532174 +Malta,MLT,1978,793675169.878579 +Malta,MLT,1979,1001300838.32335 +Malta,MLT,1980,1250242107.87969 +Malta,MLT,1981,1243469360.56838 +Malta,MLT,1982,1234518125 +Malta,MLT,1983,1165771369.00625 +Malta,MLT,1984,1101828568.76804 +Malta,MLT,1985,1117835285.50512 +Malta,MLT,1986,1435079200.34957 +Malta,MLT,1987,1751247763.41948 +Malta,MLT,1988,2019474244.19359 +Malta,MLT,1989,2118574772.11136 +Malta,MLT,1990,2547163582.33149 +Malta,MLT,1991,2750041434.26295 +Malta,MLT,1992,3021910216.71827 +Malta,MLT,1993,2709178326.78271 +Malta,MLT,1994,2998570146.54095 +Malta,MLT,1995,3439931906.61479 +Malta,MLT,1996,3570271557.88471 +Malta,MLT,1997,3705372038.70537 +Malta,MLT,1998,3923637971.04652 +Malta,MLT,1999,4127313818.33836 +Malta,MLT,2000,4306192435.82207 +Malta,MLT,2001,4331870647.71535 +Malta,MLT,2002,4689832689.83269 +Malta,MLT,2003,5456583589.39342 +Malta,MLT,2004,6062780269.0583 +Malta,MLT,2005,6394851386.64345 +Malta,MLT,2006,6757119558.3992 +Malta,MLT,2007,7880509170.54476 +Malta,MLT,2008,8977149553.24447 +Malta,MLT,2009,8528202278.41067 +Malta,MLT,2010,8741059602.64901 +Malta,MLT,2011,9500001983.37933 +Malta,MLT,2012,9198987219.66026 +Malta,MLT,2013,10131419448.978 +Malta,MLT,2014,11188785252.7566 +Malta,MLT,2015,10287007021.1557 +Malta,MLT,2016,10949092471.597 +Marshall Islands,MHL,1981,31020000 +Marshall Islands,MHL,1982,34918000 +Marshall Islands,MHL,1983,41749000 +Marshall Islands,MHL,1984,45144000 +Marshall Islands,MHL,1985,43879000 +Marshall Islands,MHL,1986,55989000 +Marshall Islands,MHL,1987,62983000 +Marshall Islands,MHL,1988,70688000 +Marshall Islands,MHL,1989,72798000 +Marshall Islands,MHL,1990,78476000 +Marshall Islands,MHL,1991,82507000 +Marshall Islands,MHL,1992,91063000 +Marshall Islands,MHL,1993,99461000 +Marshall Islands,MHL,1994,108071000 +Marshall Islands,MHL,1995,120230000 +Marshall Islands,MHL,1996,110858000 +Marshall Islands,MHL,1997,106289100 +Marshall Islands,MHL,1998,108702100 +Marshall Islands,MHL,1999,107978900 +Marshall Islands,MHL,2000,110937700 +Marshall Islands,MHL,2001,115152100 +Marshall Islands,MHL,2002,124735100 +Marshall Islands,MHL,2003,126887600 +Marshall Islands,MHL,2004,131106400 +Marshall Islands,MHL,2005,137744500 +Marshall Islands,MHL,2006,143656600 +Marshall Islands,MHL,2007,150851598.261375 +Marshall Islands,MHL,2008,152901100 +Marshall Islands,MHL,2009,152631200 +Marshall Islands,MHL,2010,164751300 +Marshall Islands,MHL,2011,172674900 +Marshall Islands,MHL,2012,185055800 +Marshall Islands,MHL,2013,190992000 +Marshall Islands,MHL,2014,183114100 +Marshall Islands,MHL,2015,179432600 +Marshall Islands,MHL,2016,183000000 +Mauritania,MRT,1960,92609222.6912849 +Mauritania,MRT,1961,107726181.218304 +Mauritania,MRT,1962,111148585.592024 +Mauritania,MRT,1963,113797356.813964 +Mauritania,MRT,1964,151897168.106199 +Mauritania,MRT,1965,172767213.286516 +Mauritania,MRT,1966,180340653.822049 +Mauritania,MRT,1967,191221777.800889 +Mauritania,MRT,1968,210695183.760251 +Mauritania,MRT,1969,199643444.567995 +Mauritania,MRT,1970,209348253.60877 +Mauritania,MRT,1971,227051054.984972 +Mauritania,MRT,1972,265009395.148159 +Mauritania,MRT,1973,333731874.379051 +Mauritania,MRT,1974,414772351.88069 +Mauritania,MRT,1975,475916514.745785 +Mauritania,MRT,1976,524407931.940588 +Mauritania,MRT,1977,540635389.589072 +Mauritania,MRT,1978,544424605.052283 +Mauritania,MRT,1979,644070364.889076 +Mauritania,MRT,1980,709041452.217718 +Mauritania,MRT,1981,747994681.876653 +Mauritania,MRT,1982,750214410.723584 +Mauritania,MRT,1983,788371855.945127 +Mauritania,MRT,1984,726937320.846135 +Mauritania,MRT,1985,683193885.003343 +Mauritania,MRT,1986,802890746.890756 +Mauritania,MRT,1987,909820553.400741 +Mauritania,MRT,1988,957377507.476686 +Mauritania,MRT,1989,981529400.534373 +Mauritania,MRT,1990,1019600770.6038 +Mauritania,MRT,1991,1443688869.96039 +Mauritania,MRT,1992,1464392416.14671 +Mauritania,MRT,1993,1249944999.42056 +Mauritania,MRT,1994,1315932644.95246 +Mauritania,MRT,1995,1415296704.11812 +Mauritania,MRT,1996,1442598431.0096 +Mauritania,MRT,1997,1401946853.20672 +Mauritania,MRT,1998,1375115534.0733 +Mauritania,MRT,1999,1405662878.85296 +Mauritania,MRT,2000,1293654175.2102 +Mauritania,MRT,2001,1295539448.36484 +Mauritania,MRT,2002,1324426606.62378 +Mauritania,MRT,2003,1563074859.52173 +Mauritania,MRT,2004,1833444740.37736 +Mauritania,MRT,2005,2184444848.97637 +Mauritania,MRT,2006,3040716679.07669 +Mauritania,MRT,2007,3356757497.1208 +Mauritania,MRT,2008,3978425730.72183 +Mauritania,MRT,2009,3670515695.69153 +Mauritania,MRT,2010,4343665119.8875 +Mauritania,MRT,2011,5179689613.50618 +Mauritania,MRT,2012,5225532807.25025 +Mauritania,MRT,2013,5724227536.49393 +Mauritania,MRT,2014,5391475878.08153 +Mauritania,MRT,2015,4844223517.89396 +Mauritania,MRT,2016,4634588338.07171 +Mauritius,MUS,1976,706991274.414428 +Mauritius,MUS,1977,827094668.018707 +Mauritius,MUS,1978,1019630847.11113 +Mauritius,MUS,1979,1216229419.31802 +Mauritius,MUS,1980,1136543003.2664 +Mauritius,MUS,1981,1147192916.68998 +Mauritius,MUS,1982,1082939379.16762 +Mauritius,MUS,1983,1094857357.63954 +Mauritius,MUS,1984,1044928624.74004 +Mauritius,MUS,1985,1080642033.34952 +Mauritius,MUS,1986,1469046114.77455 +Mauritius,MUS,1987,1888754655.15367 +Mauritius,MUS,1988,2143484487.67274 +Mauritius,MUS,1989,2191096860.28669 +Mauritius,MUS,1990,2653480001.34558 +Mauritius,MUS,1991,2856890680.60285 +Mauritius,MUS,1992,3224267547.80508 +Mauritius,MUS,1993,3263368410.01813 +Mauritius,MUS,1994,3558137040.37772 +Mauritius,MUS,1995,4040345933.29231 +Mauritius,MUS,1996,4421943910.49749 +Mauritius,MUS,1997,4187367601.73431 +Mauritius,MUS,1998,4169664285.38681 +Mauritius,MUS,1999,4291172815.63421 +Mauritius,MUS,2000,4582555124.64952 +Mauritius,MUS,2001,4536538210.66761 +Mauritius,MUS,2002,4767303153.99506 +Mauritius,MUS,2003,5609831328.0648 +Mauritius,MUS,2004,6385695187.0102 +Mauritius,MUS,2005,6283803256.01264 +Mauritius,MUS,2006,7028803365.70151 +Mauritius,MUS,2007,8150138757.15741 +Mauritius,MUS,2008,9990370016.30771 +Mauritius,MUS,2009,9128843109.15588 +Mauritius,MUS,2010,10003670690.3497 +Mauritius,MUS,2011,11518393367.2403 +Mauritius,MUS,2012,11668685524.1265 +Mauritius,MUS,2013,12129642296.4425 +Mauritius,MUS,2014,12803445933.5894 +Mauritius,MUS,2015,11681761261.0428 +Mauritius,MUS,2016,12164207329.5437 +Mexico,MEX,1960,13056168000 +Mexico,MEX,1961,14153952000 +Mexico,MEX,1962,15221056000 +Mexico,MEX,1963,16936336000 +Mexico,MEX,1964,20070136000 +Mexico,MEX,1965,21829712000 +Mexico,MEX,1966,24337232000 +Mexico,MEX,1967,26556376000 +Mexico,MEX,1968,29363632000 +Mexico,MEX,1969,32515752000 +Mexico,MEX,1970,35541712000 +Mexico,MEX,1971,39200880000 +Mexico,MEX,1972,45178120000 +Mexico,MEX,1973,55271304000 +Mexico,MEX,1974,71976544000 +Mexico,MEX,1975,88003984000 +Mexico,MEX,1976,89023915584.4156 +Mexico,MEX,1977,81825783185.8407 +Mexico,MEX,1978,102517451754.386 +Mexico,MEX,1979,134540324561.404 +Mexico,MEX,1980,194356826086.957 +Mexico,MEX,1981,250083020408.163 +Mexico,MEX,1982,173720851063.83 +Mexico,MEX,1983,148866910907.577 +Mexico,MEX,1984,175632157330.155 +Mexico,MEX,1985,184473106267.03 +Mexico,MEX,1986,129440194508.009 +Mexico,MEX,1987,140263679436.947 +Mexico,MEX,1988,183144268180.018 +Mexico,MEX,1989,222977035953.687 +Mexico,MEX,1990,262709776007.964 +Mexico,MEX,1991,314453890803.074 +Mexico,MEX,1992,363609256195.677 +Mexico,MEX,1993,503962832199.255 +Mexico,MEX,1994,527318753518.414 +Mexico,MEX,1995,343792792161.261 +Mexico,MEX,1996,397404140458.457 +Mexico,MEX,1997,480554644187.662 +Mexico,MEX,1998,502010250656.743 +Mexico,MEX,1999,579459682649.262 +Mexico,MEX,2000,683647965226.955 +Mexico,MEX,2001,724703603502.349 +Mexico,MEX,2002,741559509631.317 +Mexico,MEX,2003,713284231624.803 +Mexico,MEX,2004,770267585947.191 +Mexico,MEX,2005,866345821213.261 +Mexico,MEX,2006,965281191371.844 +Mexico,MEX,2007,1043471321169.09 +Mexico,MEX,2008,1101275278668.79 +Mexico,MEX,2009,894948748436.748 +Mexico,MEX,2010,1051128603513.77 +Mexico,MEX,2011,1171187519660.64 +Mexico,MEX,2012,1186598324461.82 +Mexico,MEX,2013,1261981728468.52 +Mexico,MEX,2014,1298398654278.73 +Mexico,MEX,2015,1151037122909.08 +Mexico,MEX,2016,1045998068645.15 +"Micronesia, Fed. Sts.",FSM,1983,106500000 +"Micronesia, Fed. Sts.",FSM,1986,112210000 +"Micronesia, Fed. Sts.",FSM,1987,116700000 +"Micronesia, Fed. Sts.",FSM,1988,124700000 +"Micronesia, Fed. Sts.",FSM,1989,135200000 +"Micronesia, Fed. Sts.",FSM,1990,147200000 +"Micronesia, Fed. Sts.",FSM,1991,166200000 +"Micronesia, Fed. Sts.",FSM,1992,178100000 +"Micronesia, Fed. Sts.",FSM,1993,198400000 +"Micronesia, Fed. Sts.",FSM,1994,202500000 +"Micronesia, Fed. Sts.",FSM,1995,222103600 +"Micronesia, Fed. Sts.",FSM,1996,218845700 +"Micronesia, Fed. Sts.",FSM,1997,206900300 +"Micronesia, Fed. Sts.",FSM,1998,219646200 +"Micronesia, Fed. Sts.",FSM,1999,220660500 +"Micronesia, Fed. Sts.",FSM,2000,233226300 +"Micronesia, Fed. Sts.",FSM,2001,240051900 +"Micronesia, Fed. Sts.",FSM,2002,241543400 +"Micronesia, Fed. Sts.",FSM,2003,244991000 +"Micronesia, Fed. Sts.",FSM,2004,239563300 +"Micronesia, Fed. Sts.",FSM,2005,249845600 +"Micronesia, Fed. Sts.",FSM,2006,252991200 +"Micronesia, Fed. Sts.",FSM,2007,255890800 +"Micronesia, Fed. Sts.",FSM,2008,261339600 +"Micronesia, Fed. Sts.",FSM,2009,277510900 +"Micronesia, Fed. Sts.",FSM,2010,294117200 +"Micronesia, Fed. Sts.",FSM,2011,310287500 +"Micronesia, Fed. Sts.",FSM,2012,325835200 +"Micronesia, Fed. Sts.",FSM,2013,315725600 +"Micronesia, Fed. Sts.",FSM,2014,318072000 +"Micronesia, Fed. Sts.",FSM,2015,314971100 +"Micronesia, Fed. Sts.",FSM,2016,322000000 +Moldova,MDA,1995,1752975841.35916 +Moldova,MDA,1996,1695130456.52174 +Moldova,MDA,1997,1930071406.92641 +Moldova,MDA,1998,1639497206.70391 +Moldova,MDA,1999,1170785047.79461 +Moldova,MDA,2000,1288429150.51394 +Moldova,MDA,2001,1480656884.38462 +Moldova,MDA,2002,1661818168.4226 +Moldova,MDA,2003,1980901553.51226 +Moldova,MDA,2004,2598231467.43671 +Moldova,MDA,2005,2988338439.31553 +Moldova,MDA,2006,3408272498.11516 +Moldova,MDA,2007,4401154128.12297 +Moldova,MDA,2008,6054806100.8468 +Moldova,MDA,2009,5439422031.39627 +Moldova,MDA,2010,5811604051.96737 +Moldova,MDA,2011,7015206498.21955 +Moldova,MDA,2012,7284686576.2835 +Moldova,MDA,2013,7985349731.46471 +Moldova,MDA,2014,7983271110.60446 +Moldova,MDA,2015,6512899540.34594 +Moldova,MDA,2016,6749515654.64419 +Monaco,MCO,1970,293073868.023221 +Monaco,MCO,1971,327651487.962757 +Monaco,MCO,1972,402460333.237637 +Monaco,MCO,1973,523552815.119127 +Monaco,MCO,1974,563939670.704419 +Monaco,MCO,1975,711922994.225545 +Monaco,MCO,1976,735339911.935065 +Monaco,MCO,1977,811250927.388998 +Monaco,MCO,1978,1000535735.38751 +Monaco,MCO,1979,1209898293.46372 +Monaco,MCO,1980,1378130995.65913 +Monaco,MCO,1981,1205166025.51592 +Monaco,MCO,1982,1143229071.77943 +Monaco,MCO,1983,1092551781.01486 +Monaco,MCO,1984,1037314956.25083 +Monaco,MCO,1985,1082851076.52158 +Monaco,MCO,1986,1515209588.2378 +Monaco,MCO,1987,1839095595.25655 +Monaco,MCO,1988,2000674667.08261 +Monaco,MCO,1989,2010116851.20284 +Monaco,MCO,1990,2481316053.85316 +Monaco,MCO,1991,2480497547.84881 +Monaco,MCO,1992,2737066955.91266 +Monaco,MCO,1993,2574439973.17387 +Monaco,MCO,1994,2720297738.93904 +Monaco,MCO,1995,3130270918.79061 +Monaco,MCO,1996,3137848783.08404 +Monaco,MCO,1997,2840182191.77105 +Monaco,MCO,1998,2934578788.86478 +Monaco,MCO,1999,2906009307.6651 +Monaco,MCO,2000,2647883820.18625 +Monaco,MCO,2001,2671401082.76436 +Monaco,MCO,2002,2905973022.1746 +Monaco,MCO,2003,3588988600.70294 +Monaco,MCO,2004,4110348444.49411 +Monaco,MCO,2005,4280072625.97622 +Monaco,MCO,2006,4663488363.0977 +Monaco,MCO,2007,5974371695.95045 +Monaco,MCO,2008,6919241412.09365 +Monaco,MCO,2009,5557245122.31576 +Monaco,MCO,2010,5350674803.33858 +Monaco,MCO,2011,6074884388.58937 +Mongolia,MNG,1981,2310099100 +Mongolia,MNG,1982,2552401933.33333 +Mongolia,MNG,1983,2725736633.33333 +Mongolia,MNG,1984,2098734600 +Mongolia,MNG,1985,2186505475 +Mongolia,MNG,1986,2896178866.66667 +Mongolia,MNG,1987,3020611600 +Mongolia,MNG,1988,3204461566.66667 +Mongolia,MNG,1989,3576966800 +Mongolia,MNG,1990,2560785660 +Mongolia,MNG,1991,2379018326.31579 +Mongolia,MNG,1992,1317611863.84977 +Mongolia,MNG,1993,768401634.154573 +Mongolia,MNG,1994,925817092.217484 +Mongolia,MNG,1995,1452165005.2384 +Mongolia,MNG,1996,1345719472.35883 +Mongolia,MNG,1997,1180934202.83801 +Mongolia,MNG,1998,1124440248.9783 +Mongolia,MNG,1999,1057408588.68269 +Mongolia,MNG,2000,1136896123.61298 +Mongolia,MNG,2001,1267997934.3125 +Mongolia,MNG,2002,1396555719.97409 +Mongolia,MNG,2003,1595297355.78349 +Mongolia,MNG,2004,1992066808.09598 +Mongolia,MNG,2005,2523471532.01083 +Mongolia,MNG,2006,3414055566.1138 +Mongolia,MNG,2007,4234999823.30839 +Mongolia,MNG,2008,5623216448.86851 +Mongolia,MNG,2009,4583850367.88972 +Mongolia,MNG,2010,7189481853.10614 +Mongolia,MNG,2011,10409797610.3499 +Mongolia,MNG,2012,12292770631.1967 +Mongolia,MNG,2013,12582122604.1921 +Mongolia,MNG,2014,12226514746.1243 +Mongolia,MNG,2015,11741338841.1321 +Mongolia,MNG,2016,11160356497.9404 +Montenegro,MNE,2000,984279598.325251 +Montenegro,MNE,2001,1159860290.16658 +Montenegro,MNE,2002,1284446123.33554 +Montenegro,MNE,2003,1707678389.68676 +Montenegro,MNE,2004,2073255525.20487 +Montenegro,MNE,2005,2257174480.78597 +Montenegro,MNE,2006,2696020574.58286 +Montenegro,MNE,2007,3668857103.75034 +Montenegro,MNE,2008,4519731946.68229 +Montenegro,MNE,2009,4141382328.42456 +Montenegro,MNE,2010,4139192052.98013 +Montenegro,MNE,2011,4538198498.74896 +Montenegro,MNE,2012,4087724527.81704 +Montenegro,MNE,2013,4464260488.58205 +Montenegro,MNE,2014,4587928884.17142 +Montenegro,MNE,2015,4019889098.36975 +Montenegro,MNE,2016,4173255530.97345 +Morocco,MAR,1960,2037150716.33238 +Morocco,MAR,1961,2025689536.60705 +Morocco,MAR,1962,2379606422.29029 +Morocco,MAR,1963,2657247327.3392 +Morocco,MAR,1964,2798339768.79755 +Morocco,MAR,1965,2948325264.30195 +Morocco,MAR,1966,2876395613.08171 +Morocco,MAR,1967,3046339294.53611 +Morocco,MAR,1968,3271415867.99723 +Morocco,MAR,1969,3651615453.01848 +Morocco,MAR,1970,3956328426.04486 +Morocco,MAR,1971,4356633663.36634 +Morocco,MAR,1972,5074117544.77482 +Morocco,MAR,1973,6242177798.33938 +Morocco,MAR,1974,7675408485.51421 +Morocco,MAR,1975,8984824182.60333 +Morocco,MAR,1976,9584323309.12136 +Morocco,MAR,1977,11049896742.3889 +Morocco,MAR,1978,13236854105.1672 +Morocco,MAR,1979,15912133569.2852 +Morocco,MAR,1980,21728770055.3777 +Morocco,MAR,1981,17788171722.4446 +Morocco,MAR,1982,17692341358.1272 +Morocco,MAR,1983,16251460689.3254 +Morocco,MAR,1984,14824728528.4604 +Morocco,MAR,1985,14991283215.7408 +Morocco,MAR,1986,19462175321.8224 +Morocco,MAR,1987,21765261041.7265 +Morocco,MAR,1988,25705296183.5037 +Morocco,MAR,1989,26314220188.0257 +Morocco,MAR,1990,30180108561.9305 +Morocco,MAR,1991,32285388165.2999 +Morocco,MAR,1992,33711069430.78 +Morocco,MAR,1993,31655473663.8348 +Morocco,MAR,1994,35604137422.5796 +Morocco,MAR,1995,39030285468.3841 +Morocco,MAR,1996,43161452678.4383 +Morocco,MAR,1997,39147844526.0838 +Morocco,MAR,1998,41806219378.6181 +Morocco,MAR,1999,41632027599.8531 +Morocco,MAR,2000,38857251336.3448 +Morocco,MAR,2001,39459581217.3759 +Morocco,MAR,2002,42236836820.6152 +Morocco,MAR,2003,52064058833.9739 +Morocco,MAR,2004,59626020162.3816 +Morocco,MAR,2005,62343022650.8742 +Morocco,MAR,2006,68640825480.9223 +Morocco,MAR,2007,79041294874.4553 +Morocco,MAR,2008,92507257783.5697 +Morocco,MAR,2009,92897320375.8176 +Morocco,MAR,2010,93216746661.5977 +Morocco,MAR,2011,101370474295.109 +Morocco,MAR,2012,98266306615.3632 +Morocco,MAR,2013,106825649872.108 +Morocco,MAR,2014,109881398474.953 +Morocco,MAR,2015,100593283696.732 +Morocco,MAR,2016,101445004812.643 +Mozambique,MOZ,1980,3526287037.03704 +Mozambique,MOZ,1981,3537099150.14164 +Mozambique,MOZ,1982,3612171957.67196 +Mozambique,MOZ,1983,3236430348.25871 +Mozambique,MOZ,1984,3376172169.81132 +Mozambique,MOZ,1985,4456240740.74074 +Mozambique,MOZ,1986,5247193069.30693 +Mozambique,MOZ,1987,2354117303.06158 +Mozambique,MOZ,1988,2093571673.65612 +Mozambique,MOZ,1989,2314159887.23319 +Mozambique,MOZ,1990,2512079324.07706 +Mozambique,MOZ,1991,3263761937.95748 +Mozambique,MOZ,1992,2291175764.66004 +Mozambique,MOZ,1993,2394823061.93212 +Mozambique,MOZ,1994,2460670287.73707 +Mozambique,MOZ,1995,2521738759.58885 +Mozambique,MOZ,1996,3523842274.89662 +Mozambique,MOZ,1997,4227273069.05991 +Mozambique,MOZ,1998,4873242526.06404 +Mozambique,MOZ,1999,5302532113.25156 +Mozambique,MOZ,2000,5016469068.50898 +Mozambique,MOZ,2001,4766928746.6914 +Mozambique,MOZ,2002,5031510908.86055 +Mozambique,MOZ,2003,5597367853.40358 +Mozambique,MOZ,2004,6831808930.39816 +Mozambique,MOZ,2005,7723846194.87446 +Mozambique,MOZ,2006,8312078525.08582 +Mozambique,MOZ,2007,9366742309.49331 +Mozambique,MOZ,2008,11494837053.4061 +Mozambique,MOZ,2009,10911698208.1015 +Mozambique,MOZ,2010,10154238250.1818 +Mozambique,MOZ,2011,13131168011.807 +Mozambique,MOZ,2012,14534278446.3087 +Mozambique,MOZ,2013,16018848990.669 +Mozambique,MOZ,2014,16961127045.8266 +Mozambique,MOZ,2015,14798399861.5582 +Mozambique,MOZ,2016,11014858591.8422 +Myanmar,MMR,2000,8905066163.58643 +Myanmar,MMR,2001,6477790688.22844 +Myanmar,MMR,2002,6777632512.0781 +Myanmar,MMR,2003,10467109977.6717 +Myanmar,MMR,2004,10567354056.4049 +Myanmar,MMR,2005,11986972418.5103 +Myanmar,MMR,2006,14502553709.8303 +Myanmar,MMR,2007,20182477480.5512 +Myanmar,MMR,2008,31862554101.9378 +Myanmar,MMR,2009,36906181380.8127 +Myanmar,MMR,2010,49540813342.4834 +Myanmar,MMR,2011,59977326085.9908 +Myanmar,MMR,2012,59731122170.0865 +Myanmar,MMR,2013,60132854536.7832 +Myanmar,MMR,2014,65574726566.3779 +Myanmar,MMR,2015,62600906116.0987 +Myanmar,MMR,2016,67429590535.8518 +Namibia,NAM,1980,2434884951.20699 +Namibia,NAM,1981,2259179124.88605 +Namibia,NAM,1982,2128089611.34647 +Namibia,NAM,1983,2308102953.05628 +Namibia,NAM,1984,1960567071.10418 +Namibia,NAM,1985,1615776820.56804 +Namibia,NAM,1986,1816754048.14004 +Namibia,NAM,1987,2310454960.70727 +Namibia,NAM,1988,2506554607.43347 +Namibia,NAM,1989,2547340984.48164 +Namibia,NAM,1990,2804379662.19611 +Namibia,NAM,1991,3012742078.00674 +Namibia,NAM,1992,3448326858.34502 +Namibia,NAM,1993,3218475900.48046 +Namibia,NAM,1994,3636645995.26867 +Namibia,NAM,1995,3942478205.72909 +Namibia,NAM,1996,3945340776.40546 +Namibia,NAM,1997,4102648719.61806 +Namibia,NAM,1998,3826527630.55551 +Namibia,NAM,1999,3818954447.99083 +Namibia,NAM,2000,3908661517.62299 +Namibia,NAM,2001,3546783708.12619 +Namibia,NAM,2002,3361251197.73829 +Namibia,NAM,2003,4931312147.21007 +Namibia,NAM,2004,6606858786.01174 +Namibia,NAM,2005,7261333794.60003 +Namibia,NAM,2006,7978734401.53585 +Namibia,NAM,2007,8740865600.24981 +Namibia,NAM,2008,8486721916.9128 +Namibia,NAM,2009,8876191120.76189 +Namibia,NAM,2010,11282192605.0374 +Namibia,NAM,2011,12409629835.6998 +Namibia,NAM,2012,13016272898.9038 +Namibia,NAM,2013,12713366873.4658 +Namibia,NAM,2014,12853963142.8124 +Namibia,NAM,2015,11491507355.6498 +Namibia,NAM,2016,10267157279.9956 +Nauru,NRU,2007,20432742.1126982 +Nauru,NRU,2008,39333572.3247894 +Nauru,NRU,2009,44290951.9252006 +Nauru,NRU,2010,49248810.5726872 +Nauru,NRU,2011,72751801.046087 +Nauru,NRU,2012,103811958.762887 +Nauru,NRU,2013,108601538.461538 +Nauru,NRU,2014,117020381.931693 +Nauru,NRU,2015,100459782.608696 +Nauru,NRU,2016,102060129.577055 +Nepal,NPL,1960,508334413.965087 +Nepal,NPL,1961,531959561.62226 +Nepal,NPL,1962,574091101.194382 +Nepal,NPL,1963,496947904.443033 +Nepal,NPL,1964,496098775.308642 +Nepal,NPL,1965,735267082.294264 +Nepal,NPL,1966,906811943.824649 +Nepal,NPL,1967,841974025.462659 +Nepal,NPL,1968,772228643.405428 +Nepal,NPL,1969,788641965.432099 +Nepal,NPL,1970,865975308.641975 +Nepal,NPL,1971,882765471.604938 +Nepal,NPL,1972,1024098804.93827 +Nepal,NPL,1973,972101724.995368 +Nepal,NPL,1974,1217953546.97604 +Nepal,NPL,1975,1575789254.46938 +Nepal,NPL,1976,1452792989.10865 +Nepal,NPL,1977,1382400000 +Nepal,NPL,1978,1604162497.45945 +Nepal,NPL,1979,1851250008.33333 +Nepal,NPL,1980,1945916583.33333 +Nepal,NPL,1981,2275583316.66667 +Nepal,NPL,1982,2395429852.43076 +Nepal,NPL,1983,2447174803.37791 +Nepal,NPL,1984,2581207387.79709 +Nepal,NPL,1985,2619913955.51556 +Nepal,NPL,1986,2850784523.37711 +Nepal,NPL,1987,2957255379.54315 +Nepal,NPL,1988,3487009748.35638 +Nepal,NPL,1989,3525228153.17361 +Nepal,NPL,1990,3627562402.66027 +Nepal,NPL,1991,3921476084.89072 +Nepal,NPL,1992,3401211581.29176 +Nepal,NPL,1993,3660041666.66667 +Nepal,NPL,1994,4066775510.20408 +Nepal,NPL,1995,4401104417.67068 +Nepal,NPL,1996,4521580381.47139 +Nepal,NPL,1997,4918691916.53516 +Nepal,NPL,1998,4856255044.39064 +Nepal,NPL,1999,5033642384.10596 +Nepal,NPL,2000,5494252207.90502 +Nepal,NPL,2001,6007061224.48979 +Nepal,NPL,2002,6050875806.66403 +Nepal,NPL,2003,6330473096.54071 +Nepal,NPL,2004,7273938314.71988 +Nepal,NPL,2005,8130258041.46706 +Nepal,NPL,2006,9043715355.8881 +Nepal,NPL,2007,10325618017.379 +Nepal,NPL,2008,12545438605.3959 +Nepal,NPL,2009,12854985464.0764 +Nepal,NPL,2010,16002656434.4746 +Nepal,NPL,2011,18913574370.76 +Nepal,NPL,2012,18851513891.066 +Nepal,NPL,2013,19271168018.482 +Nepal,NPL,2014,20002968837.9471 +Nepal,NPL,2015,21313549578.6984 +Nepal,NPL,2016,21143873417.4002 +Netherlands,NLD,1960,12276734172.0828 +Netherlands,NLD,1961,13493833739.9949 +Netherlands,NLD,1962,14647057370.1418 +Netherlands,NLD,1963,15891241386.291 +Netherlands,NLD,1964,18699380731.3465 +Netherlands,NLD,1965,21000586933.2041 +Netherlands,NLD,1966,22867203317.4022 +Netherlands,NLD,1967,25087562181.3218 +Netherlands,NLD,1968,27817605743.2503 +Netherlands,NLD,1969,31503868835.1853 +Netherlands,NLD,1970,37677621537.7123 +Netherlands,NLD,1971,44010160463.6591 +Netherlands,NLD,1972,54008338917.8797 +Netherlands,NLD,1973,70924006306.1643 +Netherlands,NLD,1974,86129928026.8875 +Netherlands,NLD,1975,98970041042.175 +Netherlands,NLD,1976,107775403067.178 +Netherlands,NLD,1977,125395875998.923 +Netherlands,NLD,1978,153870462415.971 +Netherlands,NLD,1979,177376289135.45 +Netherlands,NLD,1980,192661371425.405 +Netherlands,NLD,1981,162039376225.382 +Netherlands,NLD,1982,156456858050.673 +Netherlands,NLD,1983,151487045479.114 +Netherlands,NLD,1984,142075910370.879 +Netherlands,NLD,1985,142009922306.263 +Netherlands,NLD,1986,198298498021.227 +Netherlands,NLD,1987,241918791122.715 +Netherlands,NLD,1988,258567751142.825 +Netherlands,NLD,1989,255039560739.894 +Netherlands,NLD,1990,314267667675.178 +Netherlands,NLD,1991,323320449905.705 +Netherlands,NLD,1992,358330385839.599 +Netherlands,NLD,1993,349037818106.312 +Netherlands,NLD,1994,374291430318.44 +Netherlands,NLD,1995,446528959648.641 +Netherlands,NLD,1996,445704575163.399 +Netherlands,NLD,1997,412199006098.938 +Netherlands,NLD,1998,432476116418.574 +Netherlands,NLD,1999,441975282335.393 +Netherlands,NLD,2000,412807259996.315 +Netherlands,NLD,2001,426573601789.709 +Netherlands,NLD,2002,465368906455.863 +Netherlands,NLD,2003,571863431151.242 +Netherlands,NLD,2004,650532654581.574 +Netherlands,NLD,2005,678533764457.157 +Netherlands,NLD,2006,726649102998.369 +Netherlands,NLD,2007,839419655078.018 +Netherlands,NLD,2008,936228211513.11 +Netherlands,NLD,2009,857932759099.75 +Netherlands,NLD,2010,836389937229.197 +Netherlands,NLD,2011,893757287201.688 +Netherlands,NLD,2012,828946812396.788 +Netherlands,NLD,2013,866680000367.264 +Netherlands,NLD,2014,879635084124.987 +Netherlands,NLD,2015,750318056805.557 +Netherlands,NLD,2016,770845046231.727 +New Caledonia,NCL,1965,159594493.548808 +New Caledonia,NCL,1966,164206537.561675 +New Caledonia,NCL,1967,180036768.87301 +New Caledonia,NCL,1968,215507164.034258 +New Caledonia,NCL,1969,263108834.536684 +New Caledonia,NCL,1970,358815681.903215 +New Caledonia,NCL,1971,413634335.270097 +New Caledonia,NCL,1972,505892512.861927 +New Caledonia,NCL,1973,542294864.81243 +New Caledonia,NCL,1974,637400199.110489 +New Caledonia,NCL,1975,816647865.83143 +New Caledonia,NCL,1976,798310509.647434 +New Caledonia,NCL,1977,837616756.533737 +New Caledonia,NCL,1978,846007597.720396 +New Caledonia,NCL,1979,1047225130.24333 +New Caledonia,NCL,1980,1182457142.60648 +New Caledonia,NCL,1981,972563810.230325 +New Caledonia,NCL,1982,904619629.797268 +New Caledonia,NCL,1983,823832940.450511 +New Caledonia,NCL,1984,796018978.4713 +New Caledonia,NCL,1985,854823821.723177 +New Caledonia,NCL,1986,1201262517.87644 +New Caledonia,NCL,1987,1488113532.28584 +New Caledonia,NCL,1988,2072735787.31779 +New Caledonia,NCL,1989,2185072798.33184 +New Caledonia,NCL,1990,2529310103.83608 +New Caledonia,NCL,1991,2653781596.46008 +New Caledonia,NCL,1992,2923764926.39718 +New Caledonia,NCL,1993,3070161471.04451 +New Caledonia,NCL,1994,3038727617.03901 +New Caledonia,NCL,1995,3628440274.67 +New Caledonia,NCL,1996,3606968433.92682 +New Caledonia,NCL,1997,3291489840.57141 +New Caledonia,NCL,1998,3158806480.26107 +New Caledonia,NCL,1999,3056999988.09146 +New Caledonia,NCL,2000,2682347064.3642 +New Zealand,NZL,1960,5485854791.97096 +New Zealand,NZL,1961,5670064168.21773 +New Zealand,NZL,1962,6077496267.76294 +New Zealand,NZL,1963,6638937283.13963 +New Zealand,NZL,1964,7274144350.81809 +New Zealand,NZL,1965,5654463586.00366 +New Zealand,NZL,1966,5863733230.97616 +New Zealand,NZL,1967,5961418093.53003 +New Zealand,NZL,1968,5180597620.64135 +New Zealand,NZL,1969,5761588761.69421 +New Zealand,NZL,1971,7912290825.15868 +New Zealand,NZL,1972,9567331064.65727 +New Zealand,NZL,1973,12802281897.8712 +New Zealand,NZL,1974,13940981798.1247 +New Zealand,NZL,1975,12861983284.3912 +New Zealand,NZL,1976,13604832424.0062 +New Zealand,NZL,1977,15446825318.4556 +New Zealand,NZL,1978,18530518394.6488 +New Zealand,NZL,1979,20730241410.3977 +New Zealand,NZL,1980,23245512449.3341 +New Zealand,NZL,1981,24417617184.2478 +New Zealand,NZL,1982,24164603058.9949 +New Zealand,NZL,1983,24308622502.6288 +New Zealand,NZL,1984,21665456808.0473 +New Zealand,NZL,1985,24680306905.3708 +New Zealand,NZL,1986,30605196451.2041 +New Zealand,NZL,1987,40377592076.7564 +New Zealand,NZL,1988,45176167471.8196 +New Zealand,NZL,1989,43920222524.7085 +New Zealand,NZL,1990,45495727006.5141 +New Zealand,NZL,1991,42745329732.163 +New Zealand,NZL,1992,41649298170.9911 +New Zealand,NZL,1993,46775067750.6775 +New Zealand,NZL,1994,55315342817.0218 +New Zealand,NZL,1995,63918703506.9075 +New Zealand,NZL,1996,70140835299.0148 +New Zealand,NZL,1997,66074513017.7142 +New Zealand,NZL,1998,56227169851.0448 +New Zealand,NZL,1999,58761741657.5847 +New Zealand,NZL,2000,52623281956.7031 +New Zealand,NZL,2001,53872425916.6248 +New Zealand,NZL,2002,66628222189.3637 +New Zealand,NZL,2003,88250885550.2626 +New Zealand,NZL,2004,103905882352.941 +New Zealand,NZL,2005,114719425473.492 +New Zealand,NZL,2006,111606899682.251 +New Zealand,NZL,2007,137314617476.299 +New Zealand,NZL,2008,133279679482.674 +New Zealand,NZL,2009,121337372727.841 +New Zealand,NZL,2010,146580949348.893 +New Zealand,NZL,2011,168461998741.295 +New Zealand,NZL,2012,176192886551.397 +New Zealand,NZL,2013,190521142196.379 +New Zealand,NZL,2014,200696291789.952 +New Zealand,NZL,2015,175564427552.782 +New Zealand,NZL,2016,185017316092.654 +Nicaragua,NIC,1960,223854666.666667 +Nicaragua,NIC,1961,240524723.428571 +Nicaragua,NIC,1962,265291588.666667 +Nicaragua,NIC,1963,292916241.142857 +Nicaragua,NIC,1964,341973758.857143 +Nicaragua,NIC,1965,566542872.357143 +Nicaragua,NIC,1966,606671444 +Nicaragua,NIC,1967,657171436.714286 +Nicaragua,NIC,1968,695899980.428571 +Nicaragua,NIC,1969,747971449.571429 +Nicaragua,NIC,1970,776585681.071429 +Nicaragua,NIC,1971,826571413.428571 +Nicaragua,NIC,1972,880842890.071429 +Nicaragua,NIC,1973,1093571441.5 +Nicaragua,NIC,1974,1520900045.14286 +Nicaragua,NIC,1975,1590428522.64286 +Nicaragua,NIC,1976,1847871371.71429 +Nicaragua,NIC,1977,2239857060.57143 +Nicaragua,NIC,1978,2142128603.78571 +Nicaragua,NIC,1979,1527852635.63158 +Nicaragua,NIC,1980,2189347367.52632 +Nicaragua,NIC,1981,2448290109.65 +Nicaragua,NIC,1982,2465165179.69565 +Nicaragua,NIC,1983,2743341724.08333 +Nicaragua,NIC,1984,3105517091.41379 +Nicaragua,NIC,1985,2683816288.7907 +Nicaragua,NIC,1986,2885710608.8808 +Nicaragua,NIC,1987,3851213727.67857 +Nicaragua,NIC,1988,2630904261.83247 +Nicaragua,NIC,1989,1013184745.70715 +Nicaragua,NIC,1990,1009455476.05583 +Nicaragua,NIC,1991,1488804123.71134 +Nicaragua,NIC,1992,1792800000 +Nicaragua,NIC,1993,1756454248.36601 +Nicaragua,NIC,1994,3863185119.04762 +Nicaragua,NIC,1995,4140470000 +Nicaragua,NIC,1996,4308351902.78601 +Nicaragua,NIC,1997,4389965590.96538 +Nicaragua,NIC,1998,4635267224.84195 +Nicaragua,NIC,1999,4855717874.68247 +Nicaragua,NIC,2000,5107329007.0922 +Nicaragua,NIC,2001,5323146565.70315 +Nicaragua,NIC,2002,5224213017.54386 +Nicaragua,NIC,2003,5322454925.84746 +Nicaragua,NIC,2004,5795568204.64532 +Nicaragua,NIC,2005,6321335612.22233 +Nicaragua,NIC,2006,6763671610.70006 +Nicaragua,NIC,2007,7423377429.05927 +Nicaragua,NIC,2008,8496965842.2767 +Nicaragua,NIC,2009,8298695144.91507 +Nicaragua,NIC,2010,8758622328.66962 +Nicaragua,NIC,2011,9774316692.15985 +Nicaragua,NIC,2012,10532001129.67 +Nicaragua,NIC,2013,10982972256.3787 +Nicaragua,NIC,2014,11880438824.4494 +Nicaragua,NIC,2015,12747741539.7259 +Nicaragua,NIC,2016,13230844686.858 +Niger,NER,1960,449526872.565561 +Niger,NER,1961,485785231.729353 +Niger,NER,1962,531736599.930736 +Niger,NER,1963,586294879.4719 +Niger,NER,1964,582816396.216401 +Niger,NER,1965,673383510.242124 +Niger,NER,1966,702296079.857695 +Niger,NER,1967,665586872.839162 +Niger,NER,1968,641214226.839012 +Niger,NER,1969,625867984.42818 +Niger,NER,1970,649916621.179857 +Niger,NER,1971,693573704.422866 +Niger,NER,1972,742779659.455167 +Niger,NER,1973,946385104.967731 +Niger,NER,1974,1026137112.43707 +Niger,NER,1975,1048690931.5406 +Niger,NER,1976,1064517600.10051 +Niger,NER,1977,1291458043.7403 +Niger,NER,1978,1774365587.86851 +Niger,NER,1979,2109277663.09748 +Niger,NER,1980,2508524715.79516 +Niger,NER,1981,2170893417.98129 +Niger,NER,1982,2017612217.82752 +Niger,NER,1983,1803099561.08393 +Niger,NER,1984,1461243326.83775 +Niger,NER,1985,1440581653.32328 +Niger,NER,1986,1904097000.74963 +Niger,NER,1987,2233006101.94476 +Niger,NER,1988,2280356194.14559 +Niger,NER,1989,2179567111.0004 +Niger,NER,1990,2480673304.74309 +Niger,NER,1991,2327986215.86356 +Niger,NER,1992,2344987614.27441 +Niger,NER,1993,1606581743.78497 +Niger,NER,1994,1563207224.65066 +Niger,NER,1995,1880803361.68562 +Niger,NER,1996,1987770898.54334 +Niger,NER,1997,1845599608.44272 +Niger,NER,1998,2076737356.67897 +Niger,NER,1999,2018193703.06047 +Niger,NER,2000,1798374468.36362 +Niger,NER,2001,1945327564.65042 +Niger,NER,2002,2170481508.86916 +Niger,NER,2003,2731416346.48158 +Niger,NER,2004,3052898739.4678 +Niger,NER,2005,3405134831.8505 +Niger,NER,2006,3646728060.06463 +Niger,NER,2007,4291363390.91295 +Niger,NER,2008,5403363917.3096 +Niger,NER,2009,5397121856.35204 +Niger,NER,2010,5718589799.24366 +Niger,NER,2011,6409169889.50891 +Niger,NER,2012,6942209594.55433 +Niger,NER,2013,7667951987.6933 +Niger,NER,2014,8245312136.56543 +Niger,NER,2015,7142951342.4223 +Niger,NER,2016,7508986509.27487 +Nigeria,NGA,1960,4196092258.15484 +Nigeria,NGA,1961,4467200335.99328 +Nigeria,NGA,1962,4909302953.94092 +Nigeria,NGA,1963,5165489010.2198 +Nigeria,NGA,1964,5552822483.55033 +Nigeria,NGA,1965,5874422511.54977 +Nigeria,NGA,1966,6366792664.14672 +Nigeria,NGA,1967,5203135937.28125 +Nigeria,NGA,1968,5200895982.08036 +Nigeria,NGA,1969,6634187316.25367 +Nigeria,NGA,1970,12545849083.0183 +Nigeria,NGA,1971,9181769911.50443 +Nigeria,NGA,1972,12274416017.7976 +Nigeria,NGA,1973,15162871287.1287 +Nigeria,NGA,1974,24846641318.1242 +Nigeria,NGA,1975,27778934624.6973 +Nigeria,NGA,1976,36308883248.731 +Nigeria,NGA,1977,36035407725.3219 +Nigeria,NGA,1978,36527862208.7133 +Nigeria,NGA,1979,47259911894.2731 +Nigeria,NGA,1980,64201788122.6054 +Nigeria,NGA,1981,61076493506.4935 +Nigeria,NGA,1982,51397461685.8237 +Nigeria,NGA,1983,35451565749.2355 +Nigeria,NGA,1984,28500815241.471 +Nigeria,NGA,1985,28873977228.1115 +Nigeria,NGA,1986,20721499308.4371 +Nigeria,NGA,1987,24093203444.564 +Nigeria,NGA,1988,23272161396.8853 +Nigeria,NGA,1989,24231168858.7187 +Nigeria,NGA,1990,30757075595.3681 +Nigeria,NGA,1991,27392886872.5547 +Nigeria,NGA,1992,29300903643.0584 +Nigeria,NGA,1993,15789003752.7594 +Nigeria,NGA,1994,18086400535.5777 +Nigeria,NGA,1995,28546958641.2735 +Nigeria,NGA,1996,34987951375 +Nigeria,NGA,1997,35822342617.6978 +Nigeria,NGA,1998,32004613750 +Nigeria,NGA,1999,35870792987.9432 +Nigeria,NGA,2000,46386011231.37 +Nigeria,NGA,2001,44137994251.618 +Nigeria,NGA,2002,59116847821.5797 +Nigeria,NGA,2003,67655813930.0926 +Nigeria,NGA,2004,87845420504.485 +Nigeria,NGA,2005,112248353104.911 +Nigeria,NGA,2006,145429764861.249 +Nigeria,NGA,2007,166451213395.64 +Nigeria,NGA,2008,208064753766.47 +Nigeria,NGA,2009,169481317540.364 +Nigeria,NGA,2010,369062464570.387 +Nigeria,NGA,2011,411743801711.642 +Nigeria,NGA,2012,460953836444.364 +Nigeria,NGA,2013,514966287206.505 +Nigeria,NGA,2014,568498939784.021 +Nigeria,NGA,2015,481066152870.266 +Nigeria,NGA,2016,405082677659.868 +Northern Mariana Islands,MNP,2002,1284000000 +Northern Mariana Islands,MNP,2003,1239000000 +Northern Mariana Islands,MNP,2004,1210000000 +Northern Mariana Islands,MNP,2005,1061000000 +Northern Mariana Islands,MNP,2006,990000000 +Northern Mariana Islands,MNP,2007,938000000 +Northern Mariana Islands,MNP,2008,939000000 +Northern Mariana Islands,MNP,2009,795000000 +Northern Mariana Islands,MNP,2010,799000000 +Northern Mariana Islands,MNP,2011,733000000 +Northern Mariana Islands,MNP,2012,751000000 +Northern Mariana Islands,MNP,2013,780000000 +Northern Mariana Islands,MNP,2014,836000000 +Northern Mariana Islands,MNP,2015,922000000 +Norway,NOR,1960,5163271598.15702 +Norway,NOR,1961,5632460936.54576 +Norway,NOR,1962,6066976682.67364 +Norway,NOR,1963,6510239502.76489 +Norway,NOR,1964,7159202706.48027 +Norway,NOR,1965,8058681060.159 +Norway,NOR,1966,8696460205.3397 +Norway,NOR,1967,9514496703.39762 +Norway,NOR,1968,10159934136.7838 +Norway,NOR,1969,11063065083.4888 +Norway,NOR,1970,12814123115.2613 +Norway,NOR,1971,14583114840.0629 +Norway,NOR,1972,17358610849.701 +Norway,NOR,1973,22534253702.8686 +Norway,NOR,1974,27145693810.1341 +Norway,NOR,1975,32877805200.023 +Norway,NOR,1976,35942270686.3374 +Norway,NOR,1977,41508030431.1074 +Norway,NOR,1978,46523091009.6713 +Norway,NOR,1979,53132244623.9213 +Norway,NOR,1980,64439382896.0156 +Norway,NOR,1981,63596654760.8677 +Norway,NOR,1982,62647195537.6511 +Norway,NOR,1983,61627240831.0948 +Norway,NOR,1984,62057955032.7758 +Norway,NOR,1985,65416879914.3907 +Norway,NOR,1986,78693253275.995 +Norway,NOR,1987,94230055658.6271 +Norway,NOR,1988,101900260856.222 +Norway,NOR,1989,102633789557.535 +Norway,NOR,1990,119791683307.507 +Norway,NOR,1991,121872464483.487 +Norway,NOR,1992,130838040067.584 +Norway,NOR,1993,120579072750.596 +Norway,NOR,1994,127131461119.927 +Norway,NOR,1995,152027402449.804 +Norway,NOR,1996,163517783497.163 +Norway,NOR,1997,161354369892.838 +Norway,NOR,1998,154165219811.533 +Norway,NOR,1999,162286003692.686 +Norway,NOR,2000,171315639982.731 +Norway,NOR,2001,174003247439.305 +Norway,NOR,2002,195418347152.985 +Norway,NOR,2003,228752436371.854 +Norway,NOR,2004,264357494659.388 +Norway,NOR,2005,308722079937.912 +Norway,NOR,2006,345424664369.357 +Norway,NOR,2007,400883873279.083 +Norway,NOR,2008,461946808510.638 +Norway,NOR,2009,386383919342.271 +Norway,NOR,2010,428527064662.898 +Norway,NOR,2011,498156756965.081 +Norway,NOR,2012,509704856037.817 +Norway,NOR,2013,522746212765.957 +Norway,NOR,2014,498339751388.521 +Norway,NOR,2015,386578443732.562 +Norway,NOR,2016,370556666666.667 +Oman,OMN,1965,63287594.5113414 +Oman,OMN,1966,67768132.1758611 +Oman,OMN,1967,107152720.243027 +Oman,OMN,1968,188864890.808735 +Oman,OMN,1969,239980801.535877 +Oman,OMN,1970,256299496.040317 +Oman,OMN,1971,301010587.102984 +Oman,OMN,1972,366857738.40542 +Oman,OMN,1973,483033932.135729 +Oman,OMN,1974,1645917776.49103 +Oman,OMN,1975,2096699189.34569 +Oman,OMN,1976,2560220034.74233 +Oman,OMN,1977,2741169947.88651 +Oman,OMN,1978,2740301389.69311 +Oman,OMN,1979,3733352634.62652 +Oman,OMN,1980,5981760277.93862 +Oman,OMN,1981,7259120150.55009 +Oman,OMN,1982,7554719455.70353 +Oman,OMN,1983,7932541690.79328 +Oman,OMN,1984,8821366531.55762 +Oman,OMN,1985,10005500579.0388 +Oman,OMN,1986,7323822251.3089 +Oman,OMN,1987,7811183094.92848 +Oman,OMN,1988,8386215864.75943 +Oman,OMN,1989,9372171651.49545 +Oman,OMN,1990,11685045513.6541 +Oman,OMN,1991,11341482444.7334 +Oman,OMN,1992,12452275682.7048 +Oman,OMN,1993,12493107932.3797 +Oman,OMN,1994,12918855656.697 +Oman,OMN,1995,13802600780.2341 +Oman,OMN,1996,15277763328.9987 +Oman,OMN,1997,15837451235.3706 +Oman,OMN,1998,14085373211.9636 +Oman,OMN,1999,15710148244.4733 +Oman,OMN,2000,19507412223.6671 +Oman,OMN,2001,19452015604.6814 +Oman,OMN,2002,20142782834.8505 +Oman,OMN,2003,21633810143.0429 +Oman,OMN,2004,24763589076.723 +Oman,OMN,2005,31081924577.3732 +Oman,OMN,2006,37215864759.4278 +Oman,OMN,2007,42085305591.6775 +Oman,OMN,2008,60905331599.4798 +Oman,OMN,2009,48388296488.9467 +Oman,OMN,2010,58641621957.8941 +Oman,OMN,2011,67937307405.122 +Oman,OMN,2012,76689583316.9189 +Oman,OMN,2013,78938585285.6905 +Oman,OMN,2014,81034395037.5246 +Oman,OMN,2015,69831770995.5405 +Oman,OMN,2016,66293368010.4031 +Pakistan,PAK,1960,3707055900.88198 +Pakistan,PAK,1961,4054599181.01638 +Pakistan,PAK,1962,4233095590.0882 +Pakistan,PAK,1963,4540529105.41789 +Pakistan,PAK,1964,5130407727.84544 +Pakistan,PAK,1965,5884712095.75809 +Pakistan,PAK,1966,6466610751.78496 +Pakistan,PAK,1967,7403821902.56195 +Pakistan,PAK,1968,8090088555.2289 +Pakistan,PAK,1969,8632927257.45485 +Pakistan,PAK,1970,10027088849.223 +Pakistan,PAK,1971,10602058189.8362 +Pakistan,PAK,1972,9309109764.07784 +Pakistan,PAK,1973,6324884129.38617 +Pakistan,PAK,1974,8773030424.24242 +Pakistan,PAK,1975,11340000242.4242 +Pakistan,PAK,1976,13338484979.798 +Pakistan,PAK,1977,15126059646.4646 +Pakistan,PAK,1978,17820100626.2626 +Pakistan,PAK,1979,19707979303.0303 +Pakistan,PAK,1980,23689696767.6768 +Pakistan,PAK,1981,28100605515.1515 +Pakistan,PAK,1982,30725972786.7299 +Pakistan,PAK,1983,28691890433.0709 +Pakistan,PAK,1984,31151824658.6524 +Pakistan,PAK,1985,31144920554.0897 +Pakistan,PAK,1986,31899071053.9368 +Pakistan,PAK,1987,33351528115.351 +Pakistan,PAK,1988,38472741737.3968 +Pakistan,PAK,1989,40171019643.3511 +Pakistan,PAK,1990,40010424928.715 +Pakistan,PAK,1991,45451960731.7204 +Pakistan,PAK,1992,48635176852.7673 +Pakistan,PAK,1993,51478304859.5879 +Pakistan,PAK,1994,51894781281.8919 +Pakistan,PAK,1995,60636022422.6176 +Pakistan,PAK,1996,63320122807.1223 +Pakistan,PAK,1997,62433300338.0941 +Pakistan,PAK,1998,62191955814.3478 +Pakistan,PAK,1999,62973855718.8874 +Pakistan,PAK,2000,73952374969.7995 +Pakistan,PAK,2001,72309738921.3329 +Pakistan,PAK,2002,72306820396.2325 +Pakistan,PAK,2003,83244801092.7096 +Pakistan,PAK,2004,97977766197.6724 +Pakistan,PAK,2005,109502102510.883 +Pakistan,PAK,2006,137264061106.043 +Pakistan,PAK,2007,152385716311.916 +Pakistan,PAK,2008,170077814106.305 +Pakistan,PAK,2009,168152775283.032 +Pakistan,PAK,2010,177406854514.885 +Pakistan,PAK,2011,213587413183.996 +Pakistan,PAK,2012,224383620829.57 +Pakistan,PAK,2013,231218567178.979 +Pakistan,PAK,2014,244360888750.807 +Pakistan,PAK,2015,271049886672.733 +Pakistan,PAK,2016,283659980698.255 +Palau,PLW,1990,76888000 +Palau,PLW,1991,83855000 +Palau,PLW,1992,82451000 +Palau,PLW,1993,75907000 +Palau,PLW,1994,83527000 +Palau,PLW,1995,95237000 +Palau,PLW,1996,108203000 +Palau,PLW,1997,113213000 +Palau,PLW,1998,117320000 +Palau,PLW,1999,113485000 +Palau,PLW,2000,149300000 +Palau,PLW,2001,160000000 +Palau,PLW,2002,163500000 +Palau,PLW,2003,159900000 +Palau,PLW,2004,175300000 +Palau,PLW,2005,193300000 +Palau,PLW,2006,194700000 +Palau,PLW,2007,196000000 +Palau,PLW,2008,198100000 +Palau,PLW,2009,186400000 +Palau,PLW,2010,183800000 +Palau,PLW,2011,199900000 +Palau,PLW,2012,214200000 +Palau,PLW,2013,228700000 +Palau,PLW,2014,250900000 +Palau,PLW,2015,287400000 +Palau,PLW,2016,293000000 +Panama,PAN,1960,537147100 +Panama,PAN,1961,599026300 +Panama,PAN,1962,652120900 +Panama,PAN,1963,722784500 +Panama,PAN,1964,776137500 +Panama,PAN,1965,852485300 +Panama,PAN,1966,928833000 +Panama,PAN,1967,1034376400 +Panama,PAN,1968,1112791100 +Panama,PAN,1969,1221305700 +Panama,PAN,1970,1351006400 +Panama,PAN,1971,1523917200 +Panama,PAN,1972,1673411700 +Panama,PAN,1973,1913793400 +Panama,PAN,1974,2188307600 +Panama,PAN,1975,2435304100 +Panama,PAN,1976,2588106000 +Panama,PAN,1977,2738261900 +Panama,PAN,1978,3244558600 +Panama,PAN,1979,3704551600 +Panama,PAN,1980,4614086400 +Panama,PAN,1981,5222421500 +Panama,PAN,1982,5769767900 +Panama,PAN,1983,5923755900 +Panama,PAN,1984,6183387100 +Panama,PAN,1985,6541517100 +Panama,PAN,1986,6797834200 +Panama,PAN,1987,6827665300 +Panama,PAN,1988,5902783400 +Panama,PAN,1989,5918469800 +Panama,PAN,1990,6433967000 +Panama,PAN,1991,7074675500 +Panama,PAN,1992,8042337700 +Panama,PAN,1993,8782585400 +Panama,PAN,1994,9365289800 +Panama,PAN,1995,9573813700 +Panama,PAN,1996,9870494000 +Panama,PAN,1997,10677286100 +Panama,PAN,1998,11575486400 +Panama,PAN,1999,12130252200 +Panama,PAN,2000,12304115000 +Panama,PAN,2001,12502013400 +Panama,PAN,2002,12994310400 +Panama,PAN,2003,13693981200 +Panama,PAN,2004,15013381700 +Panama,PAN,2005,16374393900 +Panama,PAN,2006,18141666300 +Panama,PAN,2007,20958000000 +Panama,PAN,2008,24522200000 +Panama,PAN,2009,26593500000 +Panama,PAN,2010,28917200000 +Panama,PAN,2011,34373820500 +Panama,PAN,2012,39954761200 +Panama,PAN,2013,44856189500 +Panama,PAN,2014,49165773100 +Panama,PAN,2015,52132289700 +Panama,PAN,2016,55187700000 +Papua New Guinea,PNG,1960,230496032.972722 +Papua New Guinea,PNG,1961,244832035.023504 +Papua New Guinea,PNG,1962,261184037.362676 +Papua New Guinea,PNG,1963,275968039.477545 +Papua New Guinea,PNG,1964,305312043.675238 +Papua New Guinea,PNG,1965,344159480.344943 +Papua New Guinea,PNG,1966,390973233.284802 +Papua New Guinea,PNG,1967,441706910.068317 +Papua New Guinea,PNG,1968,485160824.280435 +Papua New Guinea,PNG,1969,551237316.608803 +Papua New Guinea,PNG,1970,645537126.217942 +Papua New Guinea,PNG,1971,717716130.493883 +Papua New Guinea,PNG,1972,858802035.928144 +Papua New Guinea,PNG,1973,1299105240.73285 +Papua New Guinea,PNG,1974,1467346059.99713 +Papua New Guinea,PNG,1975,1356591176.85561 +Papua New Guinea,PNG,1976,1511856584.25833 +Papua New Guinea,PNG,1977,1640763204.44781 +Papua New Guinea,PNG,1978,1947947524.33347 +Papua New Guinea,PNG,1979,2293621944.3664 +Papua New Guinea,PNG,1980,2545983007.89984 +Papua New Guinea,PNG,1981,2498068350.66865 +Papua New Guinea,PNG,1982,2368584969.53284 +Papua New Guinea,PNG,1983,2562492524.81761 +Papua New Guinea,PNG,1984,2552526263.0759 +Papua New Guinea,PNG,1985,2423373088.07358 +Papua New Guinea,PNG,1986,2648033765.69899 +Papua New Guinea,PNG,1987,3143848331.31402 +Papua New Guinea,PNG,1988,3655979702.45646 +Papua New Guinea,PNG,1989,3546460176.99115 +Papua New Guinea,PNG,1990,3219730365 +Papua New Guinea,PNG,1991,3787352286.66667 +Papua New Guinea,PNG,1992,4377984100 +Papua New Guinea,PNG,1993,4974662910 +Papua New Guinea,PNG,1994,5502648500 +Papua New Guinea,PNG,1995,4636113480.00002 +Papua New Guinea,PNG,1996,5155485419.7 +Papua New Guinea,PNG,1997,4936605079.99998 +Papua New Guinea,PNG,1998,3789428160.00001 +Papua New Guinea,PNG,1999,3477060138.33333 +Papua New Guinea,PNG,2000,3521348154.79666 +Papua New Guinea,PNG,2001,3081029665.98233 +Papua New Guinea,PNG,2002,2999542369.42117 +Papua New Guinea,PNG,2003,3536459111.2438 +Papua New Guinea,PNG,2004,3927114465.90565 +Papua New Guinea,PNG,2005,4865971718.29732 +Papua New Guinea,PNG,2006,5527856839.07482 +Papua New Guinea,PNG,2007,6340673793.54534 +Papua New Guinea,PNG,2008,8000074071.33069 +Papua New Guinea,PNG,2009,8105331929.8755 +Papua New Guinea,PNG,2010,9716103408.96554 +Papua New Guinea,PNG,2011,12873049346.2674 +Papua New Guinea,PNG,2012,15391629871.3765 +Papua New Guinea,PNG,2013,15413163674.9224 +Papua New Guinea,PNG,2014,16928680397.4185 +Paraguay,PRY,1965,400129691.269841 +Paraguay,PRY,1966,421700442.063492 +Paraguay,PRY,1967,451524124.603175 +Paraguay,PRY,1968,477012512.698413 +Paraguay,PRY,1969,512728946.031746 +Paraguay,PRY,1970,548758098.412698 +Paraguay,PRY,1971,609047284.920635 +Paraguay,PRY,1972,697291727.777778 +Paraguay,PRY,1973,889357059.52381 +Paraguay,PRY,1974,1199618980.15873 +Paraguay,PRY,1975,1351889403.1746 +Paraguay,PRY,1976,1540820245.2381 +Paraguay,PRY,1977,1912353339.68254 +Paraguay,PRY,1978,2350329157.14286 +Paraguay,PRY,1979,3135123879.36508 +Paraguay,PRY,1980,4094810488.09524 +Paraguay,PRY,1981,5219516810.31746 +Paraguay,PRY,1982,5067450002.20588 +Paraguay,PRY,1983,5237432542.46575 +Paraguay,PRY,1984,4067222369.30652 +Paraguay,PRY,1985,2966234106.19469 +Paraguay,PRY,1986,3439716561.65443 +Paraguay,PRY,1987,3778316380.23952 +Paraguay,PRY,1988,4082625952.73809 +Paraguay,PRY,1989,4599970618.44348 +Paraguay,PRY,1990,5695201563.42495 +Paraguay,PRY,1991,6984367762.90371 +Paraguay,PRY,1992,7157424031.06045 +Paraguay,PRY,1993,7249533620.30614 +Paraguay,PRY,1994,7870982170.98217 +Paraguay,PRY,1995,9062131307.88275 +Paraguay,PRY,1996,9788391732.82899 +Paraguay,PRY,1997,9965225496.5884 +Paraguay,PRY,1998,9024567484.2013 +Paraguay,PRY,1999,8392549702.31511 +Paraguay,PRY,2000,8195993230.74275 +Paraguay,PRY,2001,7662595075.90241 +Paraguay,PRY,2002,6325151760.0669 +Paraguay,PRY,2003,6588103836.34739 +Paraguay,PRY,2004,8033877360.41697 +Paraguay,PRY,2005,8734653809.49561 +Paraguay,PRY,2006,10646157920.3209 +Paraguay,PRY,2007,13794910633.8518 +Paraguay,PRY,2008,18504130752.9922 +Paraguay,PRY,2009,15929902138.1363 +Paraguay,PRY,2010,20030528042.9171 +Paraguay,PRY,2011,25099681460.8943 +Paraguay,PRY,2012,24595319573.7548 +Paraguay,PRY,2013,28965906502.2306 +Paraguay,PRY,2014,30881166852.3116 +Paraguay,PRY,2015,27282581335.7964 +Paraguay,PRY,2016,27440632933.5561 +Peru,PER,1960,2571908062.07692 +Peru,PER,1961,2899654840.36567 +Peru,PER,1962,3286773187.87687 +Peru,PER,1963,3600957771.15299 +Peru,PER,1964,4356913870.23508 +Peru,PER,1965,5166861068.42164 +Peru,PER,1966,6113607728.15672 +Peru,PER,1967,6204253758.57616 +Peru,PER,1968,5736083835.22481 +Peru,PER,1969,6420909789.63824 +Peru,PER,1970,7432223176.77261 +Peru,PER,1971,8289582883.50129 +Peru,PER,1972,9189413409.01292 +Peru,PER,1973,10994381894.7984 +Peru,PER,1974,13858441211.2196 +Peru,PER,1975,16877163792.1284 +Peru,PER,1976,15947709379.6507 +Peru,PER,1977,14620386673.8544 +Peru,PER,1978,12495779622.071 +Peru,PER,1979,15962459447.2168 +Peru,PER,1980,18134029179.6393 +Peru,PER,1981,21649137620.3055 +Peru,PER,1982,21793496819.3379 +Peru,PER,1983,17345624453.6916 +Peru,PER,1984,17599660054.286 +Peru,PER,1985,16548827018.2872 +Peru,PER,1986,15244232957.876 +Peru,PER,1987,20702298396.9717 +Peru,PER,1988,15439408447.2288 +Peru,PER,1989,22499559086.0343 +Peru,PER,1990,26410386669.3609 +Peru,PER,1991,34672122380.7687 +Peru,PER,1992,36139225287.9079 +Peru,PER,1993,35158109999.4973 +Peru,PER,1994,44882079766.8913 +Peru,PER,1995,53312793687.3836 +Peru,PER,1996,55252414130.3019 +Peru,PER,1997,58147522522.5225 +Peru,PER,1998,55501467877.381 +Peru,PER,1999,50187324567.883 +Peru,PER,2000,51744749133.213 +Peru,PER,2001,52030158775.4055 +Peru,PER,2002,54777553515.0809 +Peru,PER,2003,58731030121.8671 +Peru,PER,2004,66768703497.5687 +Peru,PER,2005,76060606060.606 +Peru,PER,2006,88643193061.748 +Peru,PER,2007,102170981144.136 +Peru,PER,2008,120550599815.441 +Peru,PER,2009,120822986521.479 +Peru,PER,2010,147528937028.778 +Peru,PER,2011,171761737046.585 +Peru,PER,2012,192648999090.082 +Peru,PER,2013,201217661645.509 +Peru,PER,2014,201049665375.132 +Peru,PER,2015,189212096470.293 +Peru,PER,2016,192093512185.011 +Philippines,PHL,1960,6684568805.06881 +Philippines,PHL,1961,7256966966.22556 +Philippines,PHL,1962,4399827767.96704 +Philippines,PHL,1963,4875309866.34017 +Philippines,PHL,1964,5271404668.36735 +Philippines,PHL,1965,5784398976.9821 +Philippines,PHL,1966,6371459304.41018 +Philippines,PHL,1967,6809134235.54298 +Philippines,PHL,1968,7591603053.43511 +Philippines,PHL,1969,8408229699.14295 +Philippines,PHL,1970,6687204834.3687 +Philippines,PHL,1971,7408305735.65309 +Philippines,PHL,1972,8017468688.2004 +Philippines,PHL,1973,10082885603.0668 +Philippines,PHL,1974,13781139969.6519 +Philippines,PHL,1975,14893969287.6557 +Philippines,PHL,1976,17097563270.2982 +Philippines,PHL,1977,19648106122.0079 +Philippines,PHL,1978,22706155475.3048 +Philippines,PHL,1979,27502168726.9573 +Philippines,PHL,1980,32450541843.0652 +Philippines,PHL,1981,35646416952.5425 +Philippines,PHL,1982,37140163934.4262 +Philippines,PHL,1983,33212180658.1659 +Philippines,PHL,1984,31408492876.691 +Philippines,PHL,1985,30734335448.9905 +Philippines,PHL,1986,29868339080.8263 +Philippines,PHL,1987,33195933429.6008 +Philippines,PHL,1988,37885440418.6834 +Philippines,PHL,1989,42575183905.5606 +Philippines,PHL,1990,44311593755.7845 +Philippines,PHL,1991,45417561302.2497 +Philippines,PHL,1992,52976344928.9564 +Philippines,PHL,1993,54368083953.1119 +Philippines,PHL,1994,64084460124.4644 +Philippines,PHL,1995,74119987244.5011 +Philippines,PHL,1996,82848140618.0266 +Philippines,PHL,1997,82344260570.6685 +Philippines,PHL,1998,72207025219.4752 +Philippines,PHL,1999,82995147089.9742 +Philippines,PHL,2000,81026297144.2795 +Philippines,PHL,2001,76262072022.215 +Philippines,PHL,2002,81357602950.1818 +Philippines,PHL,2003,83908206456.0645 +Philippines,PHL,2004,91371239764.8818 +Philippines,PHL,2005,103071585462.599 +Philippines,PHL,2006,122210719245.902 +Philippines,PHL,2007,149359920005.894 +Philippines,PHL,2008,174195135053.121 +Philippines,PHL,2009,168334599538.168 +Philippines,PHL,2010,199590774784.581 +Philippines,PHL,2011,224143083706.777 +Philippines,PHL,2012,250092093547.532 +Philippines,PHL,2013,271836123723.678 +Philippines,PHL,2014,284584522898.935 +Philippines,PHL,2015,292774099014.19 +Philippines,PHL,2016,304905406845.908 +Poland,POL,1990,65977749038.438 +Poland,POL,1991,85500935934.9901 +Poland,POL,1992,94337050693.2727 +Poland,POL,1993,96045645026.178 +Poland,POL,1994,110803391516.698 +Poland,POL,1995,142137319587.629 +Poland,POL,1996,159942880456.956 +Poland,POL,1997,159117799530.388 +Poland,POL,1998,174388271853.6 +Poland,POL,1999,169717677900.734 +Poland,POL,2000,171885598582.637 +Poland,POL,2001,190521263343.023 +Poland,POL,2002,198680637254.902 +Poland,POL,2003,217518642324.505 +Poland,POL,2004,255102252843.395 +Poland,POL,2005,306134635593.744 +Poland,POL,2006,344826430298.147 +Poland,POL,2007,429249647594.607 +Poland,POL,2008,533815789473.684 +Poland,POL,2009,439796160379.475 +Poland,POL,2010,479321128909.23 +Poland,POL,2011,528819945375.376 +Poland,POL,2012,500344281382.345 +Poland,POL,2013,524214789307.993 +Poland,POL,2014,545151778520.387 +Poland,POL,2015,477336782066.587 +Poland,POL,2016,469508680416.119 +Portugal,PRT,1960,3193200404.37297 +Portugal,PRT,1961,3417516639.37596 +Portugal,PRT,1962,3668222357.65702 +Portugal,PRT,1963,3905734459.72693 +Portugal,PRT,1964,4235608177.67102 +Portugal,PRT,1965,4687464054.83455 +Portugal,PRT,1966,5135387845.97108 +Portugal,PRT,1967,5740241165.63433 +Portugal,PRT,1968,6354262628.33537 +Portugal,PRT,1969,6969025825.62869 +Portugal,PRT,1970,8109032775.45328 +Portugal,PRT,1971,9202512367.49117 +Portugal,PRT,1972,11240223128.2431 +Portugal,PRT,1973,15092052330.3352 +Portugal,PRT,1974,17514112075.7695 +Portugal,PRT,1975,19349512941.1765 +Portugal,PRT,1976,20334835543.7666 +Portugal,PRT,1977,21441635411.2101 +Portugal,PRT,1978,23489924726.2774 +Portugal,PRT,1979,26625439344.2623 +Portugal,PRT,1980,32899759311.1734 +Portugal,PRT,1981,31980423452.7687 +Portugal,PRT,1982,30530759334.0061 +Portugal,PRT,1983,27242331885.6316 +Portugal,PRT,1984,25220451794.029 +Portugal,PRT,1985,27118476173.6675 +Portugal,PRT,1986,38749715721.7531 +Portugal,PRT,1987,48187667852.5687 +Portugal,PRT,1988,56352797353.7604 +Portugal,PRT,1989,60600056659.0272 +Portugal,PRT,1990,78721607509.4923 +Portugal,PRT,1991,89242382961.0101 +Portugal,PRT,1992,107602689040.689 +Portugal,PRT,1993,95019103603.042 +Portugal,PRT,1994,99698453260.8696 +Portugal,PRT,1995,118133634071.912 +Portugal,PRT,1996,122629812841.175 +Portugal,PRT,1997,117046198970.84 +Portugal,PRT,1998,123981736420.303 +Portugal,PRT,1999,127465545493.288 +Portugal,PRT,2000,118358489957.619 +Portugal,PRT,2001,121545880984.34 +Portugal,PRT,2002,134228697534.35 +Portugal,PRT,2003,164964195259.594 +Portugal,PRT,2004,189187437298.237 +Portugal,PRT,2005,197304513120.259 +Portugal,PRT,2006,208566948939.907 +Portugal,PRT,2007,240169336162.059 +Portugal,PRT,2008,262007590449.685 +Portugal,PRT,2009,243745748819.116 +Portugal,PRT,2010,238303443425.21 +Portugal,PRT,2011,244895101712.451 +Portugal,PRT,2012,216368178659.447 +Portugal,PRT,2013,226073492966.495 +Portugal,PRT,2014,229629822121.601 +Portugal,PRT,2015,199082291239.307 +Portugal,PRT,2016,204564700289.118 +Puerto Rico,PRI,1960,1691900000 +Puerto Rico,PRI,1961,1865100000 +Puerto Rico,PRI,1962,2094400000 +Puerto Rico,PRI,1963,2333600000 +Puerto Rico,PRI,1964,2570500000 +Puerto Rico,PRI,1965,2881500000 +Puerto Rico,PRI,1966,3170500000 +Puerto Rico,PRI,1967,3532700000 +Puerto Rico,PRI,1968,3941700000 +Puerto Rico,PRI,1969,4460700000 +Puerto Rico,PRI,1970,5034700000 +Puerto Rico,PRI,1971,5646800000 +Puerto Rico,PRI,1972,6328900000 +Puerto Rico,PRI,1973,7002400000 +Puerto Rico,PRI,1974,7684800000 +Puerto Rico,PRI,1975,8198300000 +Puerto Rico,PRI,1976,8968600000 +Puerto Rico,PRI,1977,9910900000 +Puerto Rico,PRI,1978,11165000000 +Puerto Rico,PRI,1979,12750000000 +Puerto Rico,PRI,1980,14436100000 +Puerto Rico,PRI,1981,15955700000 +Puerto Rico,PRI,1982,16764200000 +Puerto Rico,PRI,1983,17276600000 +Puerto Rico,PRI,1984,19162600000 +Puerto Rico,PRI,1985,20289200000 +Puerto Rico,PRI,1986,21969400000 +Puerto Rico,PRI,1987,23878000000 +Puerto Rico,PRI,1988,26178400000 +Puerto Rico,PRI,1989,28266800000 +Puerto Rico,PRI,1990,30603919000 +Puerto Rico,PRI,1991,32287031000 +Puerto Rico,PRI,1992,34630430000 +Puerto Rico,PRI,1993,36922456000 +Puerto Rico,PRI,1994,39690630000 +Puerto Rico,PRI,1995,42647331000 +Puerto Rico,PRI,1996,45340835000 +Puerto Rico,PRI,1997,48187039000 +Puerto Rico,PRI,1998,54086409000 +Puerto Rico,PRI,1999,57840954000 +Puerto Rico,PRI,2000,61701810000 +Puerto Rico,PRI,2001,69668635000 +Puerto Rico,PRI,2002,72546194000 +Puerto Rico,PRI,2003,75833996000 +Puerto Rico,PRI,2004,80322313000 +Puerto Rico,PRI,2005,83914521340.5431 +Puerto Rico,PRI,2006,87276164364.6388 +Puerto Rico,PRI,2007,89524131617.1909 +Puerto Rico,PRI,2008,93639316000 +Puerto Rico,PRI,2009,96385638000 +Puerto Rico,PRI,2010,98381268000 +Puerto Rico,PRI,2011,100351670000 +Puerto Rico,PRI,2012,101080738000 +Puerto Rico,PRI,2013,103134778000 +Qatar,QAT,1970,301791301.791302 +Qatar,QAT,1971,387700084.245998 +Qatar,QAT,1972,510259940.720474 +Qatar,QAT,1973,793884368.040437 +Qatar,QAT,1974,2401403227.44085 +Qatar,QAT,1975,2512784033.37828 +Qatar,QAT,1976,3284301332.18953 +Qatar,QAT,1977,3617580171.76055 +Qatar,QAT,1978,4052000412.70087 +Qatar,QAT,1979,5633000318.02401 +Qatar,QAT,1980,7829094613.07082 +Qatar,QAT,1981,8661263763.73626 +Qatar,QAT,1982,7596703214.28571 +Qatar,QAT,1983,6467582307.69231 +Qatar,QAT,1984,6704395824.17583 +Qatar,QAT,1985,6153296456.04396 +Qatar,QAT,1986,5053021950.54945 +Qatar,QAT,1987,5446428681.31868 +Qatar,QAT,1988,6038187032.96703 +Qatar,QAT,1989,6487912087.91209 +Qatar,QAT,1990,7360439423.07692 +Qatar,QAT,1991,6883516483.51648 +Qatar,QAT,1992,7646153983.51648 +Qatar,QAT,1993,7156593653.84615 +Qatar,QAT,1994,7374450769.23077 +Qatar,QAT,1995,8137911978.02198 +Qatar,QAT,1996,9059340384.61539 +Qatar,QAT,1997,11297802115.3846 +Qatar,QAT,1998,10255495027.4725 +Qatar,QAT,1999,12393131868.1319 +Qatar,QAT,2000,17759890109.8901 +Qatar,QAT,2001,17538461538.4615 +Qatar,QAT,2002,19363736263.7363 +Qatar,QAT,2003,23533791208.7912 +Qatar,QAT,2004,31734065934.0659 +Qatar,QAT,2005,44530494505.4945 +Qatar,QAT,2006,60882142857.1428 +Qatar,QAT,2007,79712087912.0879 +Qatar,QAT,2008,115270054945.055 +Qatar,QAT,2009,97798351648.3516 +Qatar,QAT,2010,125122306346.154 +Qatar,QAT,2011,167775274725.275 +Qatar,QAT,2012,186833516483.516 +Qatar,QAT,2013,198727747252.747 +Qatar,QAT,2014,206224725274.725 +Qatar,QAT,2015,164641483516.484 +Qatar,QAT,2016,152468681318.681 +Romania,ROU,1987,38413636363.6364 +Romania,ROU,1988,40809523809.5238 +Romania,ROU,1989,42105263157.8947 +Romania,ROU,1990,38995454545.4545 +Romania,ROU,1991,28998684210.5263 +Romania,ROU,1992,25121666666.6667 +Romania,ROU,1993,26362894736.8421 +Romania,ROU,1994,30074440483.3837 +Romania,ROU,1995,37662075750.123 +Romania,ROU,1996,37182938696.0752 +Romania,ROU,1997,35838588169.6429 +Romania,ROU,1998,41976002703.9207 +Romania,ROU,1999,36183003978.3474 +Romania,ROU,2000,37438527799.5302 +Romania,ROU,2001,40716836998.0386 +Romania,ROU,2002,46174557555.5892 +Romania,ROU,2003,59867801204.8193 +Romania,ROU,2004,76216441462.1442 +Romania,ROU,2005,99697566667.8107 +Romania,ROU,2006,123533036667.853 +Romania,ROU,2007,171536685395.563 +Romania,ROU,2008,208181626900.631 +Romania,ROU,2009,167422949529.4 +Romania,ROU,2010,167998080493.408 +Romania,ROU,2011,185362855081.021 +Romania,ROU,2012,171664638717.49 +Romania,ROU,2013,191549024910.604 +Romania,ROU,2014,199493490982.921 +Romania,ROU,2015,177522705145.168 +Romania,ROU,2016,186690595273.12 +Russian Federation,RUS,1989,506500154001.466 +Russian Federation,RUS,1990,516814258695.568 +Russian Federation,RUS,1991,517962962962.963 +Russian Federation,RUS,1992,460290556900.726 +Russian Federation,RUS,1993,435083713850.837 +Russian Federation,RUS,1994,395077301248.464 +Russian Federation,RUS,1995,395531066563.296 +Russian Federation,RUS,1996,391719993756.828 +Russian Federation,RUS,1997,404926534140.017 +Russian Federation,RUS,1998,270953116950.026 +Russian Federation,RUS,1999,195905767668.562 +Russian Federation,RUS,2000,259708496267.33 +Russian Federation,RUS,2001,306602673980.117 +Russian Federation,RUS,2002,345110438692.185 +Russian Federation,RUS,2003,430347770731.787 +Russian Federation,RUS,2004,591016690742.798 +Russian Federation,RUS,2005,764017107992.391 +Russian Federation,RUS,2006,989930542278.695 +Russian Federation,RUS,2007,1299705247685.76 +Russian Federation,RUS,2008,1660844408499.61 +Russian Federation,RUS,2009,1222643696991.85 +Russian Federation,RUS,2010,1524916112078.87 +Russian Federation,RUS,2011,2031768558635.85 +Russian Federation,RUS,2012,2170143623037.67 +Russian Federation,RUS,2013,2230625004653.55 +Russian Federation,RUS,2014,2063662281005.13 +Russian Federation,RUS,2015,1365865245098.18 +Russian Federation,RUS,2016,1283162348132.89 +Rwanda,RWA,1960,119000024 +Rwanda,RWA,1961,122000016 +Rwanda,RWA,1962,125000008 +Rwanda,RWA,1963,128000000 +Rwanda,RWA,1964,129999994 +Rwanda,RWA,1965,148799980 +Rwanda,RWA,1966,124525702.857143 +Rwanda,RWA,1967,159560018 +Rwanda,RWA,1968,172200018 +Rwanda,RWA,1969,188700037 +Rwanda,RWA,1970,219900006 +Rwanda,RWA,1971,222952578.196381 +Rwanda,RWA,1972,246457838.336681 +Rwanda,RWA,1973,290746157.145921 +Rwanda,RWA,1974,308458423.183854 +Rwanda,RWA,1975,571863295.740122 +Rwanda,RWA,1976,637754162.101094 +Rwanda,RWA,1977,746650558.55469 +Rwanda,RWA,1978,905709147.27019 +Rwanda,RWA,1979,1109346220.52885 +Rwanda,RWA,1980,1254765349.93185 +Rwanda,RWA,1981,1407062607.63214 +Rwanda,RWA,1982,1407242640.23211 +Rwanda,RWA,1983,1479688125.8852 +Rwanda,RWA,1984,1587412957.22263 +Rwanda,RWA,1985,1715625839.17973 +Rwanda,RWA,1986,1944711061.30888 +Rwanda,RWA,1987,2157434025.16467 +Rwanda,RWA,1988,2395493877.51365 +Rwanda,RWA,1989,2405021932.89997 +Rwanda,RWA,1990,2550185618.14774 +Rwanda,RWA,1991,1911600969.76612 +Rwanda,RWA,1992,2029026704.02707 +Rwanda,RWA,1993,1971525998.87685 +Rwanda,RWA,1994,753636370.454546 +Rwanda,RWA,1995,1293535010.94467 +Rwanda,RWA,1996,1382334879.40812 +Rwanda,RWA,1997,1851558301.7002 +Rwanda,RWA,1998,1989343495.21844 +Rwanda,RWA,1999,1817655328.06755 +Rwanda,RWA,2000,1734938264.47371 +Rwanda,RWA,2001,1674685094.01639 +Rwanda,RWA,2002,1677447150.10691 +Rwanda,RWA,2003,1845979298.99285 +Rwanda,RWA,2004,2089188828.797 +Rwanda,RWA,2005,2581465863.87859 +Rwanda,RWA,2006,3152016556.51526 +Rwanda,RWA,2007,3824811913.22869 +Rwanda,RWA,2008,4860576609.21568 +Rwanda,RWA,2009,5379378135.44102 +Rwanda,RWA,2010,5774003744.2708 +Rwanda,RWA,2011,6491683831.5094 +Rwanda,RWA,2012,7315702176.36442 +Rwanda,RWA,2013,7622526429.08839 +Rwanda,RWA,2014,8016288347.03577 +Rwanda,RWA,2015,8261034257.63248 +Rwanda,RWA,2016,8376048904.58291 +Samoa,WSM,1982,121221651.619316 +Samoa,WSM,1983,111862823.574979 +Samoa,WSM,1984,109200934.328518 +Samoa,WSM,1985,95572172.9835657 +Samoa,WSM,1986,100947848.64478 +Samoa,WSM,1987,111713922.141578 +Samoa,WSM,1988,133016065.416065 +Samoa,WSM,1989,122888609.715243 +Samoa,WSM,1990,125766269.755358 +Samoa,WSM,1991,125597205.422315 +Samoa,WSM,1992,132303041.36253 +Samoa,WSM,1993,133122897.196262 +Samoa,WSM,1994,221098106.508876 +Samoa,WSM,1995,224865731.381903 +Samoa,WSM,1996,249908970.658971 +Samoa,WSM,1997,285475591.89651 +Samoa,WSM,1998,269481523.200465 +Samoa,WSM,1999,258833766.580017 +Samoa,WSM,2000,269019710.327456 +Samoa,WSM,2001,273088357.1637 +Samoa,WSM,2002,288078881.433056 +Samoa,WSM,2003,338838639.378435 +Samoa,WSM,2004,420320176.359437 +Samoa,WSM,2005,462644663.06818 +Samoa,WSM,2006,508505414.405727 +Samoa,WSM,2007,550967295.278082 +Samoa,WSM,2008,644143266.155792 +Samoa,WSM,2009,560967765.481746 +Samoa,WSM,2010,643056627.690783 +Samoa,WSM,2011,739777274.127838 +Samoa,WSM,2012,801152293.280623 +Samoa,WSM,2013,804816205.686687 +Samoa,WSM,2014,803574789.064072 +Samoa,WSM,2015,803976511.515065 +Samoa,WSM,2016,785916937.388673 +San Marino,SMR,1999,853373879.731819 +San Marino,SMR,2000,773907642.414748 +San Marino,SMR,2001,815205233.062791 +San Marino,SMR,2002,879957209.923907 +San Marino,SMR,2003,1122981525.35502 +San Marino,SMR,2004,1317357834.61634 +San Marino,SMR,2005,1375416604.48689 +San Marino,SMR,2006,1469000145.311 +San Marino,SMR,2007,1687567364.11695 +San Marino,SMR,2008,1899879955.48332 +Sao Tome and Principe,STP,2001,72230284.4325876 +Sao Tome and Principe,STP,2002,80531992.1217606 +Sao Tome and Principe,STP,2003,96343906.4298042 +Sao Tome and Principe,STP,2004,105360801.941831 +Sao Tome and Principe,STP,2005,126194166.230985 +Sao Tome and Principe,STP,2006,134441116.924998 +Sao Tome and Principe,STP,2007,145827429.572302 +Sao Tome and Principe,STP,2008,188021168.8418 +Sao Tome and Principe,STP,2009,187821029.033169 +Sao Tome and Principe,STP,2010,197454053.145088 +Sao Tome and Principe,STP,2011,233213522.645348 +Sao Tome and Principe,STP,2012,252560557.083061 +Sao Tome and Principe,STP,2013,302925489.683914 +Sao Tome and Principe,STP,2014,348463457.918686 +Sao Tome and Principe,STP,2015,317696178.684618 +Sao Tome and Principe,STP,2016,351054248.477335 +Saudi Arabia,SAU,1968,4187777711.11111 +Saudi Arabia,SAU,1969,4485777644.44444 +Saudi Arabia,SAU,1970,5377333333.33333 +Saudi Arabia,SAU,1971,7184853347.5974 +Saudi Arabia,SAU,1972,9664157498.5524 +Saudi Arabia,SAU,1973,14947391140.1284 +Saudi Arabia,SAU,1974,45412957746.4789 +Saudi Arabia,SAU,1975,46773368205.5947 +Saudi Arabia,SAU,1976,64005665722.3796 +Saudi Arabia,SAU,1977,74188249978.724 +Saudi Arabia,SAU,1978,80265619484.6452 +Saudi Arabia,SAU,1979,111859676267.555 +Saudi Arabia,SAU,1980,164541738058.737 +Saudi Arabia,SAU,1981,184291796008.869 +Saudi Arabia,SAU,1982,153239017560.236 +Saudi Arabia,SAU,1983,129171635311.143 +Saudi Arabia,SAU,1984,119624858115.778 +Saudi Arabia,SAU,1985,103897846493.65 +Saudi Arabia,SAU,1986,86961922765.3254 +Saudi Arabia,SAU,1987,85695861148.1976 +Saudi Arabia,SAU,1988,88256074766.3551 +Saudi Arabia,SAU,1989,95344459279.0387 +Saudi Arabia,SAU,1990,117630271802.48 +Saudi Arabia,SAU,1991,132223268484.752 +Saudi Arabia,SAU,1992,137087876662.752 +Saudi Arabia,SAU,1993,132967901424.382 +Saudi Arabia,SAU,1994,135174886501.234 +Saudi Arabia,SAU,1995,143343036351.096 +Saudi Arabia,SAU,1996,158662398750.805 +Saudi Arabia,SAU,1997,165963557420.458 +Saudi Arabia,SAU,1998,146775498092.612 +Saudi Arabia,SAU,1999,161716960000 +Saudi Arabia,SAU,2000,189514926222.667 +Saudi Arabia,SAU,2001,184137469724.863 +Saudi Arabia,SAU,2002,189605920240.516 +Saudi Arabia,SAU,2003,215807655244.424 +Saudi Arabia,SAU,2004,258742133333.333 +Saudi Arabia,SAU,2005,328459608764.111 +Saudi Arabia,SAU,2006,376900133511.348 +Saudi Arabia,SAU,2007,415964509673.115 +Saudi Arabia,SAU,2008,519796800000 +Saudi Arabia,SAU,2009,429097866666.667 +Saudi Arabia,SAU,2010,528207200000 +Saudi Arabia,SAU,2011,671238840108.229 +Saudi Arabia,SAU,2012,735974843348.664 +Saudi Arabia,SAU,2013,746647127407.619 +Saudi Arabia,SAU,2014,756350347320.381 +Saudi Arabia,SAU,2015,654269902888.715 +Saudi Arabia,SAU,2016,646438380568.715 +Senegal,SEN,1960,792824707.345294 +Senegal,SEN,1961,836493109.152284 +Senegal,SEN,1962,857425916.243935 +Senegal,SEN,1963,886387156.125059 +Senegal,SEN,1964,939145851.154484 +Senegal,SEN,1965,955834893.28571 +Senegal,SEN,1966,984942988.068955 +Senegal,SEN,1967,984605369.32995 +Senegal,SEN,1968,1034293645.25718 +Senegal,SEN,1969,983621024.109038 +Senegal,SEN,1970,1024832915.04328 +Senegal,SEN,1971,1058120427.15534 +Senegal,SEN,1972,1280328245.00174 +Senegal,SEN,1973,1471913473.60034 +Senegal,SEN,1974,1658273721.28587 +Senegal,SEN,1975,2235746644.74234 +Senegal,SEN,1976,2266860655.65881 +Senegal,SEN,1977,2320786490.70314 +Senegal,SEN,1978,2591178368.03735 +Senegal,SEN,1979,3226678628.31043 +Senegal,SEN,1980,3503282102.95741 +Senegal,SEN,1981,3176771103.46059 +Senegal,SEN,1982,3109677455.66655 +Senegal,SEN,1983,2774199193.31559 +Senegal,SEN,1984,2705535756.06004 +Senegal,SEN,1985,2962199835.95355 +Senegal,SEN,1986,4189860416.18118 +Senegal,SEN,1987,5040708115.08482 +Senegal,SEN,1988,4985153202.5374 +Senegal,SEN,1989,4913065110.53161 +Senegal,SEN,1990,5716644272.04692 +Senegal,SEN,1991,5617236032.86556 +Senegal,SEN,1992,6004885321.34354 +Senegal,SEN,1993,5678827998.8247 +Senegal,SEN,1994,3877196914.93966 +Senegal,SEN,1995,4878719133.22771 +Senegal,SEN,1996,5065830414.04947 +Senegal,SEN,1997,4672503920.19866 +Senegal,SEN,1998,5030344074.0413 +Senegal,SEN,1999,5144045359.98185 +Senegal,SEN,2000,4679604753.55711 +Senegal,SEN,2001,4877602059.50983 +Senegal,SEN,2002,5333862371.27113 +Senegal,SEN,2003,6858952880.10003 +Senegal,SEN,2004,8031344381.09898 +Senegal,SEN,2005,8707015771.00113 +Senegal,SEN,2006,9358710935.43366 +Senegal,SEN,2007,11284603070.5653 +Senegal,SEN,2008,13428461873.5568 +Senegal,SEN,2009,12809044977.5918 +Senegal,SEN,2010,12937300245.1506 +Senegal,SEN,2011,14368348214.2024 +Senegal,SEN,2012,14202389641.9262 +Senegal,SEN,2013,14810978041.4667 +Senegal,SEN,2014,15308965398.5779 +Senegal,SEN,2015,13609978076.4224 +Senegal,SEN,2016,14765462631.3216 +Serbia,SRB,1995,16750000000 +Serbia,SRB,1996,20948677839.851 +Serbia,SRB,1997,24147996549.5662 +Serbia,SRB,1998,18284194680.3844 +Serbia,SRB,1999,18409364146.9794 +Serbia,SRB,2000,6540247190.33529 +Serbia,SRB,2001,12267175481.2542 +Serbia,SRB,2002,16116843146.4806 +Serbia,SRB,2003,21188704081.2428 +Serbia,SRB,2004,24861483280.6339 +Serbia,SRB,2005,26252007830.4639 +Serbia,SRB,2006,30607991862.4843 +Serbia,SRB,2007,40289556656.1455 +Serbia,SRB,2008,49259526052.7426 +Serbia,SRB,2009,42616653299.9115 +Serbia,SRB,2010,39460357730.5224 +Serbia,SRB,2011,46466728666.6103 +Serbia,SRB,2012,40742313861.1374 +Serbia,SRB,2013,45519650911.4138 +Serbia,SRB,2014,44210806365.6817 +Serbia,SRB,2015,37160332465.1645 +Serbia,SRB,2016,37745114708.3113 +Seychelles,SYC,1960,12012025.2477875 +Seychelles,SYC,1961,11592024.3649978 +Seychelles,SYC,1962,12642026.5719722 +Seychelles,SYC,1963,13923029.264481 +Seychelles,SYC,1964,15393032.3542452 +Seychelles,SYC,1965,15603032.7956401 +Seychelles,SYC,1966,16443034.5612196 +Seychelles,SYC,1967,16632032.8138977 +Seychelles,SYC,1968,16074027.3495878 +Seychelles,SYC,1969,16452027.9927472 +Seychelles,SYC,1970,18432031.3616773 +Seychelles,SYC,1971,21965951.7214804 +Seychelles,SYC,1972,30645121.0127585 +Seychelles,SYC,1973,36896278.223337 +Seychelles,SYC,1974,43134498.6932177 +Seychelles,SYC,1975,47803145.9560303 +Seychelles,SYC,1976,49278979.547035 +Seychelles,SYC,1977,64526398.6582547 +Seychelles,SYC,1978,85552369.9145942 +Seychelles,SYC,1979,127261099.24396 +Seychelles,SYC,1980,147357222.779802 +Seychelles,SYC,1981,154902869.02139 +Seychelles,SYC,1982,147912069.766502 +Seychelles,SYC,1983,146712850.50997 +Seychelles,SYC,1984,151313241.983564 +Seychelles,SYC,1985,168887539.130029 +Seychelles,SYC,1986,207850623.638215 +Seychelles,SYC,1987,249267039.781191 +Seychelles,SYC,1988,283828769.031683 +Seychelles,SYC,1989,304832867.395046 +Seychelles,SYC,1990,368584758.942457 +Seychelles,SYC,1991,374359556.084926 +Seychelles,SYC,1992,433667193.814795 +Seychelles,SYC,1993,473916819.453826 +Seychelles,SYC,1994,486451204.557142 +Seychelles,SYC,1995,508221508.221508 +Seychelles,SYC,1996,503068472.20266 +Seychelles,SYC,1997,562958836.519905 +Seychelles,SYC,1998,608369282.225727 +Seychelles,SYC,1999,622985493.682733 +Seychelles,SYC,2000,614879764.780006 +Seychelles,SYC,2001,622262057.191635 +Seychelles,SYC,2002,697518248.175182 +Seychelles,SYC,2003,705704816.042365 +Seychelles,SYC,2004,839319927.272727 +Seychelles,SYC,2005,919103254.545455 +Seychelles,SYC,2006,1016418229.25159 +Seychelles,SYC,2007,1033561654.0568 +Seychelles,SYC,2008,967199593.960157 +Seychelles,SYC,2009,847397850.094417 +Seychelles,SYC,2010,969936525.298729 +Seychelles,SYC,2011,1065826669.89742 +Seychelles,SYC,2012,1134267367.19206 +Seychelles,SYC,2013,1411061260.70839 +Seychelles,SYC,2014,1422530791.5588 +Seychelles,SYC,2015,1437722206.38754 +Seychelles,SYC,2016,1427323889.0961 +Sierra Leone,SLE,1960,322009471.57364 +Sierra Leone,SLE,1961,327834680.563822 +Sierra Leone,SLE,1962,342721579.824661 +Sierra Leone,SLE,1963,348546952.141512 +Sierra Leone,SLE,1964,371848114.755577 +Sierra Leone,SLE,1965,359379856.248057 +Sierra Leone,SLE,1966,375479849.80806 +Sierra Leone,SLE,1967,348795303.000385 +Sierra Leone,SLE,1968,329860091.944037 +Sierra Leone,SLE,1969,408690163.476065 +Sierra Leone,SLE,1970,434410373.76415 +Sierra Leone,SLE,1971,419549425.077086 +Sierra Leone,SLE,1972,465381089.98454 +Sierra Leone,SLE,1973,575230234.387058 +Sierra Leone,SLE,1974,648590642.939888 +Sierra Leone,SLE,1975,679335901.117451 +Sierra Leone,SLE,1976,594895672.333848 +Sierra Leone,SLE,1977,691777758.395115 +Sierra Leone,SLE,1978,960728338.93643 +Sierra Leone,SLE,1979,1109374722.08294 +Sierra Leone,SLE,1980,1100685844.92284 +Sierra Leone,SLE,1981,1114830471.91787 +Sierra Leone,SLE,1982,1295361885.92419 +Sierra Leone,SLE,1983,995104305.347074 +Sierra Leone,SLE,1984,1087471861.98928 +Sierra Leone,SLE,1985,856890498.625834 +Sierra Leone,SLE,1986,490181456.62441 +Sierra Leone,SLE,1987,701307602.28443 +Sierra Leone,SLE,1988,1055083945.37738 +Sierra Leone,SLE,1989,932974411.917142 +Sierra Leone,SLE,1990,649644826.800447 +Sierra Leone,SLE,1991,779981458.921489 +Sierra Leone,SLE,1992,679997997.597117 +Sierra Leone,SLE,1993,768812334.801762 +Sierra Leone,SLE,1994,911915970.683484 +Sierra Leone,SLE,1995,870758739.40678 +Sierra Leone,SLE,1996,941742152.709895 +Sierra Leone,SLE,1997,850218033.622007 +Sierra Leone,SLE,1998,672375927.347148 +Sierra Leone,SLE,1999,669384768.87263 +Sierra Leone,SLE,2000,635874002.198748 +Sierra Leone,SLE,2001,1079478387.83576 +Sierra Leone,SLE,2002,1239004287.75607 +Sierra Leone,SLE,2003,1371442565.69701 +Sierra Leone,SLE,2004,1431208677.30352 +Sierra Leone,SLE,2005,1627854494.80246 +Sierra Leone,SLE,2006,1885112201.85278 +Sierra Leone,SLE,2007,2158496872.85796 +Sierra Leone,SLE,2008,2505458705.03338 +Sierra Leone,SLE,2009,2489985963.16808 +Sierra Leone,SLE,2010,2616610911.07222 +Sierra Leone,SLE,2011,2942546781.04548 +Sierra Leone,SLE,2012,3801862611.36414 +Sierra Leone,SLE,2013,4920343194.99339 +Sierra Leone,SLE,2014,5015157815.73406 +Sierra Leone,SLE,2015,4251779857.0134 +Sierra Leone,SLE,2016,3668876080.33846 +Singapore,SGP,1960,704462302.365086 +Singapore,SGP,1961,764308114.464916 +Singapore,SGP,1962,825885273.748857 +Singapore,SGP,1963,917222004.442702 +Singapore,SGP,1964,893734483.209199 +Singapore,SGP,1965,974193126.878348 +Singapore,SGP,1966,1095910100.61414 +Singapore,SGP,1967,1237423232.7192 +Singapore,SGP,1968,1425029400.2352 +Singapore,SGP,1969,1659055272.44218 +Singapore,SGP,1970,1919508689.40285 +Singapore,SGP,1971,2262544100.3528 +Singapore,SGP,1972,2719900350.73917 +Singapore,SGP,1973,3693760000 +Singapore,SGP,1974,5216773825.99496 +Singapore,SGP,1975,5633386679.7981 +Singapore,SGP,1976,6326445409.69089 +Singapore,SGP,1977,6617532782.90432 +Singapore,SGP,1978,7515823563.17127 +Singapore,SGP,1979,9294635004.39754 +Singapore,SGP,1980,11893405683.8039 +Singapore,SGP,1981,14171819540.4446 +Singapore,SGP,1982,16078856439.627 +Singapore,SGP,1983,17775280373.8318 +Singapore,SGP,1984,19735920492.1912 +Singapore,SGP,1985,19138296376.1661 +Singapore,SGP,1986,18569292304.8952 +Singapore,SGP,1987,20897630201.1573 +Singapore,SGP,1988,25337226970.5603 +Singapore,SGP,1989,30423573842.1785 +Singapore,SGP,1990,36152027893.1446 +Singapore,SGP,1991,45474442836.4689 +Singapore,SGP,1992,52156414978.5144 +Singapore,SGP,1993,60644572348.0629 +Singapore,SGP,1994,73777792326.8299 +Singapore,SGP,1995,87890009877.24 +Singapore,SGP,1996,96403758865.2482 +Singapore,SGP,1997,100163995150.862 +Singapore,SGP,1998,85707636233.2696 +Singapore,SGP,1999,86283126843.6578 +Singapore,SGP,2000,95833932714.6172 +Singapore,SGP,2001,89286208628.6767 +Singapore,SGP,2002,91941192896.2359 +Singapore,SGP,2003,97001377568.5914 +Singapore,SGP,2004,114188557567.152 +Singapore,SGP,2005,127417688055.756 +Singapore,SGP,2006,147797218201.271 +Singapore,SGP,2007,179981288567.447 +Singapore,SGP,2008,192225881687.752 +Singapore,SGP,2009,192408387762.118 +Singapore,SGP,2010,236421782178.218 +Singapore,SGP,2011,275599459373.509 +Singapore,SGP,2012,289162118908.538 +Singapore,SGP,2013,302510668904.339 +Singapore,SGP,2014,308142766948.149 +Singapore,SGP,2015,296840704102.415 +Singapore,SGP,2016,296965712342.019 +Slovak Republic,SVK,1990,12694544692.7374 +Slovak Republic,SVK,1991,14213045493.8806 +Slovak Republic,SVK,1992,15431288006.2104 +Slovak Republic,SVK,1993,16452201100.9604 +Slovak Republic,SVK,1994,20079363625.5784 +Slovak Republic,SVK,1995,25733043137.2549 +Slovak Republic,SVK,1996,27821913814.9556 +Slovak Republic,SVK,1997,27660149541.1805 +Slovak Republic,SVK,1998,29828899205.7277 +Slovak Republic,SVK,1999,30415095887.492 +Slovak Republic,SVK,2000,29114875621.8905 +Slovak Republic,SVK,2001,30703017449.6644 +Slovak Republic,SVK,2002,35083608130.9994 +Slovak Republic,SVK,2003,46731767494.3567 +Slovak Republic,SVK,2004,57240535137.8197 +Slovak Republic,SVK,2005,62697540106.9519 +Slovak Republic,SVK,2006,70596729394.0534 +Slovak Republic,SVK,2007,86304245825.349 +Slovak Republic,SVK,2008,100324627215.468 +Slovak Republic,SVK,2009,88945625173.6593 +Slovak Republic,SVK,2010,89501012915.7314 +Slovak Republic,SVK,2011,98181259740.0919 +Slovak Republic,SVK,2012,93413992955.8972 +Slovak Republic,SVK,2013,98478349315.3252 +Slovak Republic,SVK,2014,100760596988.198 +Slovak Republic,SVK,2015,87267593788.1986 +Slovak Republic,SVK,2016,89551834322.5767 +Slovenia,SVN,1995,21273055398.3017 +Slovenia,SVN,1996,21480023016.9972 +Slovenia,SVN,1997,20749140606.2425 +Slovenia,SVN,1998,22125435372.187 +Slovenia,SVN,1999,22689994990.1121 +Slovenia,SVN,2000,20342201356.0052 +Slovenia,SVN,2001,20875387068.1145 +Slovenia,SVN,2002,23563576758.1047 +Slovenia,SVN,2003,29697448108.2957 +Slovenia,SVN,2004,34470227453.9113 +Slovenia,SVN,2005,36346974008.2079 +Slovenia,SVN,2006,39587732028.6037 +Slovenia,SVN,2007,48114688201.4782 +Slovenia,SVN,2008,55589849128.4605 +Slovenia,SVN,2009,50244793831.6199 +Slovenia,SVN,2010,48013606745.4803 +Slovenia,SVN,2011,51290792018.1074 +Slovenia,SVN,2012,46258247574.7506 +Slovenia,SVN,2013,47688566993.1107 +Slovenia,SVN,2014,49530147015.8794 +Slovenia,SVN,2015,42776716631.0769 +Slovenia,SVN,2016,43990635176.0523 +Solomon Islands,SLB,1967,25203524.0325638 +Solomon Islands,SLB,1968,28084252.7582748 +Solomon Islands,SLB,1969,28606411.398041 +Solomon Islands,SLB,1971,50056882.8213879 +Solomon Islands,SLB,1972,40606712.050639 +Solomon Islands,SLB,1973,55272108.8435374 +Solomon Islands,SLB,1974,84539332.282562 +Solomon Islands,SLB,1975,74617096.4785967 +Solomon Islands,SLB,1976,83099107.9066357 +Solomon Islands,SLB,1977,93147039.2548237 +Solomon Islands,SLB,1978,111022089.96223 +Solomon Islands,SLB,1979,151270207.852194 +Solomon Islands,SLB,1980,168715353.097132 +Solomon Islands,SLB,1981,187313261.319237 +Solomon Islands,SLB,1982,188446092.06055 +Solomon Islands,SLB,1983,180219397.527425 +Solomon Islands,SLB,1984,252806783.386983 +Solomon Islands,SLB,1985,232306861.156132 +Solomon Islands,SLB,1986,210737869.652598 +Solomon Islands,SLB,1987,238606299.605651 +Solomon Islands,SLB,1988,310684273.709484 +Solomon Islands,SLB,1989,332286760.858189 +Solomon Islands,SLB,1990,302515026.890225 +Solomon Islands,SLB,1991,320355090.61441 +Solomon Islands,SLB,1992,378778047.197842 +Solomon Islands,SLB,1993,410923236.189102 +Solomon Islands,SLB,1994,464756638.512487 +Solomon Islands,SLB,1995,519334096.714525 +Solomon Islands,SLB,1996,565163750.56079 +Solomon Islands,SLB,1997,567919502.811483 +Solomon Islands,SLB,1998,471177008.057148 +Solomon Islands,SLB,1999,482214092.308964 +Solomon Islands,SLB,2000,435103853.485036 +Solomon Islands,SLB,2001,400463452.065176 +Solomon Islands,SLB,2002,341661643.551446 +Solomon Islands,SLB,2003,332738245.913215 +Solomon Islands,SLB,2004,375111894.932329 +Solomon Islands,SLB,2005,413909879.281265 +Solomon Islands,SLB,2006,456705433.996978 +Solomon Islands,SLB,2007,516074228.959749 +Solomon Islands,SLB,2008,608293860.271816 +Solomon Islands,SLB,2009,597765363.128492 +Solomon Islands,SLB,2010,671585343.170686 +Solomon Islands,SLB,2011,886498370.696086 +Solomon Islands,SLB,2012,1025125081.57494 +Solomon Islands,SLB,2013,1059695156.18795 +Solomon Islands,SLB,2014,1156563122.85602 +Solomon Islands,SLB,2015,1129164718.81436 +Solomon Islands,SLB,2016,1202125000 +Somalia,SOM,2013,5352000000 +Somalia,SOM,2014,5647000000 +Somalia,SOM,2015,5925000000 +Somalia,SOM,2016,6217000000 +South Africa,ZAF,1960,7575248495.0301 +South Africa,ZAF,1961,7972840543.18914 +South Africa,ZAF,1962,8497830043.39913 +South Africa,ZAF,1963,9423211535.76928 +South Africa,ZAF,1964,10373792524.1495 +South Africa,ZAF,1965,11334173316.5337 +South Africa,ZAF,1966,12354752904.9419 +South Africa,ZAF,1967,13777124457.5108 +South Africa,ZAF,1968,14894302113.9577 +South Africa,ZAF,1969,16780064398.712 +South Africa,ZAF,1970,18418031639.3672 +South Africa,ZAF,1971,20334172259.5078 +South Africa,ZAF,1972,21358137114.6091 +South Africa,ZAF,1973,29293948126.8012 +South Africa,ZAF,1974,36806475349.5217 +South Africa,ZAF,1975,38114942528.7356 +South Africa,ZAF,1976,36601885924.563 +South Africa,ZAF,1977,40649724011.0396 +South Africa,ZAF,1978,46737580496.7801 +South Africa,ZAF,1979,57647268408.5511 +South Africa,ZAF,1980,82984078068.8238 +South Africa,ZAF,1981,89629496832.7955 +South Africa,ZAF,1982,82696902010.2943 +South Africa,ZAF,1983,88786580362.8407 +South Africa,ZAF,1984,87880468268.6383 +South Africa,ZAF,1985,69208451592.5576 +South Africa,ZAF,1986,82107924006.1732 +South Africa,ZAF,1987,107414974090.178 +South Africa,ZAF,1988,118331510445.149 +South Africa,ZAF,1989,128902675070.723 +South Africa,ZAF,1990,115553279480.54 +South Africa,ZAF,1991,123943432441.241 +South Africa,ZAF,1992,134545231416.55 +South Africa,ZAF,1993,134309759157.817 +South Africa,ZAF,1994,139752450152.078 +South Africa,ZAF,1995,155460285076.232 +South Africa,ZAF,1996,147607982694.857 +South Africa,ZAF,1997,152586154513.889 +South Africa,ZAF,1998,137774361015.14 +South Africa,ZAF,1999,136631966609.379 +South Africa,ZAF,2000,136361854808.496 +South Africa,ZAF,2001,121516070204.783 +South Africa,ZAF,2002,115482368343.658 +South Africa,ZAF,2003,175256916996.047 +South Africa,ZAF,2004,228593566165.088 +South Africa,ZAF,2005,257772710832.953 +South Africa,ZAF,2006,271638484826.109 +South Africa,ZAF,2007,299415505152.298 +South Africa,ZAF,2008,286769839732.726 +South Africa,ZAF,2009,295936485832.635 +South Africa,ZAF,2010,375349396273.835 +South Africa,ZAF,2011,416418862155.872 +South Africa,ZAF,2012,396327771010.962 +South Africa,ZAF,2013,366623856821.783 +South Africa,ZAF,2014,350850571747.123 +South Africa,ZAF,2015,317406594612.388 +South Africa,ZAF,2016,294840648284.114 +South Sudan,SSD,2008,15550136278.8696 +South Sudan,SSD,2009,12231362022.6859 +South Sudan,SSD,2010,15727363443.0995 +South Sudan,SSD,2011,17826697892.2717 +South Sudan,SSD,2012,10368813559.322 +South Sudan,SSD,2013,13257635694.9153 +South Sudan,SSD,2014,13282084033.8983 +South Sudan,SSD,2015,9015221096.24474 +Spain,ESP,1960,12072126075.397 +Spain,ESP,1961,13834300571.4849 +Spain,ESP,1962,16138545209.246 +Spain,ESP,1963,19074913947.7196 +Spain,ESP,1964,21343844643.7341 +Spain,ESP,1965,24756958694.9238 +Spain,ESP,1966,28721062242.1634 +Spain,ESP,1967,31647119228.1982 +Spain,ESP,1968,31475548481.4095 +Spain,ESP,1969,36038711599.541 +Spain,ESP,1970,40881655098.6451 +Spain,ESP,1971,46492797365.2695 +Spain,ESP,1972,58971806626.9739 +Spain,ESP,1973,78425934894.3461 +Spain,ESP,1974,97009800115.3735 +Spain,ESP,1975,114465300289.855 +Spain,ESP,1976,118185307386.222 +Spain,ESP,1977,132089531434.83 +Spain,ESP,1978,160163483072.917 +Spain,ESP,1979,214019077342.588 +Spain,ESP,1980,232134606637.271 +Spain,ESP,1981,202257045774.013 +Spain,ESP,1982,195464408602.151 +Spain,ESP,1983,170486866357.309 +Spain,ESP,1984,171635463361.623 +Spain,ESP,1985,180302412230.92 +Spain,ESP,1986,250638463466.793 +Spain,ESP,1987,317882187036.787 +Spain,ESP,1988,375138723325.239 +Spain,ESP,1989,413630538018.271 +Spain,ESP,1990,535101248775.71 +Spain,ESP,1991,575598537069.656 +Spain,ESP,1992,629202392003.901 +Spain,ESP,1993,523649481762.322 +Spain,ESP,1994,529121577319.588 +Spain,ESP,1995,612939685081.398 +Spain,ESP,1996,640998292394.588 +Spain,ESP,1997,588692045454.545 +Spain,ESP,1998,617041986858.225 +Spain,ESP,1999,633194118900.49 +Spain,ESP,2000,595402616546.895 +Spain,ESP,2001,625975838926.175 +Spain,ESP,2002,705145868624.13 +Spain,ESP,2003,906853273137.698 +Spain,ESP,2004,1069555500372.49 +Spain,ESP,2005,1157276458151.97 +Spain,ESP,2006,1264551499184.54 +Spain,ESP,2007,1479341637010.68 +Spain,ESP,2008,1634989014208.29 +Spain,ESP,2009,1499074742984.16 +Spain,ESP,2010,1431587612302.26 +Spain,ESP,2011,1488017213511.01 +Spain,ESP,2012,1335945712662.3 +Spain,ESP,2013,1361775869855.87 +Spain,ESP,2014,1375856057716.82 +Spain,ESP,2015,1192955480686.43 +Spain,ESP,2016,1232088185030.31 +Sri Lanka,LKA,1960,1409873949.57983 +Sri Lanka,LKA,1961,1444327731.09244 +Sri Lanka,LKA,1962,1434156378.60082 +Sri Lanka,LKA,1963,1240672268.90756 +Sri Lanka,LKA,1964,1309747899.15966 +Sri Lanka,LKA,1965,1698319327.73109 +Sri Lanka,LKA,1966,1751470588.23529 +Sri Lanka,LKA,1967,1859465020.57613 +Sri Lanka,LKA,1968,1801344537.81513 +Sri Lanka,LKA,1969,1965546218.48739 +Sri Lanka,LKA,1970,2296470588.23529 +Sri Lanka,LKA,1971,2369308600.33727 +Sri Lanka,LKA,1972,2553936348.40871 +Sri Lanka,LKA,1973,2875625000 +Sri Lanka,LKA,1974,3574586466.16541 +Sri Lanka,LKA,1975,3791298145.50642 +Sri Lanka,LKA,1976,3591319857.31272 +Sri Lanka,LKA,1977,4104509582.86359 +Sri Lanka,LKA,1978,2733183856.50224 +Sri Lanka,LKA,1979,3364611432.24149 +Sri Lanka,LKA,1980,4024621899.57653 +Sri Lanka,LKA,1981,4415844155.84416 +Sri Lanka,LKA,1982,4768765016.81884 +Sri Lanka,LKA,1983,5167913302.16745 +Sri Lanka,LKA,1984,6043474842.76729 +Sri Lanka,LKA,1985,5978460972.01767 +Sri Lanka,LKA,1986,6405210563.88294 +Sri Lanka,LKA,1987,6682167119.56522 +Sri Lanka,LKA,1988,6978371581.26375 +Sri Lanka,LKA,1989,6987267683.77254 +Sri Lanka,LKA,1990,8032551173.24014 +Sri Lanka,LKA,1991,9000362581.58086 +Sri Lanka,LKA,1992,9703011635.86585 +Sri Lanka,LKA,1993,10338679635.7616 +Sri Lanka,LKA,1994,11717604208.8223 +Sri Lanka,LKA,1995,13029697560.9756 +Sri Lanka,LKA,1996,13897738375.2488 +Sri Lanka,LKA,1997,15091913883.7091 +Sri Lanka,LKA,1998,15794972847.1683 +Sri Lanka,LKA,1999,15656327859.5696 +Sri Lanka,LKA,2000,16330814179.9766 +Sri Lanka,LKA,2001,15746229581.5619 +Sri Lanka,LKA,2002,16536535647.0834 +Sri Lanka,LKA,2003,18881765437.2151 +Sri Lanka,LKA,2004,20662525941.2986 +Sri Lanka,LKA,2005,24406252456.5141 +Sri Lanka,LKA,2006,28279814924.5918 +Sri Lanka,LKA,2007,32350248410.8216 +Sri Lanka,LKA,2008,40713812309.7316 +Sri Lanka,LKA,2009,42066217871.5349 +Sri Lanka,LKA,2010,56725745039.336 +Sri Lanka,LKA,2011,65292741296.5382 +Sri Lanka,LKA,2012,68434399083.41 +Sri Lanka,LKA,2013,74317806754.5267 +Sri Lanka,LKA,2014,79356456990.2891 +Sri Lanka,LKA,2015,80611989527.2158 +Sri Lanka,LKA,2016,81321876307.2556 +St. Kitts and Nevis,KNA,1960,12366563.6119699 +St. Kitts and Nevis,KNA,1961,12483229.3064224 +St. Kitts and Nevis,KNA,1962,12541562.1536487 +St. Kitts and Nevis,KNA,1963,12833226.3897801 +St. Kitts and Nevis,KNA,1964,13416554.8620428 +St. Kitts and Nevis,KNA,1965,13593932.3220537 +St. Kitts and Nevis,KNA,1966,14469078.1796966 +St. Kitts and Nevis,KNA,1967,16742338.2519864 +St. Kitts and Nevis,KNA,1968,14600000 +St. Kitts and Nevis,KNA,1969,15850000 +St. Kitts and Nevis,KNA,1970,16300000 +St. Kitts and Nevis,KNA,1971,19624746.4503043 +St. Kitts and Nevis,KNA,1972,22944849.1155047 +St. Kitts and Nevis,KNA,1973,24196018.3767228 +St. Kitts and Nevis,KNA,1974,31514856.3078422 +St. Kitts and Nevis,KNA,1975,33364055.2995392 +St. Kitts and Nevis,KNA,1976,30095602.2944551 +St. Kitts and Nevis,KNA,1977,44680000 +St. Kitts and Nevis,KNA,1978,49095407.4074074 +St. Kitts and Nevis,KNA,1979,58296666.6666667 +St. Kitts and Nevis,KNA,1980,67715444.4444444 +St. Kitts and Nevis,KNA,1981,79026037.037037 +St. Kitts and Nevis,KNA,1982,84381407.4074074 +St. Kitts and Nevis,KNA,1983,85100481.4814815 +St. Kitts and Nevis,KNA,1984,95898444.4444444 +St. Kitts and Nevis,KNA,1985,106057000 +St. Kitts and Nevis,KNA,1986,125272259.259259 +St. Kitts and Nevis,KNA,1987,140705629.62963 +St. Kitts and Nevis,KNA,1988,165745740.740741 +St. Kitts and Nevis,KNA,1989,185094777.777778 +St. Kitts and Nevis,KNA,1990,208740444.444444 +St. Kitts and Nevis,KNA,1991,209880962.962963 +St. Kitts and Nevis,KNA,1992,229434518.518519 +St. Kitts and Nevis,KNA,1993,249676000 +St. Kitts and Nevis,KNA,1994,277567259.259259 +St. Kitts and Nevis,KNA,1995,299699666.666667 +St. Kitts and Nevis,KNA,1996,318742962.962963 +St. Kitts and Nevis,KNA,1997,357237682.126571 +St. Kitts and Nevis,KNA,1998,364975456.93842 +St. Kitts and Nevis,KNA,1999,389984428.873318 +St. Kitts and Nevis,KNA,2000,420515112.890668 +St. Kitts and Nevis,KNA,2001,461078096.244393 +St. Kitts and Nevis,KNA,2002,483120201.312424 +St. Kitts and Nevis,KNA,2003,465850733.325919 +St. Kitts and Nevis,KNA,2004,502561637.037037 +St. Kitts and Nevis,KNA,2005,543167674.444444 +St. Kitts and Nevis,KNA,2006,636218025.185185 +St. Kitts and Nevis,KNA,2007,674008481.481481 +St. Kitts and Nevis,KNA,2008,738942555.555555 +St. Kitts and Nevis,KNA,2009,723209111.111111 +St. Kitts and Nevis,KNA,2010,705015370.37037 +St. Kitts and Nevis,KNA,2011,753225962.962963 +St. Kitts and Nevis,KNA,2012,734462666.666667 +St. Kitts and Nevis,KNA,2013,788163888.888889 +St. Kitts and Nevis,KNA,2014,847778185.185185 +St. Kitts and Nevis,KNA,2015,876478555.555555 +St. Kitts and Nevis,KNA,2016,916896555.555555 +St. Lucia,LCA,1977,87350296.2962963 +St. Lucia,LCA,1978,102094111.111111 +St. Lucia,LCA,1979,123935518.518519 +St. Lucia,LCA,1980,146341370.37037 +St. Lucia,LCA,1981,167970111.111111 +St. Lucia,LCA,1982,182754000 +St. Lucia,LCA,1983,197228777.777778 +St. Lucia,LCA,1984,217663037.037037 +St. Lucia,LCA,1985,241273000 +St. Lucia,LCA,1986,289689703.703704 +St. Lucia,LCA,1987,319999000 +St. Lucia,LCA,1988,366541074.074074 +St. Lucia,LCA,1989,415518111.111111 +St. Lucia,LCA,1990,483962444.444444 +St. Lucia,LCA,1991,513753814.814815 +St. Lucia,LCA,1992,566894740.740741 +St. Lucia,LCA,1993,574870407.407407 +St. Lucia,LCA,1994,600005555.555556 +St. Lucia,LCA,1995,640449518.518518 +St. Lucia,LCA,1996,662196185.185185 +St. Lucia,LCA,1997,676949592.592593 +St. Lucia,LCA,1998,737554888.888889 +St. Lucia,LCA,1999,775934814.814815 +St. Lucia,LCA,2000,784159222.222222 +St. Lucia,LCA,2001,743808074.074074 +St. Lucia,LCA,2002,748346592.592593 +St. Lucia,LCA,2003,823837148.148148 +St. Lucia,LCA,2004,893107222.222222 +St. Lucia,LCA,2005,951207370.37037 +St. Lucia,LCA,2006,1062617148.14815 +St. Lucia,LCA,2007,1150526259.25926 +St. Lucia,LCA,2008,1187075814.81481 +St. Lucia,LCA,2009,1180949888.88889 +St. Lucia,LCA,2010,1241892814.81481 +St. Lucia,LCA,2011,1280623888.88889 +St. Lucia,LCA,2012,1298815407.40741 +St. Lucia,LCA,2013,1318052185.18519 +St. Lucia,LCA,2014,1386188629.62963 +St. Lucia,LCA,2015,1431135703.7037 +St. Lucia,LCA,2016,1378627407.40741 +St. Vincent and the Grenadines,VCT,1960,13066557.7786852 +St. Vincent and the Grenadines,VCT,1961,13999883.3343055 +St. Vincent and the Grenadines,VCT,1962,14524878.959342 +St. Vincent and the Grenadines,VCT,1963,13708219.0981742 +St. Vincent and the Grenadines,VCT,1964,14758210.3482471 +St. Vincent and the Grenadines,VCT,1965,15108207.4316047 +St. Vincent and the Grenadines,VCT,1966,16099865.8344514 +St. Vincent and the Grenadines,VCT,1967,15835177.9329133 +St. Vincent and the Grenadines,VCT,1968,15350000 +St. Vincent and the Grenadines,VCT,1969,16650000 +St. Vincent and the Grenadines,VCT,1970,18450000 +St. Vincent and the Grenadines,VCT,1971,20051648.1847182 +St. Vincent and the Grenadines,VCT,1972,27585488.9918284 +St. Vincent and the Grenadines,VCT,1973,30165373.6218865 +St. Vincent and the Grenadines,VCT,1974,32924215.8581726 +St. Vincent and the Grenadines,VCT,1975,33237164.715642 +St. Vincent and the Grenadines,VCT,1976,32792480.9729606 +St. Vincent and the Grenadines,VCT,1977,49353148.1481481 +St. Vincent and the Grenadines,VCT,1978,60844777.7777778 +St. Vincent and the Grenadines,VCT,1979,71096370.3703704 +St. Vincent and the Grenadines,VCT,1980,82340333.3333333 +St. Vincent and the Grenadines,VCT,1981,102086555.555556 +St. Vincent and the Grenadines,VCT,1982,113759185.185185 +St. Vincent and the Grenadines,VCT,1983,122255333.333333 +St. Vincent and the Grenadines,VCT,1984,135025000 +St. Vincent and the Grenadines,VCT,1985,145641703.703704 +St. Vincent and the Grenadines,VCT,1986,160846666.666667 +St. Vincent and the Grenadines,VCT,1987,175580629.62963 +St. Vincent and the Grenadines,VCT,1988,200726703.703704 +St. Vincent and the Grenadines,VCT,1989,214745000 +St. Vincent and the Grenadines,VCT,1990,240365259.259259 +St. Vincent and the Grenadines,VCT,1991,254829629.62963 +St. Vincent and the Grenadines,VCT,1992,277954111.111111 +St. Vincent and the Grenadines,VCT,1993,286307814.814815 +St. Vincent and the Grenadines,VCT,1994,289438481.481481 +St. Vincent and the Grenadines,VCT,1995,316008481.481481 +St. Vincent and the Grenadines,VCT,1996,331489703.703704 +St. Vincent and the Grenadines,VCT,1997,347770000 +St. Vincent and the Grenadines,VCT,1998,373619851.851852 +St. Vincent and the Grenadines,VCT,1999,390719148.148148 +St. Vincent and the Grenadines,VCT,2000,396270000 +St. Vincent and the Grenadines,VCT,2001,430040370.37037 +St. Vincent and the Grenadines,VCT,2002,461883444.444444 +St. Vincent and the Grenadines,VCT,2003,481806296.296296 +St. Vincent and the Grenadines,VCT,2004,521975111.111111 +St. Vincent and the Grenadines,VCT,2005,550728666.666667 +St. Vincent and the Grenadines,VCT,2006,610930044.444444 +St. Vincent and the Grenadines,VCT,2007,651833314.814815 +St. Vincent and the Grenadines,VCT,2008,695428851.851852 +St. Vincent and the Grenadines,VCT,2009,674922481.481482 +St. Vincent and the Grenadines,VCT,2010,681225962.962963 +St. Vincent and the Grenadines,VCT,2011,676129407.407407 +St. Vincent and the Grenadines,VCT,2012,692933740.740741 +St. Vincent and the Grenadines,VCT,2013,721207148.148148 +St. Vincent and the Grenadines,VCT,2014,727912814.814815 +St. Vincent and the Grenadines,VCT,2015,737683555.555555 +St. Vincent and the Grenadines,VCT,2016,770796555.555556 +Sudan,SDN,1960,1307333333.33333 +Sudan,SDN,1961,1419333333.33333 +Sudan,SDN,1962,1541666666.66667 +Sudan,SDN,1963,1568333333.33333 +Sudan,SDN,1964,1611333333.33333 +Sudan,SDN,1965,1679333333.33333 +Sudan,SDN,1966,1723000000 +Sudan,SDN,1967,1865666666.66667 +Sudan,SDN,1968,1947333333.33333 +Sudan,SDN,1969,2144333333.33333 +Sudan,SDN,1970,2437666666.66667 +Sudan,SDN,1971,2656000000 +Sudan,SDN,1972,2882000000 +Sudan,SDN,1973,3571666666.66667 +Sudan,SDN,1974,4595000000 +Sudan,SDN,1975,5598000000 +Sudan,SDN,1976,6979333333.33333 +Sudan,SDN,1977,8704000000 +Sudan,SDN,1978,7670500000 +Sudan,SDN,1979,9032250000 +Sudan,SDN,1980,7459833333.33333 +Sudan,SDN,1981,10016500000 +Sudan,SDN,1982,9240000000 +Sudan,SDN,1983,8230153846.15384 +Sudan,SDN,1984,9701357142.85714 +Sudan,SDN,1985,12403733333.3333 +Sudan,SDN,1986,15769062500 +Sudan,SDN,1987,20155555555.5556 +Sudan,SDN,1988,15399166666.6667 +Sudan,SDN,1989,15291507936.5079 +Sudan,SDN,1990,12408647540.9836 +Sudan,SDN,1991,11379222222.2222 +Sudan,SDN,1992,7034219712.52567 +Sudan,SDN,1993,8881785938.48085 +Sudan,SDN,1994,12794192334.2541 +Sudan,SDN,1995,13829744878.6366 +Sudan,SDN,1996,9018243044.45155 +Sudan,SDN,1997,11681494637.3041 +Sudan,SDN,1998,11250327988.0478 +Sudan,SDN,1999,10682045258.3647 +Sudan,SDN,2000,12257418326.0734 +Sudan,SDN,2001,13182979783.533 +Sudan,SDN,2002,14803189092.7044 +Sudan,SDN,2003,17646503525.1743 +Sudan,SDN,2004,21457470202.7839 +Sudan,SDN,2005,26524538565.7403 +Sudan,SDN,2006,35822408611.5588 +Sudan,SDN,2007,45898948564.0593 +Sudan,SDN,2008,54526580231.5568 +Sudan,SDN,2009,53150209167.934 +Sudan,SDN,2010,65634109236.7736 +Sudan,SDN,2011,67327289319.733 +Sudan,SDN,2012,68125631150.2939 +Sudan,SDN,2013,72065940085.772 +Sudan,SDN,2014,82151588418.8325 +Sudan,SDN,2015,97156119150 +Sudan,SDN,2016,95584380032.2061 +Suriname,SUR,1960,93850000 +Suriname,SUR,1961,98400000 +Suriname,SUR,1962,103500000 +Suriname,SUR,1963,110000000 +Suriname,SUR,1964,120850000 +Suriname,SUR,1965,138650000 +Suriname,SUR,1966,171100000 +Suriname,SUR,1967,198450000 +Suriname,SUR,1968,220600000 +Suriname,SUR,1969,233450000 +Suriname,SUR,1970,247150000 +Suriname,SUR,1971,270650000 +Suriname,SUR,1972,287600000 +Suriname,SUR,1973,305300000 +Suriname,SUR,1974,368600000 +Suriname,SUR,1975,465000000 +Suriname,SUR,1976,505500000 +Suriname,SUR,1977,641000000 +Suriname,SUR,1978,735500000 +Suriname,SUR,1979,783000000 +Suriname,SUR,1980,794900000 +Suriname,SUR,1981,889050000 +Suriname,SUR,1982,915150000 +Suriname,SUR,1983,883600000 +Suriname,SUR,1984,864150000 +Suriname,SUR,1985,873250000 +Suriname,SUR,1986,891000000 +Suriname,SUR,1987,979850000 +Suriname,SUR,1988,1160900000 +Suriname,SUR,1989,542520000 +Suriname,SUR,1990,388300000 +Suriname,SUR,1991,448300000 +Suriname,SUR,1992,404600000 +Suriname,SUR,1993,428794117.647059 +Suriname,SUR,1994,605492537.313433 +Suriname,SUR,1995,693970588.235294 +Suriname,SUR,1996,860630922.693267 +Suriname,SUR,1997,929607500 +Suriname,SUR,1998,945000000 +Suriname,SUR,1999,885444186.046512 +Suriname,SUR,2000,892164393.939394 +Suriname,SUR,2001,763465550.458716 +Suriname,SUR,2002,1078402127.65957 +Suriname,SUR,2003,1271196078.43137 +Suriname,SUR,2004,1484092538.40527 +Suriname,SUR,2005,1793754804.70037 +Suriname,SUR,2006,2626380435.17877 +Suriname,SUR,2007,2936612021.85792 +Suriname,SUR,2008,3532969034.60838 +Suriname,SUR,2009,3875409836.06557 +Suriname,SUR,2010,4368398047.64333 +Suriname,SUR,2011,4422276621.78703 +Suriname,SUR,2012,4980000000 +Suriname,SUR,2013,5145757575.75758 +Suriname,SUR,2014,5240606060.60606 +Suriname,SUR,2015,4878731707.26948 +Suriname,SUR,2016,3620699735.1962 +Swaziland,SWZ,1960,35076158.4768305 +Swaziland,SWZ,1961,43025199.4960101 +Swaziland,SWZ,1962,45927061.4587708 +Swaziland,SWZ,1963,54128377.4324513 +Swaziland,SWZ,1964,64979280.4143917 +Swaziland,SWZ,1965,70278594.4281114 +Swaziland,SWZ,1966,76858462.8307434 +Swaziland,SWZ,1967,74758504.8299034 +Swaziland,SWZ,1968,79798404.0319194 +Swaziland,SWZ,1969,105417891.642167 +Swaziland,SWZ,1970,112137757.244855 +Swaziland,SWZ,1971,136465324.384787 +Swaziland,SWZ,1972,146741251.46351 +Swaziland,SWZ,1973,221902017.291066 +Swaziland,SWZ,1974,264311994.113319 +Swaziland,SWZ,1975,288302907.369844 +Swaziland,SWZ,1976,272539098.436063 +Swaziland,SWZ,1977,304047838.086477 +Swaziland,SWZ,1978,340616375.344986 +Swaziland,SWZ,1979,412093133.760988 +Swaziland,SWZ,1980,542000513.610683 +Swaziland,SWZ,1981,571542674.577818 +Swaziland,SWZ,1982,537575980.843618 +Swaziland,SWZ,1983,555336145.767884 +Swaziland,SWZ,1984,494475699.857656 +Swaziland,SWZ,1985,361014890.45841 +Swaziland,SWZ,1986,449146608.315098 +Swaziland,SWZ,1987,584135559.921414 +Swaziland,SWZ,1988,692016714.317132 +Swaziland,SWZ,1989,696915430.663057 +Swaziland,SWZ,1990,1114703088.1614 +Swaziland,SWZ,1991,1156141998.33412 +Swaziland,SWZ,1992,1284766234.2216 +Swaziland,SWZ,1993,1357206995.74624 +Swaziland,SWZ,1994,1419293454.99606 +Swaziland,SWZ,1995,1698982437.76019 +Swaziland,SWZ,1996,1602760100.48147 +Swaziland,SWZ,1997,1716699913.19444 +Swaziland,SWZ,1998,1576904292.4588 +Swaziland,SWZ,1999,1547884442.26205 +Swaziland,SWZ,2000,1738100848.09721 +Swaziland,SWZ,2001,1542477354.99355 +Swaziland,SWZ,2002,1432228172.702 +Swaziland,SWZ,2003,2197612760.58535 +Swaziland,SWZ,2004,2770082815.20915 +Swaziland,SWZ,2005,3178126547.9298 +Swaziland,SWZ,2006,3291353812.85535 +Swaziland,SWZ,2007,3469363931.16431 +Swaziland,SWZ,2008,3294093359.62088 +Swaziland,SWZ,2009,3580417156.54024 +Swaziland,SWZ,2010,4438778553.63533 +Swaziland,SWZ,2011,4820499498.18209 +Swaziland,SWZ,2012,4807282468.0268 +Swaziland,SWZ,2013,4575596300.78922 +Swaziland,SWZ,2014,4486261555.30421 +Swaziland,SWZ,2015,4137638726.69274 +Swaziland,SWZ,2016,3727303664.17403 +Sweden,SWE,1960,14842870293.4207 +Sweden,SWE,1961,16147160122.7882 +Sweden,SWE,1962,17511477311.4463 +Sweden,SWE,1963,18954132365.5148 +Sweden,SWE,1964,21137242560.8543 +Sweden,SWE,1965,23260320646.2745 +Sweden,SWE,1966,25302033132.3312 +Sweden,SWE,1967,27463409201.8822 +Sweden,SWE,1968,29143383490.5896 +Sweden,SWE,1969,31649203885.888 +Sweden,SWE,1970,37555366021.0315 +Sweden,SWE,1971,40980345656.3725 +Sweden,SWE,1972,48263914958.8443 +Sweden,SWE,1973,58567384058.8006 +Sweden,SWE,1974,65082581294.7696 +Sweden,SWE,1975,81716751697.8951 +Sweden,SWE,1976,88102107647.0993 +Sweden,SWE,1977,93136775102.6419 +Sweden,SWE,1978,102969762221.976 +Sweden,SWE,1979,121646718574.328 +Sweden,SWE,1980,140088635568.375 +Sweden,SWE,1981,127858412114.39 +Sweden,SWE,1982,112767844570.719 +Sweden,SWE,1983,103533702638.547 +Sweden,SWE,1984,107661673734.858 +Sweden,SWE,1985,112514448261.835 +Sweden,SWE,1986,148376104539.839 +Sweden,SWE,1987,180429286795.786 +Sweden,SWE,1988,204068257817.6 +Sweden,SWE,1989,214875344909.957 +Sweden,SWE,1990,258154283908.9 +Sweden,SWE,1991,270362531376.602 +Sweden,SWE,1992,280312318915.485 +Sweden,SWE,1993,209950792712.696 +Sweden,SWE,1994,226079963711.768 +Sweden,SWE,1995,264051981551.316 +Sweden,SWE,1996,288103936773.039 +Sweden,SWE,1997,264477727278.681 +Sweden,SWE,1998,266800462898.904 +Sweden,SWE,1999,270847937645.236 +Sweden,SWE,2000,259802012617.057 +Sweden,SWE,2001,239917320966.977 +Sweden,SWE,2002,263926220332.543 +Sweden,SWE,2003,331108912605.271 +Sweden,SWE,2004,381705425301.746 +Sweden,SWE,2005,389042298376.845 +Sweden,SWE,2006,420032121655.688 +Sweden,SWE,2007,487816328342.309 +Sweden,SWE,2008,513965650650.119 +Sweden,SWE,2009,429657033107.737 +Sweden,SWE,2010,488377689564.921 +Sweden,SWE,2011,563109663291.177 +Sweden,SWE,2012,543880647757.404 +Sweden,SWE,2013,578742001487.571 +Sweden,SWE,2014,573817719109.402 +Sweden,SWE,2015,495694356611.551 +Sweden,SWE,2016,510999796581.597 +Switzerland,CHE,1960,9522746719.21614 +Switzerland,CHE,1961,10712712465.0522 +Switzerland,CHE,1962,11879982758.5619 +Switzerland,CHE,1963,13063643795.7884 +Switzerland,CHE,1964,14480556571.5476 +Switzerland,CHE,1965,15346741669.7575 +Switzerland,CHE,1966,16480058704.8531 +Switzerland,CHE,1967,17740013179.26 +Switzerland,CHE,1968,18942729779.1 +Switzerland,CHE,1969,20524886616.4789 +Switzerland,CHE,1980,118710309542.281 +Switzerland,CHE,1981,108721034568.781 +Switzerland,CHE,1982,111431738019.012 +Switzerland,CHE,1983,111035571006.622 +Switzerland,CHE,1984,106019113631.527 +Switzerland,CHE,1985,107496240242.562 +Switzerland,CHE,1986,154095512368.67 +Switzerland,CHE,1987,192981619165.773 +Switzerland,CHE,1988,208774024533.588 +Switzerland,CHE,1989,201572650956.66 +Switzerland,CHE,1990,257420293190.325 +Switzerland,CHE,1991,260459896582.985 +Switzerland,CHE,1992,271133679206.372 +Switzerland,CHE,1993,263691005481.862 +Switzerland,CHE,1994,291913801052.862 +Switzerland,CHE,1995,341759012938.689 +Switzerland,CHE,1996,329619351051.78 +Switzerland,CHE,1997,286604024805.347 +Switzerland,CHE,1998,294977518761.208 +Switzerland,CHE,1999,289884127679.404 +Switzerland,CHE,2000,271659728209.379 +Switzerland,CHE,2001,278628772872.719 +Switzerland,CHE,2002,301127808995.252 +Switzerland,CHE,2003,351982634291.23 +Switzerland,CHE,2004,393541693928.428 +Switzerland,CHE,2005,407535656039.19 +Switzerland,CHE,2006,429195591242.622 +Switzerland,CHE,2007,477407802315.895 +Switzerland,CHE,2008,551546962699.658 +Switzerland,CHE,2009,539528229942.101 +Switzerland,CHE,2010,581208562423.374 +Switzerland,CHE,2011,696278717728.137 +Switzerland,CHE,2012,665054050620.785 +Switzerland,CHE,2013,684835034384.327 +Switzerland,CHE,2014,702705544908.583 +Switzerland,CHE,2015,670789928809.882 +Switzerland,CHE,2016,659827235193.83 +Syrian Arab Republic,SYR,1960,857704431.686497 +Syrian Arab Republic,SYR,1961,945244992.211306 +Syrian Arab Republic,SYR,1962,1110565863.53737 +Syrian Arab Republic,SYR,1963,1200447429.35563 +Syrian Arab Republic,SYR,1964,1339494290.42432 +Syrian Arab Republic,SYR,1965,1472036550.70992 +Syrian Arab Republic,SYR,1966,1342287556.59602 +Syrian Arab Republic,SYR,1967,1580229795.10881 +Syrian Arab Republic,SYR,1968,1753746369.66049 +Syrian Arab Republic,SYR,1969,2245011571.98652 +Syrian Arab Republic,SYR,1970,2140383695.94618 +Syrian Arab Republic,SYR,1971,2589851693.01656 +Syrian Arab Republic,SYR,1972,3059682162.06566 +Syrian Arab Republic,SYR,1973,3239488104.60091 +Syrian Arab Republic,SYR,1974,5159557176.25012 +Syrian Arab Republic,SYR,1975,6826980766.8048 +Syrian Arab Republic,SYR,1976,7633528920.63247 +Syrian Arab Republic,SYR,1977,7696011359.94156 +Syrian Arab Republic,SYR,1978,9275203105.57946 +Syrian Arab Republic,SYR,1979,9929682184.32718 +Syrian Arab Republic,SYR,1980,13062421024.9337 +Syrian Arab Republic,SYR,1981,15518199247.3393 +Syrian Arab Republic,SYR,1982,16298905397.0701 +Syrian Arab Republic,SYR,1983,17589184556.6946 +Syrian Arab Republic,SYR,1984,17503082982.2832 +Syrian Arab Republic,SYR,1985,16403544510.5268 +Syrian Arab Republic,SYR,1986,13293209270.1036 +Syrian Arab Republic,SYR,1987,11356215712.9326 +Syrian Arab Republic,SYR,1988,10577042354.799 +Syrian Arab Republic,SYR,1989,9853396225.58749 +Syrian Arab Republic,SYR,1990,12308624283.9787 +Syrian Arab Republic,SYR,1991,12981833333.3333 +Syrian Arab Republic,SYR,1992,13253565898.9558 +Syrian Arab Republic,SYR,1993,13695962019.2084 +Syrian Arab Republic,SYR,1994,10122020000 +Syrian Arab Republic,SYR,1995,11396706586.8263 +Syrian Arab Republic,SYR,1996,13789560878.2435 +Syrian Arab Republic,SYR,1997,14505233968.8716 +Syrian Arab Republic,SYR,1998,15200846138.4615 +Syrian Arab Republic,SYR,1999,15873875968.9922 +Syrian Arab Republic,SYR,2000,19325894913.1254 +Syrian Arab Republic,SYR,2001,21099833783.503 +Syrian Arab Republic,SYR,2002,21582248881.6592 +Syrian Arab Republic,SYR,2003,21828144686.0394 +Syrian Arab Republic,SYR,2004,25086930693.0693 +Syrian Arab Republic,SYR,2005,28858965517.2414 +Syrian Arab Republic,SYR,2006,33332844574.7801 +Syrian Arab Republic,SYR,2007,40405006007.2086 +Tajikistan,TJK,1990,2629395066.27017 +Tajikistan,TJK,1991,2534720480.3244 +Tajikistan,TJK,1992,1909246640.80838 +Tajikistan,TJK,1993,1646693642.11088 +Tajikistan,TJK,1994,1346074611.47792 +Tajikistan,TJK,1995,1231523034.78397 +Tajikistan,TJK,1996,1043893062.60575 +Tajikistan,TJK,1997,921843144.229059 +Tajikistan,TJK,1998,1320126706.15503 +Tajikistan,TJK,1999,1086567377.60543 +Tajikistan,TJK,2000,860550305.832491 +Tajikistan,TJK,2001,1080774007.25065 +Tajikistan,TJK,2002,1221113780.25397 +Tajikistan,TJK,2003,1554125530.8029 +Tajikistan,TJK,2004,2076148695.50581 +Tajikistan,TJK,2005,2312319579.02843 +Tajikistan,TJK,2006,2830236053.84429 +Tajikistan,TJK,2007,3719497371.09659 +Tajikistan,TJK,2008,5161336170.46084 +Tajikistan,TJK,2009,4979481980.35098 +Tajikistan,TJK,2010,5642178579.58438 +Tajikistan,TJK,2011,6522732202.50748 +Tajikistan,TJK,2012,7633049792.09321 +Tajikistan,TJK,2013,8506674782.75471 +Tajikistan,TJK,2014,9236309138.04277 +Tajikistan,TJK,2015,7853450374.0001 +Tajikistan,TJK,2016,6951657158.90093 +Tanzania,TZA,1988,5100405772.46327 +Tanzania,TZA,1989,4420168102.39306 +Tanzania,TZA,1990,4258743262.82876 +Tanzania,TZA,1991,4956588278.56144 +Tanzania,TZA,1992,4601413263.52894 +Tanzania,TZA,1993,4257702196.53864 +Tanzania,TZA,1994,4510846967.8742 +Tanzania,TZA,1995,5255221424.80962 +Tanzania,TZA,1996,6496195450.61034 +Tanzania,TZA,1997,7683852496.84499 +Tanzania,TZA,1998,9345174219.07253 +Tanzania,TZA,1999,9697847263.63196 +Tanzania,TZA,2000,10185786382.8283 +Tanzania,TZA,2001,10383560602.8537 +Tanzania,TZA,2002,10805599892.7355 +Tanzania,TZA,2003,11659129888.8021 +Tanzania,TZA,2004,12825801580.9281 +Tanzania,TZA,2005,16929976600.142 +Tanzania,TZA,2006,18610460326.5437 +Tanzania,TZA,2007,21501741757.484 +Tanzania,TZA,2008,27368386358.131 +Tanzania,TZA,2009,28573777052.4542 +Tanzania,TZA,2010,31407908612.0943 +Tanzania,TZA,2011,33878631649.4157 +Tanzania,TZA,2012,39087748240.4403 +Tanzania,TZA,2013,44333456244.744 +Tanzania,TZA,2014,48197218326.7942 +Tanzania,TZA,2015,45628247290.4618 +Tanzania,TZA,2016,47431038892.2441 +Thailand,THA,1960,2760747471.88624 +Thailand,THA,1961,3034043574.06071 +Thailand,THA,1962,3308912796.93487 +Thailand,THA,1963,3540403456.55305 +Thailand,THA,1964,3889129942.30769 +Thailand,THA,1965,4388937649.03846 +Thailand,THA,1966,5279230817.30769 +Thailand,THA,1967,5638461442.30769 +Thailand,THA,1968,6081009427.88461 +Thailand,THA,1969,6695336567.30769 +Thailand,THA,1970,7086538437.5 +Thailand,THA,1971,7375000024.03846 +Thailand,THA,1972,8177884552.88461 +Thailand,THA,1973,10838587357.7466 +Thailand,THA,1974,13703000530.0587 +Thailand,THA,1975,14882747955.0328 +Thailand,THA,1976,16985211146.0238 +Thailand,THA,1977,19779315170.0237 +Thailand,THA,1978,24006570178.1561 +Thailand,THA,1979,27371699082.7126 +Thailand,THA,1980,32353440726.8856 +Thailand,THA,1981,34846107862.3673 +Thailand,THA,1982,36589797857.4006 +Thailand,THA,1983,40042826244.2337 +Thailand,THA,1984,41797592963.4424 +Thailand,THA,1985,38900692712.1496 +Thailand,THA,1986,43096746122.4614 +Thailand,THA,1987,50535438696.4094 +Thailand,THA,1988,61667199834.7428 +Thailand,THA,1989,72250877410.3183 +Thailand,THA,1990,85343063965.9182 +Thailand,THA,1991,98234695722.0341 +Thailand,THA,1992,111452869378.467 +Thailand,THA,1993,128889832382.818 +Thailand,THA,1994,146683499005.964 +Thailand,THA,1995,169278552851.272 +Thailand,THA,1996,183035154107.494 +Thailand,THA,1997,150180268649.388 +Thailand,THA,1998,113675706127.265 +Thailand,THA,1999,126668932159.508 +Thailand,THA,2000,126392308497.749 +Thailand,THA,2001,120296746256.631 +Thailand,THA,2002,134300851255.002 +Thailand,THA,2003,152280653543.725 +Thailand,THA,2004,172895476152.592 +Thailand,THA,2005,189318499954.003 +Thailand,THA,2006,221758486880.313 +Thailand,THA,2007,262942650543.771 +Thailand,THA,2008,291383081231.82 +Thailand,THA,2009,281710095724.761 +Thailand,THA,2010,341105009515.333 +Thailand,THA,2011,370818747396.833 +Thailand,THA,2012,397559992407.45 +Thailand,THA,2013,420528737876.717 +Thailand,THA,2014,406521561093.357 +Thailand,THA,2015,399234547137.472 +Thailand,THA,2016,406839679301.943 +Timor-Leste,TLS,2000,368000000 +Timor-Leste,TLS,2001,452000000 +Timor-Leste,TLS,2002,444000000 +Timor-Leste,TLS,2003,453000000 +Timor-Leste,TLS,2004,466000000 +Timor-Leste,TLS,2005,491000000 +Timor-Leste,TLS,2006,463000000 +Timor-Leste,TLS,2007,559000000 +Timor-Leste,TLS,2008,694000000 +Timor-Leste,TLS,2009,827000000 +Timor-Leste,TLS,2010,943000000 +Timor-Leste,TLS,2011,1148000000 +Timor-Leste,TLS,2012,1293000000 +Timor-Leste,TLS,2013,1312000000 +Timor-Leste,TLS,2014,1399000000 +Timor-Leste,TLS,2015,1441718600 +Togo,TGO,1960,121128073.114022 +Togo,TGO,1961,126396469.707058 +Togo,TGO,1962,132237441.630863 +Togo,TGO,1963,143255784.510751 +Togo,TGO,1964,166104067.630043 +Togo,TGO,1965,187300336.365369 +Togo,TGO,1966,216136263.912497 +Togo,TGO,1967,231706475.464114 +Togo,TGO,1968,241956910.658103 +Togo,TGO,1969,267732446.378413 +Togo,TGO,1970,253976626.166639 +Togo,TGO,1971,286537524.990331 +Togo,TGO,1972,335677636.893737 +Togo,TGO,1973,406479906.159652 +Togo,TGO,1974,560437742.594972 +Togo,TGO,1975,617321669.390877 +Togo,TGO,1976,619375134.18051 +Togo,TGO,1977,777435020.475847 +Togo,TGO,1978,824263841.539264 +Togo,TGO,1979,891775906.631015 +Togo,TGO,1980,1136408814.19692 +Togo,TGO,1981,962347000.991788 +Togo,TGO,1982,821651918.724626 +Togo,TGO,1983,765746590.616849 +Togo,TGO,1984,718148959.610872 +Togo,TGO,1985,762359722.701402 +Togo,TGO,1986,1060911735.26065 +Togo,TGO,1987,1249099130.02277 +Togo,TGO,1988,1378847487.41137 +Togo,TGO,1989,1352949662.75172 +Togo,TGO,1990,1628427515.41881 +Togo,TGO,1991,1602299862.9243 +Togo,TGO,1992,1692959110.18022 +Togo,TGO,1993,1233496846.33493 +Togo,TGO,1994,982624324.505898 +Togo,TGO,1995,1309382885.33029 +Togo,TGO,1996,1465448290.34132 +Togo,TGO,1997,1498950899.08774 +Togo,TGO,1998,1587345950.9743 +Togo,TGO,1999,1576094566.48548 +Togo,TGO,2000,1294250233.18894 +Togo,TGO,2001,1332328999.09077 +Togo,TGO,2002,1474630207.08242 +Togo,TGO,2003,1673690429.61609 +Togo,TGO,2004,1937074572.08687 +Togo,TGO,2005,2115154262.03025 +Togo,TGO,2006,2202809251.31304 +Togo,TGO,2007,2523462557.38975 +Togo,TGO,2008,3163416242.05877 +Togo,TGO,2009,3163000528.8167 +Togo,TGO,2010,3172945644.5585 +Togo,TGO,2011,3756023159.96 +Togo,TGO,2012,3866617462.61854 +Togo,TGO,2013,4080929201.27925 +Togo,TGO,2014,4482880424.33988 +Togo,TGO,2015,4087628275.95594 +Togo,TGO,2016,4399995986.56477 +Tonga,TON,1975,32506741.7201204 +Tonga,TON,1976,30036416.9619944 +Tonga,TON,1977,34139387.8908849 +Tonga,TON,1978,41567471.6721987 +Tonga,TON,1979,44667002.0120724 +Tonga,TON,1980,53260077.4311091 +Tonga,TON,1981,62242013.3302689 +Tonga,TON,1982,62068161.0711025 +Tonga,TON,1983,60863963.963964 +Tonga,TON,1984,64248354.5414656 +Tonga,TON,1985,60058663.3144773 +Tonga,TON,1986,68195855.6149733 +Tonga,TON,1987,81667133.4546982 +Tonga,TON,1988,106657267.367342 +Tonga,TON,1989,106344854.986095 +Tonga,TON,1990,113563821.577404 +Tonga,TON,1991,132201141.446861 +Tonga,TON,1992,137066290.550071 +Tonga,TON,1993,138489884.393064 +Tonga,TON,1994,193775943.038933 +Tonga,TON,1995,202547013.927138 +Tonga,TON,1996,219583570.094975 +Tonga,TON,1997,212155124.65374 +Tonga,TON,1998,188686997.319035 +Tonga,TON,1999,196686674.669868 +Tonga,TON,2000,202363492.160332 +Tonga,TON,2001,181244788.473329 +Tonga,TON,2002,182737040.095422 +Tonga,TON,2003,202543202.004099 +Tonga,TON,2004,229358214.792003 +Tonga,TON,2005,262176133.72543 +Tonga,TON,2006,294137737.070038 +Tonga,TON,2007,300143056.873221 +Tonga,TON,2008,349459648.569023 +Tonga,TON,2009,318151987.342379 +Tonga,TON,2010,369435481.362435 +Tonga,TON,2011,423015937.994651 +Tonga,TON,2012,472441382.972447 +Tonga,TON,2013,449387934.017765 +Tonga,TON,2014,443475142.084644 +Tonga,TON,2015,435430325.223892 +Tonga,TON,2016,395159628.7518 +Trinidad and Tobago,TTO,1960,535670127.748935 +Trinidad and Tobago,TTO,1961,584961208.656595 +Trinidad and Tobago,TTO,1962,619319197.340022 +Trinidad and Tobago,TTO,1963,678235373.038558 +Trinidad and Tobago,TTO,1964,711893367.55527 +Trinidad and Tobago,TTO,1965,736568861.926151 +Trinidad and Tobago,TTO,1966,723735635.536371 +Trinidad and Tobago,TTO,1967,761981474.023359 +Trinidad and Tobago,TTO,1968,758899950 +Trinidad and Tobago,TTO,1969,779200000 +Trinidad and Tobago,TTO,1970,821850000 +Trinidad and Tobago,TTO,1971,896754316.674262 +Trinidad and Tobago,TTO,1972,1083381044.08473 +Trinidad and Tobago,TTO,1973,1308799458.96284 +Trinidad and Tobago,TTO,1974,2042031901.42217 +Trinidad and Tobago,TTO,1975,2442667573.04821 +Trinidad and Tobago,TTO,1976,2500410583.79177 +Trinidad and Tobago,TTO,1977,3138666666.66667 +Trinidad and Tobago,TTO,1978,3562333458.33333 +Trinidad and Tobago,TTO,1979,4602416625 +Trinidad and Tobago,TTO,1980,6235833333.33333 +Trinidad and Tobago,TTO,1981,6992083333.33333 +Trinidad and Tobago,TTO,1982,8140416666.66667 +Trinidad and Tobago,TTO,1983,7763750000 +Trinidad and Tobago,TTO,1984,7757083333.33333 +Trinidad and Tobago,TTO,1985,7375918367.34694 +Trinidad and Tobago,TTO,1986,4794444444.44444 +Trinidad and Tobago,TTO,1987,4797777777.77778 +Trinidad and Tobago,TTO,1988,4496852073.46896 +Trinidad and Tobago,TTO,1989,4323058823.52941 +Trinidad and Tobago,TTO,1990,5068000000 +Trinidad and Tobago,TTO,1991,5307905882.35294 +Trinidad and Tobago,TTO,1992,5439552941.17647 +Trinidad and Tobago,TTO,1993,4669488516.37981 +Trinidad and Tobago,TTO,1994,4947205860.01451 +Trinidad and Tobago,TTO,1995,5329214163.22001 +Trinidad and Tobago,TTO,1996,5759537726.26601 +Trinidad and Tobago,TTO,1997,5737751331.63779 +Trinidad and Tobago,TTO,1998,6043694330.21609 +Trinidad and Tobago,TTO,1999,6808982520.75759 +Trinidad and Tobago,TTO,2000,8154338232.95978 +Trinidad and Tobago,TTO,2001,8824873259.32105 +Trinidad and Tobago,TTO,2002,9008273720.93395 +Trinidad and Tobago,TTO,2003,11305459802.0683 +Trinidad and Tobago,TTO,2004,13280275123.0354 +Trinidad and Tobago,TTO,2005,15982282462.3786 +Trinidad and Tobago,TTO,2006,18369070085.3888 +Trinidad and Tobago,TTO,2007,21642304045.512 +Trinidad and Tobago,TTO,2008,27870257894.2347 +Trinidad and Tobago,TTO,2009,19175196445.7936 +Trinidad and Tobago,TTO,2010,22157948396.2042 +Trinidad and Tobago,TTO,2011,25433011405.3017 +Trinidad and Tobago,TTO,2012,25694164489.2373 +Trinidad and Tobago,TTO,2013,26436221401.2976 +Trinidad and Tobago,TTO,2014,26175906133.4665 +Trinidad and Tobago,TTO,2015,23559287483.9276 +Trinidad and Tobago,TTO,2016,20989155539.8436 +Tunisia,TUN,1965,991047619.047619 +Tunisia,TUN,1966,1040952380.95238 +Tunisia,TUN,1967,1085714285.71429 +Tunisia,TUN,1968,1214666666.66667 +Tunisia,TUN,1969,1289904761.90476 +Tunisia,TUN,1970,1439238095.2381 +Tunisia,TUN,1971,1685217058.71103 +Tunisia,TUN,1972,2237476420.03773 +Tunisia,TUN,1973,2730787476.28084 +Tunisia,TUN,1974,3545933562.42841 +Tunisia,TUN,1975,4328610489.68432 +Tunisia,TUN,1976,4507929104.47761 +Tunisia,TUN,1977,5109324009.32401 +Tunisia,TUN,1978,5968044209.51466 +Tunisia,TUN,1979,7188191881.91882 +Tunisia,TUN,1980,8744134354.16152 +Tunisia,TUN,1981,8428513568.24625 +Tunisia,TUN,1982,8133401049.60217 +Tunisia,TUN,1983,8350176782.55746 +Tunisia,TUN,1984,8254891864.05767 +Tunisia,TUN,1985,8410185739.96405 +Tunisia,TUN,1986,9018136020.15113 +Tunisia,TUN,1987,9696271268.25148 +Tunisia,TUN,1988,10096292842.1543 +Tunisia,TUN,1989,10102075213.3151 +Tunisia,TUN,1990,12290568181.8182 +Tunisia,TUN,1991,13074782608.6957 +Tunisia,TUN,1992,15497286295.7938 +Tunisia,TUN,1993,14608946896.483 +Tunisia,TUN,1994,15632463424.2784 +Tunisia,TUN,1995,18030876599.3444 +Tunisia,TUN,1996,19587322786.1105 +Tunisia,TUN,1997,20746360430.4187 +Tunisia,TUN,1998,21803372266.6198 +Tunisia,TUN,1999,22943685719.103 +Tunisia,TUN,2000,21473188881.5933 +Tunisia,TUN,2001,22066101341.4888 +Tunisia,TUN,2002,23142294436.2383 +Tunisia,TUN,2003,27453084982.5378 +Tunisia,TUN,2004,31183139301.4853 +Tunisia,TUN,2005,32273007553.5687 +Tunisia,TUN,2006,34378437265.2141 +Tunisia,TUN,2007,38908069299.204 +Tunisia,TUN,2008,44856586316.0458 +Tunisia,TUN,2009,43454935940.1614 +Tunisia,TUN,2010,44050929160.2627 +Tunisia,TUN,2011,45810626509.4474 +Tunisia,TUN,2012,45044176963.9542 +Tunisia,TUN,2013,46255308672.3703 +Tunisia,TUN,2014,47603227896.5659 +Tunisia,TUN,2015,43156606851.5498 +Tunisia,TUN,2016,42062549394.7859 +Turkey,TUR,1960,13995067817.5092 +Turkey,TUR,1961,8022222222.22222 +Turkey,TUR,1962,8922222222.22222 +Turkey,TUR,1963,10355555555.5556 +Turkey,TUR,1964,11177777777.7778 +Turkey,TUR,1965,11944444444.4444 +Turkey,TUR,1966,14122222222.2222 +Turkey,TUR,1967,15666666666.6667 +Turkey,TUR,1968,17500000000 +Turkey,TUR,1969,19466666666.6667 +Turkey,TUR,1970,17086956521.7391 +Turkey,TUR,1971,16256619963.7997 +Turkey,TUR,1972,20431095406.3604 +Turkey,TUR,1973,25724381625.4417 +Turkey,TUR,1974,35599913836.4328 +Turkey,TUR,1975,44633707242.7642 +Turkey,TUR,1976,51280134554.2889 +Turkey,TUR,1977,58676813687.3681 +Turkey,TUR,1978,65147022485.7919 +Turkey,TUR,1979,89394085658.2038 +Turkey,TUR,1980,68789289565.7434 +Turkey,TUR,1981,71040020140.4436 +Turkey,TUR,1982,64546332580.7583 +Turkey,TUR,1983,61678280115.4987 +Turkey,TUR,1984,59989909457.8379 +Turkey,TUR,1985,67234948264.5987 +Turkey,TUR,1986,75728009962.7878 +Turkey,TUR,1987,87172789528.3316 +Turkey,TUR,1988,90852814004.9917 +Turkey,TUR,1989,107143348667.094 +Turkey,TUR,1990,150676291094.21 +Turkey,TUR,1991,150027833333.333 +Turkey,TUR,1992,158459130434.783 +Turkey,TUR,1993,180169736363.636 +Turkey,TUR,1994,130690172297.297 +Turkey,TUR,1995,169485941048.035 +Turkey,TUR,1996,181475555282.555 +Turkey,TUR,1997,189834649111.257 +Turkey,TUR,1998,275768695818.949 +Turkey,TUR,1999,255884300382.044 +Turkey,TUR,2000,272979390595.01 +Turkey,TUR,2001,200254104151.474 +Turkey,TUR,2002,238423907922.754 +Turkey,TUR,2003,311825946795.017 +Turkey,TUR,2004,404776328437.599 +Turkey,TUR,2005,501422832613.554 +Turkey,TUR,2006,552504962256.438 +Turkey,TUR,2007,675754121015.013 +Turkey,TUR,2008,764322933264.927 +Turkey,TUR,2009,644656538878.423 +Turkey,TUR,2010,771876943465.616 +Turkey,TUR,2011,832546462299.564 +Turkey,TUR,2012,873981840596.288 +Turkey,TUR,2013,950595641011.836 +Turkey,TUR,2014,934167774215.031 +Turkey,TUR,2015,859383637728.94 +Turkey,TUR,2016,857748989287.642 +Turkmenistan,TKM,1987,2331358819.75954 +Turkmenistan,TKM,1988,3010982414.24425 +Turkmenistan,TKM,1989,3006988216.55045 +Turkmenistan,TKM,1990,3189539641.3171 +Turkmenistan,TKM,1991,3208098919.0146 +Turkmenistan,TKM,1992,3200539816.0601 +Turkmenistan,TKM,1993,3179225948.58114 +Turkmenistan,TKM,1994,2561118608.35516 +Turkmenistan,TKM,1995,2482228439.71407 +Turkmenistan,TKM,1996,2379281767.9558 +Turkmenistan,TKM,1997,2450084970.24741 +Turkmenistan,TKM,1998,2605688065.08338 +Turkmenistan,TKM,1999,2450686659.778 +Turkmenistan,TKM,2000,2904662604.82053 +Turkmenistan,TKM,2001,3534771968.51189 +Turkmenistan,TKM,2002,4462028988.72949 +Turkmenistan,TKM,2003,5977440582.80171 +Turkmenistan,TKM,2004,6838351088.46688 +Turkmenistan,TKM,2005,8104355716.8784 +Turkmenistan,TKM,2006,10277598152.4249 +Turkmenistan,TKM,2007,12664165103.1895 +Turkmenistan,TKM,2008,19271523178.8079 +Turkmenistan,TKM,2009,20214385964.9123 +Turkmenistan,TKM,2010,22583157894.7368 +Turkmenistan,TKM,2011,29233333333.3333 +Turkmenistan,TKM,2012,35164210526.3158 +Turkmenistan,TKM,2013,39197543859.6491 +Turkmenistan,TKM,2014,43524210526.3158 +Turkmenistan,TKM,2015,35799628571.4286 +Turkmenistan,TKM,2016,36179885714.2857 +Tuvalu,TUV,1990,8824447.74022325 +Tuvalu,TUV,1991,9365165.91369372 +Tuvalu,TUV,1992,9742949.47121034 +Tuvalu,TUV,1993,9630762.95389637 +Tuvalu,TUV,1994,10886825.5592923 +Tuvalu,TUV,1995,11025945.1445515 +Tuvalu,TUV,1996,12334846.2320995 +Tuvalu,TUV,1997,12700905.4475286 +Tuvalu,TUV,1998,12757632.8684508 +Tuvalu,TUV,1999,13687141.1058778 +Tuvalu,TUV,2000,13742057.0500928 +Tuvalu,TUV,2001,13196544.946726 +Tuvalu,TUV,2002,15450994.2410084 +Tuvalu,TUV,2003,18231078.5394643 +Tuvalu,TUV,2004,21534931.6075894 +Tuvalu,TUV,2005,21839098.8927071 +Tuvalu,TUV,2006,22902861.4457831 +Tuvalu,TUV,2007,27030374.0272781 +Tuvalu,TUV,2008,30290219.7617849 +Tuvalu,TUV,2009,27101076.2751521 +Tuvalu,TUV,2010,31823518.6204366 +Tuvalu,TUV,2011,39312016.5033522 +Tuvalu,TUV,2012,39875750.6730172 +Tuvalu,TUV,2013,38322359.5288666 +Tuvalu,TUV,2014,37259689.9224806 +Tuvalu,TUV,2015,32673277.7402149 +Tuvalu,TUV,2016,34218878.4390841 +Uganda,UGA,1960,423008385.744235 +Uganda,UGA,1961,441524109.014675 +Uganda,UGA,1962,449012578.616352 +Uganda,UGA,1963,516147798.742138 +Uganda,UGA,1964,589056603.773585 +Uganda,UGA,1965,884873949.579832 +Uganda,UGA,1966,925770308.123249 +Uganda,UGA,1967,967647058.823529 +Uganda,UGA,1968,1037815126.05042 +Uganda,UGA,1969,1169047619.04762 +Uganda,UGA,1970,1260084033.61345 +Uganda,UGA,1971,1417787114.84594 +Uganda,UGA,1972,1491596638.65546 +Uganda,UGA,1973,1702521008.40336 +Uganda,UGA,1974,2100142653.35235 +Uganda,UGA,1975,2359555555.55556 +Uganda,UGA,1976,2447300000 +Uganda,UGA,1977,2936470588.23529 +Uganda,UGA,1978,2420260869.56522 +Uganda,UGA,1979,2139025000 +Uganda,UGA,1980,1244610000 +Uganda,UGA,1981,1337300000 +Uganda,UGA,1982,2177500000 +Uganda,UGA,1983,2240333333.33333 +Uganda,UGA,1984,3615647477.05434 +Uganda,UGA,1985,3519666338.52454 +Uganda,UGA,1986,3923232122.12784 +Uganda,UGA,1987,6269511614.66235 +Uganda,UGA,1988,6508931651.66667 +Uganda,UGA,1989,5276480985.99937 +Uganda,UGA,1990,4304398865.88268 +Uganda,UGA,1991,3321729057.12215 +Uganda,UGA,1992,2857457860.05088 +Uganda,UGA,1993,3220439044.18949 +Uganda,UGA,1994,3990430446.71216 +Uganda,UGA,1995,5755818947.42125 +Uganda,UGA,1996,6044585326.938 +Uganda,UGA,1997,6269333313.17108 +Uganda,UGA,1998,6584815846.52754 +Uganda,UGA,1999,5998563257.94659 +Uganda,UGA,2000,6193246837.09687 +Uganda,UGA,2001,5840503868.57245 +Uganda,UGA,2002,6178563590.89254 +Uganda,UGA,2003,6336696288.98214 +Uganda,UGA,2004,7940362799.17997 +Uganda,UGA,2005,9013834373.41246 +Uganda,UGA,2006,9942597779.99265 +Uganda,UGA,2007,12292813603.2327 +Uganda,UGA,2008,14239026629.639 +Uganda,UGA,2009,21203769124.656 +Uganda,UGA,2010,20179411062.934 +Uganda,UGA,2011,20508595015.4506 +Uganda,UGA,2012,23516083900.4013 +Uganda,UGA,2013,24879053946.9135 +Uganda,UGA,2014,27927875335.8068 +Uganda,UGA,2015,27856380083.2478 +Uganda,UGA,2016,25527910090.6285 +Ukraine,UKR,1987,64087694038.2333 +Ukraine,UKR,1988,74703517902.6642 +Ukraine,UKR,1989,82709161099.1244 +Ukraine,UKR,1990,81456918678.5008 +Ukraine,UKR,1991,77464561149.5103 +Ukraine,UKR,1992,73942235330.437 +Ukraine,UKR,1993,65648559903.0571 +Ukraine,UKR,1994,52549555149.1978 +Ukraine,UKR,1995,48213868178.0873 +Ukraine,UKR,1996,44558077827.135 +Ukraine,UKR,1997,50150399791.647 +Ukraine,UKR,1998,41883241471.7365 +Ukraine,UKR,1999,31580639045.454 +Ukraine,UKR,2000,31261527363.144 +Ukraine,UKR,2001,38009344576.6088 +Ukraine,UKR,2002,42392896031.2394 +Ukraine,UKR,2003,50132953288.203 +Ukraine,UKR,2004,64883060725.7003 +Ukraine,UKR,2005,86142018069.3504 +Ukraine,UKR,2006,107753069306.931 +Ukraine,UKR,2007,142719009900.99 +Ukraine,UKR,2008,179992405832.321 +Ukraine,UKR,2009,117227769791.56 +Ukraine,UKR,2010,136013155905.036 +Ukraine,UKR,2011,163159671670.265 +Ukraine,UKR,2012,175781379051.433 +Ukraine,UKR,2013,183310146378.081 +Ukraine,UKR,2014,133503411375.739 +Ukraine,UKR,2015,91030959454.6961 +Ukraine,UKR,2016,93270479388.5243 +United Arab Emirates,ARE,1975,14720672506.5004 +United Arab Emirates,ARE,1976,19213022691.0526 +United Arab Emirates,ARE,1977,24871775164.6043 +United Arab Emirates,ARE,1978,23775831783.4263 +United Arab Emirates,ARE,1979,31225463217.7582 +United Arab Emirates,ARE,1980,43598748449.0479 +United Arab Emirates,ARE,1981,49333424135.1131 +United Arab Emirates,ARE,1982,46622718605.2847 +United Arab Emirates,ARE,1983,42803323345.1376 +United Arab Emirates,ARE,1984,41807954235.903 +United Arab Emirates,ARE,1985,40603650231.5445 +United Arab Emirates,ARE,1986,33943612094.7971 +United Arab Emirates,ARE,1987,36384908744.2114 +United Arab Emirates,ARE,1988,36275674203.2144 +United Arab Emirates,ARE,1989,41464995913.9199 +United Arab Emirates,ARE,1990,50701443748.2975 +United Arab Emirates,ARE,1991,51552165622.4462 +United Arab Emirates,ARE,1992,54239171887.769 +United Arab Emirates,ARE,1993,55625170253.337 +United Arab Emirates,ARE,1994,59305093979.842 +United Arab Emirates,ARE,1995,65743666575.8649 +United Arab Emirates,ARE,1996,73571233996.1863 +United Arab Emirates,ARE,1997,78839008444.5655 +United Arab Emirates,ARE,1998,75674336283.1858 +United Arab Emirates,ARE,1999,84445473110.9598 +United Arab Emirates,ARE,2000,104337372362.151 +United Arab Emirates,ARE,2001,103311640571.818 +United Arab Emirates,ARE,2002,109816201497.617 +United Arab Emirates,ARE,2003,124346358066.712 +United Arab Emirates,ARE,2004,147824370319.946 +United Arab Emirates,ARE,2005,180617018379.85 +United Arab Emirates,ARE,2006,222116541865.214 +United Arab Emirates,ARE,2007,257916133424.098 +United Arab Emirates,ARE,2008,315474615738.598 +United Arab Emirates,ARE,2009,253547358747.447 +United Arab Emirates,ARE,2010,289880430197.25 +United Arab Emirates,ARE,2011,350908390046.358 +United Arab Emirates,ARE,2012,374817974113.476 +United Arab Emirates,ARE,2013,390427289210.786 +United Arab Emirates,ARE,2014,403197682887.717 +United Arab Emirates,ARE,2015,357949199759.586 +United Arab Emirates,ARE,2016,348743265715.395 +United Kingdom,GBR,1960,72328047042.1588 +United Kingdom,GBR,1961,76694360635.9159 +United Kingdom,GBR,1962,80601939635.2483 +United Kingdom,GBR,1963,85443766670.4279 +United Kingdom,GBR,1964,93387598813.9269 +United Kingdom,GBR,1965,100595782309.165 +United Kingdom,GBR,1966,107090721447.057 +United Kingdom,GBR,1967,111185383409.521 +United Kingdom,GBR,1968,104702736248.084 +United Kingdom,GBR,1969,112676874821.987 +United Kingdom,GBR,1970,130671946244.3 +United Kingdom,GBR,1971,148113896325.14 +United Kingdom,GBR,1972,169965034965.035 +United Kingdom,GBR,1973,192537971582.558 +United Kingdom,GBR,1974,206131369798.971 +United Kingdom,GBR,1975,241756637168.142 +United Kingdom,GBR,1976,232614555256.065 +United Kingdom,GBR,1977,263066457352.172 +United Kingdom,GBR,1978,335883029721.956 +United Kingdom,GBR,1979,438994070309.191 +United Kingdom,GBR,1980,564947710899.373 +United Kingdom,GBR,1981,540765675241.158 +United Kingdom,GBR,1982,515048916841.37 +United Kingdom,GBR,1983,489618008185.539 +United Kingdom,GBR,1984,461487097632.349 +United Kingdom,GBR,1985,489285164271.047 +United Kingdom,GBR,1986,601452653180.885 +United Kingdom,GBR,1987,745162608269.325 +United Kingdom,GBR,1988,910122732123.799 +United Kingdom,GBR,1989,926884816753.927 +United Kingdom,GBR,1990,1093169389204.55 +United Kingdom,GBR,1991,1142797178130.51 +United Kingdom,GBR,1992,1179659529659.53 +United Kingdom,GBR,1993,1061388722255.55 +United Kingdom,GBR,1994,1140489745944.29 +United Kingdom,GBR,1995,1320255641470.73 +United Kingdom,GBR,1996,1392979719188.77 +United Kingdom,GBR,1997,1537090700720.37 +United Kingdom,GBR,1998,1623564094070.88 +United Kingdom,GBR,1999,1652167933991.26 +United Kingdom,GBR,2000,1635441065214.1 +United Kingdom,GBR,2001,1613034403339.57 +United Kingdom,GBR,2002,1757571942446.04 +United Kingdom,GBR,2003,2028488163265.31 +United Kingdom,GBR,2004,2389004027828.63 +United Kingdom,GBR,2005,2508103636363.64 +United Kingdom,GBR,2006,2678277828886.84 +United Kingdom,GBR,2007,3063005202080.83 +United Kingdom,GBR,2008,2875463235294.12 +United Kingdom,GBR,2009,2367127278392.27 +United Kingdom,GBR,2010,2429680444512.61 +United Kingdom,GBR,2011,2608824654243.59 +United Kingdom,GBR,2012,2646002634059.62 +United Kingdom,GBR,2013,2719509472492.7 +United Kingdom,GBR,2014,2998833559195.71 +United Kingdom,GBR,2015,2861090726739.55 +United Kingdom,GBR,2016,2618885692029.18 +United States,USA,1960,543300000000 +United States,USA,1961,563300000000 +United States,USA,1962,605100000000 +United States,USA,1963,638600000000 +United States,USA,1964,685800000000 +United States,USA,1965,743700000000 +United States,USA,1966,815000000000 +United States,USA,1967,861700000000 +United States,USA,1968,942500000000 +United States,USA,1969,1019900000000 +United States,USA,1970,1075884000000 +United States,USA,1971,1167770000000 +United States,USA,1972,1282449000000 +United States,USA,1973,1428549000000 +United States,USA,1974,1548825000000 +United States,USA,1975,1688923000000 +United States,USA,1976,1877587000000 +United States,USA,1977,2085951000000 +United States,USA,1978,2356571000000 +United States,USA,1979,2632143000000 +United States,USA,1980,2862505000000 +United States,USA,1981,3210956000000 +United States,USA,1982,3344991000000 +United States,USA,1983,3638137000000 +United States,USA,1984,4040693000000 +United States,USA,1985,4346734000000 +United States,USA,1986,4590155000000 +United States,USA,1987,4870217000000 +United States,USA,1988,5252629000000 +United States,USA,1989,5657693000000 +United States,USA,1990,5979589000000 +United States,USA,1991,6174043000000 +United States,USA,1992,6539299000000 +United States,USA,1993,6878718000000 +United States,USA,1994,7308755000000 +United States,USA,1995,7664060000000 +United States,USA,1996,8100201000000 +United States,USA,1997,8608515000000 +United States,USA,1998,9089168000000 +United States,USA,1999,9660624000000 +United States,USA,2000,10284779000000 +United States,USA,2001,10621824000000 +United States,USA,2002,10977514000000 +United States,USA,2003,11510670000000 +United States,USA,2004,12274928000000 +United States,USA,2005,13093726000000 +United States,USA,2006,13855888000000 +United States,USA,2007,14477635000000 +United States,USA,2008,14718582000000 +United States,USA,2009,14418739000000 +United States,USA,2010,14964372000000 +United States,USA,2011,15517926000000 +United States,USA,2012,16155255000000 +United States,USA,2013,16691517000000 +United States,USA,2014,17393103000000 +United States,USA,2015,18036648000000 +United States,USA,2016,18569100000000 +Uruguay,URY,1960,1242289212.04285 +Uruguay,URY,1961,1547388812.89969 +Uruguay,URY,1962,1710004464.40897 +Uruguay,URY,1963,1539681533.63786 +Uruguay,URY,1964,1975701727.50862 +Uruguay,URY,1965,1890769326.14221 +Uruguay,URY,1966,1809183974.52669 +Uruguay,URY,1967,1597721080.00991 +Uruguay,URY,1968,1593675330.16467 +Uruguay,URY,1969,2004435483.87097 +Uruguay,URY,1970,2137096774.19355 +Uruguay,URY,1971,2807258064.51613 +Uruguay,URY,1972,2189418001.37898 +Uruguay,URY,1973,3964295672.52444 +Uruguay,URY,1974,4090209681.97172 +Uruguay,URY,1975,3538283322.07726 +Uruguay,URY,1976,3667161241.48372 +Uruguay,URY,1977,4114667062.64917 +Uruguay,URY,1978,4910257282.93153 +Uruguay,URY,1979,7181185277.98651 +Uruguay,URY,1980,10163020115.7344 +Uruguay,URY,1981,11048335541.4933 +Uruguay,URY,1982,9178802162.6616 +Uruguay,URY,1983,5102281255.99986 +Uruguay,URY,1984,4850241442.17643 +Uruguay,URY,1985,4732017873.38369 +Uruguay,URY,1986,5880112788.40947 +Uruguay,URY,1987,7367494080.40014 +Uruguay,URY,1988,8213515458.51139 +Uruguay,URY,1989,8438951476.06644 +Uruguay,URY,1990,9298839655.23139 +Uruguay,URY,1991,11206193313.0458 +Uruguay,URY,1992,12878157305.7481 +Uruguay,URY,1993,15002144584.3663 +Uruguay,URY,1994,17474578502.4346 +Uruguay,URY,1995,19297663096.5506 +Uruguay,URY,1996,20515465834.0682 +Uruguay,URY,1997,23969746849.9221 +Uruguay,URY,1998,25385928198.3212 +Uruguay,URY,1999,23983945190.6202 +Uruguay,URY,2000,22823255801.8447 +Uruguay,URY,2001,20898788416.6348 +Uruguay,URY,2002,13606494599.4261 +Uruguay,URY,2003,12045631092.5353 +Uruguay,URY,2004,13686329890.1191 +Uruguay,URY,2005,17362857683.8545 +Uruguay,URY,2006,19579457966.0538 +Uruguay,URY,2007,23410572621.533 +Uruguay,URY,2008,30366213095.4256 +Uruguay,URY,2009,31660911290.3226 +Uruguay,URY,2010,40284481661.8726 +Uruguay,URY,2011,47962439293.3697 +Uruguay,URY,2012,51264390121.4144 +Uruguay,URY,2013,57531233350.9101 +Uruguay,URY,2014,57236013077.5187 +Uruguay,URY,2015,53274304214.8174 +Uruguay,URY,2016,52419720713.7316 +Uzbekistan,UZB,1990,13360607990.6751 +Uzbekistan,UZB,1991,13677622222.2222 +Uzbekistan,UZB,1992,12941297376.0933 +Uzbekistan,UZB,1993,13099013835.5111 +Uzbekistan,UZB,1994,12899156990.6156 +Uzbekistan,UZB,1995,13350468917.4115 +Uzbekistan,UZB,1996,13948892215.5689 +Uzbekistan,UZB,1997,14744603773.5849 +Uzbekistan,UZB,1998,14988971210.8383 +Uzbekistan,UZB,1999,17078465982.0282 +Uzbekistan,UZB,2000,13760374487.51 +Uzbekistan,UZB,2001,11401351420.1718 +Uzbekistan,UZB,2002,9687951055.22541 +Uzbekistan,UZB,2003,10128112401.4248 +Uzbekistan,UZB,2004,12030023547.8807 +Uzbekistan,UZB,2005,14307509838.8053 +Uzbekistan,UZB,2006,17330833852.919 +Uzbekistan,UZB,2007,22311393927.8817 +Uzbekistan,UZB,2008,29549438883.8338 +Uzbekistan,UZB,2009,33689223673.2577 +Uzbekistan,UZB,2010,39332770928.9426 +Uzbekistan,UZB,2011,45915191189.3237 +Uzbekistan,UZB,2012,51821573338.1312 +Uzbekistan,UZB,2013,57690453460.6205 +Uzbekistan,UZB,2014,63067077178.5381 +Uzbekistan,UZB,2015,66903804142.5395 +Uzbekistan,UZB,2016,67220335569.6147 +Vanuatu,VUT,1979,119258835.335525 +Vanuatu,VUT,1980,113423181.338956 +Vanuatu,VUT,1981,98746405.3924806 +Vanuatu,VUT,1982,98144643.8965575 +Vanuatu,VUT,1983,110123779.812821 +Vanuatu,VUT,1984,135553763.982667 +Vanuatu,VUT,1985,123698506.111363 +Vanuatu,VUT,1986,118691396.764915 +Vanuatu,VUT,1987,130834145.053665 +Vanuatu,VUT,1988,148545381.418421 +Vanuatu,VUT,1989,144482170.248703 +Vanuatu,VUT,1990,158397403.041175 +Vanuatu,VUT,1991,188869985.673352 +Vanuatu,VUT,1992,196142585.014816 +Vanuatu,VUT,1993,188080374.400605 +Vanuatu,VUT,1994,219260341.050642 +Vanuatu,VUT,1995,233902114.8683 +Vanuatu,VUT,1996,245177633.168933 +Vanuatu,VUT,1997,255890221.800293 +Vanuatu,VUT,1998,262301252.769228 +Vanuatu,VUT,1999,267999225.256634 +Vanuatu,VUT,2000,272014693.050806 +Vanuatu,VUT,2001,257926881.72043 +Vanuatu,VUT,2002,262603781.799059 +Vanuatu,VUT,2003,314463144.04219 +Vanuatu,VUT,2004,364996869.129618 +Vanuatu,VUT,2005,394962552.336108 +Vanuatu,VUT,2006,439376794.094041 +Vanuatu,VUT,2007,526428309.945088 +Vanuatu,VUT,2008,607958616.143415 +Vanuatu,VUT,2009,610066628.693058 +Vanuatu,VUT,2010,700804286.224354 +Vanuatu,VUT,2011,792149700.679116 +Vanuatu,VUT,2012,781702874.106058 +Vanuatu,VUT,2013,801787555.861121 +Vanuatu,VUT,2014,814954306.971033 +Vanuatu,VUT,2015,742432131.041002 +Vanuatu,VUT,2016,773502895.927602 +"Venezuela, RB",VEN,1960,8736939393.93939 +"Venezuela, RB",VEN,1961,9058121212.12121 +"Venezuela, RB",VEN,1962,10022000000 +"Venezuela, RB",VEN,1963,10823878787.8788 +"Venezuela, RB",VEN,1964,9111000000 +"Venezuela, RB",VEN,1965,9496244444.44444 +"Venezuela, RB",VEN,1966,9984400000 +"Venezuela, RB",VEN,1967,10356422222.2222 +"Venezuela, RB",VEN,1968,11343444444.4444 +"Venezuela, RB",VEN,1969,11795044444.4444 +"Venezuela, RB",VEN,1970,12848755555.5556 +"Venezuela, RB",VEN,1971,14625295454.5455 +"Venezuela, RB",VEN,1972,15922863636.3636 +"Venezuela, RB",VEN,1973,19466279069.7674 +"Venezuela, RB",VEN,1974,28985627906.9767 +"Venezuela, RB",VEN,1975,31303581395.3488 +"Venezuela, RB",VEN,1976,36187023255.814 +"Venezuela, RB",VEN,1977,42263209302.3256 +"Venezuela, RB",VEN,1978,46426511627.907 +"Venezuela, RB",VEN,1979,55653325581.3953 +"Venezuela, RB",VEN,1980,67018023255.814 +"Venezuela, RB",VEN,1981,75367139534.8837 +"Venezuela, RB",VEN,1982,76559883720.9302 +"Venezuela, RB",VEN,1983,78540255813.9535 +"Venezuela, RB",VEN,1984,56091900000 +"Venezuela, RB",VEN,1985,57935746666.6667 +"Venezuela, RB",VEN,1986,58793864197.5309 +"Venezuela, RB",VEN,1987,45343793103.4483 +"Venezuela, RB",VEN,1988,58428406896.5517 +"Venezuela, RB",VEN,1989,42119835734.8703 +"Venezuela, RB",VEN,1990,47028010660.9808 +"Venezuela, RB",VEN,1991,51749026408.4507 +"Venezuela, RB",VEN,1992,58450099415.2047 +"Venezuela, RB",VEN,1993,58124193832.5991 +"Venezuela, RB",VEN,1994,56531046464.6465 +"Venezuela, RB",VEN,1995,74906532239.819 +"Venezuela, RB",VEN,1996,68263823148.8138 +"Venezuela, RB",VEN,1997,85843534588.6206 +"Venezuela, RB",VEN,1998,91331203433.1629 +"Venezuela, RB",VEN,1999,97976886247.3172 +"Venezuela, RB",VEN,2000,117140723529.412 +"Venezuela, RB",VEN,2001,122903960204.505 +"Venezuela, RB",VEN,2002,92893587733.6549 +"Venezuela, RB",VEN,2003,83620628582.1082 +"Venezuela, RB",VEN,2004,112453382329.615 +"Venezuela, RB",VEN,2005,145510008134.75 +"Venezuela, RB",VEN,2006,183477522123.894 +"Venezuela, RB",VEN,2007,230364012575.687 +"Venezuela, RB",VEN,2008,315953388510.678 +"Venezuela, RB",VEN,2009,329787628928.472 +"Venezuela, RB",VEN,2010,393190682070.491 +"Venezuela, RB",VEN,2011,316482190800.364 +"Venezuela, RB",VEN,2012,381286237847.667 +"Venezuela, RB",VEN,2013,371006299120.244 +Vietnam,VNM,1985,14094687820.7445 +Vietnam,VNM,1986,26336617261.6954 +Vietnam,VNM,1987,36658108340.6777 +Vietnam,VNM,1988,25423812719.4906 +Vietnam,VNM,1989,6293304840.51995 +Vietnam,VNM,1990,6471740490.99897 +Vietnam,VNM,1991,9613369548.20359 +Vietnam,VNM,1992,9866990092.18743 +Vietnam,VNM,1993,13180953965.6854 +Vietnam,VNM,1994,16286434068.4834 +Vietnam,VNM,1995,20736163924.0492 +Vietnam,VNM,1996,24657470352.545 +Vietnam,VNM,1997,26843701147.496 +Vietnam,VNM,1998,27209601995.8245 +Vietnam,VNM,1999,28683657995.128 +Vietnam,VNM,2000,33640085738.648 +Vietnam,VNM,2001,35291349197.4254 +Vietnam,VNM,2002,37947904054.452 +Vietnam,VNM,2003,42717072869.3917 +Vietnam,VNM,2004,49424107709.8946 +Vietnam,VNM,2005,57633255618.2731 +Vietnam,VNM,2006,66371664817.0436 +Vietnam,VNM,2007,77414425532.2452 +Vietnam,VNM,2008,99130304099.1274 +Vietnam,VNM,2009,106014601171.036 +Vietnam,VNM,2010,115931749697.241 +Vietnam,VNM,2011,135539487317.008 +Vietnam,VNM,2012,155820001920.492 +Vietnam,VNM,2013,171222025117.381 +Vietnam,VNM,2014,186204652922.262 +Vietnam,VNM,2015,193241108709.536 +Vietnam,VNM,2016,202615893443.73 +Virgin Islands (U.S.),VIR,1960,24200000 +Virgin Islands (U.S.),VIR,1961,25700000 +Virgin Islands (U.S.),VIR,1962,36900000 +Virgin Islands (U.S.),VIR,1963,41400000 +Virgin Islands (U.S.),VIR,1964,53800000 +Virgin Islands (U.S.),VIR,1965,66500000 +Virgin Islands (U.S.),VIR,1966,84100000 +Virgin Islands (U.S.),VIR,1967,115400000 +Virgin Islands (U.S.),VIR,1968,173800000 +Virgin Islands (U.S.),VIR,1969,211300000 +Virgin Islands (U.S.),VIR,1970,219000000 +Virgin Islands (U.S.),VIR,1971,257000000 +Virgin Islands (U.S.),VIR,1972,307100000 +Virgin Islands (U.S.),VIR,1973,351600000 +Virgin Islands (U.S.),VIR,1974,395400000 +Virgin Islands (U.S.),VIR,1975,399800000 +Virgin Islands (U.S.),VIR,1976,440000000 +Virgin Islands (U.S.),VIR,1977,461800000 +Virgin Islands (U.S.),VIR,1978,512900000 +Virgin Islands (U.S.),VIR,1979,606700032 +Virgin Islands (U.S.),VIR,1980,727800000 +Virgin Islands (U.S.),VIR,1981,821800000 +Virgin Islands (U.S.),VIR,1982,832600000 +Virgin Islands (U.S.),VIR,1983,916899968 +Virgin Islands (U.S.),VIR,1984,985400000 +Virgin Islands (U.S.),VIR,1985,990400000 +Virgin Islands (U.S.),VIR,1986,1035600000 +Virgin Islands (U.S.),VIR,1987,1147800064 +Virgin Islands (U.S.),VIR,1988,1204600064 +Virgin Islands (U.S.),VIR,1989,1343900032 +Virgin Islands (U.S.),VIR,1990,1564700032 +Virgin Islands (U.S.),VIR,1991,1671200000 +Virgin Islands (U.S.),VIR,1992,1770899968 +Virgin Islands (U.S.),VIR,1993,1996000000 +Virgin Islands (U.S.),VIR,2002,3269000000 +Virgin Islands (U.S.),VIR,2003,3453000000 +Virgin Islands (U.S.),VIR,2004,3799000000 +Virgin Islands (U.S.),VIR,2005,4439000000 +Virgin Islands (U.S.),VIR,2006,4504000000 +Virgin Islands (U.S.),VIR,2007,4803000000 +Virgin Islands (U.S.),VIR,2008,4250000000 +Virgin Islands (U.S.),VIR,2009,4203000000 +Virgin Islands (U.S.),VIR,2010,4339000000 +Virgin Islands (U.S.),VIR,2011,4239000000 +Virgin Islands (U.S.),VIR,2012,4095000000 +Virgin Islands (U.S.),VIR,2013,3764000000 +Virgin Islands (U.S.),VIR,2014,3624000000 +Virgin Islands (U.S.),VIR,2015,3765000000 +West Bank and Gaza,PSE,1994,2843300000 +West Bank and Gaza,PSE,1995,3282800000 +West Bank and Gaza,PSE,1996,3409600000 +West Bank and Gaza,PSE,1997,3759800000 +West Bank and Gaza,PSE,1998,4067800000 +West Bank and Gaza,PSE,1999,4271200000 +West Bank and Gaza,PSE,2000,4313600000 +West Bank and Gaza,PSE,2001,4003700000 +West Bank and Gaza,PSE,2002,3555800000 +West Bank and Gaza,PSE,2003,3968000000 +West Bank and Gaza,PSE,2004,4329200000 +West Bank and Gaza,PSE,2005,4831800000 +West Bank and Gaza,PSE,2006,4910100000 +West Bank and Gaza,PSE,2007,5505800000 +West Bank and Gaza,PSE,2008,6673500000 +West Bank and Gaza,PSE,2009,7268200000 +West Bank and Gaza,PSE,2010,8913100000 +West Bank and Gaza,PSE,2011,10465400000 +West Bank and Gaza,PSE,2012,11279400000 +West Bank and Gaza,PSE,2013,12476000000 +West Bank and Gaza,PSE,2014,12715600000 +West Bank and Gaza,PSE,2015,12673000000 +West Bank and Gaza,PSE,2016,13397100000 +"Yemen, Rep.",YEM,1990,5647251908.39695 +"Yemen, Rep.",YEM,1991,5930370370.37037 +"Yemen, Rep.",YEM,1992,6463649985.01648 +"Yemen, Rep.",YEM,1993,5368270614.8468 +"Yemen, Rep.",YEM,1994,4167356037.1517 +"Yemen, Rep.",YEM,1995,4258788725.44991 +"Yemen, Rep.",YEM,1996,5785685310.86668 +"Yemen, Rep.",YEM,1997,6839039029.748 +"Yemen, Rep.",YEM,1998,6325219772.93811 +"Yemen, Rep.",YEM,1999,7641101221.43876 +"Yemen, Rep.",YEM,2000,9636342274.82408 +"Yemen, Rep.",YEM,2001,9854042164.67463 +"Yemen, Rep.",YEM,2002,10693278291.8149 +"Yemen, Rep.",YEM,2003,11777768086.8693 +"Yemen, Rep.",YEM,2004,13873500887.5612 +"Yemen, Rep.",YEM,2005,16753769531.6987 +"Yemen, Rep.",YEM,2006,19081722875.3022 +"Yemen, Rep.",YEM,2007,21656517484.2538 +"Yemen, Rep.",YEM,2008,26910851361.7555 +"Yemen, Rep.",YEM,2009,25130274124.2524 +"Yemen, Rep.",YEM,2010,30906749533.221 +"Yemen, Rep.",YEM,2011,32726417878.391 +"Yemen, Rep.",YEM,2012,35401339869.3055 +"Yemen, Rep.",YEM,2013,40415233436.1767 +"Yemen, Rep.",YEM,2014,43228583935.0365 +"Yemen, Rep.",YEM,2015,37733919936.2465 +"Yemen, Rep.",YEM,2016,27317605346.0714 +Zambia,ZMB,1960,713000000 +Zambia,ZMB,1961,696285714.285714 +Zambia,ZMB,1962,693142857.142857 +Zambia,ZMB,1963,718714285.714286 +Zambia,ZMB,1964,839428571.428571 +Zambia,ZMB,1965,1082857142.85714 +Zambia,ZMB,1966,1264285714.28571 +Zambia,ZMB,1967,1368000000 +Zambia,ZMB,1968,1605857142.85714 +Zambia,ZMB,1969,1965714285.71429 +Zambia,ZMB,1970,1825285714.28571 +Zambia,ZMB,1971,1687000000 +Zambia,ZMB,1972,1910714285.71429 +Zambia,ZMB,1973,2268714285.71429 +Zambia,ZMB,1974,3121833333.33333 +Zambia,ZMB,1975,2618666666.66667 +Zambia,ZMB,1976,2746714285.71429 +Zambia,ZMB,1977,2483000000 +Zambia,ZMB,1978,2813375000 +Zambia,ZMB,1979,3325500000 +Zambia,ZMB,1980,3829500000 +Zambia,ZMB,1981,3872666666.66667 +Zambia,ZMB,1982,3994777777.77778 +Zambia,ZMB,1983,3216307692.30769 +Zambia,ZMB,1984,2739444444.44445 +Zambia,ZMB,1985,2281258064.51613 +Zambia,ZMB,1986,1661948717.94872 +Zambia,ZMB,1987,2269894736.84211 +Zambia,ZMB,1988,3713614457.83133 +Zambia,ZMB,1989,3998637681.15942 +Zambia,ZMB,1990,3285217391.30435 +Zambia,ZMB,1991,3378882352.94118 +Zambia,ZMB,1992,3181921787.7095 +Zambia,ZMB,1993,3273237853.35689 +Zambia,ZMB,1994,3656647744.24858 +Zambia,ZMB,1995,3807067121.8609 +Zambia,ZMB,1996,3597220962.00017 +Zambia,ZMB,1997,4303281932.29365 +Zambia,ZMB,1998,3537683046.02331 +Zambia,ZMB,1999,3404311976.54941 +Zambia,ZMB,2000,3600683039.73254 +Zambia,ZMB,2001,4094480988.11931 +Zambia,ZMB,2002,4193845678.17033 +Zambia,ZMB,2003,4901839731.26571 +Zambia,ZMB,2004,6221077674.77871 +Zambia,ZMB,2005,8331870169.14977 +Zambia,ZMB,2006,12756858899.2812 +Zambia,ZMB,2007,14056957976.2648 +Zambia,ZMB,2008,17910858637.9048 +Zambia,ZMB,2009,15328342303.9575 +Zambia,ZMB,2010,20265556273.582 +Zambia,ZMB,2011,23460098339.7453 +Zambia,ZMB,2012,25503370699.2015 +Zambia,ZMB,2013,28045460442.1876 +Zambia,ZMB,2014,27150630607.2032 +Zambia,ZMB,2015,21154394545.895 +Zambia,ZMB,2016,19551093303.3377 +Zimbabwe,ZWE,1960,1052990400 +Zimbabwe,ZWE,1961,1096646600 +Zimbabwe,ZWE,1962,1117601600 +Zimbabwe,ZWE,1963,1159511700 +Zimbabwe,ZWE,1964,1217138000 +Zimbabwe,ZWE,1965,1311435800 +Zimbabwe,ZWE,1966,1281749500 +Zimbabwe,ZWE,1967,1397002000 +Zimbabwe,ZWE,1968,1479599900 +Zimbabwe,ZWE,1969,1747998800 +Zimbabwe,ZWE,1970,1884206300 +Zimbabwe,ZWE,1971,2178716300 +Zimbabwe,ZWE,1972,2677729400 +Zimbabwe,ZWE,1973,3309353600 +Zimbabwe,ZWE,1974,3982161400 +Zimbabwe,ZWE,1975,4371300700 +Zimbabwe,ZWE,1976,4318372000 +Zimbabwe,ZWE,1977,4364382100 +Zimbabwe,ZWE,1978,4351600500 +Zimbabwe,ZWE,1979,5177459400 +Zimbabwe,ZWE,1980,6678868200 +Zimbabwe,ZWE,1981,8011373800 +Zimbabwe,ZWE,1982,8539700700 +Zimbabwe,ZWE,1983,7764067000 +Zimbabwe,ZWE,1984,6352125900 +Zimbabwe,ZWE,1985,5637259300 +Zimbabwe,ZWE,1986,6217523700 +Zimbabwe,ZWE,1987,6741215100 +Zimbabwe,ZWE,1988,7814784100 +Zimbabwe,ZWE,1989,8286322700 +Zimbabwe,ZWE,1990,8783816700 +Zimbabwe,ZWE,1991,8641481700 +Zimbabwe,ZWE,1992,6751472200 +Zimbabwe,ZWE,1993,6563813300 +Zimbabwe,ZWE,1994,6890675000 +Zimbabwe,ZWE,1995,7111270700 +Zimbabwe,ZWE,1996,8553146600 +Zimbabwe,ZWE,1997,8529571600 +Zimbabwe,ZWE,1998,6401968200 +Zimbabwe,ZWE,1999,6858013100 +Zimbabwe,ZWE,2000,6689957600 +Zimbabwe,ZWE,2001,6777384700 +Zimbabwe,ZWE,2002,6342116400 +Zimbabwe,ZWE,2003,5727591800 +Zimbabwe,ZWE,2004,5805598400 +Zimbabwe,ZWE,2005,5755215200 +Zimbabwe,ZWE,2006,5443896500 +Zimbabwe,ZWE,2007,5291950100 +Zimbabwe,ZWE,2008,4415702800 +Zimbabwe,ZWE,2009,8366794000 +Zimbabwe,ZWE,2010,10052045200 +Zimbabwe,ZWE,2011,12071733500 +Zimbabwe,ZWE,2012,14058378300 +Zimbabwe,ZWE,2013,15223528900 +Zimbabwe,ZWE,2014,15834069900 +Zimbabwe,ZWE,2015,16072380200 +Zimbabwe,ZWE,2016,16289212000 diff --git a/main/tests/data/jorf.xml b/main/tests/data/jorf.xml new file mode 100644 index 000000000..3838036d6 --- /dev/null +++ b/main/tests/data/jorf.xml @@ -0,0 +1,1002 @@ + +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 19902. Membres ayant voix consultativeet Mles correcteurs et examinateurs des différentes épreuves du concours +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantDenisenseignante à l'Institut national des télécommunications +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantDeryresponsable Scolarité à l'Institut national des télécommunications +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantLonguetassistante à l'université Paris-IX (Dauphine) +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 19901. Membres ayant voix délibérativeAchouchedirecteur de l'Institut national des télécommunications +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 19901. Membres ayant voix délibérativeVarlootprésident de la société Télésystèmes +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantCastaprofesseur à l'université Paris-IX (Dauphine) +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantGoulvestredirecteur des études de la section Gestion de l'Institut national des télécommunications +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantU.E.R. de deuxième cycle Gestion de l'université Paris-IX (Dauphine)Directeur de l' +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le chef du service du personnel de la direction générale des télécommunications ou son représentantPecouldirecteur de la section Gestion de l'Institut national des télécommunications +membre du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunicationsArrêteArt. 1er. - La composition du jury du concours d'admission en première et deuxième année de la section Gestion de l'Institut national des télécommunications est fixée comme suit pour l'année 1990M. le représentant du syndicat des industries du téléphone, du télégraphe et de leurs applications télématiquesInstitutReprésentant de l'association des anciens élèves de l'national des télécommunications +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du ministre chargé de la santéLe directeur général de la santé ou son représentant, présidentLe sous-directeur des professions de santé ou son représentantLefèvre-Paul (Lina)inspecteur général des affaires sociales, Représentants du ministre chargé des universités +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du ministre chargé de la santéLe directeur général de la santé ou son représentant, présidentLe sous-directeur des professions de santé ou son représentantLefevre-Paul (Lina)inspecteur général des affaires sociales +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du ministre chargé des universitésLe sous-directeur des enseignements universitaires ou son représentantForest (Nadine)Professeurprésidente de l'université Paris-VII +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé de la santéLe directeur général de la santé ou son représentant, présidentLe sous-directeur des professions de santé ou son représentantLefèvre-Paul (Lina)inspecteur général des affaires sociales +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsReprésentantes de l'Organisation nationaledes syndicats de sages-femmesBergera (Suzanne)membre de l'Organisation nationale des syndicats de sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsReprésentantes de l'Organisation nationaledes syndicats de sages-femmesForestier (Rolande)présidente de l'Organisation nationale des syndicats de sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsReprésentantes du Conseil nationalde l'ordre des sages-femmesAtechian (Nicole)membre du Conseil national de l'ordre des sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsReprésentantes du Conseil nationalde l'ordre des sages-femmesHaefele (Marie-Madeleine)membre du Conseil national de l'ordre des sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsReprésentantes du Conseil nationalde l'ordre des sages-femmesRigal (Christiane)membre du Conseil national de l'ordre des sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentante de l'Union nationaledes syndicats de sages-femmesPouvreau (Yvonne)membre de l'Union nationale des syndicats de sages-femmes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des universitésLe sous-directeur des enseignements technologiques ou son représentantCerf (Claudine)sage-femme, directrice de l'école de sages-femmes du centre médico-chirurgical Foch +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentant de la Confédérationdes syndicats médicaux françaisPayennevilleDocteurmembre de la Confédération des syndicats médicaux français +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentant de la Fédération des médecins de FranceWisner (Jean-Pierre)Docteurmembre de la Fédération des médecins de France +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentant de la Fédération françaisedes médecins généralistes M.G. FranceVedrine (Bernard)Docteurvice-président de la Fédération française des médecins généralistes M.G. France +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du Conseil nationalde l'ordre des médecinsBailly (Pierre)Docteurmembre du Conseil national de l'ordre des médecins +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du Conseil nationalde l'ordre des médecinsJung (François)Docteurmembre du Conseil national de l'ordre des médecins +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du Conseil nationalde l'ordre des médecinsKlepping (Alfred)Professeurmembre du Conseil national de l'ordre des médecins +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du ministre chargé de la santéLe directeur général de la santé ou son représentant, présidentLe sous-directeur des professions de santé ou son représentantLe sous-directeur des enseignements universitaires ou son représentantBonfils (Serge)ProfesseurU.F.R. de médecine Xavier-Bichat de l'université Paris-VII +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du ministre chargé de la santéLe directeur général de la santé ou son représentant, présidentLe sous-directeur des professions de santé ou son représentantLe sous-directeur des enseignements universitaires ou son représentantMalvy (Paul)ProfesseurU.F.R. de médecine de l'université de Nantes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19721o Commission compétente pour les médecinsReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsLe sous-directeur des naturalisations ou son représentantReprésentant de l'Académie nationale de médecineOrcel (Louis)Professeur +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentant de la Confédération nationaledes syndicats dentairesBabaretDocteurmembre de la Confédération nationale des syndicats dentaires +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentant de la Confédération nationaledes syndicats dentairesErnouf (Georges-Eric)Docteurmembre de la Confédération nationale des syndicats dentaires +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentant de la Fédération odontologique de France et des territoires associésMeyer (Roger)Docteurprésident de la Fédération odontologique de France et des territoires associés +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du Conseil nationalde l'ordre des médecins et des chirurgiens-dentistesBeck (René)Docteurprésident du Conseil national de l'ordre des chirurgiens-dentistes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du Conseil nationalde l'ordre des médecins et des chirurgiens-dentistesOlive (André)Docteurmembre du Conseil national de l'ordre des chirurgiens-dentistes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du Conseil nationalde l'ordre des médecins et des chirurgiens-dentistesRicard (Roger)Docteurmembre du Conseil national de l'ordre des chirurgiens-dentistes +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du ministre chargé des relations extérieuresLe chef de service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsLe sous-directeur des naturalisations ou son représentantReprésentant de l'Académie nationale de chirurgie dentaireWeill (Robert)président de l'Académie nationale de chirurgie dentaire +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19722o Commission compétente pour les chirurgiens-dentistesReprésentants du ministre chargé des universitésLe sous-directeur des enseignements universitaires ou son représentantBorelProfesseurdoyen de l'U.F.R. d'odontologie de l'université Clermont-Ferrand-I +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des relations extérieuresLe chef du service de coopération et de développement ou son représentantLe sous-directeur des étrangers en France ou son représentantLe sous-directeur de la recherche et de la technologie ou son représentantReprésentant du ministre chargé de la sécurité socialeLe directeur de la sécurité sociale ou son représentantReprésentant du ministre chargé des naturalisationsLe sous-directeur des naturalisations ou son représentantReprésentant de l'Académie nationale de médecineSureau (Claude)Professeurmembre de l'Académie nationale de médecine +13 juillet 1972Membre des commissions prévues par l'article L. 356 du code de la santé publique, complété par l'article 1er (II) de la loi no 72-661 du 13 juillet 19723o Commission compétente pour les sages-femmesReprésentants du ministre chargé des universitésLe sous-directeur des enseignements technologiques ou son représentantBarrat (Jacques)Professeurdirecteur de l'école de sages-femmes de l'hôpital Saint-Antoine +dans la 2e section du cadre des officiers générauxpar anticipation etLe Mer (André, Marie)Médecin général inspecteur, médecin chef des services hors classe +dans la 2e section du cadre des officiers générauxManent (Pierre, Jean-Paul)Médecin chef des services de classe normale +dans la 2e section du cadre des officiers généraux du service de santé des arméesAu grade de général de brigadeMercier (Jean, Maurice)Colonel du corps technique et administratif du service de santé des armées +Laurent (Bernard, Georges)Ingénieur général de 2e classe de l'armementchef du bureau des affaires internationales de la direction des engins +dans la 1re section du cadre des ingénieurs généraux des études et techniques d'armementAu grade d'ingénieur général de 2e classeSalaun (François)Ingénieur en chef de 1re classeMaintenu dans ses fonctions +dans la 2e section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeClerte (Jean)Ingénieur en chef de l'armement +Bertrandias (Yves, Louis)Ingénieur général de 1re classe de l'armementchargé de mission auprès du directeur des constructions navales +Lieutaud (Bernard)Ingénieur général de 2e classe de l'armementadjoint au délégué aux relations internationales +dans la 2e section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeGuillarm (Jean-Claude, René)Ingénieur en chef de l'armement +dans la 2e section du cadre des officiers du corps technique et administratif de l'armementAu grade d'officier général de 2e classeAudo (Pierre, Jean, Joseph)Officier en chef de 1re classe +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeDuval (Emmanuel, Paul, Michel, Marie)Ingénieur en chef de l'armementMaintenu dans ses fonctions +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classePianasso (Daniel, Henri, Joseph)Ingénieur en chef de l'armement +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 1re classeCharlet (Guy, Emile, Denis, Gaston)Ingénieur général de 2e classe de l'armementMaintenu dans ses fonctions +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeSalles (Pierre)Ingénieur en chef de l'armementMaintenu dans ses fonctions +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeMaintenu dans ses fonctionsGautier (Jean-Jacques)Ingénieur en chef de l'armementMaintenu en position de service détaché +Chanut (François, Charles, Henri)Ingénieur général de 2e classe de l'armementplacé en service détaché auprès de la Société européenne de propulsiondans les cadresà la même dateadmis par anticipation etdans la 2e section du cadre des ingénieurs généraux de l'armement +Davenas (Alain, Yves, Etienne, Firmin)Ingénieur général de 2e classe de l'armementplacé en service détaché auprès de la Société nationale des poudres et explosifsdans les cadresà la même dateadmis par anticipation etdans la 2e section du cadre des ingénieurs généraux de l'armement +dans la 1re section du cadre des contrôleurs généraux des arméesau grade de contrôleur général des arméesCharron (Bernard, René, Pierre)Contrôleur des armées +dans la 1re section du cadre des contrôleurs généraux des arméesau grade de contrôleur général des arméesMattheos (Louis, Jérôme)Contrôleur des armées +dans la 1re section du cadre des contrôleurs généraux des arméesau grade de contrôleur général des arméesRamsay (Michel)Contrôleur des armées +dans la 1re section du cadre des officiers généraux de l'armée de terreAnglard (Marc, Camille, Robert)Général de division +dans la 1re section du cadre des officiers généraux de l'armée de terreMonchal (Amédée, Marc, Yves)Général de division +dans la 1re section du cadre des officiers généraux de l'armée de terreSevrin (Michel, Marie, Emile, Joseph)Général de division +Rinaudo (Jacques, Alfred, Eugène)Général de brigadechargé de mission auprès du général chef d'état-major des armées +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeDoussineau (Claude, Auguste)Colonel de l'infanterie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeLajouanie (Michel, André, Marie, Joseph)Colonel de l'arme blindée et de la cavalerie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeAurousseau (André, Joseph, Louis, Emile)Colonel de l'arme blindée et de la cavalerie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadePacaud (Patrick, Jean, Gilbert)Colonel des troupes de marine +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeKandel (Jacques, Marie, Michel)Colonel du cadre spécial +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadePostic (Eugène, Louis)Colonel de l'infanterie +Mascaro (René, Antoine, Jean)Général de brigadecommandant la brigade logistique du 2e corps d'armée +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeButel (Jacques)Colonel du train +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeRimaud (Bertrand, Pierre, Marie, Joseph)Colonel de l'arme blindée et de la cavalerie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeBoulard de Gatellier (Henri, Marie, Joseph)Colonel de l'arme blindée et de la cavalerie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeBlock (Albert, Germain)Colonel du train de +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeGovys (Charles, André)Colonel de l'infanterie +dans la 2e section du cadre des officiers généraux de la marineAu grade de contre-amiralNourry (Jean-Pierre, Marie, Victor, Bernard)Capitaine de vaisseau +dans la 2e section du cadre des officiers généraux de la marineAu grade de contre-amiralAlbouy (Jean, Pierre, Hervé, Maximilien)Capitaine de vaisseau +dans la 2e section du cadre des officiers généraux de la marineAu grade de contre-amiralRichard (Guy, André, Charles)Capitaine de vaisseau +dans la 2e section du cadre des officiers généraux de la marineAu grade de contre-amiralMiguet (Jean)Capitaine de vaisseau +chargée de mission au cabinet du Président de la Républiqueappelée à d'autres fonctionsDominique Hernu +GeronimiProcureur général près la cour d'appel d'AgenJean Volffprocureur de la République près le tribunal de grande instance de MulhouseDupuis +Procureur général près la cour d'appel d'Aix-en-ProvenceClaude Salavagioneprocureur de la République près le tribunal de grande instance de MarseilleBrun +GeronimiProcureur général près la cour d'appel de BastiaMichel Percevalavocat général près la cour d'appel de VersaillesMonestie +GeronimiProcureur général près la cour d'appel de DijonJean Stefaniprocureur de la République près le tribunal de grande instance de NiceEstrangin +Procureur général près la cour d'appel de DouaiJean Geronimiprocureur général près la cour d'appel de RouenMoyal +GeronimiProcureur général près la cour d'appel de NîmesMonique Guemannavocat général près la cour d'appel de MontpellierDufour +Procureur général près la cour d'appel de RennesJacques Brunprocureur général près la cour d'appel de RiomGalmiche +GeronimiProcureur général près la cour d'appel de RiomAndré Carolprocureur de la République près le tribunal de grande instance de GrenobleBrun +Procureur général près la cour d'appel de RouenJean-Pierre Monestieprocureur général près la cour d'appel de Bastia +Premier président de la cour d'appel d'Aix-en-ProvenceHenri Boulardprésident du tribunal de grande instance de Créteil +Mignucciadmisà faire valoir ses droits à la retraitePremier président de la cour d'appel de MetzStéphane Lapeireprésident du tribunal de grande instance de NancyMonboisse +Mignucciadmisà faire valoir ses droits à la retraitePremier président de la cour d'appel de NancyJean-Claude Girousseprésident du tribunal de grande instance d'Aix-en-ProvenceSellier +Mignucciadmisà faire valoir ses droits à la retraitePremier président de la cour d'appel de PauJean-Pierre Pechprésident du tribunal de grande instance de ToulouseBerlioz +conseillers à la Cour de cassationJoëlle Fossereauprésident de chambre à la cour d'appel de VersaillesDevouassoud +conseillers à la Cour de cassationJean Apollisinspecteur général adjoint des services judiciairesBernard de Saint Affrique +conseillers à la Cour de cassationClaudius Bertheasprésident de la chambre d'accusation à la cour d'appel de ParisBenhamou +conseillers à la Cour de cassationStéphane Boittiauxprocureur de la République près le tribunal de grande instance de NanterreLe Tallec +conseillers à la Cour de cassationCamille Carliozprésident de chambre à la cour d'appel de LyonDiemer +conseillers à la Cour de cassationGérard Gelineau-Larrivetprésident de chambre à la cour d'appel de ParisBonodeau +conseillers à la Cour de cassationPierre Leclercqprésident de chambre à la cour d'appel de Paris, en position de disponibilitéGoudet +conseillers à la Cour de cassationRené Monboissepremier président de la cour d'appel de MetzDutheillet-Lamonthezie +Anne Coudert, épouse Credevilleconseiller référendaire à la Cour de cassation, second grade, second groupeconseiller référendaire à ladite cour, premier grade, second groupeHerbecq +Bernard Dutheillet-Lamonthezieconseiller à la Cour de cassationprésident de chambre à ladite courAuboin +chargée de mission au cabinet du ministre délégué auprès du ministre de l'équipement, du logement, des transports et de la merchargé du logementNoëlle de la Bevière +dans la 2e section du cadre des officiers généraux de l'armée de l'air par anticipation etLemarchal (Francis, Louis)Général de brigade aérienne +dans la 2e section du cadre des officiers généraux de l'armée de l'air par anticipation etBeaugrand (Jacques)Général de brigade aérienne +dans la 2e section du cadre des officiers généraux de l'armée de l'air par anticipation etGellé (Henri, Louis, Marie, Joseph)Général de brigade aérienne +dans la 1re section du cadre des officiers généraux de l'armée de l'air àVougny (Philippe, André, Gaston)Général de corps aérien +au titre du congé du personnel navigant de l'armée de l'airAu grade de général de brigade aérienneHeimburger (Henri)Colonel du corps des officiers de l'air +au titre du congé du personnel navigant de l'armée de l'airAu grade de général de brigade aérienneRomary (Marc)Colonel du corps des officiers de l'air +dans la 2e section du cadre des officiers généraux de l'armée de l'airAu grade de commissaire général de brigade aérienneDujardin (Jacques, Alain, Lucien)Commissaire colonel du corps des commissaires de l'air +dans la 2e section du cadre des officiers généraux de l'armée de l'airAu grade de général de brigade aérienneGilles (Pierre, Joseph, Emile)Colonel du corps des officiers de l'air +dans la 2e section du cadre des officiers généraux de l'armée de l'airAu grade de général de brigade aérienneSouque (Pierre, Henri)Colonel du corps des officiers des bases de l'air +aux fonctions exercéesconseiller technique au cabinet du ministre de l'agriculture et de la forêt parAlain Bergerappelé à d'autres fonctions +Gilles Pelsyconseiller technique au cabinet du ministre de l'agriculture et de la forêt +Guy-Noël Lebeldirecteur à la Délégation à l'aménagement du territoire et à l'action régionaleClaire Bazy-Malaurie +Georges Fillioudconseiller d'Etatprésident du conseil d'administration de l'Institut national de l'audiovisuel +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Becdelievre (Claire) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Benaben (Marie- Pierre) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Hakim (Géraldine) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Le Boulho (Isabelle) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Puget (Michèle) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Tordjman (Jeanine) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Urbani (Isabelle) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Allouch (Elie) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Balme (Cyril) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Bois (Patrick) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Langlois (Frédéric) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Lourde Rocheblave (Henri) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Moritz (Richard) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Pineau (Laurent) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Sarrazin (Eric) +élèves à la suite des épreuves du second concours d'entrée au Centre national d'études supérieures de sécurité sociale de 1989Vaille (Christophe) +René Couveinhes tendant à la création d'une commission d'enquête chargée de déterminer dans quelles conditionsà partir de quels éléments la décision de fermer l'étang de Thau et d'interdire la vente des huîtres et des moules qui ya été prise le13 décembre 1989(renvoyée à la commission des lois)
      • No 1170. - Rapport deRobert Savyau nom de la commission des lois, en nouvelle lecture, sur le projet de loi (no 1114), modifié par le Sénat en deuxième lecture, relatif à la limitation des dépenses électorales et à la clarification du financement des activités politiques
      • No 1176. - Rapport deAlain Richardrapporteur général, au nom de la commission des finances, en vue de la lecture définitive du projet de loi de finances rectificative pour 1989 (no 1175)
      +conseiller techniqueAnnick Moreladministrateur civil, au cabinet du ministre de la solidarité, de la santé et de la protection sociale +dans la 2e section du cadre des officiers générauxpar anticipation etNivlet (Léon)Général de division +dans la 2e section du cadre des officiers générauxAu grade de général de brigadeJammet (Maurice, Jean, François)Colonel +dans la 2e section du cadre des officiers générauxAu grade de général de brigadeTeisseyre (Gérard, Louis, Urbain)Colonel +Pérennez (Paul, Joseph)Général de brigadecommandant la gendarmerie outre-mer +secrétaire général du Conseil économique et socialFrançois Lavondes +Bourdin (Josselyne), épouse Texiergreffier au tribunal d'instance de Saint-Amand-Montrondrégisseur titulaireMaugenest +Moreau (Sylvie), épouse Garciaagent technique de bureau au tribunal d'instance de Saint-Amand-Montrondrégisseur suppléantBresle +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportAu titre du transport urbain de personnesLucas (Viviane)Betrancourt (Georges) +à la commission des transports de personnes du Conseil national des transportsélus locauxMaires ou présidents d'autorités organisatrices de transportChantemesse (Guy)Magre (André) +à la commission des transports de personnes du Conseil national des transportsélus locauxMaires ou présidents d'autorités organisatrices de transportDethier (André)vice-président du syndicat intercommunal de l'agglomération de Nevers +à la commission des transports de personnes du Conseil national des transportsreprésentants de l'EtatReprésentant du ministre chargé des transports aériensReprésentant du ministre chargé de l'économie, Représentant du ministre chargé du tourismeMichaud (Jean-Luc)directeur des industries touristiquesFrangialli (Francesco) +à la commission des transports de personnes du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de personnesBlanc (Christian)président-directeur général de la R.A.T.PRousselot (Michel) +à la commission des transports de personnes du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de personnesAu titre du transport urbainBaronnet-Fruges (Francis)Dossot (Jack) +à la commission des transports de personnes du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de personnesAu titre du transport urbainCouplan (Jean-Pierre)vice-président de l'Union des transports publics; Suppléant +à la commission des transports de personnes du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de personnesAu titre du transport urbainLaferrere (Dominique)membre du bureau de l'U.T.P +à la commission des transports de personnes du Conseil national des transportsreprésentants des syndicats représentatifs, Au titre du transport maritimeBilien (Robert)(C.G.T.)Lagain (François) +à la commission des transports de personnes du Conseil national des transportsreprésentants des usagersdes transports de personnesAllante (Charles)Bommelaer (André) +à la commission des transports de personnes du Conseil national des transportsreprésentants des usagersdes transports de personnesAmiral Lacaille (Jean)secrétaire général de l'Union routière de France +à la commission sociale et de la sécurité du Conseil national des transportsélus locauxMaire ou président d'autorité organisatricedes transports urbainsBoissiéras (Jacques)vice-président de la communauté urbaine de Bordeaux +à la commission sociale et de la sécurité du Conseil national des transportsélus locauxMaire ou président d'autorité organisatricedes transports urbainsNormand (Jean)président du syndicat intercommunal des transports urbains de l'agglomération rennaiseBurckel (Jean-Claude) +à la commission sociale et de la sécurité du Conseil national des transportsélus locauxreprésentants de l'EtatReprésentant du ministre chargé des transports aériensChappert (Jean)ingénieur général des ponts et chausséesVilliers (Jacques) +à la commission sociale et de la sécurité du Conseil national des transportsélus locauxreprésentants de l'EtatReprésentant du ministre chargé des transports aériensOliviero (Edmond)ingénieur en chef de l'aviation civile +à la commission sociale et de la sécurité du Conseil national des transportspersonnalités compétentes en matière socialedans les transportsTrorial (Jacques)président délégué général de l'Association pour le développement de la formation professionnelle dans les transportsAncelin (Jacques) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesAu titre du transport ferroviairePourdieu (Jean)directeur du personnel de la S.N.C.FDescoutures (Pierre) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesAu titre du transport fluvialBernière (Claude)administrateur de la chambre nationale de la batellerie artisanaleDelesalle (Achille) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportAu titre du transport urbain de personnesBes de Berc (François)membre de l'Union des transports publics, directeur du centre régional Via Transexel +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaire(C.G.T.)Dardevet (Robert)(C.G.T.)Decaillon (Joël) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaire(F.M.C.)Roche (Paul)(F.M.C.)Desorme (Jean-Claude) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport maritime(C.G.T.)Bilien (Robert)(C.G.T.)Lagain (François) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport routier urbain(F.O.)Lecoeuvre (Jean-Pierre)(F.O.)Vandecasteele (Jean) +à la section permanente du Conseil national des transportsAu titre du transport maritimeBilien (Robert)de la Fédération nationale des syndicats maritimes C.G.TLagain (François) +à la section permanente du Conseil national des transportsélus locauxMaire ou président d'autorité organisatrice de transport urbainAuxiette (Jacques)président de l'Union des transports publicsGadrat (Jean-Michel) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportAu titre du transport fluvialCarmentran (Jean-Jacques)de la Confédération générale du travail-Force ouvrière +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportAu titre du transport fluvialVandecasteele (Jean)Valladon (René) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesLefebvre (Jean)past-président du G.N.T.CBerthod (Jean-Claude) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesRouch (Robert)président du Groupement national des transports combinés; SuppléantBerthod (Jean-Claude) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesqui participent aux opérations de transportBernière (Claude)administrateur de la chambre nationale de la batellerie artisanale +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesqui participent aux opérations de transportBoussageon (François)président de l'Union des usagers de véhicules de transport privéde Laage de Meux +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesqui participent aux opérations de transportDepierre (Didier)secrétaire général de l'U.V.T.P +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants d'entreprisesqui participent aux opérations de transportFléchet (Jacques)président du comité des armateurs fluviauxDelesalle (Achille) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants des syndicats représentatifs, Au titre du transport routier et urbainDoriat (Gilbert)Carmentran (Jacques) +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportreprésentants des syndicats représentatifs, Au titre du transport routier et urbainLecoeuvre (Jean-Pierre)de la Confédération générale du travail-Force ouvrière +à la section permanente du Conseil national des transportsreprésentants de l'EtatReprésentants du ministre chargé de l'économie, Représentant du ministre chargé des transports aériensChappert (Jean)ingénieur général des ponts et chausséesVilliers (Jacques) +membres de la commission des transports de marchandises du Conseil national des transportsAu titre du transport fluvial, du transport aérien et du transport maritime(C.G.T.)Belhaire (Bernard)(F.O.)Lagain (François)Valladon (René) +membres de la commission des transports de marchandises du Conseil national des transportsAu titre du transport fluvial, du transport aérien et du transport maritime(C.G.T.)Bilien (Robert)(C.G.T.)Lagain (François) +membres de la commission des transports de marchandises du Conseil national des transportsAu titre du transport fluvial, du transport aérien et du transport maritime(C.G.T.)Le Moal (Daniel)(F.O.)Lagain (François) +membres de la commission des transports de marchandises du Conseil national des transports(F.M.C.) et Vieilleville (Roland) (F.G.A.A.C.)Roche (Paul)(F.M.C.)Desorme (Jean-Claude) +membres de la commission des transports de marchandises du Conseil national des transportspersonnalités compétentesen matière de transports de marchandisesBerthod (Jean-Claude)président du comité de liaison des transports et de la manutentionMory (Jean-Nicolas) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants de l'EtatReprésentant du ministre chargé de la posteDi Maggio (Antoine)chargé de la sous-direction Exploitation à la direction des services Courrier de la direction générale de la posteHely (Rémy) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants de l'EtatReprésentant du ministre chargé des transports aériensChappert (Jean)ingénieur général des ponts et chausséesVilliers (Jacques) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants de l'EtatReprésentants du ministre chargé de l'économie, Représentant du ministre chargé du commerce extérieurMoulin (Gérard)sous-directeur aux affaires multilatérales de la direction des relations économiques extérieuresLefas (Patrick) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport combiné rail-routeLefebvre (Jean)past-président du G.N.T.CBerthod (Jean-Claude) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport combiné rail-routeRouch (Robert)président du Groupement national des transports combinés; SuppléantBerthod (Jean-Claude) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport ferroviaireMarcillet (Jérôme)directeur adjoint de la direction commerciale fret S.N.C.FBernadet (Jean-Paul) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport fluvialBernière (Claude)administrateur de la chambre nationale de la batellerie artisanaleDelesalle (Achille) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants des syndicats représentatifs, Au titre du transport ferroviaireArraez (Louis)(C.G.T.) +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants des syndicats représentatifs, Au titre du transport ferroviaireDardevet (Robert)(C.G.T.)Decaillon (Joël) +à la section permanente du Conseil national des transportsAu titre du transport maritimeSuppléantGiboteau (Françoise)(C.G.T.) +à la commission des transports de personnes du Conseil national des transportsélus locauxSuppléantBoissiéras (Jacques)vice-président de la communauté urbaine de Bordeaux +à la commission des transports de personnes du Conseil national des transportsélus locauxSuppléantCorajoud (Jean-Louis)président du syndicat intercommunal des transports urbains de l'agglomération annecienne +à la commission des transports de personnes du Conseil national des transportsélus locauxSuppléantNormand (Jean)président du syndicat intercommunal des transports urbains de l'agglomération rennaiseBurckel (Jean-Claude) +à la commission des transports de personnes du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de personnesSuppléantCronier (Robert)directeur attaché à la direction générale de la R.A.T.P +à la commission des transports de personnes du Conseil national des transportsreprésentants des usagersSuppléantFournier-Perilhou (Jean)directeur général de l'Association française des automobiles-clubs +à la commission sociale et de la sécurité du Conseil national des transportspersonnalités compétentes en matière socialedans les transportsSuppléantde Marne (Jehan)secrétaire général du comité de liaison des transports et de la manutention +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesAu titre du transport ferroviaireSuppléantBreau (Alain)directeur général de la S.C.E.T.A +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesAu titre du transport fluvialSuppléantLamot (Adolphe)président de la C.N.B.A +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportSuppléantBaudot (Roger)chef du service Administration et réglementation de la R.A.T.P +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportSuppléantBlanc (Christian)président-directeur général de la R.A.T.P +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportSuppléantBourgeois (Guy)délégué général de l'U.T.P +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants d'entreprisesconcourant à l'activité de transportSuppléantCronier (Robert)Baudot (Roger) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaireSuppléantBesson (Quentin)(C.G.T.) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaireSuppléantDelpeuch (Jean-Michel)(F.M.C.) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaireSuppléantDenise (Jean-Luc)(F.G.A.A.C.) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaireSuppléantGuillot (Robert)(F.G.A.A.C.)Vieilleville (Roland) +à la commission sociale et de la sécurité du Conseil national des transportsreprésentants des syndicats représentatifs, au plan national, des salariés dans le secteur des transportsAu titre du transport ferroviaireSuppléantHontang (Michel)(F.G.A.A.C.) +à la section permanente du Conseil national des transportsélus locauxMaire ou président d'autorité organisatrice de transport urbainSuppléantFeugère (Serge)président du syndicat intercommunal des transports urbains de l'agglomération roannaise +à la section permanente du Conseil national des transportspersonnalités compétentes en matière de transportSuppléantValladon (René)(F.O.) +à la section permanente du Conseil national des transportsreprésentants de l'EtatReprésentants du ministre chargé de l'économie, Représentant du ministre chargé des transports aériensSuppléantWeishaupt (Gabriel)ingénieur général de l'aviation civile +membres de la commission des transports de marchandises du Conseil national des transportspersonnalités compétentesen matière de transports de marchandisesSuppléantTrorial (Jacques)président délégué général de l'A.F.T +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants de l'EtatReprésentant du ministre chargé des transports aériensSuppléantWeishaupt (Gabriel)ingénieur général de l'aviation civile +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants de l'EtatReprésentants du ministre chargé de l'économie, Représentant du ministre chargé du commerce extérieurSuppléantDelleur (Philippe)chef du bureau des services à la D.R.E.E +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport ferroviaireSuppléantPeter (Jacques)directeur du Sernam +membres de la commission des transports de marchandises du Conseil national des transportsreprésentants d'entreprises concourantà l'activité de transport de marchandisesAu titre du transport fluvialSuppléantLamot (Adolphe)président de la C.N.B.A +membres de la commission des transports de marchandises du Conseil national des transportsSuppléantHontang (Michel)(F.G.A.A.C.) +Lizzit (Alain)sous-directeur au centre de détention d'Oermingensur placeau grade de directeur de 2e classe +Page (André)sous-directeur à la maison d'arrêt d'Amienssur placeau grade de directeur de 2e classe +Bianchi (Francis)sous-directeur à la maison d'arrêt de Lyonsur placeau grade de directeur de 2e classe +Gass (Armand)sous-directeur au centre de détention d'Eyssessur placeau grade de directeur de 2e classe +Guittard (Serge)sous-directeur au centre pénitentiaire de Fresnessur placeau grade de directeur de 2e classe +Katz (Patrice)sous-directeur à la maison d'Arrêt de Fleury-Mérogissur placeau grade de directeur de 2e classe +Sellon (Jean-Claude)sous-directeur au centre pénitentiaire de Perpignansur placeau grade de directeur de 2e classe +Pierre Billoir (Constituants élémentaires, milieux dilués et optiques)professeurs des universités (disciplines scientifiques), dans les établissements d'enseignement supérieur +Tupignon (Marie-France), épouse Nabetcommis au tribunal de grande instance d'Evreuxrégisseur suppléantGosse +chargé des anciens combattants et des victimes de guerrechargé des relations avec le ParlementPaule Quilichini au cabinet du secrétaire d'Etat +au cabinet du secrétaire d'Etat chargé des anciens combattants et des victimes de guerreAttachée parlementaireAnne Cussinet +Alain Viviendéputédans le cadre des dispositions de l'article L.O. 144 du code électoral susvisé, une mission temporaire auprès du Premier ministre +Chilly (Carole), épouse Tentelieragent technique de bureau au tribunal d'instance de Clichyrégisseur titulaireLeroux +Roussel (Anne)greffier en chef du tribunal d'instance de Clichyrégisseur suppléantTentelier +Berthier (Monique), épouse Bissoncommis au tribunal de grande instance de Melunrégisseur titulaireVejus +Bertrand (Marc)greffier au tribunal de grande instance de Nancyrégisseur titulaireThouvenot +Raffin (Brigitte)commis à la cour d'appel de Chambéryrégisseur suppléantde Bouclans +Thoraval (Agnès), épouse Fontainesténodactylographe au tribunal d'instance de Paris (15e)régisseur suppléantBourdon +Vidal (Marie-Claire), épouse Boescommis au tribunal de grande instance de Carpentrasrégisseur suppléantCampos +Xavier Henry de Villeneuveprésident du conseil d'administration de la Banque de Bretagne +membres de la commission permanente du Conseil supérieur de la prud'homiereprésentants des salariés1oSur proposition de la Confédération françaisedémocratique du travail (C.F.D.T.)TitulaireBattut (Catherine)Aubron (Marcel) +membres du Conseil supérieur de la prud'homiereprésentants des employeurs1oSur proposition du Conseil nationaldu patronat français (C.N.P.F.)TitulaireTellier (Dominique)Albert-Sorel (André) +membres du Conseil supérieur de la prud'homiereprésentants des employeurs2oSur proposition de la Confédération généraledes petites et moyennes entreprises (C.G.P.M.E.)TitulaireAlbert-Sorel (André)Blanchard (Guy) +membres de la commission permanente du Conseil supérieur de la prud'homiereprésentants des salariés2oSur proposition de la Confédération généraledu travail Force ouvrière (C.G.T.-F.O.)SuppléantLopez-Rivoire (Véronique)Dumont (Edwige) +membres du Conseil supérieur de la prud'homiereprésentants des employeurs1oSur proposition du Conseil nationaldu patronat français (C.N.P.F.)SuppléantFranchini (Michèle)Tellier (Dominique) +membres de la commission permanente du Conseil supérieur de la prud'homiereprésentants des salariés1oSur proposition de la Confédération françaisedémocratique du travail (C.F.D.T.)SuppléantCaron (Michel)Clement (Marcel) +membres du Conseil supérieur de la prud'homiereprésentants des employeurs2oSur proposition de la Confédération généraledes petites et moyennes entreprises (C.G.P.M.E.)SuppléantBlanchard (Guy)Tissie (Georges) +Gérard Cureaupréfet hors cadre, directeur général de l'administration au ministère de l'intérieurpréfet de la région Bourgogne, préfet de la Côte-d'Or +Edouard Lacroixpréfet de la région Bourgogne, préfet de la Côte-d'Orpréfet de la région Bretagne, préfet d'Ille-et-Vilaine +conseiller technique au cabinet du ministreJacques Genthialsous-directeurappelé à d'autres fonctions +Michel Durafourministre d'Etat, ministre de la fonction publique et des réformes administrativesl'intérim du Premier ministre pendant l'absence de celui-ci +aux fonctions exercéesconseiller technique au cabinet du secrétaire d'Etat auprès du ministre de l'équipement, du logement, des transports et de la merchargé des transports routiers et fluviauxparBernard-Gely (Anne)appelée à d'autres fonctions +au cabinet du secrétaire d'Etat auprès du ministre de l'équipement, du logement, des transports et de la merchargé des transports routiers et fluviauxConseiller techniqueingénieur en chef des ponts et chausséesHossard (Claude) +Jean Dominépréfet du Pas-de-Calaispréfet hors cadre +Henri Hurandsous-préfet, secrétaire général de la préfecture du Nord (1re catégorie)préfet de la Haute-Corse +Bernard Boucaultpréfet de la Haute-Corsepréfet hors cadre +Gilbert Carrerepréfet hors cadredirecteur général de l'administration au ministère de l'intérieurGérard Cureau +Ryba (Fabienne), épouse Lefeburecommis au tribunal d'instance de Roubaixrégisseur suppléantBonte +aux fonctions exercéesconseiller technique au cabinet du secrétaire d'Etat auprès du ministre du travail, de l'emploi et de la formation professionnellechargé de la formation professionnelleparFrançois Peronnetappelé à d'autres fonctions +maître des requêtes au Conseil d'EtatChristian Vigouroux +économisteappelé à d'autres fonctionsEmmanuel Davril +préfetappelé à d'autres fonctionsGilbert Carrère +François Nicoullaudconseiller des affaires étrangèresconseiller auprès du ministre +Bernard Boucaultpréfetdirecteur adjoint du cabinet du ministre +Christian Vigourouxmaître des requêtes au Conseil d'Etatdirecteur du cabinet du ministre +Fabienne Costaattachée parlementaire +Michel Soulierpréfet de l'Ariègeau bénéfice du congé spécial +Prevostdirecteur de la succursale de Longwyà la succursale de VesoulAndres +Jolyotdirecteur de la succursale de Vienneà la succursale de MelunMeyer +Touyerasdirecteur de la succursale de Poitiersà la succursale de ToulouseDubois +Jacquet (Jean-Marie)directeur de bureau à la succursale de Marseilledirecteur de la succursale de DigneGoursot +Maffeidirecteur de la succursale de Briveà la succursale de NancyRoman +Armand (Louis)directeur de la succursale de Vichyà la succursale de VienneJolyot +Baudry (Hubert, Francisque, Paul, Marie)directeur de bureau à la succursale de Toulousedirecteur de la succursale de BriveMaffei +Bonhomme (Jean, Louis)directeur de bureau à la succursale de Chalon-sur-Saônedirecteur de la succursale de LongwyPrevost +Le Berre (Marcel, Bénoni)directeur de bureau à la succursale de Caendirecteur de la succursale d'Alençon +Renaud (Raymond, Marcel, Marie)directeur de bureau à la succursale de Melundirecteur de la succursale de Vichy
      • Arnaud (Louis)
      +Romandirecteur de la succursale de Nancyà la succursale de PoitiersTouyeras +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la solidarité, de la santé et de la protection socialeTitulaireGodard (Aline)sous-directeur de la prévention générale et de l'environnement +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'équipement, des transports et de la mer (au titre de l'urbanisme et du logement)TitulaireAntier (Claude)sous-directeur de l'urbanisme local à la direction de l'architecture et de l'urbanisme +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportsTitulaireAndrieu (Christine)chef du bureau de la santé scolaire et de l'action sociale +au Conseil national du bruitreprésentants de l'EtatReprésentant le garde des sceaux, ministre de la justiceTitulaireGuerin (Didier)chef de bureau à la sous-direction de la législation criminelle +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la défenseTitulairePeyrequeou (Christian)sous-directeur du domaine et de l'environnement (direction de l'administration générale) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la recherche et de la technologieTitulaireMonin (Raoul)chef du département Mécanique, optique, aéronautique à la délégation à l'innovation et à la technologie +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre délégué auprès du ministre d'Etat, ministre de l'économie, des finances et du budgetchargé du budgetTitulaireLiger (Dominique)administrateur civil à la direction du budget +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'équipement, des transports et de la mer (au titre de l'urbanisme et du logement)TitulaireCheccaglini (Paul)urbaniste en chef de l'Etatchargé de la sous-direction de la qualité de l'habitat à la direction de la construction; Suppléant +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'équipement, du logement, des transports et de la mer (au titre des transports)TitulaireLauer (André)ingénieur en chef des ponts et chaussées, chef du centre d'études des transports urbains +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'industrie et de l'aménagement du territoireTitulaireOudet (Jean-Paul)chef du département Environnement (service d'action régionale pour la sécurité et la compétitivité industrielle [S.A.R.S.C.I.]) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'intérieurTitulaireWyss (Jean-Paul)administrateur civil, chef du bureau de la prévention et de la protection sociale (direction des libertés publiques et des affaires juridiques) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre du travail, de l'emploi et de la formation professionnelleTitulaireBrun (François)sous-directeur des conditions de travail et de la protection contre les risques du travail +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'économie, des finances et du budgetchargé de la consommationTitulaireTraynard (Etienne)chef du bureau de la qualité et de la sécurité des produits industriels à la direction générale de la concurrence, de la consommation et de la répression des fraudes +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportschargé de la jeunesse et des sportsDéputé désigné par l'Assemblée nationaleGaillard (Claude) +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportschargé de la jeunesse et des sportsSénateur désigné par le SénatDescours (Charles) +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportschargé de la jeunesse et des sportsTitulaireRemy (Dominique)chef du bureau des affaires communes +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du Premier ministrechargé de l'environnement et de la prévention des risques technologiques et naturels majeursTitulaireBidou (Dominique)délégué à la qualité de la vie +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la solidarité, de la santé et de la protection socialeSuppléantRichart-Lebrun (Annie)chef du bureau Environnement, toxicologie, alimentation +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportsSuppléantLeclerc (Suzanne)chargée de mission auprès du directeur des écoles +au Conseil national du bruitreprésentants de l'EtatReprésentant le garde des sceaux, ministre de la justiceSuppléantLagauche (Philippe)chef de bureau à la sous-direction de la justice criminelle +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la défenseSuppléantSarpentier (Jacques)administrateur civil au bureau du patrimoine et de l'environnement (direction de l'administration générale) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de la recherche et de la technologieSuppléantLeclerc +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre délégué auprès du ministre d'Etat, ministre de l'économie, des finances et du budgetchargé du budgetSuppléantGrancher (Arnaud)inspecteur des impôts +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'équipement, du logement, des transports et de la mer (au titre des transports)SuppléantBahuau (Jacques)ingénieur général de l'aviation civile +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'industrie et de l'aménagement du territoireSuppléantPressense (Loïc)chargé de mission (service des biens d'équipements industriels [Serbe]) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre de l'intérieurSuppléantBronner (François)administrateur civil du bureau du contrôle de la légalité et des affaires juridiques (direction générale des collectivités locales) +au Conseil national du bruitreprésentants de l'EtatReprésentant le ministre du travail, de l'emploi et de la formation professionnelleSuppléantLouit (Paul)ingénieur en chef des mines, conseiller scientifique à la direction des relations du travail +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'économie, des finances et du budgetchargé de la consommationSuppléantGenain (Patrick)inspecteur +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportschargé de la jeunesse et des sportsSuppléantHardy (Claude)chef de la mission des relations avec les fédérations (département de la vie fédérale) +au Conseil national du bruitreprésentants de l'EtatReprésentant le secrétaire d'Etat auprès du Premier ministrechargé de l'environnement et de la prévention des risques technologiques et naturels majeursSuppléantSchmeltz (Pierre)chef de la mission Bruit +Depeyre (Dominique)greffier au tribunal de grande instance de Parisrégisseur suppléantEckert +membres du jury chargé d'apprécier la seconde catégorie de candidatsMonchambert (Sabine)conseiller de tribunal administratif à Strasbourg +membres du jury chargé d'apprécier la seconde catégorie de candidatsVolk-Matheron (Madeleine)chargée de mission auprès de l'Institut français de navigation +membres du jury chargé d'apprécier la seconde catégorie de candidatsBuniet (Christian)maître de conférences à l'université Lyon-III-Jean-Moulin +membres du jury chargé d'apprécier la seconde catégorie de candidatsChalvidan (Pierre)maître de conférences à l'université Paris-XII +membres du jury chargé d'apprécier la seconde catégorie de candidatsQuillet (Nicolas)sous-préfet +membres du jury chargé d'apprécier la seconde catégorie de candidatsRaillard (Jean-Paul)professeur agrégé à Nantes +membres du jury chargé d'apprécier la seconde catégorie de candidatsRichez (Yves)attaché d'administration centrale au ministère de l'agriculture et de la forêt +membres du jury chargé d'apprécier la seconde catégorie de candidatsTietz (Marc-Philippe)inspecteur principal des télécommunications +membres du jury chargé d'apprécier la seconde catégorie de candidatsTronchon (Pierre)chef de service administratif des services extérieurs du ministère de l'équipement et du logement à Perpignan +membres du jury chargé d'apprécier la seconde catégorie de candidatsEn cas d'empêchement deFrançois (André)la présidence des jurys sera assurée par +membres du jury chargé d'apprécier la seconde catégorie de candidatsEn cas d'empêchement deLuchaire (Yves)pour la première catégorie +membres du jury chargé d'apprécier la seconde catégorie de candidatsEn cas d'empêchement deQuillet (Nicolas)pour la seconde catégorie +15 janvier 1990Provost (Françoise)à la 1re classe du grade de secrétaire adjoint principal des affaires étrangèresau 1er échelon +aux fonctions exercées au cabinet du ministre parDominique Charvetconseiller techniqueappelé à d'autres fonctions +Marcel Morinpréfet, adjoint pour la sécurité auprès du préfet des Bouches-du-Rhôneau bénéfice du congé spécial +Jean-Marie Diemerpréfet hors cadreau bénéfice du congé spécial +Georges Bastelicapréfet hors cadreau bénéfice du congé spécial +administrateur civil hors classeconseiller technique au cabinet du ministre de l'industrie et de l'aménagement du territoireBruno Gremillot +conseiller des affaires étrangèresconseiller technique au cabinet du ministre de l'industrie et de l'aménagement du territoireDenys Gauer +en général, , selon la jurisprudence du Conseil d'Etat, et selon la doctrine, que pour assurer la continuité de l'action gouvernementale et donc pour des attributions administratives générales qui sont traditionnellement dévolues au Premier ministre et qu'il ne peut assurer directementLa mise en oeuvre de l'article 49-3 est contraire à l'esprit de la ConstitutionLes dispositions minutieuses adoptées en 1958, quant à l'engagement de la responsabilité du Gouvernement (adoption au conseil des ministres, engagement du Gouvernement par le Premier ministre, délais) démontrent que les constituants ont cherché à apporter des garanties procédurales pour éviter tout engagement de responsabilité intempestif par un membre autre que le chef du GouvernementLa procédure mise en oeuvre par l'article 49-3, et plus spécialement dans son alinéa 3, rappelle que les constituants ont voulu mettre fin aux procédés employés sous les Républiques précédentes, selon lesquels le Gouvernement voyait sa responsabilité engagée par un ministre sans l'accord du Premier ministreLa lecture des travaux préparatoires de la Constitution prouve clairement que les constituants ont voulu éviter cet écueil en considérant que le Premier ministre, et lui seul, pouvait, selon une procédure solennelle et orale, engager la responsabilité du Gouvernement devant l'Assemblée nationaleLa mise en oeuvre de l'article 49-3 est contraire à l'usage de la ConstitutionJean Poperenministre chargé des relations avec le Parlement lors de la deuxième séance du28 avril 1989pour l'adoption en première lecture, du projet de loi approuvant le Xe Plan (procédure qui n'a fait l'objet d'aucun recours) +en général, , selon la jurisprudence du Conseil d'Etat, et selon la doctrine, que pour assurer la continuité de l'action gouvernementale et donc pour des attributions administratives générales qui sont traditionnellement dévolues au Premier ministre et qu'il ne peut assurer directementQu'enfin les attributions essentielles qui fondent le régime parlementaire de la Ve République ne peuvent être exercées que par leurs titulairesqu'ainsi le Président de la République assurant l'intérim ne peut exercer le droit de dissolution14 décembre 1989Lionel Jospinministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sports, n'a été publié au Journal officiel que le15 décembre 1989Qu'en conséquence, et en vertu du décret-loi du5 novembre 1870qui dispose que les règlements publiés au Journal officiel n'entrent en vigueur qu'un jour franc après leur promulgation, le ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sports, était incompétent lors de la deuxième séance du15 décembre 1989pour engager la responsabilité du Gouvernement sur le projet de loi portant diverses dispositions relatives à la sécurité sociale et à la santé qui a été considéré comme adopté en l'absence du vote d'une motion de censure +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en première annéeen application de l'article 5 de l'arrêté du25 février 1987précité, à la suite du concours spécial ouvert en 1989 et réservé aux élèves de l'enseignement technique, les candidats françaispar ordre alphabétiquePairault (Dominique) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersFloelo (Frode)capitaine (Norvège) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersTay (Lim)lieutenant (Singapour) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats étrangersTjiptahardja (Tisna)Indonésie +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisKhiar (Karim-Patrick) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueFonctionnaires civils françaisLeblond (Florence) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisGaertner (Nathalie) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisMathieu (Lise) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisProvost (Marie-Laure) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisSoulé (Véronique) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueAdnot (Philippe) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueBenisty (Laurent) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueBouchet (François) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueClerc (Jean-Pierre) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueCornolle (Didier) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueDal (François) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueDemoulin (François) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueDoux (Jean-Christophe) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueEtchevers (Olivier) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueFairbank (Xavier) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueGousset (Frédéric) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueIssarni (Alain) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueLaurent (Hervé) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueLemercier (Cyril) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueOuillet (Pierre-Yves) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueOuziel (Robin) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueParis (Jean-Christophe) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiquePenicaud (Jérôme) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueReb (Stéphane) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueRohmer (Philippe) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueRoves (Jean-Paul) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les ingénieurs de l'armement option Technique classés par ordre alphabétiqueStock (Jean-Noël) +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueBrivoal (Hervé)lieutenant de vaisseau +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueDanneels (Christophe)capitaine de l'armée de l'air +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqued'Anselme (Marc)chef d'escadron +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueFerran (Alain)capitaine de l'armée de l'air +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueHuret (Jérôme)capitaine de l'armée de l'air +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueLaplane (Denis)lieutenant de vaisseau +élèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les officiers français classés par ordre alphabétiqueVernis (Nicolas)capitaine de l'armée de l'air +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en première annéeen application de l'article 5 de l'arrêté du25 février 1987précité, à la suite du concours spécial ouvert en 1989 et réservé aux élèves de l'enseignement technique, les candidats françaispar ordre alphabétiqueSancho (José) +élèves, à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en première annéeen application de l'article 5 de l'arrêté du25 février 1987précité, à la suite du concours spécial ouvert en 1989 et réservé aux élèves de l'enseignement technique, les candidats françaispar ordre alphabétiqueVuillermoz (Guillaume) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueFonctionnaires civils françaisHervieu (Philippe) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersCuervo (Abel)lieutenant (Argentine) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersQuevedo (Hernan)capitaine de corvette (Chili) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersRafkaoui (Abdelaziz)lieutenant (Maroc) +sur titreauditeurs à l'Ecole nationale supérieure de l'aéronautique et de l'espaceen application de l'article 9 de l'arrêté du25 février 1987précité, les fonctionnaires et militairespar ordre alphabétiqueOfficiers étrangersVargas (Victor)lieutenant (Pérou) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats étrangersChaouche (Abdel)Tunisie +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisBeguier (Jean-Philippe) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisBenisaoune (Omar) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisBreteau (Jérôme) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisCanas (Daniel) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisCanevet (Pierre) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisDahan (Philippe) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisDejean (Frédéric) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisDellal (Karim) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisJacob (David) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisJasmin (François) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisMourgues (Stéphane) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisMuscat (Patrick) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisReboul (Michel) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisSannino (Jean-Michel) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisSoursou (Marc) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisViala (Pierre-Louis) +sur titreélèves à l'Ecole nationale supérieure de l'aéronautique et de l'espace, en deuxième annéeen application de l'article 5 de l'arrêté du25 février 1987précité, les candidatspar ordre alphabétiqueCandidats françaisVotruba (Laurent) +Jean-Louis Guigoudirecteur à la Délégation à l'aménagement du territoire et à l'action régionale +dans la 2e section du cadre des ingénieurs généraux de l'armement par anticipation etDeletang (Pierre, Roger)Ingénieur général de 2e classe de l'armement +dans la 1re section du cadre des ingénieurs généraux de l'armementAu grade d'ingénieur général de 2e classeEsper (Philippe)Ingénieur en chef de l'armementmaintenu en position de service détaché +Orszag (Alain, Georges)Ingénieur général de 1re classe de l'armementplacé en service détaché auprès de la société Quanteldans les cadresà la même dateadmis par anticipation etdans la 2e section du cadre des ingénieurs généraux de l'armement +Brissot (Pierre, Henri, Louis)Ingénieur général de 2e classe de l'armementplacé en service détaché auprès du ministère de l'industrie et de l'aménagement du territoiredans les cadresà la même dateadmis par anticipation etdans la 2e section du cadre des ingénieurs généraux de l'armement +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeFredon (Jean, François)Colonel de l'infanterie +Bury (Bernard, Jean, Charles)Général de brigadeadjoint au général commandant la 11e division militaire territoriale +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeGuillaume (Roland, Louis, Etienne)Colonel de l'artillerie +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeLe Page (Maurice, Paul, Marie)Colonel des troupes de marine +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadePintoux (Bernard, Georges)Colonel de l'infanterie +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeUrwald (Paul, Claude, Robert)Colonel des troupes de marine +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de divisionAumonier (Pierre, Yves, Michel)Général de brigade +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de divisionDuguet (Jean, Louis, André)Général de brigade +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de divisionPennacchioni (Dominique, Jean, François)Général de brigade +Heimburger (Henri)Général de brigade aériennedans la 2e section du cadre des officiers généraux de l'armée de l'airpar anticipation et +dans la 1re section du cadre des officiers généraux de l'armée de l'airàNorlain (Bernard, Marie, Léon, Ignace)Général de division aérienne +dans la 1re section du cadre des officiers généraux de l'armée de l'air, avec maintien dans ses fonctionsAu grade de général de brigade aérienneColin (Francis, René, Emile)Colonel du corps des officiers de l'air +membre de l'intercommission dans le domaine de la recherche sur le vieillissementau titre du deuxième alinéa de l'article 1er et du sixième alinéa de l'article 6 de l'arrêté du14 novembre 1986précitéPiette (François)Pous (Jacques) +Jacky Simoninspecteur général de l'administration de l'éducation nationaledirecteur des personnels d'inspection et de direction à l'administration centrale du ministère de l'éducation nationale, de la jeunesse et des sports +Jacques Genthialinspecteur général des services actifs de la police nationaledirecteur des services actifs de la police nationale, directeur central de la police judiciaire +François Barrédélégué aux arts plastiquesDominique Bozo +Achille Lerchegénéral d'armée aérienneconseiller d'Etat en service ordinaire (tour extérieur) +Pierre Paponprésident du conseil d'administration de l'Institut français de recherche pour l'exploitation de la mer +Denis Fermansecond sous-gouverneurPhilippe Lagayette +Philippe Lagayettesecond sous-gouverneur de la Banque de Francepremier sous-gouverneur de la Banque de FranceJacques Waitzenegger +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Baffeleuf (Georges, Joseph, Jean)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Carbonneaux (Jean, Francis)Général de division +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Chollet (Jean-Paul)Général de corps aérien +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Clarke de Dromantin (Christian, Octave, Marie, Roger)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Coatanéa (Alain)Vice-amiral d'escadre +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Codet (Jacques, Jean, Numa)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Coullon (Jean-Claude)Général d'arméeinspecteur général de l'armée de terremembre de droit +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Deveaud (Jacques, Antoine, Mary)Général de corps aérien +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Dupuy de la Grand'Rive (André)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Duthoit (Alain, Joseph, Paul, Louis)Vice-amiral d'escadre +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Farret (Robert, Auguste, Antoine)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Fennebresque (Michel, Marie, Jacques, Gustave)Général d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Fleury (Jean, André, Anne)Général d'armée aériennechef d'état-major de l'armée de l'air, vice-président +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Forray (Gilbert)Général d'arméechef d'état-major de l'armée de terre, vice-président +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Goupil (Yves, Jean, Marie, Marcel)Vice-amiral d'escadre +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Guillon (Daniel, Maurice)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Lanata (Vincent, Paul, André)Général d'armée aérienneinspecteur général de l'armée de l'airmembre de droit +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Langre (Ghislain)Vice-amiral d'escadre de +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Lartigau (Claude, Jean, Régis)Général de corps aérien +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Lebrun (Jean-Claude, René, Guislain)Général de corps aérien +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Lefebvre (Dominique, Léon, Marie, Joseph)Vice-amiral d'escadre +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Le Mélédo (Gilbert, Yves, Marie)Vice-amiral d'escadre +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Lemoine (Jean-Marie)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Louzeau (Bernard)Amiralchef d'état-major de la marine, vice-président +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Merveilleux du Vignaux (Michel, Jean, Marie, Etienne)Amiralinspecteur général de la marinemembre de droit +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Norlain (Bernard, Marie, Léon, Ignace)Général de division aérienne +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Préaud (Henry, Marie, Pierre)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Roué (Jean, Louis)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Valéry (Daniel)Général de corps d'armée +Membre des conseils supérieurs de l'armée de terre, de la marine et de l'armée de l'air pour l'année 1990Vallat (François, Claude, Alphonse)Général de corps aérien +dans la 1re section du cadre des officiers généraux avec maintien dans ses fonctionsJoliff (Jean)Médecin chef des services de classe normale +dans la 1re section du cadre des officiers généraux avec maintien dans ses fonctionsLagrave (Guy, Jean, Marie)Médecin général, médecin chef des services hors classe +Leguay (Gérard, Georges, Denis)Médecin général, médecin chef des services de classe normaledirecteur de l'école d'application du service de santé pour l'armée de l'air et du centre d'études et de recherches de médecine aéro- spatiale +Droniou (Jean, Michel, Francis)Médecin général, médecin chef des services de classe normaledirecteur du service de santé de la 2e région aérienne +Timbal (Jean, Alexandre, François)Médecin général inspecteur, médecin chef des services hors classeinspecteur du service de santé pour l'armée de l'air +Bernier (Alain, Francis)Général de brigade aériennecommandant des transmissions de l'armée de l'air +Duvivier (Jean-Pierre)Général de division aériennedirecteur de l'infrastructure de l'air +Mattheos (Louis, Jérôme)Contrôleur général des arméescontrôleur général de la 2e région maritime à Brest +conseiller technique au secrétariat général de la présidence de la Républiqueappelé à d'autres fonctionsMarc Boudier +Bernard Cottinconseiller commercialchargé de mission au cabinet du Premier ministreJérôme Adam +Charles Maloministre plénipotentiaire hors classeà la dignité d'ambassadeur de France pour prendre rangà compter de la date de signature du présent décret +Celerier (Bernard)responsable de l'application des programmes de production de semence mis en oeuvre par l'Union de coopératives désignée ci-dessus +Michel Grangereresponsable de l'application du programme de production de semences mis en oeuvre par la coopérative désignée ci-dessus +Anne Lauvergeonchargée de mission à la présidence de la République +Beaumard (Moïse)responsable de l'application du programme de production de semence mis en oeuvre par l'Union des coopératives Ouest génétique élevage reproduction +Jean-Claude Platresponsable de l'application du programme de production de semences mis en oeuvre par l'union de coopératives ci-dessus mentionnée +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeBourhis (Jean-François)au 2e échelon avec une ancienneté conservée de 2 ans +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeChanu (Pierre-Yves)au 1er échelon avec une ancienneté conservée de 7 mois 10 jours +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeFoubert (Michel)au 5e échelon avec une ancienneté conservée de 1 an 2 mois 21 jours +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeGuerin (Jean-Pierre)au 1er échelon avec une ancienneté conservée de 9 mois 5 jours +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeLe Marec (Eric)au 1er échelon +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classeMonamicq (Claude)au 1er échelon avec une ancienneté conservée de 1 an 10 mois +24 janvier 1990au grade d'attaché principal d'administration centrale de 2e classePeltier (Régis)au 1er échelon avec une ancienneté conservée de 1 an 10 mois 25 jours +Jérôme Chapuisatprofesseur d'universitérecteur de l'académie d'AmiensEdouard Bridoux +Michel Gayraudprofesseur d'universitérecteur de l'académie de NantesMaurice Quenet +Marie Richardadministrateur civildirecteur de la jeunesse et de la vie associative +Parriaud (Jean-Claude)président de la commission technique de sécurité +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Caspi +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Coblentz +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989David +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Faure +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Gabillard +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Pascal (André) +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Pascal (Jean-Pierre) +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Pilon +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Savelli +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Scherrer +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Vial +membres de la sous-commission des transports guidésinstituée par l'article 6 de l'arrté du 4 août 1989Villiers +Faure (Marcel)président de la sous-commission des transports guidés
      • Dobias
      • Parriaudpeuvent participerde besoin aux travaux des sous-commissions
      +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Chich +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Doguet +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Jaget +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Joyau +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Lombard +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Pascal (Michel) +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Thyry +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Trarieux +membres de la sous-commission du transport routierinstituée par l'article 5 de l'arrêté du 4 août 1989Viriot +Doguet (André)président de la sous-commission du transport routier +au cabinet du ministre délégué auprès du ministre de l'équipement, du logement, des transports et de la merchargé du logementConseiller techniquePatrice Lanco +Hervé Barréconseiller technique chargé des relations internationales au cabinet du ministre délégué auprès du ministre de l'industrie et de l'aménagement du territoirechargé du tourisme +aux fonctions exercées au cabinet du ministreconseiller techniqueparJean-Baptiste Danelingénieur du génie rural, des eaux et des forêtsappelé à d'autres fonctions +Delaporte (Blandine), épouse Guillotincommis au tribunal d'instance de Vannesrégisseur titulaireMaignan +Leblanc (Jocelyne), épouse Nevezcommis au tribunal de grande instance de Chambéryrégisseur titulaired'Argento +Perie (Geneviève), épouse Lacadéeagent technique de bureau au tribunal d'instance de Saint-Palaisrégisseur titulaireUrruty +Franchet (Joëlle)sténodactylographe au tribunal d'instance de Palaiseaurégisseur suppléantRene-Corrail +Masset (Andrée), épouse Perretcommis au tribunal de grande instance de Chambéryrégisseur suppléantNevez +Saly (Monique)greffier au tribunal de grande instance de Carcassonnerégisseur suppléantBernabe +Vinnac (Marie-Jeanne), épouse Urrutygreffier au tribunal d'instance de Saint-Palaisrégisseur suppléantLacadée +Charconnet (Pierre)greffier divisionnaire à la cour d'appel de Dijonrégisseur suppléantEspinosa +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLe candidat ci-aprèsIssa (Imad)Syrien +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsBalmes (Etienne, Pierre) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsCourtois (Jean-Yves) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsFeron (Eric, Marie, Jacques) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsJoux (Antoine, Jean, Lucien) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsPannetier (Christophe, Jean) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsRenard (Pierre-Yves, Bernard, Jean) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes ingénieurs de l'armement ci-aprèsSourdive (David, Jean, Dorian) +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes officiers étrangers ci-aprèsFreitas (Sergio, Garcia Da Costa)Brésilien +auditeur à l'Ecole nationale supérieure de techniques avancéesen application des articles 6 et 9 de l'arrêté du25 février 1987précitéLes officiers étrangers ci-aprèsGoussaert (Filip, Gérard, André)Belge +élève à l'Ecole nationale supérieure de techniques avancées, en troisième annéeen application de l'article 7 de l'arrêté du25 février 1987précité, l'auditeur étranger ci-aprèsNjine Djonkam (Pierre)Camerounais +en deuxième année les candidats ci-aprèsCandidats étrangersLaiarinandrasana (Randriamboarison, Lucien, Fidèle)Malgache +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Avril (Sylvie, Nicole) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Martin (Joëlle, Louise) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Renault (Isabelle, Marie, Anne, Cécile) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Zibi (Joëlle, Marguerite) +en deuxième année les ingénieurs de l'armement ci-aprèsClaudon, épouse Pailloux (RaphaëleChristine, Renée) +en deuxième année les ingénieurs de l'armement ci-aprèsGodefroy (Esther, Florence, Marie, Alix) +en deuxième année les ingénieurs de l'armement ci-aprèsLéger (Florence, Marie, Thérèse) +en deuxième année les candidats ci-aprèsCandidats étrangersAdrou Bakkali (Noureddine)Marocain +en deuxième année les candidats ci-aprèsCandidats étrangersArfaoui (Hichem)Tunisien +en deuxième année les candidats ci-aprèsCandidats étrangersFeghoul (Mohamed)Algérien +en deuxième année les candidats ci-aprèsCandidats étrangersMouradian (Rudolf)Libanais +en deuxième année les candidats ci-aprèsCandidats étrangersSaadi (Abdelhamid)Marocain +en deuxième année les candidats ci-aprèsCandidats françaisBodin (Bernard, Henri) +en deuxième année les candidats ci-aprèsCandidats françaisCanal (Pierre, Claude, Raymond) +en deuxième année les candidats ci-aprèsCandidats françaisCoppolani (Jean-François) +en deuxième année les candidats ci-aprèsCandidats françaisLamotte (Bruno) +en deuxième année les candidats ci-aprèsCandidats françaisLegrand (Jean-François) +en deuxième année les candidats ci-aprèsCandidats françaisMonluc (Bernard) +en deuxième année les candidats ci-aprèsCandidats françaisPoux (André, Marcel) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Alexis (Antoine, François) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Armengaud (Alexandre) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Belicard (Jean-Etienne) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Bezombes (Patrick, Gilbert, Christian) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Boschat (Jean-François) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Brunet (Philippe) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Campos-Barreau (Georges, François) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Cavaretta (Fabrice, Léopold, Lucien) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Chauvin (Eric, Franck) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Coucy (de)(Patrick, Yves) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Darbois (Matthieu, Philippe) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Demonsant (Eric, Jacques) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Depardieu (Gilles, Thierry, Emmanuel) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Deperrois (Vincent, Pierre, Marie) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Deren (Tanguy, Pierre) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Doridot (Stéphane, Pierre) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Faou (Michel) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Fleury (Pierre, Daniel) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Fourcade-Cancelle (Jean-Charles, Henri) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Goubin (Hervé, Alain) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Granal (Laurent, Claude) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Kandel (Jean-Baptiste, Jacques) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Lahaye (Gilles, Jean, Louis) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Laporte (Vincent, Christophe) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Lièvre (Guillaume, Marie) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Luscan (Bruno, Paul) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Magnan (Bruno, René) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Navarre (Jean-François, Maurice, Emile) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Palfrey (Philippe, Edouard) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Ravier (Pascal, Olivier, Régis) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Richelet (Bernard, Ghislain, Marie, Allain)Capitaine (air) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Roche (François-Xavier) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Sourdois (Jean-Luc, Bernard, Henri) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Temperville (Pierre, Claude, Daniel) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Tuloup (Marc, François, Julien) +en deuxième année les candidats ci-aprèsLieutenant de vaisseau (marine)Yatzimirsky (Nicolas) +en deuxième année les ingénieurs de l'armement ci-aprèsAymar (Philippe, Armand) +en deuxième année les ingénieurs de l'armement ci-aprèsBaillot d'Estivaux (Jean-Marie) +en deuxième année les ingénieurs de l'armement ci-aprèsBenessy (Claude) +en deuxième année les ingénieurs de l'armement ci-aprèsDumas (Christophe, Yves) +en deuxième année les ingénieurs de l'armement ci-aprèsDumesnil de Maricourt (Antoine, Marie, François, Philippe) +en deuxième année les ingénieurs de l'armement ci-aprèsEven (Michel, Alain, Jean) +en deuxième année les ingénieurs de l'armement ci-aprèsFiorini (Pierre, Joseph, François) +en deuxième année les ingénieurs de l'armement ci-aprèsGiannoni (Dominique) +en deuxième année les ingénieurs de l'armement ci-aprèsKinkelin (Gérulf, Wilhelm, Bernhard) +en deuxième année les ingénieurs de l'armement ci-aprèsLagathu (Jean-Luc, Emile, Marie) +en deuxième année les ingénieurs de l'armement ci-aprèsMartinot-Lagarde (Vincent, André, Claude) +en deuxième année les ingénieurs de l'armement ci-aprèsPivet (Sylvestre, Max, Marie) +en deuxième année les ingénieurs de l'armement ci-aprèsQuenez (Jean-Marc, Pierre, Jules) +en deuxième année les ingénieurs de l'armement ci-aprèsRedaud (Christophe, Hervé, Marie) +en deuxième année les ingénieurs de l'armement ci-aprèsRougier (Yves, Jean, Bernard, Jacques) +en deuxième année les ingénieurs de l'armement ci-aprèsSaubot (Alexandre, François, Marie) +en deuxième année les ingénieurs de l'armement ci-aprèsVideau (Jean-Christophe, Philippe) +Metzler (Catherine), épouse Lafortunegreffier au tribunal d'instance de Paris-3erégisseur titulaireLarray +Deschamps (Josiane), épouse Deleanicommis au tribunal d'instance de Saint-Jean-de-Mauriennerégisseur suppléantOlive +Brasse (Brigitte), épouse Vattiersténodactylographe au tribunal d'instance de Rouenrégisseur suppléantBertrand +Pauthier (Janine), épouse Pageagent d'administration principal au tribunal d'instance de Baume-les-Damesrégisseur suppléantCouderc +Camet (Françoise)administrateur civilchef du service des autorisations, adjointe au chef du service des autorisations et des analyses économiques +au cabinet du ministre du commerce extérieurConseiller techniqueSerge Perrine +Farçat (Patrick)administrateur civil, chef du service des autorisations et des analyses économiques du Conseil supérieur de l'audiovisuelen sus de ses fonctions actuelles, chef du service des études au sein de la direction des affaires techniques et des études du conseil +Karamour (Marie-Noëlle)agent technique de bureau au tribunal d'instance de Brestrégisseur suppléantLe Mest +Lenoir (Sylviane), épouse Yovanovitchagent technique de bureau au tribunal d'instance de Troyesrégisseur suppléantTapin +Sourd (Henri)commis au tribunal de grande instance de Saint-Omerrégisseur suppléantVincent +Gardoll (Jacques)greffier en chef du tribunal d'instance du Blancrégisseur titulaireArrault +au titre des dispositions de l'article 16paragraphe b, du décret no 65-426 du4 juin 1965Pene (Cédric) +au titre des dispositions de l'article 16paragraphe b, du décret no 65-426 du4 juin 1965Taddei (François)admis au concours spécial d'admission à l'Ecole nationale du génie rural, des eaux et des forêts réservé aux titulaires de diplômes délivrés par certaines écoles scientifiques +Cambriel (Françoise)premier greffier au tribunal de grande instance de Daxrégisseur suppléantJentet +Patrice Magnierpréfet de Dordognepréfet de l'Aisne +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de la défensede Lavernhe (Géraud) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de la défenseVan Grevenynghe (Philippe) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de l'économie, des finances et du budgetCrifasi (Aldo) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de l'éducation nationale, de la jeunesse et des sportsRhim (Renaud) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de l'équipement, du logement, des transports et de la merde Rocquigny du Fayel (Bruno) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsMinistère de l'intérieurDyevre (Eric) +dans les administrations centrales de l'Etat ci-après définies les administrateurs civilsServices du Premier ministreMossa (Charles) +directeur du cabinetJean-Claude Lesourd +au cabinet du ministre délégué auprès du ministre de l'industrie et de l'aménagement du territoirechargé du tourismeDirecteur du cabinetsous-préfet de 1re classeMarcel Peres +sur titres en 1989élèves étrangers en deuxième année d'études à l'Ecole nationale supérieure des ingénieurs des études et techniques d'armementDépartement Electronique de défenseAzher Khasim (Muhaimin)de nationalité irakienne +sur titres en 1989élèves étrangers en deuxième année d'études à l'Ecole nationale supérieure des ingénieurs des études et techniques d'armementDépartement Electronique de défenseShenan Dshar (Ismail)de nationalité irakienne +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesLa Constitution du sujet dans les sociétés contemporainesHistoire et archéologie de la civilisation médiévaleFrançoise Piponniermaître de conférences à l'Ecole des hautes études en sciences sociales +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesAnthropologie de la naturePhilippe Descolamaître de conférences à l'Ecole des hautes études en sciences sociales +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesEthnologie de l'Europe du Sud-OuestDaniel Fabremaître de conférences à l'université Toulouse-III +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesHistoire de la sainteté dans les sociétés musulmanes arabophonesMichel Chodkiewicz +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesLa Constitution du sujet dans les sociétés contemporainesHistoire et critique de l'humanismeYves Hersantmaître de conférences à l'Ecole des hautes études en sciences sociales +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesLa Constitution du sujet dans les sociétés contemporainesHistoire et espaceBernard Lepetitmaître de conférences à l'Ecole des hautes études en sciences sociales +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesLa Constitution du sujet dans les sociétés contemporainesPhilosophie du politiquePierre Rosanvallonmaître de conférences à l'Ecole des hautes études en sciences sociales +titularisésdirecteur d'études non cumulant à l'Ecole des hautes études en sciences socialesLa Constitution du sujet dans les sociétés contemporainesXVIIIe-XXe sièclesMarcel Gauchetingénieur d'études à l'Ecole des hautes études en sciences sociales +conseiller technique qu'exerçaitMichel Scialomappelé à d'autres fonctions +conseiller techniquechargé de la communication, qu'exerçaitAlain Filleron +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la dansePersonnalités qualifiéesBessy (Claude)titulaire +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la dansePersonnalités qualifiéesde Vulpian (Claude)suppléante +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la dansePersonnalités qualifiéesDuboc (Odile)titulaire +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la dansePersonnalités qualifiéesPomares (Jean)suppléant +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des professionnelsde Blois (Kat)suppléante +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des professionnelsHaussaire (Micheline)titulaire +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des professionnelsRobinson (Jacqueline)titulaire +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des professionnelsSchwarz (Janine)suppléante +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants des usagersLorintitulaire +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants des usagersLongour (Marie-Christine)suppléante +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des collectivités territorialesGabin (Jean-Claude)maire adjoint chargé de la culture à Bagnolet +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants de l'EtatLe préfet de la région Ile-de-France ou son représentant, présidentLe directeur régional des affaires culturelles d'Ile-de-France ou son représentant, vice-présidentUn conseiller pour la musique et la danse de la région Ile-de-FranceUn inspecteur de la danseReprésentants des collectivités territorialesTaddei (Jacques)maire adjoint chargé de la culture à Rueil-Malmaison +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants des usagersBen Samoun (Georges)suppléant +15 septembre 1989Membre de la commission no 1 chargée de donner un avis sur les demandes de dispense du diplôme d'Etat de professeur de danseReprésentants des usagersGuelpa (Jean)titulaire +Arsène Delamonpréfetà faire valoir ses droits à la retraite +Jean-Claude Hirelprésident-directeur général de la société Orkem S.A +Bernard Courtoispréfet en service détachépréfet hors cadre +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationTitulairesLéopold Bargainconseiller honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationTitulairesArnaud Dupré de Pomarèdeconseiller honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationTitulairesJacques Fefferconseiller honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationSuppléantsJosé Chevreauconseiller +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationSuppléantsGérard Gelineau-Larrivetconseiller +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour de cassationSuppléantsJacques Paulotconseiller +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesTitulairesJean Chazalconseiller maître +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesTitulairesJean Langloisconseiller maître honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesTitulairesJean Mialetconseiller maître honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesSuppléantsJacques Balousconseiller maître +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesSuppléantsGeorges Lescuyerconseiller maître +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre de la Cour des comptesSuppléantsGuy Rosierconseiller maître +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatTitulairesXavier de Christenconseiller d'Etat honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatTitulairesFrançois Gazierconseiller d'Etat honoraire, président +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatTitulairesClaude Lasryconseiller d'Etat honoraire +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatSuppléantsJean-Frédéric de Leussemaître des requêtes +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatSuppléantsBernard Stirnmaître des requêtes +membres de la commission des sondages instituée par la loi du19 juillet 1977susviséeAu titre du Conseil d'EtatSuppléantsThierry Tuotmaître des requêtes +conseillers techniques au cabinet du Premier ministreBérengère Quincy +conseillers techniques au cabinet du Premier ministreJean-Maurice Ripertconseillers des affaires étrangèreschargés de mission +Philippe Petitconseiller des affaires étrangères, conseiller techniqueconseiller diplomatique au cabinet du Premier ministre +au cabinet du ministre d'Etat, ministre des affaires étrangèresDirecteur adjoint du cabinetconseiller des affaires étrangèresDominique Girard +Jacques Bonacossaconseiller maître à la Cour des comptessecrétaire général du Conseil économique et social +Joseph Ramonconseiller des affaires étrangères de 1re classe, consul général de France à Madridambassadeur extraordinaire et plénipotentiaire de la République française en République du HondurasPierre Dumon +Bourgeais (Jean-Claude)administrateur civilla direction administrative et financière du Conseil supérieur de l'audiovisuel +pour exercer les fonctions suivantes au sein de ladite commissionPrésidentBoisséingénieur général des mines +pour exercer les fonctions suivantes au sein de ladite commissionRapporteur général adjointFlandriningénieur des instruments de mesure +pour exercer les fonctions suivantes au sein de ladite commissionRapporteur généralGuilletingénieur en chef des mines +pour exercer les fonctions suivantes au sein de ladite commissionSécrétaireLabelleingénieur de l'industrie et des mines +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur option TAConcours G.E.I +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours spécial pour titulaires du D.E.U.G., Section ATrinquier (Hélène) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MMichel (Sylvie) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MRandon (Catherine) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MRibet (Hélène) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MTerral (Karine) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PGombert (Sandrine) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PHortoland (Cécile) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PRuphy (Stéphanie) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PSandis (Hélène)) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementBittoun (Lise) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisBardin (Pascale) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisMartinez (Anne-Marie) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours spécial pour titulaires du D.E.U.G., Section AAlbagnac (Rémi) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours spécial pour titulaires du D.E.U.G., Section ABaïche (Philippe) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours spécial pour titulaires du D.E.U.G., Section ACossard (Stéphane) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours spécial pour titulaires du D.E.U.G., Section APrat (Damien) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur option TADavid (Fabrice) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MAmbry (Matthieu) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MAubrun (Jean-François) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MBarbot (Denis) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MBertucci (François) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MCarles (Pierre) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MCarpentier (Pierre) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MCavignac (Christophe) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MDaugeron (Frédéric) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MDiaz (Diégo) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MGuesnerie (Mickaël) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MHalimi (Alain) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MHespert (Jean-Michel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MJolibois (Stéphane) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MLambolez (Stéphane) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MLefebvre (Didier) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MLion (Didier) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MNaut (Pierre-Louis) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MOberlé (Christian) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MPage (Alain) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MPlasse (Axel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MRostain (Philippe) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MSerughetti (Marc) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme MStricker (Daniel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PBarbara (Olivier) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PBergeron (Lionel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PBourdet (Jean-Baptiste) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PCharles (Eric) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PCouchat (Pascal) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PDattler (Stéphane) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PFaget (Jean-Gabriel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PFaghani (Dariush) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PGuerraz (Olivier) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PHeibst (Cyrille) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PJacquin (Thierry) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PLabiole (Eric) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PLaghdaf (Mohamed) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PLallement (Patrice) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PLanzalavi (Charles) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PLoiseau (Christophe) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PMichoud (Jean-Luc) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PNeu-Fabert (Jean-François) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PPenduff (Thierry) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PRenaud (Philippe) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PRollier (Emmanuel) +à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèvespar ordre de classement, en première année d'études, à la suite des concours ouverts en 1989Concours sur programme PVoisard (Patrick) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementAimé (Frédéric) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementAlphonsout (Bruno) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementDecourt (Philippe) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementExilié (Laurent) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementForlen (Bertrand) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementKoeke (Nicolas) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementPoulachon (Eric) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementPujol (Yves) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementRio (Jean-Pierre) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementSourbe (Laurent) +élèves, en deuxième année, en 1989 à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquespar ordre alphabétique, les ingénieurs des études et techniques d'armementTrotin (Eric) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves étrangersEl Akoum Abdul (Majid) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves étrangersGunawan (Heru) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisBonicel (Patrick) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisBonnet (Jean-Christophe) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisCrété (Guillaume) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisDamian (Eric) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisDubrulle (François) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisKlings (Claude) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisMimoun (Ary) +sur titres en 1989par ordre alphabétique, en deuxième année d'études, à l'Ecole nationale supérieure d'ingénieurs de constructions aéronautiquesélèves françaisReynaud (Jean-Yves) +dans la 2e section du cadre des ingénieurs généraux de l'armementpar anticipation etDivan (Jacques)Ingénieur général de 1re classe de l'armement +Gonin (François, Louis)Ingénieur général de 2e classe de l'armementchef du service technique des programmes aéronautiques +Lasserre (Michel, François)Ingénieur général de 1re classe de l'armementadjoint au délégué général pour l'armement +Jacques Royetconseiller des affaires étrangères de 1re classe, en mission à l'administration centraleambassadeur extraordinaire et plénipotentiaire de la République française en République dominicaineClaude Fouquet +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueJean-Yves Buissondirecteur général de l'Association des paralysés de France, 17, boulevard Auguste-Blanqui, 75013 Paris +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueHugues de Chasseydirecteur adjoint du Comité national de liaison pour la réadaptation des handicapés, 38, boulevard Raspail, 75007 Paris +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueLucien Depaysau titre de la Fédération nationale des associations de retraités, 57, avenue Franklin-Roosevelt, 75008 Paris +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueRaymond Hardysecrétaire général du Comité national pour la promotion sociale des aveugles, 49, rue Blanche, 75009 Paris +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueAxel Isabeyresponsable du service juridique auprès de l'Union des associations de parents et amis des personnes handicapées mentales, 15, rue Coysevox, 75018 Paris +membres de la commission dite “de la marque distinctive”personnalités qualifiéesen raison de leur activité dans le domaine philanthropiqueJean Savydirecteur de la revue Réadaptation, 10, rue de Sèvres, 75007 Paris +Barthelemyconseiller à la cour d'appel de Parisvice-président de la commission dite “de la marque distinctive” +Jean-Max Brunetconseiller au tribunal administratif de Parisprésident de la commission dite “de la marque distinctive” +16 février 199026 janvier 1990la médaille militaireARMEE ACTIVEGENDARMERIE NATIONALEAvec effet du21 décembre 1989Albert (François, Etienne)13 septembre 1957, maréchal des logis-chef; 13 ans 3 mois de services. Tué dans l'accomplissement de son devoir le 20 décembre 1989 +16 février 199026 janvier 1990la médaille militaireARMEE ACTIVEGENDARMERIE NATIONALEAvec effet du21 décembre 1989Chatain (André, Jacques, Cyprien)21 août 1952, gendarme; 16 ans 3 mois de services. Tué dans l'accomplissement de son devoir le 20 décembre 1989 +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesCunha (Maria do Ceu) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesMetadjer (Fathia) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisedémocratique du travail (C.F.D.T.)TitulairesGravier (Marthe) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisedémocratique du travail (C.F.D.T.)TitulairesYanat (Hania) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre de la Fédération nationaledes syndicats d'exploitants agricoles (F.N.S.E.A.)Titulairede Lamer (Pierre) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesBouziri (Saïd) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesGaso-Cuenca (José, Gabriel) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesKhalfi (Ali) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceTitulairesN'Diaye (Momar, Talla) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales d'employeursSur désignation du Conseil national du patronat français (C.N.P.F.) en accord avec la Confédération générale des petites et moyennes entreprises (C.G.P.M.E.)TitulaireLaisne (Yves) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisede l'encadrement (C.G.C.)TitulaireChassagnac (Michel) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération généraledu travail Force ouvrière (C.G.T.-F.O.)TitulairesBernard (Alphonse) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération généraledu travail Force ouvrière (C.G.T.-F.O.)TitulairesFerdaoussi (Mokhtar) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationfrançaise des travailleurs chrétiens (C.F.T.C.)TitulaireTherry (Jean-Pierre) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationgénérale du travail (C.G.T.)TitulairesBellanger (Jean) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationgénérale du travail (C.G.T.)TitulairesPinto Carvalho (José) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Fédération de l'éducation nationale (F.E.N.)TitulaireLe Neouannic (Guy) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsBouabdallah (Nacéra) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsRibault (Véra) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsSalom (Gaye) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre de la Caisse nationale des allocations familiales (C.N.A.F.)Titulaire: le directeur de la Caisse nationale des allocations familiales (C.N.A.F.)SuppléantGrymonpre (Jean) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre de la Caisse nationale des allocations familiales (C.N.A.F.)Titulaire: le directeur de la Caisse nationale des allocations familiales (C.N.A.F.)Suppléant: un représentant désigné par le directeur de la C.N.A.FAu titre de l'Union nationale des associations familiales (U.N.A.F.)TitulaireTenegal (Michel) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre de la Fédération nationaledes syndicats d'exploitants agricoles (F.N.S.E.A.)SuppléantBore (René-Jacques) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsDaboussi (Abdelmajid) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsKharmoudi (Mustapha) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des communautés immigrées résidant en FranceSuppléantsSoumare (Diadie) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales d'employeursSur désignation du Conseil national du patronat français (C.N.P.F.) en accord avec la Confédération générale des petites et moyennes entreprises (C.G.P.M.E.)SuppléantTissie (Georges) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisede l'encadrement (C.G.C.)SuppléantMessina (Georges) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisedémocratique du travail (C.F.D.T.)SuppléantsBouille (François) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération françaisedémocratique du travail (C.F.D.T.)SuppléantsJamal (Lahoussain) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédération généraledu travail Force ouvrière (C.G.T.-F.O.)SuppléantQuemada (Bruno) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationfrançaise des travailleurs chrétiens (C.F.T.C.)SuppléantMartins Ferreira (Manuel) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationgénérale du travail (C.G.T.)SuppléantsLamarre (Jean) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Confédérationgénérale du travail (C.G.T.)SuppléantsSoilihi (Aboudou) +membres du conseil d'administration du Fonds d'action sociale pour les travailleurs immigrés et leurs famillesAu titre des organisations syndicales de salariésSur désignation de la Fédération de l'éducation nationale (F.E.N.)SuppléantTouchard (Francis) +membres de la commission interministérielle des installations nucléaires de base1oreprésentant du ministre de la défenseTitulaireEmile ArnaudIngénieur généralchargé de mission Atome de la délégation générale pour l'armement +membres de la commission interministérielle des installations nucléaires de base3oreprésentants du ministre de l'intérieura) Premier représentant5oreprésentant du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportsTitulairePierre Lehmanndirecteur de l'Institut national de physique nucléaire et de physique des particules (INP2P3) +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiqueb) Deuxième représentantTitulairePierre Bacherdirecteur technique à la direction de l'équipement +président de la commission interministérielle des installations nucléaires de baseRoger Grégoireprésident de section honoraire au Conseil d'Etat +membres de la commission interministérielle des installations nucléaires de base3oreprésentants du ministre de l'intérieura) Premier représentantTitulaire: le haut fonctionnaire chargé des mesures de défenseSuppléant: le directeur, adjoint au directeur général des collectivités localesb) Deuxième représentantTitulaire: le directeur de la sécurité civileSuppléantGuénonchargée de mission pour les installations nucléaires à la sous-direction des risques naturels et technologiques +membres de la commission interministérielle des installations nucléaires de base3oreprésentants du ministre de l'intérieura) Premier représentantTitulaire: le haut fonctionnaire chargé des mesures de défenseSuppléantFrançoise Dauphinattaché principal d'administration centrale +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricoleSuppléant: l'adjoint au sous-directeur de l'aménagement foncier et de l'hydraulique agricole9oreprésentants du ministre de l'industrie et de l'aménagement du territoireb) Deuxième représentantTitulaire: le directeur général de l'énergie et des matières premièresSuppléant: le chef du service nucléaire de la direction générale de l'énergie et des matières premièresc) Troisième représentantTitulaire: le directeur du gaz, de l'électricité et du charbonSuppléant: le chef du service technique de l'énergie électrique et des grands barrages10oreprésentants du ministre de la solidarité, de la santé et de la protection socialea) Premier représentantTitulaire: le directeur général de la santéSuppléantAnnie Richard-Lebrunadministrateur civil, chef du bureau 1B à la sous-direction de la prévention générale et de l'environnement +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricoleSuppléant: l'adjoint au sous-directeur de l'aménagement foncier et de l'hydraulique agricole9oreprésentants du ministre de l'industrie et de l'aménagement du territoireb) Deuxième représentantTitulaire: le directeur général de l'énergie et des matières premièresSuppléant: le chef du service nucléaire de la direction générale de l'énergie et des matières premièresc) Troisième représentantTitulaire: le directeur du gaz, de l'électricité et du charbonSuppléant: le chef du service technique de l'énergie électrique et des grands barrages10oreprésentants du ministre de la solidarité, de la santé et de la protection socialeb) Deuxième représentantTitulaire: le sous-directeur de la prévention générale et de l'environnementSuppléantJoëlle Carmesingénieur sanitaire à la sous-direction de la prévention générale et de l'environnement +membres de la commission interministérielle des installations nucléaires de base18omembres choisisen raison de leur compétence particulière dans le domaine nucléaireSur proposition du ministre de l'industrie et de l'aménagement du territoirea) Titulaire: le président du groupe permanent chargé des réacteurs nucléairesSuppléant: le vice-président du groupe permanent chargé des réacteurs nucléairesb) Titulaire: le président du groupe permanent chargé des installations nucléaires de base autres que les réacteurs nucléaires, à l'exception des installations destinées au stockage à long terme des déchets radioactifsSuppléant: le vice-président du groupe permanent chargé des installations nucléaires de base autres que les réacteurs nucléaires, à l'exception des installations destinées au stockage à long terme des déchets radioactifsSur proposition du ministre de la solidarité, de la santé et de la protection socialechargé de la familleTitulaireClaude ParmentierProfesseurinstitut Gustave-Roussy, à Villejuif +membres de la commission interministérielle des installations nucléaires de base18omembres choisisen raison de leur compétence particulière dans le domaine nucléaireSur proposition du ministre de l'industrie et de l'aménagement du territoirea) Titulaire: le président du groupe permanent chargé des réacteurs nucléairesSuppléant: le vice-président du groupe permanent chargé des réacteurs nucléairesb) Titulaire: le président du groupe permanent chargé des installations nucléaires de base autres que les réacteurs nucléaires, à l'exception des installations destinées au stockage à long terme des déchets radioactifsSuppléantJean-François DuplanProfesseurdirecteur de la fondation Bergonie, à Bordeaux +membres de la commission interministérielle des installations nucléaires de base1oreprésentant du ministre de la défenseSuppléantClaude BelotColonelde la mission Atome +membres de la commission interministérielle des installations nucléaires de base2oreprésentant du ministre du travail, de l'emploi et de la formation professionnelleTitulaire: le directeur des relations du travailSuppléantJean-Luc Pasquierchef du bureau CT4 à la direction des relations du travail +membres de la commission interministérielle des installations nucléaires de base3oreprésentants du ministre de l'intérieura) Premier représentant5oreprésentant du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportsSuppléantJ.-P. Manininspecteur Hygiène et sécurité +membres de la commission interministérielle des installations nucléaires de base3oreprésentants du ministre de l'intérieura) Premier représentantTitulaire: le haut fonctionnaire chargé des mesures de défenseSuppléant: le directeur, adjoint au directeur général des collectivités localesb) Deuxième représentant4oreprésentant du ministre d'Etat, ministre de l'économie, des finances et du budgetTitulaireAlain Bourdelatadministrateur civil +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme16oreprésentant du service central de protection contre les rayonnements ionisantsTitulaire: le directeur du service central de protection contre les rayonnements ionisantsSuppléant: le directeur adjoint du service central de protection contre les rayonnements ionisants17oreprésentant de l'Institut national de la recherche agronomiqueTitulaireRoland Choquetdélégué national prévention de l'I.N.R.A +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme16oreprésentant du service central de protection contre les rayonnements ionisantsTitulaire: le directeur du service central de protection contre les rayonnements ionisantsSuppléantLéon Guégendirecteur de la station de recherches de nutrition de Jouy-en-Josas +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiquea) Premier représentantTitulaire: le chef du département d'analyse de sûretéSuppléant: le chef des services d'analyse de sûreté des réacteurs14oreprésentants d'Electricité de Francea) Premier représentantTitulaireBernard Nocchef de mission sûreté nucléaire au service de la production thermique +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiquea) Premier représentantTitulaire: le chef du département d'analyse de sûretéSuppléant: le chef des services d'analyse de sûreté des réacteursb) Deuxième représentantTitulaire: le chef du département de protection sanitaireSuppléant: le chef des services d'hygiène radiologique13oreprésentant du Centre national de recherche scientifique: TitulaireClaude Teissierinspecteur général de l'hygiène et de la sécurité +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiquea) Premier représentantTitulaire: le chef du département d'analyse de sûretéSuppléant: le chef des services d'analyse de sûreté des réacteursb) Deuxième représentantTitulaire: le chef du département de protection sanitaireSuppléantJean-Claude Franckingénieur, chef du service de sécurité de L.U.R.E +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiquea) Premier représentantTitulaire: le chef du département d'analyse de sûretéSuppléantMichel Debeschef du département sûreté nucléaire +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole12oreprésentants du Commissariat à l'énergie atomiqueb) Deuxième représentantSuppléantRobert Morinadjoint au chef du service études et projets thermiques et nucléaires à la direction de l'équipement +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricole15oreprésentant de l'Institut national de la santé et de la recherche médicaleTitulaire: le directeur général de l'Institut national de la santé et de la recherche médicaleSuppléantJean-Pierre Moroniprofesseur agrégé +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricoleSuppléant: l'adjoint au sous-directeur de l'aménagement foncier et de l'hydraulique agricole9oreprésentants du ministre de l'industrie et de l'aménagement du territoirea) Premier représentantTitulaire: le chef adjoint du service central de sûreté des installations nucléairesSuppléantRoger Flandrinchargé de la quatrième sous-direction du service central de sûreté des installations nucléaires +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricoleSuppléant: l'adjoint au sous-directeur de l'aménagement foncier et de l'hydraulique agricole9oreprésentants du ministre de l'industrie et de l'aménagement du territoireb) Deuxième représentantTitulaire: le directeur général de l'énergie et des matières premièresSuppléant: le chef du service nucléaire de la direction générale de l'énergie et des matières premièresc) Troisième représentantTitulaire: le directeur du gaz, de l'électricité et du charbonSuppléant: le chef du service technique de l'énergie électrique et des grands barrages10oreprésentants du ministre de la solidarité, de la santé et de la protection socialeb) Deuxième représentant11oreprésentant du ministre de l'équipement, du logement, des transports et de la mer (au titre des transports)TitulaireMarius Belmainchef de la mission du transport des matières dangereuses +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanisme8oreprésentant du ministre de l'agriculture et de la forêtTitulaire: le sous-directeur de l'aménagement foncier et de l'hydraulique agricoleSuppléant: l'adjoint au sous-directeur de l'aménagement foncier et de l'hydraulique agricole9oreprésentants du ministre de l'industrie et de l'aménagement du territoireb) Deuxième représentantTitulaire: le directeur général de l'énergie et des matières premièresSuppléant: le chef du service nucléaire de la direction générale de l'énergie et des matières premièresc) Troisième représentantTitulaire: le directeur du gaz, de l'électricité et du charbonSuppléantEmile Bersonsecrétaire général de la commission interministérielle pour le transport des matières dangereuses +membres de la commission interministérielle des installations nucléaires de base6oreprésentant du ministre de l'équipement, du logement, des transports et de la merTitulaire: le directeur de l'architecture et de l'urbanismeSuppléant: le chef du bureau des documents d'urbanisme, des autorisations d'occupation des sols et de la publicité7oreprésentants du secrétaire d'Etat auprès du Premier ministrechargé de l'environnement et de la prévention des risques technologiques et naturels majeursa) Au titre de l'environnementTitulaire: le directeur de l'eau, de la prévention des pollutions et des risquesSuppléant: le chargé de mission pour les questions d'énergie auprès du directeur de l'eau, de la prévention des pollutions et des risquesb) Au titre de la prévention des risques technologiques et naturels majeurs: Titulaire: le chef du service central de sûreté des installations nucléairesSuppléantJacky Ferchauxchargé de mission auprès du chef du service central de sûreté des installations nucléaires +Secrétaire permanentJacques Prospertchargé de mission au service central de sûreté des installations nucléairesPierre Slizewicz +Secrétaire permanent suppléantYves Kaluznychargé de sous-direction au service central de sûreté des installations nucléairesPatick Lebrun +19 décembre 1989Bougon (Pierre)conseiller maître honoraire à la Cour des comptesmembre du Conseil supérieur des chambres régionales des comptes +5 février 1990Holleaux (André)conseiller d'Etatmembre du Conseil supérieur des chambres régionales des comptes +Ollivier (Yvon)préfet des Alpes-Maritimesmembre du Conseil supérieur des chambres régionales des comptes +Denis Delbourgconseiller des affaires étrangèreschargé de missionconseiller technique au cabinet du Premier ministre +Frechet (Marielle)greffier au tribunal d'instance de Dolerégisseur titulaireCzech +Genty (Monique)greffier au tribunal d'instance de Fontenay-le-Comterégisseur titulairePaquet +Bonnenfant (Michèle), épouse Trichetagent technique de bureau au tribunal d'instance de Fontenay-le-Comterégisseur suppléantGenty +chef de cabinet du secrétaire d'Etat auprès du Premier ministrechargé de l'action humanitaireRoger Parentsous-préfetappelé à d'autres fonctions +au cabinet du secrétaire d'Etat auprès du Premier ministrechargé de l'action humanitaireChef de cabinetattaché principal d'administration centraleMichel Amiel +au cabinet du ministre délégué auprès du ministre de l'équipement, du logement, des transports et de la merchargé de la merChef de cabinetJean-Louis BouazizMichel Laneret +23 mars 1973pour 1990 membres du comité de sélectionprévu à l'article 10 du décret du14 mars 1973relatif au statut particulier du corps de l'inspection générale des finances:Chassagne (Yvette)conseiller maître honoraire à la Cour des comptes, présidente +23 mars 1973pour 1990 membres du comité de sélectionprévu à l'article 10 du décret du14 mars 1973relatif au statut particulier du corps de l'inspection générale des finances:Le directeur général de l'administration et de la fonction publique ou son représentantLe chef du service de l'inspection générale des finances ou son représentantBolliet (Anne)inspecteur des finances de 2e classe, représentant du personnel +23 mars 1973pour 1990 membres du comité de sélectionprévu à l'article 10 du décret du14 mars 1973relatif au statut particulier du corps de l'inspection générale des finances:Le directeur général de l'administration et de la fonction publique ou son représentantLe chef du service de l'inspection générale des finances ou son représentantLeclerc (Jean-Pierre)inspecteur général des finances, représentant du personnelau titre d'inspecteur des finances de 1re classe +Bucher (Marie-Claude), épouse Capelleagent technique de bureau au tribunal d'instance de Cambrairégisseur titulaireChampagne +Loret (Maryse)greffierrégisseur titulaireBachelet +Loret (Maryse)greffierrégisseur titulaireBachelet +Ruellou (Gwenaëlle), épouse Le Ducommis au tribunal de grande instance de Saint-Nazairerégisseur titulaireLe Biez +Michèle Zwang-Graillotchargée de mission au cabinet du Président de la République +André Grammontingénieur général d'agronomiedirecteur de l'espace rural et de la forêtJean Armengaud +Régis Paranqueinspecteur général des finances +Louis Monchovetsous-préfet hors classe, sous-préfet de Vienne (1re catégorie)préfet des Alpes-de-Haute-Provence +Pierre Breuilpréfet de la Haute-Loirepréfet hors cadre; il sera placé en position de service détaché +Bernard Leurquinpréfet des Alpes-de-Haute-Provencepréfet hors cadre +Jean-François Lionnetconseiller des affaires étrangères de 1re classe, en fonctions à l'administration centraleambassadeur extraordinaire et plénipotentiaire de la République française en République du GhanaMichel Auchère +Pierre Sebastianipréfet des Ardennespréfet de la Dordogne +membre du comité de l'énergie atomiquepersonnalité qualifiéeen raison de sa compétence dans le domaine scientifique et industrielJean-Jacques Payan +Hélène Mathieudéléguée au développement et aux formations +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Adexpra Radio Dijon Campus (Radio Dijon Campus) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)France (Radio Espérance) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Radio 70 (Radio 70)Association Radio Amitié (Radio Amitié) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Radio 70 (Radio 70)Association Radio Cactus (Radio Cactus) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Radio 70 (Radio 70)Association Radio Valentin Longvic (Radio Valentin Longvic) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Collectif Radio Sud (Radio Sud Besançon) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)de Villers-le-Lac (Radio Collège Villers-le-Lac) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Evasion FM (Evasion FM) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Inter-Med (Beur FM) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Jacques-Prévert (Radio-Prévert) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Les Amis de Radio Horizon (Radio Horizon) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association porte-voix (Radio Cinq)Association portugaise de Besançon (Présence portugaise)Association Voix de l'immigré (Radio V.T.I. [Voix des travailleurs immigrés])C.D.I. (Centre diocésain d'information) (Mont-Roland Radio)Collectif Radio Bip (Bisontine indépendante et populaire) (Collectif Radio Bip)Louis-Pergaud (Radio Collège Pergaud) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Association Vauban organisations et informations radiodiffusées (Radio Vauban Stéréo [Franche-Comté])Association C.D.A.R.S. (Radio Courtoisie) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Parabole 21 (Radio Parabole)Radio Atlantique FM (Radio Atlantique FM) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Bresse (Radio Bresse) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Contact FM (Radio Contact FM) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Decibelle Luzy (Radio Decibelle Luzy) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Decibels (Radio Decibels) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Decize (Radio Decize) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Flotteurs Association (Radio Flotteurs) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Fourvière (Radio Fourvière) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Avallon Force 5 (Radio Avallon Force 2)Radio Metal (Fusion) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Morvan Force 5 (Radio Morvan Force 5)Radio Nevers FM (Radio Nevers) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Morvan Force 5 (Radio Morvan Force 5)Radio Pilouri FM (Radio Espérance) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Val de Morteau (Radio Val de Morteau)Radio Villages (Radio Villages) +à concourir dans le cadre de l'appel aux candidatures du14 novembre 1989susviséCatégorie A97,2 locale U.P.Y. radio (97,2 radio locale)A.E.R.A., Association évangélique de radiodiffusion et audiovisuel (Radio Omega)Association pour la communication radiophonique en Charollais, Brionnais, Bourbonnais et Clunysois (fréquence Charollais rock and roll FM)Association pour la communication radiophonique en val de Saône et Bresse bourguignonne (fréquence Bresse bourguignonne-Radio Swing)Association pour le développement des métiers de la communication chez les jeunes (Radio Biberon toxique)Les Amis du Dimanche matin (fréquence Amitiés Vesoul)Les Sorciers loisirs et culture (Radio Sorciers)Maison des jeunes et de la culture (Radio Escargot)OHM 89 (le Poste)Radio Val de Morteau (Radio Val de Morteau)Tonic FM (Tonic FM) +12 janvier 1990Celeri (Paul)Docteurmédecin général de la santécadresadmisà faire valoir ses droits à la retraite +conseiller technique au cabinet du ministreappelée à d'autres fonctionsHélène Mathieu +conseiller techniqueArielle Texier +Brunet (Agnès), épouse Cibillonagent technique de bureau au tribunal de grande instance d'Albertvillerégisseur suppléantDalla Costa +conseiller technique au cabinet du ministre d'Etat, ministre de l'économie, des finances et du budget, qu'exerçaitAndré Renaudincommissaire contrôleur des assurancesappelé à d'autres fonctions +Pierre Bertinottiadministrateur des P.T.Tconseiller technique au cabinet du ministre d'Etat, ministre de l'économie, des finances et du budget +Brivot (Marie-Pierre), épouse Magnacgreffier au tribunal d'instance de Charollesrégisseur suppléantDeveley +Grasselli (Françoise), épouse Gasparouxsténodactylographe au tribunal de grande instance de Tarasconrégisseur suppléantThiers +8 mars 19909 février 1990ARMEE ACTIVEAu grade de chevalierARMEE DE L'AIRAvec effet du9 janvier 1990Corps des officiers de l'airRomanoff (Nicolas, Marie, Rurik)6 septembre 1965, lieutenant; 4 ans 4 mois de services, 2 ans 6 mois de bonifications. Décédé dans l'accomplissement de son devoir le8 janvier 1990 +Marc Avrdirecteur général de l'Institut national de l'audiovisuel +Jacques Polyprésident du conseil d'administration du Centre de coopération internationale en recherche agronomique pour le développement +Pierre Breuilpréfet hors cadredélégué à l'espace aérien +Michel Lajuspréfetà faire valoir ses droits à la retraite +Dominique Buradministrateur civil hors classedirecteur de l'Office des migrations internationales +Jacqueline Rougiéconseiller référendaire de 1re classe à la Cour des comptes, en disponibilitémaître de conférences associé près l'université de Pauconseiller maître (hors tour) +Christian Cardonconseiller référendaire de 1re classe à la Cour des comptes, en disponibilitédélégué interministériel au projet Euro-Disneyland en Franceconseiller maître (hors tour) +Jean-Michel de Mourguesconseiller référendaire de 1re classe à la Cour des comptes, en disponibilité auprès du ministère de l'économie, des finances et du budgetchef de service près la commission des finances au Sénatconseiller maître (hors tour) +Jean Recoulesconseiller référendaire de 1re classe à la Cour des comptesconseiller maîtreBonacossa +Michel Rocagelconseiller référendaire de 1re classe à la Cour des comptesconseiller maîtreBernard +Michel Mayconseiller maître à la Cour des comptesprésident de chambreBernard +Christian Fremontadministrateur civil hors classe en service détachépréfet de l'Ariège +Roland Hodelpréfet du Jurapréfet du Cher +José Inizanadministrateur civil hors classe en service détachépréfet de la Haute-Loire +Jean-François Denispréfet délégué pour le développement économique auprès du haut-commissaire de la République en Nouvelle-Calédoniepréfet du Jura +5 mars 1990au choixau grade d'attaché principal d'administration centrale de 2e classeDumas (Josette)au 5e échelon, avec une ancienneté conservée de 1 mois +5 mars 1990au choixau grade d'attaché principal d'administration centrale de 2e classeJacquemin-Bilet (Monique)au 7e échelon, avec une ancienneté conservée de 10 mois 28 jours +Jean-Pierre Guyotconseiller des affaires étrangères de 1re classe (Orient), ambassadeur extraordinaire et plénipotentiaire de la République française en République de Zambieambassadeur extraordinaire et plénipotentiaire de la République française auprès du Sultanat d'OmanCharles Jeantelot +directeur adjoint du cabinet du ministre d'Etat, ministre de l'éducation nationale, de la jeunesse et des sportsingénieur en chef des ponts et chausséesRoland PeyletPierre Trincal +Rannou (Jean, Théophile)Général de brigade aériennechef de la division Forces nucléaires à l'état-major des armées +au titre du congé du personnel navigant de l'armée de l'airAu grade de général de brigade aérienneTavernier (Jean-Claude, Joseph, Constant)Colonel du corps des officiers de l'air +au titre du congé du personnel navigant de l'armée de l'airAu grade de général de brigade aérienneVzdoulsky (Serge)Colonel du corps des officiers de l'air +Guyader (Yvon)Général de brigade aériennedirecteur de la circulation aérienne militairevice-président du Conseil supérieur de l'infrastructure et de la navigation aériennes +dans la 1re section du cadre des officiers généraux de l'armée de l'air avec maintien dans leurs fonctionsAu grade de général de brigade aérienneChrétien (Claude, Michel, Emmanuel)Colonel du corps des officiers de l'air +dans la 1re section du cadre des officiers généraux de l'armée de l'air avec maintien dans leurs fonctionsAu grade de général de division aérienneMercier (Georges, Victor, Marie)Général de brigade aérienne +dans la 1re section du cadre des officiers généraux de l'armée de l'air avec maintien dans leurs fonctionsAu grade de général de division aérienneGautier (Claude, Philippe)Général de brigade aérienne +Merlo (Marc, Marie, Maurice)Contre-amiralcommandant l'escadre de la Méditerranée +Tripier (Michel, Pierre, Marie, Louis)Vice-amiralpréfet maritime de la 3e région maritime et commandant en chef pour la Méditerranée +dans la 2e section du cadre des officiers généraux de la marineAu grade de contre-amiralRolland (Jean, Yves)Capitaine de vaisseau +dans la 2e section du cadre des officiers généraux de la marineAu grade de commissaire général de 2e classeJaquemet (Robert, Marie, Jean)Commissaire en chef de 1re classe +dans la 1re section du cadre des officiers généraux de la marineavec maintien dans leurs fonctionsAu grade de contre-amiralLafargue (François, Marcel, Paul, Henri, Georges)Capitaine de vaisseau +dans la 1re section du cadre des officiers généraux de la marineavec maintien dans leurs fonctionsAu grade de contre-amiralRouyer (Christian, Pierre, Antoine)Capitaine de vaisseau +dans la 1re section du cadre des officiers généraux de la marineavec maintien dans leurs fonctionsAu grade de vice-amiralSainte-Claire Deville (Philippe, Maurice)Contre-amiral +dans la 2e section du cadre des officiers générauxAu grade de magistrat généralHugot (Georges, Paul, Hubert)Magistrat militaire de 1re classe +Bergeron (René, Régis, Joseph, Alain, Charles)Contrôleur général des arméespar anticipation etdans la 2e section du cadre des contrôleurs généraux des armées +Philippe-Charles Pailliartdélégué à l'information et à la communication +dans la 2e section du cadre des officiers générauxpar anticipation etDartigues (Bernard, Joseph)Pharmacien chimiste général, pharmacien chimiste chef des services hors classe +dans la 1re section du cadre des officiers généraux avec maintien dans les fonctionsSaint-Blancard (Jacques, Charles, Gabriel)Pharmacien chimiste chef des services de classe normale +Rieunau (Pierre, René)Médecin général, médecin chef des services hors classeinspecteur technique des réserves du service de santé des armées +dans la 2e section du cadre des officiers généraux de l'armée de terrepar anticipation etCostedoat-Lamarque (Pierre, René, Henri)Commissaire général de brigade +dans la 2e section du cadre des officiers généraux de l'armée de terrepar anticipation etChevallier-Rufigny (Jean, Théobald, Marie, Jacques, Hubert)Général de brigade +dans la 2e section du cadre des officiers généraux de l'armée de terrepar anticipation etMartini (André, Honoré)Général de brigade +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeBâton (Jacques, Alfred, Louis)Colonel de l'arme blindée et de la cavalerie +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeCrène (Yves, Gabriel, Jean, Max)Colonel de l'artillerie +dans la 1re section du cadre des officiers généraux de l'armée de terre, avec maintien dans leurs fonctionsAu grade de général de brigadeForterre (Pierre, Henri)Colonel de l'arme blindée et de la cavalerie +Laurent (Jean, André, Robert)Commissaire général de brigadedirecteur du commissariat de l'armée de terre de la 4e région militaire +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeWeyders (Marc, Jean, Lucien)Colonel de l'artillerie +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeAviau de Ternay (Bernard, Marie, Joseph)Colonel de l'infanterie d' +dans la 2e section du cadre des officiers généraux de l'armée de terreAu grade de général de brigadeSchiffer (Pierre, Marie, Emile, Joseph)Colonel de l'arme blindée et de la cavalerie +dans la 2e section du cadre des officiers générauxpar anticipation etPortet (André, Roger, Gaston)Général de brigade +dans la 2e section du cadre des officiers générauxpar anticipation etSibaud (Reynaud, Pierre)Général de division +dans la 1re section du cadre des officiers généraux, avec maintien dans leurs fonctionsAu grade de général de brigadeAndré (Yves, Arthur)Colonel +dans la 1re section du cadre des officiers généraux, avec maintien dans leurs fonctionsAu grade de général de brigadeLe Stéon (Emile, Louis, Ange)Colonel +chef de cabinetchargé des relations avec le Parlement et les élus, exercées au cabinet du ministre de l'industrie et de l'aménagement du territoire parPhilippe-Charles Pailliartappelé à d'autres fonctions +Sylvain Fourcassiechef de cabinet du ministre de l'industrie et de l'aménagement du territoire +conseiller technique au cabinet du ministre d'Etat, ministre de l'économie, des finances et du budget, qu'exerçaitFrançois Mulleradministrateur civil hors classeappelé à d'autres fonctions +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Abrossimov (Christine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Ahmadi (Catherine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Bernard (Marie-Laure) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Brosseau (Anne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Chemla (Eliane) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Cohard (Isabelle) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Cordier (Marie-Pierre) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Dufay (Fabienne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Engel (Laurence) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Goubet-Milhaud (Christine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Granet (Françoise) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Hubault (Catherine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Laigneau (Marianne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Lambolez (Fabienne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Laszlo (Sophie) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Lecourbe (Anne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Malvasio (Florence) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Mangin (Florence) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Meyzonnier (Catherine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Otani (Sylvie) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Philibert (Anne) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Roux (Valérie) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Scemama (Nathalie) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Vergnet (Sylvie) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Acar (Bruno) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Angermann (Frédéric) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Bacquaert (Christophe) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Bannel (Cédric) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Bernard (Michel) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Bigot (Christophe) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Blanc (Yannick) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Brassac (Frédéric) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Brouat (François) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Carayon (François) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Champomier (Jean- Michel) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Collière (Philippe) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Coustet (Olivier) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Couvert-Castera (Olivier) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Cremel (Bruno) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Duchemin (Claude) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Ducrocq (Marc-Antoine) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Dupont (Xavier) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Durand (Michel) +élèves à l'Ecole nationale d'administration et rattachés à la promotion 1990-1992Formery (Benoît) +
      diff --git a/main/tests/server/src/com/google/refine/tests/ProjectManagerStub.java b/main/tests/server/src/com/google/refine/tests/ProjectManagerStub.java index 4c3ee3e86..813f4e17d 100644 --- a/main/tests/server/src/com/google/refine/tests/ProjectManagerStub.java +++ b/main/tests/server/src/com/google/refine/tests/ProjectManagerStub.java @@ -35,13 +35,15 @@ package com.google.refine.tests; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import org.apache.tools.tar.TarOutputStream; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.history.HistoryEntryManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.IMetadata; +import com.google.refine.model.medadata.MetadataFormat; /** * Stub used to make protected methods public for testing @@ -83,7 +85,7 @@ public class ProjectManagerStub extends ProjectManager { } @Override - public void saveMetadata(ProjectMetadata metadata, long projectId) throws Exception { + public void saveMetadata(IMetadata metadata, long projectId) throws Exception { // empty } @@ -103,5 +105,4 @@ public class ProjectManagerStub extends ProjectManager { protected void saveWorkspace() { // empty } - } diff --git a/main/tests/server/src/com/google/refine/tests/ProjectManagerTests.java b/main/tests/server/src/com/google/refine/tests/ProjectManagerTests.java index 0f356f7e5..9b34f400a 100644 --- a/main/tests/server/src/com/google/refine/tests/ProjectManagerTests.java +++ b/main/tests/server/src/com/google/refine/tests/ProjectManagerTests.java @@ -51,8 +51,9 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectMetadata; +import com.google.refine.ProjectManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.process.ProcessManager; import com.google.refine.tests.model.ProjectStub; @@ -73,11 +74,14 @@ public class ProjectManagerTests extends RefineTest { public void SetUp(){ pm = new ProjectManagerStub(); SUT = spy(pm); + project = mock(Project.class); metadata = mock(ProjectMetadata.class); + procmgr = mock(ProcessManager.class); when(project.getProcessManager()).thenReturn(procmgr); when(procmgr.hasPending()).thenReturn(false); // always false for now, but should test separately + when(project.getMetadata()).thenReturn(metadata); // cannot wire metadata directly with project, need mock object } @AfterMethod @@ -90,12 +94,9 @@ public class ProjectManagerTests extends RefineTest { @Test public void canRegisterProject(){ - SUT.registerProject(project, metadata); - AssertProjectRegistered(); - verify(metadata, times(1)).getTags(); - + verify(metadata).getTags(); verifyNoMoreInteractions(project); verifyNoMoreInteractions(metadata); } @@ -110,16 +111,18 @@ public class ProjectManagerTests extends RefineTest { //run test SUT.ensureProjectSaved(project.id); - //assert and verify + //assert and verify it's registered under ProjectManager AssertProjectRegistered(); + try { - verify(SUT, times(1)).saveMetadata(metadata, project.id); + // make sure the ProjectManager does save the metadata + verify(SUT).saveMetadata(metadata, project.id); } catch (Exception e) { Assert.fail(); } this.verifySaveTimeCompared(1); - verify(SUT, times(1)).saveProject(project); - verify(metadata, times(1)).getTags(); + verify(SUT).saveProject(project); + verify(metadata).getTags(); //ensure end verifyNoMoreInteractions(project); @@ -130,25 +133,24 @@ public class ProjectManagerTests extends RefineTest { @Test public void canSaveAllModified(){ + // 1. register project 1 whenGetSaveTimes(project, metadata); //5 minute difference registerProject(project, metadata); - //add a second project to the cache + // 2. add a second project to the cache Project project2 = spy(new ProjectStub(2)); ProjectMetadata metadata2 = mock(ProjectMetadata.class); whenGetSaveTimes(project2, metadata2, 10); //not modified since the last save but within 30 seconds flush limit registerProject(project2, metadata2); - //check that the two projects are not the same + // 3. check that the two projects are not the same Assert.assertFalse(project.id == project2.id); + // 4. save all projects SUT.save(true); - verifySaved(project, metadata); - - verifySaved(project2, metadata2); - - verify(SUT, times(1)).saveWorkspace(); +// verifySaved(project2, metadata2); + verify(SUT).saveWorkspace(); } @Test @@ -160,17 +162,18 @@ public class ProjectManagerTests extends RefineTest { SUT.save(true); - verify(metadata, times(1)).getModified(); - verify(metadata, times(1)).getTags(); - verify(project, times(1)).getProcessManager(); - verify(project, times(2)).getLastSave(); - verify(project, times(1)).dispose(); + verify(metadata).getModified(); + verify(metadata).getTags(); + verify(project, never()).getMetadata(); + verify(project).getProcessManager(); + verify(project).getLastSave(); + verify(project).dispose(); verify(SUT, never()).saveProject(project); Assert.assertEquals(SUT.getProject(0), null); verifyNoMoreInteractions(project); verifyNoMoreInteractions(metadata); - verify(SUT, times(1)).saveWorkspace(); + verify(SUT).saveWorkspace(); } @Test @@ -182,7 +185,7 @@ public class ProjectManagerTests extends RefineTest { verify(SUT, never()).saveProjects(Mockito.anyBoolean()); verify(SUT, never()).saveWorkspace(); - verify(metadata, times(1)).getTags(); + verify(metadata).getTags(); verifyNoMoreInteractions(project); verifyNoMoreInteractions(metadata); } @@ -197,7 +200,7 @@ public class ProjectManagerTests extends RefineTest { SUT.save(false); //not busy verifySaved(project, metadata); - verify(SUT, times(1)).saveWorkspace(); + verify(SUT).saveWorkspace(); } //TODO test canSaveAllModifiedWithRaceCondition @@ -205,10 +208,11 @@ public class ProjectManagerTests extends RefineTest { //-------------helpers------------- protected void registerProject(){ - this.registerProject(project, metadata); + SUT.registerProject(project, metadata); } - protected void registerProject(Project proj, ProjectMetadata meta){ - SUT.registerProject(proj, meta); + + protected void registerProject(Project proj, ProjectMetadata metadata) { + SUT.registerProject(proj, metadata); } protected void AssertProjectRegistered(){ @@ -244,12 +248,17 @@ public class ProjectManagerTests extends RefineTest { verify(metadata, times(times)).getModified(); verify(project, times(times)).getLastSave(); } - + + /** + * @see ProjectManager#save(boolean allModified) + * @param proj + * @param meta + */ protected void verifySaved(Project proj, ProjectMetadata meta){ - verify(meta, times(1)).getModified(); - verify(proj, times(2)).getLastSave(); - verify(SUT, times(1)).saveProject(proj); - verify(meta, times(1)).getTags(); + verify(meta).getModified(); + verify(proj).getLastSave(); + verify(SUT).saveProject(proj); + verify(meta).getTags(); verifyNoMoreInteractions(proj); verifyNoMoreInteractions(meta); diff --git a/main/tests/server/src/com/google/refine/tests/RefineTest.java b/main/tests/server/src/com/google/refine/tests/RefineTest.java index fe998e52b..7640ee55a 100644 --- a/main/tests/server/src/com/google/refine/tests/RefineTest.java +++ b/main/tests/server/src/com/google/refine/tests/RefineTest.java @@ -33,30 +33,188 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; +import com.google.refine.ProjectManager; +import com.google.refine.RefineServlet; +import com.google.refine.importers.SeparatorBasedImporter; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.io.FileProjectManager; import com.google.refine.model.Cell; import com.google.refine.model.Column; +import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.tests.util.TestUtils; import com.google.refine.util.JSONUtilities; +/** + * A base class containing various utilities to help testing Refine. + */ public class RefineTest { protected Logger logger; + + boolean testFailed; + protected File workspaceDir; + protected RefineServlet servlet; + private List projects = new ArrayList(); + private List importingJobs = new ArrayList(); @BeforeSuite public void init() { System.setProperty("log4j.configuration", "tests.log4j.properties"); + try { + workspaceDir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); + File jsonPath = new File(workspaceDir, "workspace.json"); + FileUtils.writeStringToFile(jsonPath, "{\"projectIDs\":[]\n" + + ",\"preferences\":{\"entries\":{\"scripting.starred-expressions\":" + + "{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647," + + "\"list\":[]},\"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]}}}}"); + FileProjectManager.initialize(workspaceDir); + } catch (IOException e) { + workspaceDir = null; + e.printStackTrace(); + } + // This just keeps track of any failed test, for cleanupWorkspace + testFailed = false; + } + + @BeforeMethod + protected void initProjectManager() { + servlet = new RefineServletStub(); + ProjectManager.singleton = new ProjectManagerStub(); + ImportingManager.initialize(servlet); + } + + protected Project createProjectWithColumns(String projectName, String... columnNames) throws IOException, ModelException { + Project project = new Project(); + ProjectMetadata pm = new ProjectMetadata(); + pm.setName(projectName); + ProjectManager.singleton.registerProject(project, pm); + + if (columnNames != null) { + for(String columnName : columnNames) { + int index = project.columnModel.allocateNewCellIndex(); + Column column = new Column(index,columnName); + project.columnModel.addColumn(index, column, true); + } + } + return project; + } + + /** + * Helper to create a project from a CSV encoded as a file. Not much + * control is given on the import options, because this method is intended + * to be a quick way to create a project for a test. For more control over + * the import, just call the importer directly. + * + * @param input + * contents of the CSV file to create the project from + * @return + */ + protected Project createCSVProject(String input) { + return createCSVProject("test project", input); + } + + /** + * Helper to create a project from a CSV encoded as a file. Not much + * control is given on the import options, because this method is intended + * to be a quick way to create a project for a test. For more control over + * the import, just call the importer directly. + * + * The projects created via this method and their importing jobs will be disposed of + * at the end of each test. + * + * @param projectName + * the name of the project to create + * @param input + * the content of the file, encoded as a CSV (with "," as a separator) + * @return + */ + protected Project createCSVProject(String projectName, String input) { + + + Project project = new Project(); + + ProjectMetadata metadata = new ProjectMetadata(); + metadata.setName(projectName); + + JSONObject options = mock(JSONObject.class); + prepareImportOptions(options, ",", -1, 0, 0, 1, false, false); + + ImportingJob job = ImportingManager.createJob(); + + SeparatorBasedImporter importer = new SeparatorBasedImporter(); + + List exceptions = new ArrayList(); + importer.parseOneFile(project, metadata, job, "filesource", new StringReader(input), -1, options, exceptions); + project.update(); + ProjectManager.singleton.registerProject(project, metadata); + + projects.add(project); + importingJobs.add(job); + return project; + } + + /** + * Initializes the importing options for the CSV importer. + * @param options + * @param sep + * @param limit + * @param skip + * @param ignoreLines + * @param headerLines + * @param guessValueType + * @param ignoreQuotes + */ + private void prepareImportOptions(JSONObject options, + String sep, int limit, int skip, int ignoreLines, + int headerLines, boolean guessValueType, boolean ignoreQuotes) { + + whenGetStringOption("separator", options, sep); + whenGetIntegerOption("limit", options, limit); + whenGetIntegerOption("skipDataLines", options, skip); + whenGetIntegerOption("ignoreLines", options, ignoreLines); + whenGetIntegerOption("headerLines", options, headerLines); + whenGetBooleanOption("guessCellValueTypes", options, guessValueType); + whenGetBooleanOption("processQuotes", options, !ignoreQuotes); + whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + } + + /** + * Cleans up the projects and jobs created with createCSVProject + */ + @AfterMethod + protected void cleanupProjectsAndJobs() { + for(ImportingJob job : importingJobs) { + ImportingManager.disposeJob(job.id); + } + for(Project project: projects) { + ProjectManager.singleton.deleteProject(project.id); + } + servlet = null; } /** diff --git a/main/tests/server/src/com/google/refine/tests/browsing/facets/TextSearchFacetTests.java b/main/tests/server/src/com/google/refine/tests/browsing/facets/TextSearchFacetTests.java index 345ee166b..97bf45951 100644 --- a/main/tests/server/src/com/google/refine/tests/browsing/facets/TextSearchFacetTests.java +++ b/main/tests/server/src/com/google/refine/tests/browsing/facets/TextSearchFacetTests.java @@ -33,50 +33,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.browsing.facets; -import static org.mockito.Mockito.mock; - -import java.io.File; import java.io.IOException; -import java.io.StringReader; -import java.util.List; -import java.util.ArrayList; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.RefineServlet; -import com.google.refine.importers.SeparatorBasedImporter; -import com.google.refine.importing.ImportingJob; -import com.google.refine.importing.ImportingManager; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.ModelException; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.browsing.RowFilter; import com.google.refine.browsing.facets.TextSearchFacet; -import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class TextSearchFacetTests extends RefineTest { // dependencies - private RefineServlet servlet; private Project project; - private ProjectMetadata pm; - private JSONObject options; - private ImportingJob job; - private SeparatorBasedImporter importer; private TextSearchFacet textfilter; - private JSONObject textsearchfacet; private RowFilter rowfilter; + private JSONObject textsearchfacet; @Override @BeforeTest @@ -86,41 +66,14 @@ public class TextSearchFacetTests extends RefineTest { @BeforeMethod public void setUp() throws JSONException, IOException, ModelException { - servlet = new RefineServletStub(); - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - pm = new ProjectMetadata(); - pm.setName("TextSearchFacet test"); - ProjectManager.singleton.registerProject(project, pm); - options = mock(JSONObject.class); - - ImportingManager.initialize(servlet); - job = ImportingManager.createJob(); - importer = new SeparatorBasedImporter(); - - String csv = "Value\n" + project = createCSVProject("TextSearchFacet", + "Value\n" + "a\n" + "b\n" + "ab\n" - + "Abc\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + + "Abc\n"); } - - @AfterMethod - public void tearDown() { - ImportingManager.disposeJob(job.id); - ProjectManager.singleton.deleteProject(project.id); - job = null; - project = null; - pm = null; - options = null; - } - + /** * Test to demonstrate the intended behaviour of the function */ @@ -245,21 +198,5 @@ public class TextSearchFacetTests extends RefineTest { Assert.assertEquals(rowfilter.filterRow(project, 2, project.rows.get(2)),false); Assert.assertEquals(rowfilter.filterRow(project, 3, project.rows.get(3)),true); } - - private void prepareOptions( - String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { - - whenGetStringOption("separator", options, sep); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - } - - } diff --git a/main/tests/server/src/com/google/refine/tests/commands/project/SetProjectMetadataCommandTests.java b/main/tests/server/src/com/google/refine/tests/commands/project/SetProjectMetadataCommandTests.java index 0e8ae9a2c..a405f09c3 100644 --- a/main/tests/server/src/com/google/refine/tests/commands/project/SetProjectMetadataCommandTests.java +++ b/main/tests/server/src/com/google/refine/tests/commands/project/SetProjectMetadataCommandTests.java @@ -33,7 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.commands.project; -import static org.mockito.Matchers.anyLong; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -57,9 +57,9 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.commands.project.SetProjectMetadataCommand; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.RefineTest; public class SetProjectMetadataCommandTests extends RefineTest { @@ -102,6 +102,8 @@ public class SetProjectMetadataCommandTests extends RefineTest { // mock dependencies when(request.getParameter("project")).thenReturn(PROJECT_ID); when(projMan.getProject(anyLong())).thenReturn(proj); + + // below two should return the same object when(proj.getMetadata()).thenReturn(metadata); try { @@ -141,7 +143,7 @@ public class SetProjectMetadataCommandTests extends RefineTest { } // verify - verify(request, times(2)).getParameter("project"); + verify(request, times(1)).getParameter("name"); verify(projMan, times(1)).getProject(PROJECT_ID_LONG); verify(response, times(1)) @@ -154,6 +156,7 @@ public class SetProjectMetadataCommandTests extends RefineTest { } verify(pw, times(1)).write("{ \"code\" : \"ok\" }"); +// Assert.assertEquals(proj.getProjectMetadata().getName(), "subject"); Assert.assertEquals(proj.getMetadata().getSubject(), SUBJECT); } @@ -189,7 +192,7 @@ public class SetProjectMetadataCommandTests extends RefineTest { } verify(pw, times(1)).write("{ \"code\" : \"ok\" }"); - JSONObject obj = (JSONObject) proj.getMetadata().getUserMetadata().get(0); + JSONObject obj = (JSONObject) ((ProjectMetadata)proj.getMetadata()).getUserMetadata().get(0); Assert.assertEquals(obj.get("name"), "clientID"); Assert.assertEquals(obj.get("value"), "IBM"); } diff --git a/main/tests/server/src/com/google/refine/tests/commands/project/ValidateOperationTests.java b/main/tests/server/src/com/google/refine/tests/commands/project/ValidateOperationTests.java new file mode 100644 index 000000000..54af83701 --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/commands/project/ValidateOperationTests.java @@ -0,0 +1,269 @@ +package com.google.refine.tests.commands.project; + +import static org.mockito.Mockito.mock; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.everit.json.schema.ValidationException; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.BDDMockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.testng.Assert; + +import com.google.refine.ProjectManager; +import com.google.refine.importers.SeparatorBasedImporter; +import com.google.refine.model.Column; +import com.google.refine.model.medadata.DataPackageMetadata; +import com.google.refine.model.medadata.MetadataFactory; +import com.google.refine.model.medadata.MetadataFormat; +import com.google.refine.model.medadata.validator.ValidateOperation; +import com.google.refine.tests.importers.TsvCsvImporterTests; +import com.google.refine.util.ParsingUtilities; + +import io.frictionlessdata.tableschema.Field; +import io.frictionlessdata.tableschema.exceptions.ForeignKeyException; +import io.frictionlessdata.tableschema.exceptions.PrimaryKeyException; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(MetadataFactory.class) +public class ValidateOperationTests extends TsvCsvImporterTests { + private SeparatorBasedImporter parser = null; + + // variables + private static String input; + private DataPackageMetadata dataPackageMetadata; + + // mocks + private ProjectManager projMan = null; + + private String optionsString; + + @BeforeClass + public static void readData() throws IOException{ + //create input to test with + input = getFileContent("gdp.csv"); + // create an data type issue on the fly + input = input.replace("28434203615.4795", "XXXXXXXXXXXXX"); +// String input = "Country Name,Country Code,Year,Value\n" + +// "Arab World,ARB,1968,25760683041.0826\n" + +// "China, CHN,1968,16289212000\n" + +// "Arab World,ARB,1969,28434203615.4795XXX\n"; + } + + @Before + public void SetUp() throws JSONException, IOException, ValidationException, PrimaryKeyException, ForeignKeyException { + super.setUp(); + + optionsString = "{\"columnNames\": [\"Country Name\",\"Country Code\",\"Year\",\"Value\"]}"; + + // mockup + projMan = mock(ProjectManager.class); + ProjectManager.singleton = projMan; + + dataPackageMetadata = new DataPackageMetadata(); + String content = getJSONContent("datapackage-sample.json"); + dataPackageMetadata.loadFromStream(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8.name()))); + + // mock dependencies + //given + PowerMockito.mockStatic(MetadataFactory.class); + BDDMockito.given(MetadataFactory.buildMetadata(MetadataFormat.DATAPACKAGE_METADATA)).willReturn(dataPackageMetadata); + + parser = new SeparatorBasedImporter(); + parseOneFile(); + } + + @After + public void TearDown() { + projMan = null; + ProjectManager.singleton = null; + project = null; + } + + /** + * type or format error + */ + @Test + public void testTypeorFormatError() { + JSONObject optionObj = new JSONObject(optionsString); + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertTrue(result.getJSONArray("validation-reports").length() > 0, + "should get records in report"); + } + + @Test + public void testMinimumConstraint() { + // options + optionsString = "{\"columnNames\": [\"Year\"]}"; + JSONObject optionObj = new JSONObject(optionsString); + + // add Constraint + String contraintKey = Field.CONSTRAINT_KEY_MINIMUM; + String contraintValue = "1962"; + Column column = project.columnModel.getColumnByName("Year"); + column.setType(Field.FIELD_TYPE_STRING); + addConstraint(column, contraintKey, contraintValue); + + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertTrue(result.getJSONArray("validation-reports").length() > 0, + "should get records in report"); + } + + @Test + public void testMaximumConstraint() { + // options + optionsString = "{\"columnNames\": [\"Year\"]}"; + JSONObject optionObj = new JSONObject(optionsString); + + // add Constraint + String contraintKey = Field.CONSTRAINT_KEY_MAXIMUM; + String contraintValue = "2015"; + Column column = project.columnModel.getColumnByName("Year"); + column.setType(Field.FIELD_TYPE_STRING); + addConstraint(column, contraintKey, contraintValue); + + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertTrue(result.getJSONArray("validation-reports").length() > 0, + "should get records in report"); + } + + @Test + public void testPatternConstraint() { + // options + optionsString = "{\"columnNames\": [\"Year\"]}"; + JSONObject optionObj = new JSONObject(optionsString); + + // add Constraint + String contraintKey = Field.CONSTRAINT_KEY_PATTERN; + String contraintValue = "[0-9]{4}"; + Column column = project.columnModel.getColumnByName("Year"); + column.setType(Field.FIELD_TYPE_STRING); + addConstraint(column, contraintKey, contraintValue); + + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertTrue(result.getJSONArray("validation-reports").length() == 0, + "should NOT get records in re" + + "port"); + } + + @Test + public void testEnumerableConstraint() { + // options + optionsString = "{\"columnNames\": [\"Year\"]}"; + JSONObject optionObj = new JSONObject(optionsString); + + // add Constraint + String contraintKey = Field.CONSTRAINT_KEY_ENUM; + String contraintValueStr = "[\"2010\",\"1990\",\"2015\"]"; + List contraintValue = ParsingUtilities.evaluateJsonStringToArray(contraintValueStr).toList(); + Column column = project.columnModel.getColumnByName("Year"); + column.setType(Field.FIELD_TYPE_STRING); + addConstraint(column, contraintKey, contraintValue); + + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertTrue(result.getJSONArray("validation-reports").length() > 0, + "should get records in report"); + } + + @Test + public void testEnumerableConstraint_ShouldReturnEmpty() { + // options + optionsString = "{\"columnNames\": [\"Year\"]}"; + JSONObject optionObj = new JSONObject(optionsString); + + // add Constraint + String contraintKey = Field.CONSTRAINT_KEY_ENUM; + String contraintValueStr = "["; + for (int i = 1950;i < 2018;i++) { + contraintValueStr += "\"" + i + "\","; + } + contraintValueStr += "]"; + + List contraintValue = ParsingUtilities.evaluateJsonStringToArray(contraintValueStr).toList(); + Column column = project.columnModel.getColumnByName("Year"); + column.setType(Field.FIELD_TYPE_STRING); + addConstraint(column, contraintKey, contraintValue); + + // run + JSONObject result = startValidateOperation(optionObj); + + Assert.assertEquals(result.getJSONArray("validation-reports").length(), 0); + } + + + private void addConstraint(Column column, String contraintKey, Object contraintValue) { + java.lang.reflect.Field f1; + try { + f1 = column.getClass().getDeclaredField("constraints"); + f1.setAccessible(true); + Map existingMap = (HashMap)f1.get(column); + if (existingMap == null) { + existingMap = new HashMap(); + } + existingMap.put(contraintKey, contraintValue); + f1.set(column, existingMap); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + + private JSONObject startValidateOperation(JSONObject options) { + // SUT + JSONObject report = new ValidateOperation(project, options).startProcess(); + + System.out.println("validation report:" + report.toString(2)); + + return report; + } + + private String getJSONContent(String fileName) throws IOException { + InputStream in = this.getClass().getClassLoader() + .getResourceAsStream(fileName); + String content = org.apache.commons.io.IOUtils.toString(in); + + return content; + } + + + private void parseOneFile() throws IOException{ + String sep = ","; + prepareOptions(sep, -1, 0, 0, 1, false, false); + parseOneFile(parser, new StringReader(input)); + + Assert.assertEquals(project.columnModel.columns.size(), 4); + } + + private static String getFileContent(String fileName) throws IOException { + InputStream in = ValidateOperationTests.class.getClassLoader() + .getResourceAsStream(fileName); + String content = org.apache.commons.io.IOUtils.toString(in); + + return content; + } +} diff --git a/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java b/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java index d7e95e015..2fb77fbbb 100644 --- a/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java @@ -40,11 +40,11 @@ import static org.mockito.Mockito.when; import java.io.IOException; import java.io.StringWriter; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.OffsetDateTime; +import java.util.Calendar; +import java.util.Date; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -199,13 +199,13 @@ public class CsvExporterTests extends RefineTest { } @Test - public void exportDateColumns(){ + public void exportDateColumnsPreVersion28(){ CreateGrid(1,2); - LocalDateTime localDate = LocalDateTime.now(); - OffsetDateTime date = OffsetDateTime.now(ZoneId.of("Z")); + Calendar calendar = Calendar.getInstance(); + Date date = new Date(); when(options.getProperty("printColumnHeader")).thenReturn("false"); - project.rows.get(0).cells.set(0, new Cell(localDate, null)); + project.rows.get(0).cells.set(0, new Cell(calendar, null)); project.rows.get(0).cells.set(1, new Cell(date, null)); try { @@ -214,12 +214,11 @@ public class CsvExporterTests extends RefineTest { Assert.fail(); } - String expectedOutput = ParsingUtilities.localDateToString(localDate) + "," + - ParsingUtilities.dateToString(date) + "\n"; + String expectedOutput = ParsingUtilities.instantToLocalDateTimeString(calendar.toInstant()) + "," + + ParsingUtilities.instantToLocalDateTimeString(date.toInstant()) + "\n"; Assert.assertEquals(writer.toString(), expectedOutput); } - //helper methods protected void CreateColumns(int noOfColumns){ @@ -243,4 +242,36 @@ public class CsvExporterTests extends RefineTest { project.rows.add(row); } } + + /** + * Given 2017-12-15T22:30:36.65(Z), convert to 2017-12-15T22:30:36.650(Z) + * @param dateTime + * @return + */ + protected String alignFractionalDigits(String dateTime) { + String[] parts = dateTime.split("\\."); + if (parts.length < 2) + return dateTime; + + String fraction = parts[1].replace("Z", ""); + + return parts[0] + "." + + StringUtils.rightPad(fraction, 3, "0") + + (dateTime.endsWith("Z") ? "Z" : ""); + } + + @Test + public void alignFractionalDigitsTest(){ + String input = "2017-12-15T22:30:36.65"; + String expected = "2017-12-15T22:30:36.650"; + Assert.assertEquals(alignFractionalDigits(input), expected); + } + + @Test + public void alignFractionalDigitsWithZTest(){ + String input = "2017-12-15T22:30:36.65Z"; + String expected = "2017-12-15T22:30:36.650Z"; + Assert.assertEquals(alignFractionalDigits(input), expected); + } + } diff --git a/main/tests/server/src/com/google/refine/tests/exporters/HtmlExporterTests.java b/main/tests/server/src/com/google/refine/tests/exporters/HtmlExporterTests.java index 47b38ca0e..46133ed3a 100644 --- a/main/tests/server/src/com/google/refine/tests/exporters/HtmlExporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/exporters/HtmlExporterTests.java @@ -50,7 +50,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; import com.google.refine.exporters.HtmlTableExporter; import com.google.refine.exporters.WriterExporter; @@ -59,6 +58,7 @@ import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.ProjectManagerStub; import com.google.refine.tests.RefineTest; diff --git a/main/tests/server/src/com/google/refine/tests/exporters/TemplatingExporterTests.java b/main/tests/server/src/com/google/refine/tests/exporters/TemplatingExporterTests.java index d9830a067..49f23aa2f 100644 --- a/main/tests/server/src/com/google/refine/tests/exporters/TemplatingExporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/exporters/TemplatingExporterTests.java @@ -48,7 +48,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; import com.google.refine.exporters.TemplatingExporter; import com.google.refine.exporters.WriterExporter; @@ -57,6 +56,7 @@ import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.ProjectManagerStub; import com.google.refine.tests.RefineTest; diff --git a/main/tests/server/src/com/google/refine/tests/exporters/XlsExporterTests.java b/main/tests/server/src/com/google/refine/tests/exporters/XlsExporterTests.java index f5e18e7f6..93daf4348 100644 --- a/main/tests/server/src/com/google/refine/tests/exporters/XlsExporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/exporters/XlsExporterTests.java @@ -50,7 +50,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; import com.google.refine.exporters.StreamExporter; import com.google.refine.exporters.XlsExporter; @@ -59,6 +58,7 @@ import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.ProjectManagerStub; import com.google.refine.tests.RefineTest; diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java index fbc93b5ff..4145c6ceb 100644 --- a/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java @@ -1,37 +1,22 @@ package com.google.refine.tests.expr.functions; -import static org.mockito.Mockito.mock; - -import java.io.StringReader; -import java.util.ArrayList; import java.util.Calendar; -import java.util.List; import java.util.Properties; -import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.RefineServlet; import com.google.refine.expr.EvalError; import com.google.refine.expr.HasFieldsListImpl; import com.google.refine.expr.WrappedRow; import com.google.refine.grel.ControlFunctionRegistry; import com.google.refine.grel.Function; -import com.google.refine.importers.SeparatorBasedImporter; -import com.google.refine.importing.ImportingJob; -import com.google.refine.importing.ImportingManager; import com.google.refine.model.Project; import com.google.refine.model.Row; -import com.google.refine.tests.ProjectManagerStub; -import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; /** @@ -47,82 +32,33 @@ public class CrossFunctionTests extends RefineTest { } // dependencies - RefineServlet servlet; Project projectGift; Project projectAddress; - ProjectMetadata metadata; - ImportingJob job; - JSONObject options; - SeparatorBasedImporter importer; - + + // data from: https://github.com/OpenRefine/OpenRefine/wiki/GREL-Other-Functions @BeforeMethod public void SetUp() { bindings = new Properties(); - servlet = new RefineServletStub(); - ProjectManager.singleton = new ProjectManagerStub(); - ImportingManager.initialize(servlet); - projectAddress = new Project(); - - job = ImportingManager.createJob(); - options = mock(JSONObject.class); - importer = new SeparatorBasedImporter(); + String projectName = "My Address Book"; + String input = "friend,address\n" + + "john,120 Main St.\n" + + "mary,50 Broadway Ave.\n" + + "john,999 XXXXXX St.\n" // john's 2nd address + + "anne,17 Morning Crescent\n"; + projectAddress = createCSVProject(projectName, input); + + projectName = "Christmas Gifts"; + input = "gift,recipient\n" + + "lamp,mary\n" + + "clock,john\n"; + projectGift = createCSVProject(projectName, input); - createMyAddressBook(); - projectGift = createChristmasGifts(); bindings.put("project", projectGift); - // add a column address based on column recipient bindings.put("columnName", "recipient"); } - // data from: https://github.com/OpenRefine/OpenRefine/wiki/GREL-Other-Functions - private Project createMyAddressBook() { - String projectName = "My Address Book"; - String input = "friend;address\n" - + "john;120 Main St.\n" - + "mary;50 Broadway Ave.\n" - + "john;999 XXXXXX St.\n" // john's 2nd address - + "anne;17 Morning Crescent\n"; - return createProject(projectName, input); - } - - private Project createChristmasGifts() { - String projectName = "Christmas Gifts"; - String input = "gift;recipient\n" - + "lamp;mary\n" - + "clock;john\n"; - return createProject(projectName, input); - } - - private Project createProject(String projectName, String input) { - Project project = new Project(); - ProjectMetadata metadata = new ProjectMetadata(); - - metadata.setName(projectName); - prepareOptions(";", -1, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, metadata, job, "filesource", new StringReader(input), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, metadata); - - return project; - } - - @AfterMethod - public void TearDown() { - ImportingManager.disposeJob(job.id); - ProjectManager.singleton.deleteProject(projectGift.id); - ProjectManager.singleton.deleteProject(projectAddress.id); - job = null; - metadata = null; - projectGift = null; - projectAddress = null; - options = null; - importer = null; - } - - @Test public void crossFunctionOneToOneTest() throws Exception { Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", "mary", "My Address Book", "friend")).get(0)).row); @@ -189,20 +125,4 @@ public class CrossFunctionTests extends RefineTest { return function.call(bindings,args); } } - - - private void prepareOptions( - String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { - - whenGetStringOption("separator", options, sep); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - } - } diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/ExpressionUtilsTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/ExpressionUtilsTests.java new file mode 100644 index 000000000..9f371cb12 --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/ExpressionUtilsTests.java @@ -0,0 +1,77 @@ +/* + +Copyright 2017, Owen Stephens +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.tests.expr.functions; + + +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.expr.ExpressionUtils; +import com.google.refine.tests.RefineTest; + + +public class ExpressionUtilsTests extends RefineTest { + + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + + // -----------------tests------------ + + @Test + public void testSameValueTrue() { + Assert.assertTrue(ExpressionUtils.sameValue(null,null)); + Assert.assertTrue(ExpressionUtils.sameValue("","")); + Assert.assertTrue(ExpressionUtils.sameValue("one","one")); + Assert.assertTrue(ExpressionUtils.sameValue(1,1)); + Assert.assertTrue(ExpressionUtils.sameValue(1.0,1.00)); + Assert.assertTrue(ExpressionUtils.sameValue(true,true)); + } + + @Test + public void testSameValueFalse() { + Assert.assertFalse(ExpressionUtils.sameValue("",null)); + Assert.assertFalse(ExpressionUtils.sameValue(null,"")); + Assert.assertFalse(ExpressionUtils.sameValue("one","two")); + Assert.assertFalse(ExpressionUtils.sameValue(1,2)); + Assert.assertFalse(ExpressionUtils.sameValue(1,1.0)); + Assert.assertFalse(ExpressionUtils.sameValue(true,false)); + } +} diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/FindFunctionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/FindFunctionTests.java new file mode 100644 index 000000000..acfccd90c --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/FindFunctionTests.java @@ -0,0 +1,75 @@ + +package com.google.refine.tests.expr.functions; + +import java.util.Properties; + +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.RefineServlet; +import com.google.refine.grel.ControlFunctionRegistry; +import com.google.refine.grel.Function; +import com.google.refine.tests.RefineServletStub; +import com.google.refine.tests.RefineTest; + +/** + * Test cases for find function. + */ +public class FindFunctionTests extends RefineTest { + static Properties bindings; + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + // dependencies + RefineServlet servlet; + + @BeforeMethod + public void SetUp() { + bindings = new Properties(); + + servlet = new RefineServletStub(); + } + + @AfterMethod + public void TearDown() { + } + + + @Test + public void findFunctionFindAllTest() throws Exception { + String[] matches = (String[]) invoke("find", "This is a test string for testing find.", "test"); + Assert.assertEquals(matches[0], "test"); + Assert.assertEquals(matches[1], "test"); + } + + @Test + public void findFunctionFindAllTest2() throws Exception { + String[] matches = (String[]) invoke("find", "hello 123456 goodbye.", "\\d{6}|hello"); + Assert.assertEquals(matches[0], "hello"); + Assert.assertEquals(matches[1], "123456"); + } + + /** + * Lookup a control function by name and invoke it with a variable number of args + */ + private static Object invoke(String name,Object... args) { + // registry uses static initializer, so no need to set it up + Function function = ControlFunctionRegistry.getFunction(name); + if (function == null) { + throw new IllegalArgumentException("Unknown function "+name); + } + if (args == null) { + return function.call(bindings,new Object[0]); + } else { + return function.call(bindings,args); + } + } +} diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java index 3811f8d4a..cb050e8aa 100644 --- a/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java @@ -33,9 +33,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.expr.functions; -import static org.mockito.Mockito.mock; - -import java.io.File; import java.io.IOException; import java.util.Properties; @@ -47,27 +44,22 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; import com.google.refine.expr.EvalError; import com.google.refine.grel.ControlFunctionRegistry; import com.google.refine.grel.Function; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.Cell; -import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class FunctionTests extends RefineTest { static Properties bindings; Project project; - Properties options; JSONObject engine_config; Engine engine; @@ -80,21 +72,9 @@ public class FunctionTests extends RefineTest { @BeforeMethod public void SetUp() throws IOException, ModelException { + + project = createProjectWithColumns("FunctionTests", "Column A"); bindings = new Properties(); - - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("TNG Test Project"); - ProjectManager.singleton.registerProject(project, pm); - - int index = project.columnModel.allocateNewCellIndex(); - Column column = new Column(index,"Column A"); - project.columnModel.addColumn(index, column, true); - - options = mock(Properties.class); - bindings.put("project", project); // Five rows of a's and five of 1s diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/booleans/BooleanTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/booleans/BooleanTests.java index 387ccff91..0b57420bc 100644 --- a/main/tests/server/src/com/google/refine/tests/expr/functions/booleans/BooleanTests.java +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/booleans/BooleanTests.java @@ -33,13 +33,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.expr.functions.booleans; -import static org.mockito.Mockito.mock; - -import java.io.File; import java.io.IOException; import java.util.Properties; -import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -47,20 +43,15 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.browsing.Engine; import com.google.refine.expr.EvalError; import com.google.refine.grel.ControlFunctionRegistry; import com.google.refine.grel.Function; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.Cell; -import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class BooleanTests extends RefineTest { @@ -83,11 +74,8 @@ public class BooleanTests extends RefineTest { }; - static Properties bindings; - Project project; - Properties options; - JSONObject engine_config; - Engine engine; + static private Properties bindings; + private Project project; @Override @@ -99,19 +87,7 @@ public class BooleanTests extends RefineTest { @BeforeMethod public void SetUp() throws IOException, ModelException { bindings = new Properties(); - - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("TNG Test Project"); - ProjectManager.singleton.registerProject(project, pm); - - int index = project.columnModel.allocateNewCellIndex(); - Column column = new Column(index,"Column A"); - project.columnModel.addColumn(index, column, true); - - options = mock(Properties.class); + project = createProjectWithColumns("BooleanTests", "Column A"); bindings.put("project", project); diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/strings/RangeTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/strings/RangeTests.java new file mode 100644 index 000000000..10117e17b --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/strings/RangeTests.java @@ -0,0 +1,322 @@ +package com.google.refine.tests.expr.functions.strings; + +import java.util.Properties; + +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.expr.EvalError; +import com.google.refine.grel.ControlFunctionRegistry; +import com.google.refine.grel.Function; +import com.google.refine.tests.RefineTest; + +/** + * Tests for the range function. + */ +public class RangeTests extends RefineTest { + + private static Properties bindings; + + private static final Integer[] EMPTY_ARRAY = new Integer[0]; + + private static final Integer[] ONE_AND_THREE = new Integer[] {1, 3}; + private static final Integer[] FIVE_AND_THREE = new Integer[] {5, 3}; + + private static final Integer[] ZERO_TO_TWO = new Integer[] {0, 1, 2}; + private static final Integer[] ONE_TO_FOUR = new Integer[] {1, 2, 3, 4}; + private static final Integer[] FIVE_TO_TWO = new Integer[] {5, 4, 3, 2}; + + private static final Integer[] NEGATIVE_ONE_TO_FOUR = new Integer[] {-1, 0, 1, 2, 3, 4}; + private static final Integer[] ONE_TO_NEGATIVE_FOUR = new Integer[] {1, 0, -1, -2, -3, -4}; + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + @BeforeMethod + public void setUp() { + bindings = new Properties(); + } + + @AfterMethod + public void tearDown() { + bindings = null; + } + + /** + * Lookup a control function by name and invoke it with a variable number of args + */ + private static Object invoke(String name,Object... args) { + // registry uses static initializer, so no need to set it up + Function function = ControlFunctionRegistry.getFunction(name); + if (function == null) { + throw new IllegalArgumentException("Unknown function " + name); + } + if (args == null) { + return function.call(bindings, new Object[0]); + } else { + return function.call(bindings, args); + } + } + + @Test + public void testRangeInvalidParams() { + // Test number of arguments + Assert.assertTrue(invoke("range") instanceof EvalError); + Assert.assertTrue(invoke("range", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "1, 2, 3, 4") instanceof EvalError); + Assert.assertTrue(invoke("range", "1, 2, 3", "4") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "2, 3, 4") instanceof EvalError); + Assert.assertTrue(invoke("range", "1, 2", "3", "4") instanceof EvalError); + Assert.assertTrue(invoke("range", 1, 2, 3, 4) instanceof EvalError); + + // Test invalid single string argument types + Assert.assertTrue(invoke("range", "null") instanceof EvalError); + Assert.assertTrue(invoke("range", "a") instanceof EvalError); + + // Test invalid single string numeric arguments + Assert.assertTrue(invoke("range", "1,") instanceof EvalError); + Assert.assertTrue(invoke("range", ",") instanceof EvalError); + Assert.assertTrue(invoke("range", ",2") instanceof EvalError); + Assert.assertTrue(invoke("range", "1.5") instanceof EvalError); + Assert.assertTrue(invoke("range", ",12.3, 2") instanceof EvalError); + + // Test invalid double string arguments + Assert.assertTrue(invoke("range", "1", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", "1") instanceof EvalError); + + Assert.assertTrue(invoke("range", "1,", "2") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "2,") instanceof EvalError); + Assert.assertTrue(invoke("range", "1.5", "3") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "3.5") instanceof EvalError); + + // Test invalid triple string arguments + Assert.assertTrue(invoke("range", "", "", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", "1", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", "", "1") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "2", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", "1", "2") instanceof EvalError); + + Assert.assertTrue(invoke("range", "1,", "2", "1") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "2,", "1") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "2", "1,") instanceof EvalError); + Assert.assertTrue(invoke("range", "1.5", "3", "1") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "3.5", "1") instanceof EvalError); + Assert.assertTrue(invoke("range", "1", "3,", "1.5") instanceof EvalError); + + // Test invalid numeric arguments + Assert.assertTrue(invoke("range", 1.2) instanceof EvalError); + Assert.assertTrue(invoke("range", 1.2, 4.5) instanceof EvalError); + Assert.assertTrue(invoke("range", 1.2, 5, 3) instanceof EvalError); + + // Test invalid mixed arguments + Assert.assertTrue(invoke("range", 1, "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", 1) instanceof EvalError); + Assert.assertTrue(invoke("range", 1, "a") instanceof EvalError); + Assert.assertTrue(invoke("range", "a", 1) instanceof EvalError); + Assert.assertTrue(invoke("range", 1, "", "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", 1, "") instanceof EvalError); + Assert.assertTrue(invoke("range", "", "", 1) instanceof EvalError); + Assert.assertTrue(invoke("range", 1.5, "2", 1) instanceof EvalError); + } + + @Test + public void testRangeValidSingleStringParams() { + // Test valid single string containing one arg + Assert.assertEquals(((Integer[]) invoke("range", "3")), ZERO_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", " 3 "))), ZERO_TO_TWO); + + // Test valid single string containing two args + Assert.assertEquals(((Integer[]) (invoke("range", "1, 1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5, 1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", " 1 ,5"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5 "))), ONE_TO_FOUR); + + // Test valid single string containing three args + Assert.assertEquals(((Integer[]) (invoke("range", "1, 1, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 1, 1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5, -1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5, 1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5, 2"))), ONE_AND_THREE); + Assert.assertEquals(((Integer[]) (invoke("range", "5, 1, -2"))), FIVE_AND_THREE); + Assert.assertEquals(((Integer[]) (invoke("range", "5, 1, -1"))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", "5, 1, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5, 1, 1"))), EMPTY_ARRAY); + + Assert.assertEquals(((Integer[]) (invoke("range", " 1 , 5, 1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5 ,1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5, 1 "))), ONE_TO_FOUR); + } + + @Test + public void testRangeValidDoubleStringParams() { + // Test valid double string containing two args + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "2", "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "1"))), new Integer[] {-1, 0}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "5"))), ONE_TO_FOUR); + + Assert.assertEquals(((Integer[]) (invoke("range", " -1 ", "1"))), new Integer[] {-1, 0}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", " 5 "))), ONE_TO_FOUR); + + // Test valid double string containing three args + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5, -1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5, 1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5, 1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5, -1"))), ONE_TO_NEGATIVE_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5, 2"))), new Integer[] {-1, 1, 3}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5, -2"))), new Integer[] {1, -1, -3}); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5, 10"))), new Integer[] {-1}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5, -10"))), new Integer[] {1}); + + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, -5", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, -5", "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", "1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, -5", "-1"))), ONE_TO_NEGATIVE_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", "2"))), new Integer[] {-1, 1, 3}); + Assert.assertEquals(((Integer[]) (invoke("range", "1, -5", "-2"))), new Integer[] {1, -1, -3}); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", "10"))), new Integer[] {-1}); + Assert.assertEquals(((Integer[]) (invoke("range", "1, -5", "-10"))), new Integer[] {1}); + + Assert.assertEquals(((Integer[]) (invoke("range", " -1 , 5", "1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5" , "1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5", " 1 "))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", " -1 ", "5, 1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", " 5 , 1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", " -1 ", "5, 1 "))), NEGATIVE_ONE_TO_FOUR); + } + + @Test public void testRangeValidTripleStringParams() { + // Test valid triple string containing three arguments + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5", "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5", "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5", "1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5", "-1"))), ONE_TO_NEGATIVE_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5", "2"))), new Integer[] {-1, 1, 3}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5", "-2"))), new Integer[] {1, -1, -3}); + Assert.assertEquals(((Integer[]) (invoke("range", "-1", "5", "10"))), new Integer[] {-1}); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "-5", "-10"))), new Integer[] {1}); + + Assert.assertEquals(((Integer[]) (invoke("range", " -1 , 5, 1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5 , 1"))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "-1, 5, 1 "))), NEGATIVE_ONE_TO_FOUR); + } + + @Test + public void testRangeValidIntegerParams() { + // Test valid single integer argument + Assert.assertEquals(((Integer[]) (invoke("range", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5))), new Integer[] {0, 1, 2, 3, 4}); + + // Test valid double integer arguments + Assert.assertEquals(((Integer[]) (invoke("range", -1, 5))), NEGATIVE_ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1))), EMPTY_ARRAY); + + // Test valid triple integer arguments + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, -1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, 1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, 1))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, 2))), ONE_AND_THREE); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, -2))), FIVE_AND_THREE); + } + + @Test + public void testRangeValidMixedParams() { + // Test two valid arguments, with a single string arg (containing one arg) and a single Integer arg + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5"))), ONE_TO_FOUR); + + // Test two valid arguments, with a single string arg (containing two args) and a single Integer arg + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5", -1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5", 1))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1, 5", 2))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5, -1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5, 0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5, 1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5, 2"))), ONE_AND_THREE); + + // Test three valid arguments, with a single string arg (containing one arg) and two Integer args + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, -1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, 1))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, 2))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, 1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, -1))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, -2))), FIVE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", -1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", 1))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", 2))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", 1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", -1))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", -2))), FIVE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, "1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 1, 5, "2"))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, "-1"))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", 5, 1, "-2"))), FIVE_AND_THREE); + + // Test three valid arguments, with two string args and one Integer arg + Assert.assertEquals(((Integer[]) (invoke("range", "1", "5", -1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "5", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "5", 1))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1", "5", 2))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", "5", "1", 1))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", "1", 0))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", "1", -1))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", "5", "1", -2))), FIVE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, "1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", "1", 5, "2"))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, "-1"))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", "5", 1, "-2"))), FIVE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", "-1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", "1"))), ONE_TO_FOUR); + Assert.assertEquals(((Integer[]) (invoke("range", 1, "5", "2"))), ONE_AND_THREE); + + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", "1"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", "0"))), EMPTY_ARRAY); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", "-1"))), FIVE_TO_TWO); + Assert.assertEquals(((Integer[]) (invoke("range", 5, "1", "-2"))), FIVE_AND_THREE); + } +} diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/strings/ToFromConversionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/strings/ToFromConversionTests.java index fbc6d9a9e..5db608229 100644 --- a/main/tests/server/src/com/google/refine/tests/expr/functions/strings/ToFromConversionTests.java +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/strings/ToFromConversionTests.java @@ -98,10 +98,9 @@ public class ToFromConversionTests extends RefineTest { @Test public void testToNumber() { -// Assert.assertTrue(invoke("toNumber") instanceof EvalError); - Assert.assertNull(invoke("toNumber")); -// Assert.assertTrue(invoke("toNumber", (Object) null) instanceof EvalError); - Assert.assertNull(invoke("toNumber", (Object) null)); + Assert.assertTrue(invoke("toNumber") instanceof EvalError); + Assert.assertTrue(invoke("toNumber", (Object) null) instanceof EvalError); + Assert.assertTrue(invoke("toNumber", "") instanceof EvalError); Assert.assertTrue(invoke("toNumber", "string") instanceof EvalError); Assert.assertEquals(invoke("toNumber", "0.0"), 0.0); Assert.assertEquals(invoke("toNumber", "123"), Long.valueOf(123)); @@ -128,9 +127,9 @@ public class ToFromConversionTests extends RefineTest { @Test public void testToDate() throws CalendarParserException { -// Assert.assertTrue(invoke("toDate") instanceof EvalError); - Assert.assertNull(invoke("toDate")); + Assert.assertTrue(invoke("toDate") instanceof EvalError); Assert.assertTrue(invoke("toDate", (Object) null) instanceof EvalError); + Assert.assertTrue(invoke("toDate", "") instanceof EvalError); Assert.assertTrue(invoke("toDate", 1.0) instanceof EvalError); Assert.assertTrue(invoke("toDate", "2012-03-01","xxx") instanceof EvalError); // bad format string Assert.assertTrue(invoke("toDate", "2012-03-01") instanceof GregorianCalendar); diff --git a/main/tests/server/src/com/google/refine/tests/history/HistoryTests.java b/main/tests/server/src/com/google/refine/tests/history/HistoryTests.java index 08549a7bf..b29a7e56d 100644 --- a/main/tests/server/src/com/google/refine/tests/history/HistoryTests.java +++ b/main/tests/server/src/com/google/refine/tests/history/HistoryTests.java @@ -47,10 +47,10 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.history.History; import com.google.refine.history.HistoryEntry; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.RefineTest; diff --git a/main/tests/server/src/com/google/refine/tests/importers/ImporterTest.java b/main/tests/server/src/com/google/refine/tests/importers/ImporterTest.java index d5d65e018..3e1f033ed 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/ImporterTest.java +++ b/main/tests/server/src/com/google/refine/tests/importers/ImporterTest.java @@ -5,12 +5,14 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import static org.mockito.Mockito.when; +import org.json.JSONArray; import org.json.JSONObject; import org.mockito.Mockito; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.importers.ImportingParserBase; import com.google.refine.importers.tree.ImportColumnGroup; @@ -19,10 +21,11 @@ import com.google.refine.importers.tree.XmlImportUtilities; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingManager; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; -abstract class ImporterTest extends RefineTest { +abstract public class ImporterTest extends RefineTest { //mock dependencies protected Project project; protected ProjectMetadata metadata; @@ -38,7 +41,9 @@ abstract class ImporterTest extends RefineTest { ImportingManager.initialize(servlet); project = new Project(); metadata = new ProjectMetadata(); - job = ImportingManager.createJob(); + ImportingJob spiedJob = ImportingManager.createJob(); + job = Mockito.spy(spiedJob); + when(job.getRetrievalRecord()).thenReturn(new JSONObject()); options = Mockito.mock(JSONObject.class); } diff --git a/main/tests/server/src/com/google/refine/tests/importers/JsonImporterTests.java b/main/tests/server/src/com/google/refine/tests/importers/JsonImporterTests.java index 35b911a85..3f6ba1973 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/JsonImporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/importers/JsonImporterTests.java @@ -96,7 +96,6 @@ public class JsonImporterTests extends ImporterTest { public void canParseSample(){ RunTest(getSample()); - log(project); assertProjectCreated(project, 4, 6); Row row = project.rows.get(0); @@ -109,7 +108,6 @@ public class JsonImporterTests extends ImporterTest { public void canParseSampleWithDuplicateNestedElements(){ RunTest(getSampleWithDuplicateNestedElements()); - log(project); assertProjectCreated(project, 4, 12); Row row = project.rows.get(0); @@ -125,7 +123,6 @@ public class JsonImporterTests extends ImporterTest { RunTest(getSampleWithLineBreak()); - log(project); assertProjectCreated(project, 4, 6); Row row = project.rows.get(3); @@ -139,7 +136,6 @@ public class JsonImporterTests extends ImporterTest { public void testElementsWithVaryingStructure(){ RunTest(getSampleWithVaryingStructure()); - log(project); assertProjectCreated(project, 5, 6); Assert.assertEquals( project.columnModel.getColumnByCellIndex(4).getName(), JsonImporter.ANONYMOUS + " - genre"); @@ -156,7 +152,6 @@ public class JsonImporterTests extends ImporterTest { @Test public void testElementWithNestedTree(){ RunTest(getSampleWithTreeStructure()); - log(project); assertProjectCreated(project, 5, 6); Assert.assertEquals(project.columnModel.columnGroups.size(),1); @@ -180,7 +175,6 @@ public class JsonImporterTests extends ImporterTest { JSONUtilities.safePut(options, "recordPath", path); RunTest(mqlOutput, options); - log(project); assertProjectCreated(project,3,16); } @@ -214,7 +208,6 @@ public class JsonImporterTests extends ImporterTest { " }\n" + "]\n"; RunTest(ScraperwikiOutput); - log(project); assertProjectCreated(project,9,2); } @@ -299,7 +292,6 @@ public class JsonImporterTests extends ImporterTest { public void testJsonDatatypes(){ RunTest(getSampleWithDataTypes()); - log(project); assertProjectCreated(project, 2, 21,4); Assert.assertEquals( project.columnModel.getColumnByCellIndex(0).getName(), JsonImporter.ANONYMOUS + " - id"); @@ -370,7 +362,6 @@ public class JsonImporterTests extends ImporterTest { String fileName = "grid_small.json"; RunComplexJSONTest(getComplexJSON(fileName)); - log(project); logger.info("************************ columnu number:" + project.columnModel.columns.size() + ". \tcolumn groups number:" + project.columnModel.columnGroups.size() + ".\trow number:" + project.rows.size() + ".\trecord number:" + project.recordModel.getRecordCount()) ; diff --git a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java index 8a2ac99e2..5d714ad98 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java @@ -58,10 +58,10 @@ public class TsvCsvImporterTests extends ImporterTest { } //constants - String SAMPLE_ROW = "NDB_No,Shrt_Desc,Water"; + private String SAMPLE_ROW = "NDB_No,Shrt_Desc,Water"; //System Under Test - SeparatorBasedImporter SUT = null; + private SeparatorBasedImporter SUT = null; @Override @BeforeMethod @@ -487,6 +487,33 @@ public class TsvCsvImporterTests extends ImporterTest { Assert.assertEquals(project.rows.get(0).cells.get(1).value, "data2"); } + + @Test(dataProvider = "CSV-TSV-AutoDetermine") + public void customQuoteCharacter(String sep){ + //create input to test with + String inputSeparator = sep == null ? "\t" : sep; + String input = "'col1'" + inputSeparator + "'col2'" + inputSeparator + "'col3'\n" + + "'data1'" + inputSeparator + "'data2'" + inputSeparator + "'data3'"; + + + try { + prepareOptions(sep, -1, 0, 0, 1, false, false, "'"); + parseOneFile(SUT, new StringReader(input)); + } catch (Exception e) { + Assert.fail("Exception during file parse",e); + } + + Assert.assertEquals(project.columnModel.columns.size(), 3); + Assert.assertEquals(project.columnModel.columns.get(0).getName(), "col1"); + Assert.assertEquals(project.columnModel.columns.get(1).getName(), "col2"); + Assert.assertEquals(project.columnModel.columns.get(2).getName(), "col3"); + Assert.assertEquals(project.rows.size(), 1); + Assert.assertEquals(project.rows.get(0).cells.size(), 3); + Assert.assertEquals(project.rows.get(0).cells.get(0).value, "data1"); + Assert.assertEquals(project.rows.get(0).cells.get(1).value, "data2"); + Assert.assertEquals(project.rows.get(0).cells.get(2).value, "data3"); + } + //---------------------read tests------------------------ @Test public void readCsvWithProperties() { @@ -543,11 +570,18 @@ public class TsvCsvImporterTests extends ImporterTest { }; } - private void prepareOptions( + protected void prepareOptions( + String sep, int limit, int skip, int ignoreLines, + int headerLines, boolean guessValueType, boolean ignoreQuotes) { + prepareOptions(sep, limit, skip, ignoreLines, headerLines, guessValueType, ignoreQuotes, "\""); + } + + protected void prepareOptions( String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { + int headerLines, boolean guessValueType, boolean ignoreQuotes, String quoteCharacter) { whenGetStringOption("separator", options, sep); + whenGetStringOption("quoteCharacter", options, quoteCharacter); whenGetIntegerOption("limit", options, limit); whenGetIntegerOption("skipDataLines", options, skip); whenGetIntegerOption("ignoreLines", options, ignoreLines); diff --git a/main/tests/server/src/com/google/refine/tests/importers/WikitextImporterTests.java b/main/tests/server/src/com/google/refine/tests/importers/WikitextImporterTests.java index 1686e89dc..a9120820a 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/WikitextImporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/importers/WikitextImporterTests.java @@ -80,10 +80,10 @@ public class WikitextImporterTests extends ImporterTest { + "|-\n" + "|}\n"; try { - prepareOptions(0, true, true, null); - parse(input); + prepareOptions(0, 0, true, true, null); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 3); Assert.assertEquals(project.rows.size(), 2); @@ -93,6 +93,35 @@ public class WikitextImporterTests extends ImporterTest { Assert.assertEquals(project.rows.get(1).cells.get(2).value, "f"); } + /** + * Issue #1448 + * https://github.com/OpenRefine/OpenRefine/issues/1448 + */ + @Test + public void readTableWithMisplacedHeaders() { + String input = "\n" + + "{|\n" + + "|-\n" + + "| a || b
      2 || c \n" + + "|-\n" + + "| d\n" + + "! e\n" + + "| f
      \n" + + "|-\n" + + "|}\n"; + try { + prepareOptions(0, 0, true, true, null); + parse(input); + } catch (Exception e) { + Assert.fail("Parsing failed", e); + } + Assert.assertEquals(project.columnModel.columns.size(), 3); + Assert.assertEquals(project.rows.size(), 2); + Assert.assertEquals(project.rows.get(0).cells.size(), 3); + Assert.assertEquals(project.rows.get(1).cells.get(1).value, "e"); + Assert.assertEquals(project.rows.get(1).cells.get(2).value, "f"); + } + @Test public void readTableWithLinks() { // Data credits: Wikipedia contributors, https://de.wikipedia.org/w/index.php?title=Agenturen_der_Europäischen_Union&action=edit @@ -108,10 +137,10 @@ public class WikitextImporterTests extends ImporterTest { +"|}\n"; try { - prepareOptions(0, true, true, "https://de.wikipedia.org/wiki/"); - parse(input); + prepareOptions(0, 0, true, true, "https://de.wikipedia.org/wiki/"); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 3); Assert.assertEquals(project.rows.size(), 3); @@ -153,10 +182,10 @@ public class WikitextImporterTests extends ImporterTest { +"|}\n"; try { - prepareOptions(-1, true, true, null); - parse(input); + prepareOptions(-1, 1, true, true, null); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 7); Assert.assertEquals(project.rows.get(0).cells.get(0).value, "Europäisches Zentrum für die Förderung der Berufsbildung"); @@ -186,14 +215,14 @@ public class WikitextImporterTests extends ImporterTest { +"|}\n"; try { - prepareOptions(-1, true, true, null); - parse(input); + prepareOptions(-1, 1, true, true, null); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 6); - Assert.assertNull(project.rows.get(1).cells.get(2).value); - Assert.assertNull(project.rows.get(1).cells.get(3).value); + Assert.assertNull(project.rows.get(1).cells.get(2)); + Assert.assertNull(project.rows.get(1).cells.get(3)); Assert.assertEquals(project.rows.get(1).cells.get(4).value, "Butter"); } @@ -212,10 +241,10 @@ public class WikitextImporterTests extends ImporterTest { +"|}\n"; try { - prepareOptions(-1, true, true, null); - parse(input); + prepareOptions(-1, 1, true, true, null); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 5); Assert.assertEquals(project.rows.get(0).cells.get(1).value, "b"); @@ -240,10 +269,10 @@ public class WikitextImporterTests extends ImporterTest { +"|}\n"; try { - prepareOptions(-1, true, true, null); - parse(input); + prepareOptions(-1, 1, true, true, null); + parse(input); } catch (Exception e) { - Assert.fail("Parsing failed", e); + Assert.fail("Parsing failed", e); } Assert.assertEquals(project.columnModel.columns.size(), 5); Assert.assertEquals(project.rows.get(0).cells.get(1).value, "b"); @@ -252,6 +281,34 @@ public class WikitextImporterTests extends ImporterTest { Assert.assertEquals(project.rows.get(1).cells.get(4).value, "http://gnu.org"); Assert.assertEquals(project.rows.get(1).cells.get(2).value, "http://microsoft.com/"); } + + /** + * Include templates and image filenames + */ + @Test + public void readTableWithTemplates() { + String input = "\n" + + "{|\n" + + "|-\n" + + "| {{free to read}} || b || c \n" + + "|-\n" + + "| d\n" + + "| [[File:My logo.svg|70px]]\n" + + "| f
      \n" + + "|-\n" + + "|}\n"; + try { + prepareOptions(0, 0, true, true, null); + parse(input); + } catch (Exception e) { + Assert.fail("Parsing failed", e); + } + Assert.assertEquals(project.columnModel.columns.size(), 3); + Assert.assertEquals(project.rows.size(), 2); + Assert.assertEquals(project.rows.get(0).cells.size(), 3); + Assert.assertEquals(project.rows.get(0).cells.get(0).value, "{{free to read}}"); + Assert.assertEquals(project.rows.get(1).cells.get(1).value, "[[File:My logo.svg]]"); + } //--helpers-- @@ -260,16 +317,17 @@ public class WikitextImporterTests extends ImporterTest { } private void prepareOptions( - int limit, boolean blankSpanningCells, + int limit, int headerLines, boolean blankSpanningCells, boolean guessValueType, String wikiUrl) { whenGetIntegerOption("limit", options, limit); + whenGetIntegerOption("headerLines", options, headerLines); whenGetBooleanOption("guessCellValueTypes", options, guessValueType); whenGetBooleanOption("blankSpanningCells", options, blankSpanningCells); whenGetBooleanOption("storeBlankCellsAsNulls", options, true); whenGetBooleanOption("parseReferences", options, true); + whenGetBooleanOption("includeRawTemplates", options, true); whenGetStringOption("wikiUrl", options, wikiUrl); - whenGetIntegerOption("headerLines", options, 1); whenGetStringOption("reconService", options, "https://tools.wmflabs.org/openrefine-wikidata/en/api"); } } diff --git a/main/tests/server/src/com/google/refine/tests/importing/ImportingUtilitiesTests.java b/main/tests/server/src/com/google/refine/tests/importing/ImportingUtilitiesTests.java index 850c45df3..c4a13daa6 100644 --- a/main/tests/server/src/com/google/refine/tests/importing/ImportingUtilitiesTests.java +++ b/main/tests/server/src/com/google/refine/tests/importing/ImportingUtilitiesTests.java @@ -1,21 +1,76 @@ + package com.google.refine.tests.importing; +import java.io.InputStream; +import java.util.LinkedList; + +import org.json.JSONArray; import org.json.JSONObject; import org.testng.Assert; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.refine.ProjectMetadata; +import com.google.refine.importers.XmlImporter; +import com.google.refine.importers.tree.TreeImportingParserBase; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; import com.google.refine.importing.ImportingUtilities; -import com.google.refine.tests.RefineTest; +import com.google.refine.model.Column; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.tests.importers.ImporterTest; +import com.google.refine.util.JSONUtilities; -public class ImportingUtilitiesTests extends RefineTest { +public class ImportingUtilitiesTests extends ImporterTest { - @Test - public void createProjectMetadataTest() throws Exception { - JSONObject optionObj = new JSONObject("{\"projectName\":\"acme\",\"projectTags\":[],\"created\":\"2017-12-18T13:28:40.659\",\"modified\":\"2017-12-20T09:28:06.654\",\"creator\":\"\",\"contributors\":\"\",\"subject\":\"\",\"description\":\"\",\"rowCount\":50,\"customMetadata\":{}}"); - ProjectMetadata pm = ImportingUtilities.createProjectMetadata(optionObj); + @Override + @BeforeMethod + public void setUp(){ + super.setUp(); + } + + @Test + public void createProjectMetadataTest() + throws Exception { + JSONObject optionObj = new JSONObject( + "{\"projectName\":\"acme\",\"projectTags\":[],\"created\":\"2017-12-18T13:28:40.659\",\"modified\":\"2017-12-20T09:28:06.654\",\"creator\":\"\",\"contributors\":\"\",\"subject\":\"\",\"description\":\"\",\"rowCount\":50,\"customMetadata\":{}}"); + ProjectMetadata pm = ImportingUtilities.createProjectMetadata(optionObj); Assert.assertEquals(pm.getName(), "acme"); Assert.assertEquals(pm.getEncoding(), "UTF-8"); Assert.assertTrue(pm.getTags().length == 0); } + + @Test + public void inferColumnTypeTest() + throws Exception { + ImportingManager.registerFormat("text/xml", "XML files", "XmlParserUI", new com.google.refine.importers.XmlImporter()); + XmlImporter xmlImporter = new XmlImporter(); + String fileName = "jorf.xml"; + InputStream in = this.getClass().getClassLoader() + .getResourceAsStream(fileName); + options = getNestedOptions(job, xmlImporter); + job.getRetrievalRecord(); + + parseOneInputStream(new XmlImporter(), + in, + options); + + ImportingUtilities.inferColumnType(project); + + Assert.assertTrue(project.columnModel.columns.size() == 58); + Assert.assertTrue(project.columnModel.getColumnByName("result - source_id").getType().equals("string")); + Assert.assertTrue(project.columnModel.getColumnByName("result - person - sexe").getType().equals("boolean")); + } + + private JSONObject getNestedOptions(ImportingJob job, TreeImportingParserBase parser) { + JSONObject options = parser.createParserUIInitializationData( + job, new LinkedList(), "text/json"); + + JSONArray path = new JSONArray(); + JSONUtilities.append(path, "results"); + JSONUtilities.append(path, "result"); +// JSONUtilities.append(path, "object"); + + JSONUtilities.safePut(options, "recordPath", path); + return options; + } } diff --git a/main/tests/server/src/com/google/refine/tests/model/CacheTests.java b/main/tests/server/src/com/google/refine/tests/model/CacheTests.java index 3c3f1c33c..8acb8f49b 100644 --- a/main/tests/server/src/com/google/refine/tests/model/CacheTests.java +++ b/main/tests/server/src/com/google/refine/tests/model/CacheTests.java @@ -32,10 +32,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.google.refine.tests.model; - -import static org.mockito.Mockito.mock; - -import java.io.File; import java.io.IOException; import java.util.Properties; @@ -48,32 +44,22 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; import com.google.refine.browsing.RowVisitor; import com.google.refine.expr.functions.FacetCount; import com.google.refine.grel.Function; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.Cell; -import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.operations.EngineDependentOperation; import com.google.refine.operations.row.RowRemovalOperation; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class CacheTests extends RefineTest { - - //{project=1718051861971, engine= ...} - //engine={ "facets" : ..., "mode":"row-based"}} - //facets = [{"type":"list","name":"row","columnName":"row","expression":"facetCount(value, 'value', 'row') > 1","omitBlank":false,"omitError":false,"selection":[],"selectBlank":false,"selectError":false,"invert":false}] - - // {project=1718051861971, engine= - + // Equivalent to duplicate facet on Column A with true selected static final String ENGINE_JSON_DUPLICATES = "{\"facets\":[{\"type\":\"list\",\"name\":\"facet A\",\"columnName\":\"Column A\",\"expression\":\"facetCount(value, 'value', 'Column A') > 1\",\"omitBlank\":false,\"omitError\":false,\"selection\":[{\"v\":{\"v\":true,\"l\":\"true\"}}],\"selectBlank\":false,\"selectError\":false,\"invert\":false}],\"mode\":\"row-based\"}}"; @@ -92,21 +78,10 @@ public class CacheTests extends RefineTest { @BeforeMethod public void SetUp() throws JSONException, IOException, ModelException { - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("TNG Test Project"); - ProjectManager.singleton.registerProject(project, pm); - - int index = project.columnModel.allocateNewCellIndex(); - Column column = new Column(index,"Column A"); - project.columnModel.addColumn(index, column, true); + project = createProjectWithColumns("CacheTests", "Column A"); - options = mock(Properties.class); engine = new Engine(project); engine_config = new JSONObject(ENGINE_JSON_DUPLICATES); -// engine_config.getJSONArray("facets").getJSONObject(0).getJSONArray("selection").put(new JSONArray()); engine.initializeFromJSON(engine_config); engine.setMode(Engine.Mode.RowBased); @@ -118,7 +93,6 @@ public class CacheTests extends RefineTest { @AfterMethod public void TearDown() { project = null; - options = null; engine = null; bindings = null; } diff --git a/main/tests/server/src/com/google/refine/tests/model/ReconTests.java b/main/tests/server/src/com/google/refine/tests/model/ReconTests.java index 0b4a04411..5d6466c99 100644 --- a/main/tests/server/src/com/google/refine/tests/model/ReconTests.java +++ b/main/tests/server/src/com/google/refine/tests/model/ReconTests.java @@ -2,11 +2,13 @@ package com.google.refine.tests.model; import java.util.ArrayList; +import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.refine.model.recon.ReconConfig; import com.google.refine.model.recon.StandardReconConfig; import com.google.refine.tests.RefineTest; @@ -44,4 +46,32 @@ public class ReconTests extends RefineTest { Assert.assertTrue(!Double.isInfinite(r)); Assert.assertTrue(!Double.isNaN(r)); } + + /** + * Regression for issue #1517: + * JSON deserialization exception due to the upgrade of org.json library in data package PR + * @throws Exception + */ + @Test + public void limitJSONKeyTest() throws Exception { + JSONObject obj = new JSONObject( + " {\n" + + " \"mode\": \"standard-service\",\n" + + " \"service\": \"https://tools.wmflabs.org/openrefine-wikidata/en/api\",\n" + + " \"identifierSpace\": \"http://www.wikidata.org/entity/\",\n" + + " \"schemaSpace\": \"http://www.wikidata.org/prop/direct/\",\n" + + " \"type\": {\n" + + " \"id\": \"Q13442814\",\n" + + " \"name\": \"scientific article\"\n" + + " },\n" + + " \"autoMatch\": true,\n" + + " \"columnDetails\": [],\n" + + " \"limit\": 0\n" + + " }"); + + ReconConfig config = StandardReconConfig.reconstruct(obj); + + // Assert the object is created + Assert.assertTrue(config != null); + } } diff --git a/main/tests/server/src/com/google/refine/tests/model/TextSearchFacetTests.java b/main/tests/server/src/com/google/refine/tests/model/TextSearchFacetTests.java new file mode 100644 index 000000000..38ed2f299 --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/model/TextSearchFacetTests.java @@ -0,0 +1,265 @@ +/* + +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +package com.google.refine.tests.model; + +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.ProjectManager; +import com.google.refine.RefineServlet; +import com.google.refine.browsing.RowFilter; +import com.google.refine.browsing.facets.TextSearchFacet; +import com.google.refine.importers.SeparatorBasedImporter; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.io.FileProjectManager; +import com.google.refine.model.ModelException; +import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; +import com.google.refine.tests.RefineServletStub; +import com.google.refine.tests.RefineTest; +import com.google.refine.tests.util.TestUtils; + + +public class TextSearchFacetTests extends RefineTest { + // dependencies + private RefineServlet servlet; + private Project project; + private ProjectMetadata pm; + private JSONObject options; + private ImportingJob job; + private SeparatorBasedImporter importer; + private TextSearchFacet textfilter; + private JSONObject textsearchfacet; + private RowFilter rowfilter; + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + @BeforeMethod + public void setUp() throws JSONException, IOException, ModelException { + servlet = new RefineServletStub(); + File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); + FileProjectManager.initialize(dir); + project = new Project(); + pm = new ProjectMetadata(); + pm.setName("TextSearchFacet test"); + ProjectManager.singleton.registerProject(project, pm); + options = mock(JSONObject.class); + + ImportingManager.initialize(servlet); + job = ImportingManager.createJob(); + importer = new SeparatorBasedImporter(); + + String csv = "Value\n" + + "a\n" + + "b\n" + + "ab\n" + + "Abc\n"; + prepareOptions(",", 10, 0, 0, 1, false, false); + List exceptions = new ArrayList(); + importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); + project.update(); + ProjectManager.singleton.registerProject(project, pm); + } + + @AfterMethod + public void tearDown() { + ImportingManager.disposeJob(job.id); + ProjectManager.singleton.deleteProject(project.id); + job = null; + project = null; + pm = null; + options = null; + } + + /** + * Test to demonstrate the intended behaviour of the function + */ + + @Test + public void testTextFilter() throws Exception { + //Apply text filter "a" + + //Column: "Value" + //Filter Query: "a" + //Mode: "text" + //Case sensitive: False + //Invert: False + String filter = "{\"type\":\"text\"," + + "\"name\":\"Value\"," + + "\"columnName\":\"Value\"," + + "\"mode\":\"text\"," + + "\"caseSensitive\":false," + + "\"invert\":false," + + "\"query\":\"a\"}"; + + //Add the facet to the project and create a row filter + textfilter = new TextSearchFacet(); + textsearchfacet = new JSONObject(filter); + textfilter.initializeFromJSON(project,textsearchfacet); + rowfilter = textfilter.getRowFilter(project); + + //Check each row in the project against the filter + Assert.assertEquals(rowfilter.filterRow(project, 0, project.rows.get(0)),true); + Assert.assertEquals(rowfilter.filterRow(project, 1, project.rows.get(1)),false); + Assert.assertEquals(rowfilter.filterRow(project, 2, project.rows.get(2)),true); + Assert.assertEquals(rowfilter.filterRow(project, 3, project.rows.get(3)),true); + } + + @Test + public void testInvertedTextFilter() throws Exception { + //Apply inverted text filter "a" + + //Column: "Value" + //Filter Query: "a" + //Mode: "text" + //Case sensitive: False + //Invert: True + String filter = "{\"type\":\"text\"," + + "\"name\":\"Value\"," + + "\"columnName\":\"Value\"," + + "\"mode\":\"text\"," + + "\"caseSensitive\":false," + + "\"invert\":true," + + "\"query\":\"a\"}"; + + //Add the facet to the project and create a row filter + textfilter = new TextSearchFacet(); + textsearchfacet = new JSONObject(filter); + textfilter.initializeFromJSON(project,textsearchfacet); + rowfilter = textfilter.getRowFilter(project); + + //Check each row in the project against the filter + Assert.assertEquals(rowfilter.filterRow(project, 0, project.rows.get(0)),false); + Assert.assertEquals(rowfilter.filterRow(project, 1, project.rows.get(1)),true); + Assert.assertEquals(rowfilter.filterRow(project, 2, project.rows.get(2)),false); + Assert.assertEquals(rowfilter.filterRow(project, 3, project.rows.get(3)),false); + } + + @Test + public void testRegExFilter() throws Exception { + //Apply regular expression filter "[bc]" + + //Column: "Value" + //Filter Query: "[bc]" + //Mode: "regex" + //Case sensitive: False + //Invert: False + String filter = "{\"type\":\"text\"," + + "\"name\":\"Value\"," + + "\"columnName\":\"Value\"," + + "\"mode\":\"regex\"," + + "\"caseSensitive\":false," + + "\"invert\":false," + + "\"query\":\"[bc]\"}"; + + //Add the facet to the project and create a row filter + textfilter = new TextSearchFacet(); + textsearchfacet = new JSONObject(filter); + textfilter.initializeFromJSON(project,textsearchfacet); + rowfilter = textfilter.getRowFilter(project); + + //Check each row in the project against the filter + Assert.assertEquals(rowfilter.filterRow(project, 0, project.rows.get(0)),false); + Assert.assertEquals(rowfilter.filterRow(project, 1, project.rows.get(1)),true); + Assert.assertEquals(rowfilter.filterRow(project, 2, project.rows.get(2)),true); + Assert.assertEquals(rowfilter.filterRow(project, 3, project.rows.get(3)),true); + } + + @Test + public void testCaseSensitiveFilter() throws Exception { + //Apply case-sensitive filter "A" + + //Column: "Value" + //Filter Query: "A" + //Mode: "text" + //Case sensitive: True + //Invert: False + String filter = "{\"type\":\"text\"," + + "\"name\":\"Value\"," + + "\"columnName\":\"Value\"," + + "\"mode\":\"text\"," + + "\"caseSensitive\":true," + + "\"invert\":false," + + "\"query\":\"A\"}"; + + //Add the facet to the project and create a row filter + textfilter = new TextSearchFacet(); + textsearchfacet = new JSONObject(filter); + textfilter.initializeFromJSON(project,textsearchfacet); + rowfilter = textfilter.getRowFilter(project); + + //Check each row in the project against the filter + //Expect to retrieve one row containing "Abc" + Assert.assertEquals(rowfilter.filterRow(project, 0, project.rows.get(0)),false); + Assert.assertEquals(rowfilter.filterRow(project, 1, project.rows.get(1)),false); + Assert.assertEquals(rowfilter.filterRow(project, 2, project.rows.get(2)),false); + Assert.assertEquals(rowfilter.filterRow(project, 3, project.rows.get(3)),true); + } + + private void prepareOptions( + String sep, int limit, int skip, int ignoreLines, + int headerLines, boolean guessValueType, boolean ignoreQuotes) { + + whenGetStringOption("separator", options, sep); + whenGetIntegerOption("limit", options, limit); + whenGetIntegerOption("skipDataLines", options, skip); + whenGetIntegerOption("ignoreLines", options, ignoreLines); + whenGetIntegerOption("headerLines", options, headerLines); + whenGetBooleanOption("guessCellValueTypes", options, guessValueType); + whenGetBooleanOption("processQuotes", options, !ignoreQuotes); + whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + } + + +} + diff --git a/main/tests/server/src/com/google/refine/tests/model/UrlFetchingTests.java b/main/tests/server/src/com/google/refine/tests/model/UrlFetchingTests.java index 66c4564e8..44101862c 100644 --- a/main/tests/server/src/com/google/refine/tests/model/UrlFetchingTests.java +++ b/main/tests/server/src/com/google/refine/tests/model/UrlFetchingTests.java @@ -33,39 +33,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.model; -import static org.mockito.Mockito.mock; - -import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.util.Properties; import org.json.JSONException; import org.json.JSONObject; +import org.json.JSONArray; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.browsing.Engine; -import com.google.refine.io.FileProjectManager; import com.google.refine.expr.ExpressionUtils; import com.google.refine.model.Cell; -import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.Row; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.process.Process; import com.google.refine.process.ProcessManager; import com.google.refine.operations.OnError; import com.google.refine.operations.EngineDependentOperation; import com.google.refine.operations.column.ColumnAdditionByFetchingURLsOperation; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class UrlFetchingTests extends RefineTest { @@ -79,44 +71,15 @@ public class UrlFetchingTests extends RefineTest { } // dependencies - Project project; - Properties options; - JSONObject engine_config; - Engine engine; - Properties bindings; + private Project project; + private Properties options; + private JSONObject engine_config; @BeforeMethod public void SetUp() throws JSONException, IOException, ModelException { - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("URL Fetching Test Project"); - ProjectManager.singleton.registerProject(project, pm); - - int index = project.columnModel.allocateNewCellIndex(); - Column column = new Column(index,"fruits"); - project.columnModel.addColumn(index, column, true); - - options = mock(Properties.class); - engine = new Engine(project); - engine_config = new JSONObject(ENGINE_JSON_URLS); - engine.initializeFromJSON(engine_config); - engine.setMode(Engine.Mode.RowBased); - - bindings = new Properties(); - bindings.put("project", project); - + project = createProjectWithColumns("UrlFetchingTests", "fruits"); } - @AfterMethod - public void TearDown() { - project = null; - options = null; - engine = null; - bindings = null; - } - private boolean isHostReachable(String host, int timeout){ boolean state = false; @@ -143,6 +106,7 @@ public class UrlFetchingTests extends RefineTest { row.setCell(0, new Cell(i < 5 ? "apple":"orange", null)); project.rows.add(row); } + EngineDependentOperation op = new ColumnAdditionByFetchingURLsOperation(engine_config, "fruits", "\"https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new&city=\"+value", @@ -150,7 +114,8 @@ public class UrlFetchingTests extends RefineTest { "rand", 1, 500, - true); + true, + null); ProcessManager pm = project.getProcessManager(); Process process = op.createProcess(project, options); process.startPerforming(pm); @@ -169,7 +134,7 @@ public class UrlFetchingTests extends RefineTest { // Inspect rows String ref_val = (String)project.rows.get(0).getCellValue(1).toString(); - if (ref_val.startsWith("HTTP error")) + if (ref_val.startsWith("HTTP error")) return; Assert.assertTrue(ref_val != "apple"); // just to make sure I picked the right column for (int i = 1; i < 4; i++) { @@ -177,7 +142,7 @@ public class UrlFetchingTests extends RefineTest { // all random values should be equal due to caching Assert.assertEquals(project.rows.get(i).getCellValue(1).toString(), ref_val); } - Assert.assertFalse(process.isRunning()); + Assert.assertFalse(process.isRunning()); } /** @@ -195,6 +160,7 @@ public class UrlFetchingTests extends RefineTest { Row row2 = new Row(2); row2.setCell(0, new Cell("http://anursiebcuiesldcresturce.detur/anusclbc", null)); // well-formed but invalid project.rows.add(row2); + EngineDependentOperation op = new ColumnAdditionByFetchingURLsOperation(engine_config, "fruits", "value", @@ -202,7 +168,9 @@ public class UrlFetchingTests extends RefineTest { "junk", 1, 50, - true); + true, + null); + ProcessManager pm = project.getProcessManager(); Process process = op.createProcess(project, options); process.startPerforming(pm); @@ -221,4 +189,59 @@ public class UrlFetchingTests extends RefineTest { Assert.assertTrue(ExpressionUtils.isError(project.rows.get(2).getCellValue(newCol))); } + @Test + public void testHttpHeaders() throws Exception { + Row row0 = new Row(2); + row0.setCell(0, new Cell("http://headers.jsontest.com", null)); + /* + http://headers.jsontest.com is a service which returns the HTTP request headers + as JSON. For example: + { + "X-Cloud-Trace-Context": "579a1a2ee5c778dfc0810a3bf131ba4e/11053223648711966807", + "Authorization": "Basic", + "Host": "headers.jsontest.com", + "User-Agent": "OpenRefine", + "Accept": "*" + } + */ + + project.rows.add(row0); + + String userAgentValue = "OpenRefine"; + String authorizationValue = "Basic"; + String acceptValue = "*/*"; + String jsonString = "[{\"name\": \"authorization\",\"value\": \""+authorizationValue+ + "\"},{\"name\": \"user-agent\",\"value\": \""+userAgentValue+ + "\"},{\"name\": \"accept\",\"value\": \""+acceptValue+"\"}]"; + + JSONArray httpHeadersJson = new JSONArray(jsonString); + + EngineDependentOperation op = new ColumnAdditionByFetchingURLsOperation(engine_config, + "fruits", + "value", + OnError.StoreError, + "junk", + 1, + 50, + true, + httpHeadersJson); + ProcessManager pm = project.getProcessManager(); + Process process = op.createProcess(project, options); + process.startPerforming(pm); + Assert.assertTrue(process.isRunning()); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + Assert.fail("Test interrupted"); + } + Assert.assertFalse(process.isRunning()); + + int newCol = project.columnModel.getColumnByName("junk").getCellIndex(); + JSONObject headersUsed = new JSONObject(project.rows.get(0).getCellValue(newCol).toString()); + // Inspect the results we got from remote service + Assert.assertEquals(headersUsed.getString("User-Agent"), userAgentValue); + Assert.assertEquals(headersUsed.getString("Authorization"), authorizationValue); + Assert.assertEquals(headersUsed.getString("Accept"), acceptValue); + } + } diff --git a/main/tests/server/src/com/google/refine/tests/model/changes/MassChangeTests.java b/main/tests/server/src/com/google/refine/tests/model/changes/MassChangeTests.java index 1de455304..b0b631340 100644 --- a/main/tests/server/src/com/google/refine/tests/model/changes/MassChangeTests.java +++ b/main/tests/server/src/com/google/refine/tests/model/changes/MassChangeTests.java @@ -3,7 +3,6 @@ package com.google.refine.tests.model.changes; import static org.testng.AssertJUnit.assertTrue; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -13,17 +12,13 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.model.changes.CellAtRow; import com.google.refine.model.changes.ColumnAdditionChange; import com.google.refine.model.changes.MassChange; import com.google.refine.history.Change; -import com.google.refine.io.FileProjectManager; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class MassChangeTests extends RefineTest { @@ -38,12 +33,7 @@ public class MassChangeTests extends RefineTest { @BeforeMethod public void SetUp() throws IOException, ModelException { - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("TNG Test Project"); - ProjectManager.singleton.registerProject(project, pm); + project = createProjectWithColumns("MassChangeTest"); } /** diff --git a/main/tests/server/src/com/google/refine/tests/operations/cell/JoinMultiValuedCellsTests.java b/main/tests/server/src/com/google/refine/tests/operations/cell/JoinMultiValuedCellsTests.java index aa32ef2b0..fbac646cf 100644 --- a/main/tests/server/src/com/google/refine/tests/operations/cell/JoinMultiValuedCellsTests.java +++ b/main/tests/server/src/com/google/refine/tests/operations/cell/JoinMultiValuedCellsTests.java @@ -33,49 +33,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.operations.cell; -import static org.mockito.Mockito.mock; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; import java.util.Properties; -import java.util.List; -import java.util.ArrayList; -import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.RefineServlet; -import com.google.refine.importers.SeparatorBasedImporter; -import com.google.refine.importing.ImportingJob; -import com.google.refine.importing.ImportingManager; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.AbstractOperation; -import com.google.refine.model.ModelException; import com.google.refine.model.Project; -import com.google.refine.process.Process; import com.google.refine.operations.cell.MultiValuedCellJoinOperation; -import com.google.refine.tests.RefineServletStub; +import com.google.refine.process.Process; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class JoinMultiValuedCellsTests extends RefineTest { - // dependencies - private Project project; - private ProjectMetadata pm; - private JSONObject options; - private ImportingJob job; - private SeparatorBasedImporter importer; - @Override @BeforeTest @@ -83,48 +55,18 @@ public class JoinMultiValuedCellsTests extends RefineTest { logger = LoggerFactory.getLogger(this.getClass()); } - @BeforeMethod - public void setUp() throws JSONException, IOException, ModelException { - RefineServlet servlet = new RefineServletStub(); - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - pm = new ProjectMetadata(); - pm.setName("JoinMultiValuedCells test"); - ProjectManager.singleton.registerProject(project, pm); - options = mock(JSONObject.class); - - ImportingManager.initialize(servlet); - job = ImportingManager.createJob(); - importer = new SeparatorBasedImporter(); - } - - @AfterMethod - public void tearDown() { - ImportingManager.disposeJob(job.id); - ProjectManager.singleton.deleteProject(project.id); - job = null; - project = null; - pm = null; - options = null; - } - - /** + /* * Test to demonstrate the intended behaviour of the function */ @Test public void testJoinMultiValuedCells() throws Exception { - String csv = "Key,Value\n" - + "Record_1,one\n" - + ",two\n" - + ",three\n" - + ",four\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + Project project = createCSVProject( + "Key,Value\n" + + "Record_1,one\n" + + ",two\n" + + ",three\n" + + ",four\n"); AbstractOperation op = new MultiValuedCellJoinOperation( "Value", @@ -142,16 +84,13 @@ public class JoinMultiValuedCellsTests extends RefineTest { @Test public void testJoinMultiValuedCellsMultipleSpaces() throws Exception { - String csv = "Key,Value\n" + Project project = createCSVProject( + "Key,Value\n" + "Record_1,one\n" + ",two\n" + ",three\n" - + ",four\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + + ",four\n"); + AbstractOperation op = new MultiValuedCellJoinOperation( "Value", @@ -167,19 +106,6 @@ public class JoinMultiValuedCellsTests extends RefineTest { Assert.assertEquals(project.rows.get(0).getCellValue(valueCol), "one, ,two, ,three, ,four"); } - private void prepareOptions( - String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { - - whenGetStringOption("separator", options, sep); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - } } diff --git a/main/tests/server/src/com/google/refine/tests/operations/cell/KeyValueColumnizeTests.java b/main/tests/server/src/com/google/refine/tests/operations/cell/KeyValueColumnizeTests.java index ed54846d7..d44cdeaf1 100644 --- a/main/tests/server/src/com/google/refine/tests/operations/cell/KeyValueColumnizeTests.java +++ b/main/tests/server/src/com/google/refine/tests/operations/cell/KeyValueColumnizeTests.java @@ -38,9 +38,9 @@ import static org.mockito.Mockito.mock; import java.io.File; import java.io.IOException; import java.io.StringReader; -import java.util.Properties; -import java.util.List; import java.util.ArrayList; +import java.util.List; +import java.util.Properties; import org.json.JSONException; import org.json.JSONObject; @@ -52,7 +52,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.RefineServlet; import com.google.refine.importers.SeparatorBasedImporter; import com.google.refine.importing.ImportingJob; @@ -61,8 +60,9 @@ import com.google.refine.io.FileProjectManager; import com.google.refine.model.AbstractOperation; import com.google.refine.model.ModelException; import com.google.refine.model.Project; -import com.google.refine.process.Process; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.operations.cell.KeyValueColumnizeOperation; +import com.google.refine.process.Process; import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; import com.google.refine.tests.util.TestUtils; diff --git a/main/tests/server/src/com/google/refine/tests/operations/cell/SplitMultiValuedCellsTests.java b/main/tests/server/src/com/google/refine/tests/operations/cell/SplitMultiValuedCellsTests.java index eb38efef4..d4d8fc6f0 100644 --- a/main/tests/server/src/com/google/refine/tests/operations/cell/SplitMultiValuedCellsTests.java +++ b/main/tests/server/src/com/google/refine/tests/operations/cell/SplitMultiValuedCellsTests.java @@ -33,50 +33,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.operations.cell; -import static org.mockito.Mockito.mock; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; import java.util.Properties; -import java.util.List; -import java.util.ArrayList; -import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.RefineServlet; -import com.google.refine.importers.SeparatorBasedImporter; -import com.google.refine.importing.ImportingJob; -import com.google.refine.importing.ImportingManager; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.AbstractOperation; -import com.google.refine.model.ModelException; import com.google.refine.model.Project; import com.google.refine.process.Process; import com.google.refine.operations.cell.MultiValuedCellSplitOperation; -import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class SplitMultiValuedCellsTests extends RefineTest { - // dependencies - private RefineServlet servlet; - private Project project; - private ProjectMetadata pm; - private JSONObject options; - private ImportingJob job; - private SeparatorBasedImporter importer; - @Override @BeforeTest @@ -84,32 +56,6 @@ public class SplitMultiValuedCellsTests extends RefineTest { logger = LoggerFactory.getLogger(this.getClass()); } - @BeforeMethod - public void SetUp() throws JSONException, IOException, ModelException { - servlet = new RefineServletStub(); - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - pm = new ProjectMetadata(); - pm.setName("SplitMultiValuedCells test"); - ProjectManager.singleton.registerProject(project, pm); - options = mock(JSONObject.class); - - ImportingManager.initialize(servlet); - job = ImportingManager.createJob(); - importer = new SeparatorBasedImporter(); - } - - @AfterMethod - public void TearDown() { - ImportingManager.disposeJob(job.id); - ProjectManager.singleton.deleteProject(project.id); - job = null; - project = null; - pm = null; - options = null; - } - /** * Test to demonstrate the intended behaviour of the function, for issue #1268 * https://github.com/OpenRefine/OpenRefine/issues/1268 @@ -117,13 +63,9 @@ public class SplitMultiValuedCellsTests extends RefineTest { @Test public void testSplitMultiValuedCellsTextSeparator() throws Exception { - String csv = "Key,Value\n" - + "Record_1,one:two;three four\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + Project project = createCSVProject( + "Key,Value\n" + + "Record_1,one:two;three four\n"); AbstractOperation op = new MultiValuedCellSplitOperation( "Value", @@ -144,13 +86,9 @@ public class SplitMultiValuedCellsTests extends RefineTest { @Test public void testSplitMultiValuedCellsRegExSeparator() throws Exception { - String csv = "Key,Value\n" - + "Record_1,one:two;three four\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + Project project = createCSVProject( + "Key,Value\n" + + "Record_1,one:two;three four\n"); AbstractOperation op = new MultiValuedCellSplitOperation( "Value", @@ -175,13 +113,10 @@ public class SplitMultiValuedCellsTests extends RefineTest { @Test public void testSplitMultiValuedCellsLengths() throws Exception { - String csv = "Key,Value\n" - + "Record_1,one:two;three four\n"; - prepareOptions(",", 10, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, pm, job, "filesource", new StringReader(csv), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, pm); + Project project = createCSVProject( + "Key,Value\n" + + "Record_1,one:two;three four\n"); + int[] lengths = {4,4,6,4}; AbstractOperation op = new MultiValuedCellSplitOperation( @@ -204,20 +139,6 @@ public class SplitMultiValuedCellsTests extends RefineTest { Assert.assertEquals(project.rows.get(3).getCellValue(valueCol), "four"); } - private void prepareOptions( - String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { - - whenGetStringOption("separator", options, sep); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - } - } diff --git a/main/tests/server/src/com/google/refine/tests/operations/cell/TransposeTests.java b/main/tests/server/src/com/google/refine/tests/operations/cell/TransposeTests.java index 947fbe668..ad810eb08 100644 --- a/main/tests/server/src/com/google/refine/tests/operations/cell/TransposeTests.java +++ b/main/tests/server/src/com/google/refine/tests/operations/cell/TransposeTests.java @@ -33,34 +33,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.tests.operations.cell; -import static org.mockito.Mockito.mock; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; import java.util.Properties; -import org.json.JSONObject; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; -import com.google.refine.RefineServlet; -import com.google.refine.history.HistoryEntry; -import com.google.refine.importers.SeparatorBasedImporter; -import com.google.refine.importing.ImportingJob; -import com.google.refine.importing.ImportingManager; import com.google.refine.model.AbstractOperation; import com.google.refine.model.Project; +import com.google.refine.model.medadata.ProjectMetadata; import com.google.refine.operations.cell.KeyValueColumnizeOperation; import com.google.refine.process.Process; -import com.google.refine.tests.ProjectManagerStub; -import com.google.refine.tests.RefineServletStub; import com.google.refine.tests.RefineTest; public class TransposeTests extends RefineTest { @@ -71,67 +55,34 @@ public class TransposeTests extends RefineTest { logger = LoggerFactory.getLogger(this.getClass()); } - // dependencies - RefineServlet servlet; - Project project; - ProjectMetadata metadata; - ImportingJob job; - JSONObject options; - SeparatorBasedImporter importer; - - @BeforeMethod - public void SetUp() { - servlet = new RefineServletStub(); - ProjectManager.singleton = new ProjectManagerStub(); - ImportingManager.initialize(servlet); - project = new Project(); - metadata = new ProjectMetadata(); - - job = ImportingManager.createJob(); - options = mock(JSONObject.class); - importer = new SeparatorBasedImporter(); - } - - @AfterMethod - public void TearDown() { - ImportingManager.disposeJob(job.id); - ProjectManager.singleton.deleteProject(project.id); - job = null; - metadata = null; - project = null; - options = null; - importer = null; - } - + /** + * Test in the case where an ID is available in the first column. + * @throws Exception + */ @Test - public void keyValueComumnize() throws Exception { - String input = "ID;Cat;Val\n" - + "1;a;1\n" - + "1;b;3\n" - + "2;b;4\n" - + "2;c;5\n" - + "3;a;2\n" - + "3;b;5\n" - + "3;d;3\n"; - - prepareOptions(";", -1, 0, 0, 1, false, false); - List exceptions = new ArrayList(); - importer.parseOneFile(project, metadata, job, "filesource", new StringReader(input), -1, options, exceptions); - project.update(); - ProjectManager.singleton.registerProject(project, metadata); + public void testKeyValueColumnizeWithID() throws Exception { + Project project = createCSVProject( + "ID,Cat,Val\n" + + "1,a,1\n" + + "1,b,3\n" + + "2,b,4\n" + + "2,c,5\n" + + "3,a,2\n" + + "3,b,5\n" + + "3,d,3\n"); AbstractOperation op = new KeyValueColumnizeOperation( "Cat", "Val", null); Process process = op.createProcess(project, new Properties()); - HistoryEntry historyEntry = process.performImmediate(); + process.performImmediate(); // Expected output from the GUI. - // ID;a;b;c;d - // 1;1;3;; - // 2;;4;5; - // 3;2;5;;3 + // ID,a,b,c,d + // 1,1,3,, + // 2,,4,5, + // 3,2,5,,3 Assert.assertEquals(project.columnModel.columns.size(), 5); Assert.assertEquals(project.columnModel.columns.get(0).getName(), "ID"); Assert.assertEquals(project.columnModel.columns.get(1).getName(), "a"); @@ -142,38 +93,59 @@ public class TransposeTests extends RefineTest { // The actual row data structure has to leave the columns model untouched for redo/undo purpose. // So we have 2 empty columns(column 1,2) on the row level. - // 1;1;3;; + // 1,1,3,, Assert.assertEquals(project.rows.get(0).cells.get(0).value, "1"); Assert.assertEquals(project.rows.get(0).cells.get(3).value, "1"); Assert.assertEquals(project.rows.get(0).cells.get(4).value, "3"); - // 2;;4;5; + // 2,,4,5, Assert.assertEquals(project.rows.get(1).cells.get(0).value, "2"); Assert.assertEquals(project.rows.get(1).cells.get(4).value, "4"); Assert.assertEquals(project.rows.get(1).cells.get(5).value, "5"); - // 3;2;5;;3 + // 3,2,5,,3 Assert.assertEquals(project.rows.get(2).cells.get(0).value, "3"); Assert.assertEquals(project.rows.get(2).cells.get(3).value, "2"); Assert.assertEquals(project.rows.get(2).cells.get(4).value, "5"); Assert.assertEquals(project.rows.get(2).cells.get(6).value, "3"); } + + /** + * Test to demonstrate the intended behaviour of the function when no id is available, for issue #1214 + * https://github.com/OpenRefine/OpenRefine/issues/1214 + */ + @Test + public void testKeyValueColumnizeWithoutID() throws Exception { + Project project = createCSVProject( + "Key,Value\n" + + "merchant,Katie\n" + + "fruit,apple\n" + + "price,1.2\n" + + "fruit,pear\n" + + "price,1.5\n" + + "merchant,John\n" + + "fruit,banana\n" + + "price,3.1\n"); + AbstractOperation op = new KeyValueColumnizeOperation( + "Key", + "Value", + null); + Process process = op.createProcess(project, new Properties()); + process.performImmediate(); - - - private void prepareOptions( - String sep, int limit, int skip, int ignoreLines, - int headerLines, boolean guessValueType, boolean ignoreQuotes) { - - whenGetStringOption("separator", options, sep); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - } - + int merchantCol = project.columnModel.getColumnByName("merchant").getCellIndex(); + int fruitCol = project.columnModel.getColumnByName("fruit").getCellIndex(); + int priceCol = project.columnModel.getColumnByName("price").getCellIndex(); + + Assert.assertEquals(project.rows.get(0).getCellValue(merchantCol), "Katie"); + Assert.assertEquals(project.rows.get(1).getCellValue(merchantCol), null); + Assert.assertEquals(project.rows.get(2).getCellValue(merchantCol), "John"); + Assert.assertEquals(project.rows.get(0).getCellValue(fruitCol), "apple"); + Assert.assertEquals(project.rows.get(1).getCellValue(fruitCol), "pear"); + Assert.assertEquals(project.rows.get(2).getCellValue(fruitCol), "banana"); + Assert.assertEquals(project.rows.get(0).getCellValue(priceCol), "1.2"); + Assert.assertEquals(project.rows.get(1).getCellValue(priceCol), "1.5"); + Assert.assertEquals(project.rows.get(2).getCellValue(priceCol), "3.1"); + } } diff --git a/main/tests/server/src/com/google/refine/tests/recon/DataExtensionTests.java b/main/tests/server/src/com/google/refine/tests/recon/DataExtensionTests.java index a8f921645..33abd060e 100644 --- a/main/tests/server/src/com/google/refine/tests/recon/DataExtensionTests.java +++ b/main/tests/server/src/com/google/refine/tests/recon/DataExtensionTests.java @@ -35,11 +35,10 @@ package com.google.refine.tests.recon; import static org.mockito.Mockito.mock; -import java.io.File; import java.io.IOException; -import java.util.Properties; -import java.util.List; import java.util.ArrayList; +import java.util.List; +import java.util.Properties; import org.json.JSONException; import org.json.JSONObject; @@ -50,26 +49,18 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.google.refine.ProjectManager; -import com.google.refine.ProjectMetadata; import com.google.refine.browsing.Engine; -import com.google.refine.browsing.RowVisitor; -import com.google.refine.grel.Function; -import com.google.refine.io.FileProjectManager; import com.google.refine.model.Cell; -import com.google.refine.model.Column; import com.google.refine.model.ModelException; import com.google.refine.model.Project; -import com.google.refine.model.Row; import com.google.refine.model.Recon; import com.google.refine.model.ReconCandidate; +import com.google.refine.model.Row; import com.google.refine.process.Process; import com.google.refine.process.ProcessManager; -import com.google.refine.operations.OnError; import com.google.refine.operations.EngineDependentOperation; import com.google.refine.operations.recon.ExtendDataOperation; import com.google.refine.tests.RefineTest; -import com.google.refine.tests.util.TestUtils; public class DataExtensionTests extends RefineTest { @@ -90,29 +81,16 @@ public class DataExtensionTests extends RefineTest { Properties options; JSONObject engine_config; Engine engine; - Properties bindings; @BeforeMethod public void SetUp() throws JSONException, IOException, ModelException { - File dir = TestUtils.createTempDirectory("openrefine-test-workspace-dir"); - FileProjectManager.initialize(dir); - project = new Project(); - ProjectMetadata pm = new ProjectMetadata(); - pm.setName("Data Extension Test Project"); - ProjectManager.singleton.registerProject(project, pm); - - int index = project.columnModel.allocateNewCellIndex(); - Column column = new Column(index,"country"); - project.columnModel.addColumn(index, column, true); + project = createProjectWithColumns("DataExtensionTests", "country"); options = mock(Properties.class); engine = new Engine(project); engine_config = new JSONObject(ENGINE_JSON_URLS); engine.initializeFromJSON(engine_config); engine.setMode(Engine.Mode.RowBased); - - bindings = new Properties(); - bindings.put("project", project); Row row = new Row(2); row.setCell(0, reconciledCell("Iran", "Q794")); @@ -133,7 +111,6 @@ public class DataExtensionTests extends RefineTest { project = null; options = null; engine = null; - bindings = null; } static public Cell reconciledCell(String name, String id) { diff --git a/main/webapp/WEB-INF/lib-src/datapackage-java-1.0-SNAPSHOT-sources.jar b/main/webapp/WEB-INF/lib-src/datapackage-java-1.0-SNAPSHOT-sources.jar new file mode 100644 index 000000000..0e5959abf Binary files /dev/null and b/main/webapp/WEB-INF/lib-src/datapackage-java-1.0-SNAPSHOT-sources.jar differ diff --git a/main/webapp/WEB-INF/lib-src/tableschema-java-1.0-SNAPSHOT-sources.jar b/main/webapp/WEB-INF/lib-src/tableschema-java-1.0-SNAPSHOT-sources.jar new file mode 100644 index 000000000..d9ddf411e Binary files /dev/null and b/main/webapp/WEB-INF/lib-src/tableschema-java-1.0-SNAPSHOT-sources.jar differ diff --git a/main/webapp/WEB-INF/lib/bsh-2.0b4.jar b/main/webapp/WEB-INF/lib/bsh-2.0b4.jar new file mode 100644 index 000000000..36fe03d71 Binary files /dev/null and b/main/webapp/WEB-INF/lib/bsh-2.0b4.jar differ diff --git a/main/webapp/WEB-INF/lib/byte-buddy-1.6.14.jar b/main/webapp/WEB-INF/lib/byte-buddy-1.6.14.jar new file mode 100644 index 000000000..04c0f8478 Binary files /dev/null and b/main/webapp/WEB-INF/lib/byte-buddy-1.6.14.jar differ diff --git a/main/webapp/WEB-INF/lib/byte-buddy-agent-1.6.14.jar b/main/webapp/WEB-INF/lib/byte-buddy-agent-1.6.14.jar new file mode 100644 index 000000000..350e6cc98 Binary files /dev/null and b/main/webapp/WEB-INF/lib/byte-buddy-agent-1.6.14.jar differ diff --git a/main/webapp/WEB-INF/lib/cglib-nodep-2.2.2.jar b/main/webapp/WEB-INF/lib/cglib-nodep-2.2.2.jar new file mode 100644 index 000000000..02d81e880 Binary files /dev/null and b/main/webapp/WEB-INF/lib/cglib-nodep-2.2.2.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-beanutils-1.9.3.jar b/main/webapp/WEB-INF/lib/commons-beanutils-1.9.3.jar new file mode 100644 index 000000000..6728154e5 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-beanutils-1.9.3.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-codec-1.6.jar b/main/webapp/WEB-INF/lib/commons-codec-1.6.jar deleted file mode 100644 index ee1bc49ac..000000000 Binary files a/main/webapp/WEB-INF/lib/commons-codec-1.6.jar and /dev/null differ diff --git a/main/webapp/WEB-INF/lib/commons-collections-3.2.2.jar b/main/webapp/WEB-INF/lib/commons-collections-3.2.2.jar new file mode 100644 index 000000000..fa5df82a6 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-collections-3.2.2.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-csv-1.5.jar b/main/webapp/WEB-INF/lib/commons-csv-1.5.jar new file mode 100644 index 000000000..eb4775e30 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-csv-1.5.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-digester-1.8.1.jar b/main/webapp/WEB-INF/lib/commons-digester-1.8.1.jar new file mode 100644 index 000000000..7abda9696 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-digester-1.8.1.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-lang3-3.6.jar b/main/webapp/WEB-INF/lib/commons-lang3-3.6.jar new file mode 100644 index 000000000..c25fd4d32 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-lang3-3.6.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-logging-1.1.1.jar b/main/webapp/WEB-INF/lib/commons-logging-1.1.1.jar deleted file mode 100644 index 1deef144c..000000000 Binary files a/main/webapp/WEB-INF/lib/commons-logging-1.1.1.jar and /dev/null differ diff --git a/main/webapp/WEB-INF/lib/commons-logging-1.2.jar b/main/webapp/WEB-INF/lib/commons-logging-1.2.jar new file mode 100644 index 000000000..93a3b9f6d Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-logging-1.2.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-text-1.1.jar b/main/webapp/WEB-INF/lib/commons-text-1.1.jar new file mode 100644 index 000000000..73fb4ae72 Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-text-1.1.jar differ diff --git a/main/webapp/WEB-INF/lib/commons-validator-1.5.1.jar b/main/webapp/WEB-INF/lib/commons-validator-1.5.1.jar new file mode 100644 index 000000000..f90c6958c Binary files /dev/null and b/main/webapp/WEB-INF/lib/commons-validator-1.5.1.jar differ diff --git a/main/webapp/WEB-INF/lib/datapackage-java-1.0-SNAPSHOT.jar b/main/webapp/WEB-INF/lib/datapackage-java-1.0-SNAPSHOT.jar new file mode 100644 index 000000000..a5486d34f Binary files /dev/null and b/main/webapp/WEB-INF/lib/datapackage-java-1.0-SNAPSHOT.jar differ diff --git a/main/webapp/WEB-INF/lib/hamcrest-all-1.3.jar b/main/webapp/WEB-INF/lib/hamcrest-all-1.3.jar new file mode 100644 index 000000000..6f62ba00c Binary files /dev/null and b/main/webapp/WEB-INF/lib/hamcrest-all-1.3.jar differ diff --git a/main/webapp/WEB-INF/lib/hamcrest-core-1.3.jar b/main/webapp/WEB-INF/lib/hamcrest-core-1.3.jar new file mode 100644 index 000000000..9d5fe16e3 Binary files /dev/null and b/main/webapp/WEB-INF/lib/hamcrest-core-1.3.jar differ diff --git a/main/webapp/WEB-INF/lib/javassist-3.21.0-GA.jar b/main/webapp/WEB-INF/lib/javassist-3.21.0-GA.jar new file mode 100644 index 000000000..64549c4ad Binary files /dev/null and b/main/webapp/WEB-INF/lib/javassist-3.21.0-GA.jar differ diff --git a/main/webapp/WEB-INF/lib/jcommander-1.48.jar b/main/webapp/WEB-INF/lib/jcommander-1.48.jar new file mode 100644 index 000000000..ad0a12c95 Binary files /dev/null and b/main/webapp/WEB-INF/lib/jcommander-1.48.jar differ diff --git a/main/webapp/WEB-INF/lib/joda-time-2.9.9.jar b/main/webapp/WEB-INF/lib/joda-time-2.9.9.jar new file mode 100644 index 000000000..b3080c435 Binary files /dev/null and b/main/webapp/WEB-INF/lib/joda-time-2.9.9.jar differ diff --git a/main/webapp/WEB-INF/lib/json-20100208.jar b/main/webapp/WEB-INF/lib/json-20100208.jar deleted file mode 100644 index fa95275c6..000000000 Binary files a/main/webapp/WEB-INF/lib/json-20100208.jar and /dev/null differ diff --git a/main/webapp/WEB-INF/lib/json-20160810.jar b/main/webapp/WEB-INF/lib/json-20160810.jar new file mode 100644 index 000000000..a90e44852 Binary files /dev/null and b/main/webapp/WEB-INF/lib/json-20160810.jar differ diff --git a/main/webapp/WEB-INF/lib/junit-4.12.jar b/main/webapp/WEB-INF/lib/junit-4.12.jar new file mode 100644 index 000000000..3a7fc266c Binary files /dev/null and b/main/webapp/WEB-INF/lib/junit-4.12.jar differ diff --git a/main/webapp/WEB-INF/lib/mockito-core-2.8.9.jar b/main/webapp/WEB-INF/lib/mockito-core-2.8.9.jar new file mode 100644 index 000000000..a6a361223 Binary files /dev/null and b/main/webapp/WEB-INF/lib/mockito-core-2.8.9.jar differ diff --git a/main/webapp/WEB-INF/lib/objenesis-2.5.jar b/main/webapp/WEB-INF/lib/objenesis-2.5.jar new file mode 100644 index 000000000..7b60a8b43 Binary files /dev/null and b/main/webapp/WEB-INF/lib/objenesis-2.5.jar differ diff --git a/main/webapp/WEB-INF/lib/opencsv-4.0.jar b/main/webapp/WEB-INF/lib/opencsv-4.0.jar new file mode 100644 index 000000000..13ce91af2 Binary files /dev/null and b/main/webapp/WEB-INF/lib/opencsv-4.0.jar differ diff --git a/main/webapp/WEB-INF/lib/org.everit.json.schema-1.5.1.jar b/main/webapp/WEB-INF/lib/org.everit.json.schema-1.5.1.jar new file mode 100644 index 000000000..89d7f7f5f Binary files /dev/null and b/main/webapp/WEB-INF/lib/org.everit.json.schema-1.5.1.jar differ diff --git a/main/webapp/WEB-INF/lib/powermock-mockito2-1.7.1-full.jar b/main/webapp/WEB-INF/lib/powermock-mockito2-1.7.1-full.jar new file mode 100644 index 000000000..bfed6fada Binary files /dev/null and b/main/webapp/WEB-INF/lib/powermock-mockito2-1.7.1-full.jar differ diff --git a/main/webapp/WEB-INF/lib/tableschema-java-1.0-SNAPSHOT.jar b/main/webapp/WEB-INF/lib/tableschema-java-1.0-SNAPSHOT.jar new file mode 100644 index 000000000..4cc32a03f Binary files /dev/null and b/main/webapp/WEB-INF/lib/tableschema-java-1.0-SNAPSHOT.jar differ diff --git a/main/webapp/WEB-INF/lib/testng-6.9.10.jar b/main/webapp/WEB-INF/lib/testng-6.9.10.jar new file mode 100644 index 000000000..a9e0f76d4 Binary files /dev/null and b/main/webapp/WEB-INF/lib/testng-6.9.10.jar differ diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js index 0acb0d393..23e5304eb 100644 --- a/main/webapp/modules/core/MOD-INF/controller.js +++ b/main/webapp/modules/core/MOD-INF/controller.js @@ -65,10 +65,12 @@ function registerCommands() { RS.registerCommand(module, "import-project", new Packages.com.google.refine.commands.project.ImportProjectCommand()); RS.registerCommand(module, "export-project", new Packages.com.google.refine.commands.project.ExportProjectCommand()); RS.registerCommand(module, "export-rows", new Packages.com.google.refine.commands.project.ExportRowsCommand()); + RS.registerCommand(module, "project-data-package", new Packages.com.google.refine.commands.project.PackageProjectCommand()); RS.registerCommand(module, "get-project-metadata", new Packages.com.google.refine.commands.project.GetProjectMetadataCommand()); RS.registerCommand(module, "get-all-project-metadata", new Packages.com.google.refine.commands.workspace.GetAllProjectMetadataCommand()); RS.registerCommand(module, "set-metaData", new Packages.com.google.refine.commands.project.SetProjectMetadataCommand()); + RS.registerCommand(module, "get-imetaData", new Packages.com.google.refine.commands.project.GetMetadataCommand()); RS.registerCommand(module, "get-all-project-tags", new Packages.com.google.refine.commands.workspace.GetAllProjectTagsCommand()); RS.registerCommand(module, "set-project-tags", new Packages.com.google.refine.commands.project.SetProjectTagsCommand()); @@ -276,6 +278,8 @@ function registerImporting() { IM.registerMimeType("application/rdf+xml", "text/xml/rdf"); IM.registerMimeType("application/marc", "text/marc"); + + IM.registerUrlRewriter(new Packages.com.google.refine.model.medadata.DataPackageUrlRewriter()); /* * Format guessers: these take a format derived from extensions or mime-types, @@ -323,6 +327,7 @@ function init() { "externals/jquery.i18n.js", "externals/tablesorter/jquery.tablesorter.min.js", "externals/moment-with-locales.min.js", + "externals/jsoneditor/jsoneditor.js", "externals/select2/select2.min.js", "externals/jquery.lavalamp.min.js", @@ -360,7 +365,8 @@ function init() { "scripts/index/parser-interfaces/wikitext-parser-ui.js", "scripts/reconciliation/recon-manager.js", // so that reconciliation functions are available to importers - "scripts/index/edit-metadata-dialog.js" + "scripts/index/edit-metadata-dialog.js", + "scripts/project/edit-general-metadata-dialog.js" ] ); @@ -376,6 +382,7 @@ function init() { "styles/pure.css", "styles/util/dialog.less", "styles/util/encoding.less", + "externals/jsoneditor/jsoneditor.css", "styles/index.less", "styles/index/create-project-ui.less", @@ -391,7 +398,7 @@ function init() { "styles/index/fixed-width-parser-ui.less", "styles/index/xml-parser-ui.less", "styles/index/json-parser-ui.less", - "styles/index/wikitext-parser-ui.less", + "styles/index/wikitext-parser-ui.less" ] ); @@ -408,6 +415,7 @@ function init() { "externals/date.js", "externals/jquery.i18n.js", "externals/underscore-min.js", + "externals/jsoneditor/jsoneditor.js", "scripts/project.js", @@ -459,7 +467,9 @@ function init() { "scripts/dialogs/templating-exporter-dialog.js", "scripts/dialogs/column-reordering-dialog.js", "scripts/dialogs/custom-tabular-exporter-dialog.js", - "scripts/dialogs/expression-column-dialog.js" + "scripts/dialogs/expression-column-dialog.js", + "scripts/project/edit-general-metadata-dialog.js", + "scripts/dialogs/http-headers-dialog.js", ] ); @@ -470,6 +480,7 @@ function init() { "externals/suggest/css/suggest-4_3.min.css", "externals/jquery-ui/css/ui-lightness/jquery-ui-1.10.3.custom.css", "externals/imgareaselect/css/imgareaselect-default.css", + "externals/jsoneditor/jsoneditor.css", "styles/jquery-ui-overrides.less", "styles/common.less", diff --git a/main/webapp/modules/core/about.html b/main/webapp/modules/core/about.html index 2015c0580..4bcee66ae 100644 --- a/main/webapp/modules/core/about.html +++ b/main/webapp/modules/core/about.html @@ -60,7 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      diff --git a/main/webapp/modules/core/error.vt b/main/webapp/modules/core/error.vt index 74f33a7d3..6437401dc 100644 --- a/main/webapp/modules/core/error.vt +++ b/main/webapp/modules/core/error.vt @@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

      Something went wrong!

      diff --git a/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.png b/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.png new file mode 100644 index 000000000..c9ae20b46 Binary files /dev/null and b/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.png differ diff --git a/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.svg b/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.svg new file mode 100644 index 000000000..9e48bdcb0 --- /dev/null +++ b/main/webapp/modules/core/externals/jsoneditor/img/jsoneditor-icons.svg @@ -0,0 +1,899 @@ + + + JSON Editor Icons + + + + image/svg+xml + + JSON Editor Icons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/webapp/modules/core/externals/jsoneditor/jsoneditor.css b/main/webapp/modules/core/externals/jsoneditor/jsoneditor.css new file mode 100644 index 000000000..a490eb3db --- /dev/null +++ b/main/webapp/modules/core/externals/jsoneditor/jsoneditor.css @@ -0,0 +1,1065 @@ +/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */ + +div.jsoneditor .jsoneditor-search input { + height: auto; + border: inherit; +} + +div.jsoneditor .jsoneditor-search input:focus { + border: none !important; + box-shadow: none !important; +} + +div.jsoneditor table { + border-collapse: collapse; + width: auto; +} + +div.jsoneditor td, +div.jsoneditor th { + padding: 0; + display: table-cell; + text-align: left; + vertical-align: inherit; + border-radius: inherit; +} + + +div.jsoneditor-field, +div.jsoneditor-value, +div.jsoneditor-readonly { + border: 1px solid transparent; + min-height: 16px; + min-width: 32px; + padding: 2px; + margin: 1px; + word-wrap: break-word; + float: left; +} + +/* adjust margin of p elements inside editable divs, needed for Opera, IE */ + +div.jsoneditor-field p, +div.jsoneditor-value p { + margin: 0; +} + +div.jsoneditor-value { + word-break: break-word; +} + +div.jsoneditor-readonly { + min-width: 16px; + color: gray; +} + +div.jsoneditor-empty { + border-color: lightgray; + border-style: dashed; + border-radius: 2px; +} + +div.jsoneditor-field.jsoneditor-empty::after, +div.jsoneditor-value.jsoneditor-empty::after { + pointer-events: none; + color: lightgray; + font-size: 8pt; +} + +div.jsoneditor-field.jsoneditor-empty::after { + content: "field"; +} + +div.jsoneditor-value.jsoneditor-empty::after { + content: "value"; +} + +div.jsoneditor-value.jsoneditor-url, +a.jsoneditor-value.jsoneditor-url { + color: green; + text-decoration: underline; +} + +a.jsoneditor-value.jsoneditor-url { + display: inline-block; + padding: 2px; + margin: 2px; +} + +a.jsoneditor-value.jsoneditor-url:hover, +a.jsoneditor-value.jsoneditor-url:focus { + color: #ee422e; +} + +div.jsoneditor td.jsoneditor-separator { + padding: 3px 0; + vertical-align: top; + color: gray; +} + +div.jsoneditor-field[contenteditable=true]:focus, +div.jsoneditor-field[contenteditable=true]:hover, +div.jsoneditor-value[contenteditable=true]:focus, +div.jsoneditor-value[contenteditable=true]:hover, +div.jsoneditor-field.jsoneditor-highlight, +div.jsoneditor-value.jsoneditor-highlight { + background-color: #FFFFAB; + border: 1px solid yellow; + border-radius: 2px; +} + +div.jsoneditor-field.jsoneditor-highlight-active, +div.jsoneditor-field.jsoneditor-highlight-active:focus, +div.jsoneditor-field.jsoneditor-highlight-active:hover, +div.jsoneditor-value.jsoneditor-highlight-active, +div.jsoneditor-value.jsoneditor-highlight-active:focus, +div.jsoneditor-value.jsoneditor-highlight-active:hover { + background-color: #ffee00; + border: 1px solid #ffc700; + border-radius: 2px; +} + +div.jsoneditor-value.jsoneditor-string { + color: #008000; +} + +div.jsoneditor-value.jsoneditor-object, +div.jsoneditor-value.jsoneditor-array { + min-width: 16px; + color: #808080; +} + +div.jsoneditor-value.jsoneditor-number { + color: #ee422e; +} + +div.jsoneditor-value.jsoneditor-boolean { + color: #ff8c00; +} + +div.jsoneditor-value.jsoneditor-null { + color: #004ED0; +} + +div.jsoneditor-value.jsoneditor-invalid { + color: #000000; +} + +div.jsoneditor-tree button { + width: 24px; + height: 24px; + padding: 0; + margin: 0; + border: none; + cursor: pointer; + background: transparent url("img/jsoneditor-icons.png"); +} + +div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree, +div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree { + cursor: pointer; +} + +div.jsoneditor-tree button.jsoneditor-collapsed { + background-position: 0 -48px; +} + +div.jsoneditor-tree button.jsoneditor-expanded { + background-position: 0 -72px; +} + +div.jsoneditor-tree button.jsoneditor-contextmenu { + background-position: -48px -72px; +} + +div.jsoneditor-tree button.jsoneditor-contextmenu:hover, +div.jsoneditor-tree button.jsoneditor-contextmenu:focus, +div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu { + background-position: -48px -48px; +} + +div.jsoneditor-tree *:focus { + outline: none; +} + +div.jsoneditor-tree button:focus { + /* TODO: nice outline for buttons with focus + outline: #97B0F8 solid 2px; + box-shadow: 0 0 8px #97B0F8; + */ + background-color: #f5f5f5; + outline: #e5e5e5 solid 1px; +} + +div.jsoneditor-tree button.jsoneditor-invisible { + visibility: hidden; + background: none; +} + +div.jsoneditor { + color: #1A1A1A; + border: 1px solid #3883fa; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + height: 100%; + position: relative; + padding: 0; + line-height: 100%; +} + +div.jsoneditor-tree table.jsoneditor-tree { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + margin: 0; +} + +div.jsoneditor-outer { + position: static; + width: 100%; + height: 100%; + margin: -35px 0 0 0; + padding: 35px 0 0 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +div.jsoneditor-outer.has-nav-bar { + margin: -61px 0 0 0; + padding: 61px 0 0 0; +} + +div.jsoneditor-outer.has-status-bar { + margin: -35px 0 -26px 0; + padding: 35px 0 26px 0; +} + +textarea.jsoneditor-text, +.ace-jsoneditor { + min-height: 150px; +} + +div.jsoneditor-tree { + width: 100%; + height: 100%; + position: relative; + overflow: auto; +} + +textarea.jsoneditor-text { + width: 100%; + height: 100%; + margin: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + outline-width: 0; + border: none; + background-color: white; + resize: none; +} + +tr.jsoneditor-highlight, +tr.jsoneditor-selected { + background-color: #d3d3d3; +} + +tr.jsoneditor-selected button.jsoneditor-dragarea, +tr.jsoneditor-selected button.jsoneditor-contextmenu { + visibility: hidden; +} + +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu { + visibility: visible; +} + +div.jsoneditor-tree button.jsoneditor-dragarea { + background: url("img/jsoneditor-icons.png") -72px -72px; + cursor: move; +} + +div.jsoneditor-tree button.jsoneditor-dragarea:hover, +div.jsoneditor-tree button.jsoneditor-dragarea:focus, +tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea { + background-position: -72px -48px; +} + +div.jsoneditor tr, +div.jsoneditor th, +div.jsoneditor td { + padding: 0; + margin: 0; +} + +div.jsoneditor td { + vertical-align: top; +} + +div.jsoneditor td.jsoneditor-tree { + vertical-align: top; +} + +div.jsoneditor-field, +div.jsoneditor-value, +div.jsoneditor td, +div.jsoneditor th, +div.jsoneditor textarea, +.jsoneditor-schema-error { + font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif; + font-size: 10pt; + color: #1A1A1A; +} + +/* popover */ + +.jsoneditor-schema-error { + cursor: default; + display: inline-block; + /*font-family: arial, sans-serif;*/ + height: 24px; + line-height: 24px; + position: relative; + text-align: center; + width: 24px; +} + +div.jsoneditor-tree .jsoneditor-schema-error { + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.png") -168px -48px; +} + +.jsoneditor-schema-error .jsoneditor-popover { + background-color: #4c4c4c; + border-radius: 3px; + box-shadow: 0 0 5px rgba(0,0,0,0.4); + color: #fff; + display: none; + padding: 7px 10px; + position: absolute; + width: 200px; + z-index: 4; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above { + bottom: 32px; + left: -98px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below { + top: 32px; + left: -98px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left { + top: -7px; + right: 32px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right { + top: -7px; + left: 32px; +} + +.jsoneditor-schema-error .jsoneditor-popover:before { + border-right: 7px solid transparent; + border-left: 7px solid transparent; + content: ''; + display: block; + left: 50%; + margin-left: -7px; + position: absolute; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before { + border-top: 7px solid #4c4c4c; + bottom: -7px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before { + border-bottom: 7px solid #4c4c4c; + top: -7px; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before { + border-left: 7px solid #4c4c4c; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + content: ''; + top: 19px; + right: -14px; + left: inherit; + margin-left: inherit; + margin-top: -7px; + position: absolute; +} + +.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before { + border-right: 7px solid #4c4c4c; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + content: ''; + top: 19px; + left: -14px; + margin-left: inherit; + margin-top: -7px; + position: absolute; +} + +.jsoneditor-schema-error:hover .jsoneditor-popover, +.jsoneditor-schema-error:focus .jsoneditor-popover { + display: block; + -webkit-animation: fade-in .3s linear 1, move-up .3s linear 1; + -moz-animation: fade-in .3s linear 1, move-up .3s linear 1; + -ms-animation: fade-in .3s linear 1, move-up .3s linear 1; +} + +@-webkit-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@-moz-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@-ms-keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +/*@-webkit-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/*@-moz-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/*@-ms-keyframes move-up {*/ + +/*from { bottom: 24px; }*/ + +/*to { bottom: 32px; }*/ + +/*}*/ + +/* JSON schema errors displayed at the bottom of the editor in mode text and code */ + +.jsoneditor .jsoneditor-text-errors { + width: 100%; + border-collapse: collapse; + background-color: #ffef8b; + border-top: 1px solid #ffd700; +} + +.jsoneditor .jsoneditor-text-errors td { + padding: 3px 6px; + vertical-align: middle; +} + +.jsoneditor-text-errors .jsoneditor-schema-error { + border: none; + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.png") -168px -48px; +} +/* ContextMenu - main menu */ + +div.jsoneditor-contextmenu-root { + position: relative; + width: 0; + height: 0; +} + +div.jsoneditor-contextmenu { + position: absolute; + box-sizing: content-box; + z-index: 99999; +} + +div.jsoneditor-contextmenu ul, +div.jsoneditor-contextmenu li { + box-sizing: content-box; + position: relative; +} + +div.jsoneditor-contextmenu ul { + position: relative; + left: 0; + top: 0; + width: 128px; + background: white; + border: 1px solid #d3d3d3; + box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3); + list-style: none; + margin: 0; + padding: 0; +} + +div.jsoneditor-contextmenu ul li button { + position: relative; + padding: 0 4px 0 0; + margin: 0; + width: 128px; + height: auto; + border: none; + cursor: pointer; + color: #4d4d4d; + background: transparent; + font-size: 10pt; + font-family: arial, sans-serif; + box-sizing: border-box; + text-align: left; +} + +/* Fix button padding in firefox */ + +div.jsoneditor-contextmenu ul li button::-moz-focus-inner { + padding: 0; + border: 0; +} + +div.jsoneditor-contextmenu ul li button:hover, +div.jsoneditor-contextmenu ul li button:focus { + color: #1a1a1a; + background-color: #f5f5f5; + outline: none; +} + +div.jsoneditor-contextmenu ul li button.jsoneditor-default { + width: 96px; + /* 128px - 32px */ +} + +div.jsoneditor-contextmenu ul li button.jsoneditor-expand { + float: right; + width: 32px; + height: 24px; + border-left: 1px solid #e5e5e5; +} + +div.jsoneditor-contextmenu div.jsoneditor-icon { + position: absolute; + top: 0; + left: 0; + width: 24px; + height: 24px; + border: none; + padding: 0; + margin: 0; + background: url url("img/jsoneditor-icons.png"); +} + +div.jsoneditor-contextmenu ul li ul div.jsoneditor-icon { + margin-left: 24px; +} + +div.jsoneditor-contextmenu div.jsoneditor-text { + padding: 4px 0 4px 24px; + word-wrap: break-word; +} + +div.jsoneditor-contextmenu div.jsoneditor-text.jsoneditor-right-margin { + padding-right: 24px; +} + +div.jsoneditor-contextmenu ul li button div.jsoneditor-expand { + position: absolute; + top: 0; + right: 0; + width: 24px; + height: 24px; + padding: 0; + margin: 0 4px 0 0; + background: url("img/jsoneditor-icons.png") 0 -72px; + opacity: 0.4; +} + +div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand, +div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand { + opacity: 1; +} + +div.jsoneditor-contextmenu div.jsoneditor-separator { + height: 0; + border-top: 1px solid #e5e5e5; + padding-top: 5px; + margin-top: 5px; +} + +div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon { + background-position: -24px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon { + background-position: -24px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon { + background-position: 0 -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon { + background-position: 0 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon { + background-position: 0 -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon { + background-position: 0 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon { + background-position: -48px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon { + background-position: -48px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon { + background-position: -168px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon { + background-position: -168px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon { + background-position: -192px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon { + background-position: -192px 0; +} + +/* ContextMenu - sub menu */ + +div.jsoneditor-contextmenu ul li button.jsoneditor-selected, +div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover, +div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus { + color: white; + background-color: #ee422e; +} + +div.jsoneditor-contextmenu ul li { + overflow: hidden; +} + +div.jsoneditor-contextmenu ul li ul { + display: none; + position: relative; + left: -10px; + top: 0; + border: none; + box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5); + padding: 0 10px; + /* TODO: transition is not supported on IE8-9 */ + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + + + +div.jsoneditor-contextmenu ul li ul li button { + padding-left: 24px; + animation: all ease-in-out 1s; +} + +div.jsoneditor-contextmenu ul li ul li button:hover, +div.jsoneditor-contextmenu ul li ul li button:focus { + background-color: #f5f5f5; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon { + background-position: -144px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon { + background-position: -144px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon { + background-position: -120px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon { + background-position: -120px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon { + background-position: -72px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon { + background-position: -72px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon { + background-position: -96px -24px; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon, +div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon { + background-position: -96px 0; +} + +div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon { + background: url none; + width: 6px; +} +div.jsoneditor-menu { + width: 100%; + height: 35px; + padding: 2px; + margin: 0; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: white; + background-color: #3883fa; + border-bottom: 1px solid #3883fa; +} + +div.jsoneditor-menu > button, +div.jsoneditor-menu > div.jsoneditor-modes > button { + width: 26px; + height: 26px; + margin: 2px; + padding: 0; + border-radius: 2px; + border: 1px solid transparent; + background: transparent url("img/jsoneditor-icons.png"); + color: white; + opacity: 0.8; + font-family: arial, sans-serif; + font-size: 10pt; + float: left; +} + +div.jsoneditor-menu > button:hover, +div.jsoneditor-menu > div.jsoneditor-modes > button:hover { + background-color: rgba(255,255,255,0.2); + border: 1px solid rgba(255,255,255,0.4); +} + +div.jsoneditor-menu > button:focus, +div.jsoneditor-menu > button:active, +div.jsoneditor-menu > div.jsoneditor-modes > button:focus, +div.jsoneditor-menu > div.jsoneditor-modes > button:active { + background-color: rgba(255,255,255,0.3); +} + +div.jsoneditor-menu > button:disabled, +div.jsoneditor-menu > div.jsoneditor-modes > button:disabled { + opacity: 0.5; +} + +div.jsoneditor-menu > button.jsoneditor-collapse-all { + background-position: 0 -96px; +} + +div.jsoneditor-menu > button.jsoneditor-expand-all { + background-position: 0 -120px; +} + +div.jsoneditor-menu > button.jsoneditor-undo { + background-position: -24px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-undo:disabled { + background-position: -24px -120px; +} + +div.jsoneditor-menu > button.jsoneditor-redo { + background-position: -48px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-redo:disabled { + background-position: -48px -120px; +} + +div.jsoneditor-menu > button.jsoneditor-compact { + background-position: -72px -96px; +} + +div.jsoneditor-menu > button.jsoneditor-format { + background-position: -72px -120px; +} + +div.jsoneditor-menu > button.jsoneditor-repair { + background-position: -96px -96px; +} + +div.jsoneditor-menu > div.jsoneditor-modes { + display: inline-block; + float: left; +} + +div.jsoneditor-menu > div.jsoneditor-modes > button { + background: url none; + width: auto; + padding-left: 6px; + padding-right: 6px; +} + +div.jsoneditor-menu > button.jsoneditor-separator, +div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator { + margin-left: 10px; +} + +div.jsoneditor-menu a { + font-family: arial, sans-serif; + font-size: 10pt; + color: white; + opacity: 0.8; + vertical-align: middle; +} + +div.jsoneditor-menu a:hover { + opacity: 1; +} + +div.jsoneditor-menu a.jsoneditor-poweredBy { + font-size: 8pt; + position: absolute; + right: 0; + top: 0; + padding: 10px; +} +table.jsoneditor-search input, +table.jsoneditor-search div.jsoneditor-results { + font-family: arial, sans-serif; + font-size: 10pt; + color: #1A1A1A; + background: transparent; + /* For Firefox */ +} + +table.jsoneditor-search div.jsoneditor-results { + color: white; + padding-right: 5px; + line-height: 24px; +} + +table.jsoneditor-search { + position: absolute; + right: 4px; + top: 4px; + border-collapse: collapse; + border-spacing: 0; +} + +table.jsoneditor-search div.jsoneditor-frame { + border: 1px solid transparent; + background-color: white; + padding: 0 2px; + margin: 0; +} + +table.jsoneditor-search div.jsoneditor-frame table { + border-collapse: collapse; +} + +table.jsoneditor-search input { + width: 120px; + border: none; + outline: none; + margin: 1px; + line-height: 20px; +} + +table.jsoneditor-search button { + width: 16px; + height: 24px; + padding: 0; + margin: 0; + border: none; + background: url("img/jsoneditor-icons.png"); + vertical-align: top; +} + +table.jsoneditor-search button:hover { + background-color: transparent; +} + +table.jsoneditor-search button.jsoneditor-refresh { + width: 18px; + background-position: -99px -73px; +} + +table.jsoneditor-search button.jsoneditor-next { + cursor: pointer; + background-position: -124px -73px; +} + +table.jsoneditor-search button.jsoneditor-next:hover { + background-position: -124px -49px; +} + +table.jsoneditor-search button.jsoneditor-previous { + cursor: pointer; + background-position: -148px -73px; + margin-right: 2px; +} + +table.jsoneditor-search button.jsoneditor-previous:hover { + background-position: -148px -49px; +} +div.jsoneditor div.autocomplete.dropdown { + position: absolute; + background: white; + box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3); + border: 1px solid #d3d3d3; + z-index: 100; + overflow-x: hidden; + overflow-y: auto; + cursor: default; + margin: 0; + padding-left: 2pt; + padding-right: 5pt; + text-align: left; + outline: 0; + font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif; + font-size: 10pt; +} + +div.jsoneditor div.autocomplete.dropdown .item { + color: #333; +} + +div.jsoneditor div.autocomplete.dropdown .item.hover { + background-color: #ddd; +} + +div.jsoneditor div.autocomplete.hint { + color: #aaa; + top: 4px; + left: 4px; +} +div.jsoneditor-treepath { + padding: 0 5px; + overflow: hidden; +} + +div.jsoneditor-treepath div.jsoneditor-contextmenu-root { + position: absolute; + left: 0; +} + +div.jsoneditor-treepath span.jsoneditor-treepath-element { + margin: 1px; + font-family: arial, sans-serif; + font-size: 10pt; +} + +div.jsoneditor-treepath span.jsoneditor-treepath-seperator { + margin: 2px; + font-size: 9pt; + font-family: arial, sans-serif; +} + +div.jsoneditor-treepath span.jsoneditor-treepath-element:hover, +div.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover { + cursor: pointer; + text-decoration: underline; +} +div.jsoneditor-statusbar { + line-height: 26px; + height: 26px; + margin-top: -26px; + color: #808080; + background-color: #ebebeb; + border-top: 1px solid #d3d3d3; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-size: 10pt; +} + +div.jsoneditor-statusbar > .jsoneditor-curserinfo-label { + margin: 0 2px 0 4px; +} + +div.jsoneditor-statusbar > .jsoneditor-curserinfo-val { + margin-right: 12px; +} + +div.jsoneditor-statusbar > .jsoneditor-curserinfo-count { + margin-left: 4px; +} +div.jsoneditor-navigation-bar { + width: 100%; + height: 26px; + line-height: 26px; + padding: 0; + margin: 0; + border-bottom: 1px solid #d3d3d3; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #808080; + background-color: #ebebeb; + font-size: 10pt; +} + +div.jsoneditor-navigation-bar.nav-bar-empty:after { + content: 'Select a node ...'; + color: rgba(104, 104, 91, 0.56); + position: absolute; + margin-left: 5px; +} \ No newline at end of file diff --git a/main/webapp/modules/core/externals/jsoneditor/jsoneditor.js b/main/webapp/modules/core/externals/jsoneditor/jsoneditor.js new file mode 100644 index 000000000..c48653054 --- /dev/null +++ b/main/webapp/modules/core/externals/jsoneditor/jsoneditor.js @@ -0,0 +1,38359 @@ +/*! + * jsoneditor.js + * + * @brief + * JSONEditor is a web-based tool to view, edit, format, and validate JSON. + * It has various modes such as a tree editor, a code editor, and a plain text + * editor. + * + * Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+ + * + * @license + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * Copyright (c) 2011-2017 Jos de Jong, http://jsoneditoronline.org + * + * @author Jos de Jong, + * @version 5.11.0 + * @date 2017-11-22 + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["JSONEditor"] = factory(); + else + root["JSONEditor"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Ajv; + try { + Ajv = __webpack_require__(1); + } + catch (err) { + // no problem... when we need Ajv we will throw a neat exception + } + + var treemode = __webpack_require__(51); + var textmode = __webpack_require__(64); + var util = __webpack_require__(54); + + /** + * @constructor JSONEditor + * @param {Element} container Container element + * @param {Object} [options] Object with options. available options: + * {String} mode Editor mode. Available values: + * 'tree' (default), 'view', + * 'form', 'text', and 'code'. + * {function} onChange Callback method, triggered + * on change of contents + * {function} onError Callback method, triggered + * when an error occurs + * {Boolean} search Enable search box. + * True by default + * Only applicable for modes + * 'tree', 'view', and 'form' + * {Boolean} history Enable history (undo/redo). + * True by default + * Only applicable for modes + * 'tree', 'view', and 'form' + * {String} name Field name for the root node. + * Only applicable for modes + * 'tree', 'view', and 'form' + * {Number} indentation Number of indentation + * spaces. 4 by default. + * Only applicable for + * modes 'text' and 'code' + * {boolean} escapeUnicode If true, unicode + * characters are escaped. + * false by default. + * {boolean} sortObjectKeys If true, object keys are + * sorted before display. + * false by default. + * @param {Object | undefined} json JSON object + */ + function JSONEditor (container, options, json) { + if (!(this instanceof JSONEditor)) { + throw new Error('JSONEditor constructor called without "new".'); + } + + // check for unsupported browser (IE8 and older) + var ieVersion = util.getInternetExplorerVersion(); + if (ieVersion != -1 && ieVersion < 9) { + throw new Error('Unsupported browser, IE9 or newer required. ' + + 'Please install the newest version of your browser.'); + } + + if (options) { + // check for deprecated options + if (options.error) { + console.warn('Option "error" has been renamed to "onError"'); + options.onError = options.error; + delete options.error; + } + if (options.change) { + console.warn('Option "change" has been renamed to "onChange"'); + options.onChange = options.change; + delete options.change; + } + if (options.editable) { + console.warn('Option "editable" has been renamed to "onEditable"'); + options.onEditable = options.editable; + delete options.editable; + } + + // validate options + if (options) { + var VALID_OPTIONS = [ + 'ajv', 'schema', 'schemaRefs','templates', + 'ace', 'theme','autocomplete', + 'onChange', 'onEditable', 'onError', 'onModeChange', + 'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', + 'sortObjectKeys', 'navigationBar', 'statusBar' + ]; + + Object.keys(options).forEach(function (option) { + if (VALID_OPTIONS.indexOf(option) === -1) { + console.warn('Unknown option "' + option + '". This option will be ignored'); + } + }); + } + } + + if (arguments.length) { + this._create(container, options, json); + } + } + + /** + * Configuration for all registered modes. Example: + * { + * tree: { + * mixin: TreeEditor, + * data: 'json' + * }, + * text: { + * mixin: TextEditor, + * data: 'text' + * } + * } + * + * @type { Object. } + */ + JSONEditor.modes = {}; + + // debounce interval for JSON schema vaidation in milliseconds + JSONEditor.prototype.DEBOUNCE_INTERVAL = 150; + + /** + * Create the JSONEditor + * @param {Element} container Container element + * @param {Object} [options] See description in constructor + * @param {Object | undefined} json JSON object + * @private + */ + JSONEditor.prototype._create = function (container, options, json) { + this.container = container; + this.options = options || {}; + this.json = json || {}; + + var mode = this.options.mode || (this.options.modes && this.options.modes[0]) || 'tree'; + this.setMode(mode); + }; + + /** + * Destroy the editor. Clean up DOM, event listeners, and web workers. + */ + JSONEditor.prototype.destroy = function () {}; + + /** + * Set JSON object in editor + * @param {Object | undefined} json JSON data + */ + JSONEditor.prototype.set = function (json) { + this.json = json; + }; + + /** + * Get JSON from the editor + * @returns {Object} json + */ + JSONEditor.prototype.get = function () { + return this.json; + }; + + /** + * Set string containing JSON for the editor + * @param {String | undefined} jsonText + */ + JSONEditor.prototype.setText = function (jsonText) { + this.json = util.parse(jsonText); + }; + + /** + * Get stringified JSON contents from the editor + * @returns {String} jsonText + */ + JSONEditor.prototype.getText = function () { + return JSON.stringify(this.json); + }; + + /** + * Set a field name for the root node. + * @param {String | undefined} name + */ + JSONEditor.prototype.setName = function (name) { + if (!this.options) { + this.options = {}; + } + this.options.name = name; + }; + + /** + * Get the field name for the root node. + * @return {String | undefined} name + */ + JSONEditor.prototype.getName = function () { + return this.options && this.options.name; + }; + + /** + * Change the mode of the editor. + * JSONEditor will be extended with all methods needed for the chosen mode. + * @param {String} mode Available modes: 'tree' (default), 'view', 'form', + * 'text', and 'code'. + */ + JSONEditor.prototype.setMode = function (mode) { + var container = this.container; + var options = util.extend({}, this.options); + var oldMode = options.mode; + var data; + var name; + + options.mode = mode; + var config = JSONEditor.modes[mode]; + if (config) { + try { + var asText = (config.data == 'text'); + name = this.getName(); + data = this[asText ? 'getText' : 'get'](); // get text or json + + this.destroy(); + util.clear(this); + util.extend(this, config.mixin); + this.create(container, options); + + this.setName(name); + this[asText ? 'setText' : 'set'](data); // set text or json + + if (typeof config.load === 'function') { + try { + config.load.call(this); + } + catch (err) { + console.error(err); + } + } + + if (typeof options.onModeChange === 'function' && mode !== oldMode) { + try { + options.onModeChange(mode, oldMode); + } + catch (err) { + console.error(err); + } + } + } + catch (err) { + this._onError(err); + } + } + else { + throw new Error('Unknown mode "' + options.mode + '"'); + } + }; + + /** + * Get the current mode + * @return {string} + */ + JSONEditor.prototype.getMode = function () { + return this.options.mode; + }; + + /** + * Throw an error. If an error callback is configured in options.error, this + * callback will be invoked. Else, a regular error is thrown. + * @param {Error} err + * @private + */ + JSONEditor.prototype._onError = function(err) { + if (this.options && typeof this.options.onError === 'function') { + this.options.onError(err); + } + else { + throw err; + } + }; + + /** + * Set a JSON schema for validation of the JSON object. + * To remove the schema, call JSONEditor.setSchema(null) + * @param {Object | null} schema + * @param {Object.=} schemaRefs Schemas that are referenced using the `$ref` property from the JSON schema that are set in the `schema` option, + + the object structure in the form of `{reference_key: schemaObject}` + */ + JSONEditor.prototype.setSchema = function (schema, schemaRefs) { + // compile a JSON schema validator if a JSON schema is provided + if (schema) { + var ajv; + try { + // grab ajv from options if provided, else create a new instance + ajv = this.options.ajv || Ajv({ allErrors: true, verbose: true }); + + } + catch (err) { + console.warn('Failed to create an instance of Ajv, JSON Schema validation is not available. Please use a JSONEditor bundle including Ajv, or pass an instance of Ajv as via the configuration option `ajv`.'); + } + + if (ajv) { + if(schemaRefs) { + for (var ref in schemaRefs) { + ajv.removeSchema(ref); // When updating a schema - old refs has to be removed first + if(schemaRefs[ref]) { + ajv.addSchema(schemaRefs[ref], ref); + } + } + this.options.schemaRefs = schemaRefs; + } + this.validateSchema = ajv.compile(schema); + + // add schema to the options, so that when switching to an other mode, + // the set schema is not lost + this.options.schema = schema; + + // validate now + this.validate(); + } + + this.refresh(); // update DOM + } + else { + // remove current schema + this.validateSchema = null; + this.options.schema = null; + this.options.schemaRefs = null; + this.validate(); // to clear current error messages + this.refresh(); // update DOM + } + }; + + /** + * Validate current JSON object against the configured JSON schema + * Throws an exception when no JSON schema is configured + */ + JSONEditor.prototype.validate = function () { + // must be implemented by treemode and textmode + }; + + /** + * Refresh the rendered contents + */ + JSONEditor.prototype.refresh = function () { + // can be implemented by treemode and textmode + }; + + /** + * Register a plugin with one ore multiple modes for the JSON Editor. + * + * A mode is described as an object with properties: + * + * - `mode: String` The name of the mode. + * - `mixin: Object` An object containing the mixin functions which + * will be added to the JSONEditor. Must contain functions + * create, get, getText, set, and setText. May have + * additional functions. + * When the JSONEditor switches to a mixin, all mixin + * functions are added to the JSONEditor, and then + * the function `create(container, options)` is executed. + * - `data: 'text' | 'json'` The type of data that will be used to load the mixin. + * - `[load: function]` An optional function called after the mixin + * has been loaded. + * + * @param {Object | Array} mode A mode object or an array with multiple mode objects. + */ + JSONEditor.registerMode = function (mode) { + var i, prop; + + if (util.isArray(mode)) { + // multiple modes + for (i = 0; i < mode.length; i++) { + JSONEditor.registerMode(mode[i]); + } + } + else { + // validate the new mode + if (!('mode' in mode)) throw new Error('Property "mode" missing'); + if (!('mixin' in mode)) throw new Error('Property "mixin" missing'); + if (!('data' in mode)) throw new Error('Property "data" missing'); + var name = mode.mode; + if (name in JSONEditor.modes) { + throw new Error('Mode "' + name + '" already registered'); + } + + // validate the mixin + if (typeof mode.mixin.create !== 'function') { + throw new Error('Required function "create" missing on mixin'); + } + var reserved = ['setMode', 'registerMode', 'modes']; + for (i = 0; i < reserved.length; i++) { + prop = reserved[i]; + if (prop in mode.mixin) { + throw new Error('Reserved property "' + prop + '" not allowed in mixin'); + } + } + + JSONEditor.modes[name] = mode; + } + }; + + // register tree and text modes + JSONEditor.registerMode(treemode); + JSONEditor.registerMode(textmode); + + module.exports = JSONEditor; + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var compileSchema = __webpack_require__(2) + , resolve = __webpack_require__(3) + , Cache = __webpack_require__(19) + , SchemaObject = __webpack_require__(13) + , stableStringify = __webpack_require__(16) + , formats = __webpack_require__(20) + , rules = __webpack_require__(21) + , $dataMetaSchema = __webpack_require__(44) + , patternGroups = __webpack_require__(45) + , util = __webpack_require__(11) + , co = __webpack_require__(18); + + module.exports = Ajv; + + Ajv.prototype.validate = validate; + Ajv.prototype.compile = compile; + Ajv.prototype.addSchema = addSchema; + Ajv.prototype.addMetaSchema = addMetaSchema; + Ajv.prototype.validateSchema = validateSchema; + Ajv.prototype.getSchema = getSchema; + Ajv.prototype.removeSchema = removeSchema; + Ajv.prototype.addFormat = addFormat; + Ajv.prototype.errorsText = errorsText; + + Ajv.prototype._addSchema = _addSchema; + Ajv.prototype._compile = _compile; + + Ajv.prototype.compileAsync = __webpack_require__(46); + var customKeyword = __webpack_require__(47); + Ajv.prototype.addKeyword = customKeyword.add; + Ajv.prototype.getKeyword = customKeyword.get; + Ajv.prototype.removeKeyword = customKeyword.remove; + + var errorClasses = __webpack_require__(15); + Ajv.ValidationError = errorClasses.Validation; + Ajv.MissingRefError = errorClasses.MissingRef; + Ajv.$dataMetaSchema = $dataMetaSchema; + + var META_SCHEMA_ID = 'http://json-schema.org/draft-06/schema'; + + var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ]; + var META_SUPPORT_DATA = ['/properties']; + + /** + * Creates validator instance. + * Usage: `Ajv(opts)` + * @param {Object} opts optional options + * @return {Object} ajv instance + */ + function Ajv(opts) { + if (!(this instanceof Ajv)) return new Ajv(opts); + opts = this._opts = util.copy(opts) || {}; + setLogger(this); + this._schemas = {}; + this._refs = {}; + this._fragments = {}; + this._formats = formats(opts.format); + var schemaUriFormat = this._schemaUriFormat = this._formats['uri-reference']; + this._schemaUriFormatFunc = function (str) { return schemaUriFormat.test(str); }; + + this._cache = opts.cache || new Cache; + this._loadingSchemas = {}; + this._compilations = []; + this.RULES = rules(); + this._getId = chooseGetId(opts); + + opts.loopRequired = opts.loopRequired || Infinity; + if (opts.errorDataPath == 'property') opts._errorDataPathProperty = true; + if (opts.serialize === undefined) opts.serialize = stableStringify; + this._metaOpts = getMetaSchemaOptions(this); + + if (opts.formats) addInitialFormats(this); + addDraft6MetaSchema(this); + if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); + addInitialSchemas(this); + if (opts.patternGroups) patternGroups(this); + } + + + + /** + * Validate data using schema + * Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize. + * @this Ajv + * @param {String|Object} schemaKeyRef key, ref or schema object + * @param {Any} data to be validated + * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). + */ + function validate(schemaKeyRef, data) { + var v; + if (typeof schemaKeyRef == 'string') { + v = this.getSchema(schemaKeyRef); + if (!v) throw new Error('no schema with key or ref "' + schemaKeyRef + '"'); + } else { + var schemaObj = this._addSchema(schemaKeyRef); + v = schemaObj.validate || this._compile(schemaObj); + } + + var valid = v(data); + if (v.$async === true) + return this._opts.async == '*' ? co(valid) : valid; + this.errors = v.errors; + return valid; + } + + + /** + * Create validating function for passed schema. + * @this Ajv + * @param {Object} schema schema object + * @param {Boolean} _meta true if schema is a meta-schema. Used internally to compile meta schemas of custom keywords. + * @return {Function} validating function + */ + function compile(schema, _meta) { + var schemaObj = this._addSchema(schema, undefined, _meta); + return schemaObj.validate || this._compile(schemaObj); + } + + + /** + * Adds schema to the instance. + * @this Ajv + * @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. + * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + * @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead. + * @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + */ + function addSchema(schema, key, _skipValidation, _meta) { + if (Array.isArray(schema)){ + for (var i=0; i} errors optional array of validation errors, if not passed errors from the instance are used. + * @param {Object} options optional options with properties `separator` and `dataVar`. + * @return {String} human readable string with all errors descriptions + */ + function errorsText(errors, options) { + errors = errors || this.errors; + if (!errors) return 'No errors'; + options = options || {}; + var separator = options.separator === undefined ? ', ' : options.separator; + var dataVar = options.dataVar === undefined ? 'data' : options.dataVar; + + var text = ''; + for (var i=0; i= 0) return { index: index, compiling: true }; + index = this._compilations.length; + this._compilations[index] = { + schema: schema, + root: root, + baseId: baseId + }; + return { index: index, compiling: false }; + } + + + /** + * Removes the schema from the currently compiled list + * @this Ajv + * @param {Object} schema schema to compile + * @param {Object} root root object + * @param {String} baseId base schema ID + */ + function endCompiling(schema, root, baseId) { + /* jshint validthis: true */ + var i = compIndex.call(this, schema, root, baseId); + if (i >= 0) this._compilations.splice(i, 1); + } + + + /** + * Index of schema compilation in the currently compiled list + * @this Ajv + * @param {Object} schema schema to compile + * @param {Object} root root object + * @param {String} baseId base schema ID + * @return {Integer} compilation index + */ + function compIndex(schema, root, baseId) { + /* jshint validthis: true */ + for (var i=0; i', '"', '`', ' ', '\r', '\n', '\t'], + + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), + + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''].concat(unwise), + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), + hostEndingChars = ['/', '?', '#'], + hostnameMaxLen = 255, + hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = __webpack_require__(7); + + function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && isObject(url) && url instanceof Url) return url; + + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; + } + + Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + var rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { + + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); + } + + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); + } + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; + + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; + } + } + } + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + // IDNA Support: Returns a puny coded representation of "domain". + // It only converts the part of the domain name that + // has non ASCII characters. I.e. it dosent matter if + // you call it with a domain that already is in ASCII. + var domainArray = this.hostname.split('.'); + var newOut = []; + for (var i = 0; i < domainArray.length; ++i) { + var s = domainArray[i]; + newOut.push(s.match(/[^A-Za-z0-9_-]/) ? + 'xn--' + punycode.encode(s) : s); + } + this.hostname = newOut.join('.'); + } + + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } + } + + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { + + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); + } + rest = rest.split(ae).join(esc); + } + } + + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = querystring.parse(this.query); + } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ''; + this.query = {}; + } + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; + } + + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; + } + + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; + }; + + // format a parsed object into a url string + function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (isString(obj)) obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); + } + + Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } + + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; + } + } + + if (this.query && + isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } + + var search = this.search || (query && ('?' + query)) || ''; + + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; + + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } + + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; + + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + + return protocol + host + pathname + search + hash; + }; + + function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); + } + + Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); + }; + + function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); + } + + Url.prototype.resolveObject = function(relative) { + if (isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } + + var result = new Url(); + Object.keys(this).forEach(function(k) { + result[k] = this[k]; + }, this); + + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + Object.keys(relative).forEach(function(k) { + if (k !== 'protocol') + result[k] = relative[k]; + }); + + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } + + result.href = result.format(); + return result; + } + + if (relative.protocol && relative.protocol !== result.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + Object.keys(relative).forEach(function(k) { + result[k] = relative[k]; + }); + result.href = result.format(); + return result; + } + + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; + } + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; + } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (result.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = result.pathname && result.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; + + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); + } + result.host = ''; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + } + + if (isRelAbs) { + // it's absolute. + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (!isNullOrUndefined(relative.search)) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + result.hostname = result.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + result.search = relative.search; + result.query = relative.query; + //to support http.request + if (!isNull(result.pathname) || !isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + result.pathname = null; + //to support http.request + if (result.search) { + result.path = '/' + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (result.host || relative.host) && (last === '.' || last === '..') || + last === ''); + + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last == '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } + + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); + + // put the host back + if (psychotic) { + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); + } + + //to support request.http + if (!isNull(result.pathname) || !isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + }; + + Url.prototype.parseHost = function() { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) this.hostname = host; + }; + + function isString(arg) { + return typeof arg === "string"; + } + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + + function isNull(arg) { + return arg === null; + } + function isNullOrUndefined(arg) { + return arg == null; + } + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */ + ;(function(root) { + + /** Detect free variables */ + var freeExports = typeof exports == 'object' && exports && + !exports.nodeType && exports; + var freeModule = typeof module == 'object' && module && + !module.nodeType && module; + var freeGlobal = typeof global == 'object' && global; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * http://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.3.2', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + /** Expose `punycode` */ + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + true + ) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return punycode; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (freeExports && freeModule) { + if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = punycode; + } else { // in Narwhal or RingoJS v0.7.0- + for (key in punycode) { + punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); + } + } + } else { // in Rhino or a web browser + root.punycode = punycode; + } + + }(this)); + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6)(module), (function() { return this; }()))) + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.decode = exports.parse = __webpack_require__(8); + exports.encode = exports.stringify = __webpack_require__(9); + + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + 'use strict'; + + // If obj.hasOwnProperty has been overridden, then calling + // obj.hasOwnProperty(prop) will break. + // See: https://github.com/joyent/node/issues/1707 + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + module.exports = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}; + + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } + + var regexp = /\+/g; + qs = qs.split(sep); + + var maxKeys = 1000; + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } + + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } + + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; + + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; + } + + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); + + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (Array.isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; + } + } + + return obj; + }; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + 'use strict'; + + var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; + + case 'boolean': + return v ? 'true' : 'false'; + + case 'number': + return isFinite(v) ? v : ''; + + default: + return ''; + } + }; + + module.exports = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + if (obj === null) { + obj = undefined; + } + + if (typeof obj === 'object') { + return Object.keys(obj).map(function(k) { + var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; + if (Array.isArray(obj[k])) { + return obj[k].map(function(v) { + return ks + encodeURIComponent(stringifyPrimitive(v)); + }).join(sep); + } else { + return ks + encodeURIComponent(stringifyPrimitive(obj[k])); + } + }).join(sep); + + } + + if (!name) return ''; + return encodeURIComponent(stringifyPrimitive(name)) + eq + + encodeURIComponent(stringifyPrimitive(obj)); + }; + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + 'use strict'; + + module.exports = function equal(a, b) { + if (a === b) return true; + + var arrA = Array.isArray(a) + , arrB = Array.isArray(b) + , i; + + if (arrA && arrB) { + if (a.length != b.length) return false; + for (i = 0; i < a.length; i++) + if (!equal(a[i], b[i])) return false; + return true; + } + + if (arrA != arrB) return false; + + if (a && b && typeof a === 'object' && typeof b === 'object') { + var keys = Object.keys(a); + if (keys.length !== Object.keys(b).length) return false; + + var dateA = a instanceof Date + , dateB = b instanceof Date; + if (dateA && dateB) return a.getTime() == b.getTime(); + if (dateA != dateB) return false; + + var regexpA = a instanceof RegExp + , regexpB = b instanceof RegExp; + if (regexpA && regexpB) return a.toString() == b.toString(); + if (regexpA != regexpB) return false; + + for (i = 0; i < keys.length; i++) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = 0; i < keys.length; i++) + if(!equal(a[keys[i]], b[keys[i]])) return false; + + return true; + } + + return false; + }; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + module.exports = { + copy: copy, + checkDataType: checkDataType, + checkDataTypes: checkDataTypes, + coerceToTypes: coerceToTypes, + toHash: toHash, + getProperty: getProperty, + escapeQuotes: escapeQuotes, + equal: __webpack_require__(10), + ucs2length: __webpack_require__(12), + varOccurences: varOccurences, + varReplace: varReplace, + cleanUpCode: cleanUpCode, + finalCleanUpCode: finalCleanUpCode, + schemaHasRules: schemaHasRules, + schemaHasRulesExcept: schemaHasRulesExcept, + toQuotedString: toQuotedString, + getPathExpr: getPathExpr, + getPath: getPath, + getData: getData, + unescapeFragment: unescapeFragment, + unescapeJsonPointer: unescapeJsonPointer, + escapeFragment: escapeFragment, + escapeJsonPointer: escapeJsonPointer + }; + + + function copy(o, to) { + to = to || {}; + for (var key in o) to[key] = o[key]; + return to; + } + + + function checkDataType(dataType, data, negate) { + var EQUAL = negate ? ' !== ' : ' === ' + , AND = negate ? ' || ' : ' && ' + , OK = negate ? '!' : '' + , NOT = negate ? '' : '!'; + switch (dataType) { + case 'null': return data + EQUAL + 'null'; + case 'array': return OK + 'Array.isArray(' + data + ')'; + case 'object': return '(' + OK + data + AND + + 'typeof ' + data + EQUAL + '"object"' + AND + + NOT + 'Array.isArray(' + data + '))'; + case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND + + NOT + '(' + data + ' % 1)' + + AND + data + EQUAL + data + ')'; + default: return 'typeof ' + data + EQUAL + '"' + dataType + '"'; + } + } + + + function checkDataTypes(dataTypes, data) { + switch (dataTypes.length) { + case 1: return checkDataType(dataTypes[0], data, true); + default: + var code = ''; + var types = toHash(dataTypes); + if (types.array && types.object) { + code = types.null ? '(': '(!' + data + ' || '; + code += 'typeof ' + data + ' !== "object")'; + delete types.null; + delete types.array; + delete types.object; + } + if (types.number) delete types.integer; + for (var t in types) + code += (code ? ' && ' : '' ) + checkDataType(t, data, true); + + return code; + } + } + + + var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]); + function coerceToTypes(optionCoerceTypes, dataTypes) { + if (Array.isArray(dataTypes)) { + var types = []; + for (var i=0; i= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl); + return paths[lvl - up]; + } + + if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl); + data = 'data' + ((lvl - up) || ''); + if (!jsonPointer) return data; + } + + var expr = data; + var segments = jsonPointer.split('/'); + for (var i=0; i= 0xD800 && value <= 0xDBFF && pos < len) { + // high surrogate, and there is a next character + value = str.charCodeAt(pos); + if ((value & 0xFC00) == 0xDC00) pos++; // low surrogate + } + } + return length; + }; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(11); + + module.exports = SchemaObject; + + function SchemaObject(obj) { + util.copy(obj, this); + } + + +/***/ }, +/* 14 */ +/***/ function(module, exports) { + + 'use strict'; + + var traverse = module.exports = function (schema, opts, cb) { + if (typeof opts == 'function') { + cb = opts; + opts = {}; + } + _traverse(opts, cb, schema, '', schema); + }; + + + traverse.keywords = { + additionalItems: true, + items: true, + contains: true, + additionalProperties: true, + propertyNames: true, + not: true + }; + + traverse.arrayKeywords = { + items: true, + allOf: true, + anyOf: true, + oneOf: true + }; + + traverse.propsKeywords = { + definitions: true, + properties: true, + patternProperties: true, + dependencies: true + }; + + traverse.skipKeywords = { + enum: true, + const: true, + required: true, + maximum: true, + minimum: true, + exclusiveMaximum: true, + exclusiveMinimum: true, + multipleOf: true, + maxLength: true, + minLength: true, + pattern: true, + format: true, + maxItems: true, + minItems: true, + uniqueItems: true, + maxProperties: true, + minProperties: true + }; + + + function _traverse(opts, cb, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) { + if (schema && typeof schema == 'object' && !Array.isArray(schema)) { + cb(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex); + for (var key in schema) { + var sch = schema[key]; + if (Array.isArray(sch)) { + if (key in traverse.arrayKeywords) { + for (var i=0; i 2) res = slice.call(arguments, 1); + resolve(res); + }); + }); + } + + /** + * Convert an array of "yieldables" to a promise. + * Uses `Promise.all()` internally. + * + * @param {Array} obj + * @return {Promise} + * @api private + */ + + function arrayToPromise(obj) { + return Promise.all(obj.map(toPromise, this)); + } + + /** + * Convert an object of "yieldables" to a promise. + * Uses `Promise.all()` internally. + * + * @param {Object} obj + * @return {Promise} + * @api private + */ + + function objectToPromise(obj){ + var results = new obj.constructor(); + var keys = Object.keys(obj); + var promises = []; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var promise = toPromise.call(this, obj[key]); + if (promise && isPromise(promise)) defer(promise, key); + else results[key] = obj[key]; + } + return Promise.all(promises).then(function () { + return results; + }); + + function defer(promise, key) { + // predefine the key in the result + results[key] = undefined; + promises.push(promise.then(function (res) { + results[key] = res; + })); + } + } + + /** + * Check if `obj` is a promise. + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ + + function isPromise(obj) { + return 'function' == typeof obj.then; + } + + /** + * Check if `obj` is a generator. + * + * @param {Mixed} obj + * @return {Boolean} + * @api private + */ + + function isGenerator(obj) { + return 'function' == typeof obj.next && 'function' == typeof obj.throw; + } + + /** + * Check if `obj` is a generator function. + * + * @param {Mixed} obj + * @return {Boolean} + * @api private + */ + function isGeneratorFunction(obj) { + var constructor = obj.constructor; + if (!constructor) return false; + if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; + return isGenerator(constructor.prototype); + } + + /** + * Check for plain object. + * + * @param {Mixed} val + * @return {Boolean} + * @api private + */ + + function isObject(val) { + return Object == val.constructor; + } + + +/***/ }, +/* 19 */ +/***/ function(module, exports) { + + 'use strict'; + + + var Cache = module.exports = function Cache() { + this._cache = {}; + }; + + + Cache.prototype.put = function Cache_put(key, value) { + this._cache[key] = value; + }; + + + Cache.prototype.get = function Cache_get(key) { + return this._cache[key]; + }; + + + Cache.prototype.del = function Cache_del(key) { + delete this._cache[key]; + }; + + + Cache.prototype.clear = function Cache_clear() { + this._cache = {}; + }; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(11); + + var DATE = /^\d\d\d\d-(\d\d)-(\d\d)$/; + var DAYS = [0,31,29,31,30,31,30,31,31,30,31,30,31]; + var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i; + var HOSTNAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i; + var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; + var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; + // uri-template: https://tools.ietf.org/html/rfc6570 + var URITEMPLATE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i; + // For the source: https://gist.github.com/dperini/729294 + // For test cases: https://mathiasbynens.be/demo/url-regex + // @todo Delete current URL in favour of the commented out URL rule when this issue is fixed https://github.com/eslint/eslint/issues/7983. + // var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu; + var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i; + var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i; + var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i; + var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; + + + module.exports = formats; + + function formats(mode) { + mode = mode == 'full' ? 'full' : 'fast'; + return util.copy(formats[mode]); + } + + + formats.fast = { + // date: http://tools.ietf.org/html/rfc3339#section-5.6 + date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/, + // date-time: http://tools.ietf.org/html/rfc3339#section-5.6 + time: /^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i, + 'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i, + // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js + uri: /^(?:[a-z][a-z0-9+-.]*)(?::|\/)\/?[^\s]*$/i, + 'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i, + 'uri-template': URITEMPLATE, + url: URL, + // email (sources from jsen validator): + // http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address#answer-8829363 + // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address (search for 'willful violation') + email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i, + hostname: HOSTNAME, + // optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html + ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, + // optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, + regex: regex, + // uuid: http://tools.ietf.org/html/rfc4122 + uuid: UUID, + // JSON-pointer: https://tools.ietf.org/html/rfc6901 + // uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A + 'json-pointer': JSON_POINTER, + // relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00 + 'relative-json-pointer': RELATIVE_JSON_POINTER + }; + + + formats.full = { + date: date, + time: time, + 'date-time': date_time, + uri: uri, + 'uri-reference': URIREF, + 'uri-template': URITEMPLATE, + url: URL, + email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, + hostname: hostname, + ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, + ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, + regex: regex, + uuid: UUID, + 'json-pointer': JSON_POINTER, + 'relative-json-pointer': RELATIVE_JSON_POINTER + }; + + + function date(str) { + // full-date from http://tools.ietf.org/html/rfc3339#section-5.6 + var matches = str.match(DATE); + if (!matches) return false; + + var month = +matches[1]; + var day = +matches[2]; + return month >= 1 && month <= 12 && day >= 1 && day <= DAYS[month]; + } + + + function time(str, full) { + var matches = str.match(TIME); + if (!matches) return false; + + var hour = matches[1]; + var minute = matches[2]; + var second = matches[3]; + var timeZone = matches[5]; + return hour <= 23 && minute <= 59 && second <= 59 && (!full || timeZone); + } + + + var DATE_TIME_SEPARATOR = /t|\s/i; + function date_time(str) { + // http://tools.ietf.org/html/rfc3339#section-5.6 + var dateTime = str.split(DATE_TIME_SEPARATOR); + return dateTime.length == 2 && date(dateTime[0]) && time(dateTime[1], true); + } + + + function hostname(str) { + // https://tools.ietf.org/html/rfc1034#section-3.5 + // https://tools.ietf.org/html/rfc1123#section-2 + return str.length <= 255 && HOSTNAME.test(str); + } + + + var NOT_URI_FRAGMENT = /\/|:/; + function uri(str) { + // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "." + return NOT_URI_FRAGMENT.test(str) && URI.test(str); + } + + + var Z_ANCHOR = /[^\\]\\Z/; + function regex(str) { + if (Z_ANCHOR.test(str)) return false; + try { + new RegExp(str); + return true; + } catch(e) { + return false; + } + } + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var ruleModules = __webpack_require__(22) + , toHash = __webpack_require__(11).toHash; + + module.exports = function rules() { + var RULES = [ + { type: 'number', + rules: [ { 'maximum': ['exclusiveMaximum'] }, + { 'minimum': ['exclusiveMinimum'] }, 'multipleOf', 'format'] }, + { type: 'string', + rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] }, + { type: 'array', + rules: [ 'maxItems', 'minItems', 'uniqueItems', 'contains', 'items' ] }, + { type: 'object', + rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames', + { 'properties': ['additionalProperties', 'patternProperties'] } ] }, + { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf' ] } + ]; + + var ALL = [ 'type' ]; + var KEYWORDS = [ + 'additionalItems', '$schema', '$id', 'id', 'title', + 'description', 'default', 'definitions' + ]; + var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; + RULES.all = toHash(ALL); + RULES.types = toHash(TYPES); + + RULES.forEach(function (group) { + group.rules = group.rules.map(function (keyword) { + var implKeywords; + if (typeof keyword == 'object') { + var key = Object.keys(keyword)[0]; + implKeywords = keyword[key]; + keyword = key; + implKeywords.forEach(function (k) { + ALL.push(k); + RULES.all[k] = true; + }); + } + ALL.push(keyword); + var rule = RULES.all[keyword] = { + keyword: keyword, + code: ruleModules[keyword], + implements: implKeywords + }; + return rule; + }); + + if (group.type) RULES.types[group.type] = group; + }); + + RULES.keywords = toHash(ALL.concat(KEYWORDS)); + RULES.custom = {}; + + return RULES; + }; + + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + //all requires must be explicit because browserify won't work with dynamic requires + module.exports = { + '$ref': __webpack_require__(23), + allOf: __webpack_require__(24), + anyOf: __webpack_require__(25), + const: __webpack_require__(26), + contains: __webpack_require__(27), + dependencies: __webpack_require__(28), + 'enum': __webpack_require__(29), + format: __webpack_require__(30), + items: __webpack_require__(31), + maximum: __webpack_require__(32), + minimum: __webpack_require__(32), + maxItems: __webpack_require__(33), + minItems: __webpack_require__(33), + maxLength: __webpack_require__(34), + minLength: __webpack_require__(34), + maxProperties: __webpack_require__(35), + minProperties: __webpack_require__(35), + multipleOf: __webpack_require__(36), + not: __webpack_require__(37), + oneOf: __webpack_require__(38), + pattern: __webpack_require__(39), + properties: __webpack_require__(40), + propertyNames: __webpack_require__(41), + required: __webpack_require__(42), + uniqueItems: __webpack_require__(43), + validate: __webpack_require__(17) + }; + + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_ref(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $async, $refCode; + if ($schema == '#' || $schema == '#/') { + if (it.isRoot) { + $async = it.async; + $refCode = 'validate'; + } else { + $async = it.root.schema.$async === true; + $refCode = 'root.refVal[0]'; + } + } else { + var $refVal = it.resolveRef(it.baseId, $schema, it.isRoot); + if ($refVal === undefined) { + var $message = it.MissingRefError.message(it.baseId, $schema); + if (it.opts.missingRefs == 'fail') { + it.logger.error($message); + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('$ref') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { ref: \'' + (it.util.escapeQuotes($schema)) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'can\\\'t resolve reference ' + (it.util.escapeQuotes($schema)) + '\' '; + } + if (it.opts.verbose) { + out += ' , schema: ' + (it.util.toQuotedString($schema)) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + if ($breakOnError) { + out += ' if (false) { '; + } + } else if (it.opts.missingRefs == 'ignore') { + it.logger.warn($message); + if ($breakOnError) { + out += ' if (true) { '; + } + } else { + throw new it.MissingRefError(it.baseId, $schema, $message); + } + } else if ($refVal.inline) { + var $it = it.util.copy(it); + $it.level++; + var $nextValid = 'valid' + $it.level; + $it.schema = $refVal.schema; + $it.schemaPath = ''; + $it.errSchemaPath = $schema; + var $code = it.validate($it).replace(/validate\.schema/g, $refVal.code); + out += ' ' + ($code) + ' '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + } + } else { + $async = $refVal.$async === true; + $refCode = $refVal.code; + } + } + if ($refCode) { + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; + if (it.opts.passContext) { + out += ' ' + ($refCode) + '.call(this, '; + } else { + out += ' ' + ($refCode) + '( '; + } + out += ' ' + ($data) + ', (dataPath || \'\')'; + if (it.errorPath != '""') { + out += ' + ' + (it.errorPath); + } + var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData', + $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty'; + out += ' , ' + ($parentData) + ' , ' + ($parentDataProperty) + ', rootData) '; + var __callValidate = out; + out = $$outStack.pop(); + if ($async) { + if (!it.async) throw new Error('async schema referenced by sync schema'); + if ($breakOnError) { + out += ' var ' + ($valid) + '; '; + } + out += ' try { ' + (it.yieldAwait) + ' ' + (__callValidate) + '; '; + if ($breakOnError) { + out += ' ' + ($valid) + ' = true; '; + } + out += ' } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; '; + if ($breakOnError) { + out += ' ' + ($valid) + ' = false; '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($valid) + ') { '; + } + } else { + out += ' if (!' + (__callValidate) + ') { if (vErrors === null) vErrors = ' + ($refCode) + '.errors; else vErrors = vErrors.concat(' + ($refCode) + '.errors); errors = vErrors.length; } '; + if ($breakOnError) { + out += ' else { '; + } + } + } + return out; + } + + +/***/ }, +/* 24 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_allOf(it, $keyword, $ruleType) { + var out = ' '; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $currentBaseId = $it.baseId, + $allSchemasEmpty = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + $allSchemasEmpty = false; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if ($breakOnError) { + if ($allSchemasEmpty) { + out += ' if (true) { '; + } else { + out += ' ' + ($closingBraces.slice(0, -1)) + ' '; + } + } + out = it.util.cleanUpCode(out); + return out; + } + + +/***/ }, +/* 25 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_anyOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $noEmptySchema = $schema.every(function($sch) { + return it.util.schemaHasRules($sch, it.RULES.all); + }); + if ($noEmptySchema) { + var $currentBaseId = $it.baseId; + out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = false; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' ' + ($valid) + ' = ' + ($valid) + ' || ' + ($nextValid) + '; if (!' + ($valid) + ') { '; + $closingBraces += '}'; + } + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' ' + ($closingBraces) + ' if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('anyOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should match some schema in anyOf\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; return false; '; + } + } + out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + if (it.opts.allErrors) { + out += ' } '; + } + out = it.util.cleanUpCode(out); + } else { + if ($breakOnError) { + out += ' if (true) { '; + } + } + return out; + } + + +/***/ }, +/* 26 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_const(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + if (!$isData) { + out += ' var schema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ';'; + } + out += 'var ' + ($valid) + ' = equal(' + ($data) + ', schema' + ($lvl) + '); if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('const') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should be equal to constant\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' }'; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 27 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_contains(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $idx = 'i' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $currentBaseId = it.baseId, + $nonEmptySchema = it.util.schemaHasRules($schema, it.RULES.all); + out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';'; + if ($nonEmptySchema) { + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' var ' + ($nextValid) + ' = false; for (var ' + ($idx) + ' = 0; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' if (' + ($nextValid) + ') break; } '; + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' ' + ($closingBraces) + ' if (!' + ($nextValid) + ') {'; + } else { + out += ' if (' + ($data) + '.length == 0) {'; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('contains') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should contain a valid item\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + if ($nonEmptySchema) { + out += ' errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + } + if (it.opts.allErrors) { + out += ' } '; + } + out = it.util.cleanUpCode(out); + return out; + } + + +/***/ }, +/* 28 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_dependencies(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $schemaDeps = {}, + $propertyDeps = {}, + $ownProperties = it.opts.ownProperties; + for ($property in $schema) { + var $sch = $schema[$property]; + var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps; + $deps[$property] = $sch; + } + out += 'var ' + ($errs) + ' = errors;'; + var $currentErrorPath = it.errorPath; + out += 'var missing' + ($lvl) + ';'; + for (var $property in $propertyDeps) { + $deps = $propertyDeps[$property]; + if ($deps.length) { + out += ' if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') '; + } + if ($breakOnError) { + out += ' && ( '; + var arr1 = $deps; + if (arr1) { + var $propertyKey, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $propertyKey = arr1[$i += 1]; + if ($i) { + out += ' || '; + } + var $prop = it.util.getProperty($propertyKey), + $useData = $data + $prop; + out += ' ( ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) '; + } + } + out += ')) { '; + var $propertyPath = 'missing' + $lvl, + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should have '; + if ($deps.length == 1) { + out += 'property ' + (it.util.escapeQuotes($deps[0])); + } else { + out += 'properties ' + (it.util.escapeQuotes($deps.join(", "))); + } + out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + } else { + out += ' ) { '; + var arr2 = $deps; + if (arr2) { + var $propertyKey, i2 = -1, + l2 = arr2.length - 1; + while (i2 < l2) { + $propertyKey = arr2[i2 += 1]; + var $prop = it.util.getProperty($propertyKey), + $missingProperty = it.util.escapeQuotes($propertyKey), + $useData = $data + $prop; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should have '; + if ($deps.length == 1) { + out += 'property ' + (it.util.escapeQuotes($deps[0])); + } else { + out += 'properties ' + (it.util.escapeQuotes($deps.join(", "))); + } + out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '; + } + } + } + out += ' } '; + if ($breakOnError) { + $closingBraces += '}'; + out += ' else { '; + } + } + } + it.errorPath = $currentErrorPath; + var $currentBaseId = $it.baseId; + for (var $property in $schemaDeps) { + var $sch = $schemaDeps[$property]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + out += ' ' + ($nextValid) + ' = true; if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') '; + } + out += ') { '; + $it.schema = $sch; + $it.schemaPath = $schemaPath + it.util.getProperty($property); + $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($property); + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; + } + + +/***/ }, +/* 29 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_enum(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $i = 'i' + $lvl, + $vSchema = 'schema' + $lvl; + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + ';'; + } + out += 'var ' + ($valid) + ';'; + if ($isData) { + out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {'; + } + out += '' + ($valid) + ' = false;for (var ' + ($i) + '=0; ' + ($i) + '<' + ($vSchema) + '.length; ' + ($i) + '++) if (equal(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + '])) { ' + ($valid) + ' = true; break; }'; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('enum') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { allowedValues: schema' + ($lvl) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be equal to one of the allowed values\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' }'; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 30 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_format(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + if (it.opts.format === false) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $unknownFormats = it.opts.unknownFormats, + $allowUnknown = Array.isArray($unknownFormats); + if ($isData) { + var $format = 'format' + $lvl, + $isObject = 'isObject' + $lvl, + $formatType = 'formatType' + $lvl; + out += ' var ' + ($format) + ' = formats[' + ($schemaValue) + ']; var ' + ($isObject) + ' = typeof ' + ($format) + ' == \'object\' && !(' + ($format) + ' instanceof RegExp) && ' + ($format) + '.validate; var ' + ($formatType) + ' = ' + ($isObject) + ' && ' + ($format) + '.type || \'string\'; if (' + ($isObject) + ') { '; + if (it.async) { + out += ' var async' + ($lvl) + ' = ' + ($format) + '.async; '; + } + out += ' ' + ($format) + ' = ' + ($format) + '.validate; } if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || '; + } + out += ' ('; + if ($unknownFormats != 'ignore') { + out += ' (' + ($schemaValue) + ' && !' + ($format) + ' '; + if ($allowUnknown) { + out += ' && self._opts.unknownFormats.indexOf(' + ($schemaValue) + ') == -1 '; + } + out += ') || '; + } + out += ' (' + ($format) + ' && ' + ($formatType) + ' == \'' + ($ruleType) + '\' && !(typeof ' + ($format) + ' == \'function\' ? '; + if (it.async) { + out += ' (async' + ($lvl) + ' ? ' + (it.yieldAwait) + ' ' + ($format) + '(' + ($data) + ') : ' + ($format) + '(' + ($data) + ')) '; + } else { + out += ' ' + ($format) + '(' + ($data) + ') '; + } + out += ' : ' + ($format) + '.test(' + ($data) + '))))) {'; + } else { + var $format = it.formats[$schema]; + if (!$format) { + if ($unknownFormats == 'ignore') { + it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } else if ($allowUnknown && $unknownFormats.indexOf($schema) >= 0) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } else { + throw new Error('unknown format "' + $schema + '" is used in schema at path "' + it.errSchemaPath + '"'); + } + } + var $isObject = typeof $format == 'object' && !($format instanceof RegExp) && $format.validate; + var $formatType = $isObject && $format.type || 'string'; + if ($isObject) { + var $async = $format.async === true; + $format = $format.validate; + } + if ($formatType != $ruleType) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } + if ($async) { + if (!it.async) throw new Error('async format in sync schema'); + var $formatRef = 'formats' + it.util.getProperty($schema) + '.validate'; + out += ' if (!(' + (it.yieldAwait) + ' ' + ($formatRef) + '(' + ($data) + '))) { '; + } else { + out += ' if (! '; + var $formatRef = 'formats' + it.util.getProperty($schema); + if ($isObject) $formatRef += '.validate'; + if (typeof $format == 'function') { + out += ' ' + ($formatRef) + '(' + ($data) + ') '; + } else { + out += ' ' + ($formatRef) + '.test(' + ($data) + ') '; + } + out += ') { '; + } + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('format') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { format: '; + if ($isData) { + out += '' + ($schemaValue); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match format "'; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + (it.util.escapeQuotes($schema)); + } + out += '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 31 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_items(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $idx = 'i' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $currentBaseId = it.baseId; + out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';'; + if (Array.isArray($schema)) { + var $additionalItems = it.schema.additionalItems; + if ($additionalItems === false) { + out += ' ' + ($valid) + ' = ' + ($data) + '.length <= ' + ($schema.length) + '; '; + var $currErrSchemaPath = $errSchemaPath; + $errSchemaPath = it.errSchemaPath + '/additionalItems'; + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('additionalItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schema.length) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have more than ' + ($schema.length) + ' items\' '; + } + if (it.opts.verbose) { + out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + $errSchemaPath = $currErrSchemaPath; + if ($breakOnError) { + $closingBraces += '}'; + out += ' else { '; + } + } + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($i) + ') { '; + var $passData = $data + '[' + $i + ']'; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + $it.errorPath = it.util.getPathExpr(it.errorPath, $i, it.opts.jsonPointers, true); + $it.dataPathArr[$dataNxt] = $i; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if (typeof $additionalItems == 'object' && it.util.schemaHasRules($additionalItems, it.RULES.all)) { + $it.schema = $additionalItems; + $it.schemaPath = it.schemaPath + '.additionalItems'; + $it.errSchemaPath = it.errSchemaPath + '/additionalItems'; + out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($schema.length) + ') { for (var ' + ($idx) + ' = ' + ($schema.length) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' } } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } else if (it.util.schemaHasRules($schema, it.RULES.all)) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' for (var ' + ($idx) + ' = ' + (0) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' }'; + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; + } + + +/***/ }, +/* 32 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate__limit(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $isMax = $keyword == 'maximum', + $exclusiveKeyword = $isMax ? 'exclusiveMaximum' : 'exclusiveMinimum', + $schemaExcl = it.schema[$exclusiveKeyword], + $isDataExcl = it.opts.$data && $schemaExcl && $schemaExcl.$data, + $op = $isMax ? '<' : '>', + $notOp = $isMax ? '>' : '<', + $errorKeyword = undefined; + if ($isDataExcl) { + var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr), + $exclusive = 'exclusive' + $lvl, + $exclType = 'exclType' + $lvl, + $exclIsNumber = 'exclIsNumber' + $lvl, + $opExpr = 'op' + $lvl, + $opStr = '\' + ' + $opExpr + ' + \''; + out += ' var schemaExcl' + ($lvl) + ' = ' + ($schemaValueExcl) + '; '; + $schemaValueExcl = 'schemaExcl' + $lvl; + out += ' var ' + ($exclusive) + '; var ' + ($exclType) + ' = typeof ' + ($schemaValueExcl) + '; if (' + ($exclType) + ' != \'boolean\' && ' + ($exclType) + ' != \'undefined\' && ' + ($exclType) + ' != \'number\') { '; + var $errorKeyword = $exclusiveKeyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_exclusiveLimit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'' + ($exclusiveKeyword) + ' should be boolean\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($exclType) + ' == \'number\' ? ( (' + ($exclusive) + ' = ' + ($schemaValue) + ' === undefined || ' + ($schemaValueExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ') ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValueExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) : ( (' + ($exclusive) + ' = ' + ($schemaValueExcl) + ' === true) ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValue) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { var op' + ($lvl) + ' = ' + ($exclusive) + ' ? \'' + ($op) + '\' : \'' + ($op) + '=\';'; + } else { + var $exclIsNumber = typeof $schemaExcl == 'number', + $opStr = $op; + if ($exclIsNumber && $isData) { + var $opExpr = '\'' + $opStr + '\''; + out += ' if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ( ' + ($schemaValue) + ' === undefined || ' + ($schemaExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ' ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { '; + } else { + if ($exclIsNumber && $schema === undefined) { + $exclusive = true; + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $schemaValue = $schemaExcl; + $notOp += '='; + } else { + if ($exclIsNumber) $schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema); + if ($schemaExcl === ($exclIsNumber ? $schemaValue : true)) { + $exclusive = true; + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $notOp += '='; + } else { + $exclusive = false; + $opStr += '='; + } + } + var $opExpr = '\'' + $opStr + '\''; + out += ' if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' || ' + ($data) + ' !== ' + ($data) + ') { '; + } + } + $errorKeyword = $errorKeyword || $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { comparison: ' + ($opExpr) + ', limit: ' + ($schemaValue) + ', exclusive: ' + ($exclusive) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be ' + ($opStr) + ' '; + if ($isData) { + out += '\' + ' + ($schemaValue); + } else { + out += '' + ($schemaValue) + '\''; + } + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 33 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate__limitItems(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxItems' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($data) + '.length ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have '; + if ($keyword == 'maxItems') { + out += 'more'; + } else { + out += 'less'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' items\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 34 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate__limitLength(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxLength' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + if (it.opts.unicode === false) { + out += ' ' + ($data) + '.length '; + } else { + out += ' ucs2length(' + ($data) + ') '; + } + out += ' ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitLength') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be '; + if ($keyword == 'maxLength') { + out += 'longer'; + } else { + out += 'shorter'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' characters\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 35 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate__limitProperties(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxProperties' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' Object.keys(' + ($data) + ').length ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have '; + if ($keyword == 'maxProperties') { + out += 'more'; + } else { + out += 'less'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' properties\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 36 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_multipleOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + out += 'var division' + ($lvl) + ';if ('; + if ($isData) { + out += ' ' + ($schemaValue) + ' !== undefined && ( typeof ' + ($schemaValue) + ' != \'number\' || '; + } + out += ' (division' + ($lvl) + ' = ' + ($data) + ' / ' + ($schemaValue) + ', '; + if (it.opts.multipleOfPrecision) { + out += ' Math.abs(Math.round(division' + ($lvl) + ') - division' + ($lvl) + ') > 1e-' + (it.opts.multipleOfPrecision) + ' '; + } else { + out += ' division' + ($lvl) + ' !== parseInt(division' + ($lvl) + ') '; + } + out += ' ) '; + if ($isData) { + out += ' ) '; + } + out += ' ) { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('multipleOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { multipleOf: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be multiple of '; + if ($isData) { + out += '\' + ' + ($schemaValue); + } else { + out += '' + ($schemaValue) + '\''; + } + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 37 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_not(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + $it.level++; + var $nextValid = 'valid' + $it.level; + if (it.util.schemaHasRules($schema, it.RULES.all)) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' var ' + ($errs) + ' = errors; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.createErrors = false; + var $allErrorsOption; + if ($it.opts.allErrors) { + $allErrorsOption = $it.opts.allErrors; + $it.opts.allErrors = false; + } + out += ' ' + (it.validate($it)) + ' '; + $it.createErrors = true; + if ($allErrorsOption) $it.opts.allErrors = $allErrorsOption; + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' if (' + ($nextValid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be valid\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + if (it.opts.allErrors) { + out += ' } '; + } + } else { + out += ' var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be valid\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if ($breakOnError) { + out += ' if (false) { '; + } + } + return out; + } + + +/***/ }, +/* 38 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_oneOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + out += 'var ' + ($errs) + ' = errors;var prevValid' + ($lvl) + ' = false;var ' + ($valid) + ' = false;'; + var $currentBaseId = $it.baseId; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + } else { + out += ' var ' + ($nextValid) + ' = true; '; + } + if ($i) { + out += ' if (' + ($nextValid) + ' && prevValid' + ($lvl) + ') ' + ($valid) + ' = false; else { '; + $closingBraces += '}'; + } + out += ' if (' + ($nextValid) + ') ' + ($valid) + ' = prevValid' + ($lvl) + ' = true;'; + } + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += '' + ($closingBraces) + 'if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('oneOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should match exactly one schema in oneOf\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; return false; '; + } + } + out += '} else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; }'; + if (it.opts.allErrors) { + out += ' } '; + } + return out; + } + + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_pattern(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $regexp = $isData ? '(new RegExp(' + $schemaValue + '))' : it.usePattern($schema); + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || '; + } + out += ' !' + ($regexp) + '.test(' + ($data) + ') ) { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('pattern') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { pattern: '; + if ($isData) { + out += '' + ($schemaValue); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match pattern "'; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + (it.util.escapeQuotes($schema)); + } + out += '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; + } + + +/***/ }, +/* 40 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_properties(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $key = 'key' + $lvl, + $idx = 'idx' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $dataProperties = 'dataProperties' + $lvl; + var $schemaKeys = Object.keys($schema || {}), + $pProperties = it.schema.patternProperties || {}, + $pPropertyKeys = Object.keys($pProperties), + $aProperties = it.schema.additionalProperties, + $someProperties = $schemaKeys.length || $pPropertyKeys.length, + $noAdditional = $aProperties === false, + $additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length, + $removeAdditional = it.opts.removeAdditional, + $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional, + $ownProperties = it.opts.ownProperties, + $currentBaseId = it.baseId; + var $required = it.schema.required; + if ($required && !(it.opts.v5 && $required.$data) && $required.length < it.opts.loopRequired) var $requiredHash = it.util.toHash($required); + if (it.opts.patternGroups) { + var $pgProperties = it.schema.patternGroups || {}, + $pgPropertyKeys = Object.keys($pgProperties); + } + out += 'var ' + ($errs) + ' = errors;var ' + ($nextValid) + ' = true;'; + if ($ownProperties) { + out += ' var ' + ($dataProperties) + ' = undefined;'; + } + if ($checkAdditional) { + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + if ($someProperties) { + out += ' var isAdditional' + ($lvl) + ' = !(false '; + if ($schemaKeys.length) { + if ($schemaKeys.length > 5) { + out += ' || validate.schema' + ($schemaPath) + '[' + ($key) + '] '; + } else { + var arr1 = $schemaKeys; + if (arr1) { + var $propertyKey, i1 = -1, + l1 = arr1.length - 1; + while (i1 < l1) { + $propertyKey = arr1[i1 += 1]; + out += ' || ' + ($key) + ' == ' + (it.util.toQuotedString($propertyKey)) + ' '; + } + } + } + } + if ($pPropertyKeys.length) { + var arr2 = $pPropertyKeys; + if (arr2) { + var $pProperty, $i = -1, + l2 = arr2.length - 1; + while ($i < l2) { + $pProperty = arr2[$i += 1]; + out += ' || ' + (it.usePattern($pProperty)) + '.test(' + ($key) + ') '; + } + } + } + if (it.opts.patternGroups && $pgPropertyKeys.length) { + var arr3 = $pgPropertyKeys; + if (arr3) { + var $pgProperty, $i = -1, + l3 = arr3.length - 1; + while ($i < l3) { + $pgProperty = arr3[$i += 1]; + out += ' || ' + (it.usePattern($pgProperty)) + '.test(' + ($key) + ') '; + } + } + } + out += ' ); if (isAdditional' + ($lvl) + ') { '; + } + if ($removeAdditional == 'all') { + out += ' delete ' + ($data) + '[' + ($key) + ']; '; + } else { + var $currentErrorPath = it.errorPath; + var $additionalProperty = '\' + ' + $key + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + } + if ($noAdditional) { + if ($removeAdditional) { + out += ' delete ' + ($data) + '[' + ($key) + ']; '; + } else { + out += ' ' + ($nextValid) + ' = false; '; + var $currErrSchemaPath = $errSchemaPath; + $errSchemaPath = it.errSchemaPath + '/additionalProperties'; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('additionalProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { additionalProperty: \'' + ($additionalProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have additional properties\' '; + } + if (it.opts.verbose) { + out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + $errSchemaPath = $currErrSchemaPath; + if ($breakOnError) { + out += ' break; '; + } + } + } else if ($additionalIsSchema) { + if ($removeAdditional == 'failing') { + out += ' var ' + ($errs) + ' = errors; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.schema = $aProperties; + $it.schemaPath = it.schemaPath + '.additionalProperties'; + $it.errSchemaPath = it.errSchemaPath + '/additionalProperties'; + $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' if (!' + ($nextValid) + ') { errors = ' + ($errs) + '; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete ' + ($data) + '[' + ($key) + ']; } '; + it.compositeRule = $it.compositeRule = $wasComposite; + } else { + $it.schema = $aProperties; + $it.schemaPath = it.schemaPath + '.additionalProperties'; + $it.errSchemaPath = it.errSchemaPath + '/additionalProperties'; + $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + } + } + it.errorPath = $currentErrorPath; + } + if ($someProperties) { + out += ' } '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + var $useDefaults = it.opts.useDefaults && !it.compositeRule; + if ($schemaKeys.length) { + var arr4 = $schemaKeys; + if (arr4) { + var $propertyKey, i4 = -1, + l4 = arr4.length - 1; + while (i4 < l4) { + $propertyKey = arr4[i4 += 1]; + var $sch = $schema[$propertyKey]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + var $prop = it.util.getProperty($propertyKey), + $passData = $data + $prop, + $hasDefault = $useDefaults && $sch.default !== undefined; + $it.schema = $sch; + $it.schemaPath = $schemaPath + $prop; + $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($propertyKey); + $it.errorPath = it.util.getPath(it.errorPath, $propertyKey, it.opts.jsonPointers); + $it.dataPathArr[$dataNxt] = it.util.toQuotedString($propertyKey); + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + $code = it.util.varReplace($code, $nextData, $passData); + var $useData = $passData; + } else { + var $useData = $nextData; + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; '; + } + if ($hasDefault) { + out += ' ' + ($code) + ' '; + } else { + if ($requiredHash && $requiredHash[$propertyKey]) { + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { ' + ($nextValid) + ' = false; '; + var $currentErrorPath = it.errorPath, + $currErrSchemaPath = $errSchemaPath, + $missingProperty = it.util.escapeQuotes($propertyKey); + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + $errSchemaPath = it.errSchemaPath + '/required'; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + $errSchemaPath = $currErrSchemaPath; + it.errorPath = $currentErrorPath; + out += ' } else { '; + } else { + if ($breakOnError) { + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { ' + ($nextValid) + ' = true; } else { '; + } else { + out += ' if (' + ($useData) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ' ) { '; + } + } + out += ' ' + ($code) + ' } '; + } + } + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if ($pPropertyKeys.length) { + var arr5 = $pPropertyKeys; + if (arr5) { + var $pProperty, i5 = -1, + l5 = arr5.length - 1; + while (i5 < l5) { + $pProperty = arr5[i5 += 1]; + var $sch = $pProperties[$pProperty]; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + $it.schema = $sch; + $it.schemaPath = it.schemaPath + '.patternProperties' + it.util.getProperty($pProperty); + $it.errSchemaPath = it.errSchemaPath + '/patternProperties/' + it.util.escapeFragment($pProperty); + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + out += ' if (' + (it.usePattern($pProperty)) + '.test(' + ($key) + ')) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else ' + ($nextValid) + ' = true; '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + } + if (it.opts.patternGroups && $pgPropertyKeys.length) { + var arr6 = $pgPropertyKeys; + if (arr6) { + var $pgProperty, i6 = -1, + l6 = arr6.length - 1; + while (i6 < l6) { + $pgProperty = arr6[i6 += 1]; + var $pgSchema = $pgProperties[$pgProperty], + $sch = $pgSchema.schema; + if (it.util.schemaHasRules($sch, it.RULES.all)) { + $it.schema = $sch; + $it.schemaPath = it.schemaPath + '.patternGroups' + it.util.getProperty($pgProperty) + '.schema'; + $it.errSchemaPath = it.errSchemaPath + '/patternGroups/' + it.util.escapeFragment($pgProperty) + '/schema'; + out += ' var pgPropCount' + ($lvl) + ' = 0; '; + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + out += ' if (' + (it.usePattern($pgProperty)) + '.test(' + ($key) + ')) { pgPropCount' + ($lvl) + '++; '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else ' + ($nextValid) + ' = true; '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + var $pgMin = $pgSchema.minimum, + $pgMax = $pgSchema.maximum; + if ($pgMin !== undefined || $pgMax !== undefined) { + out += ' var ' + ($valid) + ' = true; '; + var $currErrSchemaPath = $errSchemaPath; + if ($pgMin !== undefined) { + var $limit = $pgMin, + $reason = 'minimum', + $moreOrLess = 'less'; + out += ' ' + ($valid) + ' = pgPropCount' + ($lvl) + ' >= ' + ($pgMin) + '; '; + $errSchemaPath = it.errSchemaPath + '/patternGroups/minimum'; + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('patternGroups') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { reason: \'' + ($reason) + '\', limit: ' + ($limit) + ', pattern: \'' + (it.util.escapeQuotes($pgProperty)) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have ' + ($moreOrLess) + ' than ' + ($limit) + ' properties matching pattern "' + (it.util.escapeQuotes($pgProperty)) + '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($pgMax !== undefined) { + out += ' else '; + } + } + if ($pgMax !== undefined) { + var $limit = $pgMax, + $reason = 'maximum', + $moreOrLess = 'more'; + out += ' ' + ($valid) + ' = pgPropCount' + ($lvl) + ' <= ' + ($pgMax) + '; '; + $errSchemaPath = it.errSchemaPath + '/patternGroups/maximum'; + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('patternGroups') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { reason: \'' + ($reason) + '\', limit: ' + ($limit) + ', pattern: \'' + (it.util.escapeQuotes($pgProperty)) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have ' + ($moreOrLess) + ' than ' + ($limit) + ' properties matching pattern "' + (it.util.escapeQuotes($pgProperty)) + '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + } + $errSchemaPath = $currErrSchemaPath; + if ($breakOnError) { + out += ' if (' + ($valid) + ') { '; + $closingBraces += '}'; + } + } + } + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; + } + + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_propertyNames(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + if (it.util.schemaHasRules($schema, it.RULES.all)) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + var $key = 'key' + $lvl, + $idx = 'idx' + $lvl, + $i = 'i' + $lvl, + $invalidName = '\' + ' + $key + ' + \'', + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $dataProperties = 'dataProperties' + $lvl, + $ownProperties = it.opts.ownProperties, + $currentBaseId = it.baseId; + out += ' var ' + ($errs) + ' = errors; '; + if ($ownProperties) { + out += ' var ' + ($dataProperties) + ' = undefined; '; + } + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + out += ' var startErrs' + ($lvl) + ' = errors; '; + var $passData = $key; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' if (!' + ($nextValid) + ') { for (var ' + ($i) + '=startErrs' + ($lvl) + '; ' + ($i) + '= it.opts.loopRequired, + $ownProperties = it.opts.ownProperties; + if ($breakOnError) { + out += ' var missing' + ($lvl) + '; '; + if ($loopRequired) { + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; '; + } + var $i = 'i' + $lvl, + $propertyPath = 'schema' + $lvl + '[' + $i + ']', + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers); + } + out += ' var ' + ($valid) + ' = true; '; + if ($isData) { + out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {'; + } + out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { ' + ($valid) + ' = ' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) '; + } + out += '; if (!' + ($valid) + ') break; } '; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + } else { + out += ' if ( '; + var arr2 = $required; + if (arr2) { + var $propertyKey, $i = -1, + l2 = arr2.length - 1; + while ($i < l2) { + $propertyKey = arr2[$i += 1]; + if ($i) { + out += ' || '; + } + var $prop = it.util.getProperty($propertyKey), + $useData = $data + $prop; + out += ' ( ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) '; + } + } + out += ') { '; + var $propertyPath = 'missing' + $lvl, + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + } + } else { + if ($loopRequired) { + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; '; + } + var $i = 'i' + $lvl, + $propertyPath = 'schema' + $lvl + '[' + $i + ']', + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers); + } + if ($isData) { + out += ' if (' + ($vSchema) + ' && !Array.isArray(' + ($vSchema) + ')) { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if (' + ($vSchema) + ' !== undefined) { '; + } + out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { if (' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } '; + if ($isData) { + out += ' } '; + } + } else { + var arr3 = $required; + if (arr3) { + var $propertyKey, i3 = -1, + l3 = arr3.length - 1; + while (i3 < l3) { + $propertyKey = arr3[i3 += 1]; + var $prop = it.util.getProperty($propertyKey), + $missingProperty = it.util.escapeQuotes($propertyKey), + $useData = $data + $prop; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '; + } + } + } + } + it.errorPath = $currentErrorPath; + } else if ($breakOnError) { + out += ' if (true) {'; + } + return out; + } + + +/***/ }, +/* 43 */ +/***/ function(module, exports) { + + 'use strict'; + module.exports = function generate_uniqueItems(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + if (($schema || $isData) && it.opts.uniqueItems !== false) { + if ($isData) { + out += ' var ' + ($valid) + '; if (' + ($schemaValue) + ' === false || ' + ($schemaValue) + ' === undefined) ' + ($valid) + ' = true; else if (typeof ' + ($schemaValue) + ' != \'boolean\') ' + ($valid) + ' = false; else { '; + } + out += ' var ' + ($valid) + ' = true; if (' + ($data) + '.length > 1) { var i = ' + ($data) + '.length, j; outer: for (;i--;) { for (j = i; j--;) { if (equal(' + ($data) + '[i], ' + ($data) + '[j])) { ' + ($valid) + ' = false; break outer; } } } } '; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('uniqueItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { i: i, j: j } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have duplicate items (items ## \' + j + \' and \' + i + \' are identical)\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + } else { + if ($breakOnError) { + out += ' if (true) { '; + } + } + return out; + } + + +/***/ }, +/* 44 */ +/***/ function(module, exports) { + + 'use strict'; + + var KEYWORDS = [ + 'multipleOf', + 'maximum', + 'exclusiveMaximum', + 'minimum', + 'exclusiveMinimum', + 'maxLength', + 'minLength', + 'pattern', + 'additionalItems', + 'maxItems', + 'minItems', + 'uniqueItems', + 'maxProperties', + 'minProperties', + 'required', + 'additionalProperties', + 'enum', + 'format', + 'const' + ]; + + module.exports = function (metaSchema, keywordsJsonPointers) { + for (var i=0; i 0) { + this.autoScrollStep = ((top + margin) - mouseY) / 3; + } + else if (mouseY > bottom - margin && + height + content.scrollTop < content.scrollHeight) { + this.autoScrollStep = ((bottom - margin) - mouseY) / 3; + } + else { + this.autoScrollStep = undefined; + } + + if (this.autoScrollStep) { + if (!this.autoScrollTimer) { + this.autoScrollTimer = setInterval(function () { + if (me.autoScrollStep) { + content.scrollTop -= me.autoScrollStep; + } + else { + me.stopAutoScroll(); + } + }, interval); + } + } + else { + this.stopAutoScroll(); + } + }; + + /** + * Stop auto scrolling. Only applicable when scrolling + */ + treemode.stopAutoScroll = function () { + if (this.autoScrollTimer) { + clearTimeout(this.autoScrollTimer); + delete this.autoScrollTimer; + } + if (this.autoScrollStep) { + delete this.autoScrollStep; + } + }; + + + /** + * Set the focus to an element in the editor, set text selection, and + * set scroll position. + * @param {Object} selection An object containing fields: + * {Element | undefined} dom The dom element + * which has focus + * {Range | TextRange} range A text selection + * {Node[]} nodes Nodes in case of multi selection + * {Number} scrollTop Scroll position + */ + treemode.setSelection = function (selection) { + if (!selection) { + return; + } + + if ('scrollTop' in selection && this.content) { + // TODO: animated scroll + this.content.scrollTop = selection.scrollTop; + } + if (selection.nodes) { + // multi-select + this.select(selection.nodes); + } + if (selection.range) { + util.setSelectionOffset(selection.range); + } + if (selection.dom) { + selection.dom.focus(); + } + }; + + /** + * Get the current focus + * @return {Object} selection An object containing fields: + * {Element | undefined} dom The dom element + * which has focus + * {Range | TextRange} range A text selection + * {Node[]} nodes Nodes in case of multi selection + * {Number} scrollTop Scroll position + */ + treemode.getSelection = function () { + var range = util.getSelectionOffset(); + if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs) + range = null; + } + + return { + dom: this.focusTarget, + range: range, + nodes: this.multiselection.nodes.slice(0), + scrollTop: this.content ? this.content.scrollTop : 0 + }; + }; + + /** + * Adjust the scroll position such that given top position is shown at 1/4 + * of the window height. + * @param {Number} top + * @param {function(boolean)} [callback] Callback, executed when animation is + * finished. The callback returns true + * when animation is finished, or false + * when not. + */ + treemode.scrollTo = function (top, callback) { + var content = this.content; + if (content) { + var editor = this; + // cancel any running animation + if (editor.animateTimeout) { + clearTimeout(editor.animateTimeout); + delete editor.animateTimeout; + } + if (editor.animateCallback) { + editor.animateCallback(false); + delete editor.animateCallback; + } + + // calculate final scroll position + var height = content.clientHeight; + var bottom = content.scrollHeight - height; + var finalScrollTop = Math.min(Math.max(top - height / 4, 0), bottom); + + // animate towards the new scroll position + var animate = function () { + var scrollTop = content.scrollTop; + var diff = (finalScrollTop - scrollTop); + if (Math.abs(diff) > 3) { + content.scrollTop += diff / 3; + editor.animateCallback = callback; + editor.animateTimeout = setTimeout(animate, 50); + } + else { + // finished + if (callback) { + callback(true); + } + content.scrollTop = finalScrollTop; + delete editor.animateTimeout; + delete editor.animateCallback; + } + }; + animate(); + } + else { + if (callback) { + callback(false); + } + } + }; + + /** + * Create main frame + * @private + */ + treemode._createFrame = function () { + // create the frame + this.frame = document.createElement('div'); + this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode; + this.container.appendChild(this.frame); + + // create one global event listener to handle all events from all nodes + var editor = this; + function onEvent(event) { + // when switching to mode "code" or "text" via the menu, some events + // are still fired whilst the _onEvent methods is already removed. + if (editor._onEvent) { + editor._onEvent(event); + } + } + this.frame.onclick = function (event) { + var target = event.target;// || event.srcElement; + + onEvent(event); + + // prevent default submit action of buttons when editor is located + // inside a form + if (target.nodeName == 'BUTTON') { + event.preventDefault(); + } + }; + this.frame.oninput = onEvent; + this.frame.onchange = onEvent; + this.frame.onkeydown = onEvent; + this.frame.onkeyup = onEvent; + this.frame.oncut = onEvent; + this.frame.onpaste = onEvent; + this.frame.onmousedown = onEvent; + this.frame.onmouseup = onEvent; + this.frame.onmouseover = onEvent; + this.frame.onmouseout = onEvent; + // Note: focus and blur events do not propagate, therefore they defined + // using an eventListener with useCapture=true + // see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html + util.addEventListener(this.frame, 'focus', onEvent, true); + util.addEventListener(this.frame, 'blur', onEvent, true); + this.frame.onfocusin = onEvent; // for IE + this.frame.onfocusout = onEvent; // for IE + + // create menu + this.menu = document.createElement('div'); + this.menu.className = 'jsoneditor-menu'; + this.frame.appendChild(this.menu); + + // create expand all button + var expandAll = document.createElement('button'); + expandAll.type = 'button'; + expandAll.className = 'jsoneditor-expand-all'; + expandAll.title = 'Expand all fields'; + expandAll.onclick = function () { + editor.expandAll(); + }; + this.menu.appendChild(expandAll); + + // create collapse all button + var collapseAll = document.createElement('button'); + collapseAll.type = 'button'; + collapseAll.title = 'Collapse all fields'; + collapseAll.className = 'jsoneditor-collapse-all'; + collapseAll.onclick = function () { + editor.collapseAll(); + }; + this.menu.appendChild(collapseAll); + + // create undo/redo buttons + if (this.history) { + // create undo button + var undo = document.createElement('button'); + undo.type = 'button'; + undo.className = 'jsoneditor-undo jsoneditor-separator'; + undo.title = 'Undo last action (Ctrl+Z)'; + undo.onclick = function () { + editor._onUndo(); + }; + this.menu.appendChild(undo); + this.dom.undo = undo; + + // create redo button + var redo = document.createElement('button'); + redo.type = 'button'; + redo.className = 'jsoneditor-redo'; + redo.title = 'Redo (Ctrl+Shift+Z)'; + redo.onclick = function () { + editor._onRedo(); + }; + this.menu.appendChild(redo); + this.dom.redo = redo; + + // register handler for onchange of history + this.history.onChange = function () { + undo.disabled = !editor.history.canUndo(); + redo.disabled = !editor.history.canRedo(); + }; + this.history.onChange(); + } + + // create mode box + if (this.options && this.options.modes && this.options.modes.length) { + var me = this; + this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) { + me.modeSwitcher.destroy(); + + // switch mode and restore focus + me.setMode(mode); + me.modeSwitcher.focus(); + }); + } + + // create search box + if (this.options.search) { + this.searchBox = new SearchBox(this, this.menu); + } + + if(this.options.navigationBar) { + // create second menu row for treepath + this.navBar = document.createElement('div'); + this.navBar.className = 'jsoneditor-navigation-bar nav-bar-empty'; + this.frame.appendChild(this.navBar); + + this.treePath = new TreePath(this.navBar); + this.treePath.onSectionSelected(this._onTreePathSectionSelected.bind(this)); + this.treePath.onContextMenuItemSelected(this._onTreePathMenuItemSelected.bind(this)); + } + }; + + /** + * Perform an undo action + * @private + */ + treemode._onUndo = function () { + if (this.history) { + // undo last action + this.history.undo(); + + // fire change event + this._onChange(); + } + }; + + /** + * Perform a redo action + * @private + */ + treemode._onRedo = function () { + if (this.history) { + // redo last action + this.history.redo(); + + // fire change event + this._onChange(); + } + }; + + /** + * Event handler + * @param event + * @private + */ + treemode._onEvent = function (event) { + if (event.type == 'keydown') { + this._onKeyDown(event); + } + + if (event.type == 'focus') { + this.focusTarget = event.target; + } + + if (event.type == 'mousedown') { + this._startDragDistance(event); + } + if (event.type == 'mousemove' || event.type == 'mouseup' || event.type == 'click') { + this._updateDragDistance(event); + } + + var node = Node.getNodeFromTarget(event.target); + + if (this.options && this.options.navigationBar && node && (event.type == 'keydown' || event.type == 'mousedown')) { + this._updateTreePath(node.getNodePath()); + } + + if (node && node.selected) { + if (event.type == 'click') { + if (event.target == node.dom.menu) { + this.showContextMenu(event.target); + + // stop propagation (else we will open the context menu of a single node) + return; + } + + // deselect a multi selection + if (!event.hasMoved) { + this.deselect(); + } + } + + if (event.type == 'mousedown') { + // drag multiple nodes + Node.onDragStart(this.multiselection.nodes, event); + } + } + else { + if (event.type == 'mousedown') { + this.deselect(); + + if (node && event.target == node.dom.drag) { + // drag a singe node + Node.onDragStart(node, event); + } + else if (!node || (event.target != node.dom.field && event.target != node.dom.value && event.target != node.dom.select)) { + // select multiple nodes + this._onMultiSelectStart(event); + } + } + } + + if (node) { + node.onEvent(event); + } + }; + + /** + * Update TreePath components + * @param {Array} pathNodes list of nodes in path from root to selection + * @private + */ + treemode._updateTreePath = function (pathNodes) { + if (pathNodes && pathNodes.length) { + util.removeClassName(this.navBar, 'nav-bar-empty'); + + var pathObjs = []; + pathNodes.forEach(function (node) { + var pathObj = { + name: getName(node), + node: node, + children: [] + } + if (node.childs && node.childs.length) { + node.childs.forEach(function (childNode) { + pathObj.children.push({ + name: getName(childNode), + node: childNode + }); + }); + } + pathObjs.push(pathObj); + }); + this.treePath.setPath(pathObjs); + } else { + util.addClassName(this.navBar, 'nav-bar-empty'); + } + + function getName(node) { + return node.field || (isNaN(node.index) ? node.type : node.index); + } + }; + + /** + * Callback for tree path section selection - focus the selected node in the tree + * @param {Object} pathObj path object that was represents the selected section node + * @private + */ + treemode._onTreePathSectionSelected = function (pathObj) { + if(pathObj && pathObj.node) { + pathObj.node.expandTo(); + pathObj.node.focus(); + } + }; + + /** + * Callback for tree path menu item selection - rebuild the path accrding to the new selection and focus the selected node in the tree + * @param {Object} pathObj path object that was represents the parent section node + * @param {String} selection selected section child + * @private + */ + treemode._onTreePathMenuItemSelected = function (pathObj, selection) { + if(pathObj && pathObj.children.length) { + var selectionObj = pathObj.children.find(function (obj) { + return obj.name === selection; + }); + if(selectionObj && selectionObj.node) { + this._updateTreePath(selectionObj.node.getNodePath()); + selectionObj.node.expandTo(); + selectionObj.node.focus(); + } + } + }; + + treemode._startDragDistance = function (event) { + this.dragDistanceEvent = { + initialTarget: event.target, + initialPageX: event.pageX, + initialPageY: event.pageY, + dragDistance: 0, + hasMoved: false + }; + }; + + treemode._updateDragDistance = function (event) { + if (!this.dragDistanceEvent) { + this._startDragDistance(event); + } + + var diffX = event.pageX - this.dragDistanceEvent.initialPageX; + var diffY = event.pageY - this.dragDistanceEvent.initialPageY; + + this.dragDistanceEvent.dragDistance = Math.sqrt(diffX * diffX + diffY * diffY); + this.dragDistanceEvent.hasMoved = + this.dragDistanceEvent.hasMoved || this.dragDistanceEvent.dragDistance > 10; + + event.dragDistance = this.dragDistanceEvent.dragDistance; + event.hasMoved = this.dragDistanceEvent.hasMoved; + + return event.dragDistance; + }; + + /** + * Start multi selection of nodes by dragging the mouse + * @param event + * @private + */ + treemode._onMultiSelectStart = function (event) { + var node = Node.getNodeFromTarget(event.target); + + if (this.options.mode !== 'tree' || this.options.onEditable !== undefined) { + // dragging not allowed in modes 'view' and 'form' + // TODO: allow multiselection of items when option onEditable is specified + return; + } + + this.multiselection = { + start: node || null, + end: null, + nodes: [] + }; + + this._startDragDistance(event); + + var editor = this; + if (!this.mousemove) { + this.mousemove = util.addEventListener(window, 'mousemove', function (event) { + editor._onMultiSelect(event); + }); + } + if (!this.mouseup) { + this.mouseup = util.addEventListener(window, 'mouseup', function (event ) { + editor._onMultiSelectEnd(event); + }); + } + + }; + + /** + * Multiselect nodes by dragging + * @param event + * @private + */ + treemode._onMultiSelect = function (event) { + event.preventDefault(); + + this._updateDragDistance(event); + if (!event.hasMoved) { + return; + } + + var node = Node.getNodeFromTarget(event.target); + + if (node) { + if (this.multiselection.start == null) { + this.multiselection.start = node; + } + this.multiselection.end = node; + } + + // deselect previous selection + this.deselect(); + + // find the selected nodes in the range from first to last + var start = this.multiselection.start; + var end = this.multiselection.end || this.multiselection.start; + if (start && end) { + // find the top level childs, all having the same parent + this.multiselection.nodes = this._findTopLevelNodes(start, end); + this.select(this.multiselection.nodes); + } + }; + + /** + * End of multiselect nodes by dragging + * @param event + * @private + */ + treemode._onMultiSelectEnd = function (event) { + // set focus to the context menu button of the first node + if (this.multiselection.nodes[0]) { + this.multiselection.nodes[0].dom.menu.focus(); + } + + this.multiselection.start = null; + this.multiselection.end = null; + + // cleanup global event listeners + if (this.mousemove) { + util.removeEventListener(window, 'mousemove', this.mousemove); + delete this.mousemove; + } + if (this.mouseup) { + util.removeEventListener(window, 'mouseup', this.mouseup); + delete this.mouseup; + } + }; + + /** + * deselect currently selected nodes + * @param {boolean} [clearStartAndEnd=false] If true, the `start` and `end` + * state is cleared too. + */ + treemode.deselect = function (clearStartAndEnd) { + this.multiselection.nodes.forEach(function (node) { + node.setSelected(false); + }); + this.multiselection.nodes = []; + + if (clearStartAndEnd) { + this.multiselection.start = null; + this.multiselection.end = null; + } + }; + + /** + * select nodes + * @param {Node[] | Node} nodes + */ + treemode.select = function (nodes) { + if (!Array.isArray(nodes)) { + return this.select([nodes]); + } + + if (nodes) { + this.deselect(); + + this.multiselection.nodes = nodes.slice(0); + + var first = nodes[0]; + nodes.forEach(function (node) { + node.setSelected(true, node === first); + }); + } + }; + + /** + * From two arbitrary selected nodes, find their shared parent node. + * From that parent node, select the two child nodes in the brances going to + * nodes `start` and `end`, and select all childs in between. + * @param {Node} start + * @param {Node} end + * @return {Array.} Returns an ordered list with child nodes + * @private + */ + treemode._findTopLevelNodes = function (start, end) { + var startPath = start.getNodePath(); + var endPath = end.getNodePath(); + var i = 0; + while (i < startPath.length && startPath[i] === endPath[i]) { + i++; + } + var root = startPath[i - 1]; + var startChild = startPath[i]; + var endChild = endPath[i]; + + if (!startChild || !endChild) { + if (root.parent) { + // startChild is a parent of endChild or vice versa + startChild = root; + endChild = root; + root = root.parent + } + else { + // we have selected the root node (which doesn't have a parent) + startChild = root.childs[0]; + endChild = root.childs[root.childs.length - 1]; + } + } + + if (root && startChild && endChild) { + var startIndex = root.childs.indexOf(startChild); + var endIndex = root.childs.indexOf(endChild); + var firstIndex = Math.min(startIndex, endIndex); + var lastIndex = Math.max(startIndex, endIndex); + + return root.childs.slice(firstIndex, lastIndex + 1); + } + else { + return []; + } + }; + + /** + * Event handler for keydown. Handles shortcut keys + * @param {Event} event + * @private + */ + treemode._onKeyDown = function (event) { + var keynum = event.which || event.keyCode; + var altKey = event.altKey; + var ctrlKey = event.ctrlKey; + var metaKey = event.metaKey; + var shiftKey = event.shiftKey; + var handled = false; + + if (keynum == 9) { // Tab or Shift+Tab + var me = this; + setTimeout(function () { + // select all text when moving focus to an editable div + util.selectContentEditable(me.focusTarget); + }, 0); + } + + if (this.searchBox) { + if (ctrlKey && keynum == 70) { // Ctrl+F + this.searchBox.dom.search.focus(); + this.searchBox.dom.search.select(); + handled = true; + } + else if (keynum == 114 || (ctrlKey && keynum == 71)) { // F3 or Ctrl+G + var focus = true; + if (!shiftKey) { + // select next search result (F3 or Ctrl+G) + this.searchBox.next(focus); + } + else { + // select previous search result (Shift+F3 or Ctrl+Shift+G) + this.searchBox.previous(focus); + } + + handled = true; + } + } + + if (this.history) { + if (ctrlKey && !shiftKey && keynum == 90) { // Ctrl+Z + // undo + this._onUndo(); + handled = true; + } + else if (ctrlKey && shiftKey && keynum == 90) { // Ctrl+Shift+Z + // redo + this._onRedo(); + handled = true; + } + } + + if ((this.options.autocomplete) && (!handled)) { + if (!ctrlKey && !altKey && !metaKey && (event.key.length == 1 || keynum == 8 || keynum == 46)) { + handled = false; + var jsonElementType = ""; + if (event.target.className.indexOf("jsoneditor-value") >= 0) jsonElementType = "value"; + if (event.target.className.indexOf("jsoneditor-field") >= 0) jsonElementType = "field"; + + var node = Node.getNodeFromTarget(event.target); + // Activate autocomplete + setTimeout(function (hnode, element) { + if (element.innerText.length > 0) { + var result = this.options.autocomplete.getOptions(element.innerText, hnode.getPath(), jsonElementType, hnode.editor); + if (typeof result.then === 'function') { + // probably a promise + if (result.then(function (obj) { + if (obj.options) + this.autocomplete.show(element, obj.startFrom, obj.options); + else + this.autocomplete.show(element, 0, obj); + }.bind(this))); + } else { + // definitely not a promise + if (result.options) + this.autocomplete.show(element, result.startFrom, result.options); + else + this.autocomplete.show(element, 0, result); + } + } + else + this.autocomplete.hideDropDown(); + + }.bind(this, node, event.target), 50); + } + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }; + + /** + * Create main table + * @private + */ + treemode._createTable = function () { + var contentOuter = document.createElement('div'); + contentOuter.className = 'jsoneditor-outer'; + if(this.options.navigationBar) { + util.addClassName(contentOuter, 'has-nav-bar'); + } + this.contentOuter = contentOuter; + + this.content = document.createElement('div'); + this.content.className = 'jsoneditor-tree'; + contentOuter.appendChild(this.content); + + this.table = document.createElement('table'); + this.table.className = 'jsoneditor-tree'; + this.content.appendChild(this.table); + + // create colgroup where the first two columns don't have a fixed + // width, and the edit columns do have a fixed width + var col; + this.colgroupContent = document.createElement('colgroup'); + if (this.options.mode === 'tree') { + col = document.createElement('col'); + col.width = "24px"; + this.colgroupContent.appendChild(col); + } + col = document.createElement('col'); + col.width = "24px"; + this.colgroupContent.appendChild(col); + col = document.createElement('col'); + this.colgroupContent.appendChild(col); + this.table.appendChild(this.colgroupContent); + + this.tbody = document.createElement('tbody'); + this.table.appendChild(this.tbody); + + this.frame.appendChild(contentOuter); + }; + + /** + * Show a contextmenu for this node. + * Used for multiselection + * @param {HTMLElement} anchor Anchor element to attach the context menu to. + * @param {function} [onClose] Callback method called when the context menu + * is being closed. + */ + treemode.showContextMenu = function (anchor, onClose) { + var items = []; + var editor = this; + + // create duplicate button + items.push({ + text: 'Duplicate', + title: 'Duplicate selected fields (Ctrl+D)', + className: 'jsoneditor-duplicate', + click: function () { + Node.onDuplicate(editor.multiselection.nodes); + } + }); + + // create remove button + items.push({ + text: 'Remove', + title: 'Remove selected fields (Ctrl+Del)', + className: 'jsoneditor-remove', + click: function () { + Node.onRemove(editor.multiselection.nodes); + } + }); + + var menu = new ContextMenu(items, {close: onClose}); + menu.show(anchor, this.content); + }; + + + // define modes + module.exports = [ + { + mode: 'tree', + mixin: treemode, + data: 'json' + }, + { + mode: 'view', + mixin: treemode, + data: 'json' + }, + { + mode: 'form', + mixin: treemode, + data: 'json' + } + ]; + + +/***/ }, +/* 52 */ +/***/ function(module, exports) { + + 'use strict'; + + /** + * The highlighter can highlight/unhighlight a node, and + * animate the visibility of a context menu. + * @constructor Highlighter + */ + function Highlighter () { + this.locked = false; + } + + /** + * Hightlight given node and its childs + * @param {Node} node + */ + Highlighter.prototype.highlight = function (node) { + if (this.locked) { + return; + } + + if (this.node != node) { + // unhighlight current node + if (this.node) { + this.node.setHighlight(false); + } + + // highlight new node + this.node = node; + this.node.setHighlight(true); + } + + // cancel any current timeout + this._cancelUnhighlight(); + }; + + /** + * Unhighlight currently highlighted node. + * Will be done after a delay + */ + Highlighter.prototype.unhighlight = function () { + if (this.locked) { + return; + } + + var me = this; + if (this.node) { + this._cancelUnhighlight(); + + // do the unhighlighting after a small delay, to prevent re-highlighting + // the same node when moving from the drag-icon to the contextmenu-icon + // or vice versa. + this.unhighlightTimer = setTimeout(function () { + me.node.setHighlight(false); + me.node = undefined; + me.unhighlightTimer = undefined; + }, 0); + } + }; + + /** + * Cancel an unhighlight action (if before the timeout of the unhighlight action) + * @private + */ + Highlighter.prototype._cancelUnhighlight = function () { + if (this.unhighlightTimer) { + clearTimeout(this.unhighlightTimer); + this.unhighlightTimer = undefined; + } + }; + + /** + * Lock highlighting or unhighlighting nodes. + * methods highlight and unhighlight do not work while locked. + */ + Highlighter.prototype.lock = function () { + this.locked = true; + }; + + /** + * Unlock highlighting or unhighlighting nodes + */ + Highlighter.prototype.unlock = function () { + this.locked = false; + }; + + module.exports = Highlighter; + + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(54); + + /** + * @constructor History + * Store action history, enables undo and redo + * @param {JSONEditor} editor + */ + function History (editor) { + this.editor = editor; + this.history = []; + this.index = -1; + + this.clear(); + + // map with all supported actions + this.actions = { + 'editField': { + 'undo': function (params) { + params.node.updateField(params.oldValue); + }, + 'redo': function (params) { + params.node.updateField(params.newValue); + } + }, + 'editValue': { + 'undo': function (params) { + params.node.updateValue(params.oldValue); + }, + 'redo': function (params) { + params.node.updateValue(params.newValue); + } + }, + 'changeType': { + 'undo': function (params) { + params.node.changeType(params.oldType); + }, + 'redo': function (params) { + params.node.changeType(params.newType); + } + }, + + 'appendNodes': { + 'undo': function (params) { + params.nodes.forEach(function (node) { + params.parent.removeChild(node); + }); + }, + 'redo': function (params) { + params.nodes.forEach(function (node) { + params.parent.appendChild(node); + }); + } + }, + 'insertBeforeNodes': { + 'undo': function (params) { + params.nodes.forEach(function (node) { + params.parent.removeChild(node); + }); + }, + 'redo': function (params) { + params.nodes.forEach(function (node) { + params.parent.insertBefore(node, params.beforeNode); + }); + } + }, + 'insertAfterNodes': { + 'undo': function (params) { + params.nodes.forEach(function (node) { + params.parent.removeChild(node); + }); + }, + 'redo': function (params) { + var afterNode = params.afterNode; + params.nodes.forEach(function (node) { + params.parent.insertAfter(params.node, afterNode); + afterNode = node; + }); + } + }, + 'removeNodes': { + 'undo': function (params) { + var parent = params.parent; + var beforeNode = parent.childs[params.index] || parent.append; + params.nodes.forEach(function (node) { + parent.insertBefore(node, beforeNode); + }); + }, + 'redo': function (params) { + params.nodes.forEach(function (node) { + params.parent.removeChild(node); + }); + } + }, + 'duplicateNodes': { + 'undo': function (params) { + params.nodes.forEach(function (node) { + params.parent.removeChild(node); + }); + }, + 'redo': function (params) { + var afterNode = params.afterNode; + params.nodes.forEach(function (node) { + params.parent.insertAfter(node, afterNode); + afterNode = node; + }); + } + }, + 'moveNodes': { + 'undo': function (params) { + params.nodes.forEach(function (node) { + params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode); + }); + }, + 'redo': function (params) { + params.nodes.forEach(function (node) { + params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode); + }); + } + }, + + 'sort': { + 'undo': function (params) { + var node = params.node; + node.hideChilds(); + node.sort = params.oldSort; + node.childs = params.oldChilds; + node.showChilds(); + }, + 'redo': function (params) { + var node = params.node; + node.hideChilds(); + node.sort = params.newSort; + node.childs = params.newChilds; + node.showChilds(); + } + } + + // TODO: restore the original caret position and selection with each undo + // TODO: implement history for actions "expand", "collapse", "scroll", "setDocument" + }; + } + + /** + * The method onChange is executed when the History is changed, and can + * be overloaded. + */ + History.prototype.onChange = function () {}; + + /** + * Add a new action to the history + * @param {String} action The executed action. Available actions: "editField", + * "editValue", "changeType", "appendNode", + * "removeNode", "duplicateNode", "moveNode" + * @param {Object} params Object containing parameters describing the change. + * The parameters in params depend on the action (for + * example for "editValue" the Node, old value, and new + * value are provided). params contains all information + * needed to undo or redo the action. + */ + History.prototype.add = function (action, params) { + this.index++; + this.history[this.index] = { + 'action': action, + 'params': params, + 'timestamp': new Date() + }; + + // remove redo actions which are invalid now + if (this.index < this.history.length - 1) { + this.history.splice(this.index + 1, this.history.length - this.index - 1); + } + + // fire onchange event + this.onChange(); + }; + + /** + * Clear history + */ + History.prototype.clear = function () { + this.history = []; + this.index = -1; + + // fire onchange event + this.onChange(); + }; + + /** + * Check if there is an action available for undo + * @return {Boolean} canUndo + */ + History.prototype.canUndo = function () { + return (this.index >= 0); + }; + + /** + * Check if there is an action available for redo + * @return {Boolean} canRedo + */ + History.prototype.canRedo = function () { + return (this.index < this.history.length - 1); + }; + + /** + * Undo the last action + */ + History.prototype.undo = function () { + if (this.canUndo()) { + var obj = this.history[this.index]; + if (obj) { + var action = this.actions[obj.action]; + if (action && action.undo) { + action.undo(obj.params); + if (obj.params.oldSelection) { + this.editor.setSelection(obj.params.oldSelection); + } + } + else { + console.error(new Error('unknown action "' + obj.action + '"')); + } + } + this.index--; + + // fire onchange event + this.onChange(); + } + }; + + /** + * Redo the last action + */ + History.prototype.redo = function () { + if (this.canRedo()) { + this.index++; + + var obj = this.history[this.index]; + if (obj) { + var action = this.actions[obj.action]; + if (action && action.redo) { + action.redo(obj.params); + if (obj.params.newSelection) { + this.editor.setSelection(obj.params.newSelection); + } + } + else { + console.error(new Error('unknown action "' + obj.action + '"')); + } + } + + // fire onchange event + this.onChange(); + } + }; + + /** + * Destroy history + */ + History.prototype.destroy = function () { + this.editor = null; + + this.history = []; + this.index = -1; + }; + + module.exports = History; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var jsonlint = __webpack_require__(55); + + /** + * Parse JSON using the parser built-in in the browser. + * On exception, the jsonString is validated and a detailed error is thrown. + * @param {String} jsonString + * @return {JSON} json + */ + exports.parse = function parse(jsonString) { + try { + return JSON.parse(jsonString); + } + catch (err) { + // try to throw a more detailed error message using validate + exports.validate(jsonString); + + // rethrow the original error + throw err; + } + }; + + /** + * Sanitize a JSON-like string containing. For example changes JavaScript + * notation into JSON notation. + * This function for example changes a string like "{a: 2, 'b': {c: 'd'}" + * into '{"a": 2, "b": {"c": "d"}' + * @param {string} jsString + * @returns {string} json + */ + exports.sanitize = function (jsString) { + // escape all single and double quotes inside strings + var chars = []; + var i = 0; + + //If JSON starts with a function (characters/digits/"_-"), remove this function. + //This is useful for "stripping" JSONP objects to become JSON + //For example: /* some comment */ function_12321321 ( [{"a":"b"}] ); => [{"a":"b"}] + var match = jsString.match(/^\s*(\/\*(.|[\r\n])*?\*\/)?\s*[\da-zA-Z_$]+\s*\(([\s\S]*)\)\s*;?\s*$/); + if (match) { + jsString = match[3]; + } + + var controlChars = { + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t' + }; + + var quote = '\''; + var quoteDbl = '"'; + var quoteLeft = '\u2018'; + var quoteRight = '\u2019'; + var quoteDblLeft = '\u201C'; + var quoteDblRight = '\u201D'; + var graveAccent = '\u0060'; + var acuteAccent = '\u00B4'; + + // helper functions to get the current/prev/next character + function curr () { return jsString.charAt(i); } + function next() { return jsString.charAt(i + 1); } + function prev() { return jsString.charAt(i - 1); } + + // get the last parsed non-whitespace character + function lastNonWhitespace () { + var p = chars.length - 1; + + while (p >= 0) { + var pp = chars[p]; + if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace + return pp; + } + p--; + } + + return ''; + } + + // skip a block comment '/* ... */' + function skipBlockComment () { + i += 2; + while (i < jsString.length && (curr() !== '*' || next() !== '/')) { + i++; + } + i += 2; + } + + // skip a comment '// ...' + function skipComment () { + i += 2; + while (i < jsString.length && (curr() !== '\n')) { + i++; + } + } + + // parse single or double quoted string + function parseString(endQuote) { + chars.push('"'); + i++; + var c = curr(); + while (i < jsString.length && c !== endQuote) { + if (c === '"' && prev() !== '\\') { + // unescaped double quote, escape it + chars.push('\\"'); + } + else if (controlChars.hasOwnProperty(c)) { + // replace unescaped control characters with escaped ones + chars.push(controlChars[c]) + } + else if (c === '\\') { + // remove the escape character when followed by a single quote ', not needed + i++; + c = curr(); + if (c !== '\'') { + chars.push('\\'); + } + chars.push(c); + } + else { + // regular character + chars.push(c); + } + + i++; + c = curr(); + } + if (c === endQuote) { + chars.push('"'); + i++; + } + } + + // parse an unquoted key + function parseKey() { + var specialValues = ['null', 'true', 'false']; + var key = ''; + var c = curr(); + + var regexp = /[a-zA-Z_$\d]/; // letter, number, underscore, dollar character + while (regexp.test(c)) { + key += c; + i++; + c = curr(); + } + + if (specialValues.indexOf(key) === -1) { + chars.push('"' + key + '"'); + } + else { + chars.push(key); + } + } + + while(i < jsString.length) { + var c = curr(); + + if (c === '/' && next() === '*') { + skipBlockComment(); + } + else if (c === '/' && next() === '/') { + skipComment(); + } + else if (c === '\u00A0' || (c >= '\u2000' && c <= '\u200A') || c === '\u202F' || c === '\u205F' || c === '\u3000') { + // special white spaces (like non breaking space) + chars.push(' ') + i++ + } + else if (c === quote) { + parseString(quote); + } + else if (c === quoteDbl) { + parseString(quoteDbl); + } + else if (c === graveAccent) { + parseString(acuteAccent); + } + else if (c === quoteLeft) { + parseString(quoteRight); + } + else if (c === quoteDblLeft) { + parseString(quoteDblRight); + } + else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) { + // an unquoted object key (like a in '{a:2}') + parseKey(); + } + else { + chars.push(c); + i++; + } + } + + return chars.join(''); + }; + + /** + * Escape unicode characters. + * For example input '\u2661' (length 1) will output '\\u2661' (length 5). + * @param {string} text + * @return {string} + */ + exports.escapeUnicodeChars = function (text) { + // see https://www.wikiwand.com/en/UTF-16 + // note: we leave surrogate pairs as two individual chars, + // as JSON doesn't interpret them as a single unicode char. + return text.replace(/[\u007F-\uFFFF]/g, function(c) { + return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4); + }) + }; + + /** + * Validate a string containing a JSON object + * This method uses JSONLint to validate the String. If JSONLint is not + * available, the built-in JSON parser of the browser is used. + * @param {String} jsonString String with an (invalid) JSON object + * @throws Error + */ + exports.validate = function validate(jsonString) { + if (typeof(jsonlint) != 'undefined') { + jsonlint.parse(jsonString); + } + else { + JSON.parse(jsonString); + } + }; + + /** + * Extend object a with the properties of object b + * @param {Object} a + * @param {Object} b + * @return {Object} a + */ + exports.extend = function extend(a, b) { + for (var prop in b) { + if (b.hasOwnProperty(prop)) { + a[prop] = b[prop]; + } + } + return a; + }; + + /** + * Remove all properties from object a + * @param {Object} a + * @return {Object} a + */ + exports.clear = function clear (a) { + for (var prop in a) { + if (a.hasOwnProperty(prop)) { + delete a[prop]; + } + } + return a; + }; + + /** + * Get the type of an object + * @param {*} object + * @return {String} type + */ + exports.type = function type (object) { + if (object === null) { + return 'null'; + } + if (object === undefined) { + return 'undefined'; + } + if ((object instanceof Number) || (typeof object === 'number')) { + return 'number'; + } + if ((object instanceof String) || (typeof object === 'string')) { + return 'string'; + } + if ((object instanceof Boolean) || (typeof object === 'boolean')) { + return 'boolean'; + } + if ((object instanceof RegExp) || (typeof object === 'regexp')) { + return 'regexp'; + } + if (exports.isArray(object)) { + return 'array'; + } + + return 'object'; + }; + + /** + * Test whether a text contains a url (matches when a string starts + * with 'http://*' or 'https://*' and has no whitespace characters) + * @param {String} text + */ + var isUrlRegex = /^https?:\/\/\S+$/; + exports.isUrl = function isUrl (text) { + return (typeof text == 'string' || text instanceof String) && + isUrlRegex.test(text); + }; + + /** + * Tes whether given object is an Array + * @param {*} obj + * @returns {boolean} returns true when obj is an array + */ + exports.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + + /** + * Retrieve the absolute left value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {Number} left The absolute left position of this element + * in the browser page. + */ + exports.getAbsoluteLeft = function getAbsoluteLeft(elem) { + var rect = elem.getBoundingClientRect(); + return rect.left + window.pageXOffset || document.scrollLeft || 0; + }; + + /** + * Retrieve the absolute top value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {Number} top The absolute top position of this element + * in the browser page. + */ + exports.getAbsoluteTop = function getAbsoluteTop(elem) { + var rect = elem.getBoundingClientRect(); + return rect.top + window.pageYOffset || document.scrollTop || 0; + }; + + /** + * add a className to the given elements style + * @param {Element} elem + * @param {String} className + */ + exports.addClassName = function addClassName(elem, className) { + var classes = elem.className.split(' '); + if (classes.indexOf(className) == -1) { + classes.push(className); // add the class to the array + elem.className = classes.join(' '); + } + }; + + /** + * add a className to the given elements style + * @param {Element} elem + * @param {String} className + */ + exports.removeClassName = function removeClassName(elem, className) { + var classes = elem.className.split(' '); + var index = classes.indexOf(className); + if (index != -1) { + classes.splice(index, 1); // remove the class from the array + elem.className = classes.join(' '); + } + }; + + /** + * Strip the formatting from the contents of a div + * the formatting from the div itself is not stripped, only from its childs. + * @param {Element} divElement + */ + exports.stripFormatting = function stripFormatting(divElement) { + var childs = divElement.childNodes; + for (var i = 0, iMax = childs.length; i < iMax; i++) { + var child = childs[i]; + + // remove the style + if (child.style) { + // TODO: test if child.attributes does contain style + child.removeAttribute('style'); + } + + // remove all attributes + var attributes = child.attributes; + if (attributes) { + for (var j = attributes.length - 1; j >= 0; j--) { + var attribute = attributes[j]; + if (attribute.specified === true) { + child.removeAttribute(attribute.name); + } + } + } + + // recursively strip childs + exports.stripFormatting(child); + } + }; + + /** + * Set focus to the end of an editable div + * code from Nico Burns + * http://stackoverflow.com/users/140293/nico-burns + * http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity + * @param {Element} contentEditableElement A content editable div + */ + exports.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) { + var range, selection; + if(document.createRange) { + range = document.createRange();//Create a range (a range is a like the selection but invisible) + range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range + range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start + selection = window.getSelection();//get the selection object (allows you to change selection) + selection.removeAllRanges();//remove any selections already made + selection.addRange(range);//make the range you have just created the visible selection + } + }; + + /** + * Select all text of a content editable div. + * http://stackoverflow.com/a/3806004/1262753 + * @param {Element} contentEditableElement A content editable div + */ + exports.selectContentEditable = function selectContentEditable(contentEditableElement) { + if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') { + return; + } + + var sel, range; + if (window.getSelection && document.createRange) { + range = document.createRange(); + range.selectNodeContents(contentEditableElement); + sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + }; + + /** + * Get text selection + * http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore + * @return {Range | TextRange | null} range + */ + exports.getSelection = function getSelection() { + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.getRangeAt && sel.rangeCount) { + return sel.getRangeAt(0); + } + } + return null; + }; + + /** + * Set text selection + * http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore + * @param {Range | TextRange | null} range + */ + exports.setSelection = function setSelection(range) { + if (range) { + if (window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + } + }; + + /** + * Get selected text range + * @return {Object} params object containing parameters: + * {Number} startOffset + * {Number} endOffset + * {Element} container HTML element holding the + * selected text element + * Returns null if no text selection is found + */ + exports.getSelectionOffset = function getSelectionOffset() { + var range = exports.getSelection(); + + if (range && 'startOffset' in range && 'endOffset' in range && + range.startContainer && (range.startContainer == range.endContainer)) { + return { + startOffset: range.startOffset, + endOffset: range.endOffset, + container: range.startContainer.parentNode + }; + } + + return null; + }; + + /** + * Set selected text range in given element + * @param {Object} params An object containing: + * {Element} container + * {Number} startOffset + * {Number} endOffset + */ + exports.setSelectionOffset = function setSelectionOffset(params) { + if (document.createRange && window.getSelection) { + var selection = window.getSelection(); + if(selection) { + var range = document.createRange(); + + if (!params.container.firstChild) { + params.container.appendChild(document.createTextNode('')); + } + + // TODO: do not suppose that the first child of the container is a textnode, + // but recursively find the textnodes + range.setStart(params.container.firstChild, params.startOffset); + range.setEnd(params.container.firstChild, params.endOffset); + + exports.setSelection(range); + } + } + }; + + /** + * Get the inner text of an HTML element (for example a div element) + * @param {Element} element + * @param {Object} [buffer] + * @return {String} innerText + */ + exports.getInnerText = function getInnerText(element, buffer) { + var first = (buffer == undefined); + if (first) { + buffer = { + 'text': '', + 'flush': function () { + var text = this.text; + this.text = ''; + return text; + }, + 'set': function (text) { + this.text = text; + } + }; + } + + // text node + if (element.nodeValue) { + return buffer.flush() + element.nodeValue; + } + + // divs or other HTML elements + if (element.hasChildNodes()) { + var childNodes = element.childNodes; + var innerText = ''; + + for (var i = 0, iMax = childNodes.length; i < iMax; i++) { + var child = childNodes[i]; + + if (child.nodeName == 'DIV' || child.nodeName == 'P') { + var prevChild = childNodes[i - 1]; + var prevName = prevChild ? prevChild.nodeName : undefined; + if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') { + innerText += '\n'; + buffer.flush(); + } + innerText += exports.getInnerText(child, buffer); + buffer.set('\n'); + } + else if (child.nodeName == 'BR') { + innerText += buffer.flush(); + buffer.set('\n'); + } + else { + innerText += exports.getInnerText(child, buffer); + } + } + + return innerText; + } + else { + if (element.nodeName == 'P' && exports.getInternetExplorerVersion() != -1) { + // On Internet Explorer, a

      with hasChildNodes()==false is + // rendered with a new line. Note that a

      with + // hasChildNodes()==true is rendered without a new line + // Other browsers always ensure there is a
      inside the

      , + // and if not, the

      does not render a new line + return buffer.flush(); + } + } + + // br or unknown + return ''; + }; + + /** + * Returns the version of Internet Explorer or a -1 + * (indicating the use of another browser). + * Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx + * @return {Number} Internet Explorer version, or -1 in case of an other browser + */ + exports.getInternetExplorerVersion = function getInternetExplorerVersion() { + if (_ieVersion == -1) { + var rv = -1; // Return value assumes failure. + if (navigator.appName == 'Microsoft Internet Explorer') + { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) { + rv = parseFloat( RegExp.$1 ); + } + } + + _ieVersion = rv; + } + + return _ieVersion; + }; + + /** + * Test whether the current browser is Firefox + * @returns {boolean} isFirefox + */ + exports.isFirefox = function isFirefox () { + return (navigator.userAgent.indexOf("Firefox") != -1); + }; + + /** + * cached internet explorer version + * @type {Number} + * @private + */ + var _ieVersion = -1; + + /** + * Add and event listener. Works for all browsers + * @param {Element} element An html element + * @param {string} action The action, for example "click", + * without the prefix "on" + * @param {function} listener The callback function to be executed + * @param {boolean} [useCapture] false by default + * @return {function} the created event listener + */ + exports.addEventListener = function addEventListener(element, action, listener, useCapture) { + if (element.addEventListener) { + if (useCapture === undefined) + useCapture = false; + + if (action === "mousewheel" && exports.isFirefox()) { + action = "DOMMouseScroll"; // For Firefox + } + + element.addEventListener(action, listener, useCapture); + return listener; + } else if (element.attachEvent) { + // Old IE browsers + var f = function () { + return listener.call(element, window.event); + }; + element.attachEvent("on" + action, f); + return f; + } + }; + + /** + * Remove an event listener from an element + * @param {Element} element An html dom element + * @param {string} action The name of the event, for example "mousedown" + * @param {function} listener The listener function + * @param {boolean} [useCapture] false by default + */ + exports.removeEventListener = function removeEventListener(element, action, listener, useCapture) { + if (element.removeEventListener) { + if (useCapture === undefined) + useCapture = false; + + if (action === "mousewheel" && exports.isFirefox()) { + action = "DOMMouseScroll"; // For Firefox + } + + element.removeEventListener(action, listener, useCapture); + } else if (element.detachEvent) { + // Old IE browsers + element.detachEvent("on" + action, listener); + } + }; + + /** + * Parse a JSON path like '.items[3].name' into an array + * @param {string} jsonPath + * @return {Array} + */ + exports.parsePath = function parsePath(jsonPath) { + var prop, remainder; + + if (jsonPath.length === 0) { + return []; + } + + // find a match like '.prop' + var match = jsonPath.match(/^\.(\w+)/); + if (match) { + prop = match[1]; + remainder = jsonPath.substr(prop.length + 1); + } + else if (jsonPath[0] === '[') { + // find a match like + var end = jsonPath.indexOf(']'); + if (end === -1) { + throw new SyntaxError('Character ] expected in path'); + } + if (end === 1) { + throw new SyntaxError('Index expected after ['); + } + + var value = jsonPath.substring(1, end); + if (value[0] === '\'') { + // ajv produces string prop names with single quotes, so we need + // to reformat them into valid double-quoted JSON strings + value = '\"' + value.substring(1, value.length - 1) + '\"'; + } + + prop = value === '*' ? value : JSON.parse(value); // parse string and number + remainder = jsonPath.substr(end + 1); + } + else { + throw new SyntaxError('Failed to parse path'); + } + + return [prop].concat(parsePath(remainder)) + }; + + /** + * Improve the error message of a JSON schema error + * @param {Object} error + * @return {Object} The error + */ + exports.improveSchemaError = function (error) { + if (error.keyword === 'enum' && Array.isArray(error.schema)) { + var enums = error.schema; + if (enums) { + enums = enums.map(function (value) { + return JSON.stringify(value); + }); + + if (enums.length > 5) { + var more = ['(' + (enums.length - 5) + ' more...)']; + enums = enums.slice(0, 5); + enums.push(more); + } + error.message = 'should be equal to one of: ' + enums.join(', '); + } + } + + if (error.keyword === 'additionalProperties') { + error.message = 'should NOT have additional property: ' + error.params.additionalProperty; + } + + return error; + }; + + /** + * Test whether the child rect fits completely inside the parent rect. + * @param {ClientRect} parent + * @param {ClientRect} child + * @param {number} margin + */ + exports.insideRect = function (parent, child, margin) { + var _margin = margin !== undefined ? margin : 0; + return child.left - _margin >= parent.left + && child.right + _margin <= parent.right + && child.top - _margin >= parent.top + && child.bottom + _margin <= parent.bottom; + }; + + /** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. + * + * Source: https://davidwalsh.name/javascript-debounce-function + * + * @param {function} func + * @param {number} wait Number in milliseconds + * @param {boolean} [immediate=false] If `immediate` is passed, trigger the + * function on the leading edge, instead + * of the trailing. + * @return {function} Return the debounced function + */ + exports.debounce = function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + }; + + /** + * Determines the difference between two texts. + * Can only detect one removed or inserted block of characters. + * @param {string} oldText + * @param {string} newText + * @return {{start: number, end: number}} Returns the start and end + * of the changed part in newText. + */ + exports.textDiff = function textDiff(oldText, newText) { + var len = newText.length; + var start = 0; + var oldEnd = oldText.length; + var newEnd = newText.length; + + while (newText.charAt(start) === oldText.charAt(start) + && start < len) { + start++; + } + + while (newText.charAt(newEnd - 1) === oldText.charAt(oldEnd - 1) + && newEnd > start && oldEnd > 0) { + newEnd--; + oldEnd--; + } + + return {start: start, end: newEnd}; + }; + + + /** + * Return an object with the selection range or cursor position (if both have the same value) + * Support also old browsers (IE8-) + * Source: http://ourcodeworld.com/articles/read/282/how-to-get-the-current-cursor-position-and-selection-within-a-text-input-or-textarea-in-javascript + * @param {DOMElement} el A dom element of a textarea or input text. + * @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text. + **/ + exports.getInputSelection = function(el) { + var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange; + + if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") { + start = el.selectionStart; + end = el.selectionEnd; + } else { + range = document.selection.createRange(); + + if (range && range.parentElement() == el) { + len = el.value.length; + normalizedValue = el.value.replace(/\r\n/g, "\n"); + + // Create a working TextRange that lives only in the input + textInputRange = el.createTextRange(); + textInputRange.moveToBookmark(range.getBookmark()); + + // Check if the start and end of the selection are at the very end + // of the input, since moveStart/moveEnd doesn't return what we want + // in those cases + endRange = el.createTextRange(); + endRange.collapse(false); + + if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { + start = end = len; + } else { + start = -textInputRange.moveStart("character", -len); + start += normalizedValue.slice(0, start).split("\n").length - 1; + + if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { + end = len; + } else { + end = -textInputRange.moveEnd("character", -len); + end += normalizedValue.slice(0, end).split("\n").length - 1; + } + } + } + } + + return { + start: start, + end: end + }; + } + + + if (typeof Element !== 'undefined') { + // Polyfill for array remove + (function () { + function polyfill (item) { + if (item.hasOwnProperty('remove')) { + return; + } + Object.defineProperty(item, 'remove', { + configurable: true, + enumerable: true, + writable: true, + value: function remove() { + if (this.parentNode != null) + this.parentNode.removeChild(this); + } + }); + } + + if (typeof Element !== 'undefined') { polyfill(Element.prototype); } + if (typeof CharacterData !== 'undefined') { polyfill(CharacterData.prototype); } + if (typeof DocumentType !== 'undefined') { polyfill(DocumentType.prototype); } + })(); + } + + + // Polyfill for startsWith + if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; + } + + // Polyfill for Array.find + if (!Array.prototype.find) { + Array.prototype.find = function(callback) { + for (var i = 0; i < this.length; i++) { + var element = this[i]; + if ( callback.call(this, element, i, this) ) { + return element; + } + } + } + } + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + /* Jison generated parser */ + var jsonlint = (function(){ + var parser = {trace: function trace() { }, + yy: {}, + symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, + terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, + productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], + performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: // replace escaped characters with actual character + this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") + .replace(/\\n/g,'\n') + .replace(/\\r/g,'\r') + .replace(/\\t/g,'\t') + .replace(/\\v/g,'\v') + .replace(/\\f/g,'\f') + .replace(/\\b/g,'\b'); + + break; + case 2:this.$ = Number(yytext); + break; + case 3:this.$ = null; + break; + case 4:this.$ = true; + break; + case 5:this.$ = false; + break; + case 6:return this.$ = $$[$0-1]; + break; + case 13:this.$ = {}; + break; + case 14:this.$ = $$[$0-1]; + break; + case 15:this.$ = [$$[$0-2], $$[$0]]; + break; + case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; + break; + case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; + break; + case 18:this.$ = []; + break; + case 19:this.$ = $$[$0-1]; + break; + case 20:this.$ = [$$[$0]]; + break; + case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); + break; + } + }, + table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], + defaultActions: {16:[2,6]}, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], // semantic value stack + lstack = [], // location stack + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + + //this.reductionCount = this.shiftCount = 0; + + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + if (typeof this.lexer.yylloc == 'undefined') + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + + if (typeof this.yy.parseError === 'function') + this.parseError = this.yy.parseError; + + function popStack (n) { + stack.length = stack.length - 2*n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + + function lex() { + var token; + token = self.lexer.lex() || 1; // $end = 1 + // if token isn't its numeric value, convert + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + + var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; + while (true) { + // retreive state number from top of stack + state = stack[stack.length-1]; + + // use default actions if available + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol == null) + symbol = lex(); + // read action for current state and first input + action = table[state] && table[state][symbol]; + } + + // handle parse error + _handle_error: + if (typeof action === 'undefined' || !action.length || !action[0]) { + + if (!recovering) { + // Report error + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'"+this.terminals_[p]+"'"); + } + var errStr = ''; + if (this.lexer.showPosition) { + errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; + } else { + errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + + (symbol == 1 /*EOF*/ ? "end of input" : + ("'"+(this.terminals_[symbol] || symbol)+"'")); + } + this.parseError(errStr, + {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + + // just recovered from another error + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } + + // discard current lookahead and grab another + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + symbol = lex(); + } + + // try to recover from error + while (1) { + // check for error recovery rule in this state + if ((TERROR.toString()) in table[state]) { + break; + } + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + popStack(1); + state = stack[stack.length-1]; + } + + preErrorSymbol = symbol; // save the lookahead token + symbol = TERROR; // insert generic error symbol as new lookahead + state = stack[stack.length-1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); + } + + switch (action[0]) { + + case 1: // shift + //this.shiftCount++; + + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + symbol = null; + if (!preErrorSymbol) { // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + + case 2: // reduce + //this.reductionCount++; + + len = this.productions_[action[1]][1]; + + // perform semantic action + yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + yyval._$ = { + first_line: lstack[lstack.length-(len||1)].first_line, + last_line: lstack[lstack.length-1].last_line, + first_column: lstack[lstack.length-(len||1)].first_column, + last_column: lstack[lstack.length-1].last_column + }; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + + if (typeof r !== 'undefined') { + return r; + } + + // pop off stack + if (len) { + stack = stack.slice(0,-1*len*2); + vstack = vstack.slice(0, -1*len); + lstack = lstack.slice(0, -1*len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + vstack.push(yyval.$); + lstack.push(yyval._$); + // goto new state = table[STATE][NONTERMINAL] + newState = table[stack[stack.length-2]][stack[stack.length-1]]; + stack.push(newState); + break; + + case 3: // accept + return true; + } + + } + + return true; + }}; + /* Jison generated lexer */ + var lexer = (function(){ + var lexer = ({EOF:1, + parseError:function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + return this; + }, + input:function () { + var ch = this._input[0]; + this.yytext+=ch; + this.yyleng++; + this.match+=ch; + this.matched+=ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, + unput:function (ch) { + this._input = ch + this._input; + return this; + }, + more:function () { + this._more = true; + return this; + }, + less:function (n) { + this._input = this.match.slice(n) + this._input; + }, + pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, + showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, + next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} + this.yytext += match[0]; + this.match += match[0]; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, + lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin:function begin(condition) { + this.conditionStack.push(condition); + }, + popState:function popState() { + return this.conditionStack.pop(); + }, + _currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, + topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, + pushState:function begin(condition) { + this.begin(condition); + }}); + lexer.options = {}; + lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + + var YYSTATE=YY_START + switch($avoiding_name_collisions) { + case 0:/* skip whitespace */ + break; + case 1:return 6 + break; + case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 + break; + case 3:return 17 + break; + case 4:return 18 + break; + case 5:return 23 + break; + case 6:return 24 + break; + case 7:return 22 + break; + case 8:return 21 + break; + case 9:return 10 + break; + case 10:return 11 + break; + case 11:return 8 + break; + case 12:return 14 + break; + case 13:return 'INVALID' + break; + } + }; + lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; + lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; + + + ; + return lexer;})() + parser.lexer = lexer; + return parser; + })(); + if (true) { + exports.parser = jsonlint; + exports.parse = jsonlint.parse.bind(jsonlint); + } + +/***/ }, +/* 56 */ +/***/ function(module, exports) { + + 'use strict'; + + /** + * @constructor SearchBox + * Create a search box in given HTML container + * @param {JSONEditor} editor The JSON Editor to attach to + * @param {Element} container HTML container element of where to + * create the search box + */ + function SearchBox (editor, container) { + var searchBox = this; + + this.editor = editor; + this.timeout = undefined; + this.delay = 200; // ms + this.lastText = undefined; + + this.dom = {}; + this.dom.container = container; + + var table = document.createElement('table'); + this.dom.table = table; + table.className = 'jsoneditor-search'; + container.appendChild(table); + var tbody = document.createElement('tbody'); + this.dom.tbody = tbody; + table.appendChild(tbody); + var tr = document.createElement('tr'); + tbody.appendChild(tr); + + var td = document.createElement('td'); + tr.appendChild(td); + var results = document.createElement('div'); + this.dom.results = results; + results.className = 'jsoneditor-results'; + td.appendChild(results); + + td = document.createElement('td'); + tr.appendChild(td); + var divInput = document.createElement('div'); + this.dom.input = divInput; + divInput.className = 'jsoneditor-frame'; + divInput.title = 'Search fields and values'; + td.appendChild(divInput); + + // table to contain the text input and search button + var tableInput = document.createElement('table'); + divInput.appendChild(tableInput); + var tbodySearch = document.createElement('tbody'); + tableInput.appendChild(tbodySearch); + tr = document.createElement('tr'); + tbodySearch.appendChild(tr); + + var refreshSearch = document.createElement('button'); + refreshSearch.type = 'button'; + refreshSearch.className = 'jsoneditor-refresh'; + td = document.createElement('td'); + td.appendChild(refreshSearch); + tr.appendChild(td); + + var search = document.createElement('input'); + // search.type = 'button'; + this.dom.search = search; + search.oninput = function (event) { + searchBox._onDelayedSearch(event); + }; + search.onchange = function (event) { // For IE 9 + searchBox._onSearch(); + }; + search.onkeydown = function (event) { + searchBox._onKeyDown(event); + }; + search.onkeyup = function (event) { + searchBox._onKeyUp(event); + }; + refreshSearch.onclick = function (event) { + search.select(); + }; + + // TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819 + td = document.createElement('td'); + td.appendChild(search); + tr.appendChild(td); + + var searchNext = document.createElement('button'); + searchNext.type = 'button'; + searchNext.title = 'Next result (Enter)'; + searchNext.className = 'jsoneditor-next'; + searchNext.onclick = function () { + searchBox.next(); + }; + td = document.createElement('td'); + td.appendChild(searchNext); + tr.appendChild(td); + + var searchPrevious = document.createElement('button'); + searchPrevious.type = 'button'; + searchPrevious.title = 'Previous result (Shift+Enter)'; + searchPrevious.className = 'jsoneditor-previous'; + searchPrevious.onclick = function () { + searchBox.previous(); + }; + td = document.createElement('td'); + td.appendChild(searchPrevious); + tr.appendChild(td); + } + + /** + * Go to the next search result + * @param {boolean} [focus] If true, focus will be set to the next result + * focus is false by default. + */ + SearchBox.prototype.next = function(focus) { + if (this.results != undefined) { + var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0; + if (index > this.results.length - 1) { + index = 0; + } + this._setActiveResult(index, focus); + } + }; + + /** + * Go to the prevous search result + * @param {boolean} [focus] If true, focus will be set to the next result + * focus is false by default. + */ + SearchBox.prototype.previous = function(focus) { + if (this.results != undefined) { + var max = this.results.length - 1; + var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max; + if (index < 0) { + index = max; + } + this._setActiveResult(index, focus); + } + }; + + /** + * Set new value for the current active result + * @param {Number} index + * @param {boolean} [focus] If true, focus will be set to the next result. + * focus is false by default. + * @private + */ + SearchBox.prototype._setActiveResult = function(index, focus) { + // de-activate current active result + if (this.activeResult) { + var prevNode = this.activeResult.node; + var prevElem = this.activeResult.elem; + if (prevElem == 'field') { + delete prevNode.searchFieldActive; + } + else { + delete prevNode.searchValueActive; + } + prevNode.updateDom(); + } + + if (!this.results || !this.results[index]) { + // out of range, set to undefined + this.resultIndex = undefined; + this.activeResult = undefined; + return; + } + + this.resultIndex = index; + + // set new node active + var node = this.results[this.resultIndex].node; + var elem = this.results[this.resultIndex].elem; + if (elem == 'field') { + node.searchFieldActive = true; + } + else { + node.searchValueActive = true; + } + this.activeResult = this.results[this.resultIndex]; + node.updateDom(); + + // TODO: not so nice that the focus is only set after the animation is finished + node.scrollTo(function () { + if (focus) { + node.focus(elem); + } + }); + }; + + /** + * Cancel any running onDelayedSearch. + * @private + */ + SearchBox.prototype._clearDelay = function() { + if (this.timeout != undefined) { + clearTimeout(this.timeout); + delete this.timeout; + } + }; + + /** + * Start a timer to execute a search after a short delay. + * Used for reducing the number of searches while typing. + * @param {Event} event + * @private + */ + SearchBox.prototype._onDelayedSearch = function (event) { + // execute the search after a short delay (reduces the number of + // search actions while typing in the search text box) + this._clearDelay(); + var searchBox = this; + this.timeout = setTimeout(function (event) { + searchBox._onSearch(); + }, + this.delay); + }; + + /** + * Handle onSearch event + * @param {boolean} [forceSearch] If true, search will be executed again even + * when the search text is not changed. + * Default is false. + * @private + */ + SearchBox.prototype._onSearch = function (forceSearch) { + this._clearDelay(); + + var value = this.dom.search.value; + var text = (value.length > 0) ? value : undefined; + if (text != this.lastText || forceSearch) { + // only search again when changed + this.lastText = text; + this.results = this.editor.search(text); + this._setActiveResult(undefined); + + // display search results + if (text != undefined) { + var resultCount = this.results.length; + switch (resultCount) { + case 0: this.dom.results.innerHTML = 'no results'; break; + case 1: this.dom.results.innerHTML = '1 result'; break; + default: this.dom.results.innerHTML = resultCount + ' results'; break; + } + } + else { + this.dom.results.innerHTML = ''; + } + } + }; + + /** + * Handle onKeyDown event in the input box + * @param {Event} event + * @private + */ + SearchBox.prototype._onKeyDown = function (event) { + var keynum = event.which; + if (keynum == 27) { // ESC + this.dom.search.value = ''; // clear search + this._onSearch(); + event.preventDefault(); + event.stopPropagation(); + } + else if (keynum == 13) { // Enter + if (event.ctrlKey) { + // force to search again + this._onSearch(true); + } + else if (event.shiftKey) { + // move to the previous search result + this.previous(); + } + else { + // move to the next search result + this.next(); + } + event.preventDefault(); + event.stopPropagation(); + } + }; + + /** + * Handle onKeyUp event in the input box + * @param {Event} event + * @private + */ + SearchBox.prototype._onKeyUp = function (event) { + var keynum = event.keyCode; + if (keynum != 27 && keynum != 13) { // !show and !Enter + this._onDelayedSearch(event); // For IE 9 + } + }; + + /** + * Clear the search results + */ + SearchBox.prototype.clear = function () { + this.dom.search.value = ''; + this._onSearch(); + }; + + /** + * Destroy the search box + */ + SearchBox.prototype.destroy = function () { + this.editor = null; + this.dom.container.removeChild(this.dom.table); + this.dom = null; + + this.results = null; + this.activeResult = null; + + this._clearDelay(); + + }; + + module.exports = SearchBox; + + +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(54); + + /** + * Node.getRootNode shim + * @param {Node} node node to check + * @return {Node} node's rootNode or `window` if there is ShadowDOM is not supported. + */ + function getRootNode(node){ + return node.getRootNode && node.getRootNode() || window; + } + + /** + * A context menu + * @param {Object[]} items Array containing the menu structure + * TODO: describe structure + * @param {Object} [options] Object with options. Available options: + * {function} close Callback called when the + * context menu is being closed. + * @constructor + */ + function ContextMenu (items, options) { + this.dom = {}; + + var me = this; + var dom = this.dom; + this.anchor = undefined; + this.items = items; + this.eventListeners = {}; + this.selection = undefined; // holds the selection before the menu was opened + this.onClose = options ? options.close : undefined; + + // create root element + var root = document.createElement('div'); + root.className = 'jsoneditor-contextmenu-root'; + dom.root = root; + + // create a container element + var menu = document.createElement('div'); + menu.className = 'jsoneditor-contextmenu'; + dom.menu = menu; + root.appendChild(menu); + + // create a list to hold the menu items + var list = document.createElement('ul'); + list.className = 'jsoneditor-menu'; + menu.appendChild(list); + dom.list = list; + dom.items = []; // list with all buttons + + // create a (non-visible) button to set the focus to the menu + var focusButton = document.createElement('button'); + focusButton.type = 'button'; + dom.focusButton = focusButton; + var li = document.createElement('li'); + li.style.overflow = 'hidden'; + li.style.height = '0'; + li.appendChild(focusButton); + list.appendChild(li); + + function createMenuItems (list, domItems, items) { + items.forEach(function (item) { + if (item.type == 'separator') { + // create a separator + var separator = document.createElement('div'); + separator.className = 'jsoneditor-separator'; + li = document.createElement('li'); + li.appendChild(separator); + list.appendChild(li); + } + else { + var domItem = {}; + + // create a menu item + var li = document.createElement('li'); + list.appendChild(li); + + // create a button in the menu item + var button = document.createElement('button'); + button.type = 'button'; + button.className = item.className; + domItem.button = button; + if (item.title) { + button.title = item.title; + } + if (item.click) { + button.onclick = function (event) { + event.preventDefault(); + me.hide(); + item.click(); + }; + } + li.appendChild(button); + + // create the contents of the button + if (item.submenu) { + // add the icon to the button + var divIcon = document.createElement('div'); + divIcon.className = 'jsoneditor-icon'; + button.appendChild(divIcon); + var divText = document.createElement('div'); + divText.className = 'jsoneditor-text' + + (item.click ? '' : ' jsoneditor-right-margin'); + divText.appendChild(document.createTextNode(item.text)); + button.appendChild(divText); + + var buttonSubmenu; + if (item.click) { + // submenu and a button with a click handler + button.className += ' jsoneditor-default'; + + var buttonExpand = document.createElement('button'); + buttonExpand.type = 'button'; + domItem.buttonExpand = buttonExpand; + buttonExpand.className = 'jsoneditor-expand'; + buttonExpand.innerHTML = '

      '; + li.appendChild(buttonExpand); + if (item.submenuTitle) { + buttonExpand.title = item.submenuTitle; + } + + buttonSubmenu = buttonExpand; + } + else { + // submenu and a button without a click handler + var divExpand = document.createElement('div'); + divExpand.className = 'jsoneditor-expand'; + button.appendChild(divExpand); + + buttonSubmenu = button; + } + + // attach a handler to expand/collapse the submenu + buttonSubmenu.onclick = function (event) { + event.preventDefault(); + me._onExpandItem(domItem); + buttonSubmenu.focus(); + }; + + // create the submenu + var domSubItems = []; + domItem.subItems = domSubItems; + var ul = document.createElement('ul'); + domItem.ul = ul; + ul.className = 'jsoneditor-menu'; + ul.style.height = '0'; + li.appendChild(ul); + createMenuItems(ul, domSubItems, item.submenu); + } + else { + // no submenu, just a button with clickhandler + button.innerHTML = '
      ' + + '
      ' + item.text + '
      '; + } + + domItems.push(domItem); + } + }); + } + createMenuItems(list, this.dom.items, items); + + // TODO: when the editor is small, show the submenu on the right instead of inline? + + // calculate the max height of the menu with one submenu expanded + this.maxHeight = 0; // height in pixels + items.forEach(function (item) { + var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24; + me.maxHeight = Math.max(me.maxHeight, height); + }); + } + + /** + * Get the currently visible buttons + * @return {Array.} buttons + * @private + */ + ContextMenu.prototype._getVisibleButtons = function () { + var buttons = []; + var me = this; + this.dom.items.forEach(function (item) { + buttons.push(item.button); + if (item.buttonExpand) { + buttons.push(item.buttonExpand); + } + if (item.subItems && item == me.expandedItem) { + item.subItems.forEach(function (subItem) { + buttons.push(subItem.button); + if (subItem.buttonExpand) { + buttons.push(subItem.buttonExpand); + } + // TODO: change to fully recursive method + }); + } + }); + + return buttons; + }; + + // currently displayed context menu, a singleton. We may only have one visible context menu + ContextMenu.visibleMenu = undefined; + + /** + * Attach the menu to an anchor + * @param {HTMLElement} anchor Anchor where the menu will be attached + * as sibling. + * @param {HTMLElement} [contentWindow] The DIV with with the (scrollable) contents + */ + ContextMenu.prototype.show = function (anchor, contentWindow) { + this.hide(); + + // determine whether to display the menu below or above the anchor + var showBelow = true; + var parent = anchor.parentNode; + var anchorRect = anchor.getBoundingClientRect(); + var parentRect = parent.getBoundingClientRect() + + if (contentWindow) { + + var contentRect = contentWindow.getBoundingClientRect(); + + if (anchorRect.bottom + this.maxHeight < contentRect.bottom) { + // fits below -> show below + } + else if (anchorRect.top - this.maxHeight > contentRect.top) { + // fits above -> show above + showBelow = false; + } + else { + // doesn't fit above nor below -> show below + } + } + + var leftGap = anchorRect.left - parentRect.left; + var topGap = anchorRect.top - parentRect.top; + + // position the menu + if (showBelow) { + // display the menu below the anchor + var anchorHeight = anchor.offsetHeight; + this.dom.menu.style.left = leftGap + 'px'; + this.dom.menu.style.top = topGap + anchorHeight + 'px'; + this.dom.menu.style.bottom = ''; + } + else { + // display the menu above the anchor + this.dom.menu.style.left = leftGap + 'px'; + this.dom.menu.style.top = topGap + 'px'; + this.dom.menu.style.bottom = '0px'; + } + + // find the root node of the page (window, or a shadow dom root element) + this.rootNode = getRootNode(anchor); + + // attach the menu to the parent of the anchor + parent.insertBefore(this.dom.root, parent.firstChild); + + // create and attach event listeners + var me = this; + var list = this.dom.list; + this.eventListeners.mousedown = util.addEventListener(this.rootNode, 'mousedown', function (event) { + // hide menu on click outside of the menu + var target = event.target; + if ((target != list) && !me._isChildOf(target, list)) { + me.hide(); + event.stopPropagation(); + event.preventDefault(); + } + }); + this.eventListeners.keydown = util.addEventListener(this.rootNode, 'keydown', function (event) { + me._onKeyDown(event); + }); + + // move focus to the first button in the context menu + this.selection = util.getSelection(); + this.anchor = anchor; + setTimeout(function () { + me.dom.focusButton.focus(); + }, 0); + + if (ContextMenu.visibleMenu) { + ContextMenu.visibleMenu.hide(); + } + ContextMenu.visibleMenu = this; + }; + + /** + * Hide the context menu if visible + */ + ContextMenu.prototype.hide = function () { + // remove the menu from the DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + if (this.onClose) { + this.onClose(); + } + } + + // remove all event listeners + // all event listeners are supposed to be attached to document. + for (var name in this.eventListeners) { + if (this.eventListeners.hasOwnProperty(name)) { + var fn = this.eventListeners[name]; + if (fn) { + util.removeEventListener(this.rootNode, name, fn); + } + delete this.eventListeners[name]; + } + } + + if (ContextMenu.visibleMenu == this) { + ContextMenu.visibleMenu = undefined; + } + }; + + /** + * Expand a submenu + * Any currently expanded submenu will be hided. + * @param {Object} domItem + * @private + */ + ContextMenu.prototype._onExpandItem = function (domItem) { + var me = this; + var alreadyVisible = (domItem == this.expandedItem); + + // hide the currently visible submenu + var expandedItem = this.expandedItem; + if (expandedItem) { + //var ul = expandedItem.ul; + expandedItem.ul.style.height = '0'; + expandedItem.ul.style.padding = ''; + setTimeout(function () { + if (me.expandedItem != expandedItem) { + expandedItem.ul.style.display = ''; + util.removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected'); + } + }, 300); // timeout duration must match the css transition duration + this.expandedItem = undefined; + } + + if (!alreadyVisible) { + var ul = domItem.ul; + ul.style.display = 'block'; + var height = ul.clientHeight; // force a reflow in Firefox + setTimeout(function () { + if (me.expandedItem == domItem) { + var childsHeight = 0; + for (var i = 0; i < ul.childNodes.length; i++) { + childsHeight += ul.childNodes[i].clientHeight; + } + ul.style.height = childsHeight + 'px'; + ul.style.padding = '5px 10px'; + } + }, 0); + util.addClassName(ul.parentNode, 'jsoneditor-selected'); + this.expandedItem = domItem; + } + }; + + /** + * Handle onkeydown event + * @param {Event} event + * @private + */ + ContextMenu.prototype._onKeyDown = function (event) { + var target = event.target; + var keynum = event.which; + var handled = false; + var buttons, targetIndex, prevButton, nextButton; + + if (keynum == 27) { // ESC + // hide the menu on ESC key + + // restore previous selection and focus + if (this.selection) { + util.setSelection(this.selection); + } + if (this.anchor) { + this.anchor.focus(); + } + + this.hide(); + + handled = true; + } + else if (keynum == 9) { // Tab + if (!event.shiftKey) { // Tab + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + if (targetIndex == buttons.length - 1) { + // move to first button + buttons[0].focus(); + handled = true; + } + } + else { // Shift+Tab + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + if (targetIndex == 0) { + // move to last button + buttons[buttons.length - 1].focus(); + handled = true; + } + } + } + else if (keynum == 37) { // Arrow Left + if (target.className == 'jsoneditor-expand') { + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + prevButton = buttons[targetIndex - 1]; + if (prevButton) { + prevButton.focus(); + } + } + handled = true; + } + else if (keynum == 38) { // Arrow Up + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + prevButton = buttons[targetIndex - 1]; + if (prevButton && prevButton.className == 'jsoneditor-expand') { + // skip expand button + prevButton = buttons[targetIndex - 2]; + } + if (!prevButton) { + // move to last button + prevButton = buttons[buttons.length - 1]; + } + if (prevButton) { + prevButton.focus(); + } + handled = true; + } + else if (keynum == 39) { // Arrow Right + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + nextButton = buttons[targetIndex + 1]; + if (nextButton && nextButton.className == 'jsoneditor-expand') { + nextButton.focus(); + } + handled = true; + } + else if (keynum == 40) { // Arrow Down + buttons = this._getVisibleButtons(); + targetIndex = buttons.indexOf(target); + nextButton = buttons[targetIndex + 1]; + if (nextButton && nextButton.className == 'jsoneditor-expand') { + // skip expand button + nextButton = buttons[targetIndex + 2]; + } + if (!nextButton) { + // move to first button + nextButton = buttons[0]; + } + if (nextButton) { + nextButton.focus(); + handled = true; + } + handled = true; + } + // TODO: arrow left and right + + if (handled) { + event.stopPropagation(); + event.preventDefault(); + } + }; + + /** + * Test if an element is a child of a parent element. + * @param {Element} child + * @param {Element} parent + * @return {boolean} isChild + */ + ContextMenu.prototype._isChildOf = function (child, parent) { + var e = child.parentNode; + while (e) { + if (e == parent) { + return true; + } + e = e.parentNode; + } + + return false; + }; + + module.exports = ContextMenu; + + +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var ContextMenu = __webpack_require__(57); + + /** + * Creates a component that visualize path selection in tree based editors + * @param {HTMLElement} container + * @constructor + */ + function TreePath(container) { + if (container) { + this.path = document.createElement('div'); + this.path.className = 'jsoneditor-treepath'; + container.appendChild(this.path); + this.reset(); + } + }; + + /** + * Reset component to initial status + */ + TreePath.prototype.reset = function () { + this.path.innerHTML = ''; + } + + /** + * Renders the component UI according to a given path objects + * @param {Array} pathObjs a list of path objects + * + */ + TreePath.prototype.setPath = function (pathObjs) { + var me = this; + this.reset(); + if (pathObjs && pathObjs.length) { + pathObjs.forEach(function (pathObj, idx) { + var pathEl = document.createElement('span'); + var sepEl; + pathEl.className = 'jsoneditor-treepath-element'; + pathEl.innerText = pathObj.name; + pathEl.onclick = _onSegmentClick.bind(me, pathObj); + + me.path.appendChild(pathEl); + + if (pathObj.children.length) { + sepEl = document.createElement('span'); + sepEl.className = 'jsoneditor-treepath-seperator'; + sepEl.innerHTML = '►'; + + sepEl.onclick = function () { + var items = []; + pathObj.children.forEach(function (child) { + items.push({ + 'text': child.name, + 'className': 'jsoneditor-type-modes' + (pathObjs[idx + 1] + 1 && pathObjs[idx + 1].name === child.name ? ' jsoneditor-selected' : ''), + 'click': _onContextMenuItemClick.bind(me, pathObj, child.name) + }); + }); + var menu = new ContextMenu(items); + menu.show(sepEl); + }; + + me.path.appendChild(sepEl, me.container); + } + + if(idx === pathObjs.length - 1) { + var leftRectPos = (sepEl || pathEl).getBoundingClientRect().left; + if(me.path.offsetWidth < leftRectPos) { + me.path.scrollLeft = leftRectPos; + } + } + }); + } + + function _onSegmentClick(pathObj) { + if (this.selectionCallback) { + this.selectionCallback(pathObj); + } + }; + + function _onContextMenuItemClick(pathObj, selection) { + if (this.contextMenuCallback) { + this.contextMenuCallback(pathObj, selection); + } + }; + }; + + /** + * set a callback function for selection of path section + * @param {Function} callback function to invoke when section is selected + */ + TreePath.prototype.onSectionSelected = function (callback) { + if (typeof callback === 'function') { + this.selectionCallback = callback; + } + }; + + /** + * set a callback function for selection of path section + * @param {Function} callback function to invoke when section is selected + */ + TreePath.prototype.onContextMenuItemSelected = function (callback) { + if (typeof callback === 'function') { + this.contextMenuCallback = callback; + } + }; + + module.exports = TreePath; + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var naturalSort = __webpack_require__(60); + var ContextMenu = __webpack_require__(57); + var appendNodeFactory = __webpack_require__(61); + var util = __webpack_require__(54); + + /** + * @constructor Node + * Create a new Node + * @param {./treemode} editor + * @param {Object} [params] Can contain parameters: + * {string} field + * {boolean} fieldEditable + * {*} value + * {String} type Can have values 'auto', 'array', + * 'object', or 'string'. + */ + function Node (editor, params) { + /** @type {./treemode} */ + this.editor = editor; + this.dom = {}; + this.expanded = false; + + if(params && (params instanceof Object)) { + this.setField(params.field, params.fieldEditable); + this.setValue(params.value, params.type); + } + else { + this.setField(''); + this.setValue(null); + } + + this._debouncedOnChangeValue = util.debounce(this._onChangeValue.bind(this), Node.prototype.DEBOUNCE_INTERVAL); + this._debouncedOnChangeField = util.debounce(this._onChangeField.bind(this), Node.prototype.DEBOUNCE_INTERVAL); + } + + // debounce interval for keyboard input in milliseconds + Node.prototype.DEBOUNCE_INTERVAL = 150; + + /** + * Determine whether the field and/or value of this node are editable + * @private + */ + Node.prototype._updateEditability = function () { + this.editable = { + field: true, + value: true + }; + + if (this.editor) { + this.editable.field = this.editor.options.mode === 'tree'; + this.editable.value = this.editor.options.mode !== 'view'; + + if ((this.editor.options.mode === 'tree' || this.editor.options.mode === 'form') && + (typeof this.editor.options.onEditable === 'function')) { + var editable = this.editor.options.onEditable({ + field: this.field, + value: this.value, + path: this.getPath() + }); + + if (typeof editable === 'boolean') { + this.editable.field = editable; + this.editable.value = editable; + } + else { + if (typeof editable.field === 'boolean') this.editable.field = editable.field; + if (typeof editable.value === 'boolean') this.editable.value = editable.value; + } + } + } + }; + + /** + * Get the path of this node + * @return {String[]} Array containing the path to this node + */ + Node.prototype.getPath = function () { + var node = this; + var path = []; + while (node) { + var field = !node.parent + ? undefined // do not add an (optional) field name of the root node + : (node.parent.type != 'array') + ? node.field + : node.index; + + if (field !== undefined) { + path.unshift(field); + } + node = node.parent; + } + return path; + }; + + /** + * Find a Node from a JSON path like '.items[3].name' + * @param {string} jsonPath + * @return {Node | null} Returns the Node when found, returns null if not found + */ + Node.prototype.findNode = function (jsonPath) { + var path = util.parsePath(jsonPath); + var node = this; + while (node && path.length > 0) { + var prop = path.shift(); + if (typeof prop === 'number') { + if (node.type !== 'array') { + throw new Error('Cannot get child node at index ' + prop + ': node is no array'); + } + node = node.childs[prop]; + } + else { // string + if (node.type !== 'object') { + throw new Error('Cannot get child node ' + prop + ': node is no object'); + } + node = node.childs.filter(function (child) { + return child.field === prop; + })[0]; + } + } + + return node; + }; + + /** + * Find all parents of this node. The parents are ordered from root node towards + * the original node. + * @return {Array.} + */ + Node.prototype.findParents = function () { + var parents = []; + var parent = this.parent; + while (parent) { + parents.unshift(parent); + parent = parent.parent; + } + return parents; + }; + + /** + * + * @param {{dataPath: string, keyword: string, message: string, params: Object, schemaPath: string} | null} error + * @param {Node} [child] When this is the error of a parent node, pointing + * to an invalid child node, the child node itself + * can be provided. If provided, clicking the error + * icon will set focus to the invalid child node. + */ + Node.prototype.setError = function (error, child) { + // ensure the dom exists + this.getDom(); + + this.error = error; + var tdError = this.dom.tdError; + if (error) { + if (!tdError) { + tdError = document.createElement('td'); + this.dom.tdError = tdError; + this.dom.tdValue.parentNode.appendChild(tdError); + } + + var popover = document.createElement('div'); + popover.className = 'jsoneditor-popover jsoneditor-right'; + popover.appendChild(document.createTextNode(error.message)); + + var button = document.createElement('button'); + button.type = 'button'; + button.className = 'jsoneditor-schema-error'; + button.appendChild(popover); + + // update the direction of the popover + button.onmouseover = button.onfocus = function updateDirection() { + var directions = ['right', 'above', 'below', 'left']; + for (var i = 0; i < directions.length; i++) { + var direction = directions[i]; + popover.className = 'jsoneditor-popover jsoneditor-' + direction; + + var contentRect = this.editor.content.getBoundingClientRect(); + var popoverRect = popover.getBoundingClientRect(); + var margin = 20; // account for a scroll bar + var fit = util.insideRect(contentRect, popoverRect, margin); + + if (fit) { + break; + } + } + }.bind(this); + + // when clicking the error icon, expand all nodes towards the invalid + // child node, and set focus to the child node + if (child) { + button.onclick = function showInvalidNode() { + child.findParents().forEach(function (parent) { + parent.expand(false); + }); + + child.scrollTo(function () { + child.focus(); + }); + }; + } + + // apply the error message to the node + while (tdError.firstChild) { + tdError.removeChild(tdError.firstChild); + } + tdError.appendChild(button); + } + else { + if (tdError) { + this.dom.tdError.parentNode.removeChild(this.dom.tdError); + delete this.dom.tdError; + } + } + }; + + /** + * Get the index of this node: the index in the list of childs where this + * node is part of + * @return {number} Returns the index, or -1 if this is the root node + */ + Node.prototype.getIndex = function () { + return this.parent ? this.parent.childs.indexOf(this) : -1; + }; + + /** + * Set parent node + * @param {Node} parent + */ + Node.prototype.setParent = function(parent) { + this.parent = parent; + }; + + /** + * Set field + * @param {String} field + * @param {boolean} [fieldEditable] + */ + Node.prototype.setField = function(field, fieldEditable) { + this.field = field; + this.previousField = field; + this.fieldEditable = (fieldEditable === true); + }; + + /** + * Get field + * @return {String} + */ + Node.prototype.getField = function() { + if (this.field === undefined) { + this._getDomField(); + } + + return this.field; + }; + + /** + * Set value. Value is a JSON structure or an element String, Boolean, etc. + * @param {*} value + * @param {String} [type] Specify the type of the value. Can be 'auto', + * 'array', 'object', or 'string' + */ + Node.prototype.setValue = function(value, type) { + var childValue, child; + + // first clear all current childs (if any) + var childs = this.childs; + if (childs) { + while (childs.length) { + this.removeChild(childs[0]); + } + } + + // TODO: remove the DOM of this Node + + this.type = this._getType(value); + + // check if type corresponds with the provided type + if (type && type != this.type) { + if (type == 'string' && this.type == 'auto') { + this.type = type; + } + else { + throw new Error('Type mismatch: ' + + 'cannot cast value of type "' + this.type + + ' to the specified type "' + type + '"'); + } + } + + if (this.type == 'array') { + // array + this.childs = []; + for (var i = 0, iMax = value.length; i < iMax; i++) { + childValue = value[i]; + if (childValue !== undefined && !(childValue instanceof Function)) { + // ignore undefined and functions + child = new Node(this.editor, { + value: childValue + }); + this.appendChild(child); + } + } + this.value = ''; + } + else if (this.type == 'object') { + // object + this.childs = []; + for (var childField in value) { + if (value.hasOwnProperty(childField)) { + childValue = value[childField]; + if (childValue !== undefined && !(childValue instanceof Function)) { + // ignore undefined and functions + child = new Node(this.editor, { + field: childField, + value: childValue + }); + this.appendChild(child); + } + } + } + this.value = ''; + + // sort object keys + if (this.editor.options.sortObjectKeys === true) { + this.sort('asc'); + } + } + else { + // value + this.childs = undefined; + this.value = value; + } + + this.previousValue = this.value; + }; + + /** + * Get value. Value is a JSON structure + * @return {*} value + */ + Node.prototype.getValue = function() { + //var childs, i, iMax; + + if (this.type == 'array') { + var arr = []; + this.childs.forEach (function (child) { + arr.push(child.getValue()); + }); + return arr; + } + else if (this.type == 'object') { + var obj = {}; + this.childs.forEach (function (child) { + obj[child.getField()] = child.getValue(); + }); + return obj; + } + else { + if (this.value === undefined) { + this._getDomValue(); + } + + return this.value; + } + }; + + /** + * Get the nesting level of this node + * @return {Number} level + */ + Node.prototype.getLevel = function() { + return (this.parent ? this.parent.getLevel() + 1 : 0); + }; + + /** + * Get jsonpath of the current node + * @return {Node[]} Returns an array with nodes + */ + Node.prototype.getNodePath = function () { + var path = this.parent ? this.parent.getNodePath() : []; + path.push(this); + return path; + }; + + /** + * Create a clone of a node + * The complete state of a clone is copied, including whether it is expanded or + * not. The DOM elements are not cloned. + * @return {Node} clone + */ + Node.prototype.clone = function() { + var clone = new Node(this.editor); + clone.type = this.type; + clone.field = this.field; + clone.fieldInnerText = this.fieldInnerText; + clone.fieldEditable = this.fieldEditable; + clone.value = this.value; + clone.valueInnerText = this.valueInnerText; + clone.expanded = this.expanded; + + if (this.childs) { + // an object or array + var cloneChilds = []; + this.childs.forEach(function (child) { + var childClone = child.clone(); + childClone.setParent(clone); + cloneChilds.push(childClone); + }); + clone.childs = cloneChilds; + } + else { + // a value + clone.childs = undefined; + } + + return clone; + }; + + /** + * Expand this node and optionally its childs. + * @param {boolean} [recurse] Optional recursion, true by default. When + * true, all childs will be expanded recursively + */ + Node.prototype.expand = function(recurse) { + if (!this.childs) { + return; + } + + // set this node expanded + this.expanded = true; + if (this.dom.expand) { + this.dom.expand.className = 'jsoneditor-expanded'; + } + + this.showChilds(); + + if (recurse !== false) { + this.childs.forEach(function (child) { + child.expand(recurse); + }); + } + }; + + /** + * Collapse this node and optionally its childs. + * @param {boolean} [recurse] Optional recursion, true by default. When + * true, all childs will be collapsed recursively + */ + Node.prototype.collapse = function(recurse) { + if (!this.childs) { + return; + } + + this.hideChilds(); + + // collapse childs in case of recurse + if (recurse !== false) { + this.childs.forEach(function (child) { + child.collapse(recurse); + }); + + } + + // make this node collapsed + if (this.dom.expand) { + this.dom.expand.className = 'jsoneditor-collapsed'; + } + this.expanded = false; + }; + + /** + * Recursively show all childs when they are expanded + */ + Node.prototype.showChilds = function() { + var childs = this.childs; + if (!childs) { + return; + } + if (!this.expanded) { + return; + } + + var tr = this.dom.tr; + var table = tr ? tr.parentNode : undefined; + if (table) { + // show row with append button + var append = this.getAppend(); + var nextTr = tr.nextSibling; + if (nextTr) { + table.insertBefore(append, nextTr); + } + else { + table.appendChild(append); + } + + // show childs + this.childs.forEach(function (child) { + table.insertBefore(child.getDom(), append); + child.showChilds(); + }); + } + }; + + /** + * Hide the node with all its childs + */ + Node.prototype.hide = function() { + var tr = this.dom.tr; + var table = tr ? tr.parentNode : undefined; + if (table) { + table.removeChild(tr); + } + this.hideChilds(); + }; + + + /** + * Recursively hide all childs + */ + Node.prototype.hideChilds = function() { + var childs = this.childs; + if (!childs) { + return; + } + if (!this.expanded) { + return; + } + + // hide append row + var append = this.getAppend(); + if (append.parentNode) { + append.parentNode.removeChild(append); + } + + // hide childs + this.childs.forEach(function (child) { + child.hide(); + }); + }; + + + /** + * Goes through the path from the node to the root and ensures that it is expanded + */ + Node.prototype.expandTo = function() { + var currentNode = this.parent; + while (currentNode) { + if (!currentNode.expanded) { + currentNode.expand(); + } + currentNode = currentNode.parent; + } + }; + + + /** + * Add a new child to the node. + * Only applicable when Node value is of type array or object + * @param {Node} node + */ + Node.prototype.appendChild = function(node) { + if (this._hasChilds()) { + // adjust the link to the parent + node.setParent(this); + node.fieldEditable = (this.type == 'object'); + if (this.type == 'array') { + node.index = this.childs.length; + } + this.childs.push(node); + + if (this.expanded) { + // insert into the DOM, before the appendRow + var newTr = node.getDom(); + var appendTr = this.getAppend(); + var table = appendTr ? appendTr.parentNode : undefined; + if (appendTr && table) { + table.insertBefore(newTr, appendTr); + } + + node.showChilds(); + } + + this.updateDom({'updateIndexes': true}); + node.updateDom({'recurse': true}); + } + }; + + + /** + * Move a node from its current parent to this node + * Only applicable when Node value is of type array or object + * @param {Node} node + * @param {Node} beforeNode + */ + Node.prototype.moveBefore = function(node, beforeNode) { + if (this._hasChilds()) { + // create a temporary row, to prevent the scroll position from jumping + // when removing the node + var tbody = (this.dom.tr) ? this.dom.tr.parentNode : undefined; + if (tbody) { + var trTemp = document.createElement('tr'); + trTemp.style.height = tbody.clientHeight + 'px'; + tbody.appendChild(trTemp); + } + + if (node.parent) { + node.parent.removeChild(node); + } + + if (beforeNode instanceof AppendNode) { + this.appendChild(node); + } + else { + this.insertBefore(node, beforeNode); + } + + if (tbody) { + tbody.removeChild(trTemp); + } + } + }; + + /** + * Move a node from its current parent to this node + * Only applicable when Node value is of type array or object. + * If index is out of range, the node will be appended to the end + * @param {Node} node + * @param {Number} index + */ + Node.prototype.moveTo = function (node, index) { + if (node.parent == this) { + // same parent + var currentIndex = this.childs.indexOf(node); + if (currentIndex < index) { + // compensate the index for removal of the node itself + index++; + } + } + + var beforeNode = this.childs[index] || this.append; + this.moveBefore(node, beforeNode); + }; + + /** + * Insert a new child before a given node + * Only applicable when Node value is of type array or object + * @param {Node} node + * @param {Node} beforeNode + */ + Node.prototype.insertBefore = function(node, beforeNode) { + if (this._hasChilds()) { + if (beforeNode == this.append) { + // append to the child nodes + + // adjust the link to the parent + node.setParent(this); + node.fieldEditable = (this.type == 'object'); + this.childs.push(node); + } + else { + // insert before a child node + var index = this.childs.indexOf(beforeNode); + if (index == -1) { + throw new Error('Node not found'); + } + + // adjust the link to the parent + node.setParent(this); + node.fieldEditable = (this.type == 'object'); + this.childs.splice(index, 0, node); + } + + if (this.expanded) { + // insert into the DOM + var newTr = node.getDom(); + var nextTr = beforeNode.getDom(); + var table = nextTr ? nextTr.parentNode : undefined; + if (nextTr && table) { + table.insertBefore(newTr, nextTr); + } + + node.showChilds(); + } + + this.updateDom({'updateIndexes': true}); + node.updateDom({'recurse': true}); + } + }; + + /** + * Insert a new child before a given node + * Only applicable when Node value is of type array or object + * @param {Node} node + * @param {Node} afterNode + */ + Node.prototype.insertAfter = function(node, afterNode) { + if (this._hasChilds()) { + var index = this.childs.indexOf(afterNode); + var beforeNode = this.childs[index + 1]; + if (beforeNode) { + this.insertBefore(node, beforeNode); + } + else { + this.appendChild(node); + } + } + }; + + /** + * Search in this node + * The node will be expanded when the text is found one of its childs, else + * it will be collapsed. Searches are case insensitive. + * @param {String} text + * @return {Node[]} results Array with nodes containing the search text + */ + Node.prototype.search = function(text) { + var results = []; + var index; + var search = text ? text.toLowerCase() : undefined; + + // delete old search data + delete this.searchField; + delete this.searchValue; + + // search in field + if (this.field != undefined) { + var field = String(this.field).toLowerCase(); + index = field.indexOf(search); + if (index != -1) { + this.searchField = true; + results.push({ + 'node': this, + 'elem': 'field' + }); + } + + // update dom + this._updateDomField(); + } + + // search in value + if (this._hasChilds()) { + // array, object + + // search the nodes childs + if (this.childs) { + var childResults = []; + this.childs.forEach(function (child) { + childResults = childResults.concat(child.search(text)); + }); + results = results.concat(childResults); + } + + // update dom + if (search != undefined) { + var recurse = false; + if (childResults.length == 0) { + this.collapse(recurse); + } + else { + this.expand(recurse); + } + } + } + else { + // string, auto + if (this.value != undefined ) { + var value = String(this.value).toLowerCase(); + index = value.indexOf(search); + if (index != -1) { + this.searchValue = true; + results.push({ + 'node': this, + 'elem': 'value' + }); + } + } + + // update dom + this._updateDomValue(); + } + + return results; + }; + + /** + * Move the scroll position such that this node is in the visible area. + * The node will not get the focus + * @param {function(boolean)} [callback] + */ + Node.prototype.scrollTo = function(callback) { + if (!this.dom.tr || !this.dom.tr.parentNode) { + // if the node is not visible, expand its parents + var parent = this.parent; + var recurse = false; + while (parent) { + parent.expand(recurse); + parent = parent.parent; + } + } + + if (this.dom.tr && this.dom.tr.parentNode) { + this.editor.scrollTo(this.dom.tr.offsetTop, callback); + } + }; + + + // stores the element name currently having the focus + Node.focusElement = undefined; + + /** + * Set focus to this node + * @param {String} [elementName] The field name of the element to get the + * focus available values: 'drag', 'menu', + * 'expand', 'field', 'value' (default) + */ + Node.prototype.focus = function(elementName) { + Node.focusElement = elementName; + + if (this.dom.tr && this.dom.tr.parentNode) { + var dom = this.dom; + + switch (elementName) { + case 'drag': + if (dom.drag) { + dom.drag.focus(); + } + else { + dom.menu.focus(); + } + break; + + case 'menu': + dom.menu.focus(); + break; + + case 'expand': + if (this._hasChilds()) { + dom.expand.focus(); + } + else if (dom.field && this.fieldEditable) { + dom.field.focus(); + util.selectContentEditable(dom.field); + } + else if (dom.value && !this._hasChilds()) { + dom.value.focus(); + util.selectContentEditable(dom.value); + } + else { + dom.menu.focus(); + } + break; + + case 'field': + if (dom.field && this.fieldEditable) { + dom.field.focus(); + util.selectContentEditable(dom.field); + } + else if (dom.value && !this._hasChilds()) { + dom.value.focus(); + util.selectContentEditable(dom.value); + } + else if (this._hasChilds()) { + dom.expand.focus(); + } + else { + dom.menu.focus(); + } + break; + + case 'value': + default: + if (dom.select) { + // enum select box + dom.select.focus(); + } + else if (dom.value && !this._hasChilds()) { + dom.value.focus(); + util.selectContentEditable(dom.value); + } + else if (dom.field && this.fieldEditable) { + dom.field.focus(); + util.selectContentEditable(dom.field); + } + else if (this._hasChilds()) { + dom.expand.focus(); + } + else { + dom.menu.focus(); + } + break; + } + } + }; + + /** + * Select all text in an editable div after a delay of 0 ms + * @param {Element} editableDiv + */ + Node.select = function(editableDiv) { + setTimeout(function () { + util.selectContentEditable(editableDiv); + }, 0); + }; + + /** + * Update the values from the DOM field and value of this node + */ + Node.prototype.blur = function() { + // retrieve the actual field and value from the DOM. + this._getDomValue(false); + this._getDomField(false); + }; + + /** + * Check if given node is a child. The method will check recursively to find + * this node. + * @param {Node} node + * @return {boolean} containsNode + */ + Node.prototype.containsNode = function(node) { + if (this == node) { + return true; + } + + var childs = this.childs; + if (childs) { + // TODO: use the js5 Array.some() here? + for (var i = 0, iMax = childs.length; i < iMax; i++) { + if (childs[i].containsNode(node)) { + return true; + } + } + } + + return false; + }; + + /** + * Move given node into this node + * @param {Node} node the childNode to be moved + * @param {Node} beforeNode node will be inserted before given + * node. If no beforeNode is given, + * the node is appended at the end + * @private + */ + Node.prototype._move = function(node, beforeNode) { + if (node == beforeNode) { + // nothing to do... + return; + } + + // check if this node is not a child of the node to be moved here + if (node.containsNode(this)) { + throw new Error('Cannot move a field into a child of itself'); + } + + // remove the original node + if (node.parent) { + node.parent.removeChild(node); + } + + // create a clone of the node + var clone = node.clone(); + node.clearDom(); + + // insert or append the node + if (beforeNode) { + this.insertBefore(clone, beforeNode); + } + else { + this.appendChild(clone); + } + + /* TODO: adjust the field name (to prevent equal field names) + if (this.type == 'object') { + } + */ + }; + + /** + * Remove a child from the node. + * Only applicable when Node value is of type array or object + * @param {Node} node The child node to be removed; + * @return {Node | undefined} node The removed node on success, + * else undefined + */ + Node.prototype.removeChild = function(node) { + if (this.childs) { + var index = this.childs.indexOf(node); + + if (index != -1) { + node.hide(); + + // delete old search results + delete node.searchField; + delete node.searchValue; + + var removedNode = this.childs.splice(index, 1)[0]; + removedNode.parent = null; + + this.updateDom({'updateIndexes': true}); + + return removedNode; + } + } + + return undefined; + }; + + /** + * Remove a child node node from this node + * This method is equal to Node.removeChild, except that _remove fire an + * onChange event. + * @param {Node} node + * @private + */ + Node.prototype._remove = function (node) { + this.removeChild(node); + }; + + /** + * Change the type of the value of this Node + * @param {String} newType + */ + Node.prototype.changeType = function (newType) { + var oldType = this.type; + + if (oldType == newType) { + // type is not changed + return; + } + + if ((newType == 'string' || newType == 'auto') && + (oldType == 'string' || oldType == 'auto')) { + // this is an easy change + this.type = newType; + } + else { + // change from array to object, or from string/auto to object/array + var table = this.dom.tr ? this.dom.tr.parentNode : undefined; + var lastTr; + if (this.expanded) { + lastTr = this.getAppend(); + } + else { + lastTr = this.getDom(); + } + var nextTr = (lastTr && lastTr.parentNode) ? lastTr.nextSibling : undefined; + + // hide current field and all its childs + this.hide(); + this.clearDom(); + + // adjust the field and the value + this.type = newType; + + // adjust childs + if (newType == 'object') { + if (!this.childs) { + this.childs = []; + } + + this.childs.forEach(function (child, index) { + child.clearDom(); + delete child.index; + child.fieldEditable = true; + if (child.field == undefined) { + child.field = ''; + } + }); + + if (oldType == 'string' || oldType == 'auto') { + this.expanded = true; + } + } + else if (newType == 'array') { + if (!this.childs) { + this.childs = []; + } + + this.childs.forEach(function (child, index) { + child.clearDom(); + child.fieldEditable = false; + child.index = index; + }); + + if (oldType == 'string' || oldType == 'auto') { + this.expanded = true; + } + } + else { + this.expanded = false; + } + + // create new DOM + if (table) { + if (nextTr) { + table.insertBefore(this.getDom(), nextTr); + } + else { + table.appendChild(this.getDom()); + } + } + this.showChilds(); + } + + if (newType == 'auto' || newType == 'string') { + // cast value to the correct type + if (newType == 'string') { + this.value = String(this.value); + } + else { + this.value = this._stringCast(String(this.value)); + } + + this.focus(); + } + + this.updateDom({'updateIndexes': true}); + }; + + /** + * Retrieve value from DOM + * @param {boolean} [silent] If true (default), no errors will be thrown in + * case of invalid data + * @private + */ + Node.prototype._getDomValue = function(silent) { + if (this.dom.value && this.type != 'array' && this.type != 'object') { + this.valueInnerText = util.getInnerText(this.dom.value); + } + + if (this.valueInnerText != undefined) { + try { + // retrieve the value + var value; + if (this.type == 'string') { + value = this._unescapeHTML(this.valueInnerText); + } + else { + var str = this._unescapeHTML(this.valueInnerText); + value = this._stringCast(str); + } + if (value !== this.value) { + this.value = value; + this._debouncedOnChangeValue(); + } + } + catch (err) { + this.value = undefined; + // TODO: sent an action with the new, invalid value? + if (silent !== true) { + throw err; + } + } + } + }; + + /** + * Handle a changed value + * @private + */ + Node.prototype._onChangeValue = function () { + // get current selection, then override the range such that we can select + // the added/removed text on undo/redo + var oldSelection = this.editor.getSelection(); + if (oldSelection.range) { + var undoDiff = util.textDiff(String(this.value), String(this.previousValue)); + oldSelection.range.startOffset = undoDiff.start; + oldSelection.range.endOffset = undoDiff.end; + } + var newSelection = this.editor.getSelection(); + if (newSelection.range) { + var redoDiff = util.textDiff(String(this.previousValue), String(this.value)); + newSelection.range.startOffset = redoDiff.start; + newSelection.range.endOffset = redoDiff.end; + } + + this.editor._onAction('editValue', { + node: this, + oldValue: this.previousValue, + newValue: this.value, + oldSelection: oldSelection, + newSelection: newSelection + }); + + this.previousValue = this.value; + }; + + /** + * Handle a changed field + * @private + */ + Node.prototype._onChangeField = function () { + // get current selection, then override the range such that we can select + // the added/removed text on undo/redo + var oldSelection = this.editor.getSelection(); + if (oldSelection.range) { + var undoDiff = util.textDiff(this.field, this.previousField); + oldSelection.range.startOffset = undoDiff.start; + oldSelection.range.endOffset = undoDiff.end; + } + var newSelection = this.editor.getSelection(); + if (newSelection.range) { + var redoDiff = util.textDiff(this.previousField, this.field); + newSelection.range.startOffset = redoDiff.start; + newSelection.range.endOffset = redoDiff.end; + } + + this.editor._onAction('editField', { + node: this, + oldValue: this.previousField, + newValue: this.field, + oldSelection: oldSelection, + newSelection: newSelection + }); + + this.previousField = this.field; + }; + + /** + * Update dom value: + * - the text color of the value, depending on the type of the value + * - the height of the field, depending on the width + * - background color in case it is empty + * @private + */ + Node.prototype._updateDomValue = function () { + var domValue = this.dom.value; + if (domValue) { + var classNames = ['jsoneditor-value']; + + + // set text color depending on value type + var value = this.value; + var type = (this.type == 'auto') ? util.type(value) : this.type; + var isUrl = type == 'string' && util.isUrl(value); + classNames.push('jsoneditor-' + type); + if (isUrl) { + classNames.push('jsoneditor-url'); + } + + // visual styling when empty + var isEmpty = (String(this.value) == '' && this.type != 'array' && this.type != 'object'); + if (isEmpty) { + classNames.push('jsoneditor-empty'); + } + + // highlight when there is a search result + if (this.searchValueActive) { + classNames.push('jsoneditor-highlight-active'); + } + if (this.searchValue) { + classNames.push('jsoneditor-highlight'); + } + + domValue.className = classNames.join(' '); + + // update title + if (type == 'array' || type == 'object') { + var count = this.childs ? this.childs.length : 0; + domValue.title = this.type + ' containing ' + count + ' items'; + } + else if (isUrl && this.editable.value) { + domValue.title = 'Ctrl+Click or Ctrl+Enter to open url in new window'; + } + else { + domValue.title = ''; + } + + // show checkbox when the value is a boolean + if (type === 'boolean' && this.editable.value) { + if (!this.dom.checkbox) { + this.dom.checkbox = document.createElement('input'); + this.dom.checkbox.type = 'checkbox'; + this.dom.tdCheckbox = document.createElement('td'); + this.dom.tdCheckbox.className = 'jsoneditor-tree'; + this.dom.tdCheckbox.appendChild(this.dom.checkbox); + + this.dom.tdValue.parentNode.insertBefore(this.dom.tdCheckbox, this.dom.tdValue); + } + + this.dom.checkbox.checked = this.value; + } + else { + // cleanup checkbox when displayed + if (this.dom.tdCheckbox) { + this.dom.tdCheckbox.parentNode.removeChild(this.dom.tdCheckbox); + delete this.dom.tdCheckbox; + delete this.dom.checkbox; + } + } + + if (this.enum && this.editable.value) { + // create select box when this node has an enum object + if (!this.dom.select) { + this.dom.select = document.createElement('select'); + this.id = this.field + "_" + new Date().getUTCMilliseconds(); + this.dom.select.id = this.id; + this.dom.select.name = this.dom.select.id; + + //Create the default empty option + this.dom.select.option = document.createElement('option'); + this.dom.select.option.value = ''; + this.dom.select.option.innerHTML = '--'; + this.dom.select.appendChild(this.dom.select.option); + + //Iterate all enum values and add them as options + for(var i = 0; i < this.enum.length; i++) { + this.dom.select.option = document.createElement('option'); + this.dom.select.option.value = this.enum[i]; + this.dom.select.option.innerHTML = this.enum[i]; + if(this.dom.select.option.value == this.value){ + this.dom.select.option.selected = true; + } + this.dom.select.appendChild(this.dom.select.option); + } + + this.dom.tdSelect = document.createElement('td'); + this.dom.tdSelect.className = 'jsoneditor-tree'; + this.dom.tdSelect.appendChild(this.dom.select); + this.dom.tdValue.parentNode.insertBefore(this.dom.tdSelect, this.dom.tdValue); + } + + // If the enum is inside a composite type display + // both the simple input and the dropdown field + if(this.schema && ( + !this.schema.hasOwnProperty("oneOf") && + !this.schema.hasOwnProperty("anyOf") && + !this.schema.hasOwnProperty("allOf")) + ) { + this.valueFieldHTML = this.dom.tdValue.innerHTML; + this.dom.tdValue.style.visibility = 'hidden'; + this.dom.tdValue.innerHTML = ''; + } else { + delete this.valueFieldHTML; + } + } + else { + // cleanup select box when displayed + if (this.dom.tdSelect) { + this.dom.tdSelect.parentNode.removeChild(this.dom.tdSelect); + delete this.dom.tdSelect; + delete this.dom.select; + this.dom.tdValue.innerHTML = this.valueFieldHTML; + this.dom.tdValue.style.visibility = ''; + delete this.valueFieldHTML; + } + } + + // strip formatting from the contents of the editable div + util.stripFormatting(domValue); + } + }; + + /** + * Update dom field: + * - the text color of the field, depending on the text + * - the height of the field, depending on the width + * - background color in case it is empty + * @private + */ + Node.prototype._updateDomField = function () { + var domField = this.dom.field; + if (domField) { + // make backgound color lightgray when empty + var isEmpty = (String(this.field) == '' && this.parent.type != 'array'); + if (isEmpty) { + util.addClassName(domField, 'jsoneditor-empty'); + } + else { + util.removeClassName(domField, 'jsoneditor-empty'); + } + + // highlight when there is a search result + if (this.searchFieldActive) { + util.addClassName(domField, 'jsoneditor-highlight-active'); + } + else { + util.removeClassName(domField, 'jsoneditor-highlight-active'); + } + if (this.searchField) { + util.addClassName(domField, 'jsoneditor-highlight'); + } + else { + util.removeClassName(domField, 'jsoneditor-highlight'); + } + + // strip formatting from the contents of the editable div + util.stripFormatting(domField); + } + }; + + /** + * Retrieve field from DOM + * @param {boolean} [silent] If true (default), no errors will be thrown in + * case of invalid data + * @private + */ + Node.prototype._getDomField = function(silent) { + if (this.dom.field && this.fieldEditable) { + this.fieldInnerText = util.getInnerText(this.dom.field); + } + + if (this.fieldInnerText != undefined) { + try { + var field = this._unescapeHTML(this.fieldInnerText); + + if (field !== this.field) { + this.field = field; + this._debouncedOnChangeField(); + } + } + catch (err) { + this.field = undefined; + // TODO: sent an action here, with the new, invalid value? + if (silent !== true) { + throw err; + } + } + } + }; + + /** + * Validate this node and all it's childs + * @return {Array.<{node: Node, error: {message: string}}>} Returns a list with duplicates + */ + Node.prototype.validate = function () { + var errors = []; + + // find duplicate keys + if (this.type === 'object') { + var keys = {}; + var duplicateKeys = []; + for (var i = 0; i < this.childs.length; i++) { + var child = this.childs[i]; + if (keys.hasOwnProperty(child.field)) { + duplicateKeys.push(child.field); + } + keys[child.field] = true; + } + + if (duplicateKeys.length > 0) { + errors = this.childs + .filter(function (node) { + return duplicateKeys.indexOf(node.field) !== -1; + }) + .map(function (node) { + return { + node: node, + error: { + message: 'duplicate key "' + node.field + '"' + } + } + }); + } + } + + // recurse over the childs + if (this.childs) { + for (var i = 0; i < this.childs.length; i++) { + var e = this.childs[i].validate(); + if (e.length > 0) { + errors = errors.concat(e); + } + } + } + + return errors; + }; + + /** + * Clear the dom of the node + */ + Node.prototype.clearDom = function() { + // TODO: hide the node first? + //this.hide(); + // TODO: recursively clear dom? + + this.dom = {}; + }; + + /** + * Get the HTML DOM TR element of the node. + * The dom will be generated when not yet created + * @return {Element} tr HTML DOM TR Element + */ + Node.prototype.getDom = function() { + var dom = this.dom; + if (dom.tr) { + return dom.tr; + } + + this._updateEditability(); + + // create row + dom.tr = document.createElement('tr'); + dom.tr.node = this; + + if (this.editor.options.mode === 'tree') { // note: we take here the global setting + var tdDrag = document.createElement('td'); + if (this.editable.field) { + // create draggable area + if (this.parent) { + var domDrag = document.createElement('button'); + domDrag.type = 'button'; + dom.drag = domDrag; + domDrag.className = 'jsoneditor-dragarea'; + domDrag.title = 'Drag to move this field (Alt+Shift+Arrows)'; + tdDrag.appendChild(domDrag); + } + } + dom.tr.appendChild(tdDrag); + + // create context menu + var tdMenu = document.createElement('td'); + var menu = document.createElement('button'); + menu.type = 'button'; + dom.menu = menu; + menu.className = 'jsoneditor-contextmenu'; + menu.title = 'Click to open the actions menu (Ctrl+M)'; + tdMenu.appendChild(dom.menu); + dom.tr.appendChild(tdMenu); + } + + // create tree and field + var tdField = document.createElement('td'); + dom.tr.appendChild(tdField); + dom.tree = this._createDomTree(); + tdField.appendChild(dom.tree); + + this.updateDom({'updateIndexes': true}); + + return dom.tr; + }; + + /** + * DragStart event, fired on mousedown on the dragarea at the left side of a Node + * @param {Node[] | Node} nodes + * @param {Event} event + */ + Node.onDragStart = function (nodes, event) { + if (!Array.isArray(nodes)) { + return Node.onDragStart([nodes], event); + } + if (nodes.length === 0) { + return; + } + + var firstNode = nodes[0]; + var lastNode = nodes[nodes.length - 1]; + var draggedNode = Node.getNodeFromTarget(event.target); + var beforeNode = lastNode._nextSibling(); + var editor = firstNode.editor; + + // in case of multiple selected nodes, offsetY prevents the selection from + // jumping when you start dragging one of the lower down nodes in the selection + var offsetY = util.getAbsoluteTop(draggedNode.dom.tr) - util.getAbsoluteTop(firstNode.dom.tr); + + if (!editor.mousemove) { + editor.mousemove = util.addEventListener(window, 'mousemove', function (event) { + Node.onDrag(nodes, event); + }); + } + + if (!editor.mouseup) { + editor.mouseup = util.addEventListener(window, 'mouseup',function (event ) { + Node.onDragEnd(nodes, event); + }); + } + + editor.highlighter.lock(); + editor.drag = { + oldCursor: document.body.style.cursor, + oldSelection: editor.getSelection(), + oldBeforeNode: beforeNode, + mouseX: event.pageX, + offsetY: offsetY, + level: firstNode.getLevel() + }; + document.body.style.cursor = 'move'; + + event.preventDefault(); + }; + + /** + * Drag event, fired when moving the mouse while dragging a Node + * @param {Node[] | Node} nodes + * @param {Event} event + */ + Node.onDrag = function (nodes, event) { + if (!Array.isArray(nodes)) { + return Node.onDrag([nodes], event); + } + if (nodes.length === 0) { + return; + } + + // TODO: this method has grown too large. Split it in a number of methods + var editor = nodes[0].editor; + var mouseY = event.pageY - editor.drag.offsetY; + var mouseX = event.pageX; + var trThis, trPrev, trNext, trFirst, trLast, trRoot; + var nodePrev, nodeNext; + var topThis, topPrev, topFirst, heightThis, bottomNext, heightNext; + var moved = false; + + // TODO: add an ESC option, which resets to the original position + + // move up/down + var firstNode = nodes[0]; + trThis = firstNode.dom.tr; + topThis = util.getAbsoluteTop(trThis); + heightThis = trThis.offsetHeight; + if (mouseY < topThis) { + // move up + trPrev = trThis; + do { + trPrev = trPrev.previousSibling; + nodePrev = Node.getNodeFromTarget(trPrev); + topPrev = trPrev ? util.getAbsoluteTop(trPrev) : 0; + } + while (trPrev && mouseY < topPrev); + + if (nodePrev && !nodePrev.parent) { + nodePrev = undefined; + } + + if (!nodePrev) { + // move to the first node + trRoot = trThis.parentNode.firstChild; + trPrev = trRoot ? trRoot.nextSibling : undefined; + nodePrev = Node.getNodeFromTarget(trPrev); + if (nodePrev == firstNode) { + nodePrev = undefined; + } + } + + if (nodePrev) { + // check if mouseY is really inside the found node + trPrev = nodePrev.dom.tr; + topPrev = trPrev ? util.getAbsoluteTop(trPrev) : 0; + if (mouseY > topPrev + heightThis) { + nodePrev = undefined; + } + } + + if (nodePrev) { + nodes.forEach(function (node) { + nodePrev.parent.moveBefore(node, nodePrev); + }); + moved = true; + } + } + else { + // move down + var lastNode = nodes[nodes.length - 1]; + trLast = (lastNode.expanded && lastNode.append) ? lastNode.append.getDom() : lastNode.dom.tr; + trFirst = trLast ? trLast.nextSibling : undefined; + if (trFirst) { + topFirst = util.getAbsoluteTop(trFirst); + trNext = trFirst; + do { + nodeNext = Node.getNodeFromTarget(trNext); + if (trNext) { + bottomNext = trNext.nextSibling ? + util.getAbsoluteTop(trNext.nextSibling) : 0; + heightNext = trNext ? (bottomNext - topFirst) : 0; + + if (nodeNext.parent.childs.length == nodes.length && + nodeNext.parent.childs[nodes.length - 1] == lastNode) { + // We are about to remove the last child of this parent, + // which will make the parents appendNode visible. + topThis += 27; + // TODO: dangerous to suppose the height of the appendNode a constant of 27 px. + } + } + + trNext = trNext.nextSibling; + } + while (trNext && mouseY > topThis + heightNext); + + if (nodeNext && nodeNext.parent) { + // calculate the desired level + var diffX = (mouseX - editor.drag.mouseX); + var diffLevel = Math.round(diffX / 24 / 2); + var level = editor.drag.level + diffLevel; // desired level + var levelNext = nodeNext.getLevel(); // level to be + + // find the best fitting level (move upwards over the append nodes) + trPrev = nodeNext.dom.tr.previousSibling; + while (levelNext < level && trPrev) { + nodePrev = Node.getNodeFromTarget(trPrev); + + var isDraggedNode = nodes.some(function (node) { + return node === nodePrev || nodePrev._isChildOf(node); + }); + + if (isDraggedNode) { + // neglect the dragged nodes themselves and their childs + } + else if (nodePrev instanceof AppendNode) { + var childs = nodePrev.parent.childs; + if (childs.length != nodes.length || childs[nodes.length - 1] != lastNode) { + // non-visible append node of a list of childs + // consisting of not only this node (else the + // append node will change into a visible "empty" + // text when removing this node). + nodeNext = Node.getNodeFromTarget(trPrev); + levelNext = nodeNext.getLevel(); + } + else { + break; + } + } + else { + break; + } + + trPrev = trPrev.previousSibling; + } + + // move the node when its position is changed + if (trLast.nextSibling != nodeNext.dom.tr) { + nodes.forEach(function (node) { + nodeNext.parent.moveBefore(node, nodeNext); + }); + moved = true; + } + } + } + } + + if (moved) { + // update the dragging parameters when moved + editor.drag.mouseX = mouseX; + editor.drag.level = firstNode.getLevel(); + } + + // auto scroll when hovering around the top of the editor + editor.startAutoScroll(mouseY); + + event.preventDefault(); + }; + + /** + * Drag event, fired on mouseup after having dragged a node + * @param {Node[] | Node} nodes + * @param {Event} event + */ + Node.onDragEnd = function (nodes, event) { + if (!Array.isArray(nodes)) { + return Node.onDrag([nodes], event); + } + if (nodes.length === 0) { + return; + } + + var firstNode = nodes[0]; + var editor = firstNode.editor; + var parent = firstNode.parent; + var firstIndex = parent.childs.indexOf(firstNode); + var beforeNode = parent.childs[firstIndex + nodes.length] || parent.append; + + // set focus to the context menu button of the first node + if (nodes[0]) { + nodes[0].dom.menu.focus(); + } + + var params = { + nodes: nodes, + oldSelection: editor.drag.oldSelection, + newSelection: editor.getSelection(), + oldBeforeNode: editor.drag.oldBeforeNode, + newBeforeNode: beforeNode + }; + + if (params.oldBeforeNode != params.newBeforeNode) { + // only register this action if the node is actually moved to another place + editor._onAction('moveNodes', params); + } + + document.body.style.cursor = editor.drag.oldCursor; + editor.highlighter.unlock(); + nodes.forEach(function (node) { + if (event.target !== node.dom.drag && event.target !== node.dom.menu) { + editor.highlighter.unhighlight(); + } + }); + delete editor.drag; + + if (editor.mousemove) { + util.removeEventListener(window, 'mousemove', editor.mousemove); + delete editor.mousemove; + } + if (editor.mouseup) { + util.removeEventListener(window, 'mouseup', editor.mouseup); + delete editor.mouseup; + } + + // Stop any running auto scroll + editor.stopAutoScroll(); + + event.preventDefault(); + }; + + /** + * Test if this node is a child of an other node + * @param {Node} node + * @return {boolean} isChild + * @private + */ + Node.prototype._isChildOf = function (node) { + var n = this.parent; + while (n) { + if (n == node) { + return true; + } + n = n.parent; + } + + return false; + }; + + /** + * Create an editable field + * @return {Element} domField + * @private + */ + Node.prototype._createDomField = function () { + return document.createElement('div'); + }; + + /** + * Set highlighting for this node and all its childs. + * Only applied to the currently visible (expanded childs) + * @param {boolean} highlight + */ + Node.prototype.setHighlight = function (highlight) { + if (this.dom.tr) { + if (highlight) { + util.addClassName(this.dom.tr, 'jsoneditor-highlight'); + } + else { + util.removeClassName(this.dom.tr, 'jsoneditor-highlight'); + } + + if (this.append) { + this.append.setHighlight(highlight); + } + + if (this.childs) { + this.childs.forEach(function (child) { + child.setHighlight(highlight); + }); + } + } + }; + + /** + * Select or deselect a node + * @param {boolean} selected + * @param {boolean} [isFirst] + */ + Node.prototype.setSelected = function (selected, isFirst) { + this.selected = selected; + + if (this.dom.tr) { + if (selected) { + util.addClassName(this.dom.tr, 'jsoneditor-selected'); + } + else { + util.removeClassName(this.dom.tr, 'jsoneditor-selected'); + } + + if (isFirst) { + util.addClassName(this.dom.tr, 'jsoneditor-first'); + } + else { + util.removeClassName(this.dom.tr, 'jsoneditor-first'); + } + + if (this.append) { + this.append.setSelected(selected); + } + + if (this.childs) { + this.childs.forEach(function (child) { + child.setSelected(selected); + }); + } + } + }; + + /** + * Update the value of the node. Only primitive types are allowed, no Object + * or Array is allowed. + * @param {String | Number | Boolean | null} value + */ + Node.prototype.updateValue = function (value) { + this.value = value; + this.updateDom(); + }; + + /** + * Update the field of the node. + * @param {String} field + */ + Node.prototype.updateField = function (field) { + this.field = field; + this.updateDom(); + }; + + /** + * Update the HTML DOM, optionally recursing through the childs + * @param {Object} [options] Available parameters: + * {boolean} [recurse] If true, the + * DOM of the childs will be updated recursively. + * False by default. + * {boolean} [updateIndexes] If true, the childs + * indexes of the node will be updated too. False by + * default. + */ + Node.prototype.updateDom = function (options) { + // update level indentation + var domTree = this.dom.tree; + if (domTree) { + domTree.style.marginLeft = this.getLevel() * 24 + 'px'; + } + + // apply field to DOM + var domField = this.dom.field; + if (domField) { + if (this.fieldEditable) { + // parent is an object + domField.contentEditable = this.editable.field; + domField.spellcheck = false; + domField.className = 'jsoneditor-field'; + } + else { + // parent is an array this is the root node + domField.className = 'jsoneditor-readonly'; + } + + var fieldText; + if (this.index != undefined) { + fieldText = this.index; + } + else if (this.field != undefined) { + fieldText = this.field; + } + else if (this._hasChilds()) { + fieldText = this.type; + } + else { + fieldText = ''; + } + domField.innerHTML = this._escapeHTML(fieldText); + + this._updateSchema(); + } + + // apply value to DOM + var domValue = this.dom.value; + if (domValue) { + var count = this.childs ? this.childs.length : 0; + if (this.type == 'array') { + domValue.innerHTML = '[' + count + ']'; + util.addClassName(this.dom.tr, 'jsoneditor-expandable'); + } + else if (this.type == 'object') { + domValue.innerHTML = '{' + count + '}'; + util.addClassName(this.dom.tr, 'jsoneditor-expandable'); + } + else { + domValue.innerHTML = this._escapeHTML(this.value); + util.removeClassName(this.dom.tr, 'jsoneditor-expandable'); + } + } + + // update field and value + this._updateDomField(); + this._updateDomValue(); + + // update childs indexes + if (options && options.updateIndexes === true) { + // updateIndexes is true or undefined + this._updateDomIndexes(); + } + + if (options && options.recurse === true) { + // recurse is true or undefined. update childs recursively + if (this.childs) { + this.childs.forEach(function (child) { + child.updateDom(options); + }); + } + } + + // update row with append button + if (this.append) { + this.append.updateDom(); + } + }; + + /** + * Locate the JSON schema of the node and check for any enum type + * @private + */ + Node.prototype._updateSchema = function () { + //Locating the schema of the node and checking for any enum type + if(this.editor && this.editor.options) { + // find the part of the json schema matching this nodes path + this.schema = this.editor.options.schema + ? Node._findSchema(this.editor.options.schema, this.getPath()) + : null; + if (this.schema) { + this.enum = Node._findEnum(this.schema); + } + else { + delete this.enum; + } + } + }; + + /** + * find an enum definition in a JSON schema, as property `enum` or inside + * one of the schemas composites (`oneOf`, `anyOf`, `allOf`) + * @param {Object} schema + * @return {Array | null} Returns the enum when found, null otherwise. + * @private + */ + Node._findEnum = function (schema) { + if (schema.enum) { + return schema.enum; + } + + var composite = schema.oneOf || schema.anyOf || schema.allOf; + if (composite) { + var match = composite.filter(function (entry) {return entry.enum}); + if (match.length > 0) { + return match[0].enum; + } + } + + return null + }; + + /** + * Return the part of a JSON schema matching given path. + * @param {Object} schema + * @param {Array.} path + * @return {Object | null} + * @private + */ + Node._findSchema = function (schema, path) { + var childSchema = schema; + var foundSchema = childSchema; + + var allSchemas = schema.oneOf || schema.anyOf || schema.allOf; + if (!allSchemas) { + allSchemas = [schema]; + } + + for (var j = 0; j < allSchemas.length; j++) { + childSchema = allSchemas[j]; + + for (var i = 0; i < path.length && childSchema; i++) { + var key = path[i]; + + if (typeof key === 'string' && childSchema.patternProperties && i == path.length - 1) { + for (var prop in childSchema.patternProperties) { + foundSchema = Node._findSchema(childSchema.patternProperties[prop], path.slice(i, path.length)); + } + } + else if (childSchema.items && childSchema.items.properties) { + childSchema = childSchema.items.properties[key]; + if (childSchema) { + foundSchema = Node._findSchema(childSchema, path.slice(i, path.length)); + } + } + else if (typeof key === 'string' && childSchema.properties) { + childSchema = childSchema.properties[key] || null; + if (childSchema) { + foundSchema = Node._findSchema(childSchema, path.slice(i, path.length)); + } + } + else if (typeof key === 'number' && childSchema.items) { + childSchema = childSchema.items; + if (childSchema) { + foundSchema = Node._findSchema(childSchema, path.slice(i, path.length)); + } + } + } + + } + return foundSchema + }; + + /** + * Update the DOM of the childs of a node: update indexes and undefined field + * names. + * Only applicable when structure is an array or object + * @private + */ + Node.prototype._updateDomIndexes = function () { + var domValue = this.dom.value; + var childs = this.childs; + if (domValue && childs) { + if (this.type == 'array') { + childs.forEach(function (child, index) { + child.index = index; + var childField = child.dom.field; + if (childField) { + childField.innerHTML = index; + } + }); + } + else if (this.type == 'object') { + childs.forEach(function (child) { + if (child.index != undefined) { + delete child.index; + + if (child.field == undefined) { + child.field = ''; + } + } + }); + } + } + }; + + /** + * Create an editable value + * @private + */ + Node.prototype._createDomValue = function () { + var domValue; + + if (this.type == 'array') { + domValue = document.createElement('div'); + domValue.innerHTML = '[...]'; + } + else if (this.type == 'object') { + domValue = document.createElement('div'); + domValue.innerHTML = '{...}'; + } + else { + if (!this.editable.value && util.isUrl(this.value)) { + // create a link in case of read-only editor and value containing an url + domValue = document.createElement('a'); + domValue.href = this.value; + domValue.target = '_blank'; + domValue.innerHTML = this._escapeHTML(this.value); + } + else { + // create an editable or read-only div + domValue = document.createElement('div'); + domValue.contentEditable = this.editable.value; + domValue.spellcheck = false; + domValue.innerHTML = this._escapeHTML(this.value); + } + } + + return domValue; + }; + + /** + * Create an expand/collapse button + * @return {Element} expand + * @private + */ + Node.prototype._createDomExpandButton = function () { + // create expand button + var expand = document.createElement('button'); + expand.type = 'button'; + if (this._hasChilds()) { + expand.className = this.expanded ? 'jsoneditor-expanded' : 'jsoneditor-collapsed'; + expand.title = + 'Click to expand/collapse this field (Ctrl+E). \n' + + 'Ctrl+Click to expand/collapse including all childs.'; + } + else { + expand.className = 'jsoneditor-invisible'; + expand.title = ''; + } + + return expand; + }; + + + /** + * Create a DOM tree element, containing the expand/collapse button + * @return {Element} domTree + * @private + */ + Node.prototype._createDomTree = function () { + var dom = this.dom; + var domTree = document.createElement('table'); + var tbody = document.createElement('tbody'); + domTree.style.borderCollapse = 'collapse'; // TODO: put in css + domTree.className = 'jsoneditor-values'; + domTree.appendChild(tbody); + var tr = document.createElement('tr'); + tbody.appendChild(tr); + + // create expand button + var tdExpand = document.createElement('td'); + tdExpand.className = 'jsoneditor-tree'; + tr.appendChild(tdExpand); + dom.expand = this._createDomExpandButton(); + tdExpand.appendChild(dom.expand); + dom.tdExpand = tdExpand; + + // create the field + var tdField = document.createElement('td'); + tdField.className = 'jsoneditor-tree'; + tr.appendChild(tdField); + dom.field = this._createDomField(); + tdField.appendChild(dom.field); + dom.tdField = tdField; + + // create a separator + var tdSeparator = document.createElement('td'); + tdSeparator.className = 'jsoneditor-tree'; + tr.appendChild(tdSeparator); + if (this.type != 'object' && this.type != 'array') { + tdSeparator.appendChild(document.createTextNode(':')); + tdSeparator.className = 'jsoneditor-separator'; + } + dom.tdSeparator = tdSeparator; + + // create the value + var tdValue = document.createElement('td'); + tdValue.className = 'jsoneditor-tree'; + tr.appendChild(tdValue); + dom.value = this._createDomValue(); + tdValue.appendChild(dom.value); + dom.tdValue = tdValue; + + return domTree; + }; + + /** + * Handle an event. The event is caught centrally by the editor + * @param {Event} event + */ + Node.prototype.onEvent = function (event) { + var type = event.type, + target = event.target || event.srcElement, + dom = this.dom, + node = this, + expandable = this._hasChilds(); + + // check if mouse is on menu or on dragarea. + // If so, highlight current row and its childs + if (target == dom.drag || target == dom.menu) { + if (type == 'mouseover') { + this.editor.highlighter.highlight(this); + } + else if (type == 'mouseout') { + this.editor.highlighter.unhighlight(); + } + } + + // context menu events + if (type == 'click' && target == dom.menu) { + var highlighter = node.editor.highlighter; + highlighter.highlight(node); + highlighter.lock(); + util.addClassName(dom.menu, 'jsoneditor-selected'); + this.showContextMenu(dom.menu, function () { + util.removeClassName(dom.menu, 'jsoneditor-selected'); + highlighter.unlock(); + highlighter.unhighlight(); + }); + } + + // expand events + if (type == 'click') { + if (target == dom.expand || + ((node.editor.options.mode === 'view' || node.editor.options.mode === 'form') && target.nodeName === 'DIV')) { + if (expandable) { + var recurse = event.ctrlKey; // with ctrl-key, expand/collapse all + this._onExpand(recurse); + } + } + } + + // swap the value of a boolean when the checkbox displayed left is clicked + if (type == 'change' && target == dom.checkbox) { + this.dom.value.innerHTML = !this.value; + this._getDomValue(); + } + + // update the value of the node based on the selected option + if (type == 'change' && target == dom.select) { + this.dom.value.innerHTML = dom.select.value; + this._getDomValue(); + this._updateDomValue(); + } + + // value events + var domValue = dom.value; + if (target == domValue) { + //noinspection FallthroughInSwitchStatementJS + switch (type) { + case 'blur': + case 'change': + this._getDomValue(true); + this._updateDomValue(); + if (this.value) { + domValue.innerHTML = this._escapeHTML(this.value); + } + break; + + case 'input': + //this._debouncedGetDomValue(true); // TODO + this._getDomValue(true); + this._updateDomValue(); + break; + + case 'keydown': + case 'mousedown': + // TODO: cleanup + this.editor.selection = this.editor.getSelection(); + break; + + case 'click': + if (event.ctrlKey && this.editable.value) { + // if read-only, we use the regular click behavior of an anchor + if (util.isUrl(this.value)) { + event.preventDefault(); + window.open(this.value, '_blank'); + } + } + break; + + case 'keyup': + //this._debouncedGetDomValue(true); // TODO + this._getDomValue(true); + this._updateDomValue(); + break; + + case 'cut': + case 'paste': + setTimeout(function () { + node._getDomValue(true); + node._updateDomValue(); + }, 1); + break; + } + } + + // field events + var domField = dom.field; + if (target == domField) { + switch (type) { + case 'blur': + case 'change': + this._getDomField(true); + this._updateDomField(); + if (this.field) { + domField.innerHTML = this._escapeHTML(this.field); + } + break; + + case 'input': + this._getDomField(true); + this._updateSchema(); + this._updateDomField(); + this._updateDomValue(); + break; + + case 'keydown': + case 'mousedown': + this.editor.selection = this.editor.getSelection(); + break; + + case 'keyup': + this._getDomField(true); + this._updateDomField(); + break; + + case 'cut': + case 'paste': + setTimeout(function () { + node._getDomField(true); + node._updateDomField(); + }, 1); + break; + } + } + + // focus + // when clicked in whitespace left or right from the field or value, set focus + var domTree = dom.tree; + if (target == domTree.parentNode && type == 'click' && !event.hasMoved) { + var left = (event.offsetX != undefined) ? + (event.offsetX < (this.getLevel() + 1) * 24) : + (event.pageX < util.getAbsoluteLeft(dom.tdSeparator));// for FF + if (left || expandable) { + // node is expandable when it is an object or array + if (domField) { + util.setEndOfContentEditable(domField); + domField.focus(); + } + } + else { + if (domValue && !this.enum) { + util.setEndOfContentEditable(domValue); + domValue.focus(); + } + } + } + if (((target == dom.tdExpand && !expandable) || target == dom.tdField || target == dom.tdSeparator) && + (type == 'click' && !event.hasMoved)) { + if (domField) { + util.setEndOfContentEditable(domField); + domField.focus(); + } + } + + if (type == 'keydown') { + this.onKeyDown(event); + } + }; + + /** + * Key down event handler + * @param {Event} event + */ + Node.prototype.onKeyDown = function (event) { + var keynum = event.which || event.keyCode; + var target = event.target || event.srcElement; + var ctrlKey = event.ctrlKey; + var shiftKey = event.shiftKey; + var altKey = event.altKey; + var handled = false; + var prevNode, nextNode, nextDom, nextDom2; + var editable = this.editor.options.mode === 'tree'; + var oldSelection; + var oldBeforeNode; + var nodes; + var multiselection; + var selectedNodes = this.editor.multiselection.nodes.length > 0 + ? this.editor.multiselection.nodes + : [this]; + var firstNode = selectedNodes[0]; + var lastNode = selectedNodes[selectedNodes.length - 1]; + + // console.log(ctrlKey, keynum, event.charCode); // TODO: cleanup + if (keynum == 13) { // Enter + if (target == this.dom.value) { + if (!this.editable.value || event.ctrlKey) { + if (util.isUrl(this.value)) { + window.open(this.value, '_blank'); + handled = true; + } + } + } + else if (target == this.dom.expand) { + var expandable = this._hasChilds(); + if (expandable) { + var recurse = event.ctrlKey; // with ctrl-key, expand/collapse all + this._onExpand(recurse); + target.focus(); + handled = true; + } + } + } + else if (keynum == 68) { // D + if (ctrlKey && editable) { // Ctrl+D + Node.onDuplicate(selectedNodes); + handled = true; + } + } + else if (keynum == 69) { // E + if (ctrlKey) { // Ctrl+E and Ctrl+Shift+E + this._onExpand(shiftKey); // recurse = shiftKey + target.focus(); // TODO: should restore focus in case of recursing expand (which takes DOM offline) + handled = true; + } + } + else if (keynum == 77 && editable) { // M + if (ctrlKey) { // Ctrl+M + this.showContextMenu(target); + handled = true; + } + } + else if (keynum == 46 && editable) { // Del + if (ctrlKey) { // Ctrl+Del + Node.onRemove(selectedNodes); + handled = true; + } + } + else if (keynum == 45 && editable) { // Ins + if (ctrlKey && !shiftKey) { // Ctrl+Ins + this._onInsertBefore(); + handled = true; + } + else if (ctrlKey && shiftKey) { // Ctrl+Shift+Ins + this._onInsertAfter(); + handled = true; + } + } + else if (keynum == 35) { // End + if (altKey) { // Alt+End + // find the last node + var endNode = this._lastNode(); + if (endNode) { + endNode.focus(Node.focusElement || this._getElementName(target)); + } + handled = true; + } + } + else if (keynum == 36) { // Home + if (altKey) { // Alt+Home + // find the first node + var homeNode = this._firstNode(); + if (homeNode) { + homeNode.focus(Node.focusElement || this._getElementName(target)); + } + handled = true; + } + } + else if (keynum == 37) { // Arrow Left + if (altKey && !shiftKey) { // Alt + Arrow Left + // move to left element + var prevElement = this._previousElement(target); + if (prevElement) { + this.focus(this._getElementName(prevElement)); + } + handled = true; + } + else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow left + if (lastNode.expanded) { + var appendDom = lastNode.getAppend(); + nextDom = appendDom ? appendDom.nextSibling : undefined; + } + else { + var dom = lastNode.getDom(); + nextDom = dom.nextSibling; + } + if (nextDom) { + nextNode = Node.getNodeFromTarget(nextDom); + nextDom2 = nextDom.nextSibling; + nextNode2 = Node.getNodeFromTarget(nextDom2); + if (nextNode && nextNode instanceof AppendNode && + !(lastNode.parent.childs.length == 1) && + nextNode2 && nextNode2.parent) { + oldSelection = this.editor.getSelection(); + oldBeforeNode = lastNode._nextSibling(); + + selectedNodes.forEach(function (node) { + nextNode2.parent.moveBefore(node, nextNode2); + }); + this.focus(Node.focusElement || this._getElementName(target)); + + this.editor._onAction('moveNodes', { + nodes: selectedNodes, + oldBeforeNode: oldBeforeNode, + newBeforeNode: nextNode2, + oldSelection: oldSelection, + newSelection: this.editor.getSelection() + }); + } + } + } + } + else if (keynum == 38) { // Arrow Up + if (altKey && !shiftKey) { // Alt + Arrow Up + // find the previous node + prevNode = this._previousNode(); + if (prevNode) { + this.editor.deselect(true); + prevNode.focus(Node.focusElement || this._getElementName(target)); + } + handled = true; + } + else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Up + // select multiple nodes + prevNode = this._previousNode(); + if (prevNode) { + multiselection = this.editor.multiselection; + multiselection.start = multiselection.start || this; + multiselection.end = prevNode; + nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end); + + this.editor.select(nodes); + prevNode.focus('field'); // select field as we know this always exists + } + handled = true; + } + else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Up + // find the previous node + prevNode = firstNode._previousNode(); + if (prevNode && prevNode.parent) { + oldSelection = this.editor.getSelection(); + oldBeforeNode = lastNode._nextSibling(); + + selectedNodes.forEach(function (node) { + prevNode.parent.moveBefore(node, prevNode); + }); + this.focus(Node.focusElement || this._getElementName(target)); + + this.editor._onAction('moveNodes', { + nodes: selectedNodes, + oldBeforeNode: oldBeforeNode, + newBeforeNode: prevNode, + oldSelection: oldSelection, + newSelection: this.editor.getSelection() + }); + } + handled = true; + } + } + else if (keynum == 39) { // Arrow Right + if (altKey && !shiftKey) { // Alt + Arrow Right + // move to right element + var nextElement = this._nextElement(target); + if (nextElement) { + this.focus(this._getElementName(nextElement)); + } + handled = true; + } + else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Right + dom = firstNode.getDom(); + var prevDom = dom.previousSibling; + if (prevDom) { + prevNode = Node.getNodeFromTarget(prevDom); + if (prevNode && prevNode.parent && + (prevNode instanceof AppendNode) + && !prevNode.isVisible()) { + oldSelection = this.editor.getSelection(); + oldBeforeNode = lastNode._nextSibling(); + + selectedNodes.forEach(function (node) { + prevNode.parent.moveBefore(node, prevNode); + }); + this.focus(Node.focusElement || this._getElementName(target)); + + this.editor._onAction('moveNodes', { + nodes: selectedNodes, + oldBeforeNode: oldBeforeNode, + newBeforeNode: prevNode, + oldSelection: oldSelection, + newSelection: this.editor.getSelection() + }); + } + } + } + } + else if (keynum == 40) { // Arrow Down + if (altKey && !shiftKey) { // Alt + Arrow Down + // find the next node + nextNode = this._nextNode(); + if (nextNode) { + this.editor.deselect(true); + nextNode.focus(Node.focusElement || this._getElementName(target)); + } + handled = true; + } + else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Down + // select multiple nodes + nextNode = this._nextNode(); + if (nextNode) { + multiselection = this.editor.multiselection; + multiselection.start = multiselection.start || this; + multiselection.end = nextNode; + nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end); + + this.editor.select(nodes); + nextNode.focus('field'); // select field as we know this always exists + } + handled = true; + } + else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Down + // find the 2nd next node and move before that one + if (lastNode.expanded) { + nextNode = lastNode.append ? lastNode.append._nextNode() : undefined; + } + else { + nextNode = lastNode._nextNode(); + } + var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append); + if (nextNode2 && nextNode2.parent) { + oldSelection = this.editor.getSelection(); + oldBeforeNode = lastNode._nextSibling(); + + selectedNodes.forEach(function (node) { + nextNode2.parent.moveBefore(node, nextNode2); + }); + this.focus(Node.focusElement || this._getElementName(target)); + + this.editor._onAction('moveNodes', { + nodes: selectedNodes, + oldBeforeNode: oldBeforeNode, + newBeforeNode: nextNode2, + oldSelection: oldSelection, + newSelection: this.editor.getSelection() + }); + } + handled = true; + } + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }; + + /** + * Handle the expand event, when clicked on the expand button + * @param {boolean} recurse If true, child nodes will be expanded too + * @private + */ + Node.prototype._onExpand = function (recurse) { + if (recurse) { + // Take the table offline + var table = this.dom.tr.parentNode; // TODO: not nice to access the main table like this + var frame = table.parentNode; + var scrollTop = frame.scrollTop; + frame.removeChild(table); + } + + if (this.expanded) { + this.collapse(recurse); + } + else { + this.expand(recurse); + } + + if (recurse) { + // Put the table online again + frame.appendChild(table); + frame.scrollTop = scrollTop; + } + }; + + /** + * Remove nodes + * @param {Node[] | Node} nodes + */ + Node.onRemove = function(nodes) { + if (!Array.isArray(nodes)) { + return Node.onRemove([nodes]); + } + + if (nodes && nodes.length > 0) { + var firstNode = nodes[0]; + var parent = firstNode.parent; + var editor = firstNode.editor; + var firstIndex = firstNode.getIndex(); + editor.highlighter.unhighlight(); + + // adjust the focus + var oldSelection = editor.getSelection(); + Node.blurNodes(nodes); + var newSelection = editor.getSelection(); + + // remove the nodes + nodes.forEach(function (node) { + node.parent._remove(node); + }); + + // store history action + editor._onAction('removeNodes', { + nodes: nodes.slice(0), // store a copy of the array! + parent: parent, + index: firstIndex, + oldSelection: oldSelection, + newSelection: newSelection + }); + } + }; + + + /** + * Duplicate nodes + * duplicated nodes will be added right after the original nodes + * @param {Node[] | Node} nodes + */ + Node.onDuplicate = function(nodes) { + if (!Array.isArray(nodes)) { + return Node.onDuplicate([nodes]); + } + + if (nodes && nodes.length > 0) { + var lastNode = nodes[nodes.length - 1]; + var parent = lastNode.parent; + var editor = lastNode.editor; + + editor.deselect(editor.multiselection.nodes); + + // duplicate the nodes + var oldSelection = editor.getSelection(); + var afterNode = lastNode; + var clones = nodes.map(function (node) { + var clone = node.clone(); + parent.insertAfter(clone, afterNode); + afterNode = clone; + return clone; + }); + + // set selection to the duplicated nodes + if (nodes.length === 1) { + clones[0].focus(); + } + else { + editor.select(clones); + } + var newSelection = editor.getSelection(); + + editor._onAction('duplicateNodes', { + afterNode: lastNode, + nodes: clones, + parent: parent, + oldSelection: oldSelection, + newSelection: newSelection + }); + } + }; + + /** + * Handle insert before event + * @param {String} [field] + * @param {*} [value] + * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' + * @private + */ + Node.prototype._onInsertBefore = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + + var newNode = new Node(this.editor, { + field: (field != undefined) ? field : '', + value: (value != undefined) ? value : '', + type: type + }); + newNode.expand(true); + this.parent.insertBefore(newNode, this); + this.editor.highlighter.unhighlight(); + newNode.focus('field'); + var newSelection = this.editor.getSelection(); + + this.editor._onAction('insertBeforeNodes', { + nodes: [newNode], + beforeNode: this, + parent: this.parent, + oldSelection: oldSelection, + newSelection: newSelection + }); + }; + + /** + * Handle insert after event + * @param {String} [field] + * @param {*} [value] + * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' + * @private + */ + Node.prototype._onInsertAfter = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + + var newNode = new Node(this.editor, { + field: (field != undefined) ? field : '', + value: (value != undefined) ? value : '', + type: type + }); + newNode.expand(true); + this.parent.insertAfter(newNode, this); + this.editor.highlighter.unhighlight(); + newNode.focus('field'); + var newSelection = this.editor.getSelection(); + + this.editor._onAction('insertAfterNodes', { + nodes: [newNode], + afterNode: this, + parent: this.parent, + oldSelection: oldSelection, + newSelection: newSelection + }); + }; + + /** + * Handle append event + * @param {String} [field] + * @param {*} [value] + * @param {String} [type] Can be 'auto', 'array', 'object', or 'string' + * @private + */ + Node.prototype._onAppend = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + + var newNode = new Node(this.editor, { + field: (field != undefined) ? field : '', + value: (value != undefined) ? value : '', + type: type + }); + newNode.expand(true); + this.parent.appendChild(newNode); + this.editor.highlighter.unhighlight(); + newNode.focus('field'); + var newSelection = this.editor.getSelection(); + + this.editor._onAction('appendNodes', { + nodes: [newNode], + parent: this.parent, + oldSelection: oldSelection, + newSelection: newSelection + }); + }; + + /** + * Change the type of the node's value + * @param {String} newType + * @private + */ + Node.prototype._onChangeType = function (newType) { + var oldType = this.type; + if (newType != oldType) { + var oldSelection = this.editor.getSelection(); + this.changeType(newType); + var newSelection = this.editor.getSelection(); + + this.editor._onAction('changeType', { + node: this, + oldType: oldType, + newType: newType, + oldSelection: oldSelection, + newSelection: newSelection + }); + } + }; + + /** + * Sort the child's of the node. Only applicable when the node has type 'object' + * or 'array'. + * @param {String} direction Sorting direction. Available values: "asc", "desc" + * @private + */ + Node.prototype.sort = function (direction) { + if (!this._hasChilds()) { + return; + } + + var order = (direction == 'desc') ? -1 : 1; + var prop = (this.type == 'array') ? 'value': 'field'; + this.hideChilds(); + + var oldChilds = this.childs; + var oldSortOrder = this.sortOrder; + + // copy the array (the old one will be kept for an undo action + this.childs = this.childs.concat(); + + // sort the arrays + this.childs.sort(function (a, b) { + return order * naturalSort(a[prop], b[prop]); + }); + this.sortOrder = (order == 1) ? 'asc' : 'desc'; + + this.editor._onAction('sort', { + node: this, + oldChilds: oldChilds, + oldSort: oldSortOrder, + newChilds: this.childs, + newSort: this.sortOrder + }); + + this.showChilds(); + }; + + /** + * Create a table row with an append button. + * @return {HTMLElement | undefined} buttonAppend or undefined when inapplicable + */ + Node.prototype.getAppend = function () { + if (!this.append) { + this.append = new AppendNode(this.editor); + this.append.setParent(this); + } + return this.append.getDom(); + }; + + /** + * Find the node from an event target + * @param {Node} target + * @return {Node | undefined} node or undefined when not found + * @static + */ + Node.getNodeFromTarget = function (target) { + while (target) { + if (target.node) { + return target.node; + } + target = target.parentNode; + } + + return undefined; + }; + + /** + * Remove the focus of given nodes, and move the focus to the (a) node before, + * (b) the node after, or (c) the parent node. + * @param {Array. | Node} nodes + */ + Node.blurNodes = function (nodes) { + if (!Array.isArray(nodes)) { + Node.blurNodes([nodes]); + return; + } + + var firstNode = nodes[0]; + var parent = firstNode.parent; + var firstIndex = firstNode.getIndex(); + + if (parent.childs[firstIndex + nodes.length]) { + parent.childs[firstIndex + nodes.length].focus(); + } + else if (parent.childs[firstIndex - 1]) { + parent.childs[firstIndex - 1].focus(); + } + else { + parent.focus(); + } + }; + + /** + * Get the next sibling of current node + * @return {Node} nextSibling + * @private + */ + Node.prototype._nextSibling = function () { + var index = this.parent.childs.indexOf(this); + return this.parent.childs[index + 1] || this.parent.append; + }; + + /** + * Get the previously rendered node + * @return {Node | null} previousNode + * @private + */ + Node.prototype._previousNode = function () { + var prevNode = null; + var dom = this.getDom(); + if (dom && dom.parentNode) { + // find the previous field + var prevDom = dom; + do { + prevDom = prevDom.previousSibling; + prevNode = Node.getNodeFromTarget(prevDom); + } + while (prevDom && (prevNode instanceof AppendNode && !prevNode.isVisible())); + } + return prevNode; + }; + + /** + * Get the next rendered node + * @return {Node | null} nextNode + * @private + */ + Node.prototype._nextNode = function () { + var nextNode = null; + var dom = this.getDom(); + if (dom && dom.parentNode) { + // find the previous field + var nextDom = dom; + do { + nextDom = nextDom.nextSibling; + nextNode = Node.getNodeFromTarget(nextDom); + } + while (nextDom && (nextNode instanceof AppendNode && !nextNode.isVisible())); + } + + return nextNode; + }; + + /** + * Get the first rendered node + * @return {Node | null} firstNode + * @private + */ + Node.prototype._firstNode = function () { + var firstNode = null; + var dom = this.getDom(); + if (dom && dom.parentNode) { + var firstDom = dom.parentNode.firstChild; + firstNode = Node.getNodeFromTarget(firstDom); + } + + return firstNode; + }; + + /** + * Get the last rendered node + * @return {Node | null} lastNode + * @private + */ + Node.prototype._lastNode = function () { + var lastNode = null; + var dom = this.getDom(); + if (dom && dom.parentNode) { + var lastDom = dom.parentNode.lastChild; + lastNode = Node.getNodeFromTarget(lastDom); + while (lastDom && (lastNode instanceof AppendNode && !lastNode.isVisible())) { + lastDom = lastDom.previousSibling; + lastNode = Node.getNodeFromTarget(lastDom); + } + } + return lastNode; + }; + + /** + * Get the next element which can have focus. + * @param {Element} elem + * @return {Element | null} nextElem + * @private + */ + Node.prototype._previousElement = function (elem) { + var dom = this.dom; + // noinspection FallthroughInSwitchStatementJS + switch (elem) { + case dom.value: + if (this.fieldEditable) { + return dom.field; + } + // intentional fall through + case dom.field: + if (this._hasChilds()) { + return dom.expand; + } + // intentional fall through + case dom.expand: + return dom.menu; + case dom.menu: + if (dom.drag) { + return dom.drag; + } + // intentional fall through + default: + return null; + } + }; + + /** + * Get the next element which can have focus. + * @param {Element} elem + * @return {Element | null} nextElem + * @private + */ + Node.prototype._nextElement = function (elem) { + var dom = this.dom; + // noinspection FallthroughInSwitchStatementJS + switch (elem) { + case dom.drag: + return dom.menu; + case dom.menu: + if (this._hasChilds()) { + return dom.expand; + } + // intentional fall through + case dom.expand: + if (this.fieldEditable) { + return dom.field; + } + // intentional fall through + case dom.field: + if (!this._hasChilds()) { + return dom.value; + } + default: + return null; + } + }; + + /** + * Get the dom name of given element. returns null if not found. + * For example when element == dom.field, "field" is returned. + * @param {Element} element + * @return {String | null} elementName Available elements with name: 'drag', + * 'menu', 'expand', 'field', 'value' + * @private + */ + Node.prototype._getElementName = function (element) { + var dom = this.dom; + for (var name in dom) { + if (dom.hasOwnProperty(name)) { + if (dom[name] == element) { + return name; + } + } + } + return null; + }; + + /** + * Test if this node has childs. This is the case when the node is an object + * or array. + * @return {boolean} hasChilds + * @private + */ + Node.prototype._hasChilds = function () { + return this.type == 'array' || this.type == 'object'; + }; + + // titles with explanation for the different types + Node.TYPE_TITLES = { + 'auto': 'Field type "auto". ' + + 'The field type is automatically determined from the value ' + + 'and can be a string, number, boolean, or null.', + 'object': 'Field type "object". ' + + 'An object contains an unordered set of key/value pairs.', + 'array': 'Field type "array". ' + + 'An array contains an ordered collection of values.', + 'string': 'Field type "string". ' + + 'Field type is not determined from the value, ' + + 'but always returned as string.' + }; + + Node.prototype.addTemplates = function (menu, append) { + var node = this; + var templates = node.editor.options.templates; + if (templates == null) return; + if (templates.length) { + // create a separator + menu.push({ + 'type': 'separator' + }); + } + var appendData = function (name, data) { + node._onAppend(name, data); + }; + var insertData = function (name, data) { + node._onInsertBefore(name, data); + }; + templates.forEach(function (template) { + menu.push({ + text: template.text, + className: (template.className || 'jsoneditor-type-object'), + title: template.title, + click: (append ? appendData.bind(this, template.field, template.value) : insertData.bind(this, template.field, template.value)) + }); + }); + }; + + /** + * Show a contextmenu for this node + * @param {HTMLElement} anchor Anchor element to attach the context menu to + * as sibling. + * @param {function} [onClose] Callback method called when the context menu + * is being closed. + */ + Node.prototype.showContextMenu = function (anchor, onClose) { + var node = this; + var titles = Node.TYPE_TITLES; + var items = []; + + if (this.editable.value) { + items.push({ + text: 'Type', + title: 'Change the type of this field', + className: 'jsoneditor-type-' + this.type, + submenu: [ + { + text: 'Auto', + className: 'jsoneditor-type-auto' + + (this.type == 'auto' ? ' jsoneditor-selected' : ''), + title: titles.auto, + click: function () { + node._onChangeType('auto'); + } + }, + { + text: 'Array', + className: 'jsoneditor-type-array' + + (this.type == 'array' ? ' jsoneditor-selected' : ''), + title: titles.array, + click: function () { + node._onChangeType('array'); + } + }, + { + text: 'Object', + className: 'jsoneditor-type-object' + + (this.type == 'object' ? ' jsoneditor-selected' : ''), + title: titles.object, + click: function () { + node._onChangeType('object'); + } + }, + { + text: 'String', + className: 'jsoneditor-type-string' + + (this.type == 'string' ? ' jsoneditor-selected' : ''), + title: titles.string, + click: function () { + node._onChangeType('string'); + } + } + ] + }); + } + + if (this._hasChilds()) { + var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc'); + items.push({ + text: 'Sort', + title: 'Sort the childs of this ' + this.type, + className: 'jsoneditor-sort-' + direction, + click: function () { + node.sort(direction); + }, + submenu: [ + { + text: 'Ascending', + className: 'jsoneditor-sort-asc', + title: 'Sort the childs of this ' + this.type + ' in ascending order', + click: function () { + node.sort('asc'); + } + }, + { + text: 'Descending', + className: 'jsoneditor-sort-desc', + title: 'Sort the childs of this ' + this.type +' in descending order', + click: function () { + node.sort('desc'); + } + } + ] + }); + } + + if (this.parent && this.parent._hasChilds()) { + if (items.length) { + // create a separator + items.push({ + 'type': 'separator' + }); + } + + // create append button (for last child node only) + var childs = node.parent.childs; + if (node == childs[childs.length - 1]) { + var appendSubmenu = [ + { + text: 'Auto', + className: 'jsoneditor-type-auto', + title: titles.auto, + click: function () { + node._onAppend('', '', 'auto'); + } + }, + { + text: 'Array', + className: 'jsoneditor-type-array', + title: titles.array, + click: function () { + node._onAppend('', []); + } + }, + { + text: 'Object', + className: 'jsoneditor-type-object', + title: titles.object, + click: function () { + node._onAppend('', {}); + } + }, + { + text: 'String', + className: 'jsoneditor-type-string', + title: titles.string, + click: function () { + node._onAppend('', '', 'string'); + } + } + ]; + node.addTemplates(appendSubmenu, true); + items.push({ + text: 'Append', + title: 'Append a new field with type \'auto\' after this field (Ctrl+Shift+Ins)', + submenuTitle: 'Select the type of the field to be appended', + className: 'jsoneditor-append', + click: function () { + node._onAppend('', '', 'auto'); + }, + submenu: appendSubmenu + }); + } + + + + // create insert button + var insertSubmenu = [ + { + text: 'Auto', + className: 'jsoneditor-type-auto', + title: titles.auto, + click: function () { + node._onInsertBefore('', '', 'auto'); + } + }, + { + text: 'Array', + className: 'jsoneditor-type-array', + title: titles.array, + click: function () { + node._onInsertBefore('', []); + } + }, + { + text: 'Object', + className: 'jsoneditor-type-object', + title: titles.object, + click: function () { + node._onInsertBefore('', {}); + } + }, + { + text: 'String', + className: 'jsoneditor-type-string', + title: titles.string, + click: function () { + node._onInsertBefore('', '', 'string'); + } + } + ]; + node.addTemplates(insertSubmenu, false); + items.push({ + text: 'Insert', + title: 'Insert a new field with type \'auto\' before this field (Ctrl+Ins)', + submenuTitle: 'Select the type of the field to be inserted', + className: 'jsoneditor-insert', + click: function () { + node._onInsertBefore('', '', 'auto'); + }, + submenu: insertSubmenu + }); + + if (this.editable.field) { + // create duplicate button + items.push({ + text: 'Duplicate', + title: 'Duplicate this field (Ctrl+D)', + className: 'jsoneditor-duplicate', + click: function () { + Node.onDuplicate(node); + } + }); + + // create remove button + items.push({ + text: 'Remove', + title: 'Remove this field (Ctrl+Del)', + className: 'jsoneditor-remove', + click: function () { + Node.onRemove(node); + } + }); + } + } + + var menu = new ContextMenu(items, {close: onClose}); + menu.show(anchor, this.editor.content); + }; + + /** + * get the type of a value + * @param {*} value + * @return {String} type Can be 'object', 'array', 'string', 'auto' + * @private + */ + Node.prototype._getType = function(value) { + if (value instanceof Array) { + return 'array'; + } + if (value instanceof Object) { + return 'object'; + } + if (typeof(value) == 'string' && typeof(this._stringCast(value)) != 'string') { + return 'string'; + } + + return 'auto'; + }; + + /** + * cast contents of a string to the correct type. This can be a string, + * a number, a boolean, etc + * @param {String} str + * @return {*} castedStr + * @private + */ + Node.prototype._stringCast = function(str) { + var lower = str.toLowerCase(), + num = Number(str), // will nicely fail with '123ab' + numFloat = parseFloat(str); // will nicely fail with ' ' + + if (str == '') { + return ''; + } + else if (lower == 'null') { + return null; + } + else if (lower == 'true') { + return true; + } + else if (lower == 'false') { + return false; + } + else if (!isNaN(num) && !isNaN(numFloat)) { + return num; + } + else { + return str; + } + }; + + /** + * escape a text, such that it can be displayed safely in an HTML element + * @param {String} text + * @return {String} escapedText + * @private + */ + Node.prototype._escapeHTML = function (text) { + if (typeof text !== 'string') { + return String(text); + } + else { + var htmlEscaped = String(text) + .replace(/&/g, '&') // must be replaced first! + .replace(//g, '>') + .replace(/ /g, '  ') // replace double space with an nbsp and space + .replace(/^ /, ' ') // space at start + .replace(/ $/, ' '); // space at end + + var json = JSON.stringify(htmlEscaped); + var html = json.substring(1, json.length - 1); + if (this.editor.options.escapeUnicode === true) { + html = util.escapeUnicodeChars(html); + } + return html; + } + }; + + /** + * unescape a string. + * @param {String} escapedText + * @return {String} text + * @private + */ + Node.prototype._unescapeHTML = function (escapedText) { + var json = '"' + this._escapeJSON(escapedText) + '"'; + var htmlEscaped = util.parse(json); + + return htmlEscaped + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/ |\u00A0/g, ' ') + .replace(/&/g, '&'); // must be replaced last + }; + + /** + * escape a text to make it a valid JSON string. The method will: + * - replace unescaped double quotes with '\"' + * - replace unescaped backslash with '\\' + * - replace returns with '\n' + * @param {String} text + * @return {String} escapedText + * @private + */ + Node.prototype._escapeJSON = function (text) { + // TODO: replace with some smart regex (only when a new solution is faster!) + var escaped = ''; + var i = 0; + while (i < text.length) { + var c = text.charAt(i); + if (c == '\n') { + escaped += '\\n'; + } + else if (c == '\\') { + escaped += c; + i++; + + c = text.charAt(i); + if (c === '' || '"\\/bfnrtu'.indexOf(c) == -1) { + escaped += '\\'; // no valid escape character + } + escaped += c; + } + else if (c == '"') { + escaped += '\\"'; + } + else { + escaped += c; + } + i++; + } + + return escaped; + }; + + // TODO: find a nicer solution to resolve this circular dependency between Node and AppendNode + var AppendNode = appendNodeFactory(Node); + + module.exports = Node; + + +/***/ }, +/* 60 */ +/***/ function(module, exports) { + + /* + * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license + * Author: Jim Palmer (based on chunking idea from Dave Koelle) + */ + /*jshint unused:false */ + module.exports = function naturalSort (a, b) { + "use strict"; + var re = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, + sre = /(^[ ]*|[ ]*$)/g, + dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, + hre = /^0x[0-9a-f]+$/i, + ore = /^0/, + i = function(s) { return naturalSort.insensitive && ('' + s).toLowerCase() || '' + s; }, + // convert all to strings strip whitespace + x = i(a).replace(sre, '') || '', + y = i(b).replace(sre, '') || '', + // chunk/tokenize + xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + // numeric, hex or date detection + xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && x.match(dre) && Date.parse(x)), + yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null, + oFxNcL, oFyNcL; + // first try and sort Hex codes or Dates + if (yD) { + if ( xD < yD ) { return -1; } + else if ( xD > yD ) { return 1; } + } + // natural sorting through split numeric strings and default strings + for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { + // find floats not starting with '0', string or 0 if not defined (Clint Priest) + oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; + oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + else if (typeof oFxNcL !== typeof oFyNcL) { + oFxNcL += ''; + oFyNcL += ''; + } + if (oFxNcL < oFyNcL) { return -1; } + if (oFxNcL > oFyNcL) { return 1; } + } + return 0; + }; + + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var util = __webpack_require__(54); + var ContextMenu = __webpack_require__(57); + + /** + * A factory function to create an AppendNode, which depends on a Node + * @param {Node} Node + */ + function appendNodeFactory(Node) { + /** + * @constructor AppendNode + * @extends Node + * @param {TreeEditor} editor + * Create a new AppendNode. This is a special node which is created at the + * end of the list with childs for an object or array + */ + function AppendNode (editor) { + /** @type {TreeEditor} */ + this.editor = editor; + this.dom = {}; + } + + AppendNode.prototype = new Node(); + + /** + * Return a table row with an append button. + * @return {Element} dom TR element + */ + AppendNode.prototype.getDom = function () { + // TODO: implement a new solution for the append node + var dom = this.dom; + + if (dom.tr) { + return dom.tr; + } + + this._updateEditability(); + + // a row for the append button + var trAppend = document.createElement('tr'); + trAppend.node = this; + dom.tr = trAppend; + + // TODO: consistent naming + + if (this.editor.options.mode === 'tree') { + // a cell for the dragarea column + dom.tdDrag = document.createElement('td'); + + // create context menu + var tdMenu = document.createElement('td'); + dom.tdMenu = tdMenu; + var menu = document.createElement('button'); + menu.type = 'button'; + menu.className = 'jsoneditor-contextmenu'; + menu.title = 'Click to open the actions menu (Ctrl+M)'; + dom.menu = menu; + tdMenu.appendChild(dom.menu); + } + + // a cell for the contents (showing text 'empty') + var tdAppend = document.createElement('td'); + var domText = document.createElement('div'); + domText.innerHTML = '(empty)'; + domText.className = 'jsoneditor-readonly'; + tdAppend.appendChild(domText); + dom.td = tdAppend; + dom.text = domText; + + this.updateDom(); + + return trAppend; + }; + + /** + * Update the HTML dom of the Node + */ + AppendNode.prototype.updateDom = function () { + var dom = this.dom; + var tdAppend = dom.td; + if (tdAppend) { + tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px'; + // TODO: not so nice hard coded offset + } + + var domText = dom.text; + if (domText) { + domText.innerHTML = '(empty ' + this.parent.type + ')'; + } + + // attach or detach the contents of the append node: + // hide when the parent has childs, show when the parent has no childs + var trAppend = dom.tr; + if (!this.isVisible()) { + if (dom.tr.firstChild) { + if (dom.tdDrag) { + trAppend.removeChild(dom.tdDrag); + } + if (dom.tdMenu) { + trAppend.removeChild(dom.tdMenu); + } + trAppend.removeChild(tdAppend); + } + } + else { + if (!dom.tr.firstChild) { + if (dom.tdDrag) { + trAppend.appendChild(dom.tdDrag); + } + if (dom.tdMenu) { + trAppend.appendChild(dom.tdMenu); + } + trAppend.appendChild(tdAppend); + } + } + }; + + /** + * Check whether the AppendNode is currently visible. + * the AppendNode is visible when its parent has no childs (i.e. is empty). + * @return {boolean} isVisible + */ + AppendNode.prototype.isVisible = function () { + return (this.parent.childs.length == 0); + }; + + /** + * Show a contextmenu for this node + * @param {HTMLElement} anchor The element to attach the menu to. + * @param {function} [onClose] Callback method called when the context menu + * is being closed. + */ + AppendNode.prototype.showContextMenu = function (anchor, onClose) { + var node = this; + var titles = Node.TYPE_TITLES; + var appendSubmenu = [ + { + text: 'Auto', + className: 'jsoneditor-type-auto', + title: titles.auto, + click: function () { + node._onAppend('', '', 'auto'); + } + }, + { + text: 'Array', + className: 'jsoneditor-type-array', + title: titles.array, + click: function () { + node._onAppend('', []); + } + }, + { + text: 'Object', + className: 'jsoneditor-type-object', + title: titles.object, + click: function () { + node._onAppend('', {}); + } + }, + { + text: 'String', + className: 'jsoneditor-type-string', + title: titles.string, + click: function () { + node._onAppend('', '', 'string'); + } + } + ]; + node.addTemplates(appendSubmenu, true); + var items = [ + // create append button + { + 'text': 'Append', + 'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)', + 'submenuTitle': 'Select the type of the field to be appended', + 'className': 'jsoneditor-insert', + 'click': function () { + node._onAppend('', '', 'auto'); + }, + 'submenu': appendSubmenu + } + ]; + + var menu = new ContextMenu(items, {close: onClose}); + menu.show(anchor, this.editor.content); + }; + + /** + * Handle an event. The event is catched centrally by the editor + * @param {Event} event + */ + AppendNode.prototype.onEvent = function (event) { + var type = event.type; + var target = event.target || event.srcElement; + var dom = this.dom; + + // highlight the append nodes parent + var menu = dom.menu; + if (target == menu) { + if (type == 'mouseover') { + this.editor.highlighter.highlight(this.parent); + } + else if (type == 'mouseout') { + this.editor.highlighter.unhighlight(); + } + } + + // context menu events + if (type == 'click' && target == dom.menu) { + var highlighter = this.editor.highlighter; + highlighter.highlight(this.parent); + highlighter.lock(); + util.addClassName(dom.menu, 'jsoneditor-selected'); + this.showContextMenu(dom.menu, function () { + util.removeClassName(dom.menu, 'jsoneditor-selected'); + highlighter.unlock(); + highlighter.unhighlight(); + }); + } + + if (type == 'keydown') { + this.onKeyDown(event); + } + }; + + return AppendNode; + } + + module.exports = appendNodeFactory; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var ContextMenu = __webpack_require__(57); + + /** + * Create a select box to be used in the editor menu's, which allows to switch mode + * @param {HTMLElement} container + * @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view' + * @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view' + * @param {function(mode: string)} onSwitch Callback invoked on switch + * @constructor + */ + function ModeSwitcher(container, modes, current, onSwitch) { + // available modes + var availableModes = { + code: { + 'text': 'Code', + 'title': 'Switch to code highlighter', + 'click': function () { + onSwitch('code') + } + }, + form: { + 'text': 'Form', + 'title': 'Switch to form editor', + 'click': function () { + onSwitch('form'); + } + }, + text: { + 'text': 'Text', + 'title': 'Switch to plain text editor', + 'click': function () { + onSwitch('text'); + } + }, + tree: { + 'text': 'Tree', + 'title': 'Switch to tree editor', + 'click': function () { + onSwitch('tree'); + } + }, + view: { + 'text': 'View', + 'title': 'Switch to tree view', + 'click': function () { + onSwitch('view'); + } + } + }; + + // list the selected modes + var items = []; + for (var i = 0; i < modes.length; i++) { + var mode = modes[i]; + var item = availableModes[mode]; + if (!item) { + throw new Error('Unknown mode "' + mode + '"'); + } + + item.className = 'jsoneditor-type-modes' + ((current == mode) ? ' jsoneditor-selected' : ''); + items.push(item); + } + + // retrieve the title of current mode + var currentMode = availableModes[current]; + if (!currentMode) { + throw new Error('Unknown mode "' + current + '"'); + } + var currentTitle = currentMode.text; + + // create the html element + var box = document.createElement('button'); + box.type = 'button'; + box.className = 'jsoneditor-modes jsoneditor-separator'; + box.innerHTML = currentTitle + ' ▾'; + box.title = 'Switch editor mode'; + box.onclick = function () { + var menu = new ContextMenu(items); + menu.show(box); + }; + + var frame = document.createElement('div'); + frame.className = 'jsoneditor-modes'; + frame.style.position = 'relative'; + frame.appendChild(box); + + container.appendChild(frame); + + this.dom = { + container: container, + box: box, + frame: frame + }; + } + + /** + * Set focus to switcher + */ + ModeSwitcher.prototype.focus = function () { + this.dom.box.focus(); + }; + + /** + * Destroy the ModeSwitcher, remove from DOM + */ + ModeSwitcher.prototype.destroy = function () { + if (this.dom && this.dom.frame && this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + this.dom = null; + }; + + module.exports = ModeSwitcher; + + +/***/ }, +/* 63 */ +/***/ function(module, exports) { + + 'use strict'; + + function completely(config) { + config = config || {}; + config.confirmKeys = config.confirmKeys || [39, 35, 9] // right, end, tab + + var fontSize = ''; + var fontFamily = ''; + + var wrapper = document.createElement('div'); + wrapper.style.position = 'relative'; + wrapper.style.outline = '0'; + wrapper.style.border = '0'; + wrapper.style.margin = '0'; + wrapper.style.padding = '0'; + + var dropDown = document.createElement('div'); + dropDown.className = 'autocomplete dropdown'; + dropDown.style.position = 'absolute'; + dropDown.style.visibility = 'hidden'; + + var spacer; + var leftSide; // <-- it will contain the leftSide part of the textfield (the bit that was already autocompleted) + var createDropDownController = function (elem, rs) { + var rows = []; + var ix = 0; + var oldIndex = -1; + + var onMouseOver = function () { this.style.outline = '1px solid #ddd'; } + var onMouseOut = function () { this.style.outline = '0'; } + var onMouseDown = function () { p.hide(); p.onmouseselection(this.__hint, p.rs); } + + var p = { + rs: rs, + hide: function () { + elem.style.visibility = 'hidden'; + //rs.hideDropDown(); + }, + refresh: function (token, array) { + elem.style.visibility = 'hidden'; + ix = 0; + elem.innerHTML = ''; + var vph = (window.innerHeight || document.documentElement.clientHeight); + var rect = elem.parentNode.getBoundingClientRect(); + var distanceToTop = rect.top - 6; // heuristic give 6px + var distanceToBottom = vph - rect.bottom - 6; // distance from the browser border. + + rows = []; + for (var i = 0; i < array.length; i++) { + if (array[i].indexOf(token) !== 0) { continue; } + var divRow = document.createElement('div'); + divRow.className = 'item'; + //divRow.style.color = config.color; + divRow.onmouseover = onMouseOver; + divRow.onmouseout = onMouseOut; + divRow.onmousedown = onMouseDown; + divRow.__hint = array[i]; + divRow.innerHTML = token + '' + array[i].substring(token.length) + ''; + rows.push(divRow); + elem.appendChild(divRow); + } + if (rows.length === 0) { + return; // nothing to show. + } + if (rows.length === 1 && token === rows[0].__hint) { + return; // do not show the dropDown if it has only one element which matches what we have just displayed. + } + + if (rows.length < 2) return; + p.highlight(0); + + if (distanceToTop > distanceToBottom * 3) { // Heuristic (only when the distance to the to top is 4 times more than distance to the bottom + elem.style.maxHeight = distanceToTop + 'px'; // we display the dropDown on the top of the input text + elem.style.top = ''; + elem.style.bottom = '100%'; + } else { + elem.style.top = '100%'; + elem.style.bottom = ''; + elem.style.maxHeight = distanceToBottom + 'px'; + } + elem.style.visibility = 'visible'; + }, + highlight: function (index) { + if (oldIndex != -1 && rows[oldIndex]) { + rows[oldIndex].className = "item"; + } + rows[index].className = "item hover"; + oldIndex = index; + }, + move: function (step) { // moves the selection either up or down (unless it's not possible) step is either +1 or -1. + if (elem.style.visibility === 'hidden') return ''; // nothing to move if there is no dropDown. (this happens if the user hits escape and then down or up) + if (ix + step === -1 || ix + step === rows.length) return rows[ix].__hint; // NO CIRCULAR SCROLLING. + ix += step; + p.highlight(ix); + return rows[ix].__hint;//txtShadow.value = uRows[uIndex].__hint ; + }, + onmouseselection: function () { } // it will be overwritten. + }; + return p; + } + + function setEndOfContenteditable(contentEditableElement) { + var range, selection; + if (document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+ + { + range = document.createRange();//Create a range (a range is a like the selection but invisible) + range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range + range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start + selection = window.getSelection();//get the selection object (allows you to change selection) + selection.removeAllRanges();//remove any selections already made + selection.addRange(range);//make the range you have just created the visible selection + } + else if (document.selection)//IE 8 and lower + { + range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible) + range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range + range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start + range.select();//Select the range (make it the visible selection + } + } + + function calculateWidthForText(text) { + if (spacer === undefined) { // on first call only. + spacer = document.createElement('span'); + spacer.style.visibility = 'hidden'; + spacer.style.position = 'fixed'; + spacer.style.outline = '0'; + spacer.style.margin = '0'; + spacer.style.padding = '0'; + spacer.style.border = '0'; + spacer.style.left = '0'; + spacer.style.whiteSpace = 'pre'; + spacer.style.fontSize = fontSize; + spacer.style.fontFamily = fontFamily; + spacer.style.fontWeight = 'normal'; + document.body.appendChild(spacer); + } + + // Used to encode an HTML string into a plain text. + // taken from http://stackoverflow.com/questions/1219860/javascript-jquery-html-encoding + spacer.innerHTML = String(text).replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>'); + return spacer.getBoundingClientRect().right; + } + + var rs = { + onArrowDown: function () { }, // defaults to no action. + onArrowUp: function () { }, // defaults to no action. + onEnter: function () { }, // defaults to no action. + onTab: function () { }, // defaults to no action. + startFrom: 0, + options: [], + element: null, + elementHint: null, + elementStyle: null, + wrapper: wrapper, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations) + show: function (element, startPos, options) { + this.startFrom = startPos; + this.wrapper.remove(); + if (this.elementHint) { + this.elementHint.remove(); + this.elementHint = null; + } + + if (fontSize == '') { + fontSize = window.getComputedStyle(element).getPropertyValue('font-size'); + } + if (fontFamily == '') { + fontFamily = window.getComputedStyle(element).getPropertyValue('font-family'); + } + + var w = element.getBoundingClientRect().right - element.getBoundingClientRect().left; + dropDown.style.marginLeft = '0'; + dropDown.style.marginTop = element.getBoundingClientRect().height + 'px'; + this.options = options; + + if (this.element != element) { + this.element = element; + this.elementStyle = { + zIndex: this.element.style.zIndex, + position: this.element.style.position, + backgroundColor: this.element.style.backgroundColor, + borderColor: this.element.style.borderColor + } + } + + this.element.style.zIndex = 3; + this.element.style.position = 'relative'; + this.element.style.backgroundColor = 'transparent'; + this.element.style.borderColor = 'transparent'; + + this.elementHint = element.cloneNode(); + this.elementHint.className = 'autocomplete hint'; + this.elementHint.style.zIndex = 2; + this.elementHint.style.position = 'absolute'; + this.elementHint.onfocus = function () { this.element.focus(); }.bind(this); + + + + if (this.element.addEventListener) { + this.element.removeEventListener("keydown", keyDownHandler); + this.element.addEventListener("keydown", keyDownHandler, false); + this.element.removeEventListener("blur", onBlurHandler); + this.element.addEventListener("blur", onBlurHandler, false); + } + + wrapper.appendChild(this.elementHint); + wrapper.appendChild(dropDown); + element.parentElement.appendChild(wrapper); + + + this.repaint(element); + }, + setText: function (text) { + this.element.innerText = text; + }, + getText: function () { + return this.element.innerText; + }, + hideDropDown: function () { + this.wrapper.remove(); + if (this.elementHint) { + this.elementHint.remove(); + this.elementHint = null; + dropDownController.hide(); + this.element.style.zIndex = this.elementStyle.zIndex; + this.element.style.position = this.elementStyle.position; + this.element.style.backgroundColor = this.elementStyle.backgroundColor; + this.element.style.borderColor = this.elementStyle.borderColor; + } + + }, + repaint: function (element) { + var text = element.innerText; + text = text.replace('\n', ''); + + var startFrom = this.startFrom; + var options = this.options; + var optionsLength = this.options.length; + + // breaking text in leftSide and token. + + var token = text.substring(this.startFrom); + leftSide = text.substring(0, this.startFrom); + + for (var i = 0; i < optionsLength; i++) { + var opt = this.options[i]; + if (opt.indexOf(token) === 0) { // <-- how about upperCase vs. lowercase + this.elementHint.innerText = leftSide + opt; + break; + } + } + // moving the dropDown and refreshing it. + dropDown.style.left = calculateWidthForText(leftSide) + 'px'; + dropDownController.refresh(token, this.options); + this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + 10 + 'px' + var wasDropDownHidden = (dropDown.style.visibility == 'hidden'); + if (!wasDropDownHidden) + this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + dropDown.clientWidth + 'px'; + } + }; + + var dropDownController = createDropDownController(dropDown, rs); + + var keyDownHandler = function (e) { + //console.log("Keydown:" + e.keyCode); + e = e || window.event; + var keyCode = e.keyCode; + + if (this.elementHint == null) return; + + if (keyCode == 33) { return; } // page up (do nothing) + if (keyCode == 34) { return; } // page down (do nothing); + + if (keyCode == 27) { //escape + rs.hideDropDown(); + rs.element.focus(); + e.preventDefault(); + e.stopPropagation(); + return; + } + + if (config.confirmKeys.indexOf(keyCode) >= 0) { // (autocomplete triggered) + if (keyCode == 9) { + if (this.elementHint.innerText.length == 0) { + rs.onTab(); + } + } + if (this.elementHint.innerText.length > 0) { // if there is a hint + if (this.element.innerText != this.elementHint.innerText) { + this.element.innerText = this.elementHint.innerText; + rs.hideDropDown(); + setEndOfContenteditable(this.element); + if (keyCode == 9) { + rs.element.focus(); + e.preventDefault(); + e.stopPropagation(); + } + } + } + return; + } + + if (keyCode == 13) { // enter (autocomplete triggered) + if (this.elementHint.innerText.length == 0) { // if there is a hint + rs.onEnter(); + } else { + var wasDropDownHidden = (dropDown.style.visibility == 'hidden'); + dropDownController.hide(); + + if (wasDropDownHidden) { + rs.hideDropDown(); + rs.element.focus(); + rs.onEnter(); + return; + } + + this.element.innerText = this.elementHint.innerText; + rs.hideDropDown(); + setEndOfContenteditable(this.element); + e.preventDefault(); + e.stopPropagation(); + } + return; + } + + if (keyCode == 40) { // down + var m = dropDownController.move(+1); + if (m == '') { rs.onArrowDown(); } + this.elementHint.innerText = leftSide + m; + e.preventDefault(); + e.stopPropagation(); + return; + } + + if (keyCode == 38) { // up + var m = dropDownController.move(-1); + if (m == '') { rs.onArrowUp(); } + this.elementHint.innerText = leftSide + m; + e.preventDefault(); + e.stopPropagation(); + return; + } + + }.bind(rs); + + var onBlurHandler = function (e) { + rs.hideDropDown(); + //console.log("Lost focus."); + }.bind(rs); + + dropDownController.onmouseselection = function (text, rs) { + rs.element.innerText = rs.elementHint.innerText = leftSide + text; + rs.hideDropDown(); + window.setTimeout(function () { + rs.element.focus(); + setEndOfContenteditable(rs.element); + }, 1); + }; + + return rs; + } + + module.exports = completely; + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var ace = __webpack_require__(65); + var ModeSwitcher = __webpack_require__(62); + var util = __webpack_require__(54); + + // create a mixin with the functions for text mode + var textmode = {}; + + var MAX_ERRORS = 3; // maximum number of displayed errors at the bottom + + var DEFAULT_THEME = 'ace/theme/jsoneditor'; + + /** + * Create a text editor + * @param {Element} container + * @param {Object} [options] Object with options. available options: + * {String} mode Available values: + * "text" (default) + * or "code". + * {Number} indentation Number of indentation + * spaces. 2 by default. + * {function} onChange Callback method + * triggered on change + * {function} onModeChange Callback method + * triggered after setMode + * {function} onEditable Determine if textarea is readOnly + * readOnly defaults true + * {Object} ace A custom instance of + * Ace editor. + * {boolean} escapeUnicode If true, unicode + * characters are escaped. + * false by default. + * @private + */ + textmode.create = function (container, options) { + // read options + options = options || {}; + + if(typeof options.statusBar === 'undefined') { + options.statusBar = true; + } + + this.options = options; + + // indentation + if (options.indentation) { + this.indentation = Number(options.indentation); + } + else { + this.indentation = 2; // number of spaces + } + + // grab ace from options if provided + var _ace = options.ace ? options.ace : ace; + // TODO: make the option options.ace deprecated, it's not needed anymore (see #309) + + // determine mode + this.mode = (options.mode == 'code') ? 'code' : 'text'; + if (this.mode == 'code') { + // verify whether Ace editor is available and supported + if (typeof _ace === 'undefined') { + this.mode = 'text'; + console.warn('Failed to load Ace editor, falling back to plain text mode. Please use a JSONEditor bundle including Ace, or pass Ace as via the configuration option `ace`.'); + } + } + + // determine theme + this.theme = options.theme || DEFAULT_THEME; + if (this.theme === DEFAULT_THEME && _ace) { + try { + __webpack_require__(71); + } + catch (err) { + console.error(err); + } + } + + var me = this; + this.container = container; + this.dom = {}; + this.aceEditor = undefined; // ace code editor + this.textarea = undefined; // plain text editor (fallback when Ace is not available) + this.validateSchema = null; + + // create a debounced validate function + this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL); + + this.width = container.clientWidth; + this.height = container.clientHeight; + + this.frame = document.createElement('div'); + this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode; + this.frame.onclick = function (event) { + // prevent default submit action when the editor is located inside a form + event.preventDefault(); + }; + this.frame.onkeydown = function (event) { + me._onKeyDown(event); + }; + + // create menu + this.menu = document.createElement('div'); + this.menu.className = 'jsoneditor-menu'; + this.frame.appendChild(this.menu); + + // create format button + var buttonFormat = document.createElement('button'); + buttonFormat.type = 'button'; + buttonFormat.className = 'jsoneditor-format'; + buttonFormat.title = 'Format JSON data, with proper indentation and line feeds (Ctrl+\\)'; + this.menu.appendChild(buttonFormat); + buttonFormat.onclick = function () { + try { + me.format(); + me._onChange(); + } + catch (err) { + me._onError(err); + } + }; + + // create compact button + var buttonCompact = document.createElement('button'); + buttonCompact.type = 'button'; + buttonCompact.className = 'jsoneditor-compact'; + buttonCompact.title = 'Compact JSON data, remove all whitespaces (Ctrl+Shift+\\)'; + this.menu.appendChild(buttonCompact); + buttonCompact.onclick = function () { + try { + me.compact(); + me._onChange(); + } + catch (err) { + me._onError(err); + } + }; + + // create repair button + var buttonRepair = document.createElement('button'); + buttonRepair.type = 'button'; + buttonRepair.className = 'jsoneditor-repair'; + buttonRepair.title = 'Repair JSON: fix quotes and escape characters, remove comments and JSONP notation, turn JavaScript objects into JSON.'; + this.menu.appendChild(buttonRepair); + buttonRepair.onclick = function () { + try { + me.repair(); + me._onChange(); + } + catch (err) { + me._onError(err); + } + }; + + // create mode box + if (this.options && this.options.modes && this.options.modes.length) { + this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) { + // switch mode and restore focus + me.setMode(mode); + me.modeSwitcher.focus(); + }); + } + + var emptyNode = {}; + var isReadOnly = (this.options.onEditable + && typeof(this.options.onEditable === 'function') + && !this.options.onEditable(emptyNode)); + + this.content = document.createElement('div'); + this.content.className = 'jsoneditor-outer'; + this.frame.appendChild(this.content); + + this.container.appendChild(this.frame); + + if (this.mode == 'code') { + this.editorDom = document.createElement('div'); + this.editorDom.style.height = '100%'; // TODO: move to css + this.editorDom.style.width = '100%'; // TODO: move to css + this.content.appendChild(this.editorDom); + + var aceEditor = _ace.edit(this.editorDom); + aceEditor.$blockScrolling = Infinity; + aceEditor.setTheme(this.theme); + aceEditor.setOptions({ readOnly: isReadOnly }); + aceEditor.setShowPrintMargin(false); + aceEditor.setFontSize(13); + aceEditor.getSession().setMode('ace/mode/json'); + aceEditor.getSession().setTabSize(this.indentation); + aceEditor.getSession().setUseSoftTabs(true); + aceEditor.getSession().setUseWrapMode(true); + aceEditor.commands.bindKey('Ctrl-L', null); // disable Ctrl+L (is used by the browser to select the address bar) + aceEditor.commands.bindKey('Command-L', null); // disable Ctrl+L (is used by the browser to select the address bar) + this.aceEditor = aceEditor; + + // TODO: deprecated since v5.0.0. Cleanup backward compatibility some day + if (!this.hasOwnProperty('editor')) { + Object.defineProperty(this, 'editor', { + get: function () { + console.warn('Property "editor" has been renamed to "aceEditor".'); + return me.aceEditor; + }, + set: function (aceEditor) { + console.warn('Property "editor" has been renamed to "aceEditor".'); + me.aceEditor = aceEditor; + } + }); + } + + var poweredBy = document.createElement('a'); + poweredBy.appendChild(document.createTextNode('powered by ace')); + poweredBy.href = 'http://ace.ajax.org'; + poweredBy.target = '_blank'; + poweredBy.className = 'jsoneditor-poweredBy'; + poweredBy.onclick = function () { + // TODO: this anchor falls below the margin of the content, + // therefore the normal a.href does not work. We use a click event + // for now, but this should be fixed. + window.open(poweredBy.href, poweredBy.target); + }; + this.menu.appendChild(poweredBy); + + // register onchange event + aceEditor.on('change', this._onChange.bind(this)); + aceEditor.on('changeSelection', this._onSelect.bind(this)); + } + else { + // load a plain text textarea + var textarea = document.createElement('textarea'); + textarea.className = 'jsoneditor-text'; + textarea.spellcheck = false; + this.content.appendChild(textarea); + this.textarea = textarea; + this.textarea.readOnly = isReadOnly; + + // register onchange event + if (this.textarea.oninput === null) { + this.textarea.oninput = this._onChange.bind(this); + } + else { + // oninput is undefined. For IE8- + this.textarea.onchange = this._onChange.bind(this); + } + + textarea.onselect = this._onSelect.bind(this); + textarea.onmousedown = this._onMouseDown.bind(this); + textarea.onblur = this._onBlur.bind(this); + } + + if (options.statusBar) { + if (this.mode === 'code') { + util.addClassName(this.content, 'has-status-bar'); + + this.curserInfoElements = {}; + var statusBar = document.createElement('div'); + statusBar.className = 'jsoneditor-statusbar'; + this.frame.appendChild(statusBar); + + var lnLabel = document.createElement('span'); + lnLabel.className = 'jsoneditor-curserinfo-label'; + lnLabel.innerText = 'Ln:'; + + var lnVal = document.createElement('span'); + lnVal.className = 'jsoneditor-curserinfo-val'; + lnVal.innerText = 0; + + statusBar.appendChild(lnLabel); + statusBar.appendChild(lnVal); + + var colLabel = document.createElement('span'); + colLabel.className = 'jsoneditor-curserinfo-label'; + colLabel.innerText = 'Col:'; + + var colVal = document.createElement('span'); + colVal.className = 'jsoneditor-curserinfo-val'; + colVal.innerText = 0; + + statusBar.appendChild(colLabel); + statusBar.appendChild(colVal); + + this.curserInfoElements.colVal = colVal; + this.curserInfoElements.lnVal = lnVal; + + var countLabel = document.createElement('span'); + countLabel.className = 'jsoneditor-curserinfo-label'; + countLabel.innerText = 'characters selected'; + countLabel.style.display = 'none'; + + var countVal = document.createElement('span'); + countVal.className = 'jsoneditor-curserinfo-count'; + countVal.innerText = 0; + countVal.style.display = 'none'; + + this.curserInfoElements.countLabel = countLabel; + this.curserInfoElements.countVal = countVal; + + statusBar.appendChild(countVal); + statusBar.appendChild(countLabel); + } + } + + this.setSchema(this.options.schema, this.options.schemaRefs); + }; + + /** + * Handle a change: + * - Validate JSON schema + * - Send a callback to the onChange listener if provided + * @private + */ + textmode._onChange = function () { + // validate JSON schema (if configured) + this._debouncedValidate(); + + // trigger the onChange callback + if (this.options.onChange) { + try { + this.options.onChange(); + } + catch (err) { + console.error('Error in onChange callback: ', err); + } + } + }; + + /** + * Handle text selection + * Calculates the cursor position and selection range and updates menu + * @private + */ + textmode._onSelect = function () { + if(this.options.statusBar) { + if (this.textarea) { + var selectionRange = util.getInputSelection(this.textarea); + if (selectionRange.start !== selectionRange.end) { + this._setSelectionCountDisplay(Math.abs(selectionRange.end - selectionRange.start)); + } + } else if (this.aceEditor && this.curserInfoElements) { + var curserPos = this.aceEditor.getCursorPosition(); + var selectedText = this.aceEditor.getSelectedText(); + + this.curserInfoElements.lnVal.innerText = curserPos.row + 1; + this.curserInfoElements.colVal.innerText = curserPos.column + 1; + this._setSelectionCountDisplay(selectedText.length); + } + } + }; + + /** + * Event handler for keydown. Handles shortcut keys + * @param {Event} event + * @private + */ + textmode._onKeyDown = function (event) { + var keynum = event.which || event.keyCode; + var handled = false; + + if (keynum == 220 && event.ctrlKey) { + if (event.shiftKey) { // Ctrl+Shift+\ + this.compact(); + this._onChange(); + } + else { // Ctrl+\ + this.format(); + this._onChange(); + } + handled = true; + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + + this._setSelectionCountDisplay(); + }; + + /** + * Event handler for mousedown. + * @param {Event} event + * @private + */ + textmode._onMouseDown = function (event) { + this._setSelectionCountDisplay(); + }; + + /** + * Event handler for blur. + * @param {Event} event + * @private + */ + textmode._onBlur = function (event) { + this._setSelectionCountDisplay(); + }; + + textmode._setSelectionCountDisplay = function (value) { + if (this.options.statusBar && this.curserInfoElements) { + if (value && this.curserInfoElements && this.curserInfoElements.countVal) { + this.curserInfoElements.countVal.innerText = value; + this.curserInfoElements.countVal.style.display = 'inline'; + this.curserInfoElements.countLabel.style.display = 'inline'; + } else { + this.curserInfoElements.countVal.style.display = 'none'; + this.curserInfoElements.countLabel.style.display = 'none'; + } + } + }; + + /** + * Destroy the editor. Clean up DOM, event listeners, and web workers. + */ + textmode.destroy = function () { + // remove old ace editor + if (this.aceEditor) { + this.aceEditor.destroy(); + this.aceEditor = null; + } + + if (this.frame && this.container && this.frame.parentNode == this.container) { + this.container.removeChild(this.frame); + } + + if (this.modeSwitcher) { + this.modeSwitcher.destroy(); + this.modeSwitcher = null; + } + + this.textarea = null; + + this._debouncedValidate = null; + }; + + /** + * Compact the code in the text editor + */ + textmode.compact = function () { + var json = this.get(); + var text = JSON.stringify(json); + this.setText(text); + }; + + /** + * Format the code in the text editor + */ + textmode.format = function () { + var json = this.get(); + var text = JSON.stringify(json, null, this.indentation); + this.setText(text); + }; + + /** + * Repair the code in the text editor + */ + textmode.repair = function () { + var text = this.getText(); + var sanitizedText = util.sanitize(text); + this.setText(sanitizedText); + }; + + /** + * Set focus to the formatter + */ + textmode.focus = function () { + if (this.textarea) { + this.textarea.focus(); + } + if (this.aceEditor) { + this.aceEditor.focus(); + } + }; + + /** + * Resize the formatter + */ + textmode.resize = function () { + if (this.aceEditor) { + var force = false; + this.aceEditor.resize(force); + } + }; + + /** + * Set json data in the formatter + * @param {Object} json + */ + textmode.set = function(json) { + this.setText(JSON.stringify(json, null, this.indentation)); + }; + + /** + * Get json data from the formatter + * @return {Object} json + */ + textmode.get = function() { + var text = this.getText(); + var json; + + try { + json = util.parse(text); // this can throw an error + } + catch (err) { + // try to sanitize json, replace JavaScript notation with JSON notation + text = util.sanitize(text); + + // try to parse again + json = util.parse(text); // this can throw an error + } + + return json; + }; + + /** + * Get the text contents of the editor + * @return {String} jsonText + */ + textmode.getText = function() { + if (this.textarea) { + return this.textarea.value; + } + if (this.aceEditor) { + return this.aceEditor.getValue(); + } + return ''; + }; + + /** + * Set the text contents of the editor + * @param {String} jsonText + */ + textmode.setText = function(jsonText) { + var text; + + if (this.options.escapeUnicode === true) { + text = util.escapeUnicodeChars(jsonText); + } + else { + text = jsonText; + } + + if (this.textarea) { + this.textarea.value = text; + } + if (this.aceEditor) { + // prevent emitting onChange events while setting new text + var originalOnChange = this.options.onChange; + this.options.onChange = null; + + this.aceEditor.setValue(text, -1); + + this.options.onChange = originalOnChange; + } + // validate JSON schema + this.validate(); + }; + + /** + * Validate current JSON object against the configured JSON schema + * Throws an exception when no JSON schema is configured + */ + textmode.validate = function () { + // clear all current errors + if (this.dom.validationErrors) { + this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors); + this.dom.validationErrors = null; + + this.content.style.marginBottom = ''; + this.content.style.paddingBottom = ''; + } + + var doValidate = false; + var errors = []; + var json; + try { + json = this.get(); // this can fail when there is no valid json + doValidate = true; + } + catch (err) { + // no valid JSON, don't validate + } + + // only validate the JSON when parsing the JSON succeeded + if (doValidate && this.validateSchema) { + var valid = this.validateSchema(json); + if (!valid) { + errors = this.validateSchema.errors.map(function (error) { + return util.improveSchemaError(error); + }); + } + } + + if (errors.length > 0) { + // limit the number of displayed errors + var limit = errors.length > MAX_ERRORS; + if (limit) { + errors = errors.slice(0, MAX_ERRORS); + var hidden = this.validateSchema.errors.length - MAX_ERRORS; + errors.push('(' + hidden + ' more errors...)') + } + + var validationErrors = document.createElement('div'); + validationErrors.innerHTML = '' + + '' + + errors.map(function (error) { + var message; + if (typeof error === 'string') { + message = ''; + } + else { + message = '' + + ''; + } + + return '' + message + '' + }).join('') + + '' + + '
      ' + error + '
      ' + error.dataPath + '' + error.message + '
      '; + + this.dom.validationErrors = validationErrors; + this.frame.appendChild(validationErrors); + + var height = validationErrors.clientHeight; + this.content.style.marginBottom = (-height) + 'px'; + this.content.style.paddingBottom = height + 'px'; + } + + // update the height of the ace editor + if (this.aceEditor) { + var force = false; + this.aceEditor.resize(force); + } + }; + + // define modes + module.exports = [ + { + mode: 'text', + mixin: textmode, + data: 'text', + load: textmode.format + }, + { + mode: 'code', + mixin: textmode, + data: 'text', + load: textmode.format + } + ]; + + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + var ace + if (window.ace) { + // use the already loaded instance of Ace + ace = window.ace + } + else { + try { + // load brace + ace = __webpack_require__(66); + + // load required Ace plugins + __webpack_require__(68); + __webpack_require__(70); + } + catch (err) { + // failed to load brace (can be minimalist bundle). + // No worries, the editor will fall back to plain text if needed. + } + } + + module.exports = ace; + + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + /* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + + /** + * Define a module along with a payload + * @param module a name for the payload + * @param payload a function to call with (acequire, exports, module) params + */ + + (function() { + + var ACE_NAMESPACE = "ace"; + + var global = (function() { return this; })(); + if (!global && typeof window != "undefined") global = window; // strict mode + + + if (!ACE_NAMESPACE && typeof acequirejs !== "undefined") + return; + + + var define = function(module, deps, payload) { + if (typeof module !== "string") { + if (define.original) + define.original.apply(this, arguments); + else { + console.error("dropping module because define wasn\'t a string."); + console.trace(); + } + return; + } + if (arguments.length == 2) + payload = deps; + if (!define.modules[module]) { + define.payloads[module] = payload; + define.modules[module] = null; + } + }; + + define.modules = {}; + define.payloads = {}; + + /** + * Get at functionality define()ed using the function above + */ + var _acequire = function(parentId, module, callback) { + if (typeof module === "string") { + var payload = lookup(parentId, module); + if (payload != undefined) { + callback && callback(); + return payload; + } + } else if (Object.prototype.toString.call(module) === "[object Array]") { + var params = []; + for (var i = 0, l = module.length; i < l; ++i) { + var dep = lookup(parentId, module[i]); + if (dep == undefined && acequire.original) + return; + params.push(dep); + } + return callback && callback.apply(null, params) || true; + } + }; + + var acequire = function(module, callback) { + var packagedModule = _acequire("", module, callback); + if (packagedModule == undefined && acequire.original) + return acequire.original.apply(this, arguments); + return packagedModule; + }; + + var normalizeModule = function(parentId, moduleName) { + // normalize plugin acequires + if (moduleName.indexOf("!") !== -1) { + var chunks = moduleName.split("!"); + return normalizeModule(parentId, chunks[0]) + "!" + normalizeModule(parentId, chunks[1]); + } + // normalize relative acequires + if (moduleName.charAt(0) == ".") { + var base = parentId.split("/").slice(0, -1).join("/"); + moduleName = base + "/" + moduleName; + + while(moduleName.indexOf(".") !== -1 && previous != moduleName) { + var previous = moduleName; + moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); + } + } + return moduleName; + }; + + /** + * Internal function to lookup moduleNames and resolve them by calling the + * definition function if needed. + */ + var lookup = function(parentId, moduleName) { + moduleName = normalizeModule(parentId, moduleName); + + var module = define.modules[moduleName]; + if (!module) { + module = define.payloads[moduleName]; + if (typeof module === 'function') { + var exports = {}; + var mod = { + id: moduleName, + uri: '', + exports: exports, + packaged: true + }; + + var req = function(module, callback) { + return _acequire(moduleName, module, callback); + }; + + var returnValue = module(req, exports, mod); + exports = returnValue || mod.exports; + define.modules[moduleName] = exports; + delete define.payloads[moduleName]; + } + module = define.modules[moduleName] = exports || module; + } + return module; + }; + + function exportAce(ns) { + var root = global; + if (ns) { + if (!global[ns]) + global[ns] = {}; + root = global[ns]; + } + + if (!root.define || !root.define.packaged) { + define.original = root.define; + root.define = define; + root.define.packaged = true; + } + + if (!root.acequire || !root.acequire.packaged) { + acequire.original = root.acequire; + root.acequire = acequire; + root.acequire.packaged = true; + } + } + + exportAce(ACE_NAMESPACE); + + })(); + + ace.define("ace/lib/regexp",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + var real = { + exec: RegExp.prototype.exec, + test: RegExp.prototype.test, + match: String.prototype.match, + replace: String.prototype.replace, + split: String.prototype.split + }, + compliantExecNpcg = real.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups + compliantLastIndexIncrement = function () { + var x = /^/g; + real.test.call(x, ""); + return !x.lastIndex; + }(); + + if (compliantLastIndexIncrement && compliantExecNpcg) + return; + RegExp.prototype.exec = function (str) { + var match = real.exec.apply(this, arguments), + name, r2; + if ( typeof(str) == 'string' && match) { + if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { + r2 = RegExp(this.source, real.replace.call(getNativeFlags(this), "g", "")); + real.replace.call(str.slice(match.index), r2, function () { + for (var i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) + match[i] = undefined; + } + }); + } + if (this._xregexp && this._xregexp.captureNames) { + for (var i = 1; i < match.length; i++) { + name = this._xregexp.captureNames[i - 1]; + if (name) + match[name] = match[i]; + } + } + if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) + this.lastIndex--; + } + return match; + }; + if (!compliantLastIndexIncrement) { + RegExp.prototype.test = function (str) { + var match = real.exec.call(this, str); + if (match && this.global && !match[0].length && (this.lastIndex > match.index)) + this.lastIndex--; + return !!match; + }; + } + + function getNativeFlags (regex) { + return (regex.global ? "g" : "") + + (regex.ignoreCase ? "i" : "") + + (regex.multiline ? "m" : "") + + (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 + (regex.sticky ? "y" : ""); + } + + function indexOf (array, item, from) { + if (Array.prototype.indexOf) // Use the native array method if available + return array.indexOf(item, from); + for (var i = from || 0; i < array.length; i++) { + if (array[i] === item) + return i; + } + return -1; + } + + }); + + ace.define("ace/lib/es5-shim",["require","exports","module"], function(acequire, exports, module) { + + function Empty() {} + + if (!Function.prototype.bind) { + Function.prototype.bind = function bind(that) { // .length is 1 + var target = this; + if (typeof target != "function") { + throw new TypeError("Function.prototype.bind called on incompatible " + target); + } + var args = slice.call(arguments, 1); // for normal call + var bound = function () { + + if (this instanceof bound) { + + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + + } + + }; + if(target.prototype) { + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + return bound; + }; + } + var call = Function.prototype.call; + var prototypeOfArray = Array.prototype; + var prototypeOfObject = Object.prototype; + var slice = prototypeOfArray.slice; + var _toString = call.bind(prototypeOfObject.toString); + var owns = call.bind(prototypeOfObject.hasOwnProperty); + var defineGetter; + var defineSetter; + var lookupGetter; + var lookupSetter; + var supportsAccessors; + if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { + defineGetter = call.bind(prototypeOfObject.__defineGetter__); + defineSetter = call.bind(prototypeOfObject.__defineSetter__); + lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); + lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); + } + if ([1,2].splice(0).length != 2) { + if(function() { // test IE < 9 to splice bug - see issue #138 + function makeArray(l) { + var a = new Array(l+2); + a[0] = a[1] = 0; + return a; + } + var array = [], lengthBefore; + + array.splice.apply(array, makeArray(20)); + array.splice.apply(array, makeArray(26)); + + lengthBefore = array.length; //46 + array.splice(5, 0, "XXX"); // add one element + + lengthBefore + 1 == array.length + + if (lengthBefore + 1 == array.length) { + return true;// has right splice implementation without bugs + } + }()) {//IE 6/7 + var array_splice = Array.prototype.splice; + Array.prototype.splice = function(start, deleteCount) { + if (!arguments.length) { + return []; + } else { + return array_splice.apply(this, [ + start === void 0 ? 0 : start, + deleteCount === void 0 ? (this.length - start) : deleteCount + ].concat(slice.call(arguments, 2))) + } + }; + } else {//IE8 + Array.prototype.splice = function(pos, removeCount){ + var length = this.length; + if (pos > 0) { + if (pos > length) + pos = length; + } else if (pos == void 0) { + pos = 0; + } else if (pos < 0) { + pos = Math.max(length + pos, 0); + } + + if (!(pos+removeCount < length)) + removeCount = length - pos; + + var removed = this.slice(pos, pos+removeCount); + var insert = slice.call(arguments, 2); + var add = insert.length; + if (pos === length) { + if (add) { + this.push.apply(this, insert); + } + } else { + var remove = Math.min(removeCount, length - pos); + var tailOldPos = pos + remove; + var tailNewPos = tailOldPos + add - remove; + var tailCount = length - tailOldPos; + var lengthAfterRemove = length - remove; + + if (tailNewPos < tailOldPos) { // case A + for (var i = 0; i < tailCount; ++i) { + this[tailNewPos+i] = this[tailOldPos+i]; + } + } else if (tailNewPos > tailOldPos) { // case B + for (i = tailCount; i--; ) { + this[tailNewPos+i] = this[tailOldPos+i]; + } + } // else, add == remove (nothing to do) + + if (add && pos === lengthAfterRemove) { + this.length = lengthAfterRemove; // truncate array + this.push.apply(this, insert); + } else { + this.length = lengthAfterRemove + add; // reserves space + for (i = 0; i < add; ++i) { + this[pos+i] = insert[i]; + } + } + } + return removed; + }; + } + } + if (!Array.isArray) { + Array.isArray = function isArray(obj) { + return _toString(obj) == "[object Array]"; + }; + } + var boxedString = Object("a"), + splitString = boxedString[0] != "a" || !(0 in boxedString); + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function forEach(fun /*, thisp*/) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + thisp = arguments[1], + i = -1, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(); // TODO message + } + + while (++i < length) { + if (i in self) { + fun.call(thisp, self[i], i, object); + } + } + }; + } + if (!Array.prototype.map) { + Array.prototype.map = function map(fun /*, thisp*/) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0, + result = Array(length), + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) + result[i] = fun.call(thisp, self[i], i, object); + } + return result; + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function filter(fun /*, thisp */) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0, + result = [], + value, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) { + value = self[i]; + if (fun.call(thisp, value, i, object)) { + result.push(value); + } + } + } + return result; + }; + } + if (!Array.prototype.every) { + Array.prototype.every = function every(fun /*, thisp */) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && !fun.call(thisp, self[i], i, object)) { + return false; + } + } + return true; + }; + } + if (!Array.prototype.some) { + Array.prototype.some = function some(fun /*, thisp */) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && fun.call(thisp, self[i], i, object)) { + return true; + } + } + return false; + }; + } + if (!Array.prototype.reduce) { + Array.prototype.reduce = function reduce(fun /*, initial*/) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduce of empty array with no initial value"); + } + + var i = 0; + var result; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i++]; + break; + } + if (++i >= length) { + throw new TypeError("reduce of empty array with no initial value"); + } + } while (true); + } + + for (; i < length; i++) { + if (i in self) { + result = fun.call(void 0, result, self[i], i, object); + } + } + + return result; + }; + } + if (!Array.prototype.reduceRight) { + Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) { + var object = toObject(this), + self = splitString && _toString(this) == "[object String]" ? + this.split("") : + object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduceRight of empty array with no initial value"); + } + + var result, i = length - 1; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i--]; + break; + } + if (--i < 0) { + throw new TypeError("reduceRight of empty array with no initial value"); + } + } while (true); + } + + do { + if (i in this) { + result = fun.call(void 0, result, self[i], i, object); + } + } while (i--); + + return result; + }; + } + if (!Array.prototype.indexOf || ([0, 1].indexOf(1, 2) != -1)) { + Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) { + var self = splitString && _toString(this) == "[object String]" ? + this.split("") : + toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + + var i = 0; + if (arguments.length > 1) { + i = toInteger(arguments[1]); + } + i = i >= 0 ? i : Math.max(0, length + i); + for (; i < length; i++) { + if (i in self && self[i] === sought) { + return i; + } + } + return -1; + }; + } + if (!Array.prototype.lastIndexOf || ([0, 1].lastIndexOf(0, -3) != -1)) { + Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) { + var self = splitString && _toString(this) == "[object String]" ? + this.split("") : + toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + var i = length - 1; + if (arguments.length > 1) { + i = Math.min(i, toInteger(arguments[1])); + } + i = i >= 0 ? i : length - Math.abs(i); + for (; i >= 0; i--) { + if (i in self && sought === self[i]) { + return i; + } + } + return -1; + }; + } + if (!Object.getPrototypeOf) { + Object.getPrototypeOf = function getPrototypeOf(object) { + return object.__proto__ || ( + object.constructor ? + object.constructor.prototype : + prototypeOfObject + ); + }; + } + if (!Object.getOwnPropertyDescriptor) { + var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " + + "non-object: "; + Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) { + if ((typeof object != "object" && typeof object != "function") || object === null) + throw new TypeError(ERR_NON_OBJECT + object); + if (!owns(object, property)) + return; + + var descriptor, getter, setter; + descriptor = { enumerable: true, configurable: true }; + if (supportsAccessors) { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + + var getter = lookupGetter(object, property); + var setter = lookupSetter(object, property); + object.__proto__ = prototype; + + if (getter || setter) { + if (getter) descriptor.get = getter; + if (setter) descriptor.set = setter; + return descriptor; + } + } + descriptor.value = object[property]; + return descriptor; + }; + } + if (!Object.getOwnPropertyNames) { + Object.getOwnPropertyNames = function getOwnPropertyNames(object) { + return Object.keys(object); + }; + } + if (!Object.create) { + var createEmpty; + if (Object.prototype.__proto__ === null) { + createEmpty = function () { + return { "__proto__": null }; + }; + } else { + createEmpty = function () { + var empty = {}; + for (var i in empty) + empty[i] = null; + empty.constructor = + empty.hasOwnProperty = + empty.propertyIsEnumerable = + empty.isPrototypeOf = + empty.toLocaleString = + empty.toString = + empty.valueOf = + empty.__proto__ = null; + return empty; + } + } + + Object.create = function create(prototype, properties) { + var object; + if (prototype === null) { + object = createEmpty(); + } else { + if (typeof prototype != "object") + throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'"); + var Type = function () {}; + Type.prototype = prototype; + object = new Type(); + object.__proto__ = prototype; + } + if (properties !== void 0) + Object.defineProperties(object, properties); + return object; + }; + } + + function doesDefinePropertyWork(object) { + try { + Object.defineProperty(object, "sentinel", {}); + return "sentinel" in object; + } catch (exception) { + } + } + if (Object.defineProperty) { + var definePropertyWorksOnObject = doesDefinePropertyWork({}); + var definePropertyWorksOnDom = typeof document == "undefined" || + doesDefinePropertyWork(document.createElement("div")); + if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { + var definePropertyFallback = Object.defineProperty; + } + } + + if (!Object.defineProperty || definePropertyFallback) { + var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: "; + var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: " + var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " + + "on this javascript engine"; + + Object.defineProperty = function defineProperty(object, property, descriptor) { + if ((typeof object != "object" && typeof object != "function") || object === null) + throw new TypeError(ERR_NON_OBJECT_TARGET + object); + if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null) + throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor); + if (definePropertyFallback) { + try { + return definePropertyFallback.call(Object, object, property, descriptor); + } catch (exception) { + } + } + if (owns(descriptor, "value")) { + + if (supportsAccessors && (lookupGetter(object, property) || + lookupSetter(object, property))) + { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + delete object[property]; + object[property] = descriptor.value; + object.__proto__ = prototype; + } else { + object[property] = descriptor.value; + } + } else { + if (!supportsAccessors) + throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED); + if (owns(descriptor, "get")) + defineGetter(object, property, descriptor.get); + if (owns(descriptor, "set")) + defineSetter(object, property, descriptor.set); + } + + return object; + }; + } + if (!Object.defineProperties) { + Object.defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (owns(properties, property)) + Object.defineProperty(object, property, properties[property]); + } + return object; + }; + } + if (!Object.seal) { + Object.seal = function seal(object) { + return object; + }; + } + if (!Object.freeze) { + Object.freeze = function freeze(object) { + return object; + }; + } + try { + Object.freeze(function () {}); + } catch (exception) { + Object.freeze = (function freeze(freezeObject) { + return function freeze(object) { + if (typeof object == "function") { + return object; + } else { + return freezeObject(object); + } + }; + })(Object.freeze); + } + if (!Object.preventExtensions) { + Object.preventExtensions = function preventExtensions(object) { + return object; + }; + } + if (!Object.isSealed) { + Object.isSealed = function isSealed(object) { + return false; + }; + } + if (!Object.isFrozen) { + Object.isFrozen = function isFrozen(object) { + return false; + }; + } + if (!Object.isExtensible) { + Object.isExtensible = function isExtensible(object) { + if (Object(object) === object) { + throw new TypeError(); // TODO message + } + var name = ''; + while (owns(object, name)) { + name += '?'; + } + object[name] = true; + var returnValue = owns(object, name); + delete object[name]; + return returnValue; + }; + } + if (!Object.keys) { + var hasDontEnumBug = true, + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; + + for (var key in {"toString": null}) { + hasDontEnumBug = false; + } + + Object.keys = function keys(object) { + + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) { + throw new TypeError("Object.keys called on a non-object"); + } + + var keys = []; + for (var name in object) { + if (owns(object, name)) { + keys.push(name); + } + } + + if (hasDontEnumBug) { + for (var i = 0, ii = dontEnumsLength; i < ii; i++) { + var dontEnum = dontEnums[i]; + if (owns(object, dontEnum)) { + keys.push(dontEnum); + } + } + } + return keys; + }; + + } + if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; + } + var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" + + "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + + "\u2029\uFEFF"; + if (!String.prototype.trim || ws.trim()) { + ws = "[" + ws + "]"; + var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), + trimEndRegexp = new RegExp(ws + ws + "*$"); + String.prototype.trim = function trim() { + return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, ""); + }; + } + + function toInteger(n) { + n = +n; + if (n !== n) { // isNaN + n = 0; + } else if (n !== 0 && n !== (1/0) && n !== -(1/0)) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + return n; + } + + function isPrimitive(input) { + var type = typeof input; + return ( + input === null || + type === "undefined" || + type === "boolean" || + type === "number" || + type === "string" + ); + } + + function toPrimitive(input) { + var val, valueOf, toString; + if (isPrimitive(input)) { + return input; + } + valueOf = input.valueOf; + if (typeof valueOf === "function") { + val = valueOf.call(input); + if (isPrimitive(val)) { + return val; + } + } + toString = input.toString; + if (typeof toString === "function") { + val = toString.call(input); + if (isPrimitive(val)) { + return val; + } + } + throw new TypeError(); + } + var toObject = function (o) { + if (o == null) { // this matches both null and undefined + throw new TypeError("can't convert "+o+" to object"); + } + return Object(o); + }; + + }); + + ace.define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"], function(acequire, exports, module) { + "use strict"; + + acequire("./regexp"); + acequire("./es5-shim"); + + }); + + ace.define("ace/lib/dom",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + var XHTML_NS = "http://www.w3.org/1999/xhtml"; + + exports.getDocumentHead = function(doc) { + if (!doc) + doc = document; + return doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement; + }; + + exports.createElement = function(tag, ns) { + return document.createElementNS ? + document.createElementNS(ns || XHTML_NS, tag) : + document.createElement(tag); + }; + + exports.hasCssClass = function(el, name) { + var classes = (el.className + "").split(/\s+/g); + return classes.indexOf(name) !== -1; + }; + exports.addCssClass = function(el, name) { + if (!exports.hasCssClass(el, name)) { + el.className += " " + name; + } + }; + exports.removeCssClass = function(el, name) { + var classes = el.className.split(/\s+/g); + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + classes.splice(index, 1); + } + el.className = classes.join(" "); + }; + + exports.toggleCssClass = function(el, name) { + var classes = el.className.split(/\s+/g), add = true; + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + add = false; + classes.splice(index, 1); + } + if (add) + classes.push(name); + + el.className = classes.join(" "); + return add; + }; + exports.setCssClass = function(node, className, include) { + if (include) { + exports.addCssClass(node, className); + } else { + exports.removeCssClass(node, className); + } + }; + + exports.hasCssString = function(id, doc) { + var index = 0, sheets; + doc = doc || document; + + if (doc.createStyleSheet && (sheets = doc.styleSheets)) { + while (index < sheets.length) + if (sheets[index++].owningElement.id === id) return true; + } else if ((sheets = doc.getElementsByTagName("style"))) { + while (index < sheets.length) + if (sheets[index++].id === id) return true; + } + + return false; + }; + + exports.importCssString = function importCssString(cssText, id, doc) { + doc = doc || document; + if (id && exports.hasCssString(id, doc)) + return null; + + var style; + + if (id) + cssText += "\n/*# sourceURL=ace/css/" + id + " */"; + + if (doc.createStyleSheet) { + style = doc.createStyleSheet(); + style.cssText = cssText; + if (id) + style.owningElement.id = id; + } else { + style = exports.createElement("style"); + style.appendChild(doc.createTextNode(cssText)); + if (id) + style.id = id; + + exports.getDocumentHead(doc).appendChild(style); + } + }; + + exports.importCssStylsheet = function(uri, doc) { + if (doc.createStyleSheet) { + doc.createStyleSheet(uri); + } else { + var link = exports.createElement('link'); + link.rel = 'stylesheet'; + link.href = uri; + + exports.getDocumentHead(doc).appendChild(link); + } + }; + + exports.getInnerWidth = function(element) { + return ( + parseInt(exports.computedStyle(element, "paddingLeft"), 10) + + parseInt(exports.computedStyle(element, "paddingRight"), 10) + + element.clientWidth + ); + }; + + exports.getInnerHeight = function(element) { + return ( + parseInt(exports.computedStyle(element, "paddingTop"), 10) + + parseInt(exports.computedStyle(element, "paddingBottom"), 10) + + element.clientHeight + ); + }; + + exports.scrollbarWidth = function(document) { + var inner = exports.createElement("ace_inner"); + inner.style.width = "100%"; + inner.style.minWidth = "0px"; + inner.style.height = "200px"; + inner.style.display = "block"; + + var outer = exports.createElement("ace_outer"); + var style = outer.style; + + style.position = "absolute"; + style.left = "-10000px"; + style.overflow = "hidden"; + style.width = "200px"; + style.minWidth = "0px"; + style.height = "150px"; + style.display = "block"; + + outer.appendChild(inner); + + var body = document.documentElement; + body.appendChild(outer); + + var noScrollbar = inner.offsetWidth; + + style.overflow = "scroll"; + var withScrollbar = inner.offsetWidth; + + if (noScrollbar == withScrollbar) { + withScrollbar = outer.clientWidth; + } + + body.removeChild(outer); + + return noScrollbar-withScrollbar; + }; + + if (typeof document == "undefined") { + exports.importCssString = function() {}; + return; + } + + if (window.pageYOffset !== undefined) { + exports.getPageScrollTop = function() { + return window.pageYOffset; + }; + + exports.getPageScrollLeft = function() { + return window.pageXOffset; + }; + } + else { + exports.getPageScrollTop = function() { + return document.body.scrollTop; + }; + + exports.getPageScrollLeft = function() { + return document.body.scrollLeft; + }; + } + + if (window.getComputedStyle) + exports.computedStyle = function(element, style) { + if (style) + return (window.getComputedStyle(element, "") || {})[style] || ""; + return window.getComputedStyle(element, "") || {}; + }; + else + exports.computedStyle = function(element, style) { + if (style) + return element.currentStyle[style]; + return element.currentStyle; + }; + exports.setInnerHtml = function(el, innerHtml) { + var element = el.cloneNode(false);//document.createElement("div"); + element.innerHTML = innerHtml; + el.parentNode.replaceChild(element, el); + return element; + }; + + if ("textContent" in document.documentElement) { + exports.setInnerText = function(el, innerText) { + el.textContent = innerText; + }; + + exports.getInnerText = function(el) { + return el.textContent; + }; + } + else { + exports.setInnerText = function(el, innerText) { + el.innerText = innerText; + }; + + exports.getInnerText = function(el) { + return el.innerText; + }; + } + + exports.getParentWindow = function(document) { + return document.defaultView || document.parentWindow; + }; + + }); + + ace.define("ace/lib/oop",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + + exports.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } + return obj; + }; + + exports.implement = function(proto, mixin) { + exports.mixin(proto, mixin); + }; + + }); + + ace.define("ace/lib/keys",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop"], function(acequire, exports, module) { + "use strict"; + + acequire("./fixoldbrowsers"); + + var oop = acequire("./oop"); + var Keys = (function() { + var ret = { + MODIFIER_KEYS: { + 16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta' + }, + + KEY_MODS: { + "ctrl": 1, "alt": 2, "option" : 2, "shift": 4, + "super": 8, "meta": 8, "command": 8, "cmd": 8 + }, + + FUNCTION_KEYS : { + 8 : "Backspace", + 9 : "Tab", + 13 : "Return", + 19 : "Pause", + 27 : "Esc", + 32 : "Space", + 33 : "PageUp", + 34 : "PageDown", + 35 : "End", + 36 : "Home", + 37 : "Left", + 38 : "Up", + 39 : "Right", + 40 : "Down", + 44 : "Print", + 45 : "Insert", + 46 : "Delete", + 96 : "Numpad0", + 97 : "Numpad1", + 98 : "Numpad2", + 99 : "Numpad3", + 100: "Numpad4", + 101: "Numpad5", + 102: "Numpad6", + 103: "Numpad7", + 104: "Numpad8", + 105: "Numpad9", + '-13': "NumpadEnter", + 112: "F1", + 113: "F2", + 114: "F3", + 115: "F4", + 116: "F5", + 117: "F6", + 118: "F7", + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12", + 144: "Numlock", + 145: "Scrolllock" + }, + + PRINTABLE_KEYS: { + 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', + 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a', + 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', + 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', + 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', + 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.', + 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', + 219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*' + } + }; + var name, i; + for (i in ret.FUNCTION_KEYS) { + name = ret.FUNCTION_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + for (i in ret.PRINTABLE_KEYS) { + name = ret.PRINTABLE_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + oop.mixin(ret, ret.MODIFIER_KEYS); + oop.mixin(ret, ret.PRINTABLE_KEYS); + oop.mixin(ret, ret.FUNCTION_KEYS); + ret.enter = ret["return"]; + ret.escape = ret.esc; + ret.del = ret["delete"]; + ret[173] = '-'; + + (function() { + var mods = ["cmd", "ctrl", "alt", "shift"]; + for (var i = Math.pow(2, mods.length); i--;) { + ret.KEY_MODS[i] = mods.filter(function(x) { + return i & ret.KEY_MODS[x]; + }).join("-") + "-"; + } + })(); + + ret.KEY_MODS[0] = ""; + ret.KEY_MODS[-1] = "input-"; + + return ret; + })(); + oop.mixin(exports, Keys); + + exports.keyCodeToString = function(keyCode) { + var keyString = Keys[keyCode]; + if (typeof keyString != "string") + keyString = String.fromCharCode(keyCode); + return keyString.toLowerCase(); + }; + + }); + + ace.define("ace/lib/useragent",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + exports.OS = { + LINUX: "LINUX", + MAC: "MAC", + WINDOWS: "WINDOWS" + }; + exports.getOS = function() { + if (exports.isMac) { + return exports.OS.MAC; + } else if (exports.isLinux) { + return exports.OS.LINUX; + } else { + return exports.OS.WINDOWS; + } + }; + if (typeof navigator != "object") + return; + + var os = (navigator.platform.match(/mac|win|linux/i) || ["other"])[0].toLowerCase(); + var ua = navigator.userAgent; + exports.isWin = (os == "win"); + exports.isMac = (os == "mac"); + exports.isLinux = (os == "linux"); + exports.isIE = + (navigator.appName == "Microsoft Internet Explorer" || navigator.appName.indexOf("MSAppHost") >= 0) + ? parseFloat((ua.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]) + : parseFloat((ua.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]); // for ie + + exports.isOldIE = exports.isIE && exports.isIE < 9; + exports.isGecko = exports.isMozilla = (window.Controllers || window.controllers) && window.navigator.product === "Gecko"; + exports.isOldGecko = exports.isGecko && parseInt((ua.match(/rv:(\d+)/)||[])[1], 10) < 4; + exports.isOpera = window.opera && Object.prototype.toString.call(window.opera) == "[object Opera]"; + exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined; + + exports.isChrome = parseFloat(ua.split(" Chrome/")[1]) || undefined; + + exports.isAIR = ua.indexOf("AdobeAIR") >= 0; + + exports.isIPad = ua.indexOf("iPad") >= 0; + + exports.isChromeOS = ua.indexOf(" CrOS ") >= 0; + + exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream; + + if (exports.isIOS) exports.isMac = true; + + }); + + ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var keys = acequire("./keys"); + var useragent = acequire("./useragent"); + + var pressedKeys = null; + var ts = 0; + + exports.addListener = function(elem, type, callback) { + if (elem.addEventListener) { + return elem.addEventListener(type, callback, false); + } + if (elem.attachEvent) { + var wrapper = function() { + callback.call(elem, window.event); + }; + callback._wrapper = wrapper; + elem.attachEvent("on" + type, wrapper); + } + }; + + exports.removeListener = function(elem, type, callback) { + if (elem.removeEventListener) { + return elem.removeEventListener(type, callback, false); + } + if (elem.detachEvent) { + elem.detachEvent("on" + type, callback._wrapper || callback); + } + }; + exports.stopEvent = function(e) { + exports.stopPropagation(e); + exports.preventDefault(e); + return false; + }; + + exports.stopPropagation = function(e) { + if (e.stopPropagation) + e.stopPropagation(); + else + e.cancelBubble = true; + }; + + exports.preventDefault = function(e) { + if (e.preventDefault) + e.preventDefault(); + else + e.returnValue = false; + }; + exports.getButton = function(e) { + if (e.type == "dblclick") + return 0; + if (e.type == "contextmenu" || (useragent.isMac && (e.ctrlKey && !e.altKey && !e.shiftKey))) + return 2; + if (e.preventDefault) { + return e.button; + } + else { + return {1:0, 2:2, 4:1}[e.button]; + } + }; + + exports.capture = function(el, eventHandler, releaseCaptureHandler) { + function onMouseUp(e) { + eventHandler && eventHandler(e); + releaseCaptureHandler && releaseCaptureHandler(e); + + exports.removeListener(document, "mousemove", eventHandler, true); + exports.removeListener(document, "mouseup", onMouseUp, true); + exports.removeListener(document, "dragstart", onMouseUp, true); + } + + exports.addListener(document, "mousemove", eventHandler, true); + exports.addListener(document, "mouseup", onMouseUp, true); + exports.addListener(document, "dragstart", onMouseUp, true); + + return onMouseUp; + }; + + exports.addTouchMoveListener = function (el, callback) { + var startx, starty; + exports.addListener(el, "touchstart", function (e) { + var touches = e.touches; + var touchObj = touches[0]; + startx = touchObj.clientX; + starty = touchObj.clientY; + }); + exports.addListener(el, "touchmove", function (e) { + var touches = e.touches; + if (touches.length > 1) return; + + var touchObj = touches[0]; + + e.wheelX = startx - touchObj.clientX; + e.wheelY = starty - touchObj.clientY; + + startx = touchObj.clientX; + starty = touchObj.clientY; + + callback(e); + }); + }; + + exports.addMouseWheelListener = function(el, callback) { + if ("onmousewheel" in el) { + exports.addListener(el, "mousewheel", function(e) { + var factor = 8; + if (e.wheelDeltaX !== undefined) { + e.wheelX = -e.wheelDeltaX / factor; + e.wheelY = -e.wheelDeltaY / factor; + } else { + e.wheelX = 0; + e.wheelY = -e.wheelDelta / factor; + } + callback(e); + }); + } else if ("onwheel" in el) { + exports.addListener(el, "wheel", function(e) { + var factor = 0.35; + switch (e.deltaMode) { + case e.DOM_DELTA_PIXEL: + e.wheelX = e.deltaX * factor || 0; + e.wheelY = e.deltaY * factor || 0; + break; + case e.DOM_DELTA_LINE: + case e.DOM_DELTA_PAGE: + e.wheelX = (e.deltaX || 0) * 5; + e.wheelY = (e.deltaY || 0) * 5; + break; + } + + callback(e); + }); + } else { + exports.addListener(el, "DOMMouseScroll", function(e) { + if (e.axis && e.axis == e.HORIZONTAL_AXIS) { + e.wheelX = (e.detail || 0) * 5; + e.wheelY = 0; + } else { + e.wheelX = 0; + e.wheelY = (e.detail || 0) * 5; + } + callback(e); + }); + } + }; + + exports.addMultiMouseDownListener = function(elements, timeouts, eventHandler, callbackName) { + var clicks = 0; + var startX, startY, timer; + var eventNames = { + 2: "dblclick", + 3: "tripleclick", + 4: "quadclick" + }; + + function onMousedown(e) { + if (exports.getButton(e) !== 0) { + clicks = 0; + } else if (e.detail > 1) { + clicks++; + if (clicks > 4) + clicks = 1; + } else { + clicks = 1; + } + if (useragent.isIE) { + var isNewClick = Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5; + if (!timer || isNewClick) + clicks = 1; + if (timer) + clearTimeout(timer); + timer = setTimeout(function() {timer = null;}, timeouts[clicks - 1] || 600); + + if (clicks == 1) { + startX = e.clientX; + startY = e.clientY; + } + } + + e._clicks = clicks; + + eventHandler[callbackName]("mousedown", e); + + if (clicks > 4) + clicks = 0; + else if (clicks > 1) + return eventHandler[callbackName](eventNames[clicks], e); + } + function onDblclick(e) { + clicks = 2; + if (timer) + clearTimeout(timer); + timer = setTimeout(function() {timer = null;}, timeouts[clicks - 1] || 600); + eventHandler[callbackName]("mousedown", e); + eventHandler[callbackName](eventNames[clicks], e); + } + if (!Array.isArray(elements)) + elements = [elements]; + elements.forEach(function(el) { + exports.addListener(el, "mousedown", onMousedown); + if (useragent.isOldIE) + exports.addListener(el, "dblclick", onDblclick); + }); + }; + + var getModifierHash = useragent.isMac && useragent.isOpera && !("KeyboardEvent" in window) + ? function(e) { + return 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0); + } + : function(e) { + return 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); + }; + + exports.getModifierString = function(e) { + return keys.KEY_MODS[getModifierHash(e)]; + }; + + function normalizeCommandKeys(callback, e, keyCode) { + var hashId = getModifierHash(e); + + if (!useragent.isMac && pressedKeys) { + if (e.getModifierState && (e.getModifierState("OS") || e.getModifierState("Win"))) + hashId |= 8; + if (pressedKeys.altGr) { + if ((3 & hashId) != 3) + pressedKeys.altGr = 0; + else + return; + } + if (keyCode === 18 || keyCode === 17) { + var location = "location" in e ? e.location : e.keyLocation; + if (keyCode === 17 && location === 1) { + if (pressedKeys[keyCode] == 1) + ts = e.timeStamp; + } else if (keyCode === 18 && hashId === 3 && location === 2) { + var dt = e.timeStamp - ts; + if (dt < 50) + pressedKeys.altGr = true; + } + } + } + + if (keyCode in keys.MODIFIER_KEYS) { + keyCode = -1; + } + if (hashId & 8 && (keyCode >= 91 && keyCode <= 93)) { + keyCode = -1; + } + + if (!hashId && keyCode === 13) { + var location = "location" in e ? e.location : e.keyLocation; + if (location === 3) { + callback(e, hashId, -keyCode); + if (e.defaultPrevented) + return; + } + } + + if (useragent.isChromeOS && hashId & 8) { + callback(e, hashId, keyCode); + if (e.defaultPrevented) + return; + else + hashId &= ~8; + } + if (!hashId && !(keyCode in keys.FUNCTION_KEYS) && !(keyCode in keys.PRINTABLE_KEYS)) { + return false; + } + + return callback(e, hashId, keyCode); + } + + + exports.addCommandKeyListener = function(el, callback) { + var addListener = exports.addListener; + if (useragent.isOldGecko || (useragent.isOpera && !("KeyboardEvent" in window))) { + var lastKeyDownKeyCode = null; + addListener(el, "keydown", function(e) { + lastKeyDownKeyCode = e.keyCode; + }); + addListener(el, "keypress", function(e) { + return normalizeCommandKeys(callback, e, lastKeyDownKeyCode); + }); + } else { + var lastDefaultPrevented = null; + + addListener(el, "keydown", function(e) { + pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; + var result = normalizeCommandKeys(callback, e, e.keyCode); + lastDefaultPrevented = e.defaultPrevented; + return result; + }); + + addListener(el, "keypress", function(e) { + if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { + exports.stopEvent(e); + lastDefaultPrevented = null; + } + }); + + addListener(el, "keyup", function(e) { + pressedKeys[e.keyCode] = null; + }); + + if (!pressedKeys) { + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); + } + } + }; + function resetPressedKeys() { + pressedKeys = Object.create(null); + } + + if (typeof window == "object" && window.postMessage && !useragent.isOldIE) { + var postMessageId = 1; + exports.nextTick = function(callback, win) { + win = win || window; + var messageName = "zero-timeout-message-" + postMessageId; + exports.addListener(win, "message", function listener(e) { + if (e.data == messageName) { + exports.stopPropagation(e); + exports.removeListener(win, "message", listener); + callback(); + } + }); + win.postMessage(messageName, "*"); + }; + } + + + exports.nextFrame = typeof window == "object" && (window.requestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame + || window.msRequestAnimationFrame + || window.oRequestAnimationFrame); + + if (exports.nextFrame) + exports.nextFrame = exports.nextFrame.bind(window); + else + exports.nextFrame = function(callback) { + setTimeout(callback, 17); + }; + }); + + ace.define("ace/lib/lang",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + exports.last = function(a) { + return a[a.length - 1]; + }; + + exports.stringReverse = function(string) { + return string.split("").reverse().join(""); + }; + + exports.stringRepeat = function (string, count) { + var result = ''; + while (count > 0) { + if (count & 1) + result += string; + + if (count >>= 1) + string += string; + } + return result; + }; + + var trimBeginRegexp = /^\s\s*/; + var trimEndRegexp = /\s\s*$/; + + exports.stringTrimLeft = function (string) { + return string.replace(trimBeginRegexp, ''); + }; + + exports.stringTrimRight = function (string) { + return string.replace(trimEndRegexp, ''); + }; + + exports.copyObject = function(obj) { + var copy = {}; + for (var key in obj) { + copy[key] = obj[key]; + } + return copy; + }; + + exports.copyArray = function(array){ + var copy = []; + for (var i=0, l=array.length; i PLACEHOLDER.length) + data = data.substr(9); + else if (data.substr(0, 4) == PLACEHOLDER.substr(0, 4)) + data = data.substr(4, data.length - PLACEHOLDER.length + 1); + else if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0)) + data = data.slice(0, -1); + if (data == PLACEHOLDER.charAt(0)) { + } else if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0)) + data = data.slice(0, -1); + + if (data) + host.onTextInput(data); + } + if (copied) { + copied = false; + } + if (afterContextMenu) + afterContextMenu = false; + }; + var onInput = function(e) { + if (inComposition) + return; + var data = text.value; + sendText(data); + resetValue(); + }; + + var handleClipboardData = function(e, data, forceIEMime) { + var clipboardData = e.clipboardData || window.clipboardData; + if (!clipboardData || BROKEN_SETDATA) + return; + var mime = USE_IE_MIME_TYPE || forceIEMime ? "Text" : "text/plain"; + try { + if (data) { + return clipboardData.setData(mime, data) !== false; + } else { + return clipboardData.getData(mime); + } + } catch(e) { + if (!forceIEMime) + return handleClipboardData(e, data, true); + } + }; + + var doCopy = function(e, isCut) { + var data = host.getCopyText(); + if (!data) + return event.preventDefault(e); + + if (handleClipboardData(e, data)) { + if (useragent.isIOS) { + cut = isCut; + text.value = "\n aa" + data + "a a\n"; + text.setSelectionRange(4, 4 + data.length); + copied = { + value: data + }; + } + isCut ? host.onCut() : host.onCopy(); + if (!useragent.isIOS) event.preventDefault(e); + } else { + copied = true; + text.value = data; + text.select(); + setTimeout(function(){ + copied = false; + resetValue(); + resetSelection(); + isCut ? host.onCut() : host.onCopy(); + }); + } + }; + + var onCut = function(e) { + doCopy(e, true); + }; + + var onCopy = function(e) { + doCopy(e, false); + }; + + var onPaste = function(e) { + var data = handleClipboardData(e); + if (typeof data == "string") { + if (data) + host.onPaste(data, e); + if (useragent.isIE) + setTimeout(resetSelection); + event.preventDefault(e); + } + else { + text.value = ""; + pasted = true; + } + }; + + event.addCommandKeyListener(text, host.onCommandKey.bind(host)); + + event.addListener(text, "select", onSelect); + + event.addListener(text, "input", onInput); + + event.addListener(text, "cut", onCut); + event.addListener(text, "copy", onCopy); + event.addListener(text, "paste", onPaste); + var onCompositionStart = function(e) { + if (inComposition || !host.onCompositionStart || host.$readOnly) + return; + inComposition = {}; + inComposition.canUndo = host.session.$undoManager; + host.onCompositionStart(); + setTimeout(onCompositionUpdate, 0); + host.on("mousedown", onCompositionEnd); + if (inComposition.canUndo && !host.selection.isEmpty()) { + host.insert(""); + host.session.markUndoGroup(); + host.selection.clearSelection(); + } + host.session.markUndoGroup(); + }; + + var onCompositionUpdate = function() { + if (!inComposition || !host.onCompositionUpdate || host.$readOnly) + return; + var val = text.value.replace(/\x01/g, ""); + if (inComposition.lastValue === val) return; + + host.onCompositionUpdate(val); + if (inComposition.lastValue) + host.undo(); + if (inComposition.canUndo) + inComposition.lastValue = val; + if (inComposition.lastValue) { + var r = host.selection.getRange(); + host.insert(inComposition.lastValue); + host.session.markUndoGroup(); + inComposition.range = host.selection.getRange(); + host.selection.setRange(r); + host.selection.clearSelection(); + } + }; + + var onCompositionEnd = function(e) { + if (!host.onCompositionEnd || host.$readOnly) return; + var c = inComposition; + inComposition = false; + var timer = setTimeout(function() { + timer = null; + var str = text.value.replace(/\x01/g, ""); + if (inComposition) + return; + else if (str == c.lastValue) + resetValue(); + else if (!c.lastValue && str) { + resetValue(); + sendText(str); + } + }); + inputHandler = function compositionInputHandler(str) { + if (timer) + clearTimeout(timer); + str = str.replace(/\x01/g, ""); + if (str == c.lastValue) + return ""; + if (c.lastValue && timer) + host.undo(); + return str; + }; + host.onCompositionEnd(); + host.removeListener("mousedown", onCompositionEnd); + if (e.type == "compositionend" && c.range) { + host.selection.setRange(c.range); + } + var needsOnInput = + (!!useragent.isChrome && useragent.isChrome >= 53) || + (!!useragent.isWebKit && useragent.isWebKit >= 603); + + if (needsOnInput) { + onInput(); + } + }; + + + + var syncComposition = lang.delayedCall(onCompositionUpdate, 50); + + event.addListener(text, "compositionstart", onCompositionStart); + if (useragent.isGecko) { + event.addListener(text, "text", function(){syncComposition.schedule();}); + } else { + event.addListener(text, "keyup", function(){syncComposition.schedule();}); + event.addListener(text, "keydown", function(){syncComposition.schedule();}); + } + event.addListener(text, "compositionend", onCompositionEnd); + + this.getElement = function() { + return text; + }; + + this.setReadOnly = function(readOnly) { + text.readOnly = readOnly; + }; + + this.onContextMenu = function(e) { + afterContextMenu = true; + resetSelection(host.selection.isEmpty()); + host._emit("nativecontextmenu", {target: host, domEvent: e}); + this.moveToMouse(e, true); + }; + + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) + tempStyle = text.style.cssText; + text.style.cssText = (bringToFront ? "z-index:100000;" : "") + + "height:" + text.style.height + ";" + + (useragent.isIE ? "opacity:0.1;" : ""); + + var rect = host.container.getBoundingClientRect(); + var style = dom.computedStyle(host.container); + var top = rect.top + (parseInt(style.borderTopWidth) || 0); + var left = rect.left + (parseInt(rect.borderLeftWidth) || 0); + var maxTop = rect.bottom - top - text.clientHeight -2; + var move = function(e) { + text.style.left = e.clientX - left - 2 + "px"; + text.style.top = Math.min(e.clientY - top - 2, maxTop) + "px"; + }; + move(e); + + if (e.type != "mousedown") + return; + + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$keepTextAreaAtCursor = null; + + clearTimeout(closeTimeout); + if (useragent.isWin) + event.capture(host.container, move, onContextMenuClose); + }; + + this.onContextMenuClose = onContextMenuClose; + var closeTimeout; + function onContextMenuClose() { + clearTimeout(closeTimeout); + closeTimeout = setTimeout(function () { + if (tempStyle) { + text.style.cssText = tempStyle; + tempStyle = ''; + } + if (host.renderer.$keepTextAreaAtCursor == null) { + host.renderer.$keepTextAreaAtCursor = true; + host.renderer.$moveTextAreaToCursor(); + } + }, 0); + } + + var onContextMenu = function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose(); + }; + event.addListener(text, "mouseup", onContextMenu); + event.addListener(text, "mousedown", function(e) { + e.preventDefault(); + onContextMenuClose(); + }); + event.addListener(host.renderer.scroller, "contextmenu", onContextMenu); + event.addListener(text, "contextmenu", onContextMenu); + + if (useragent.isIOS) { + var typingResetTimeout = null; + var typing = false; + + parentNode.addEventListener("keydown", function (e) { + if (typingResetTimeout) clearTimeout(typingResetTimeout); + typing = true; + }); + + parentNode.addEventListener("keyup", function (e) { + typingResetTimeout = setTimeout(function () { + typing = false; + }, 100); + }); + var detectArrowKeys = function(e) { + if (document.activeElement !== text) return; + if (typing) return; + + if (cut) { + return setTimeout(function () { + cut = false; + }, 100); + } + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + text.setSelectionRange(4, 5); + if (selectionStart == selectionEnd) { + switch (selectionStart) { + case 0: host.onCommandKey(null, 0, KEYS.up); break; + case 1: host.onCommandKey(null, 0, KEYS.home); break; + case 2: host.onCommandKey(null, MODS.option, KEYS.left); break; + case 4: host.onCommandKey(null, 0, KEYS.left); break; + case 5: host.onCommandKey(null, 0, KEYS.right); break; + case 7: host.onCommandKey(null, MODS.option, KEYS.right); break; + case 8: host.onCommandKey(null, 0, KEYS.end); break; + case 9: host.onCommandKey(null, 0, KEYS.down); break; + } + } else { + switch (selectionEnd) { + case 6: host.onCommandKey(null, MODS.shift, KEYS.right); break; + case 7: host.onCommandKey(null, MODS.shift | MODS.option, KEYS.right); break; + case 8: host.onCommandKey(null, MODS.shift, KEYS.end); break; + case 9: host.onCommandKey(null, MODS.shift, KEYS.down); break; + } + switch (selectionStart) { + case 0: host.onCommandKey(null, MODS.shift, KEYS.up); break; + case 1: host.onCommandKey(null, MODS.shift, KEYS.home); break; + case 2: host.onCommandKey(null, MODS.shift | MODS.option, KEYS.left); break; + case 3: host.onCommandKey(null, MODS.shift, KEYS.left); break; + } + } + }; + document.addEventListener("selectionchange", detectArrowKeys); + host.on("destroy", function() { + document.removeEventListener("selectionchange", detectArrowKeys); + }); + } + }; + + exports.TextInput = TextInput; + }); + + ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/keyboard/textinput_ios"], function(acequire, exports, module) { + "use strict"; + + var event = acequire("../lib/event"); + var useragent = acequire("../lib/useragent"); + var dom = acequire("../lib/dom"); + var lang = acequire("../lib/lang"); + var BROKEN_SETDATA = useragent.isChrome < 18; + var USE_IE_MIME_TYPE = useragent.isIE; + + var TextInputIOS = acequire("./textinput_ios").TextInput; + var TextInput = function(parentNode, host) { + if (useragent.isIOS) + return TextInputIOS.call(this, parentNode, host); + + var text = dom.createElement("textarea"); + text.className = "ace_text-input"; + + text.setAttribute("wrap", "off"); + text.setAttribute("autocorrect", "off"); + text.setAttribute("autocapitalize", "off"); + text.setAttribute("spellcheck", false); + + text.style.opacity = "0"; + parentNode.insertBefore(text, parentNode.firstChild); + + var PLACEHOLDER = "\u2028\u2028"; + + var copied = false; + var pasted = false; + var inComposition = false; + var tempStyle = ''; + var isSelectionEmpty = true; + try { var isFocused = document.activeElement === text; } catch(e) {} + + event.addListener(text, "blur", function(e) { + host.onBlur(e); + isFocused = false; + }); + event.addListener(text, "focus", function(e) { + isFocused = true; + host.onFocus(e); + resetSelection(); + }); + this.focus = function() { + if (tempStyle) return text.focus(); + var top = text.style.top; + text.style.position = "fixed"; + text.style.top = "0px"; + text.focus(); + setTimeout(function() { + text.style.position = ""; + if (text.style.top == "0px") + text.style.top = top; + }, 0); + }; + this.blur = function() { + text.blur(); + }; + this.isFocused = function() { + return isFocused; + }; + var syncSelection = lang.delayedCall(function() { + isFocused && resetSelection(isSelectionEmpty); + }); + var syncValue = lang.delayedCall(function() { + if (!inComposition) { + text.value = PLACEHOLDER; + isFocused && resetSelection(); + } + }); + + function resetSelection(isEmpty) { + if (inComposition) + return; + inComposition = true; + + if (inputHandler) { + var selectionStart = 0; + var selectionEnd = isEmpty ? 0 : text.value.length - 1; + } else { + var selectionStart = isEmpty ? 2 : 1; + var selectionEnd = 2; + } + try { + text.setSelectionRange(selectionStart, selectionEnd); + } catch(e){} + + inComposition = false; + } + + function resetValue() { + if (inComposition) + return; + text.value = PLACEHOLDER; + if (useragent.isWebKit) + syncValue.schedule(); + } + + useragent.isWebKit || host.addEventListener('changeSelection', function() { + if (host.selection.isEmpty() != isSelectionEmpty) { + isSelectionEmpty = !isSelectionEmpty; + syncSelection.schedule(); + } + }); + + resetValue(); + if (isFocused) + host.onFocus(); + + + var isAllSelected = function(text) { + return text.selectionStart === 0 && text.selectionEnd === text.value.length; + }; + + var onSelect = function(e) { + if (copied) { + copied = false; + } else if (isAllSelected(text)) { + host.selectAll(); + resetSelection(); + } else if (inputHandler) { + resetSelection(host.selection.isEmpty()); + } + }; + + var inputHandler = null; + this.setInputHandler = function(cb) {inputHandler = cb;}; + this.getInputHandler = function() {return inputHandler;}; + var afterContextMenu = false; + + var sendText = function(data) { + if (inputHandler) { + data = inputHandler(data); + inputHandler = null; + } + if (pasted) { + resetSelection(); + if (data) + host.onPaste(data); + pasted = false; + } else if (data == PLACEHOLDER.charAt(0)) { + if (afterContextMenu) + host.execCommand("del", {source: "ace"}); + else // some versions of android do not fire keydown when pressing backspace + host.execCommand("backspace", {source: "ace"}); + } else { + if (data.substring(0, 2) == PLACEHOLDER) + data = data.substr(2); + else if (data.charAt(0) == PLACEHOLDER.charAt(0)) + data = data.substr(1); + else if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0)) + data = data.slice(0, -1); + if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0)) + data = data.slice(0, -1); + + if (data) + host.onTextInput(data); + } + if (afterContextMenu) + afterContextMenu = false; + }; + var onInput = function(e) { + if (inComposition) + return; + var data = text.value; + sendText(data); + resetValue(); + }; + + var handleClipboardData = function(e, data, forceIEMime) { + var clipboardData = e.clipboardData || window.clipboardData; + if (!clipboardData || BROKEN_SETDATA) + return; + var mime = USE_IE_MIME_TYPE || forceIEMime ? "Text" : "text/plain"; + try { + if (data) { + return clipboardData.setData(mime, data) !== false; + } else { + return clipboardData.getData(mime); + } + } catch(e) { + if (!forceIEMime) + return handleClipboardData(e, data, true); + } + }; + + var doCopy = function(e, isCut) { + var data = host.getCopyText(); + if (!data) + return event.preventDefault(e); + + if (handleClipboardData(e, data)) { + isCut ? host.onCut() : host.onCopy(); + event.preventDefault(e); + } else { + copied = true; + text.value = data; + text.select(); + setTimeout(function(){ + copied = false; + resetValue(); + resetSelection(); + isCut ? host.onCut() : host.onCopy(); + }); + } + }; + + var onCut = function(e) { + doCopy(e, true); + }; + + var onCopy = function(e) { + doCopy(e, false); + }; + + var onPaste = function(e) { + var data = handleClipboardData(e); + if (typeof data == "string") { + if (data) + host.onPaste(data, e); + if (useragent.isIE) + setTimeout(resetSelection); + event.preventDefault(e); + } + else { + text.value = ""; + pasted = true; + } + }; + + event.addCommandKeyListener(text, host.onCommandKey.bind(host)); + + event.addListener(text, "select", onSelect); + + event.addListener(text, "input", onInput); + + event.addListener(text, "cut", onCut); + event.addListener(text, "copy", onCopy); + event.addListener(text, "paste", onPaste); + if (!('oncut' in text) || !('oncopy' in text) || !('onpaste' in text)) { + event.addListener(parentNode, "keydown", function(e) { + if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) + return; + + switch (e.keyCode) { + case 67: + onCopy(e); + break; + case 86: + onPaste(e); + break; + case 88: + onCut(e); + break; + } + }); + } + var onCompositionStart = function(e) { + if (inComposition || !host.onCompositionStart || host.$readOnly) + return; + inComposition = {}; + inComposition.canUndo = host.session.$undoManager; + host.onCompositionStart(); + setTimeout(onCompositionUpdate, 0); + host.on("mousedown", onCompositionEnd); + if (inComposition.canUndo && !host.selection.isEmpty()) { + host.insert(""); + host.session.markUndoGroup(); + host.selection.clearSelection(); + } + host.session.markUndoGroup(); + }; + + var onCompositionUpdate = function() { + if (!inComposition || !host.onCompositionUpdate || host.$readOnly) + return; + var val = text.value.replace(/\u2028/g, ""); + if (inComposition.lastValue === val) return; + + host.onCompositionUpdate(val); + if (inComposition.lastValue) + host.undo(); + if (inComposition.canUndo) + inComposition.lastValue = val; + if (inComposition.lastValue) { + var r = host.selection.getRange(); + host.insert(inComposition.lastValue); + host.session.markUndoGroup(); + inComposition.range = host.selection.getRange(); + host.selection.setRange(r); + host.selection.clearSelection(); + } + }; + + var onCompositionEnd = function(e) { + if (!host.onCompositionEnd || host.$readOnly) return; + var c = inComposition; + inComposition = false; + var timer = setTimeout(function() { + timer = null; + var str = text.value.replace(/\u2028/g, ""); + if (inComposition) + return; + else if (str == c.lastValue) + resetValue(); + else if (!c.lastValue && str) { + resetValue(); + sendText(str); + } + }); + inputHandler = function compositionInputHandler(str) { + if (timer) + clearTimeout(timer); + str = str.replace(/\u2028/g, ""); + if (str == c.lastValue) + return ""; + if (c.lastValue && timer) + host.undo(); + return str; + }; + host.onCompositionEnd(); + host.removeListener("mousedown", onCompositionEnd); + if (e.type == "compositionend" && c.range) { + host.selection.setRange(c.range); + } + var needsOnInput = + (!!useragent.isChrome && useragent.isChrome >= 53) || + (!!useragent.isWebKit && useragent.isWebKit >= 603); + + if (needsOnInput) { + onInput(); + } + }; + + + + var syncComposition = lang.delayedCall(onCompositionUpdate, 50); + + event.addListener(text, "compositionstart", onCompositionStart); + if (useragent.isGecko) { + event.addListener(text, "text", function(){syncComposition.schedule();}); + } else { + event.addListener(text, "keyup", function(){syncComposition.schedule();}); + event.addListener(text, "keydown", function(){syncComposition.schedule();}); + } + event.addListener(text, "compositionend", onCompositionEnd); + + this.getElement = function() { + return text; + }; + + this.setReadOnly = function(readOnly) { + text.readOnly = readOnly; + }; + + this.onContextMenu = function(e) { + afterContextMenu = true; + resetSelection(host.selection.isEmpty()); + host._emit("nativecontextmenu", {target: host, domEvent: e}); + this.moveToMouse(e, true); + }; + + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) + tempStyle = text.style.cssText; + text.style.cssText = (bringToFront ? "z-index:100000;" : "") + + "height:" + text.style.height + ";" + + (useragent.isIE ? "opacity:0.1;" : ""); + + var rect = host.container.getBoundingClientRect(); + var style = dom.computedStyle(host.container); + var top = rect.top + (parseInt(style.borderTopWidth) || 0); + var left = rect.left + (parseInt(rect.borderLeftWidth) || 0); + var maxTop = rect.bottom - top - text.clientHeight -2; + var move = function(e) { + text.style.left = e.clientX - left - 2 + "px"; + text.style.top = Math.min(e.clientY - top - 2, maxTop) + "px"; + }; + move(e); + + if (e.type != "mousedown") + return; + + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$keepTextAreaAtCursor = null; + + clearTimeout(closeTimeout); + if (useragent.isWin) + event.capture(host.container, move, onContextMenuClose); + }; + + this.onContextMenuClose = onContextMenuClose; + var closeTimeout; + function onContextMenuClose() { + clearTimeout(closeTimeout); + closeTimeout = setTimeout(function () { + if (tempStyle) { + text.style.cssText = tempStyle; + tempStyle = ''; + } + if (host.renderer.$keepTextAreaAtCursor == null) { + host.renderer.$keepTextAreaAtCursor = true; + host.renderer.$moveTextAreaToCursor(); + } + }, 0); + } + + var onContextMenu = function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose(); + }; + event.addListener(text, "mouseup", onContextMenu); + event.addListener(text, "mousedown", function(e) { + e.preventDefault(); + onContextMenuClose(); + }); + event.addListener(host.renderer.scroller, "contextmenu", onContextMenu); + event.addListener(text, "contextmenu", onContextMenu); + }; + + exports.TextInput = TextInput; + }); + + ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var dom = acequire("../lib/dom"); + var event = acequire("../lib/event"); + var useragent = acequire("../lib/useragent"); + + var DRAG_OFFSET = 0; // pixels + var SCROLL_COOLDOWN_T = 250; // milliseconds + + function DefaultHandlers(mouseHandler) { + mouseHandler.$clickSelection = null; + + var editor = mouseHandler.editor; + editor.setDefaultHandler("mousedown", this.onMouseDown.bind(mouseHandler)); + editor.setDefaultHandler("dblclick", this.onDoubleClick.bind(mouseHandler)); + editor.setDefaultHandler("tripleclick", this.onTripleClick.bind(mouseHandler)); + editor.setDefaultHandler("quadclick", this.onQuadClick.bind(mouseHandler)); + editor.setDefaultHandler("mousewheel", this.onMouseWheel.bind(mouseHandler)); + editor.setDefaultHandler("touchmove", this.onTouchMove.bind(mouseHandler)); + + var exports = ["select", "startSelect", "selectEnd", "selectAllEnd", "selectByWordsEnd", + "selectByLinesEnd", "dragWait", "dragWaitEnd", "focusWait"]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + + mouseHandler.selectByLines = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); + mouseHandler.selectByWords = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); + } + + (function() { + + this.onMouseDown = function(ev) { + var inSelection = ev.inSelection(); + var pos = ev.getDocumentPosition(); + this.mousedownEvent = ev; + var editor = this.editor; + + var button = ev.getButton(); + if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + editor.$blockScrolling++; + if (selectionEmpty || button == 1) + editor.selection.moveToPosition(pos); + editor.$blockScrolling--; + if (button == 2) { + editor.textInput.onContextMenu(ev.domEvent); + if (!useragent.isMozilla) + ev.preventDefault(); + } + return; + } + + this.mousedownEvent.time = Date.now(); + if (inSelection && !editor.isFocused()) { + editor.focus(); + if (this.$focusTimout && !this.$clickSelection && !editor.inMultiSelectMode) { + this.setState("focusWait"); + this.captureMouse(ev); + return; + } + } + + this.captureMouse(ev); + this.startSelect(pos, ev.domEvent._clicks > 1); + return ev.preventDefault(); + }; + + this.startSelect = function(pos, waitForClickSelection) { + pos = pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); + var editor = this.editor; + editor.$blockScrolling++; + if (this.mousedownEvent.getShiftKey()) + editor.selection.selectToPosition(pos); + else if (!waitForClickSelection) + editor.selection.moveToPosition(pos); + if (!waitForClickSelection) + this.select(); + if (editor.renderer.scroller.setCapture) { + editor.renderer.scroller.setCapture(); + } + editor.setStyle("ace_selecting"); + this.setState("select"); + editor.$blockScrolling--; + }; + + this.select = function() { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + editor.$blockScrolling++; + if (this.$clickSelection) { + var cmp = this.$clickSelection.comparePoint(cursor); + + if (cmp == -1) { + anchor = this.$clickSelection.end; + } else if (cmp == 1) { + anchor = this.$clickSelection.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.$blockScrolling--; + editor.renderer.scrollCursorIntoView(); + }; + + this.extendSelectionBy = function(unitName) { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + var range = editor.selection[unitName](cursor.row, cursor.column); + editor.$blockScrolling++; + if (this.$clickSelection) { + var cmpStart = this.$clickSelection.comparePoint(range.start); + var cmpEnd = this.$clickSelection.comparePoint(range.end); + + if (cmpStart == -1 && cmpEnd <= 0) { + anchor = this.$clickSelection.end; + if (range.end.row != cursor.row || range.end.column != cursor.column) + cursor = range.start; + } else if (cmpEnd == 1 && cmpStart >= 0) { + anchor = this.$clickSelection.start; + if (range.start.row != cursor.row || range.start.column != cursor.column) + cursor = range.end; + } else if (cmpStart == -1 && cmpEnd == 1) { + cursor = range.end; + anchor = range.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.$blockScrolling--; + editor.renderer.scrollCursorIntoView(); + }; + + this.selectEnd = + this.selectAllEnd = + this.selectByWordsEnd = + this.selectByLinesEnd = function() { + this.$clickSelection = null; + this.editor.unsetStyle("ace_selecting"); + if (this.editor.renderer.scroller.releaseCapture) { + this.editor.renderer.scroller.releaseCapture(); + } + }; + + this.focusWait = function() { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + var time = Date.now(); + + if (distance > DRAG_OFFSET || time - this.mousedownEvent.time > this.$focusTimout) + this.startSelect(this.mousedownEvent.getDocumentPosition()); + }; + + this.onDoubleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + var session = editor.session; + + var range = session.getBracketRange(pos); + if (range) { + if (range.isEmpty()) { + range.start.column--; + range.end.column++; + } + this.setState("select"); + } else { + range = editor.selection.getWordRange(pos.row, pos.column); + this.setState("selectByWords"); + } + this.$clickSelection = range; + this.select(); + }; + + this.onTripleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + + this.setState("selectByLines"); + var range = editor.getSelectionRange(); + if (range.isMultiLine() && range.contains(pos.row, pos.column)) { + this.$clickSelection = editor.selection.getLineRange(range.start.row); + this.$clickSelection.end = editor.selection.getLineRange(range.end.row).end; + } else { + this.$clickSelection = editor.selection.getLineRange(pos.row); + } + this.select(); + }; + + this.onQuadClick = function(ev) { + var editor = this.editor; + + editor.selectAll(); + this.$clickSelection = editor.getSelectionRange(); + this.setState("selectAll"); + }; + + this.onMouseWheel = function(ev) { + if (ev.getAccelKey()) + return; + if (ev.getShiftKey() && ev.wheelY && !ev.wheelX) { + ev.wheelX = ev.wheelY; + ev.wheelY = 0; + } + + var editor = this.editor; + + if (!this.$lastScroll) + this.$lastScroll = { t: 0, vx: 0, vy: 0, allowed: 0 }; + + var prevScroll = this.$lastScroll; + var t = ev.domEvent.timeStamp; + var dt = t - prevScroll.t; + var vx = ev.wheelX / dt; + var vy = ev.wheelY / dt; + if (dt < SCROLL_COOLDOWN_T) { + vx = (vx + prevScroll.vx) / 2; + vy = (vy + prevScroll.vy) / 2; + } + + var direction = Math.abs(vx / vy); + + var canScroll = false; + if (direction >= 1 && editor.renderer.isScrollableBy(ev.wheelX * ev.speed, 0)) + canScroll = true; + if (direction <= 1 && editor.renderer.isScrollableBy(0, ev.wheelY * ev.speed)) + canScroll = true; + + if (canScroll) { + prevScroll.allowed = t; + } else if (t - prevScroll.allowed < SCROLL_COOLDOWN_T) { + var isSlower = Math.abs(vx) <= 1.1 * Math.abs(prevScroll.vx) + && Math.abs(vy) <= 1.1 * Math.abs(prevScroll.vy); + if (isSlower) { + canScroll = true; + prevScroll.allowed = t; + } + else { + prevScroll.allowed = 0; + } + } + + prevScroll.t = t; + prevScroll.vx = vx; + prevScroll.vy = vy; + + if (canScroll) { + editor.renderer.scrollBy(ev.wheelX * ev.speed, ev.wheelY * ev.speed); + return ev.stop(); + } + }; + + this.onTouchMove = function(ev) { + this.editor._emit("mousewheel", ev); + }; + + }).call(DefaultHandlers.prototype); + + exports.DefaultHandlers = DefaultHandlers; + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + + function calcRangeOrientation(range, cursor) { + if (range.start.row == range.end.row) + var cmp = 2 * cursor.column - range.start.column - range.end.column; + else if (range.start.row == range.end.row - 1 && !range.start.column && !range.end.column) + var cmp = cursor.column - 4; + else + var cmp = 2 * cursor.row - range.start.row - range.end.row; + + if (cmp < 0) + return {cursor: range.start, anchor: range.end}; + else + return {cursor: range.end, anchor: range.start}; + } + + }); + + ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var dom = acequire("./lib/dom"); + function Tooltip (parentNode) { + this.isOpen = false; + this.$element = null; + this.$parentNode = parentNode; + } + + (function() { + this.$init = function() { + this.$element = dom.createElement("div"); + this.$element.className = "ace_tooltip"; + this.$element.style.display = "none"; + this.$parentNode.appendChild(this.$element); + return this.$element; + }; + this.getElement = function() { + return this.$element || this.$init(); + }; + this.setText = function(text) { + dom.setInnerText(this.getElement(), text); + }; + this.setHtml = function(html) { + this.getElement().innerHTML = html; + }; + this.setPosition = function(x, y) { + this.getElement().style.left = x + "px"; + this.getElement().style.top = y + "px"; + }; + this.setClassName = function(className) { + dom.addCssClass(this.getElement(), className); + }; + this.show = function(text, x, y) { + if (text != null) + this.setText(text); + if (x != null && y != null) + this.setPosition(x, y); + if (!this.isOpen) { + this.getElement().style.display = "block"; + this.isOpen = true; + } + }; + + this.hide = function() { + if (this.isOpen) { + this.getElement().style.display = "none"; + this.isOpen = false; + } + }; + this.getHeight = function() { + return this.getElement().offsetHeight; + }; + this.getWidth = function() { + return this.getElement().offsetWidth; + }; + + this.destroy = function() { + this.isOpen = false; + if (this.$element && this.$element.parentNode) { + this.$element.parentNode.removeChild(this.$element); + } + }; + + }).call(Tooltip.prototype); + + exports.Tooltip = Tooltip; + }); + + ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"], function(acequire, exports, module) { + "use strict"; + var dom = acequire("../lib/dom"); + var oop = acequire("../lib/oop"); + var event = acequire("../lib/event"); + var Tooltip = acequire("../tooltip").Tooltip; + + function GutterHandler(mouseHandler) { + var editor = mouseHandler.editor; + var gutter = editor.renderer.$gutterLayer; + var tooltip = new GutterTooltip(editor.container); + + mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { + if (!editor.isFocused() || e.getButton() != 0) + return; + var gutterRegion = gutter.getRegion(e); + + if (gutterRegion == "foldWidgets") + return; + + var row = e.getDocumentPosition().row; + var selection = editor.session.selection; + + if (e.getShiftKey()) + selection.selectTo(row, 0); + else { + if (e.domEvent.detail == 2) { + editor.selectAll(); + return e.preventDefault(); + } + mouseHandler.$clickSelection = editor.selection.getLineRange(row); + } + mouseHandler.setState("selectByLines"); + mouseHandler.captureMouse(e); + return e.preventDefault(); + }); + + + var tooltipTimeout, mouseEvent, tooltipAnnotation; + + function showTooltip() { + var row = mouseEvent.getDocumentPosition().row; + var annotation = gutter.$annotations[row]; + if (!annotation) + return hideTooltip(); + + var maxRow = editor.session.getLength(); + if (row == maxRow) { + var screenRow = editor.renderer.pixelToScreenCoordinates(0, mouseEvent.y).row; + var pos = mouseEvent.$pos; + if (screenRow > editor.session.documentToScreenRow(pos.row, pos.column)) + return hideTooltip(); + } + + if (tooltipAnnotation == annotation) + return; + tooltipAnnotation = annotation.text.join("
      "); + + tooltip.setHtml(tooltipAnnotation); + tooltip.show(); + editor._signal("showGutterTooltip", tooltip); + editor.on("mousewheel", hideTooltip); + + if (mouseHandler.$tooltipFollowsMouse) { + moveTooltip(mouseEvent); + } else { + var gutterElement = mouseEvent.domEvent.target; + var rect = gutterElement.getBoundingClientRect(); + var style = tooltip.getElement().style; + style.left = rect.right + "px"; + style.top = rect.bottom + "px"; + } + } + + function hideTooltip() { + if (tooltipTimeout) + tooltipTimeout = clearTimeout(tooltipTimeout); + if (tooltipAnnotation) { + tooltip.hide(); + tooltipAnnotation = null; + editor._signal("hideGutterTooltip", tooltip); + editor.removeEventListener("mousewheel", hideTooltip); + } + } + + function moveTooltip(e) { + tooltip.setPosition(e.x, e.y); + } + + mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { + var target = e.domEvent.target || e.domEvent.srcElement; + if (dom.hasCssClass(target, "ace_fold-widget")) + return hideTooltip(); + + if (tooltipAnnotation && mouseHandler.$tooltipFollowsMouse) + moveTooltip(e); + + mouseEvent = e; + if (tooltipTimeout) + return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + if (mouseEvent && !mouseHandler.isMousePressed) + showTooltip(); + else + hideTooltip(); + }, 50); + }); + + event.addListener(editor.renderer.$gutter, "mouseout", function(e) { + mouseEvent = null; + if (!tooltipAnnotation || tooltipTimeout) + return; + + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + hideTooltip(); + }, 50); + }); + + editor.on("changeSession", hideTooltip); + } + + function GutterTooltip(parentNode) { + Tooltip.call(this, parentNode); + } + + oop.inherits(GutterTooltip, Tooltip); + + (function(){ + this.setPosition = function(x, y) { + var windowWidth = window.innerWidth || document.documentElement.clientWidth; + var windowHeight = window.innerHeight || document.documentElement.clientHeight; + var width = this.getWidth(); + var height = this.getHeight(); + x += 15; + y += 15; + if (x + width > windowWidth) { + x -= (x + width) - windowWidth; + } + if (y + height > windowHeight) { + y -= 20 + height; + } + Tooltip.prototype.setPosition.call(this, x, y); + }; + + }).call(GutterTooltip.prototype); + + + + exports.GutterHandler = GutterHandler; + + }); + + ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var event = acequire("../lib/event"); + var useragent = acequire("../lib/useragent"); + var MouseEvent = exports.MouseEvent = function(domEvent, editor) { + this.domEvent = domEvent; + this.editor = editor; + + this.x = this.clientX = domEvent.clientX; + this.y = this.clientY = domEvent.clientY; + + this.$pos = null; + this.$inSelection = null; + + this.propagationStopped = false; + this.defaultPrevented = false; + }; + + (function() { + + this.stopPropagation = function() { + event.stopPropagation(this.domEvent); + this.propagationStopped = true; + }; + + this.preventDefault = function() { + event.preventDefault(this.domEvent); + this.defaultPrevented = true; + }; + + this.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + this.getDocumentPosition = function() { + if (this.$pos) + return this.$pos; + + this.$pos = this.editor.renderer.screenToTextCoordinates(this.clientX, this.clientY); + return this.$pos; + }; + this.inSelection = function() { + if (this.$inSelection !== null) + return this.$inSelection; + + var editor = this.editor; + + + var selectionRange = editor.getSelectionRange(); + if (selectionRange.isEmpty()) + this.$inSelection = false; + else { + var pos = this.getDocumentPosition(); + this.$inSelection = selectionRange.contains(pos.row, pos.column); + } + + return this.$inSelection; + }; + this.getButton = function() { + return event.getButton(this.domEvent); + }; + this.getShiftKey = function() { + return this.domEvent.shiftKey; + }; + + this.getAccelKey = useragent.isMac + ? function() { return this.domEvent.metaKey; } + : function() { return this.domEvent.ctrlKey; }; + + }).call(MouseEvent.prototype); + + }); + + ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var dom = acequire("../lib/dom"); + var event = acequire("../lib/event"); + var useragent = acequire("../lib/useragent"); + + var AUTOSCROLL_DELAY = 200; + var SCROLL_CURSOR_DELAY = 200; + var SCROLL_CURSOR_HYSTERESIS = 5; + + function DragdropHandler(mouseHandler) { + + var editor = mouseHandler.editor; + + var blankImage = dom.createElement("img"); + blankImage.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (useragent.isOpera) + blankImage.style.cssText = "width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;"; + + var exports = ["dragWait", "dragWaitEnd", "startDrag", "dragReadyEnd", "onMouseDrag"]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + editor.addEventListener("mousedown", this.onMouseDown.bind(mouseHandler)); + + + var mouseTarget = editor.container; + var dragSelectionMarker, x, y; + var timerId, range; + var dragCursor, counter = 0; + var dragOperation; + var isInternal; + var autoScrollStartTime; + var cursorMovedTime; + var cursorPointOnCaretMoved; + + this.onDragStart = function(e) { + if (this.cancelDrag || !mouseTarget.draggable) { + var self = this; + setTimeout(function(){ + self.startSelect(); + self.captureMouse(e); + }, 0); + return e.preventDefault(); + } + range = editor.getSelectionRange(); + + var dataTransfer = e.dataTransfer; + dataTransfer.effectAllowed = editor.getReadOnly() ? "copy" : "copyMove"; + if (useragent.isOpera) { + editor.container.appendChild(blankImage); + blankImage.scrollTop = 0; + } + dataTransfer.setDragImage && dataTransfer.setDragImage(blankImage, 0, 0); + if (useragent.isOpera) { + editor.container.removeChild(blankImage); + } + dataTransfer.clearData(); + dataTransfer.setData("Text", editor.session.getTextRange()); + + isInternal = true; + this.setState("drag"); + }; + + this.onDragEnd = function(e) { + mouseTarget.draggable = false; + isInternal = false; + this.setState(null); + if (!editor.getReadOnly()) { + var dropEffect = e.dataTransfer.dropEffect; + if (!dragOperation && dropEffect == "move") + editor.session.remove(editor.getSelectionRange()); + editor.renderer.$cursorLayer.setBlinking(true); + } + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + }; + + this.onDragEnter = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) + addDragMarker(); + counter++; + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragOver = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) { + addDragMarker(); + counter++; + } + if (onMouseMoveTimer !== null) + onMouseMoveTimer = null; + + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragLeave = function(e) { + counter--; + if (counter <= 0 && dragSelectionMarker) { + clearDragMarker(); + dragOperation = null; + return event.preventDefault(e); + } + }; + + this.onDrop = function(e) { + if (!dragCursor) + return; + var dataTransfer = e.dataTransfer; + if (isInternal) { + switch (dragOperation) { + case "move": + if (range.contains(dragCursor.row, dragCursor.column)) { + range = { + start: dragCursor, + end: dragCursor + }; + } else { + range = editor.moveText(range, dragCursor); + } + break; + case "copy": + range = editor.moveText(range, dragCursor, true); + break; + } + } else { + var dropData = dataTransfer.getData('Text'); + range = { + start: dragCursor, + end: editor.session.insert(dragCursor, dropData) + }; + editor.focus(); + dragOperation = null; + } + clearDragMarker(); + return event.preventDefault(e); + }; + + event.addListener(mouseTarget, "dragstart", this.onDragStart.bind(mouseHandler)); + event.addListener(mouseTarget, "dragend", this.onDragEnd.bind(mouseHandler)); + event.addListener(mouseTarget, "dragenter", this.onDragEnter.bind(mouseHandler)); + event.addListener(mouseTarget, "dragover", this.onDragOver.bind(mouseHandler)); + event.addListener(mouseTarget, "dragleave", this.onDragLeave.bind(mouseHandler)); + event.addListener(mouseTarget, "drop", this.onDrop.bind(mouseHandler)); + + function scrollCursorIntoView(cursor, prevCursor) { + var now = Date.now(); + var vMovement = !prevCursor || cursor.row != prevCursor.row; + var hMovement = !prevCursor || cursor.column != prevCursor.column; + if (!cursorMovedTime || vMovement || hMovement) { + editor.$blockScrolling += 1; + editor.moveCursorToPosition(cursor); + editor.$blockScrolling -= 1; + cursorMovedTime = now; + cursorPointOnCaretMoved = {x: x, y: y}; + } else { + var distance = calcDistance(cursorPointOnCaretMoved.x, cursorPointOnCaretMoved.y, x, y); + if (distance > SCROLL_CURSOR_HYSTERESIS) { + cursorMovedTime = null; + } else if (now - cursorMovedTime >= SCROLL_CURSOR_DELAY) { + editor.renderer.scrollCursorIntoView(); + cursorMovedTime = null; + } + } + } + + function autoScroll(cursor, prevCursor) { + var now = Date.now(); + var lineHeight = editor.renderer.layerConfig.lineHeight; + var characterWidth = editor.renderer.layerConfig.characterWidth; + var editorRect = editor.renderer.scroller.getBoundingClientRect(); + var offsets = { + x: { + left: x - editorRect.left, + right: editorRect.right - x + }, + y: { + top: y - editorRect.top, + bottom: editorRect.bottom - y + } + }; + var nearestXOffset = Math.min(offsets.x.left, offsets.x.right); + var nearestYOffset = Math.min(offsets.y.top, offsets.y.bottom); + var scrollCursor = {row: cursor.row, column: cursor.column}; + if (nearestXOffset / characterWidth <= 2) { + scrollCursor.column += (offsets.x.left < offsets.x.right ? -3 : +2); + } + if (nearestYOffset / lineHeight <= 1) { + scrollCursor.row += (offsets.y.top < offsets.y.bottom ? -1 : +1); + } + var vScroll = cursor.row != scrollCursor.row; + var hScroll = cursor.column != scrollCursor.column; + var vMovement = !prevCursor || cursor.row != prevCursor.row; + if (vScroll || (hScroll && !vMovement)) { + if (!autoScrollStartTime) + autoScrollStartTime = now; + else if (now - autoScrollStartTime >= AUTOSCROLL_DELAY) + editor.renderer.scrollCursorIntoView(scrollCursor); + } else { + autoScrollStartTime = null; + } + } + + function onDragInterval() { + var prevCursor = dragCursor; + dragCursor = editor.renderer.screenToTextCoordinates(x, y); + scrollCursorIntoView(dragCursor, prevCursor); + autoScroll(dragCursor, prevCursor); + } + + function addDragMarker() { + range = editor.selection.toOrientedRange(); + dragSelectionMarker = editor.session.addMarker(range, "ace_selection", editor.getSelectionStyle()); + editor.clearSelection(); + if (editor.isFocused()) + editor.renderer.$cursorLayer.setBlinking(false); + clearInterval(timerId); + onDragInterval(); + timerId = setInterval(onDragInterval, 20); + counter = 0; + event.addListener(document, "mousemove", onMouseMove); + } + + function clearDragMarker() { + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + editor.$blockScrolling += 1; + editor.selection.fromOrientedRange(range); + editor.$blockScrolling -= 1; + if (editor.isFocused() && !isInternal) + editor.renderer.$cursorLayer.setBlinking(!editor.getReadOnly()); + range = null; + dragCursor = null; + counter = 0; + autoScrollStartTime = null; + cursorMovedTime = null; + event.removeListener(document, "mousemove", onMouseMove); + } + var onMouseMoveTimer = null; + function onMouseMove() { + if (onMouseMoveTimer == null) { + onMouseMoveTimer = setTimeout(function() { + if (onMouseMoveTimer != null && dragSelectionMarker) + clearDragMarker(); + }, 20); + } + } + + function canAccept(dataTransfer) { + var types = dataTransfer.types; + return !types || Array.prototype.some.call(types, function(type) { + return type == 'text/plain' || type == 'Text'; + }); + } + + function getDropEffect(e) { + var copyAllowed = ['copy', 'copymove', 'all', 'uninitialized']; + var moveAllowed = ['move', 'copymove', 'linkmove', 'all', 'uninitialized']; + + var copyModifierState = useragent.isMac ? e.altKey : e.ctrlKey; + var effectAllowed = "uninitialized"; + try { + effectAllowed = e.dataTransfer.effectAllowed.toLowerCase(); + } catch (e) {} + var dropEffect = "none"; + + if (copyModifierState && copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + else if (moveAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "move"; + else if (copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + + return dropEffect; + } + } + + (function() { + + this.dragWait = function() { + var interval = Date.now() - this.mousedownEvent.time; + if (interval > this.editor.getDragDelay()) + this.startDrag(); + }; + + this.dragWaitEnd = function() { + var target = this.editor.container; + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + this.selectEnd(); + }; + + this.dragReadyEnd = function(e) { + this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()); + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + this.dragWaitEnd(); + }; + + this.startDrag = function(){ + this.cancelDrag = false; + var editor = this.editor; + var target = editor.container; + target.draggable = true; + editor.renderer.$cursorLayer.setBlinking(false); + editor.setStyle("ace_dragging"); + var cursorStyle = useragent.isWin ? "default" : "move"; + editor.renderer.setCursorStyle(cursorStyle); + this.setState("dragReady"); + }; + + this.onMouseDrag = function(e) { + var target = this.editor.container; + if (useragent.isIE && this.state == "dragReady") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 3) + target.dragDrop(); + } + if (this.state === "dragWait") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 0) { + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + } + } + }; + + this.onMouseDown = function(e) { + if (!this.$dragEnabled) + return; + this.mousedownEvent = e; + var editor = this.editor; + + var inSelection = e.inSelection(); + var button = e.getButton(); + var clickCount = e.domEvent.detail || 1; + if (clickCount === 1 && button === 0 && inSelection) { + if (e.editor.inMultiSelectMode && (e.getAccelKey() || e.getShiftKey())) + return; + this.mousedownEvent.time = Date.now(); + var eventTarget = e.domEvent.target || e.domEvent.srcElement; + if ("unselectable" in eventTarget) + eventTarget.unselectable = "on"; + if (editor.getDragDelay()) { + if (useragent.isWebKit) { + this.cancelDrag = true; + var mouseTarget = editor.container; + mouseTarget.draggable = true; + } + this.setState("dragWait"); + } else { + this.startDrag(); + } + this.captureMouse(e, this.onMouseDrag.bind(this)); + e.defaultPrevented = true; + } + }; + + }).call(DragdropHandler.prototype); + + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + + exports.DragdropHandler = DragdropHandler; + + }); + + ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) { + "use strict"; + var dom = acequire("./dom"); + + exports.get = function (url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); + }; + + exports.loadScript = function(path, callback) { + var head = dom.getDocumentHead(); + var s = document.createElement('script'); + + s.src = path; + head.appendChild(s); + + s.onload = s.onreadystatechange = function(_, isAbort) { + if (isAbort || !s.readyState || s.readyState == "loaded" || s.readyState == "complete") { + s = s.onload = s.onreadystatechange = null; + if (!isAbort) + callback(); + } + }; + }; + exports.qualifyURL = function(url) { + var a = document.createElement('a'); + a.href = url; + return a.href; + }; + + }); + + ace.define("ace/lib/event_emitter",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + var EventEmitter = {}; + var stopPropagation = function() { this.propagationStopped = true; }; + var preventDefault = function() { this.defaultPrevented = true; }; + + EventEmitter._emit = + EventEmitter._dispatchEvent = function(eventName, e) { + this._eventRegistry || (this._eventRegistry = {}); + this._defaultHandlers || (this._defaultHandlers = {}); + + var listeners = this._eventRegistry[eventName] || []; + var defaultHandler = this._defaultHandlers[eventName]; + if (!listeners.length && !defaultHandler) + return; + + if (typeof e != "object" || !e) + e = {}; + + if (!e.type) + e.type = eventName; + if (!e.stopPropagation) + e.stopPropagation = stopPropagation; + if (!e.preventDefault) + e.preventDefault = preventDefault; + + listeners = listeners.slice(); + for (var i=0; i 1) + base = parts[parts.length - 2]; + var path = options[component + "Path"]; + if (path == null) { + path = options.basePath; + } else if (sep == "/") { + component = sep = ""; + } + if (path && path.slice(-1) != "/") + path += "/"; + return path + component + sep + base + this.get("suffix"); + }; + + exports.setModuleUrl = function(name, subst) { + return options.$moduleUrls[name] = subst; + }; + + exports.$loading = {}; + exports.loadModule = function(moduleName, onLoad) { + var module, moduleType; + if (Array.isArray(moduleName)) { + moduleType = moduleName[0]; + moduleName = moduleName[1]; + } + + try { + module = acequire(moduleName); + } catch (e) {} + if (module && !exports.$loading[moduleName]) + return onLoad && onLoad(module); + + if (!exports.$loading[moduleName]) + exports.$loading[moduleName] = []; + + exports.$loading[moduleName].push(onLoad); + + if (exports.$loading[moduleName].length > 1) + return; + + var afterLoad = function() { + acequire([moduleName], function(module) { + exports._emit("load.module", {name: moduleName, module: module}); + var listeners = exports.$loading[moduleName]; + exports.$loading[moduleName] = null; + listeners.forEach(function(onLoad) { + onLoad && onLoad(module); + }); + }); + }; + + if (!exports.get("packaged")) + return afterLoad(); + net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); + }; + init(true);function init(packaged) { + + if (!global || !global.document) + return; + + options.packaged = packaged || acequire.packaged || module.packaged || (global.define && __webpack_require__(67).packaged); + + var scriptOptions = {}; + var scriptUrl = ""; + var currentScript = (document.currentScript || document._currentScript ); // native or polyfill + var currentDocument = currentScript && currentScript.ownerDocument || document; + + var scripts = currentDocument.getElementsByTagName("script"); + for (var i=0; i 0){ + if (action == 0x10){ + for(i = condPos; i < ix; i++){ + levels[i] = 1; + } + condPos = -1; + } else { + condPos = -1; + } + } + cond = impTab[newState][6]; + if (cond){ + if(condPos == -1){ + condPos = ix; + } + }else{ + if (condPos > -1){ + for(i = condPos; i < ix; i++){ + levels[i] = newLevel; + } + condPos = -1; + } + } + if (charTypes[ix] == B){ + levels[ix] = 0; + } + hiLevel |= newLevel; + } + if (hasUBAT_S){ + for(i = 0; i < len; i++){ + if(charTypes[i] == S){ + levels[i] = dir; + for(var j = i - 1; j >= 0; j--){ + if(charTypes[j] == WS){ + levels[j] = dir; + }else{ + break; + } + } + } + } + } + } + + function _invertLevel(lev, levels, _array) { + if (hiLevel < lev){ + return; + } + if (lev == 1 && dir == RTL && !hasUBAT_B){ + _array.reverse(); + return; + } + var len = _array.length, start = 0, end, lo, hi, tmp; + while(start < len){ + if (levels[start] >= lev){ + end = start + 1; + while(end < len && levels[end] >= lev){ + end++; + } + for(lo = start, hi = end - 1 ; lo < hi; lo++, hi--){ + tmp = _array[lo]; + _array[lo] = _array[hi]; + _array[hi] = tmp; + } + start = end; + } + start++; + } + } + + function _getCharClass(chars, types, classes, ix) { + var cType = types[ix], wType, nType, len, i; + switch(cType){ + case L: + case R: + lastArabic = false; + case ON: + case AN: + return cType; + case EN: + return lastArabic ? AN : EN; + case AL: + lastArabic = true; + hasUBAT_AL = true; + return R; + case WS: + return ON; + case CS: + if (ix < 1 || (ix + 1) >= types.length || + ((wType = classes[ix - 1]) != EN && wType != AN) || + ((nType = types[ix + 1]) != EN && nType != AN)){ + return ON; + } + if (lastArabic){nType = AN;} + return nType == wType ? nType : ON; + case ES: + wType = ix > 0 ? classes[ix - 1] : B; + if (wType == EN && (ix + 1) < types.length && types[ix + 1] == EN){ + return EN; + } + return ON; + case ET: + if (ix > 0 && classes[ix - 1] == EN){ + return EN; + } + if (lastArabic){ + return ON; + } + i = ix + 1; + len = types.length; + while (i < len && types[i] == ET){ + i++; + } + if (i < len && types[i] == EN){ + return EN; + } + return ON; + case NSM: + len = types.length; + i = ix + 1; + while (i < len && types[i] == NSM){ + i++; + } + if (i < len){ + var c = chars[ix], rtlCandidate = (c >= 0x0591 && c <= 0x08FF) || c == 0xFB1E; + + wType = types[i]; + if (rtlCandidate && (wType == R || wType == AL)){ + return R; + } + } + + if (ix < 1 || (wType = types[ix - 1]) == B){ + return ON; + } + return classes[ix - 1]; + case B: + lastArabic = false; + hasUBAT_B = true; + return dir; + case S: + hasUBAT_S = true; + return ON; + case LRE: + case RLE: + case LRO: + case RLO: + case PDF: + lastArabic = false; + case BN: + return ON; + } + } + + function _getCharacterType( ch ) { + var uc = ch.charCodeAt(0), hi = uc >> 8; + + if (hi == 0) { + return ((uc > 0x00BF) ? L : UnicodeTBL00[uc]); + } else if (hi == 5) { + return (/[\u0591-\u05f4]/.test(ch) ? R : L); + } else if (hi == 6) { + if (/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(ch)) + return NSM; + else if (/[\u0660-\u0669\u066b-\u066c]/.test(ch)) + return AN; + else if (uc == 0x066A) + return ET; + else if (/[\u06f0-\u06f9]/.test(ch)) + return EN; + else + return AL; + } else if (hi == 0x20 && uc <= 0x205F) { + return UnicodeTBL20[uc & 0xFF]; + } else if (hi == 0xFE) { + return (uc >= 0xFE70 ? AL : ON); + } + return ON; + } + + function _isArabicDiacritics( ch ) { + return (ch >= '\u064b' && ch <= '\u0655'); + } + exports.L = L; + exports.R = R; + exports.EN = EN; + exports.ON_R = 3; + exports.AN = 4; + exports.R_H = 5; + exports.B = 6; + + exports.DOT = "\xB7"; + exports.doBidiReorder = function(text, textCharTypes, isRtl) { + if (text.length < 2) + return {}; + + var chars = text.split(""), logicalFromVisual = new Array(chars.length), + bidiLevels = new Array(chars.length), levels = []; + + dir = isRtl ? RTL : LTR; + + _computeLevels(chars, levels, chars.length, textCharTypes); + + for (var i = 0; i < logicalFromVisual.length; logicalFromVisual[i] = i, i++); + + _invertLevel(2, levels, logicalFromVisual); + _invertLevel(1, levels, logicalFromVisual); + + for (var i = 0; i < logicalFromVisual.length - 1; i++) { //fix levels to reflect character width + if (textCharTypes[i] === AN) { + levels[i] = exports.AN; + } else if (levels[i] === R && ((textCharTypes[i] > AL && textCharTypes[i] < LRE) + || textCharTypes[i] === ON || textCharTypes[i] === BN)) { + levels[i] = exports.ON_R; + } else if ((i > 0 && chars[i - 1] === '\u0644') && /\u0622|\u0623|\u0625|\u0627/.test(chars[i])) { + levels[i - 1] = levels[i] = exports.R_H; + i++; + } + } + if (chars[chars.length - 1] === exports.DOT) + levels[chars.length - 1] = exports.B; + + for (var i = 0; i < logicalFromVisual.length; i++) { + bidiLevels[i] = levels[logicalFromVisual[i]]; + } + + return {'logicalFromVisual': logicalFromVisual, 'bidiLevels': bidiLevels}; + }; + exports.hasBidiCharacters = function(text, textCharTypes){ + var ret = false; + for (var i = 0; i < text.length; i++){ + textCharTypes[i] = _getCharacterType(text.charAt(i)); + if (!ret && (textCharTypes[i] == R || textCharTypes[i] == AL)) + ret = true; + } + return ret; + }; + exports.getVisualFromLogicalIdx = function(logIdx, rowMap) { + for (var i = 0; i < rowMap.logicalFromVisual.length; i++) { + if (rowMap.logicalFromVisual[i] == logIdx) + return i; + } + return 0; + }; + + }); + + ace.define("ace/bidihandler",["require","exports","module","ace/lib/bidiutil","ace/lib/lang","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var bidiUtil = acequire("./lib/bidiutil"); + var lang = acequire("./lib/lang"); + var useragent = acequire("./lib/useragent"); + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var BidiHandler = function(session) { + this.session = session; + this.bidiMap = {}; + this.currentRow = null; + this.bidiUtil = bidiUtil; + this.charWidths = []; + this.EOL = "\xAC"; + this.showInvisibles = true; + this.isRtlDir = false; + this.line = ""; + this.wrapIndent = 0; + this.isLastRow = false; + this.EOF = "\xB6"; + this.seenBidi = false; + }; + + (function() { + this.isBidiRow = function(screenRow, docRow, splitIndex) { + if (!this.seenBidi) + return false; + if (screenRow !== this.currentRow) { + this.currentRow = screenRow; + this.updateRowLine(docRow, splitIndex); + this.updateBidiMap(); + } + return this.bidiMap.bidiLevels; + }; + + this.onChange = function(delta) { + if (!this.seenBidi) { + if (delta.action == "insert" && bidiRE.test(delta.lines.join("\n"))) { + this.seenBidi = true; + this.currentRow = null; + } + } + else { + this.currentRow = null; + } + }; + + this.getDocumentRow = function() { + var docRow = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var index = this.session.$getRowCacheIndex(rowCache, this.currentRow); + if (index >= 0) + docRow = this.session.$docRowCache[index]; + } + + return docRow; + }; + + this.getSplitIndex = function() { + var splitIndex = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow); + while (this.currentRow - splitIndex > 0) { + currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1); + if (currentIndex !== prevIndex) + break; + + prevIndex = currentIndex; + splitIndex++; + } + } + + return splitIndex; + }; + + this.updateRowLine = function(docRow, splitIndex) { + if (docRow === undefined) + docRow = this.getDocumentRow(); + + this.wrapIndent = 0; + this.isLastRow = (docRow === this.session.getLength() - 1); + this.line = this.session.getLine(docRow); + if (this.session.$useWrapMode) { + var splits = this.session.$wrapData[docRow]; + if (splits) { + if (splitIndex === undefined) + splitIndex = this.getSplitIndex(); + + if(splitIndex > 0 && splits.length) { + this.wrapIndent = splits.indent; + this.line = (splitIndex < splits.length) ? + this.line.substring(splits[splitIndex - 1], splits[splits.length - 1]) : + this.line.substring(splits[splits.length - 1]); + } else { + this.line = this.line.substring(0, splits[splitIndex]); + } + } + } + var session = this.session, shift = 0, size; + this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){ + if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) { + size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2; + shift += size - 1; + return lang.stringRepeat(bidiUtil.DOT, size); + } + return ch; + }); + }; + + this.updateBidiMap = function() { + var textCharTypes = [], endOfLine = this.isLastRow ? this.EOF : this.EOL; + var line = this.line + (this.showInvisibles ? endOfLine : bidiUtil.DOT); + if (bidiUtil.hasBidiCharacters(line, textCharTypes)) { + this.bidiMap = bidiUtil.doBidiReorder(line, textCharTypes, this.isRtlDir); + } else { + this.bidiMap = {}; + } + }; + this.markAsDirty = function() { + this.currentRow = null; + }; + this.updateCharacterWidths = function(fontMetrics) { + if (!this.seenBidi) + return; + if (this.characterWidth === fontMetrics.$characterSize.width) + return; + + var characterWidth = this.characterWidth = fontMetrics.$characterSize.width; + var bidiCharWidth = fontMetrics.$measureCharWidth("\u05d4"); + + this.charWidths[bidiUtil.L] = this.charWidths[bidiUtil.EN] = this.charWidths[bidiUtil.ON_R] = characterWidth; + this.charWidths[bidiUtil.R] = this.charWidths[bidiUtil.AN] = bidiCharWidth; + this.charWidths[bidiUtil.R_H] = useragent.isChrome ? bidiCharWidth : bidiCharWidth * 0.45; + this.charWidths[bidiUtil.B] = 0; + + this.currentRow = null; + }; + + this.getShowInvisibles = function() { + return this.showInvisibles; + }; + + this.setShowInvisibles = function(showInvisibles) { + this.showInvisibles = showInvisibles; + this.currentRow = null; + }; + + this.setEolChar = function(eolChar) { + this.EOL = eolChar; + }; + + this.setTextDir = function(isRtlDir) { + this.isRtlDir = isRtlDir; + }; + this.getPosLeft = function(col) { + col -= this.wrapIndent; + var visualIdx = bidiUtil.getVisualFromLogicalIdx(col > 0 ? col - 1 : 0, this.bidiMap), + levels = this.bidiMap.bidiLevels, left = 0; + + if (col === 0 && levels[visualIdx] % 2 !== 0) + visualIdx++; + + for (var i = 0; i < visualIdx; i++) { + left += this.charWidths[levels[i]]; + } + + if (col !== 0 && levels[visualIdx] % 2 === 0) + left += this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) + left += this.wrapIndent * this.charWidths[bidiUtil.L]; + + return left; + }; + this.getSelections = function(startCol, endCol) { + var map = this.bidiMap, levels = map.bidiLevels, level, offset = this.wrapIndent * this.charWidths[bidiUtil.L], selections = [], + selColMin = Math.min(startCol, endCol) - this.wrapIndent, selColMax = Math.max(startCol, endCol) - this.wrapIndent, + isSelected = false, isSelectedPrev = false, selectionStart = 0; + + for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) { + logIdx = map.logicalFromVisual[visIdx]; + level = levels[visIdx]; + isSelected = (logIdx >= selColMin) && (logIdx < selColMax); + if (isSelected && !isSelectedPrev) { + selectionStart = offset; + } else if (!isSelected && isSelectedPrev) { + selections.push({left: selectionStart, width: offset - selectionStart}); + } + offset += this.charWidths[level]; + isSelectedPrev = isSelected; + } + + if (isSelected && (visIdx === levels.length)) { + selections.push({left: selectionStart, width: offset - selectionStart}); + } + + return selections; + }; + this.offsetToCol = function(posX) { + var logicalIdx = 0, posX = Math.max(posX, 0), + offset = 0, visualIdx = 0, levels = this.bidiMap.bidiLevels, + charWidth = this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) { + posX -= this.wrapIndent * this.charWidths[bidiUtil.L]; + } + + while(posX > offset + charWidth/2) { + offset += charWidth; + if(visualIdx === levels.length - 1) { + charWidth = 0; + break; + } + charWidth = this.charWidths[levels[++visualIdx]]; + } + + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && (levels[visualIdx] % 2 === 0)){ + if(posX < offset) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + + } else if (visualIdx > 0 && (levels[visualIdx - 1] % 2 === 0) && (levels[visualIdx] % 2 !== 0)){ + logicalIdx = 1 + ((posX > offset) ? this.bidiMap.logicalFromVisual[visualIdx] + : this.bidiMap.logicalFromVisual[visualIdx - 1]); + + } else if ((this.isRtlDir && visualIdx === levels.length - 1 && charWidth === 0 && (levels[visualIdx - 1] % 2 === 0)) + || (!this.isRtlDir && visualIdx === 0 && (levels[visualIdx] % 2 !== 0))){ + logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx]; + } else { + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && charWidth !== 0) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } + + return (logicalIdx + this.wrapIndent); + }; + + }).call(BidiHandler.prototype); + + exports.BidiHandler = BidiHandler; + }); + + ace.define("ace/range",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + var comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + var Range = function(startRow, startColumn, endRow, endColumn) { + this.start = { + row: startRow, + column: startColumn + }; + + this.end = { + row: endRow, + column: endColumn + }; + }; + + (function() { + this.isEqual = function(range) { + return this.start.row === range.start.row && + this.end.row === range.end.row && + this.start.column === range.start.column && + this.end.column === range.end.column; + }; + this.toString = function() { + return ("Range: [" + this.start.row + "/" + this.start.column + + "] -> [" + this.end.row + "/" + this.end.column + "]"); + }; + + this.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + this.compareRange = function(range) { + var cmp, + end = range.end, + start = range.start; + + cmp = this.compare(end.row, end.column); + if (cmp == 1) { + cmp = this.compare(start.row, start.column); + if (cmp == 1) { + return 2; + } else if (cmp == 0) { + return 1; + } else { + return 0; + } + } else if (cmp == -1) { + return -2; + } else { + cmp = this.compare(start.row, start.column); + if (cmp == -1) { + return -1; + } else if (cmp == 1) { + return 42; + } else { + return 0; + } + } + }; + this.comparePoint = function(p) { + return this.compare(p.row, p.column); + }; + this.containsRange = function(range) { + return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0; + }; + this.intersects = function(range) { + var cmp = this.compareRange(range); + return (cmp == -1 || cmp == 0 || cmp == 1); + }; + this.isEnd = function(row, column) { + return this.end.row == row && this.end.column == column; + }; + this.isStart = function(row, column) { + return this.start.row == row && this.start.column == column; + }; + this.setStart = function(row, column) { + if (typeof row == "object") { + this.start.column = row.column; + this.start.row = row.row; + } else { + this.start.row = row; + this.start.column = column; + } + }; + this.setEnd = function(row, column) { + if (typeof row == "object") { + this.end.column = row.column; + this.end.row = row.row; + } else { + this.end.row = row; + this.end.column = column; + } + }; + this.inside = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column) || this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideStart = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideEnd = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); + } + } + + if (row < this.start.row) + return -1; + + if (row > this.end.row) + return 1; + + if (this.start.row === row) + return column >= this.start.column ? 0 : -1; + + if (this.end.row === row) + return column <= this.end.column ? 0 : 1; + + return 0; + }; + this.compareStart = function(row, column) { + if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.compareEnd = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else { + return this.compare(row, column); + } + }; + this.compareInside = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.clipRows = function(firstRow, lastRow) { + if (this.end.row > lastRow) + var end = {row: lastRow + 1, column: 0}; + else if (this.end.row < firstRow) + var end = {row: firstRow, column: 0}; + + if (this.start.row > lastRow) + var start = {row: lastRow + 1, column: 0}; + else if (this.start.row < firstRow) + var start = {row: firstRow, column: 0}; + + return Range.fromPoints(start || this.start, end || this.end); + }; + this.extend = function(row, column) { + var cmp = this.compare(row, column); + + if (cmp == 0) + return this; + else if (cmp == -1) + var start = {row: row, column: column}; + else + var end = {row: row, column: column}; + + return Range.fromPoints(start || this.start, end || this.end); + }; + + this.isEmpty = function() { + return (this.start.row === this.end.row && this.start.column === this.end.column); + }; + this.isMultiLine = function() { + return (this.start.row !== this.end.row); + }; + this.clone = function() { + return Range.fromPoints(this.start, this.end); + }; + this.collapseRows = function() { + if (this.end.column == 0) + return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0); + else + return new Range(this.start.row, 0, this.end.row, 0); + }; + this.toScreenRange = function(session) { + var screenPosStart = session.documentToScreenPosition(this.start); + var screenPosEnd = session.documentToScreenPosition(this.end); + + return new Range( + screenPosStart.row, screenPosStart.column, + screenPosEnd.row, screenPosEnd.column + ); + }; + this.moveBy = function(row, column) { + this.start.row += row; + this.start.column += column; + this.end.row += row; + this.end.column += column; + }; + + }).call(Range.prototype); + Range.fromPoints = function(start, end) { + return new Range(start.row, start.column, end.row, end.column); + }; + Range.comparePoints = comparePoints; + + Range.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + + + exports.Range = Range; + }); + + ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var lang = acequire("./lib/lang"); + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var Range = acequire("./range").Range; + var Selection = function(session) { + this.session = session; + this.doc = session.getDocument(); + + this.clearSelection(); + this.lead = this.selectionLead = this.doc.createAnchor(0, 0); + this.anchor = this.selectionAnchor = this.doc.createAnchor(0, 0); + + var self = this; + this.lead.on("change", function(e) { + self._emit("changeCursor"); + if (!self.$isEmpty) + self._emit("changeSelection"); + if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column) + self.$desiredColumn = null; + }); + + this.selectionAnchor.on("change", function() { + if (!self.$isEmpty) + self._emit("changeSelection"); + }); + }; + + (function() { + + oop.implement(this, EventEmitter); + this.isEmpty = function() { + return (this.$isEmpty || ( + this.anchor.row == this.lead.row && + this.anchor.column == this.lead.column + )); + }; + this.isMultiLine = function() { + if (this.isEmpty()) { + return false; + } + + return this.getRange().isMultiLine(); + }; + this.getCursor = function() { + return this.lead.getPosition(); + }; + this.setSelectionAnchor = function(row, column) { + this.anchor.setPosition(row, column); + + if (this.$isEmpty) { + this.$isEmpty = false; + this._emit("changeSelection"); + } + }; + this.getSelectionAnchor = function() { + if (this.$isEmpty) + return this.getSelectionLead(); + else + return this.anchor.getPosition(); + }; + this.getSelectionLead = function() { + return this.lead.getPosition(); + }; + this.shiftSelection = function(columns) { + if (this.$isEmpty) { + this.moveCursorTo(this.lead.row, this.lead.column + columns); + return; + } + + var anchor = this.getSelectionAnchor(); + var lead = this.getSelectionLead(); + + var isBackwards = this.isBackwards(); + + if (!isBackwards || anchor.column !== 0) + this.setSelectionAnchor(anchor.row, anchor.column + columns); + + if (isBackwards || lead.column !== 0) { + this.$moveSelection(function() { + this.moveCursorTo(lead.row, lead.column + columns); + }); + } + }; + this.isBackwards = function() { + var anchor = this.anchor; + var lead = this.lead; + return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column)); + }; + this.getRange = function() { + var anchor = this.anchor; + var lead = this.lead; + + if (this.isEmpty()) + return Range.fromPoints(lead, lead); + + if (this.isBackwards()) { + return Range.fromPoints(lead, anchor); + } + else { + return Range.fromPoints(anchor, lead); + } + }; + this.clearSelection = function() { + if (!this.$isEmpty) { + this.$isEmpty = true; + this._emit("changeSelection"); + } + }; + this.selectAll = function() { + var lastRow = this.doc.getLength() - 1; + this.setSelectionAnchor(0, 0); + this.moveCursorTo(lastRow, this.doc.getLine(lastRow).length); + }; + this.setRange = + this.setSelectionRange = function(range, reverse) { + if (reverse) { + this.setSelectionAnchor(range.end.row, range.end.column); + this.selectTo(range.start.row, range.start.column); + } else { + this.setSelectionAnchor(range.start.row, range.start.column); + this.selectTo(range.end.row, range.end.column); + } + if (this.getRange().isEmpty()) + this.$isEmpty = true; + this.$desiredColumn = null; + }; + + this.$moveSelection = function(mover) { + var lead = this.lead; + if (this.$isEmpty) + this.setSelectionAnchor(lead.row, lead.column); + + mover.call(this); + }; + this.selectTo = function(row, column) { + this.$moveSelection(function() { + this.moveCursorTo(row, column); + }); + }; + this.selectToPosition = function(pos) { + this.$moveSelection(function() { + this.moveCursorToPosition(pos); + }); + }; + this.moveTo = function(row, column) { + this.clearSelection(); + this.moveCursorTo(row, column); + }; + this.moveToPosition = function(pos) { + this.clearSelection(); + this.moveCursorToPosition(pos); + }; + this.selectUp = function() { + this.$moveSelection(this.moveCursorUp); + }; + this.selectDown = function() { + this.$moveSelection(this.moveCursorDown); + }; + this.selectRight = function() { + this.$moveSelection(this.moveCursorRight); + }; + this.selectLeft = function() { + this.$moveSelection(this.moveCursorLeft); + }; + this.selectLineStart = function() { + this.$moveSelection(this.moveCursorLineStart); + }; + this.selectLineEnd = function() { + this.$moveSelection(this.moveCursorLineEnd); + }; + this.selectFileEnd = function() { + this.$moveSelection(this.moveCursorFileEnd); + }; + this.selectFileStart = function() { + this.$moveSelection(this.moveCursorFileStart); + }; + this.selectWordRight = function() { + this.$moveSelection(this.moveCursorWordRight); + }; + this.selectWordLeft = function() { + this.$moveSelection(this.moveCursorWordLeft); + }; + this.getWordRange = function(row, column) { + if (typeof column == "undefined") { + var cursor = row || this.lead; + row = cursor.row; + column = cursor.column; + } + return this.session.getWordRange(row, column); + }; + this.selectWord = function() { + this.setSelectionRange(this.getWordRange()); + }; + this.selectAWord = function() { + var cursor = this.getCursor(); + var range = this.session.getAWordRange(cursor.row, cursor.column); + this.setSelectionRange(range); + }; + + this.getLineRange = function(row, excludeLastChar) { + var rowStart = typeof row == "number" ? row : this.lead.row; + var rowEnd; + + var foldLine = this.session.getFoldLine(rowStart); + if (foldLine) { + rowStart = foldLine.start.row; + rowEnd = foldLine.end.row; + } else { + rowEnd = rowStart; + } + if (excludeLastChar === true) + return new Range(rowStart, 0, rowEnd, this.session.getLine(rowEnd).length); + else + return new Range(rowStart, 0, rowEnd + 1, 0); + }; + this.selectLine = function() { + this.setSelectionRange(this.getLineRange()); + }; + this.moveCursorUp = function() { + this.moveCursorBy(-1, 0); + }; + this.moveCursorDown = function() { + this.moveCursorBy(1, 0); + }; + this.wouldMoveIntoSoftTab = function(cursor, tabSize, direction) { + var start = cursor.column; + var end = cursor.column + tabSize; + + if (direction < 0) { + start = cursor.column - tabSize; + end = cursor.column; + } + return this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(start, end).split(" ").length-1 == tabSize; + }; + this.moveCursorLeft = function() { + var cursor = this.lead.getPosition(), + fold; + + if (fold = this.session.getFoldAt(cursor.row, cursor.column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + } else if (cursor.column === 0) { + if (cursor.row > 0) { + this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length); + } + } + else { + var tabSize = this.session.getTabSize(); + if (this.wouldMoveIntoSoftTab(cursor, tabSize, -1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, -tabSize); + } else { + this.moveCursorBy(0, -1); + } + } + }; + this.moveCursorRight = function() { + var cursor = this.lead.getPosition(), + fold; + if (fold = this.session.getFoldAt(cursor.row, cursor.column, 1)) { + this.moveCursorTo(fold.end.row, fold.end.column); + } + else if (this.lead.column == this.doc.getLine(this.lead.row).length) { + if (this.lead.row < this.doc.getLength() - 1) { + this.moveCursorTo(this.lead.row + 1, 0); + } + } + else { + var tabSize = this.session.getTabSize(); + var cursor = this.lead; + if (this.wouldMoveIntoSoftTab(cursor, tabSize, 1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, tabSize); + } else { + this.moveCursorBy(0, 1); + } + } + }; + this.moveCursorLineStart = function() { + var row = this.lead.row; + var column = this.lead.column; + var screenRow = this.session.documentToScreenRow(row, column); + var firstColumnPosition = this.session.screenToDocumentPosition(screenRow, 0); + var beforeCursor = this.session.getDisplayLine( + row, null, firstColumnPosition.row, + firstColumnPosition.column + ); + + var leadingSpace = beforeCursor.match(/^\s*/); + if (leadingSpace[0].length != column && !this.session.$useEmacsStyleLineStart) + firstColumnPosition.column += leadingSpace[0].length; + this.moveCursorToPosition(firstColumnPosition); + }; + this.moveCursorLineEnd = function() { + var lead = this.lead; + var lineEnd = this.session.getDocumentLastRowColumnPosition(lead.row, lead.column); + if (this.lead.column == lineEnd.column) { + var line = this.session.getLine(lineEnd.row); + if (lineEnd.column == line.length) { + var textEnd = line.search(/\s+$/); + if (textEnd > 0) + lineEnd.column = textEnd; + } + } + + this.moveCursorTo(lineEnd.row, lineEnd.column); + }; + this.moveCursorFileEnd = function() { + var row = this.doc.getLength() - 1; + var column = this.doc.getLine(row).length; + this.moveCursorTo(row, column); + }; + this.moveCursorFileStart = function() { + this.moveCursorTo(0, 0); + }; + this.moveCursorLongWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + var match; + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + this.moveCursorTo(fold.end.row, fold.end.column); + return; + } + if (match = this.session.nonTokenRe.exec(rightOfCursor)) { + column += this.session.nonTokenRe.lastIndex; + this.session.nonTokenRe.lastIndex = 0; + rightOfCursor = line.substring(column); + } + if (column >= line.length) { + this.moveCursorTo(row, line.length); + this.moveCursorRight(); + if (row < this.doc.getLength() - 1) + this.moveCursorWordRight(); + return; + } + if (match = this.session.tokenRe.exec(rightOfCursor)) { + column += this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + this.moveCursorLongWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + return; + } + + var str = this.session.getFoldStringAt(row, column, -1); + if (str == null) { + str = this.doc.getLine(row).substring(0, column); + } + + var leftOfCursor = lang.stringReverse(str); + var match; + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + if (match = this.session.nonTokenRe.exec(leftOfCursor)) { + column -= this.session.nonTokenRe.lastIndex; + leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); + this.session.nonTokenRe.lastIndex = 0; + } + if (column <= 0) { + this.moveCursorTo(row, 0); + this.moveCursorLeft(); + if (row > 0) + this.moveCursorWordLeft(); + return; + } + if (match = this.session.tokenRe.exec(leftOfCursor)) { + column -= this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + + this.$shortWordEndIndex = function(rightOfCursor) { + var match, index = 0, ch; + var whitespaceRe = /\s/; + var tokenRe = this.session.tokenRe; + + tokenRe.lastIndex = 0; + if (match = this.session.tokenRe.exec(rightOfCursor)) { + index = this.session.tokenRe.lastIndex; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index ++; + + if (index < 1) { + tokenRe.lastIndex = 0; + while ((ch = rightOfCursor[index]) && !tokenRe.test(ch)) { + tokenRe.lastIndex = 0; + index ++; + if (whitespaceRe.test(ch)) { + if (index > 2) { + index--; + break; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index ++; + if (index > 2) + break; + } + } + } + } + } + tokenRe.lastIndex = 0; + + return index; + }; + + this.moveCursorShortWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + var fold = this.session.getFoldAt(row, column, 1); + if (fold) + return this.moveCursorTo(fold.end.row, fold.end.column); + + if (column == line.length) { + var l = this.doc.getLength(); + do { + row++; + rightOfCursor = this.doc.getLine(row); + } while (row < l && /^\s*$/.test(rightOfCursor)); + + if (!/^\s+/.test(rightOfCursor)) + rightOfCursor = ""; + column = 0; + } + + var index = this.$shortWordEndIndex(rightOfCursor); + + this.moveCursorTo(row, column + index); + }; + + this.moveCursorShortWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) + return this.moveCursorTo(fold.start.row, fold.start.column); + + var line = this.session.getLine(row).substring(0, column); + if (column === 0) { + do { + row--; + line = this.doc.getLine(row); + } while (row > 0 && /^\s*$/.test(line)); + + column = line.length; + if (!/\s+$/.test(line)) + line = ""; + } + + var leftOfCursor = lang.stringReverse(line); + var index = this.$shortWordEndIndex(leftOfCursor); + + return this.moveCursorTo(row, column - index); + }; + + this.moveCursorWordRight = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordRight(); + else + this.moveCursorShortWordRight(); + }; + + this.moveCursorWordLeft = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordLeft(); + else + this.moveCursorShortWordLeft(); + }; + this.moveCursorBy = function(rows, chars) { + var screenPos = this.session.documentToScreenPosition( + this.lead.row, + this.lead.column + ); + + var offsetX; + + if (chars === 0) { + if (rows !== 0) { + if (this.session.$bidiHandler.isBidiRow(screenPos.row, this.lead.row)) { + offsetX = this.session.$bidiHandler.getPosLeft(screenPos.column); + screenPos.column = Math.round(offsetX / this.session.$bidiHandler.charWidths[0]); + } else { + offsetX = screenPos.column * this.session.$bidiHandler.charWidths[0]; + } + } + + if (this.$desiredColumn) + screenPos.column = this.$desiredColumn; + else + this.$desiredColumn = screenPos.column; + } + + var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenPos.column, offsetX); + + if (rows !== 0 && chars === 0 && docPos.row === this.lead.row && docPos.column === this.lead.column) { + if (this.session.lineWidgets && this.session.lineWidgets[docPos.row]) { + if (docPos.row > 0 || rows > 0) + docPos.row++; + } + } + this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0); + }; + this.moveCursorToPosition = function(position) { + this.moveCursorTo(position.row, position.column); + }; + this.moveCursorTo = function(row, column, keepDesiredColumn) { + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + row = fold.start.row; + column = fold.start.column; + } + + this.$keepDesiredColumnOnChange = true; + var line = this.session.getLine(row); + if (/[\uDC00-\uDFFF]/.test(line.charAt(column)) && line.charAt(column - 1)) { + if (this.lead.row == row && this.lead.column == column + 1) + column = column - 1; + else + column = column + 1; + } + this.lead.setPosition(row, column); + this.$keepDesiredColumnOnChange = false; + + if (!keepDesiredColumn) + this.$desiredColumn = null; + }; + this.moveCursorToScreen = function(row, column, keepDesiredColumn) { + var pos = this.session.screenToDocumentPosition(row, column); + this.moveCursorTo(pos.row, pos.column, keepDesiredColumn); + }; + this.detach = function() { + this.lead.detach(); + this.anchor.detach(); + this.session = this.doc = null; + }; + + this.fromOrientedRange = function(range) { + this.setSelectionRange(range, range.cursor == range.start); + this.$desiredColumn = range.desiredColumn || this.$desiredColumn; + }; + + this.toOrientedRange = function(range) { + var r = this.getRange(); + if (range) { + range.start.column = r.start.column; + range.start.row = r.start.row; + range.end.column = r.end.column; + range.end.row = r.end.row; + } else { + range = r; + } + + range.cursor = this.isBackwards() ? range.start : range.end; + range.desiredColumn = this.$desiredColumn; + return range; + }; + this.getRangeOfMovements = function(func) { + var start = this.getCursor(); + try { + func(this); + var end = this.getCursor(); + return Range.fromPoints(start,end); + } catch(e) { + return Range.fromPoints(start,start); + } finally { + this.moveCursorToPosition(start); + } + }; + + this.toJSON = function() { + if (this.rangeCount) { + var data = this.ranges.map(function(r) { + var r1 = r.clone(); + r1.isBackwards = r.cursor == r.start; + return r1; + }); + } else { + var data = this.getRange(); + data.isBackwards = this.isBackwards(); + } + return data; + }; + + this.fromJSON = function(data) { + if (data.start == undefined) { + if (this.rangeList) { + this.toSingleRange(data[0]); + for (var i = data.length; i--; ) { + var r = Range.fromPoints(data[i].start, data[i].end); + if (data[i].isBackwards) + r.cursor = r.start; + this.addRange(r, true); + } + return; + } else + data = data[0]; + } + if (this.rangeList) + this.toSingleRange(data); + this.setSelectionRange(data, data.isBackwards); + }; + + this.isEqual = function(data) { + if ((data.length || this.rangeCount) && data.length != this.rangeCount) + return false; + if (!data.length || !this.ranges) + return this.getRange().isEqual(data); + + for (var i = this.ranges.length; i--; ) { + if (!this.ranges[i].isEqual(data[i])) + return false; + } + return true; + }; + + }).call(Selection.prototype); + + exports.Selection = Selection; + }); + + ace.define("ace/tokenizer",["require","exports","module","ace/config"], function(acequire, exports, module) { + "use strict"; + + var config = acequire("./config"); + var MAX_TOKEN_COUNT = 2000; + var Tokenizer = function(rules) { + this.states = rules; + + this.regExps = {}; + this.matchMappings = {}; + for (var key in this.states) { + var state = this.states[key]; + var ruleRegExps = []; + var matchTotal = 0; + var mapping = this.matchMappings[key] = {defaultToken: "text"}; + var flag = "g"; + + var splitterRurles = []; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.defaultToken) + mapping.defaultToken = rule.defaultToken; + if (rule.caseInsensitive) + flag = "gi"; + if (rule.regex == null) + continue; + + if (rule.regex instanceof RegExp) + rule.regex = rule.regex.toString().slice(1, -1); + var adjustedregex = rule.regex; + var matchcount = new RegExp("(?:(" + adjustedregex + ")|(.))").exec("a").length - 2; + if (Array.isArray(rule.token)) { + if (rule.token.length == 1 || matchcount == 1) { + rule.token = rule.token[0]; + } else if (matchcount - 1 != rule.token.length) { + this.reportError("number of classes and regexp groups doesn't match", { + rule: rule, + groupCount: matchcount - 1 + }); + rule.token = rule.token[0]; + } else { + rule.tokenArray = rule.token; + rule.token = null; + rule.onMatch = this.$arrayTokens; + } + } else if (typeof rule.token == "function" && !rule.onMatch) { + if (matchcount > 1) + rule.onMatch = this.$applyToken; + else + rule.onMatch = rule.token; + } + + if (matchcount > 1) { + if (/\\\d/.test(rule.regex)) { + adjustedregex = rule.regex.replace(/\\([0-9]+)/g, function(match, digit) { + return "\\" + (parseInt(digit, 10) + matchTotal + 1); + }); + } else { + matchcount = 1; + adjustedregex = this.removeCapturingGroups(rule.regex); + } + if (!rule.splitRegex && typeof rule.token != "string") + splitterRurles.push(rule); // flag will be known only at the very end + } + + mapping[matchTotal] = i; + matchTotal += matchcount; + + ruleRegExps.push(adjustedregex); + if (!rule.onMatch) + rule.onMatch = null; + } + + if (!ruleRegExps.length) { + mapping[0] = 0; + ruleRegExps.push("$"); + } + + splitterRurles.forEach(function(rule) { + rule.splitRegex = this.createSplitterRegexp(rule.regex, flag); + }, this); + + this.regExps[key] = new RegExp("(" + ruleRegExps.join(")|(") + ")|($)", flag); + } + }; + + (function() { + this.$setMaxTokenCount = function(m) { + MAX_TOKEN_COUNT = m | 0; + }; + + this.$applyToken = function(str) { + var values = this.splitRegex.exec(str).slice(1); + var types = this.token.apply(this, values); + if (typeof types === "string") + return [{type: types, value: str}]; + + var tokens = []; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i]) + tokens[tokens.length] = { + type: types[i], + value: values[i] + }; + } + return tokens; + }; + + this.$arrayTokens = function(str) { + if (!str) + return []; + var values = this.splitRegex.exec(str); + if (!values) + return "text"; + var tokens = []; + var types = this.tokenArray; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i + 1]) + tokens[tokens.length] = { + type: types[i], + value: values[i + 1] + }; + } + return tokens; + }; + + this.removeCapturingGroups = function(src) { + var r = src.replace( + /\[(?:\\.|[^\]])*?\]|\\.|\(\?[:=!]|(\()/g, + function(x, y) {return y ? "(?:" : x;} + ); + return r; + }; + + this.createSplitterRegexp = function(src, flag) { + if (src.indexOf("(?=") != -1) { + var stack = 0; + var inChClass = false; + var lastCapture = {}; + src.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g, function( + m, esc, parenOpen, parenClose, square, index + ) { + if (inChClass) { + inChClass = square != "]"; + } else if (square) { + inChClass = true; + } else if (parenClose) { + if (stack == lastCapture.stack) { + lastCapture.end = index+1; + lastCapture.stack = -1; + } + stack--; + } else if (parenOpen) { + stack++; + if (parenOpen.length != 1) { + lastCapture.stack = stack; + lastCapture.start = index; + } + } + return m; + }); + + if (lastCapture.end != null && /^\)*$/.test(src.substr(lastCapture.end))) + src = src.substring(0, lastCapture.start) + src.substr(lastCapture.end); + } + if (src.charAt(0) != "^") src = "^" + src; + if (src.charAt(src.length - 1) != "$") src += "$"; + + return new RegExp(src, (flag||"").replace("g", "")); + }; + this.getLineTokens = function(line, startState) { + if (startState && typeof startState != "string") { + var stack = startState.slice(0); + startState = stack[0]; + if (startState === "#tmp") { + stack.shift(); + startState = stack.shift(); + } + } else + var stack = []; + + var currentState = startState || "start"; + var state = this.states[currentState]; + if (!state) { + currentState = "start"; + state = this.states[currentState]; + } + var mapping = this.matchMappings[currentState]; + var re = this.regExps[currentState]; + re.lastIndex = 0; + + var match, tokens = []; + var lastIndex = 0; + var matchAttempts = 0; + + var token = {type: null, value: ""}; + + while (match = re.exec(line)) { + var type = mapping.defaultToken; + var rule = null; + var value = match[0]; + var index = re.lastIndex; + + if (index - value.length > lastIndex) { + var skipped = line.substring(lastIndex, index - value.length); + if (token.type == type) { + token.value += skipped; + } else { + if (token.type) + tokens.push(token); + token = {type: type, value: skipped}; + } + } + + for (var i = 0; i < match.length-2; i++) { + if (match[i + 1] === undefined) + continue; + + rule = state[mapping[i]]; + + if (rule.onMatch) + type = rule.onMatch(value, currentState, stack, line); + else + type = rule.token; + + if (rule.next) { + if (typeof rule.next == "string") { + currentState = rule.next; + } else { + currentState = rule.next(currentState, stack); + } + + state = this.states[currentState]; + if (!state) { + this.reportError("state doesn't exist", currentState); + currentState = "start"; + state = this.states[currentState]; + } + mapping = this.matchMappings[currentState]; + lastIndex = index; + re = this.regExps[currentState]; + re.lastIndex = index; + } + if (rule.consumeLineEnd) + lastIndex = index; + break; + } + + if (value) { + if (typeof type === "string") { + if ((!rule || rule.merge !== false) && token.type === type) { + token.value += value; + } else { + if (token.type) + tokens.push(token); + token = {type: type, value: value}; + } + } else if (type) { + if (token.type) + tokens.push(token); + token = {type: null, value: ""}; + for (var i = 0; i < type.length; i++) + tokens.push(type[i]); + } + } + + if (lastIndex == line.length) + break; + + lastIndex = index; + + if (matchAttempts++ > MAX_TOKEN_COUNT) { + if (matchAttempts > 2 * line.length) { + this.reportError("infinite loop with in ace tokenizer", { + startState: startState, + line: line + }); + } + while (lastIndex < line.length) { + if (token.type) + tokens.push(token); + token = { + value: line.substring(lastIndex, lastIndex += 2000), + type: "overflow" + }; + } + currentState = "start"; + stack = []; + break; + } + } + + if (token.type) + tokens.push(token); + + if (stack.length > 1) { + if (stack[0] !== currentState) + stack.unshift("#tmp", currentState); + } + return { + tokens : tokens, + state : stack.length ? stack : currentState + }; + }; + + this.reportError = config.reportError; + + }).call(Tokenizer.prototype); + + exports.Tokenizer = Tokenizer; + }); + + ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"], function(acequire, exports, module) { + "use strict"; + + var lang = acequire("../lib/lang"); + + var TextHighlightRules = function() { + + this.$rules = { + "start" : [{ + token : "empty_line", + regex : '^$' + }, { + defaultToken : "text" + }] + }; + }; + + (function() { + + this.addRules = function(rules, prefix) { + if (!prefix) { + for (var key in rules) + this.$rules[key] = rules[key]; + return; + } + for (var key in rules) { + var state = rules[key]; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.next || rule.onMatch) { + if (typeof rule.next == "string") { + if (rule.next.indexOf(prefix) !== 0) + rule.next = prefix + rule.next; + } + if (rule.nextState && rule.nextState.indexOf(prefix) !== 0) + rule.nextState = prefix + rule.nextState; + } + } + this.$rules[prefix + key] = state; + } + }; + + this.getRules = function() { + return this.$rules; + }; + + this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) { + var embedRules = typeof HighlightRules == "function" + ? new HighlightRules().getRules() + : HighlightRules; + if (states) { + for (var i = 0; i < states.length; i++) + states[i] = prefix + states[i]; + } else { + states = []; + for (var key in embedRules) + states.push(prefix + key); + } + + this.addRules(embedRules, prefix); + + if (escapeRules) { + var addRules = Array.prototype[append ? "push" : "unshift"]; + for (var i = 0; i < states.length; i++) + addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules)); + } + + if (!this.$embeds) + this.$embeds = []; + this.$embeds.push(prefix); + }; + + this.getEmbeds = function() { + return this.$embeds; + }; + + var pushState = function(currentState, stack) { + if (currentState != "start" || stack.length) + stack.unshift(this.nextState, currentState); + return this.nextState; + }; + var popState = function(currentState, stack) { + stack.shift(); + return stack.shift() || "start"; + }; + + this.normalizeRules = function() { + var id = 0; + var rules = this.$rules; + function processState(key) { + var state = rules[key]; + state.processed = true; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + var toInsert = null; + if (Array.isArray(rule)) { + toInsert = rule; + rule = {}; + } + if (!rule.regex && rule.start) { + rule.regex = rule.start; + if (!rule.next) + rule.next = []; + rule.next.push({ + defaultToken: rule.token + }, { + token: rule.token + ".end", + regex: rule.end || rule.start, + next: "pop" + }); + rule.token = rule.token + ".start"; + rule.push = true; + } + var next = rule.next || rule.push; + if (next && Array.isArray(next)) { + var stateName = rule.stateName; + if (!stateName) { + stateName = rule.token; + if (typeof stateName != "string") + stateName = stateName[0] || ""; + if (rules[stateName]) + stateName += id++; + } + rules[stateName] = next; + rule.next = stateName; + processState(stateName); + } else if (next == "pop") { + rule.next = popState; + } + + if (rule.push) { + rule.nextState = rule.next || rule.push; + rule.next = pushState; + delete rule.push; + } + + if (rule.rules) { + for (var r in rule.rules) { + if (rules[r]) { + if (rules[r].push) + rules[r].push.apply(rules[r], rule.rules[r]); + } else { + rules[r] = rule.rules[r]; + } + } + } + var includeName = typeof rule == "string" ? rule : rule.include; + if (includeName) { + if (Array.isArray(includeName)) + toInsert = includeName.map(function(x) { return rules[x]; }); + else + toInsert = rules[includeName]; + } + + if (toInsert) { + var args = [i, 1].concat(toInsert); + if (rule.noEscape) + args = args.filter(function(x) {return !x.next;}); + state.splice.apply(state, args); + i--; + } + + if (rule.keywordMap) { + rule.token = this.createKeywordMapper( + rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive + ); + delete rule.defaultToken; + } + } + } + Object.keys(rules).forEach(processState, this); + }; + + this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) { + var keywords = Object.create(null); + Object.keys(map).forEach(function(className) { + var a = map[className]; + if (ignoreCase) + a = a.toLowerCase(); + var list = a.split(splitChar || "|"); + for (var i = list.length; i--; ) + keywords[list[i]] = className; + }); + if (Object.getPrototypeOf(keywords)) { + keywords.__proto__ = null; + } + this.$keywordList = Object.keys(keywords); + map = null; + return ignoreCase + ? function(value) {return keywords[value.toLowerCase()] || defaultToken; } + : function(value) {return keywords[value] || defaultToken; }; + }; + + this.getKeywords = function() { + return this.$keywords; + }; + + }).call(TextHighlightRules.prototype); + + exports.TextHighlightRules = TextHighlightRules; + }); + + ace.define("ace/mode/behaviour",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + var Behaviour = function() { + this.$behaviours = {}; + }; + + (function () { + + this.add = function (name, action, callback) { + switch (undefined) { + case this.$behaviours: + this.$behaviours = {}; + case this.$behaviours[name]: + this.$behaviours[name] = {}; + } + this.$behaviours[name][action] = callback; + }; + + this.addBehaviours = function (behaviours) { + for (var key in behaviours) { + for (var action in behaviours[key]) { + this.add(key, action, behaviours[key][action]); + } + } + }; + + this.remove = function (name) { + if (this.$behaviours && this.$behaviours[name]) { + delete this.$behaviours[name]; + } + }; + + this.inherit = function (mode, filter) { + if (typeof mode === "function") { + var behaviours = new mode().getBehaviours(filter); + } else { + var behaviours = mode.getBehaviours(filter); + } + this.addBehaviours(behaviours); + }; + + this.getBehaviours = function (filter) { + if (!filter) { + return this.$behaviours; + } else { + var ret = {}; + for (var i = 0; i < filter.length; i++) { + if (this.$behaviours[filter[i]]) { + ret[filter[i]] = this.$behaviours[filter[i]]; + } + } + return ret; + } + }; + + }).call(Behaviour.prototype); + + exports.Behaviour = Behaviour; + }); + + ace.define("ace/token_iterator",["require","exports","module","ace/range"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("./range").Range; + var TokenIterator = function(session, initialRow, initialColumn) { + this.$session = session; + this.$row = initialRow; + this.$rowTokens = session.getTokens(initialRow); + + var token = session.getTokenAt(initialRow, initialColumn); + this.$tokenIndex = token ? token.index : -1; + }; + + (function() { + this.stepBackward = function() { + this.$tokenIndex -= 1; + + while (this.$tokenIndex < 0) { + this.$row -= 1; + if (this.$row < 0) { + this.$row = 0; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = this.$rowTokens.length - 1; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.stepForward = function() { + this.$tokenIndex += 1; + var rowCount; + while (this.$tokenIndex >= this.$rowTokens.length) { + this.$row += 1; + if (!rowCount) + rowCount = this.$session.getLength(); + if (this.$row >= rowCount) { + this.$row = rowCount - 1; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = 0; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentToken = function () { + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentTokenRow = function () { + return this.$row; + }; + this.getCurrentTokenColumn = function() { + var rowTokens = this.$rowTokens; + var tokenIndex = this.$tokenIndex; + var column = rowTokens[tokenIndex].start; + if (column !== undefined) + return column; + + column = 0; + while (tokenIndex > 0) { + tokenIndex -= 1; + column += rowTokens[tokenIndex].value.length; + } + + return column; + }; + this.getCurrentTokenPosition = function() { + return {row: this.$row, column: this.getCurrentTokenColumn()}; + }; + this.getCurrentTokenRange = function() { + var token = this.$rowTokens[this.$tokenIndex]; + var column = this.getCurrentTokenColumn(); + return new Range(this.$row, column, this.$row, column + token.value.length); + }; + + }).call(TokenIterator.prototype); + + exports.TokenIterator = TokenIterator; + }); + + ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../../lib/oop"); + var Behaviour = acequire("../behaviour").Behaviour; + var TokenIterator = acequire("../../token_iterator").TokenIterator; + var lang = acequire("../../lib/lang"); + + var SAFE_INSERT_IN_TOKENS = + ["text", "paren.rparen", "punctuation.operator"]; + var SAFE_INSERT_BEFORE_TOKENS = + ["text", "paren.rparen", "punctuation.operator", "comment"]; + + var context; + var contextCache = {}; + var defaultQuotes = {'"' : '"', "'" : "'"}; + + var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = {rangeCount: editor.multiSelect.rangeCount}; + } + if (contextCache[id]) + return context = contextCache[id]; + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; + }; + + var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; + }; + + var CstyleBehaviour = function(options) { + this.add("braces", "insertion", function(state, action, editor, session, text) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (text == '{') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '{', '}'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + if (/[\]\}\)]/.test(line[cursor.column]) || editor.inMultiSelectMode || options && options.braces) { + CstyleBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } else { + CstyleBehaviour.recordMaybeInsert(editor, session, "{"); + return { + text: '{', + selection: [1, 1] + }; + } + } + } else if (text == '}') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == '}') { + var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } else if (text == "\n" || text == "\r\n") { + initContext(editor); + var closing = ""; + if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) { + closing = lang.stringRepeat("}", context.maybeInsertedBrackets); + CstyleBehaviour.clearMaybeInsertedClosing(); + } + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar === '}') { + var openBracePos = session.findMatchingBracket({row: cursor.row, column: cursor.column+1}, '}'); + if (!openBracePos) + return null; + var next_indent = this.$getIndent(session.getLine(openBracePos.row)); + } else if (closing) { + var next_indent = this.$getIndent(line); + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + return; + } + var indent = next_indent + session.getTabString(); + + return { + text: '\n' + indent + '\n' + next_indent + closing, + selection: [1, indent.length, 1, indent.length] + }; + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + } + }); + + this.add("braces", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '{') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.end.column, range.end.column + 1); + if (rightChar == '}') { + range.end.column++; + return range; + } else { + context.maybeInsertedBrackets--; + } + } + }); + + this.add("parens", "insertion", function(state, action, editor, session, text) { + if (text == '(') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '(', ')'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, ")"); + return { + text: '()', + selection: [1, 1] + }; + } + } else if (text == ')') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ')') { + var matching = session.$findOpeningBracket(')', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("parens", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '(') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ')') { + range.end.column++; + return range; + } + } + }); + + this.add("brackets", "insertion", function(state, action, editor, session, text) { + if (text == '[') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '[', ']'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: '[]', + selection: [1, 1] + }; + } + } else if (text == ']') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ']') { + var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("brackets", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '[') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ']') { + range.end.column++; + return range; + } + } + }); + + this.add("string_dquotes", "insertion", function(state, action, editor, session, text) { + var quotes = session.$mode.$quotes || defaultQuotes; + if (text.length == 1 && quotes[text]) { + if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && (selected.length != 1 || !quotes[selected]) && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var leftChar = line.substring(cursor.column-1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + if (leftChar == "\\" && token && /escape/.test(token.type)) + return null; + + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) + pair = false; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1,1] + }; + } + } + }); + + this.add("string_dquotes", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && (selected == '"' || selected == "'")) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + + }; + + + CstyleBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { + var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); + if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) + return false; + } + iterator.stepForward(); + return iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); + }; + + CstyleBehaviour.$matchTokenType = function(token, types) { + return types.indexOf(token.type || token) > -1; + }; + + CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; + }; + + CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isMaybeInsertedClosing(cursor, line)) + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = cursor.row; + context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; + context.maybeInsertedLineEnd = line.substr(cursor.column); + context.maybeInsertedBrackets++; + }; + + CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { + return context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd; + }; + + CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) { + return context.maybeInsertedBrackets > 0 && + cursor.row === context.maybeInsertedRow && + line.substr(cursor.column) === context.maybeInsertedLineEnd && + line.substr(0, cursor.column) == context.maybeInsertedLineStart; + }; + + CstyleBehaviour.popAutoInsertedClosing = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; + }; + + CstyleBehaviour.clearMaybeInsertedClosing = function() { + if (context) { + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = -1; + } + }; + + + + oop.inherits(CstyleBehaviour, Behaviour); + + exports.CstyleBehaviour = CstyleBehaviour; + }); + + ace.define("ace/unicode",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + exports.packages = {}; + + addUnicodePackage({ + L: "0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC", + Ll: "0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A", + Lu: "0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A", + Lt: "01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC", + Lm: "02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F", + Lo: "01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC", + M: "0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26", + Mn: "0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26", + Mc: "0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC", + Me: "0488048906DE20DD-20E020E2-20E4A670-A672", + N: "0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19", + Nd: "0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19", + Nl: "16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF", + No: "00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835", + P: "0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65", + Pd: "002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D", + Ps: "0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62", + Pe: "0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63", + Pi: "00AB2018201B201C201F20392E022E042E092E0C2E1C2E20", + Pf: "00BB2019201D203A2E032E052E0A2E0D2E1D2E21", + Pc: "005F203F20402054FE33FE34FE4D-FE4FFF3F", + Po: "0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65", + S: "0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD", + Sm: "002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC", + Sc: "002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6", + Sk: "005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3", + So: "00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD", + Z: "002000A01680180E2000-200A20282029202F205F3000", + Zs: "002000A01680180E2000-200A202F205F3000", + Zl: "2028", + Zp: "2029", + C: "0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF", + Cc: "0000-001F007F-009F", + Cf: "00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB", + Co: "E000-F8FF", + Cs: "D800-DFFF", + Cn: "03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF" + }); + + function addUnicodePackage (pack) { + var codePoint = /\w{4}/g; + for (var name in pack) + exports.packages[name] = pack[name].replace(codePoint, "\\u$&"); + } + + }); + + ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour/cstyle","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"], function(acequire, exports, module) { + "use strict"; + + var Tokenizer = acequire("../tokenizer").Tokenizer; + var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules; + var CstyleBehaviour = acequire("./behaviour/cstyle").CstyleBehaviour; + var unicode = acequire("../unicode"); + var lang = acequire("../lib/lang"); + var TokenIterator = acequire("../token_iterator").TokenIterator; + var Range = acequire("../range").Range; + + var Mode = function() { + this.HighlightRules = TextHighlightRules; + }; + + (function() { + this.$defaultBehaviour = new CstyleBehaviour(); + + this.tokenRe = new RegExp("^[" + + unicode.packages.L + + unicode.packages.Mn + unicode.packages.Mc + + unicode.packages.Nd + + unicode.packages.Pc + "\\$_]+", "g" + ); + + this.nonTokenRe = new RegExp("^(?:[^" + + unicode.packages.L + + unicode.packages.Mn + unicode.packages.Mc + + unicode.packages.Nd + + unicode.packages.Pc + "\\$_]|\\s])+", "g" + ); + + this.getTokenizer = function() { + if (!this.$tokenizer) { + this.$highlightRules = this.$highlightRules || new this.HighlightRules(this.$highlightRuleConfig); + this.$tokenizer = new Tokenizer(this.$highlightRules.getRules()); + } + return this.$tokenizer; + }; + + this.lineCommentStart = ""; + this.blockComment = ""; + + this.toggleCommentLines = function(state, session, startRow, endRow) { + var doc = session.doc; + + var ignoreBlankLines = true; + var shouldRemove = true; + var minIndent = Infinity; + var tabSize = session.getTabSize(); + var insertAtTabStop = false; + + if (!this.lineCommentStart) { + if (!this.blockComment) + return false; + var lineCommentStart = this.blockComment.start; + var lineCommentEnd = this.blockComment.end; + var regexpStart = new RegExp("^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")"); + var regexpEnd = new RegExp("(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$"); + + var comment = function(line, i) { + if (testRemove(line, i)) + return; + if (!ignoreBlankLines || /\S/.test(line)) { + doc.insertInLine({row: i, column: line.length}, lineCommentEnd); + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + } + }; + + var uncomment = function(line, i) { + var m; + if (m = line.match(regexpEnd)) + doc.removeInLine(i, line.length - m[0].length, line.length); + if (m = line.match(regexpStart)) + doc.removeInLine(i, m[1].length, m[0].length); + }; + + var testRemove = function(line, row) { + if (regexpStart.test(line)) + return true; + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].type === "comment") + return true; + } + }; + } else { + if (Array.isArray(this.lineCommentStart)) { + var regexpStart = this.lineCommentStart.map(lang.escapeRegExp).join("|"); + var lineCommentStart = this.lineCommentStart[0]; + } else { + var regexpStart = lang.escapeRegExp(this.lineCommentStart); + var lineCommentStart = this.lineCommentStart; + } + regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?"); + + insertAtTabStop = session.getUseSoftTabs(); + + var uncomment = function(line, i) { + var m = line.match(regexpStart); + if (!m) return; + var start = m[1].length, end = m[0].length; + if (!shouldInsertSpace(line, start, end) && m[0][end - 1] == " ") + end--; + doc.removeInLine(i, start, end); + }; + var commentWithSpace = lineCommentStart + " "; + var comment = function(line, i) { + if (!ignoreBlankLines || /\S/.test(line)) { + if (shouldInsertSpace(line, minIndent, minIndent)) + doc.insertInLine({row: i, column: minIndent}, commentWithSpace); + else + doc.insertInLine({row: i, column: minIndent}, lineCommentStart); + } + }; + var testRemove = function(line, i) { + return regexpStart.test(line); + }; + + var shouldInsertSpace = function(line, before, after) { + var spaces = 0; + while (before-- && line.charAt(before) == " ") + spaces++; + if (spaces % tabSize != 0) + return false; + var spaces = 0; + while (line.charAt(after++) == " ") + spaces++; + if (tabSize > 2) + return spaces % tabSize != tabSize - 1; + else + return spaces % tabSize == 0; + return true; + }; + } + + function iter(fun) { + for (var i = startRow; i <= endRow; i++) + fun(doc.getLine(i), i); + } + + + var minEmptyLength = Infinity; + iter(function(line, i) { + var indent = line.search(/\S/); + if (indent !== -1) { + if (indent < minIndent) + minIndent = indent; + if (shouldRemove && !testRemove(line, i)) + shouldRemove = false; + } else if (minEmptyLength > line.length) { + minEmptyLength = line.length; + } + }); + + if (minIndent == Infinity) { + minIndent = minEmptyLength; + ignoreBlankLines = false; + shouldRemove = false; + } + + if (insertAtTabStop && minIndent % tabSize != 0) + minIndent = Math.floor(minIndent / tabSize) * tabSize; + + iter(shouldRemove ? uncomment : comment); + }; + + this.toggleBlockComment = function(state, session, range, cursor) { + var comment = this.blockComment; + if (!comment) + return; + if (!comment.start && comment[0]) + comment = comment[0]; + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + + var sel = session.selection; + var initialRange = session.selection.toOrientedRange(); + var startRow, colDiff; + + if (token && /comment/.test(token.type)) { + var startRange, endRange; + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.start); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + startRange = new Range(row, column, row, column + comment.start.length); + break; + } + token = iterator.stepBackward(); + } + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.end); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + endRange = new Range(row, column, row, column + comment.end.length); + break; + } + token = iterator.stepForward(); + } + if (endRange) + session.remove(endRange); + if (startRange) { + session.remove(startRange); + startRow = startRange.start.row; + colDiff = -comment.start.length; + } + } else { + colDiff = comment.start.length; + startRow = range.start.row; + session.insert(range.end, comment.end); + session.insert(range.start, comment.start); + } + if (initialRange.start.row == startRow) + initialRange.start.column += colDiff; + if (initialRange.end.row == startRow) + initialRange.end.column += colDiff; + session.selection.fromOrientedRange(initialRange); + }; + + this.getNextLineIndent = function(state, line, tab) { + return this.$getIndent(line); + }; + + this.checkOutdent = function(state, line, input) { + return false; + }; + + this.autoOutdent = function(state, doc, row) { + }; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + + this.createWorker = function(session) { + return null; + }; + + this.createModeDelegates = function (mapping) { + this.$embeds = []; + this.$modes = {}; + for (var i in mapping) { + if (mapping[i]) { + this.$embeds.push(i); + this.$modes[i] = new mapping[i](); + } + } + + var delegations = ["toggleBlockComment", "toggleCommentLines", "getNextLineIndent", + "checkOutdent", "autoOutdent", "transformAction", "getCompletions"]; + + for (var i = 0; i < delegations.length; i++) { + (function(scope) { + var functionName = delegations[i]; + var defaultHandler = scope[functionName]; + scope[delegations[i]] = function() { + return this.$delegator(functionName, arguments, defaultHandler); + }; + }(this)); + } + }; + + this.$delegator = function(method, args, defaultHandler) { + var state = args[0]; + if (typeof state != "string") + state = state[0]; + for (var i = 0; i < this.$embeds.length; i++) { + if (!this.$modes[this.$embeds[i]]) continue; + + var split = state.split(this.$embeds[i]); + if (!split[0] && split[1]) { + args[0] = split[1]; + var mode = this.$modes[this.$embeds[i]]; + return mode[method].apply(mode, args); + } + } + var ret = defaultHandler.apply(this, args); + return defaultHandler ? ret : undefined; + }; + + this.transformAction = function(state, action, editor, session, param) { + if (this.$behaviour) { + var behaviours = this.$behaviour.getBehaviours(); + for (var key in behaviours) { + if (behaviours[key][action]) { + var ret = behaviours[key][action].apply(this, arguments); + if (ret) { + return ret; + } + } + } + } + }; + + this.getKeywords = function(append) { + if (!this.completionKeywords) { + var rules = this.$tokenizer.rules; + var completionKeywords = []; + for (var rule in rules) { + var ruleItr = rules[rule]; + for (var r = 0, l = ruleItr.length; r < l; r++) { + if (typeof ruleItr[r].token === "string") { + if (/keyword|support|storage/.test(ruleItr[r].token)) + completionKeywords.push(ruleItr[r].regex); + } + else if (typeof ruleItr[r].token === "object") { + for (var a = 0, aLength = ruleItr[r].token.length; a < aLength; a++) { + if (/keyword|support|storage/.test(ruleItr[r].token[a])) { + var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a]; + completionKeywords.push(rule.substr(1, rule.length - 2)); + } + } + } + } + } + this.completionKeywords = completionKeywords; + } + if (!append) + return this.$keywordList; + return completionKeywords.concat(this.$keywordList || []); + }; + + this.$createKeywordList = function() { + if (!this.$highlightRules) + this.getTokenizer(); + return this.$keywordList = this.$highlightRules.$keywordList || []; + }; + + this.getCompletions = function(state, session, pos, prefix) { + var keywords = this.$keywordList || this.$createKeywordList(); + return keywords.map(function(word) { + return { + name: word, + value: word, + score: 0, + meta: "keyword" + }; + }); + }; + + this.$id = "ace/mode/text"; + }).call(Mode.prototype); + + exports.Mode = Mode; + }); + + ace.define("ace/apply_delta",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + + function throwDeltaError(delta, errorText){ + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; + } + + function positionInDocument(docLines, position) { + return position.row >= 0 && position.row < docLines.length && + position.column >= 0 && position.column <= docLines[position.row].length; + } + + function validateDelta(docLines, delta) { + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, "delta.lines must be an Array"); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError(delta, "delta.end must contained in document for 'remove' actions"); + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0)); + if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars) + throwDeltaError(delta, "delta.range must match delta lines"); + } + + exports.applyDelta = function(docLines, delta, doNotValidate) { + + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row] || ""; + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { + docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + } else { + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); + } else { + docLines.splice( + row, endRow - row + 1, + line.substring(0, startColumn) + docLines[endRow].substring(endColumn) + ); + } + break; + } + }; + }); + + ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + + var Anchor = exports.Anchor = function(doc, row, column) { + this.$onChange = this.onChange.bind(this); + this.attach(doc); + + if (typeof column == "undefined") + this.setPosition(row.row, row.column); + else + this.setPosition(row, column); + }; + + (function() { + + oop.implement(this, EventEmitter); + this.getPosition = function() { + return this.$clipPositionToDocument(this.row, this.column); + }; + this.getDocument = function() { + return this.document; + }; + this.$insertRight = false; + this.onChange = function(delta) { + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + + if (delta.start.row > this.row) + return; + + var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); + this.setPosition(point.row, point.column, true); + }; + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; + return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); + } + + function $getTransformedPoint(delta, point, moveIfEqual) { + var deltaIsInsert = delta.action == "insert"; + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { + return { + row: point.row, + column: point.column + }; + } + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + + return { + row: deltaStart.row, + column: deltaStart.column + }; + } + this.setPosition = function(row, column, noClip) { + var pos; + if (noClip) { + pos = { + row: row, + column: column + }; + } else { + pos = this.$clipPositionToDocument(row, column); + } + + if (this.row == pos.row && this.column == pos.column) + return; + + var old = { + row: this.row, + column: this.column + }; + + this.row = pos.row; + this.column = pos.column; + this._signal("change", { + old: old, + value: pos + }); + }; + this.detach = function() { + this.document.removeEventListener("change", this.$onChange); + }; + this.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; + this.$clipPositionToDocument = function(row, column) { + var pos = {}; + + if (row >= this.document.getLength()) { + pos.row = Math.max(0, this.document.getLength() - 1); + pos.column = this.document.getLine(pos.row).length; + } + else if (row < 0) { + pos.row = 0; + pos.column = 0; + } + else { + pos.row = row; + pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column)); + } + + if (column < 0) + pos.column = 0; + + return pos; + }; + + }).call(Anchor.prototype); + + }); + + ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var applyDelta = acequire("./apply_delta").applyDelta; + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var Range = acequire("./range").Range; + var Anchor = acequire("./anchor").Anchor; + + var Document = function(textOrLines) { + this.$lines = [""]; + if (textOrLines.length === 0) { + this.$lines = [""]; + } else if (Array.isArray(textOrLines)) { + this.insertMergedLines({row: 0, column: 0}, textOrLines); + } else { + this.insert({row: 0, column:0}, textOrLines); + } + }; + + (function() { + + oop.implement(this, EventEmitter); + this.setValue = function(text) { + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({row: 0, column: 0}, text); + }; + this.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); + }; + this.createAnchor = function(row, column) { + return new Anchor(this, row, column); + }; + if ("aaa".split(/a/).length === 0) { + this.$split = function(text) { + return text.replace(/\r\n|\r/g, "\n").split("\n"); + }; + } else { + this.$split = function(text) { + return text.split(/\r\n|\r|\n/); + }; + } + + + this.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r\n|\r|\n)/m); + this.$autoNewLine = match ? match[1] : "\n"; + this._signal("changeNewLineMode"); + }; + this.getNewLineCharacter = function() { + switch (this.$newLineMode) { + case "windows": + return "\r\n"; + case "unix": + return "\n"; + default: + return this.$autoNewLine || "\n"; + } + }; + + this.$autoNewLine = ""; + this.$newLineMode = "auto"; + this.setNewLineMode = function(newLineMode) { + if (this.$newLineMode === newLineMode) + return; + + this.$newLineMode = newLineMode; + this._signal("changeNewLineMode"); + }; + this.getNewLineMode = function() { + return this.$newLineMode; + }; + this.isNewLine = function(text) { + return (text == "\r\n" || text == "\r" || text == "\n"); + }; + this.getLine = function(row) { + return this.$lines[row] || ""; + }; + this.getLines = function(firstRow, lastRow) { + return this.$lines.slice(firstRow, lastRow + 1); + }; + this.getAllLines = function() { + return this.getLines(0, this.getLength()); + }; + this.getLength = function() { + return this.$lines.length; + }; + this.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); + }; + this.getLinesForRange = function(range) { + var lines; + if (range.start.row === range.end.row) { + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; + } else { + lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + } + return lines; + }; + this.insertLines = function(row, lines) { + console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); + return this.insertFullLines(row, lines); + }; + this.removeLines = function(firstRow, lastRow) { + console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."); + return this.removeFullLines(firstRow, lastRow); + }; + this.insertNewLine = function(position) { + console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."); + return this.insertMergedLines(position, ["", ""]); + }; + this.insert = function(position, text) { + if (this.getLength() <= 1) + this.$detectNewLine(text); + + return this.insertMergedLines(position, this.$split(text)); + }; + this.insertInLine = function(position, text) { + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: [text] + }, true); + + return this.clonePos(end); + }; + + this.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined; + } + var line = this.getLine(row); + if (column == undefined) + column = line.length; + column = Math.min(Math.max(column, 0), line.length); + return {row: row, column: column}; + }; + + this.clonePos = function(pos) { + return {row: pos.row, column: pos.column}; + }; + + this.pos = function(row, column) { + return {row: row, column: column}; + }; + + this.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); + } + return position; + }; + this.insertFullLines = function(row, lines) { + row = Math.min(Math.max(row, 0), this.getLength()); + var column = 0; + if (row < this.getLength()) { + lines = lines.concat([""]); + column = 0; + } else { + lines = [""].concat(lines); + row--; + column = this.$lines[row].length; + } + this.insertMergedLines({row: row, column: column}, lines); + }; + this.insertMergedLines = function(position, lines) { + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length + }; + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: lines + }); + + return this.clonePos(end); + }; + this.remove = function(range) { + var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) + }); + return this.clonePos(start); + }; + this.removeInLine = function(row, startColumn, endColumn) { + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); + + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({start: start, end: end}) + }, true); + + return this.clonePos(start); + }; + this.removeFullLines = function(firstRow, lastRow) { + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow ), this.getLength() - 1); + var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = ( deleteFirstNewLine ? firstRow - 1 : firstRow ); + var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0 ); + var endRow = ( deleteLastNewLine ? lastRow + 1 : lastRow ); + var endCol = ( deleteLastNewLine ? 0 : this.getLine(endRow).length ); + var range = new Range(startRow, startCol, endRow, endCol); + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + + this.applyDelta({ + start: range.start, + end: range.end, + action: "remove", + lines: this.getLinesForRange(range) + }); + return deletedLines; + }; + this.removeNewLine = function(row) { + if (row < this.getLength() - 1 && row >= 0) { + this.applyDelta({ + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), + action: "remove", + lines: ["", ""] + }); + } + }; + this.replace = function(range, text) { + if (!(range instanceof Range)) + range = Range.fromPoints(range.start, range.end); + if (text.length === 0 && range.isEmpty()) + return range.start; + if (text == this.getTextRange(range)) + return range.end; + + this.remove(range); + var end; + if (text) { + end = this.insert(range.start, text); + } + else { + end = range.start; + } + + return end; + }; + this.applyDeltas = function(deltas) { + for (var i=0; i=0; i--) { + this.revertDelta(deltas[i]); + } + }; + this.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; + if (isInsert ? delta.lines.length <= 1 && !delta.lines[0] + : !Range.comparePoints(delta.start, delta.end)) { + return; + } + + if (isInsert && delta.lines.length > 20000) + this.$splitAndapplyLargeDelta(delta, 20000); + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", delta); + }; + + this.$splitAndapplyLargeDelta = function(delta, MAX) { + var lines = delta.lines; + var l = lines.length; + var row = delta.start.row; + var column = delta.start.column; + var from = 0, to = 0; + do { + from = to; + to += MAX - 1; + var chunk = lines.slice(from, to); + if (to > l) { + delta.lines = chunk; + delta.start.row = row + from; + delta.start.column = column; + break; + } + chunk.push(""); + this.applyDelta({ + start: this.pos(row + from, column), + end: this.pos(row + to, column = 0), + action: delta.action, + lines: chunk + }, true); + } while(true); + }; + this.revertDelta = function(delta) { + this.applyDelta({ + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), + action: (delta.action == "insert" ? "remove" : "insert"), + lines: delta.lines.slice() + }); + }; + this.indexToPosition = function(index, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + for (var i = startRow || 0, l = lines.length; i < l; i++) { + index -= lines[i].length + newlineLength; + if (index < 0) + return {row: i, column: index + lines[i].length + newlineLength}; + } + return {row: l-1, column: lines[l-1].length}; + }; + this.positionToIndex = function(pos, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + var index = 0; + var row = Math.min(pos.row, lines.length); + for (var i = startRow || 0; i < row; ++i) + index += lines[i].length + newlineLength; + + return index + pos.column; + }; + + }).call(Document.prototype); + + exports.Document = Document; + }); + + ace.define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + + var BackgroundTokenizer = function(tokenizer, editor) { + this.running = false; + this.lines = []; + this.states = []; + this.currentLine = 0; + this.tokenizer = tokenizer; + + var self = this; + + this.$worker = function() { + if (!self.running) { return; } + + var workerStart = new Date(); + var currentLine = self.currentLine; + var endLine = -1; + var doc = self.doc; + + var startLine = currentLine; + while (self.lines[currentLine]) + currentLine++; + + var len = doc.getLength(); + var processedLines = 0; + self.running = false; + while (currentLine < len) { + self.$tokenizeRow(currentLine); + endLine = currentLine; + do { + currentLine++; + } while (self.lines[currentLine]); + processedLines ++; + if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) { + self.running = setTimeout(self.$worker, 20); + break; + } + } + self.currentLine = currentLine; + + if (endLine == -1) + endLine = currentLine; + + if (startLine <= endLine) + self.fireUpdateEvent(startLine, endLine); + }; + }; + + (function(){ + + oop.implement(this, EventEmitter); + this.setTokenizer = function(tokenizer) { + this.tokenizer = tokenizer; + this.lines = []; + this.states = []; + + this.start(0); + }; + this.setDocument = function(doc) { + this.doc = doc; + this.lines = []; + this.states = []; + + this.stop(); + }; + this.fireUpdateEvent = function(firstRow, lastRow) { + var data = { + first: firstRow, + last: lastRow + }; + this._signal("update", {data: data}); + }; + this.start = function(startRow) { + this.currentLine = Math.min(startRow || 0, this.currentLine, this.doc.getLength()); + this.lines.splice(this.currentLine, this.lines.length); + this.states.splice(this.currentLine, this.states.length); + + this.stop(); + this.running = setTimeout(this.$worker, 700); + }; + + this.scheduleStart = function() { + if (!this.running) + this.running = setTimeout(this.$worker, 700); + }; + + this.$updateOnChange = function(delta) { + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + this.lines[startRow] = null; + } else if (delta.action == "remove") { + this.lines.splice(startRow, len + 1, null); + this.states.splice(startRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(startRow, 1); + this.lines.splice.apply(this.lines, args); + this.states.splice.apply(this.states, args); + } + + this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength()); + + this.stop(); + }; + this.stop = function() { + if (this.running) + clearTimeout(this.running); + this.running = false; + }; + this.getTokens = function(row) { + return this.lines[row] || this.$tokenizeRow(row); + }; + this.getState = function(row) { + if (this.currentLine == row) + this.$tokenizeRow(row); + return this.states[row] || "start"; + }; + + this.$tokenizeRow = function(row) { + var line = this.doc.getLine(row); + var state = this.states[row - 1]; + + var data = this.tokenizer.getLineTokens(line, state, row); + + if (this.states[row] + "" !== data.state + "") { + this.states[row] = data.state; + this.lines[row + 1] = null; + if (this.currentLine > row + 1) + this.currentLine = row + 1; + } else if (this.currentLine == row) { + this.currentLine = row + 1; + } + + return this.lines[row] = data.tokens; + }; + + }).call(BackgroundTokenizer.prototype); + + exports.BackgroundTokenizer = BackgroundTokenizer; + }); + + ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"], function(acequire, exports, module) { + "use strict"; + + var lang = acequire("./lib/lang"); + var oop = acequire("./lib/oop"); + var Range = acequire("./range").Range; + + var SearchHighlight = function(regExp, clazz, type) { + this.setRegexp(regExp); + this.clazz = clazz; + this.type = type || "text"; + }; + + (function() { + this.MAX_RANGES = 500; + + this.setRegexp = function(regExp) { + if (this.regExp+"" == regExp+"") + return; + this.regExp = regExp; + this.cache = []; + }; + + this.update = function(html, markerLayer, session, config) { + if (!this.regExp) + return; + var start = config.firstRow, end = config.lastRow; + + for (var i = start; i <= end; i++) { + var ranges = this.cache[i]; + if (ranges == null) { + ranges = lang.getMatchOffsets(session.getLine(i), this.regExp); + if (ranges.length > this.MAX_RANGES) + ranges = ranges.slice(0, this.MAX_RANGES); + ranges = ranges.map(function(match) { + return new Range(i, match.offset, i, match.offset + match.length); + }); + this.cache[i] = ranges.length ? ranges : ""; + } + + for (var j = ranges.length; j --; ) { + markerLayer.drawSingleLineMarker( + html, ranges[j].toScreenRange(session), this.clazz, config); + } + } + }; + + }).call(SearchHighlight.prototype); + + exports.SearchHighlight = SearchHighlight; + }); + + ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../range").Range; + function FoldLine(foldData, folds) { + this.foldData = foldData; + if (Array.isArray(folds)) { + this.folds = folds; + } else { + folds = this.folds = [ folds ]; + } + + var last = folds[folds.length - 1]; + this.range = new Range(folds[0].start.row, folds[0].start.column, + last.end.row, last.end.column); + this.start = this.range.start; + this.end = this.range.end; + + this.folds.forEach(function(fold) { + fold.setFoldLine(this); + }, this); + } + + (function() { + this.shiftRow = function(shift) { + this.start.row += shift; + this.end.row += shift; + this.folds.forEach(function(fold) { + fold.start.row += shift; + fold.end.row += shift; + }); + }; + + this.addFold = function(fold) { + if (fold.sameRow) { + if (fold.start.row < this.startRow || fold.endRow > this.endRow) { + throw new Error("Can't add a fold to this FoldLine as it has no connection"); + } + this.folds.push(fold); + this.folds.sort(function(a, b) { + return -a.range.compareEnd(b.start.row, b.start.column); + }); + if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) { + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) { + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } + } else if (fold.start.row == this.end.row) { + this.folds.push(fold); + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (fold.end.row == this.start.row) { + this.folds.unshift(fold); + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } else { + throw new Error("Trying to add fold to FoldRow that doesn't have a matching row"); + } + fold.foldLine = this; + }; + + this.containsRow = function(row) { + return row >= this.start.row && row <= this.end.row; + }; + + this.walk = function(callback, endRow, endColumn) { + var lastEnd = 0, + folds = this.folds, + fold, + cmp, stop, isNewRow = true; + + if (endRow == null) { + endRow = this.end.row; + endColumn = this.end.column; + } + + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; + + cmp = fold.range.compareStart(endRow, endColumn); + if (cmp == -1) { + callback(null, endRow, endColumn, lastEnd, isNewRow); + return; + } + + stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow); + stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd); + if (stop || cmp === 0) { + return; + } + isNewRow = !fold.sameRow; + lastEnd = fold.end.column; + } + callback(null, endRow, endColumn, lastEnd, isNewRow); + }; + + this.getNextFoldTo = function(row, column) { + var fold, cmp; + for (var i = 0; i < this.folds.length; i++) { + fold = this.folds[i]; + cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + return { + fold: fold, + kind: "after" + }; + } else if (cmp === 0) { + return { + fold: fold, + kind: "inside" + }; + } + } + return null; + }; + + this.addRemoveChars = function(row, column, len) { + var ret = this.getNextFoldTo(row, column), + fold, folds; + if (ret) { + fold = ret.fold; + if (ret.kind == "inside" + && fold.start.column != column + && fold.start.row != row) + { + window.console && window.console.log(row, column, fold); + } else if (fold.start.row == row) { + folds = this.folds; + var i = folds.indexOf(fold); + if (i === 0) { + this.start.column += len; + } + for (i; i < folds.length; i++) { + fold = folds[i]; + fold.start.column += len; + if (!fold.sameRow) { + return; + } + fold.end.column += len; + } + this.end.column += len; + } + } + }; + + this.split = function(row, column) { + var pos = this.getNextFoldTo(row, column); + + if (!pos || pos.kind == "inside") + return null; + + var fold = pos.fold; + var folds = this.folds; + var foldData = this.foldData; + + var i = folds.indexOf(fold); + var foldBefore = folds[i - 1]; + this.end.row = foldBefore.end.row; + this.end.column = foldBefore.end.column; + folds = folds.splice(i, folds.length - i); + + var newFoldLine = new FoldLine(foldData, folds); + foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine); + return newFoldLine; + }; + + this.merge = function(foldLineNext) { + var folds = foldLineNext.folds; + for (var i = 0; i < folds.length; i++) { + this.addFold(folds[i]); + } + var foldData = this.foldData; + foldData.splice(foldData.indexOf(foldLineNext), 1); + }; + + this.toString = function() { + var ret = [this.range.toString() + ": [" ]; + + this.folds.forEach(function(fold) { + ret.push(" " + fold.toString()); + }); + ret.push("]"); + return ret.join("\n"); + }; + + this.idxToPosition = function(idx) { + var lastFoldEndColumn = 0; + + for (var i = 0; i < this.folds.length; i++) { + var fold = this.folds[i]; + + idx -= fold.start.column - lastFoldEndColumn; + if (idx < 0) { + return { + row: fold.start.row, + column: fold.start.column + idx + }; + } + + idx -= fold.placeholder.length; + if (idx < 0) { + return fold.start; + } + + lastFoldEndColumn = fold.end.column; + } + + return { + row: this.end.row, + column: this.end.column + idx + }; + }; + }).call(FoldLine.prototype); + + exports.FoldLine = FoldLine; + }); + + ace.define("ace/range_list",["require","exports","module","ace/range"], function(acequire, exports, module) { + "use strict"; + var Range = acequire("./range").Range; + var comparePoints = Range.comparePoints; + + var RangeList = function() { + this.ranges = []; + }; + + (function() { + this.comparePoints = comparePoints; + + this.pointIndex = function(pos, excludeEdges, startIndex) { + var list = this.ranges; + + for (var i = startIndex || 0; i < list.length; i++) { + var range = list[i]; + var cmpEnd = comparePoints(pos, range.end); + if (cmpEnd > 0) + continue; + var cmpStart = comparePoints(pos, range.start); + if (cmpEnd === 0) + return excludeEdges && cmpStart !== 0 ? -i-2 : i; + if (cmpStart > 0 || (cmpStart === 0 && !excludeEdges)) + return i; + + return -i-1; + } + return -i - 1; + }; + + this.add = function(range) { + var excludeEdges = !range.isEmpty(); + var startIndex = this.pointIndex(range.start, excludeEdges); + if (startIndex < 0) + startIndex = -startIndex - 1; + + var endIndex = this.pointIndex(range.end, excludeEdges, startIndex); + + if (endIndex < 0) + endIndex = -endIndex - 1; + else + endIndex++; + return this.ranges.splice(startIndex, endIndex - startIndex, range); + }; + + this.addList = function(list) { + var removed = []; + for (var i = list.length; i--; ) { + removed.push.apply(removed, this.add(list[i])); + } + return removed; + }; + + this.substractPoint = function(pos) { + var i = this.pointIndex(pos); + + if (i >= 0) + return this.ranges.splice(i, 1); + }; + this.merge = function() { + var removed = []; + var list = this.ranges; + + list = list.sort(function(a, b) { + return comparePoints(a.start, b.start); + }); + + var next = list[0], range; + for (var i = 1; i < list.length; i++) { + range = next; + next = list[i]; + var cmp = comparePoints(range.end, next.start); + if (cmp < 0) + continue; + + if (cmp == 0 && !range.isEmpty() && !next.isEmpty()) + continue; + + if (comparePoints(range.end, next.end) < 0) { + range.end.row = next.end.row; + range.end.column = next.end.column; + } + + list.splice(i, 1); + removed.push(next); + next = range; + i--; + } + + this.ranges = list; + + return removed; + }; + + this.contains = function(row, column) { + return this.pointIndex({row: row, column: column}) >= 0; + }; + + this.containsPoint = function(pos) { + return this.pointIndex(pos) >= 0; + }; + + this.rangeAtPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) + return this.ranges[i]; + }; + + + this.clipRows = function(startRow, endRow) { + var list = this.ranges; + if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow) + return []; + + var startIndex = this.pointIndex({row: startRow, column: 0}); + if (startIndex < 0) + startIndex = -startIndex - 1; + var endIndex = this.pointIndex({row: endRow, column: 0}, startIndex); + if (endIndex < 0) + endIndex = -endIndex - 1; + + var clipped = []; + for (var i = startIndex; i < endIndex; i++) { + clipped.push(list[i]); + } + return clipped; + }; + + this.removeAll = function() { + return this.ranges.splice(0, this.ranges.length); + }; + + this.attach = function(session) { + if (this.session) + this.detach(); + + this.session = session; + this.onChange = this.$onChange.bind(this); + + this.session.on('change', this.onChange); + }; + + this.detach = function() { + if (!this.session) + return; + this.session.removeListener('change', this.onChange); + this.session = null; + }; + + this.$onChange = function(delta) { + if (delta.action == "insert"){ + var start = delta.start; + var end = delta.end; + } else { + var end = delta.start; + var start = delta.end; + } + var startRow = start.row; + var endRow = end.row; + var lineDif = endRow - startRow; + + var colDiff = -start.column + end.column; + var ranges = this.ranges; + + for (var i = 0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row < startRow) + continue; + if (r.start.row > startRow) + break; + + if (r.start.row == startRow && r.start.column >= start.column ) { + if (r.start.column == start.column && this.$insertRight) { + } else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } + if (r.end.row == startRow && r.end.column >= start.column) { + if (r.end.column == start.column && this.$insertRight) { + continue; + } + if (r.end.column == start.column && colDiff > 0 && i < n - 1) { + if (r.end.column > r.start.column && r.end.column == ranges[i+1].start.column) + r.end.column -= colDiff; + } + r.end.column += colDiff; + r.end.row += lineDif; + } + } + + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } + }; + + }).call(RangeList.prototype); + + exports.RangeList = RangeList; + }); + + ace.define("ace/edit_session/fold",["require","exports","module","ace/range","ace/range_list","ace/lib/oop"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../range").Range; + var RangeList = acequire("../range_list").RangeList; + var oop = acequire("../lib/oop"); + var Fold = exports.Fold = function(range, placeholder) { + this.foldLine = null; + this.placeholder = placeholder; + this.range = range; + this.start = range.start; + this.end = range.end; + + this.sameRow = range.start.row == range.end.row; + this.subFolds = this.ranges = []; + }; + + oop.inherits(Fold, RangeList); + + (function() { + + this.toString = function() { + return '"' + this.placeholder + '" ' + this.range.toString(); + }; + + this.setFoldLine = function(foldLine) { + this.foldLine = foldLine; + this.subFolds.forEach(function(fold) { + fold.setFoldLine(foldLine); + }); + }; + + this.clone = function() { + var range = this.range.clone(); + var fold = new Fold(range, this.placeholder); + this.subFolds.forEach(function(subFold) { + fold.subFolds.push(subFold.clone()); + }); + fold.collapseChildren = this.collapseChildren; + return fold; + }; + + this.addSubFold = function(fold) { + if (this.range.isEqual(fold)) + return; + + if (!this.range.containsRange(fold)) + throw new Error("A fold can't intersect already existing fold" + fold.range + this.range); + consumeRange(fold, this.start); + + var row = fold.start.row, column = fold.start.column; + for (var i = 0, cmp = -1; i < this.subFolds.length; i++) { + cmp = this.subFolds[i].range.compare(row, column); + if (cmp != 1) + break; + } + var afterStart = this.subFolds[i]; + + if (cmp == 0) + return afterStart.addSubFold(fold); + var row = fold.range.end.row, column = fold.range.end.column; + for (var j = i, cmp = -1; j < this.subFolds.length; j++) { + cmp = this.subFolds[j].range.compare(row, column); + if (cmp != 1) + break; + } + var afterEnd = this.subFolds[j]; + + if (cmp == 0) + throw new Error("A fold can't intersect already existing fold" + fold.range + this.range); + + var consumedFolds = this.subFolds.splice(i, j - i, fold); + fold.setFoldLine(this.foldLine); + + return fold; + }; + + this.restoreRange = function(range) { + return restoreRange(range, this.start); + }; + + }).call(Fold.prototype); + + function consumePoint(point, anchor) { + point.row -= anchor.row; + if (point.row == 0) + point.column -= anchor.column; + } + function consumeRange(range, anchor) { + consumePoint(range.start, anchor); + consumePoint(range.end, anchor); + } + function restorePoint(point, anchor) { + if (point.row == 0) + point.column += anchor.column; + point.row += anchor.row; + } + function restoreRange(range, anchor) { + restorePoint(range.start, anchor); + restorePoint(range.end, anchor); + } + + }); + + ace.define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../range").Range; + var FoldLine = acequire("./fold_line").FoldLine; + var Fold = acequire("./fold").Fold; + var TokenIterator = acequire("../token_iterator").TokenIterator; + + function Folding() { + this.getFoldAt = function(row, column, side) { + var foldLine = this.getFoldLine(row); + if (!foldLine) + return null; + + var folds = foldLine.folds; + for (var i = 0; i < folds.length; i++) { + var fold = folds[i]; + if (fold.range.contains(row, column)) { + if (side == 1 && fold.range.isEnd(row, column)) { + continue; + } else if (side == -1 && fold.range.isStart(row, column)) { + continue; + } + return fold; + } + } + }; + this.getFoldsInRange = function(range) { + var start = range.start; + var end = range.end; + var foldLines = this.$foldData; + var foundFolds = []; + + start.column += 1; + end.column -= 1; + + for (var i = 0; i < foldLines.length; i++) { + var cmp = foldLines[i].range.compareRange(range); + if (cmp == 2) { + continue; + } + else if (cmp == -2) { + break; + } + + var folds = foldLines[i].folds; + for (var j = 0; j < folds.length; j++) { + var fold = folds[j]; + cmp = fold.range.compareRange(range); + if (cmp == -2) { + break; + } else if (cmp == 2) { + continue; + } else + if (cmp == 42) { + break; + } + foundFolds.push(fold); + } + } + start.column -= 1; + end.column += 1; + + return foundFolds; + }; + + this.getFoldsInRangeList = function(ranges) { + if (Array.isArray(ranges)) { + var folds = []; + ranges.forEach(function(range) { + folds = folds.concat(this.getFoldsInRange(range)); + }, this); + } else { + var folds = this.getFoldsInRange(ranges); + } + return folds; + }; + this.getAllFolds = function() { + var folds = []; + var foldLines = this.$foldData; + + for (var i = 0; i < foldLines.length; i++) + for (var j = 0; j < foldLines[i].folds.length; j++) + folds.push(foldLines[i].folds[j]); + + return folds; + }; + this.getFoldStringAt = function(row, column, trim, foldLine) { + foldLine = foldLine || this.getFoldLine(row); + if (!foldLine) + return null; + + var lastFold = { + end: { column: 0 } + }; + var str, fold; + for (var i = 0; i < foldLine.folds.length; i++) { + fold = foldLine.folds[i]; + var cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + str = this + .getLine(fold.start.row) + .substring(lastFold.end.column, fold.start.column); + break; + } + else if (cmp === 0) { + return null; + } + lastFold = fold; + } + if (!str) + str = this.getLine(fold.start.row).substring(lastFold.end.column); + + if (trim == -1) + return str.substring(0, column - lastFold.end.column); + else if (trim == 1) + return str.substring(column - lastFold.end.column); + else + return str; + }; + + this.getFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) { + return foldLine; + } else if (foldLine.end.row > docRow) { + return null; + } + } + return null; + }; + this.getNextFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.end.row >= docRow) { + return foldLine; + } + } + return null; + }; + + this.getFoldedRowCount = function(first, last) { + var foldData = this.$foldData, rowCount = last-first+1; + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i], + end = foldLine.end.row, + start = foldLine.start.row; + if (end >= last) { + if (start < last) { + if (start >= first) + rowCount -= last-start; + else + rowCount = 0; // in one fold + } + break; + } else if (end >= first){ + if (start >= first) // fold inside range + rowCount -= end-start; + else + rowCount -= end-first+1; + } + } + return rowCount; + }; + + this.$addFoldLine = function(foldLine) { + this.$foldData.push(foldLine); + this.$foldData.sort(function(a, b) { + return a.start.row - b.start.row; + }); + return foldLine; + }; + this.addFold = function(placeholder, range) { + var foldData = this.$foldData; + var added = false; + var fold; + + if (placeholder instanceof Fold) + fold = placeholder; + else { + fold = new Fold(range, placeholder); + fold.collapseChildren = range.collapseChildren; + } + this.$clipRangeToDocument(fold.range); + + var startRow = fold.start.row; + var startColumn = fold.start.column; + var endRow = fold.end.row; + var endColumn = fold.end.column; + if (!(startRow < endRow || + startRow == endRow && startColumn <= endColumn - 2)) + throw new Error("The range has to be at least 2 characters width"); + + var startFold = this.getFoldAt(startRow, startColumn, 1); + var endFold = this.getFoldAt(endRow, endColumn, -1); + if (startFold && endFold == startFold) + return startFold.addSubFold(fold); + + if (startFold && !startFold.range.isStart(startRow, startColumn)) + this.removeFold(startFold); + + if (endFold && !endFold.range.isEnd(endRow, endColumn)) + this.removeFold(endFold); + var folds = this.getFoldsInRange(fold.range); + if (folds.length > 0) { + this.removeFolds(folds); + folds.forEach(function(subFold) { + fold.addSubFold(subFold); + }); + } + + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (endRow == foldLine.start.row) { + foldLine.addFold(fold); + added = true; + break; + } else if (startRow == foldLine.end.row) { + foldLine.addFold(fold); + added = true; + if (!fold.sameRow) { + var foldLineNext = foldData[i + 1]; + if (foldLineNext && foldLineNext.start.row == endRow) { + foldLine.merge(foldLineNext); + break; + } + } + break; + } else if (endRow <= foldLine.start.row) { + break; + } + } + + if (!added) + foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold)); + + if (this.$useWrapMode) + this.$updateWrapData(foldLine.start.row, foldLine.start.row); + else + this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row); + this.$modified = true; + this._signal("changeFold", { data: fold, action: "add" }); + + return fold; + }; + + this.addFolds = function(folds) { + folds.forEach(function(fold) { + this.addFold(fold); + }, this); + }; + + this.removeFold = function(fold) { + var foldLine = fold.foldLine; + var startRow = foldLine.start.row; + var endRow = foldLine.end.row; + + var foldLines = this.$foldData; + var folds = foldLine.folds; + if (folds.length == 1) { + foldLines.splice(foldLines.indexOf(foldLine), 1); + } else + if (foldLine.range.isEnd(fold.end.row, fold.end.column)) { + folds.pop(); + foldLine.end.row = folds[folds.length - 1].end.row; + foldLine.end.column = folds[folds.length - 1].end.column; + } else + if (foldLine.range.isStart(fold.start.row, fold.start.column)) { + folds.shift(); + foldLine.start.row = folds[0].start.row; + foldLine.start.column = folds[0].start.column; + } else + if (fold.sameRow) { + folds.splice(folds.indexOf(fold), 1); + } else + { + var newFoldLine = foldLine.split(fold.start.row, fold.start.column); + folds = newFoldLine.folds; + folds.shift(); + newFoldLine.start.row = folds[0].start.row; + newFoldLine.start.column = folds[0].start.column; + } + + if (!this.$updating) { + if (this.$useWrapMode) + this.$updateWrapData(startRow, endRow); + else + this.$updateRowLengthCache(startRow, endRow); + } + this.$modified = true; + this._signal("changeFold", { data: fold, action: "remove" }); + }; + + this.removeFolds = function(folds) { + var cloneFolds = []; + for (var i = 0; i < folds.length; i++) { + cloneFolds.push(folds[i]); + } + + cloneFolds.forEach(function(fold) { + this.removeFold(fold); + }, this); + this.$modified = true; + }; + + this.expandFold = function(fold) { + this.removeFold(fold); + fold.subFolds.forEach(function(subFold) { + fold.restoreRange(subFold); + this.addFold(subFold); + }, this); + if (fold.collapseChildren > 0) { + this.foldAll(fold.start.row+1, fold.end.row, fold.collapseChildren-1); + } + fold.subFolds = []; + }; + + this.expandFolds = function(folds) { + folds.forEach(function(fold) { + this.expandFold(fold); + }, this); + }; + + this.unfold = function(location, expandInner) { + var range, folds; + if (location == null) { + range = new Range(0, 0, this.getLength(), 0); + expandInner = true; + } else if (typeof location == "number") + range = new Range(location, 0, location, this.getLine(location).length); + else if ("row" in location) + range = Range.fromPoints(location, location); + else + range = location; + + folds = this.getFoldsInRangeList(range); + if (expandInner) { + this.removeFolds(folds); + } else { + var subFolds = folds; + while (subFolds.length) { + this.expandFolds(subFolds); + subFolds = this.getFoldsInRangeList(range); + } + } + if (folds.length) + return folds; + }; + this.isRowFolded = function(docRow, startFoldRow) { + return !!this.getFoldLine(docRow, startFoldRow); + }; + + this.getRowFoldEnd = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.end.row : docRow; + }; + + this.getRowFoldStart = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.start.row : docRow; + }; + + this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) { + if (startRow == null) + startRow = foldLine.start.row; + if (startColumn == null) + startColumn = 0; + if (endRow == null) + endRow = foldLine.end.row; + if (endColumn == null) + endColumn = this.getLine(endRow).length; + var doc = this.doc; + var textLine = ""; + + foldLine.walk(function(placeholder, row, column, lastColumn) { + if (row < startRow) + return; + if (row == startRow) { + if (column < startColumn) + return; + lastColumn = Math.max(startColumn, lastColumn); + } + + if (placeholder != null) { + textLine += placeholder; + } else { + textLine += doc.getLine(row).substring(lastColumn, column); + } + }, endRow, endColumn); + return textLine; + }; + + this.getDisplayLine = function(row, endColumn, startRow, startColumn) { + var foldLine = this.getFoldLine(row); + + if (!foldLine) { + var line; + line = this.doc.getLine(row); + return line.substring(startColumn || 0, endColumn || line.length); + } else { + return this.getFoldDisplayLine( + foldLine, row, endColumn, startRow, startColumn); + } + }; + + this.$cloneFoldData = function() { + var fd = []; + fd = this.$foldData.map(function(foldLine) { + var folds = foldLine.folds.map(function(fold) { + return fold.clone(); + }); + return new FoldLine(fd, folds); + }); + + return fd; + }; + + this.toggleFold = function(tryToUnfold) { + var selection = this.selection; + var range = selection.getRange(); + var fold; + var bracketPos; + + if (range.isEmpty()) { + var cursor = range.start; + fold = this.getFoldAt(cursor.row, cursor.column); + + if (fold) { + this.expandFold(fold); + return; + } else if (bracketPos = this.findMatchingBracket(cursor)) { + if (range.comparePoint(bracketPos) == 1) { + range.end = bracketPos; + } else { + range.start = bracketPos; + range.start.column++; + range.end.column--; + } + } else if (bracketPos = this.findMatchingBracket({row: cursor.row, column: cursor.column + 1})) { + if (range.comparePoint(bracketPos) == 1) + range.end = bracketPos; + else + range.start = bracketPos; + + range.start.column++; + } else { + range = this.getCommentFoldRange(cursor.row, cursor.column) || range; + } + } else { + var folds = this.getFoldsInRange(range); + if (tryToUnfold && folds.length) { + this.expandFolds(folds); + return; + } else if (folds.length == 1 ) { + fold = folds[0]; + } + } + + if (!fold) + fold = this.getFoldAt(range.start.row, range.start.column); + + if (fold && fold.range.toString() == range.toString()) { + this.expandFold(fold); + return; + } + + var placeholder = "..."; + if (!range.isMultiLine()) { + placeholder = this.getTextRange(range); + if (placeholder.length < 4) + return; + placeholder = placeholder.trim().substring(0, 2) + ".."; + } + + this.addFold(placeholder, range); + }; + + this.getCommentFoldRange = function(row, column, dir) { + var iterator = new TokenIterator(this, row, column); + var token = iterator.getCurrentToken(); + var type = token.type; + if (token && /^comment|string/.test(type)) { + type = type.match(/comment|string/)[0]; + if (type == "comment") + type += "|doc-start"; + var re = new RegExp(type); + var range = new Range(); + if (dir != 1) { + do { + token = iterator.stepBackward(); + } while (token && re.test(token.type)); + iterator.stepForward(); + } + + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + 2; + + iterator = new TokenIterator(this, row, column); + + if (dir != -1) { + var lastRow = -1; + do { + token = iterator.stepForward(); + if (lastRow == -1) { + var state = this.getState(iterator.$row); + if (!re.test(state)) + lastRow = iterator.$row; + } else if (iterator.$row > lastRow) { + break; + } + } while (token && re.test(token.type)); + token = iterator.stepBackward(); + } else + token = iterator.getCurrentToken(); + + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = iterator.getCurrentTokenColumn() + token.value.length - 2; + return range; + } + }; + + this.foldAll = function(startRow, endRow, depth) { + if (depth == undefined) + depth = 100000; // JSON.stringify doesn't hanle Infinity + var foldWidgets = this.foldWidgets; + if (!foldWidgets) + return; // mode doesn't support folding + endRow = endRow || this.getLength(); + startRow = startRow || 0; + for (var row = startRow; row < endRow; row++) { + if (foldWidgets[row] == null) + foldWidgets[row] = this.getFoldWidget(row); + if (foldWidgets[row] != "start") + continue; + + var range = this.getFoldWidgetRange(row); + if (range && range.isMultiLine() + && range.end.row <= endRow + && range.start.row >= startRow + ) { + row = range.end.row; + try { + var fold = this.addFold("...", range); + if (fold) + fold.collapseChildren = depth; + } catch(e) {} + } + } + }; + this.$foldStyles = { + "manual": 1, + "markbegin": 1, + "markbeginend": 1 + }; + this.$foldStyle = "markbegin"; + this.setFoldStyle = function(style) { + if (!this.$foldStyles[style]) + throw new Error("invalid fold style: " + style + "[" + Object.keys(this.$foldStyles).join(", ") + "]"); + + if (this.$foldStyle == style) + return; + + this.$foldStyle = style; + + if (style == "manual") + this.unfold(); + var mode = this.$foldMode; + this.$setFolding(null); + this.$setFolding(mode); + }; + + this.$setFolding = function(foldMode) { + if (this.$foldMode == foldMode) + return; + + this.$foldMode = foldMode; + + this.off('change', this.$updateFoldWidgets); + this.off('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + this._signal("changeAnnotation"); + + if (!foldMode || this.$foldStyle == "manual") { + this.foldWidgets = null; + return; + } + + this.foldWidgets = []; + this.getFoldWidget = foldMode.getFoldWidget.bind(foldMode, this, this.$foldStyle); + this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this, this.$foldStyle); + + this.$updateFoldWidgets = this.updateFoldWidgets.bind(this); + this.$tokenizerUpdateFoldWidgets = this.tokenizerUpdateFoldWidgets.bind(this); + this.on('change', this.$updateFoldWidgets); + this.on('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + }; + + this.getParentFoldRangeData = function (row, ignoreCurrent) { + var fw = this.foldWidgets; + if (!fw || (ignoreCurrent && fw[row])) + return {}; + + var i = row - 1, firstRange; + while (i >= 0) { + var c = fw[i]; + if (c == null) + c = fw[i] = this.getFoldWidget(i); + + if (c == "start") { + var range = this.getFoldWidgetRange(i); + if (!firstRange) + firstRange = range; + if (range && range.end.row >= row) + break; + } + i--; + } + + return { + range: i !== -1 && range, + firstRange: firstRange + }; + }; + + this.onFoldWidgetClick = function(row, e) { + e = e.domEvent; + var options = { + children: e.shiftKey, + all: e.ctrlKey || e.metaKey, + siblings: e.altKey + }; + + var range = this.$toggleFoldWidget(row, options); + if (!range) { + var el = (e.target || e.srcElement); + if (el && /ace_fold-widget/.test(el.className)) + el.className += " ace_invalid"; + } + }; + + this.$toggleFoldWidget = function(row, options) { + if (!this.getFoldWidget) + return; + var type = this.getFoldWidget(row); + var line = this.getLine(row); + + var dir = type === "end" ? -1 : 1; + var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir); + + if (fold) { + if (options.children || options.all) + this.removeFold(fold); + else + this.expandFold(fold); + return fold; + } + + var range = this.getFoldWidgetRange(row, true); + if (range && !range.isMultiLine()) { + fold = this.getFoldAt(range.start.row, range.start.column, 1); + if (fold && range.isEqual(fold.range)) { + this.removeFold(fold); + return fold; + } + } + + if (options.siblings) { + var data = this.getParentFoldRangeData(row); + if (data.range) { + var startRow = data.range.start.row + 1; + var endRow = data.range.end.row; + } + this.foldAll(startRow, endRow, options.all ? 10000 : 0); + } else if (options.children) { + endRow = range ? range.end.row : this.getLength(); + this.foldAll(row + 1, endRow, options.all ? 10000 : 0); + } else if (range) { + if (options.all) + range.collapseChildren = 10000; + this.addFold("...", range); + } + + return range; + }; + + + + this.toggleFoldWidget = function(toggleParent) { + var row = this.selection.getCursor().row; + row = this.getRowFoldStart(row); + var range = this.$toggleFoldWidget(row, {}); + + if (range) + return; + var data = this.getParentFoldRangeData(row, true); + range = data.range || data.firstRange; + + if (range) { + row = range.start.row; + var fold = this.getFoldAt(row, this.getLine(row).length, 1); + + if (fold) { + this.removeFold(fold); + } else { + this.addFold("...", range); + } + } + }; + + this.updateFoldWidgets = function(delta) { + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + + if (len === 0) { + this.foldWidgets[firstRow] = null; + } else if (delta.action == 'remove') { + this.foldWidgets.splice(firstRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(firstRow, 1); + this.foldWidgets.splice.apply(this.foldWidgets, args); + } + }; + this.tokenizerUpdateFoldWidgets = function(e) { + var rows = e.data; + if (rows.first != rows.last) { + if (this.foldWidgets.length > rows.first) + this.foldWidgets.splice(rows.first, this.foldWidgets.length); + } + }; + } + + exports.Folding = Folding; + + }); + + ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"], function(acequire, exports, module) { + "use strict"; + + var TokenIterator = acequire("../token_iterator").TokenIterator; + var Range = acequire("../range").Range; + + + function BracketMatch() { + + this.findMatchingBracket = function(position, chr) { + if (position.column == 0) return null; + + var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column-1); + if (charBeforeCursor == "") return null; + + var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/); + if (!match) + return null; + + if (match[1]) + return this.$findClosingBracket(match[1], position); + else + return this.$findOpeningBracket(match[2], position); + }; + + this.getBracketRange = function(pos) { + var line = this.getLine(pos.row); + var before = true, range; + + var chr = line.charAt(pos.column-1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = {row: pos.row, column: pos.column + 1}; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + before = false; + } + if (!match) + return null; + + if (match[1]) { + var bracketPos = this.$findClosingBracket(match[1], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(pos, bracketPos); + if (!before) { + range.end.column++; + range.start.column--; + } + range.cursor = range.end; + } else { + var bracketPos = this.$findOpeningBracket(match[2], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(bracketPos, pos); + if (!before) { + range.start.column++; + range.end.column--; + } + range.cursor = range.start; + } + + return range; + }; + + this.$brackets = { + ")": "(", + "(": ")", + "]": "[", + "[": "]", + "{": "}", + "}": "{" + }; + + this.$findOpeningBracket = function(bracket, position, typeRe) { + var openBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("rparen", ".paren") + .replace(/\b(?:end)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; + var value = token.value; + + while (true) { + + while (valueIndex >= 0) { + var chr = value.charAt(valueIndex); + if (chr == openBracket) { + depth -= 1; + if (depth == 0) { + return {row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn()}; + } + } + else if (chr == bracket) { + depth += 1; + } + valueIndex -= 1; + } + do { + token = iterator.stepBackward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) + break; + + value = token.value; + valueIndex = value.length - 1; + } + + return null; + }; + + this.$findClosingBracket = function(bracket, position, typeRe) { + var closingBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + + if (!typeRe){ + typeRe = new RegExp( + "(\\.?" + + token.type.replace(".", "\\.").replace("lparen", ".paren") + .replace(/\b(?:start|begin)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn(); + + while (true) { + + var value = token.value; + var valueLength = value.length; + while (valueIndex < valueLength) { + var chr = value.charAt(valueIndex); + if (chr == closingBracket) { + depth -= 1; + if (depth == 0) { + return {row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn()}; + } + } + else if (chr == bracket) { + depth += 1; + } + valueIndex += 1; + } + do { + token = iterator.stepForward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) + break; + + valueIndex = 0; + } + + return null; + }; + } + exports.BracketMatch = BracketMatch; + + }); + + ace.define("ace/edit_session",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/bidihandler","ace/config","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var lang = acequire("./lib/lang"); + var BidiHandler = acequire("./bidihandler").BidiHandler; + var config = acequire("./config"); + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var Selection = acequire("./selection").Selection; + var TextMode = acequire("./mode/text").Mode; + var Range = acequire("./range").Range; + var Document = acequire("./document").Document; + var BackgroundTokenizer = acequire("./background_tokenizer").BackgroundTokenizer; + var SearchHighlight = acequire("./search_highlight").SearchHighlight; + + var EditSession = function(text, mode) { + this.$breakpoints = []; + this.$decorations = []; + this.$frontMarkers = {}; + this.$backMarkers = {}; + this.$markerId = 1; + this.$undoSelect = true; + + this.$foldData = []; + this.id = "session" + (++EditSession.$uid); + this.$foldData.toString = function() { + return this.join("\n"); + }; + this.on("changeFold", this.onChangeFold.bind(this)); + this.$onChange = this.onChange.bind(this); + + if (typeof text != "object" || !text.getLine) + text = new Document(text); + + this.$bidiHandler = new BidiHandler(this); + this.setDocument(text); + this.selection = new Selection(this); + + config.resetOptions(this); + this.setMode(mode); + config._signal("session", this); + }; + + + EditSession.$uid = 0; + + (function() { + + oop.implement(this, EventEmitter); + this.setDocument = function(doc) { + if (this.doc) + this.doc.removeListener("change", this.$onChange); + + this.doc = doc; + doc.on("change", this.$onChange); + + if (this.bgTokenizer) + this.bgTokenizer.setDocument(this.getDocument()); + + this.resetCaches(); + }; + this.getDocument = function() { + return this.doc; + }; + this.$resetRowCache = function(docRow) { + if (!docRow) { + this.$docRowCache = []; + this.$screenRowCache = []; + return; + } + var l = this.$docRowCache.length; + var i = this.$getRowCacheIndex(this.$docRowCache, docRow) + 1; + if (l > i) { + this.$docRowCache.splice(i, l); + this.$screenRowCache.splice(i, l); + } + }; + + this.$getRowCacheIndex = function(cacheArray, val) { + var low = 0; + var hi = cacheArray.length - 1; + + while (low <= hi) { + var mid = (low + hi) >> 1; + var c = cacheArray[mid]; + + if (val > c) + low = mid + 1; + else if (val < c) + hi = mid - 1; + else + return mid; + } + + return low -1; + }; + + this.resetCaches = function() { + this.$modified = true; + this.$wrapData = []; + this.$rowLengthCache = []; + this.$resetRowCache(0); + if (this.bgTokenizer) + this.bgTokenizer.start(0); + }; + + this.onChangeFold = function(e) { + var fold = e.data; + this.$resetRowCache(fold.start.row); + }; + + this.onChange = function(delta) { + this.$modified = true; + this.$bidiHandler.onChange(delta); + this.$resetRowCache(delta.start.row); + + var removedFolds = this.$updateInternalDataOnChange(delta); + if (!this.$fromUndo && this.$undoManager && !delta.ignore) { + this.$deltasDoc.push(delta); + if (removedFolds && removedFolds.length != 0) { + this.$deltasFold.push({ + action: "removeFolds", + folds: removedFolds + }); + } + + this.$informUndoManager.schedule(); + } + + this.bgTokenizer && this.bgTokenizer.$updateOnChange(delta); + this._signal("change", delta); + }; + this.setValue = function(text) { + this.doc.setValue(text); + this.selection.moveTo(0, 0); + + this.$resetRowCache(0); + this.$deltas = []; + this.$deltasDoc = []; + this.$deltasFold = []; + this.setUndoManager(this.$undoManager); + this.getUndoManager().reset(); + }; + this.getValue = + this.toString = function() { + return this.doc.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.getState = function(row) { + return this.bgTokenizer.getState(row); + }; + this.getTokens = function(row) { + return this.bgTokenizer.getTokens(row); + }; + this.getTokenAt = function(row, column) { + var tokens = this.bgTokenizer.getTokens(row); + var token, c = 0; + if (column == null) { + var i = tokens.length - 1; + c = this.getLine(row).length; + } else { + for (var i = 0; i < tokens.length; i++) { + c += tokens[i].value.length; + if (c >= column) + break; + } + } + token = tokens[i]; + if (!token) + return null; + token.index = i; + token.start = c - token.value.length; + return token; + }; + this.setUndoManager = function(undoManager) { + this.$undoManager = undoManager; + this.$deltas = []; + this.$deltasDoc = []; + this.$deltasFold = []; + + if (this.$informUndoManager) + this.$informUndoManager.cancel(); + + if (undoManager) { + var self = this; + + this.$syncInformUndoManager = function() { + self.$informUndoManager.cancel(); + + if (self.$deltasFold.length) { + self.$deltas.push({ + group: "fold", + deltas: self.$deltasFold + }); + self.$deltasFold = []; + } + + if (self.$deltasDoc.length) { + self.$deltas.push({ + group: "doc", + deltas: self.$deltasDoc + }); + self.$deltasDoc = []; + } + + if (self.$deltas.length > 0) { + undoManager.execute({ + action: "aceupdate", + args: [self.$deltas, self], + merge: self.mergeUndoDeltas + }); + } + self.mergeUndoDeltas = false; + self.$deltas = []; + }; + this.$informUndoManager = lang.delayedCall(this.$syncInformUndoManager); + } + }; + this.markUndoGroup = function() { + if (this.$syncInformUndoManager) + this.$syncInformUndoManager(); + }; + + this.$defaultUndoManager = { + undo: function() {}, + redo: function() {}, + reset: function() {} + }; + this.getUndoManager = function() { + return this.$undoManager || this.$defaultUndoManager; + }; + this.getTabString = function() { + if (this.getUseSoftTabs()) { + return lang.stringRepeat(" ", this.getTabSize()); + } else { + return "\t"; + } + }; + this.setUseSoftTabs = function(val) { + this.setOption("useSoftTabs", val); + }; + this.getUseSoftTabs = function() { + return this.$useSoftTabs && !this.$mode.$indentWithTabs; + }; + this.setTabSize = function(tabSize) { + this.setOption("tabSize", tabSize); + }; + this.getTabSize = function() { + return this.$tabSize; + }; + this.isTabStop = function(position) { + return this.$useSoftTabs && (position.column % this.$tabSize === 0); + }; + this.setNavigateWithinSoftTabs = function (navigateWithinSoftTabs) { + this.setOption("navigateWithinSoftTabs", navigateWithinSoftTabs); + }; + this.getNavigateWithinSoftTabs = function() { + return this.$navigateWithinSoftTabs; + }; + + this.$overwrite = false; + this.setOverwrite = function(overwrite) { + this.setOption("overwrite", overwrite); + }; + this.getOverwrite = function() { + return this.$overwrite; + }; + this.toggleOverwrite = function() { + this.setOverwrite(!this.$overwrite); + }; + this.addGutterDecoration = function(row, className) { + if (!this.$decorations[row]) + this.$decorations[row] = ""; + this.$decorations[row] += " " + className; + this._signal("changeBreakpoint", {}); + }; + this.removeGutterDecoration = function(row, className) { + this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); + this._signal("changeBreakpoint", {}); + }; + this.getBreakpoints = function() { + return this.$breakpoints; + }; + this.setBreakpoints = function(rows) { + this.$breakpoints = []; + for (var i=0; i 0) + inToken = !!line.charAt(column - 1).match(this.tokenRe); + + if (!inToken) + inToken = !!line.charAt(column).match(this.tokenRe); + + if (inToken) + var re = this.tokenRe; + else if (/^\s+$/.test(line.slice(column-1, column+1))) + var re = /\s/; + else + var re = this.nonTokenRe; + + var start = column; + if (start > 0) { + do { + start--; + } + while (start >= 0 && line.charAt(start).match(re)); + start++; + } + + var end = column; + while (end < line.length && line.charAt(end).match(re)) { + end++; + } + + return new Range(row, start, row, end); + }; + this.getAWordRange = function(row, column) { + var wordRange = this.getWordRange(row, column); + var line = this.getLine(wordRange.end.row); + + while (line.charAt(wordRange.end.column).match(/[ \t]/)) { + wordRange.end.column += 1; + } + return wordRange; + }; + this.setNewLineMode = function(newLineMode) { + this.doc.setNewLineMode(newLineMode); + }; + this.getNewLineMode = function() { + return this.doc.getNewLineMode(); + }; + this.setUseWorker = function(useWorker) { this.setOption("useWorker", useWorker); }; + this.getUseWorker = function() { return this.$useWorker; }; + this.onReloadTokenizer = function(e) { + var rows = e.data; + this.bgTokenizer.start(rows.first); + this._signal("tokenizerUpdate", e); + }; + + this.$modes = {}; + this.$mode = null; + this.$modeId = null; + this.setMode = function(mode, cb) { + if (mode && typeof mode === "object") { + if (mode.getTokenizer) + return this.$onChangeMode(mode); + var options = mode; + var path = options.path; + } else { + path = mode || "ace/mode/text"; + } + if (!this.$modes["ace/mode/text"]) + this.$modes["ace/mode/text"] = new TextMode(); + + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + cb && cb(); + return; + } + this.$modeId = path; + config.loadModule(["mode", path], function(m) { + if (this.$modeId !== path) + return cb && cb(); + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + } else if (m && m.Mode) { + m = new m.Mode(options); + if (!options) { + this.$modes[path] = m; + m.$id = path; + } + this.$onChangeMode(m); + } + cb && cb(); + }.bind(this)); + if (!this.$mode) + this.$onChangeMode(this.$modes["ace/mode/text"], true); + }; + + this.$onChangeMode = function(mode, $isPlaceholder) { + if (!$isPlaceholder) + this.$modeId = mode.$id; + if (this.$mode === mode) + return; + + this.$mode = mode; + + this.$stopWorker(); + + if (this.$useWorker) + this.$startWorker(); + + var tokenizer = mode.getTokenizer(); + + if(tokenizer.addEventListener !== undefined) { + var onReloadTokenizer = this.onReloadTokenizer.bind(this); + tokenizer.addEventListener("update", onReloadTokenizer); + } + + if (!this.bgTokenizer) { + this.bgTokenizer = new BackgroundTokenizer(tokenizer); + var _self = this; + this.bgTokenizer.addEventListener("update", function(e) { + _self._signal("tokenizerUpdate", e); + }); + } else { + this.bgTokenizer.setTokenizer(tokenizer); + } + + this.bgTokenizer.setDocument(this.getDocument()); + + this.tokenRe = mode.tokenRe; + this.nonTokenRe = mode.nonTokenRe; + + + if (!$isPlaceholder) { + if (mode.attachToSession) + mode.attachToSession(this); + this.$options.wrapMethod.set.call(this, this.$wrapMethod); + this.$setFolding(mode.foldingRules); + this.bgTokenizer.start(0); + this._emit("changeMode"); + } + }; + + this.$stopWorker = function() { + if (this.$worker) { + this.$worker.terminate(); + this.$worker = null; + } + }; + + this.$startWorker = function() { + try { + this.$worker = this.$mode.createWorker(this); + } catch (e) { + config.warn("Could not load worker", e); + this.$worker = null; + } + }; + this.getMode = function() { + return this.$mode; + }; + + this.$scrollTop = 0; + this.setScrollTop = function(scrollTop) { + if (this.$scrollTop === scrollTop || isNaN(scrollTop)) + return; + + this.$scrollTop = scrollTop; + this._signal("changeScrollTop", scrollTop); + }; + this.getScrollTop = function() { + return this.$scrollTop; + }; + + this.$scrollLeft = 0; + this.setScrollLeft = function(scrollLeft) { + if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft)) + return; + + this.$scrollLeft = scrollLeft; + this._signal("changeScrollLeft", scrollLeft); + }; + this.getScrollLeft = function() { + return this.$scrollLeft; + }; + this.getScreenWidth = function() { + this.$computeWidth(); + if (this.lineWidgets) + return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth); + return this.screenWidth; + }; + + this.getLineWidgetMaxWidth = function() { + if (this.lineWidgetsWidth != null) return this.lineWidgetsWidth; + var width = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.screenWidth > width) + width = w.screenWidth; + }); + return this.lineWidgetWidth = width; + }; + + this.$computeWidth = function(force) { + if (this.$modified || force) { + this.$modified = false; + + if (this.$useWrapMode) + return this.screenWidth = this.$wrapLimit; + + var lines = this.doc.getAllLines(); + var cache = this.$rowLengthCache; + var longestScreenLine = 0; + var foldIndex = 0; + var foldLine = this.$foldData[foldIndex]; + var foldStart = foldLine ? foldLine.start.row : Infinity; + var len = lines.length; + + for (var i = 0; i < len; i++) { + if (i > foldStart) { + i = foldLine.end.row + 1; + if (i >= len) + break; + foldLine = this.$foldData[foldIndex++]; + foldStart = foldLine ? foldLine.start.row : Infinity; + } + + if (cache[i] == null) + cache[i] = this.$getStringScreenWidth(lines[i])[0]; + + if (cache[i] > longestScreenLine) + longestScreenLine = cache[i]; + } + this.screenWidth = longestScreenLine; + } + }; + this.getLine = function(row) { + return this.doc.getLine(row); + }; + this.getLines = function(firstRow, lastRow) { + return this.doc.getLines(firstRow, lastRow); + }; + this.getLength = function() { + return this.doc.getLength(); + }; + this.getTextRange = function(range) { + return this.doc.getTextRange(range || this.selection.getRange()); + }; + this.insert = function(position, text) { + return this.doc.insert(position, text); + }; + this.remove = function(range) { + return this.doc.remove(range); + }; + this.removeFullLines = function(firstRow, lastRow){ + return this.doc.removeFullLines(firstRow, lastRow); + }; + this.undoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + + this.$fromUndo = true; + var lastUndoRange = null; + for (var i = deltas.length - 1; i != -1; i--) { + var delta = deltas[i]; + if (delta.group == "doc") { + this.doc.revertDeltas(delta.deltas); + lastUndoRange = + this.$getUndoSelection(delta.deltas, true, lastUndoRange); + } else { + delta.deltas.forEach(function(foldDelta) { + this.addFolds(foldDelta.folds); + }, this); + } + } + this.$fromUndo = false; + lastUndoRange && + this.$undoSelect && + !dontSelect && + this.selection.setSelectionRange(lastUndoRange); + return lastUndoRange; + }; + this.redoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + + this.$fromUndo = true; + var lastUndoRange = null; + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (delta.group == "doc") { + this.doc.applyDeltas(delta.deltas); + lastUndoRange = + this.$getUndoSelection(delta.deltas, false, lastUndoRange); + } + } + this.$fromUndo = false; + lastUndoRange && + this.$undoSelect && + !dontSelect && + this.selection.setSelectionRange(lastUndoRange); + return lastUndoRange; + }; + this.setUndoSelect = function(enable) { + this.$undoSelect = enable; + }; + + this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { + function isInsert(delta) { + return isUndo ? delta.action !== "insert" : delta.action === "insert"; + } + + var delta = deltas[0]; + var range, point; + var lastDeltaIsInsert = false; + if (isInsert(delta)) { + range = Range.fromPoints(delta.start, delta.end); + lastDeltaIsInsert = true; + } else { + range = Range.fromPoints(delta.start, delta.start); + lastDeltaIsInsert = false; + } + + for (var i = 1; i < deltas.length; i++) { + delta = deltas[i]; + if (isInsert(delta)) { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range.setStart(point); + } + point = delta.end; + if (range.compare(point.row, point.column) == 1) { + range.setEnd(point); + } + lastDeltaIsInsert = true; + } else { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range = Range.fromPoints(delta.start, delta.start); + } + lastDeltaIsInsert = false; + } + } + if (lastUndoRange != null) { + if (Range.comparePoints(lastUndoRange.start, range.start) === 0) { + lastUndoRange.start.column += range.end.column - range.start.column; + lastUndoRange.end.column += range.end.column - range.start.column; + } + + var cmp = lastUndoRange.compareRange(range); + if (cmp == 1) { + range.setStart(lastUndoRange.start); + } else if (cmp == -1) { + range.setEnd(lastUndoRange.end); + } + } + + return range; + }; + this.replace = function(range, text) { + return this.doc.replace(range, text); + }; + this.moveText = function(fromRange, toPosition, copy) { + var text = this.getTextRange(fromRange); + var folds = this.getFoldsInRange(fromRange); + + var toRange = Range.fromPoints(toPosition, toPosition); + if (!copy) { + this.remove(fromRange); + var rowDiff = fromRange.start.row - fromRange.end.row; + var collDiff = rowDiff ? -fromRange.end.column : fromRange.start.column - fromRange.end.column; + if (collDiff) { + if (toRange.start.row == fromRange.end.row && toRange.start.column > fromRange.end.column) + toRange.start.column += collDiff; + if (toRange.end.row == fromRange.end.row && toRange.end.column > fromRange.end.column) + toRange.end.column += collDiff; + } + if (rowDiff && toRange.start.row >= fromRange.end.row) { + toRange.start.row += rowDiff; + toRange.end.row += rowDiff; + } + } + + toRange.end = this.insert(toRange.start, text); + if (folds.length) { + var oldStart = fromRange.start; + var newStart = toRange.start; + var rowDiff = newStart.row - oldStart.row; + var collDiff = newStart.column - oldStart.column; + this.addFolds(folds.map(function(x) { + x = x.clone(); + if (x.start.row == oldStart.row) + x.start.column += collDiff; + if (x.end.row == oldStart.row) + x.end.column += collDiff; + x.start.row += rowDiff; + x.end.row += rowDiff; + return x; + })); + } + + return toRange; + }; + this.indentRows = function(startRow, endRow, indentString) { + indentString = indentString.replace(/\t/g, this.getTabString()); + for (var row=startRow; row<=endRow; row++) + this.doc.insertInLine({row: row, column: 0}, indentString); + }; + this.outdentRows = function (range) { + var rowRange = range.collapseRows(); + var deleteRange = new Range(0, 0, 0, 0); + var size = this.getTabSize(); + + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { + var line = this.getLine(i); + + deleteRange.start.row = i; + deleteRange.end.row = i; + for (var j = 0; j < size; ++j) + if (line.charAt(j) != ' ') + break; + if (j < size && line.charAt(j) == '\t') { + deleteRange.start.column = j; + deleteRange.end.column = j + 1; + } else { + deleteRange.start.column = 0; + deleteRange.end.column = j; + } + this.remove(deleteRange); + } + }; + + this.$moveLines = function(firstRow, lastRow, dir) { + firstRow = this.getRowFoldStart(firstRow); + lastRow = this.getRowFoldEnd(lastRow); + if (dir < 0) { + var row = this.getRowFoldStart(firstRow + dir); + if (row < 0) return 0; + var diff = row-firstRow; + } else if (dir > 0) { + var row = this.getRowFoldEnd(lastRow + dir); + if (row > this.doc.getLength()-1) return 0; + var diff = row-lastRow; + } else { + firstRow = this.$clipRowToDocument(firstRow); + lastRow = this.$clipRowToDocument(lastRow); + var diff = lastRow - firstRow + 1; + } + + var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE); + var folds = this.getFoldsInRange(range).map(function(x){ + x = x.clone(); + x.start.row += diff; + x.end.row += diff; + return x; + }); + + var lines = dir == 0 + ? this.doc.getLines(firstRow, lastRow) + : this.doc.removeFullLines(firstRow, lastRow); + this.doc.insertFullLines(firstRow+diff, lines); + folds.length && this.addFolds(folds); + return diff; + }; + this.moveLinesUp = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, -1); + }; + this.moveLinesDown = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 1); + }; + this.duplicateLines = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 0); + }; + + + this.$clipRowToDocument = function(row) { + return Math.max(0, Math.min(row, this.doc.getLength()-1)); + }; + + this.$clipColumnToRow = function(row, column) { + if (column < 0) + return 0; + return Math.min(this.doc.getLine(row).length, column); + }; + + + this.$clipPositionToDocument = function(row, column) { + column = Math.max(0, column); + + if (row < 0) { + row = 0; + column = 0; + } else { + var len = this.doc.getLength(); + if (row >= len) { + row = len - 1; + column = this.doc.getLine(len-1).length; + } else { + column = Math.min(this.doc.getLine(row).length, column); + } + } + + return { + row: row, + column: column + }; + }; + + this.$clipRangeToDocument = function(range) { + if (range.start.row < 0) { + range.start.row = 0; + range.start.column = 0; + } else { + range.start.column = this.$clipColumnToRow( + range.start.row, + range.start.column + ); + } + + var len = this.doc.getLength() - 1; + if (range.end.row > len) { + range.end.row = len; + range.end.column = this.doc.getLine(len).length; + } else { + range.end.column = this.$clipColumnToRow( + range.end.row, + range.end.column + ); + } + return range; + }; + this.$wrapLimit = 80; + this.$useWrapMode = false; + this.$wrapLimitRange = { + min : null, + max : null + }; + this.setUseWrapMode = function(useWrapMode) { + if (useWrapMode != this.$useWrapMode) { + this.$useWrapMode = useWrapMode; + this.$modified = true; + this.$resetRowCache(0); + if (useWrapMode) { + var len = this.getLength(); + this.$wrapData = Array(len); + this.$updateWrapData(0, len - 1); + } + + this._signal("changeWrapMode"); + } + }; + this.getUseWrapMode = function() { + return this.$useWrapMode; + }; + this.setWrapLimitRange = function(min, max) { + if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { + this.$wrapLimitRange = { min: min, max: max }; + this.$modified = true; + this.$bidiHandler.markAsDirty(); + if (this.$useWrapMode) + this._signal("changeWrapMode"); + } + }; + this.adjustWrapLimit = function(desiredLimit, $printMargin) { + var limits = this.$wrapLimitRange; + if (limits.max < 0) + limits = {min: $printMargin, max: $printMargin}; + var wrapLimit = this.$constrainWrapLimit(desiredLimit, limits.min, limits.max); + if (wrapLimit != this.$wrapLimit && wrapLimit > 1) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this.$resetRowCache(0); + this._signal("changeWrapLimit"); + } + return true; + } + return false; + }; + + this.$constrainWrapLimit = function(wrapLimit, min, max) { + if (min) + wrapLimit = Math.max(min, wrapLimit); + + if (max) + wrapLimit = Math.min(max, wrapLimit); + + return wrapLimit; + }; + this.getWrapLimit = function() { + return this.$wrapLimit; + }; + this.setWrapLimit = function (limit) { + this.setWrapLimitRange(limit, limit); + }; + this.getWrapLimitRange = function() { + return { + min : this.$wrapLimitRange.min, + max : this.$wrapLimitRange.max + }; + }; + + this.$updateInternalDataOnChange = function(delta) { + var useWrapMode = this.$useWrapMode; + var action = delta.action; + var start = delta.start; + var end = delta.end; + var firstRow = start.row; + var lastRow = end.row; + var len = lastRow - firstRow; + var removedFolds = null; + + this.$updating = true; + if (len != 0) { + if (action === "remove") { + this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); + + var foldLines = this.$foldData; + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + var foldLine = this.getFoldLine(end.row); + var idx = 0; + if (foldLine) { + foldLine.addRemoveChars(end.row, end.column, start.column - end.column); + foldLine.shiftRow(-len); + + var foldLineBefore = this.getFoldLine(firstRow); + if (foldLineBefore && foldLineBefore !== foldLine) { + foldLineBefore.merge(foldLine); + foldLine = foldLineBefore; + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= end.row) { + foldLine.shiftRow(-len); + } + } + + lastRow = firstRow; + } else { + var args = Array(len); + args.unshift(firstRow, 0); + var arr = useWrapMode ? this.$wrapData : this.$rowLengthCache; + arr.splice.apply(arr, args); + var foldLines = this.$foldData; + var foldLine = this.getFoldLine(firstRow); + var idx = 0; + if (foldLine) { + var cmp = foldLine.range.compareInside(start.row, start.column); + if (cmp == 0) { + foldLine = foldLine.split(start.row, start.column); + if (foldLine) { + foldLine.shiftRow(len); + foldLine.addRemoveChars(lastRow, 0, end.column - start.column); + } + } else + if (cmp == -1) { + foldLine.addRemoveChars(firstRow, 0, end.column - start.column); + foldLine.shiftRow(len); + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= firstRow) { + foldLine.shiftRow(len); + } + } + } + } else { + len = Math.abs(delta.start.column - delta.end.column); + if (action === "remove") { + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + len = -len; + } + var foldLine = this.getFoldLine(firstRow); + if (foldLine) { + foldLine.addRemoveChars(firstRow, start.column, len); + } + } + + if (useWrapMode && this.$wrapData.length != this.doc.getLength()) { + console.error("doc.getLength() and $wrapData.length have to be the same!"); + } + this.$updating = false; + + if (useWrapMode) + this.$updateWrapData(firstRow, lastRow); + else + this.$updateRowLengthCache(firstRow, lastRow); + + return removedFolds; + }; + + this.$updateRowLengthCache = function(firstRow, lastRow, b) { + this.$rowLengthCache[firstRow] = null; + this.$rowLengthCache[lastRow] = null; + }; + + this.$updateWrapData = function(firstRow, lastRow) { + var lines = this.doc.getAllLines(); + var tabSize = this.getTabSize(); + var wrapData = this.$wrapData; + var wrapLimit = this.$wrapLimit; + var tokens; + var foldLine; + + var row = firstRow; + lastRow = Math.min(lastRow, lines.length - 1); + while (row <= lastRow) { + foldLine = this.getFoldLine(row, foldLine); + if (!foldLine) { + tokens = this.$getDisplayTokens(lines[row]); + wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row ++; + } else { + tokens = []; + foldLine.walk(function(placeholder, row, column, lastColumn) { + var walkTokens; + if (placeholder != null) { + walkTokens = this.$getDisplayTokens( + placeholder, tokens.length); + walkTokens[0] = PLACEHOLDER_START; + for (var i = 1; i < walkTokens.length; i++) { + walkTokens[i] = PLACEHOLDER_BODY; + } + } else { + walkTokens = this.$getDisplayTokens( + lines[row].substring(lastColumn, column), + tokens.length); + } + tokens = tokens.concat(walkTokens); + }.bind(this), + foldLine.end.row, + lines[foldLine.end.row].length + 1 + ); + + wrapData[foldLine.start.row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row = foldLine.end.row + 1; + } + } + }; + var CHAR = 1, + CHAR_EXT = 2, + PLACEHOLDER_START = 3, + PLACEHOLDER_BODY = 4, + PUNCTUATION = 9, + SPACE = 10, + TAB = 11, + TAB_SPACE = 12; + + + this.$computeWrapSplits = function(tokens, wrapLimit, tabSize) { + if (tokens.length == 0) { + return []; + } + + var splits = []; + var displayLength = tokens.length; + var lastSplit = 0, lastDocSplit = 0; + + var isCode = this.$wrapAsCode; + + var indentedSoftWrap = this.$indentedSoftWrap; + var maxIndent = wrapLimit <= Math.max(2 * tabSize, 8) + || indentedSoftWrap === false ? 0 : Math.floor(wrapLimit / 2); + + function getWrapIndent() { + var indentation = 0; + if (maxIndent === 0) + return indentation; + if (indentedSoftWrap) { + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token == SPACE) + indentation += 1; + else if (token == TAB) + indentation += tabSize; + else if (token == TAB_SPACE) + continue; + else + break; + } + } + if (isCode && indentedSoftWrap !== false) + indentation += tabSize; + return Math.min(indentation, maxIndent); + } + function addSplit(screenPos) { + var displayed = tokens.slice(lastSplit, screenPos); + var len = displayed.length; + displayed.join("") + .replace(/12/g, function() { + len -= 1; + }) + .replace(/2/g, function() { + len -= 1; + }); + + if (!splits.length) { + indent = getWrapIndent(); + splits.indent = indent; + } + lastDocSplit += len; + splits.push(lastDocSplit); + lastSplit = screenPos; + } + var indent = 0; + while (displayLength - lastSplit > wrapLimit - indent) { + var split = lastSplit + wrapLimit - indent; + if (tokens[split - 1] >= SPACE && tokens[split] >= SPACE) { + addSplit(split); + continue; + } + if (tokens[split] == PLACEHOLDER_START || tokens[split] == PLACEHOLDER_BODY) { + for (split; split != lastSplit - 1; split--) { + if (tokens[split] == PLACEHOLDER_START) { + break; + } + } + if (split > lastSplit) { + addSplit(split); + continue; + } + split = lastSplit + wrapLimit; + for (split; split < tokens.length; split++) { + if (tokens[split] != PLACEHOLDER_BODY) { + break; + } + } + if (split == tokens.length) { + break; // Breaks the while-loop. + } + addSplit(split); + continue; + } + var minSplit = Math.max(split - (wrapLimit -(wrapLimit>>2)), lastSplit - 1); + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split --; + } + if (isCode) { + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split --; + } + while (split > minSplit && tokens[split] == PUNCTUATION) { + split --; + } + } else { + while (split > minSplit && tokens[split] < SPACE) { + split --; + } + } + if (split > minSplit) { + addSplit(++split); + continue; + } + split = lastSplit + wrapLimit; + if (tokens[split] == CHAR_EXT) + split--; + addSplit(split - indent); + } + return splits; + }; + this.$getDisplayTokens = function(str, offset) { + var arr = []; + var tabSize; + offset = offset || 0; + + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c == 9) { + tabSize = this.getScreenTabSize(arr.length + offset); + arr.push(TAB); + for (var n = 1; n < tabSize; n++) { + arr.push(TAB_SPACE); + } + } + else if (c == 32) { + arr.push(SPACE); + } else if((c > 39 && c < 48) || (c > 57 && c < 64)) { + arr.push(PUNCTUATION); + } + else if (c >= 0x1100 && isFullWidth(c)) { + arr.push(CHAR, CHAR_EXT); + } else { + arr.push(CHAR); + } + } + return arr; + }; + this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn == 0) + return [0, 0]; + if (maxScreenColumn == null) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charCodeAt(column); + if (c == 9) { + screenColumn += this.getScreenTabSize(screenColumn); + } + else if (c >= 0x1100 && isFullWidth(c)) { + screenColumn += 2; + } else { + screenColumn += 1; + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + + this.lineWidgets = null; + this.getRowLength = function(row) { + if (this.lineWidgets) + var h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + else + h = 0; + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1 + h; + } else { + return this.$wrapData[row].length + 1 + h; + } + }; + this.getRowLineCount = function(row) { + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1; + } else { + return this.$wrapData[row].length + 1; + } + }; + + this.getRowWrapIndent = function(screenRow) { + if (this.$useWrapMode) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + var splits = this.$wrapData[pos.row]; + return splits.length && splits[0] < pos.column ? splits.indent : 0; + } else { + return 0; + } + }; + this.getScreenLastRowColumn = function(screenRow) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + return this.documentToScreenColumn(pos.row, pos.column); + }; + this.getDocumentLastRowColumn = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.getScreenLastRowColumn(screenRow); + }; + this.getDocumentLastRowColumnPosition = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10); + }; + this.getRowSplitData = function(row) { + if (!this.$useWrapMode) { + return undefined; + } else { + return this.$wrapData[row]; + } + }; + this.getScreenTabSize = function(screenColumn) { + return this.$tabSize - screenColumn % this.$tabSize; + }; + + + this.screenToDocumentRow = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).row; + }; + + + this.screenToDocumentColumn = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).column; + }; + this.screenToDocumentPosition = function(screenRow, screenColumn, offsetX) { + if (screenRow < 0) + return {row: 0, column: 0}; + + var line; + var docRow = 0; + var docColumn = 0; + var column; + var row = 0; + var rowLength = 0; + + var rowCache = this.$screenRowCache; + var i = this.$getRowCacheIndex(rowCache, screenRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var docRow = this.$docRowCache[i]; + var doCache = screenRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var maxRow = this.getLength() - 1; + var foldLine = this.getNextFoldLine(docRow); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (row <= screenRow) { + rowLength = this.getRowLength(docRow); + if (row + rowLength > screenRow || docRow >= maxRow) { + break; + } else { + row += rowLength; + docRow++; + if (docRow > foldStart) { + docRow = foldLine.end.row+1; + foldLine = this.getNextFoldLine(docRow, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + } + + if (doCache) { + this.$docRowCache.push(docRow); + this.$screenRowCache.push(row); + } + } + + if (foldLine && foldLine.start.row <= docRow) { + line = this.getFoldDisplayLine(foldLine); + docRow = foldLine.start.row; + } else if (row + rowLength <= screenRow || docRow > maxRow) { + return { + row: maxRow, + column: this.getLine(maxRow).length + }; + } else { + line = this.getLine(docRow); + foldLine = null; + } + var wrapIndent = 0, splitIndex = Math.floor(screenRow - row); + if (this.$useWrapMode) { + var splits = this.$wrapData[docRow]; + if (splits) { + column = splits[splitIndex]; + if(splitIndex > 0 && splits.length) { + wrapIndent = splits.indent; + docColumn = splits[splitIndex - 1] || splits[splits.length - 1]; + line = line.substring(docColumn); + } + } + } + + if (offsetX !== undefined && this.$bidiHandler.isBidiRow(row + splitIndex, docRow, splitIndex)) + screenColumn = this.$bidiHandler.offsetToCol(offsetX); + + docColumn += this.$getStringScreenWidth(line, screenColumn - wrapIndent)[1]; + if (this.$useWrapMode && docColumn >= column) + docColumn = column - 1; + + if (foldLine) + return foldLine.idxToPosition(docColumn); + + return {row: docRow, column: docColumn}; + }; + this.documentToScreenPosition = function(docRow, docColumn) { + if (typeof docColumn === "undefined") + var pos = this.$clipPositionToDocument(docRow.row, docRow.column); + else + pos = this.$clipPositionToDocument(docRow, docColumn); + + docRow = pos.row; + docColumn = pos.column; + + var screenRow = 0; + var foldStartRow = null; + var fold = null; + fold = this.getFoldAt(docRow, docColumn, 1); + if (fold) { + docRow = fold.start.row; + docColumn = fold.start.column; + } + + var rowEnd, row = 0; + + + var rowCache = this.$docRowCache; + var i = this.$getRowCacheIndex(rowCache, docRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var screenRow = this.$screenRowCache[i]; + var doCache = docRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var foldLine = this.getNextFoldLine(row); + var foldStart = foldLine ?foldLine.start.row :Infinity; + + while (row < docRow) { + if (row >= foldStart) { + rowEnd = foldLine.end.row + 1; + if (rowEnd > docRow) + break; + foldLine = this.getNextFoldLine(rowEnd, foldLine); + foldStart = foldLine ?foldLine.start.row :Infinity; + } + else { + rowEnd = row + 1; + } + + screenRow += this.getRowLength(row); + row = rowEnd; + + if (doCache) { + this.$docRowCache.push(row); + this.$screenRowCache.push(screenRow); + } + } + var textLine = ""; + if (foldLine && row >= foldStart) { + textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn); + foldStartRow = foldLine.start.row; + } else { + textLine = this.getLine(docRow).substring(0, docColumn); + foldStartRow = docRow; + } + var wrapIndent = 0; + if (this.$useWrapMode) { + var wrapRow = this.$wrapData[foldStartRow]; + if (wrapRow) { + var screenRowOffset = 0; + while (textLine.length >= wrapRow[screenRowOffset]) { + screenRow ++; + screenRowOffset++; + } + textLine = textLine.substring( + wrapRow[screenRowOffset - 1] || 0, textLine.length + ); + wrapIndent = screenRowOffset > 0 ? wrapRow.indent : 0; + } + } + + return { + row: screenRow, + column: wrapIndent + this.$getStringScreenWidth(textLine)[0] + }; + }; + this.documentToScreenColumn = function(row, docColumn) { + return this.documentToScreenPosition(row, docColumn).column; + }; + this.documentToScreenRow = function(docRow, docColumn) { + return this.documentToScreenPosition(docRow, docColumn).row; + }; + this.getScreenLength = function() { + var screenRows = 0; + var fold = null; + if (!this.$useWrapMode) { + screenRows = this.getLength(); + var foldData = this.$foldData; + for (var i = 0; i < foldData.length; i++) { + fold = foldData[i]; + screenRows -= fold.end.row - fold.start.row; + } + } else { + var lastRow = this.$wrapData.length; + var row = 0, i = 0; + var fold = this.$foldData[i++]; + var foldStart = fold ? fold.start.row :Infinity; + + while (row < lastRow) { + var splits = this.$wrapData[row]; + screenRows += splits ? splits.length + 1 : 1; + row ++; + if (row > foldStart) { + row = fold.end.row+1; + fold = this.$foldData[i++]; + foldStart = fold ?fold.start.row :Infinity; + } + } + } + if (this.lineWidgets) + screenRows += this.$getWidgetScreenLength(); + + return screenRows; + }; + this.$setFontMetrics = function(fm) { + if (!this.$enableVarChar) return; + this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn === 0) + return [0, 0]; + if (!maxScreenColumn) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charAt(column); + if (c === "\t") { + screenColumn += this.getScreenTabSize(screenColumn); + } else { + screenColumn += fm.getCharacterWidth(c); + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + }; + + this.destroy = function() { + if (this.bgTokenizer) { + this.bgTokenizer.setDocument(null); + this.bgTokenizer = null; + } + this.$stopWorker(); + }; + + this.isFullWidth = isFullWidth; + function isFullWidth(c) { + if (c < 0x1100) + return false; + return c >= 0x1100 && c <= 0x115F || + c >= 0x11A3 && c <= 0x11A7 || + c >= 0x11FA && c <= 0x11FF || + c >= 0x2329 && c <= 0x232A || + c >= 0x2E80 && c <= 0x2E99 || + c >= 0x2E9B && c <= 0x2EF3 || + c >= 0x2F00 && c <= 0x2FD5 || + c >= 0x2FF0 && c <= 0x2FFB || + c >= 0x3000 && c <= 0x303E || + c >= 0x3041 && c <= 0x3096 || + c >= 0x3099 && c <= 0x30FF || + c >= 0x3105 && c <= 0x312D || + c >= 0x3131 && c <= 0x318E || + c >= 0x3190 && c <= 0x31BA || + c >= 0x31C0 && c <= 0x31E3 || + c >= 0x31F0 && c <= 0x321E || + c >= 0x3220 && c <= 0x3247 || + c >= 0x3250 && c <= 0x32FE || + c >= 0x3300 && c <= 0x4DBF || + c >= 0x4E00 && c <= 0xA48C || + c >= 0xA490 && c <= 0xA4C6 || + c >= 0xA960 && c <= 0xA97C || + c >= 0xAC00 && c <= 0xD7A3 || + c >= 0xD7B0 && c <= 0xD7C6 || + c >= 0xD7CB && c <= 0xD7FB || + c >= 0xF900 && c <= 0xFAFF || + c >= 0xFE10 && c <= 0xFE19 || + c >= 0xFE30 && c <= 0xFE52 || + c >= 0xFE54 && c <= 0xFE66 || + c >= 0xFE68 && c <= 0xFE6B || + c >= 0xFF01 && c <= 0xFF60 || + c >= 0xFFE0 && c <= 0xFFE6; + } + + }).call(EditSession.prototype); + + acequire("./edit_session/folding").Folding.call(EditSession.prototype); + acequire("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype); + + + config.defineOptions(EditSession.prototype, "session", { + wrap: { + set: function(value) { + if (!value || value == "off") + value = false; + else if (value == "free") + value = true; + else if (value == "printMargin") + value = -1; + else if (typeof value == "string") + value = parseInt(value, 10) || false; + + if (this.$wrap == value) + return; + this.$wrap = value; + if (!value) { + this.setUseWrapMode(false); + } else { + var col = typeof value == "number" ? value : null; + this.setWrapLimitRange(col, col); + this.setUseWrapMode(true); + } + }, + get: function() { + if (this.getUseWrapMode()) { + if (this.$wrap == -1) + return "printMargin"; + if (!this.getWrapLimitRange().min) + return "free"; + return this.$wrap; + } + return "off"; + }, + handlesSet: true + }, + wrapMethod: { + set: function(val) { + val = val == "auto" + ? this.$mode.type != "text" + : val != "text"; + if (val != this.$wrapAsCode) { + this.$wrapAsCode = val; + if (this.$useWrapMode) { + this.$modified = true; + this.$resetRowCache(0); + this.$updateWrapData(0, this.getLength() - 1); + } + } + }, + initialValue: "auto" + }, + indentedSoftWrap: { initialValue: true }, + firstLineNumber: { + set: function() {this._signal("changeBreakpoint");}, + initialValue: 1 + }, + useWorker: { + set: function(useWorker) { + this.$useWorker = useWorker; + + this.$stopWorker(); + if (useWorker) + this.$startWorker(); + }, + initialValue: true + }, + useSoftTabs: {initialValue: true}, + tabSize: { + set: function(tabSize) { + if (isNaN(tabSize) || this.$tabSize === tabSize) return; + + this.$modified = true; + this.$rowLengthCache = []; + this.$tabSize = tabSize; + this._signal("changeTabSize"); + }, + initialValue: 4, + handlesSet: true + }, + navigateWithinSoftTabs: {initialValue: false}, + overwrite: { + set: function(val) {this._signal("changeOverwrite");}, + initialValue: false + }, + newLineMode: { + set: function(val) {this.doc.setNewLineMode(val);}, + get: function() {return this.doc.getNewLineMode();}, + handlesSet: true + }, + mode: { + set: function(val) { this.setMode(val); }, + get: function() { return this.$modeId; } + } + }); + + exports.EditSession = EditSession; + }); + + ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"], function(acequire, exports, module) { + "use strict"; + + var lang = acequire("./lib/lang"); + var oop = acequire("./lib/oop"); + var Range = acequire("./range").Range; + + var Search = function() { + this.$options = {}; + }; + + (function() { + this.set = function(options) { + oop.mixin(this.$options, options); + return this; + }; + this.getOptions = function() { + return lang.copyObject(this.$options); + }; + this.setOptions = function(options) { + this.$options = options; + }; + this.find = function(session) { + var options = this.$options; + var iterator = this.$matchIterator(session, options); + if (!iterator) + return false; + + var firstRange = null; + iterator.forEach(function(sr, sc, er, ec) { + firstRange = new Range(sr, sc, er, ec); + if (sc == ec && options.start && options.start.start + && options.skipCurrent != false && firstRange.isEqual(options.start) + ) { + firstRange = null; + return false; + } + + return true; + }); + + return firstRange; + }; + this.findAll = function(session) { + var options = this.$options; + if (!options.needle) + return []; + this.$assembleRegExp(options); + + var range = options.range; + var lines = range + ? session.getLines(range.start.row, range.end.row) + : session.doc.getAllLines(); + + var ranges = []; + var re = options.re; + if (options.$isMultiLine) { + var len = re.length; + var maxRow = lines.length - len; + var prevRange; + outer: for (var row = re.offset || 0; row <= maxRow; row++) { + for (var j = 0; j < len; j++) + if (lines[row + j].search(re[j]) == -1) + continue outer; + + var startLine = lines[row]; + var line = lines[row + len - 1]; + var startIndex = startLine.length - startLine.match(re[0])[0].length; + var endIndex = line.match(re[len - 1])[0].length; + + if (prevRange && prevRange.end.row === row && + prevRange.end.column > startIndex + ) { + continue; + } + ranges.push(prevRange = new Range( + row, startIndex, row + len - 1, endIndex + )); + if (len > 2) + row = row + len - 2; + } + } else { + for (var i = 0; i < lines.length; i++) { + var matches = lang.getMatchOffsets(lines[i], re); + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + ranges.push(new Range(i, match.offset, i, match.offset + match.length)); + } + } + } + + if (range) { + var startColumn = range.start.column; + var endColumn = range.start.column; + var i = 0, j = ranges.length - 1; + while (i < j && ranges[i].start.column < startColumn && ranges[i].start.row == range.start.row) + i++; + + while (i < j && ranges[j].end.column > endColumn && ranges[j].end.row == range.end.row) + j--; + + ranges = ranges.slice(i, j + 1); + for (i = 0, j = ranges.length; i < j; i++) { + ranges[i].start.row += range.start.row; + ranges[i].end.row += range.start.row; + } + } + + return ranges; + }; + this.replace = function(input, replacement) { + var options = this.$options; + + var re = this.$assembleRegExp(options); + if (options.$isMultiLine) + return replacement; + + if (!re) + return; + + var match = re.exec(input); + if (!match || match[0].length != input.length) + return null; + + replacement = input.replace(re, replacement); + if (options.preserveCase) { + replacement = replacement.split(""); + for (var i = Math.min(input.length, input.length); i--; ) { + var ch = input[i]; + if (ch && ch.toLowerCase() != ch) + replacement[i] = replacement[i].toUpperCase(); + else + replacement[i] = replacement[i].toLowerCase(); + } + replacement = replacement.join(""); + } + + return replacement; + }; + + this.$assembleRegExp = function(options, $disableFakeMultiline) { + if (options.needle instanceof RegExp) + return options.re = options.needle; + + var needle = options.needle; + + if (!options.needle) + return options.re = false; + + if (!options.regExp) + needle = lang.escapeRegExp(needle); + + if (options.wholeWord) + needle = addWordBoundary(needle, options); + + var modifier = options.caseSensitive ? "gm" : "gmi"; + + options.$isMultiLine = !$disableFakeMultiline && /[\n\r]/.test(needle); + if (options.$isMultiLine) + return options.re = this.$assembleMultilineRegExp(needle, modifier); + + try { + var re = new RegExp(needle, modifier); + } catch(e) { + re = false; + } + return options.re = re; + }; + + this.$assembleMultilineRegExp = function(needle, modifier) { + var parts = needle.replace(/\r\n|\r|\n/g, "$\n^").split("\n"); + var re = []; + for (var i = 0; i < parts.length; i++) try { + re.push(new RegExp(parts[i], modifier)); + } catch(e) { + return false; + } + return re; + }; + + this.$matchIterator = function(session, options) { + var re = this.$assembleRegExp(options); + if (!re) + return false; + var backwards = options.backwards == true; + var skipCurrent = options.skipCurrent != false; + + var range = options.range; + var start = options.start; + if (!start) + start = range ? range[backwards ? "end" : "start"] : session.selection.getRange(); + + if (start.start) + start = start[skipCurrent != backwards ? "end" : "start"]; + + var firstRow = range ? range.start.row : 0; + var lastRow = range ? range.end.row : session.getLength() - 1; + + if (backwards) { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row--; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + if (options.wrap == false) + return; + for (row = lastRow, firstRow = start.row; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + }; + } + else { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row = row + 1; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + if (options.wrap == false) + return; + for (row = firstRow, lastRow = start.row; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + }; + } + + if (options.$isMultiLine) { + var len = re.length; + var forEachInLine = function(row, offset, callback) { + var startRow = backwards ? row - len + 1 : row; + if (startRow < 0) return; + var line = session.getLine(startRow); + var startIndex = line.search(re[0]); + if (!backwards && startIndex < offset || startIndex === -1) return; + for (var i = 1; i < len; i++) { + line = session.getLine(startRow + i); + if (line.search(re[i]) == -1) + return; + } + var endIndex = line.match(re[len - 1])[0].length; + if (backwards && endIndex > offset) return; + if (callback(startRow, startIndex, startRow + len - 1, endIndex)) + return true; + }; + } + else if (backwards) { + var forEachInLine = function(row, endIndex, callback) { + var line = session.getLine(row); + var matches = []; + var m, last = 0; + re.lastIndex = 0; + while((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (!length) { + if (last >= line.length) break; + re.lastIndex = last += 1; + } + if (m.index + length > endIndex) + break; + matches.push(m.index, length); + } + for (var i = matches.length - 1; i >= 0; i -= 2) { + var column = matches[i - 1]; + var length = matches[i]; + if (callback(row, column, row, column + length)) + return true; + } + }; + } + else { + var forEachInLine = function(row, startIndex, callback) { + var line = session.getLine(row); + var m; + var last = startIndex; + re.lastIndex = startIndex; + while((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (callback(row, last, row,last + length)) + return true; + if (!length) { + re.lastIndex = last += 1; + if (last >= line.length) return false; + } + } + }; + } + return {forEach: forEach}; + }; + + }).call(Search.prototype); + + function addWordBoundary(needle, options) { + function wordBoundary(c) { + if (/\w/.test(c) || options.regExp) return "\\b"; + return ""; + } + return wordBoundary(needle[0]) + needle + + wordBoundary(needle[needle.length - 1]); + } + + exports.Search = Search; + }); + + ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"], function(acequire, exports, module) { + "use strict"; + + var keyUtil = acequire("../lib/keys"); + var useragent = acequire("../lib/useragent"); + var KEY_MODS = keyUtil.KEY_MODS; + + function HashHandler(config, platform) { + this.platform = platform || (useragent.isMac ? "mac" : "win"); + this.commands = {}; + this.commandKeyBinding = {}; + this.addCommands(config); + this.$singleCommand = true; + } + + function MultiHashHandler(config, platform) { + HashHandler.call(this, config, platform); + this.$singleCommand = false; + } + + MultiHashHandler.prototype = HashHandler.prototype; + + (function() { + + + this.addCommand = function(command) { + if (this.commands[command.name]) + this.removeCommand(command); + + this.commands[command.name] = command; + + if (command.bindKey) + this._buildKeyHash(command); + }; + + this.removeCommand = function(command, keepCommand) { + var name = command && (typeof command === 'string' ? command : command.name); + command = this.commands[name]; + if (!keepCommand) + delete this.commands[name]; + var ckb = this.commandKeyBinding; + for (var keyId in ckb) { + var cmdGroup = ckb[keyId]; + if (cmdGroup == command) { + delete ckb[keyId]; + } else if (Array.isArray(cmdGroup)) { + var i = cmdGroup.indexOf(command); + if (i != -1) { + cmdGroup.splice(i, 1); + if (cmdGroup.length == 1) + ckb[keyId] = cmdGroup[0]; + } + } + } + }; + + this.bindKey = function(key, command, position) { + if (typeof key == "object" && key) { + if (position == undefined) + position = key.position; + key = key[this.platform]; + } + if (!key) + return; + if (typeof command == "function") + return this.addCommand({exec: command, bindKey: key, name: command.name || key}); + + key.split("|").forEach(function(keyPart) { + var chain = ""; + if (keyPart.indexOf(" ") != -1) { + var parts = keyPart.split(/\s+/); + keyPart = parts.pop(); + parts.forEach(function(keyPart) { + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + chain += (chain ? " " : "") + id; + this._addCommandToBinding(chain, "chainKeys"); + }, this); + chain += " "; + } + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + this._addCommandToBinding(chain + id, command, position); + }, this); + }; + + function getPosition(command) { + return typeof command == "object" && command.bindKey + && command.bindKey.position + || (command.isDefault ? -100 : 0); + } + this._addCommandToBinding = function(keyId, command, position) { + var ckb = this.commandKeyBinding, i; + if (!command) { + delete ckb[keyId]; + } else if (!ckb[keyId] || this.$singleCommand) { + ckb[keyId] = command; + } else { + if (!Array.isArray(ckb[keyId])) { + ckb[keyId] = [ckb[keyId]]; + } else if ((i = ckb[keyId].indexOf(command)) != -1) { + ckb[keyId].splice(i, 1); + } + + if (typeof position != "number") { + position = getPosition(command); + } + + var commands = ckb[keyId]; + for (i = 0; i < commands.length; i++) { + var other = commands[i]; + var otherPos = getPosition(other); + if (otherPos > position) + break; + } + commands.splice(i, 0, command); + } + }; + + this.addCommands = function(commands) { + commands && Object.keys(commands).forEach(function(name) { + var command = commands[name]; + if (!command) + return; + + if (typeof command === "string") + return this.bindKey(command, name); + + if (typeof command === "function") + command = { exec: command }; + + if (typeof command !== "object") + return; + + if (!command.name) + command.name = name; + + this.addCommand(command); + }, this); + }; + + this.removeCommands = function(commands) { + Object.keys(commands).forEach(function(name) { + this.removeCommand(commands[name]); + }, this); + }; + + this.bindKeys = function(keyList) { + Object.keys(keyList).forEach(function(key) { + this.bindKey(key, keyList[key]); + }, this); + }; + + this._buildKeyHash = function(command) { + this.bindKey(command.bindKey, command); + }; + this.parseKeys = function(keys) { + var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x){return x;}); + var key = parts.pop(); + + var keyCode = keyUtil[key]; + if (keyUtil.FUNCTION_KEYS[keyCode]) + key = keyUtil.FUNCTION_KEYS[keyCode].toLowerCase(); + else if (!parts.length) + return {key: key, hashId: -1}; + else if (parts.length == 1 && parts[0] == "shift") + return {key: key.toUpperCase(), hashId: -1}; + + var hashId = 0; + for (var i = parts.length; i--;) { + var modifier = keyUtil.KEY_MODS[parts[i]]; + if (modifier == null) { + if (typeof console != "undefined") + console.error("invalid modifier " + parts[i] + " in " + keys); + return false; + } + hashId |= modifier; + } + return {key: key, hashId: hashId}; + }; + + this.findKeyCommand = function findKeyCommand(hashId, keyString) { + var key = KEY_MODS[hashId] + keyString; + return this.commandKeyBinding[key]; + }; + + this.handleKeyboard = function(data, hashId, keyString, keyCode) { + if (keyCode < 0) return; + var key = KEY_MODS[hashId] + keyString; + var command = this.commandKeyBinding[key]; + if (data.$keyChain) { + data.$keyChain += " " + key; + command = this.commandKeyBinding[data.$keyChain] || command; + } + + if (command) { + if (command == "chainKeys" || command[command.length - 1] == "chainKeys") { + data.$keyChain = data.$keyChain || key; + return {command: "null"}; + } + } + + if (data.$keyChain) { + if ((!hashId || hashId == 4) && keyString.length == 1) + data.$keyChain = data.$keyChain.slice(0, -key.length - 1); // wait for input + else if (hashId == -1 || keyCode > 0) + data.$keyChain = ""; // reset keyChain + } + return {command: command}; + }; + + this.getStatusText = function(editor, data) { + return data.$keyChain || ""; + }; + + }).call(HashHandler.prototype); + + exports.HashHandler = HashHandler; + exports.MultiHashHandler = MultiHashHandler; + }); + + ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../lib/oop"); + var MultiHashHandler = acequire("../keyboard/hash_handler").MultiHashHandler; + var EventEmitter = acequire("../lib/event_emitter").EventEmitter; + + var CommandManager = function(platform, commands) { + MultiHashHandler.call(this, commands, platform); + this.byName = this.commands; + this.setDefaultHandler("exec", function(e) { + return e.command.exec(e.editor, e.args || {}); + }); + }; + + oop.inherits(CommandManager, MultiHashHandler); + + (function() { + + oop.implement(this, EventEmitter); + + this.exec = function(command, editor, args) { + if (Array.isArray(command)) { + for (var i = command.length; i--; ) { + if (this.exec(command[i], editor, args)) return true; + } + return false; + } + + if (typeof command === "string") + command = this.commands[command]; + + if (!command) + return false; + + if (editor && editor.$readOnly && !command.readOnly) + return false; + + if (command.isAvailable && !command.isAvailable(editor)) + return false; + + var e = {editor: editor, command: command, args: args}; + e.returnValue = this._emit("exec", e); + this._signal("afterExec", e); + + return e.returnValue === false ? false : true; + }; + + this.toggleRecording = function(editor) { + if (this.$inReplay) + return; + + editor && editor._emit("changeStatus"); + if (this.recording) { + this.macro.pop(); + this.removeEventListener("exec", this.$addCommandToMacro); + + if (!this.macro.length) + this.macro = this.oldMacro; + + return this.recording = false; + } + if (!this.$addCommandToMacro) { + this.$addCommandToMacro = function(e) { + this.macro.push([e.command, e.args]); + }.bind(this); + } + + this.oldMacro = this.macro; + this.macro = []; + this.on("exec", this.$addCommandToMacro); + return this.recording = true; + }; + + this.replay = function(editor) { + if (this.$inReplay || !this.macro) + return; + + if (this.recording) + return this.toggleRecording(editor); + + try { + this.$inReplay = true; + this.macro.forEach(function(x) { + if (typeof x == "string") + this.exec(x, editor); + else + this.exec(x[0], editor, x[1]); + }, this); + } finally { + this.$inReplay = false; + } + }; + + this.trimMacro = function(m) { + return m.map(function(x){ + if (typeof x[0] != "string") + x[0] = x[0].name; + if (!x[1]) + x = x[0]; + return x; + }); + }; + + }).call(CommandManager.prototype); + + exports.CommandManager = CommandManager; + + }); + + ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"], function(acequire, exports, module) { + "use strict"; + + var lang = acequire("../lib/lang"); + var config = acequire("../config"); + var Range = acequire("../range").Range; + + function bindKey(win, mac) { + return {win: win, mac: mac}; + } + exports.commands = [{ + name: "showSettingsMenu", + bindKey: bindKey("Ctrl-,", "Command-,"), + exec: function(editor) { + config.loadModule("ace/ext/settings_menu", function(module) { + module.init(editor); + editor.showSettingsMenu(); + }); + }, + readOnly: true + }, { + name: "goToNextError", + bindKey: bindKey("Alt-E", "F4"), + exec: function(editor) { + config.loadModule("ace/ext/error_marker", function(module) { + module.showErrorMarker(editor, 1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, { + name: "goToPreviousError", + bindKey: bindKey("Alt-Shift-E", "Shift-F4"), + exec: function(editor) { + config.loadModule("ace/ext/error_marker", function(module) { + module.showErrorMarker(editor, -1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, { + name: "selectall", + bindKey: bindKey("Ctrl-A", "Command-A"), + exec: function(editor) { editor.selectAll(); }, + readOnly: true + }, { + name: "centerselection", + bindKey: bindKey(null, "Ctrl-L"), + exec: function(editor) { editor.centerSelection(); }, + readOnly: true + }, { + name: "gotoline", + bindKey: bindKey("Ctrl-L", "Command-L"), + exec: function(editor) { + var line = parseInt(prompt("Enter line number:"), 10); + if (!isNaN(line)) { + editor.gotoLine(line); + } + }, + readOnly: true + }, { + name: "fold", + bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"), + exec: function(editor) { editor.session.toggleFold(false); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "unfold", + bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"), + exec: function(editor) { editor.session.toggleFold(true); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "toggleFoldWidget", + bindKey: bindKey("F2", "F2"), + exec: function(editor) { editor.session.toggleFoldWidget(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "toggleParentFoldWidget", + bindKey: bindKey("Alt-F2", "Alt-F2"), + exec: function(editor) { editor.session.toggleFoldWidget(true); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "foldall", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { editor.session.foldAll(); }, + scrollIntoView: "center", + readOnly: true + }, { + name: "foldOther", + bindKey: bindKey("Alt-0", "Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + editor.session.unfold(editor.selection.getAllRanges()); + }, + scrollIntoView: "center", + readOnly: true + }, { + name: "unfoldall", + bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"), + exec: function(editor) { editor.session.unfold(); }, + scrollIntoView: "center", + readOnly: true + }, { + name: "findnext", + bindKey: bindKey("Ctrl-K", "Command-G"), + exec: function(editor) { editor.findNext(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "findprevious", + bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"), + exec: function(editor) { editor.findPrevious(); }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "selectOrFindNext", + bindKey: bindKey("Alt-K", "Ctrl-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findNext(); + }, + readOnly: true + }, { + name: "selectOrFindPrevious", + bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findPrevious(); + }, + readOnly: true + }, { + name: "find", + bindKey: bindKey("Ctrl-F", "Command-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor);}); + }, + readOnly: true + }, { + name: "overwrite", + bindKey: "Insert", + exec: function(editor) { editor.toggleOverwrite(); }, + readOnly: true + }, { + name: "selecttostart", + bindKey: bindKey("Ctrl-Shift-Home", "Command-Shift-Home|Command-Shift-Up"), + exec: function(editor) { editor.getSelection().selectFileStart(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "gotostart", + bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"), + exec: function(editor) { editor.navigateFileStart(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "selectup", + bindKey: bindKey("Shift-Up", "Shift-Up|Ctrl-Shift-P"), + exec: function(editor) { editor.getSelection().selectUp(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "golineup", + bindKey: bindKey("Up", "Up|Ctrl-P"), + exec: function(editor, args) { editor.navigateUp(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttoend", + bindKey: bindKey("Ctrl-Shift-End", "Command-Shift-End|Command-Shift-Down"), + exec: function(editor) { editor.getSelection().selectFileEnd(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "gotoend", + bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"), + exec: function(editor) { editor.navigateFileEnd(); }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "selectdown", + bindKey: bindKey("Shift-Down", "Shift-Down|Ctrl-Shift-N"), + exec: function(editor) { editor.getSelection().selectDown(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "golinedown", + bindKey: bindKey("Down", "Down|Ctrl-N"), + exec: function(editor, args) { editor.navigateDown(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectwordleft", + bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"), + exec: function(editor) { editor.getSelection().selectWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotowordleft", + bindKey: bindKey("Ctrl-Left", "Option-Left"), + exec: function(editor) { editor.navigateWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttolinestart", + bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left|Ctrl-Shift-A"), + exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotolinestart", + bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"), + exec: function(editor) { editor.navigateLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectleft", + bindKey: bindKey("Shift-Left", "Shift-Left|Ctrl-Shift-B"), + exec: function(editor) { editor.getSelection().selectLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotoleft", + bindKey: bindKey("Left", "Left|Ctrl-B"), + exec: function(editor, args) { editor.navigateLeft(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectwordright", + bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"), + exec: function(editor) { editor.getSelection().selectWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotowordright", + bindKey: bindKey("Ctrl-Right", "Option-Right"), + exec: function(editor) { editor.navigateWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttolineend", + bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right|Shift-End|Ctrl-Shift-E"), + exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotolineend", + bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"), + exec: function(editor) { editor.navigateLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectright", + bindKey: bindKey("Shift-Right", "Shift-Right"), + exec: function(editor) { editor.getSelection().selectRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotoright", + bindKey: bindKey("Right", "Right|Ctrl-F"), + exec: function(editor, args) { editor.navigateRight(args.times); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectpagedown", + bindKey: "Shift-PageDown", + exec: function(editor) { editor.selectPageDown(); }, + readOnly: true + }, { + name: "pagedown", + bindKey: bindKey(null, "Option-PageDown"), + exec: function(editor) { editor.scrollPageDown(); }, + readOnly: true + }, { + name: "gotopagedown", + bindKey: bindKey("PageDown", "PageDown|Ctrl-V"), + exec: function(editor) { editor.gotoPageDown(); }, + readOnly: true + }, { + name: "selectpageup", + bindKey: "Shift-PageUp", + exec: function(editor) { editor.selectPageUp(); }, + readOnly: true + }, { + name: "pageup", + bindKey: bindKey(null, "Option-PageUp"), + exec: function(editor) { editor.scrollPageUp(); }, + readOnly: true + }, { + name: "gotopageup", + bindKey: "PageUp", + exec: function(editor) { editor.gotoPageUp(); }, + readOnly: true + }, { + name: "scrollup", + bindKey: bindKey("Ctrl-Up", null), + exec: function(e) { e.renderer.scrollBy(0, -2 * e.renderer.layerConfig.lineHeight); }, + readOnly: true + }, { + name: "scrolldown", + bindKey: bindKey("Ctrl-Down", null), + exec: function(e) { e.renderer.scrollBy(0, 2 * e.renderer.layerConfig.lineHeight); }, + readOnly: true + }, { + name: "selectlinestart", + bindKey: "Shift-Home", + exec: function(editor) { editor.getSelection().selectLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectlineend", + bindKey: "Shift-End", + exec: function(editor) { editor.getSelection().selectLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "togglerecording", + bindKey: bindKey("Ctrl-Alt-E", "Command-Option-E"), + exec: function(editor) { editor.commands.toggleRecording(editor); }, + readOnly: true + }, { + name: "replaymacro", + bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"), + exec: function(editor) { editor.commands.replay(editor); }, + readOnly: true + }, { + name: "jumptomatching", + bindKey: bindKey("Ctrl-P", "Ctrl-P"), + exec: function(editor) { editor.jumpToMatching(); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "selecttomatching", + bindKey: bindKey("Ctrl-Shift-P", "Ctrl-Shift-P"), + exec: function(editor) { editor.jumpToMatching(true); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "expandToMatching", + bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"), + exec: function(editor) { editor.jumpToMatching(true, true); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "passKeysToBrowser", + bindKey: bindKey(null, null), + exec: function() {}, + passEvent: true, + readOnly: true + }, { + name: "copy", + exec: function(editor) { + }, + readOnly: true + }, + { + name: "cut", + exec: function(editor) { + var range = editor.getSelectionRange(); + editor._emit("cut", range); + + if (!editor.selection.isEmpty()) { + editor.session.remove(range); + editor.clearSelection(); + } + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "paste", + exec: function(editor, args) { + editor.$handlePaste(args); + }, + scrollIntoView: "cursor" + }, { + name: "removeline", + bindKey: bindKey("Ctrl-D", "Command-D"), + exec: function(editor) { editor.removeLines(); }, + scrollIntoView: "cursor", + multiSelectAction: "forEachLine" + }, { + name: "duplicateSelection", + bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"), + exec: function(editor) { editor.duplicateSelection(); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "sortlines", + bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"), + exec: function(editor) { editor.sortLines(); }, + scrollIntoView: "selection", + multiSelectAction: "forEachLine" + }, { + name: "togglecomment", + bindKey: bindKey("Ctrl-/", "Command-/"), + exec: function(editor) { editor.toggleCommentLines(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "toggleBlockComment", + bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"), + exec: function(editor) { editor.toggleBlockComment(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "modifyNumberUp", + bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"), + exec: function(editor) { editor.modifyNumber(1); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "modifyNumberDown", + bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"), + exec: function(editor) { editor.modifyNumber(-1); }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "replace", + bindKey: bindKey("Ctrl-H", "Command-Option-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor, true);}); + } + }, { + name: "undo", + bindKey: bindKey("Ctrl-Z", "Command-Z"), + exec: function(editor) { editor.undo(); } + }, { + name: "redo", + bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"), + exec: function(editor) { editor.redo(); } + }, { + name: "copylinesup", + bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"), + exec: function(editor) { editor.copyLinesUp(); }, + scrollIntoView: "cursor" + }, { + name: "movelinesup", + bindKey: bindKey("Alt-Up", "Option-Up"), + exec: function(editor) { editor.moveLinesUp(); }, + scrollIntoView: "cursor" + }, { + name: "copylinesdown", + bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"), + exec: function(editor) { editor.copyLinesDown(); }, + scrollIntoView: "cursor" + }, { + name: "movelinesdown", + bindKey: bindKey("Alt-Down", "Option-Down"), + exec: function(editor) { editor.moveLinesDown(); }, + scrollIntoView: "cursor" + }, { + name: "del", + bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"), + exec: function(editor) { editor.remove("right"); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "backspace", + bindKey: bindKey( + "Shift-Backspace|Backspace", + "Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H" + ), + exec: function(editor) { editor.remove("left"); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "cut_or_delete", + bindKey: bindKey("Shift-Delete", null), + exec: function(editor) { + if (editor.selection.isEmpty()) { + editor.remove("left"); + } else { + return false; + } + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolinestart", + bindKey: bindKey("Alt-Backspace", "Command-Backspace"), + exec: function(editor) { editor.removeToLineStart(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolineend", + bindKey: bindKey("Alt-Delete", "Ctrl-K|Command-Delete"), + exec: function(editor) { editor.removeToLineEnd(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolinestarthard", + bindKey: bindKey("Ctrl-Shift-Backspace", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.start.column = 0; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolineendhard", + bindKey: bindKey("Ctrl-Shift-Delete", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.end.column = Number.MAX_VALUE; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removewordleft", + bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"), + exec: function(editor) { editor.removeWordLeft(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removewordright", + bindKey: bindKey("Ctrl-Delete", "Alt-Delete"), + exec: function(editor) { editor.removeWordRight(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "outdent", + bindKey: bindKey("Shift-Tab", "Shift-Tab"), + exec: function(editor) { editor.blockOutdent(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "indent", + bindKey: bindKey("Tab", "Tab"), + exec: function(editor) { editor.indent(); }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "blockoutdent", + bindKey: bindKey("Ctrl-[", "Ctrl-["), + exec: function(editor) { editor.blockOutdent(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "blockindent", + bindKey: bindKey("Ctrl-]", "Ctrl-]"), + exec: function(editor) { editor.blockIndent(); }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "insertstring", + exec: function(editor, str) { editor.insert(str); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "inserttext", + exec: function(editor, args) { + editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "splitline", + bindKey: bindKey(null, "Ctrl-O"), + exec: function(editor) { editor.splitLine(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "transposeletters", + bindKey: bindKey("Alt-Shift-X", "Ctrl-T"), + exec: function(editor) { editor.transposeLetters(); }, + multiSelectAction: function(editor) {editor.transposeSelections(1); }, + scrollIntoView: "cursor" + }, { + name: "touppercase", + bindKey: bindKey("Ctrl-U", "Ctrl-U"), + exec: function(editor) { editor.toUpperCase(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "tolowercase", + bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"), + exec: function(editor) { editor.toLowerCase(); }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "expandtoline", + bindKey: bindKey("Ctrl-Shift-L", "Command-Shift-L"), + exec: function(editor) { + var range = editor.selection.getRange(); + + range.start.column = range.end.column = 0; + range.end.row++; + editor.selection.setRange(range, false); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "joinlines", + bindKey: bindKey(null, null), + exec: function(editor) { + var isBackwards = editor.selection.isBackwards(); + var selectionStart = isBackwards ? editor.selection.getSelectionLead() : editor.selection.getSelectionAnchor(); + var selectionEnd = isBackwards ? editor.selection.getSelectionAnchor() : editor.selection.getSelectionLead(); + var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length; + var selectedText = editor.session.doc.getTextRange(editor.selection.getRange()); + var selectedCount = selectedText.replace(/\n\s*/, " ").length; + var insertLine = editor.session.doc.getLine(selectionStart.row); + + for (var i = selectionStart.row + 1; i <= selectionEnd.row + 1; i++) { + var curLine = lang.stringTrimLeft(lang.stringTrimRight(editor.session.doc.getLine(i))); + if (curLine.length !== 0) { + curLine = " " + curLine; + } + insertLine += curLine; + } + + if (selectionEnd.row + 1 < (editor.session.doc.getLength() - 1)) { + insertLine += editor.session.doc.getNewLineCharacter(); + } + + editor.clearSelection(); + editor.session.doc.replace(new Range(selectionStart.row, 0, selectionEnd.row + 2, 0), insertLine); + + if (selectedCount > 0) { + editor.selection.moveCursorTo(selectionStart.row, selectionStart.column); + editor.selection.selectTo(selectionStart.row, selectionStart.column + selectedCount); + } else { + firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length > firstLineEndCol ? (firstLineEndCol + 1) : firstLineEndCol; + editor.selection.moveCursorTo(selectionStart.row, firstLineEndCol); + } + }, + multiSelectAction: "forEach", + readOnly: true + }, { + name: "invertSelection", + bindKey: bindKey(null, null), + exec: function(editor) { + var endRow = editor.session.doc.getLength() - 1; + var endCol = editor.session.doc.getLine(endRow).length; + var ranges = editor.selection.rangeList.ranges; + var newRanges = []; + if (ranges.length < 1) { + ranges = [editor.selection.getRange()]; + } + + for (var i = 0; i < ranges.length; i++) { + if (i == (ranges.length - 1)) { + if (!(ranges[i].end.row === endRow && ranges[i].end.column === endCol)) { + newRanges.push(new Range(ranges[i].end.row, ranges[i].end.column, endRow, endCol)); + } + } + + if (i === 0) { + if (!(ranges[i].start.row === 0 && ranges[i].start.column === 0)) { + newRanges.push(new Range(0, 0, ranges[i].start.row, ranges[i].start.column)); + } + } else { + newRanges.push(new Range(ranges[i-1].end.row, ranges[i-1].end.column, ranges[i].start.row, ranges[i].start.column)); + } + } + + editor.exitMultiSelectMode(); + editor.clearSelection(); + + for(var i = 0; i < newRanges.length; i++) { + editor.selection.addRange(newRanges[i], false); + } + }, + readOnly: true, + scrollIntoView: "none" + }]; + + }); + + ace.define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands","ace/config","ace/token_iterator"], function(acequire, exports, module) { + "use strict"; + + acequire("./lib/fixoldbrowsers"); + + var oop = acequire("./lib/oop"); + var dom = acequire("./lib/dom"); + var lang = acequire("./lib/lang"); + var useragent = acequire("./lib/useragent"); + var TextInput = acequire("./keyboard/textinput").TextInput; + var MouseHandler = acequire("./mouse/mouse_handler").MouseHandler; + var FoldHandler = acequire("./mouse/fold_handler").FoldHandler; + var KeyBinding = acequire("./keyboard/keybinding").KeyBinding; + var EditSession = acequire("./edit_session").EditSession; + var Search = acequire("./search").Search; + var Range = acequire("./range").Range; + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var CommandManager = acequire("./commands/command_manager").CommandManager; + var defaultCommands = acequire("./commands/default_commands").commands; + var config = acequire("./config"); + var TokenIterator = acequire("./token_iterator").TokenIterator; + var Editor = function(renderer, session) { + var container = renderer.getContainerElement(); + this.container = container; + this.renderer = renderer; + this.id = "editor" + (++Editor.$uid); + + this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands); + if (typeof document == "object") { + this.textInput = new TextInput(renderer.getTextAreaContainer(), this); + this.renderer.textarea = this.textInput.getElement(); + this.$mouseHandler = new MouseHandler(this); + new FoldHandler(this); + } + + this.keyBinding = new KeyBinding(this); + + this.$blockScrolling = 0; + this.$search = new Search().set({ + wrap: true + }); + + this.$historyTracker = this.$historyTracker.bind(this); + this.commands.on("exec", this.$historyTracker); + + this.$initOperationListeners(); + + this._$emitInputEvent = lang.delayedCall(function() { + this._signal("input", {}); + if (this.session && this.session.bgTokenizer) + this.session.bgTokenizer.scheduleStart(); + }.bind(this)); + + this.on("change", function(_, _self) { + _self._$emitInputEvent.schedule(31); + }); + + this.setSession(session || new EditSession("")); + config.resetOptions(this); + config._signal("editor", this); + }; + + Editor.$uid = 0; + + (function(){ + + oop.implement(this, EventEmitter); + + this.$initOperationListeners = function() { + function last(a) {return a[a.length - 1];} + + this.selections = []; + this.commands.on("exec", this.startOperation.bind(this), true); + this.commands.on("afterExec", this.endOperation.bind(this), true); + + this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this)); + + this.on("change", function() { + this.curOp || this.startOperation(); + this.curOp.docChanged = true; + }.bind(this), true); + + this.on("changeSelection", function() { + this.curOp || this.startOperation(); + this.curOp.selectionChanged = true; + }.bind(this), true); + }; + + this.curOp = null; + this.prevOp = {}; + this.startOperation = function(commadEvent) { + if (this.curOp) { + if (!commadEvent || this.curOp.command) + return; + this.prevOp = this.curOp; + } + if (!commadEvent) { + this.previousCommand = null; + commadEvent = {}; + } + + this.$opResetTimer.schedule(); + this.curOp = { + command: commadEvent.command || {}, + args: commadEvent.args, + scrollTop: this.renderer.scrollTop + }; + if (this.curOp.command.name && this.curOp.command.scrollIntoView !== undefined) + this.$blockScrolling++; + }; + + this.endOperation = function(e) { + if (this.curOp) { + if (e && e.returnValue === false) + return this.curOp = null; + this._signal("beforeEndOperation"); + var command = this.curOp.command; + if (command.name && this.$blockScrolling > 0) + this.$blockScrolling--; + var scrollIntoView = command && command.scrollIntoView; + if (scrollIntoView) { + switch (scrollIntoView) { + case "center-animate": + scrollIntoView = "animate"; + case "center": + this.renderer.scrollCursorIntoView(null, 0.5); + break; + case "animate": + case "cursor": + this.renderer.scrollCursorIntoView(); + break; + case "selectionPart": + var range = this.selection.getRange(); + var config = this.renderer.layerConfig; + if (range.start.row >= config.lastRow || range.end.row <= config.firstRow) { + this.renderer.scrollSelectionIntoView(this.selection.anchor, this.selection.lead); + } + break; + default: + break; + } + if (scrollIntoView == "animate") + this.renderer.animateScrolling(this.curOp.scrollTop); + } + + this.prevOp = this.curOp; + this.curOp = null; + } + }; + this.$mergeableCommands = ["backspace", "del", "insertstring"]; + this.$historyTracker = function(e) { + if (!this.$mergeUndoDeltas) + return; + + var prev = this.prevOp; + var mergeableCommands = this.$mergeableCommands; + var shouldMerge = prev.command && (e.command.name == prev.command.name); + if (e.command.name == "insertstring") { + var text = e.args; + if (this.mergeNextCommand === undefined) + this.mergeNextCommand = true; + + shouldMerge = shouldMerge + && this.mergeNextCommand // previous command allows to coalesce with + && (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type + + this.mergeNextCommand = true; + } else { + shouldMerge = shouldMerge + && mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable + } + + if ( + this.$mergeUndoDeltas != "always" + && Date.now() - this.sequenceStartTime > 2000 + ) { + shouldMerge = false; // the sequence is too long + } + + if (shouldMerge) + this.session.mergeUndoDeltas = true; + else if (mergeableCommands.indexOf(e.command.name) !== -1) + this.sequenceStartTime = Date.now(); + }; + this.setKeyboardHandler = function(keyboardHandler, cb) { + if (keyboardHandler && typeof keyboardHandler === "string") { + this.$keybindingId = keyboardHandler; + var _self = this; + config.loadModule(["keybinding", keyboardHandler], function(module) { + if (_self.$keybindingId == keyboardHandler) + _self.keyBinding.setKeyboardHandler(module && module.handler); + cb && cb(); + }); + } else { + this.$keybindingId = null; + this.keyBinding.setKeyboardHandler(keyboardHandler); + cb && cb(); + } + }; + this.getKeyboardHandler = function() { + return this.keyBinding.getKeyboardHandler(); + }; + this.setSession = function(session) { + if (this.session == session) + return; + if (this.curOp) this.endOperation(); + this.curOp = {}; + + var oldSession = this.session; + if (oldSession) { + this.session.off("change", this.$onDocumentChange); + this.session.off("changeMode", this.$onChangeMode); + this.session.off("tokenizerUpdate", this.$onTokenizerUpdate); + this.session.off("changeTabSize", this.$onChangeTabSize); + this.session.off("changeWrapLimit", this.$onChangeWrapLimit); + this.session.off("changeWrapMode", this.$onChangeWrapMode); + this.session.off("changeFold", this.$onChangeFold); + this.session.off("changeFrontMarker", this.$onChangeFrontMarker); + this.session.off("changeBackMarker", this.$onChangeBackMarker); + this.session.off("changeBreakpoint", this.$onChangeBreakpoint); + this.session.off("changeAnnotation", this.$onChangeAnnotation); + this.session.off("changeOverwrite", this.$onCursorChange); + this.session.off("changeScrollTop", this.$onScrollTopChange); + this.session.off("changeScrollLeft", this.$onScrollLeftChange); + + var selection = this.session.getSelection(); + selection.off("changeCursor", this.$onCursorChange); + selection.off("changeSelection", this.$onSelectionChange); + } + + this.session = session; + if (session) { + this.$onDocumentChange = this.onDocumentChange.bind(this); + session.on("change", this.$onDocumentChange); + this.renderer.setSession(session); + + this.$onChangeMode = this.onChangeMode.bind(this); + session.on("changeMode", this.$onChangeMode); + + this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this); + session.on("tokenizerUpdate", this.$onTokenizerUpdate); + + this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer); + session.on("changeTabSize", this.$onChangeTabSize); + + this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this); + session.on("changeWrapLimit", this.$onChangeWrapLimit); + + this.$onChangeWrapMode = this.onChangeWrapMode.bind(this); + session.on("changeWrapMode", this.$onChangeWrapMode); + + this.$onChangeFold = this.onChangeFold.bind(this); + session.on("changeFold", this.$onChangeFold); + + this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this); + this.session.on("changeFrontMarker", this.$onChangeFrontMarker); + + this.$onChangeBackMarker = this.onChangeBackMarker.bind(this); + this.session.on("changeBackMarker", this.$onChangeBackMarker); + + this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this); + this.session.on("changeBreakpoint", this.$onChangeBreakpoint); + + this.$onChangeAnnotation = this.onChangeAnnotation.bind(this); + this.session.on("changeAnnotation", this.$onChangeAnnotation); + + this.$onCursorChange = this.onCursorChange.bind(this); + this.session.on("changeOverwrite", this.$onCursorChange); + + this.$onScrollTopChange = this.onScrollTopChange.bind(this); + this.session.on("changeScrollTop", this.$onScrollTopChange); + + this.$onScrollLeftChange = this.onScrollLeftChange.bind(this); + this.session.on("changeScrollLeft", this.$onScrollLeftChange); + + this.selection = session.getSelection(); + this.selection.on("changeCursor", this.$onCursorChange); + + this.$onSelectionChange = this.onSelectionChange.bind(this); + this.selection.on("changeSelection", this.$onSelectionChange); + + this.onChangeMode(); + + this.$blockScrolling += 1; + this.onCursorChange(); + this.$blockScrolling -= 1; + + this.onScrollTopChange(); + this.onScrollLeftChange(); + this.onSelectionChange(); + this.onChangeFrontMarker(); + this.onChangeBackMarker(); + this.onChangeBreakpoint(); + this.onChangeAnnotation(); + this.session.getUseWrapMode() && this.renderer.adjustWrapLimit(); + this.renderer.updateFull(); + } else { + this.selection = null; + this.renderer.setSession(session); + } + + this._signal("changeSession", { + session: session, + oldSession: oldSession + }); + + this.curOp = null; + + oldSession && oldSession._signal("changeEditor", {oldEditor: this}); + session && session._signal("changeEditor", {editor: this}); + + if (session && session.bgTokenizer) + session.bgTokenizer.scheduleStart(); + }; + this.getSession = function() { + return this.session; + }; + this.setValue = function(val, cursorPos) { + this.session.doc.setValue(val); + + if (!cursorPos) + this.selectAll(); + else if (cursorPos == 1) + this.navigateFileEnd(); + else if (cursorPos == -1) + this.navigateFileStart(); + + return val; + }; + this.getValue = function() { + return this.session.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.resize = function(force) { + this.renderer.onResize(force); + }; + this.setTheme = function(theme, cb) { + this.renderer.setTheme(theme, cb); + }; + this.getTheme = function() { + return this.renderer.getTheme(); + }; + this.setStyle = function(style) { + this.renderer.setStyle(style); + }; + this.unsetStyle = function(style) { + this.renderer.unsetStyle(style); + }; + this.getFontSize = function () { + return this.getOption("fontSize") || + dom.computedStyle(this.container, "fontSize"); + }; + this.setFontSize = function(size) { + this.setOption("fontSize", size); + }; + + this.$highlightBrackets = function() { + if (this.session.$bracketHighlight) { + this.session.removeMarker(this.session.$bracketHighlight); + this.session.$bracketHighlight = null; + } + + if (this.$highlightPending) { + return; + } + var self = this; + this.$highlightPending = true; + setTimeout(function() { + self.$highlightPending = false; + var session = self.session; + if (!session || !session.bgTokenizer) return; + var pos = session.findMatchingBracket(self.getCursorPosition()); + if (pos) { + var range = new Range(pos.row, pos.column, pos.row, pos.column + 1); + } else if (session.$mode.getMatching) { + var range = session.$mode.getMatching(self.session); + } + if (range) + session.$bracketHighlight = session.addMarker(range, "ace_bracket", "text"); + }, 50); + }; + this.$highlightTags = function() { + if (this.$highlightTagPending) + return; + var self = this; + this.$highlightTagPending = true; + setTimeout(function() { + self.$highlightTagPending = false; + + var session = self.session; + if (!session || !session.bgTokenizer) return; + + var pos = self.getCursorPosition(); + var iterator = new TokenIterator(self.session, pos.row, pos.column); + var token = iterator.getCurrentToken(); + + if (!token || !/\b(?:tag-open|tag-name)/.test(token.type)) { + session.removeMarker(session.$tagHighlight); + session.$tagHighlight = null; + return; + } + + if (token.type.indexOf("tag-open") != -1) { + token = iterator.stepForward(); + if (!token) + return; + } + + var tag = token.value; + var depth = 0; + var prevToken = iterator.stepBackward(); + + if (prevToken.value == '<'){ + do { + prevToken = token; + token = iterator.stepForward(); + + if (token && token.value === tag && token.type.indexOf('tag-name') !== -1) { + if (prevToken.value === '<'){ + depth++; + } else if (prevToken.value === '= 0); + } else { + do { + token = prevToken; + prevToken = iterator.stepBackward(); + + if (token && token.value === tag && token.type.indexOf('tag-name') !== -1) { + if (prevToken.value === '<') { + depth++; + } else if (prevToken.value === ' 1)) + highlight = false; + } + + if (session.$highlightLineMarker && !highlight) { + session.removeMarker(session.$highlightLineMarker.id); + session.$highlightLineMarker = null; + } else if (!session.$highlightLineMarker && highlight) { + var range = new Range(highlight.row, highlight.column, highlight.row, Infinity); + range.id = session.addMarker(range, "ace_active-line", "screenLine"); + session.$highlightLineMarker = range; + } else if (highlight) { + session.$highlightLineMarker.start.row = highlight.row; + session.$highlightLineMarker.end.row = highlight.row; + session.$highlightLineMarker.start.column = highlight.column; + session._signal("changeBackMarker"); + } + }; + + this.onSelectionChange = function(e) { + var session = this.session; + + if (session.$selectionMarker) { + session.removeMarker(session.$selectionMarker); + } + session.$selectionMarker = null; + + if (!this.selection.isEmpty()) { + var range = this.selection.getRange(); + var style = this.getSelectionStyle(); + session.$selectionMarker = session.addMarker(range, "ace_selection", style); + } else { + this.$updateHighlightActiveLine(); + } + + var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp(); + this.session.highlight(re); + + this._signal("changeSelection"); + }; + + this.$getSelectionHighLightRegexp = function() { + var session = this.session; + + var selection = this.getSelectionRange(); + if (selection.isEmpty() || selection.isMultiLine()) + return; + + var startOuter = selection.start.column - 1; + var endOuter = selection.end.column + 1; + var line = session.getLine(selection.start.row); + var lineCols = line.length; + var needle = line.substring(Math.max(startOuter, 0), + Math.min(endOuter, lineCols)); + if ((startOuter >= 0 && /^[\w\d]/.test(needle)) || + (endOuter <= lineCols && /[\w\d]$/.test(needle))) + return; + + needle = line.substring(selection.start.column, selection.end.column); + if (!/^[\w\d]+$/.test(needle)) + return; + + var re = this.$search.$assembleRegExp({ + wholeWord: true, + caseSensitive: true, + needle: needle + }); + + return re; + }; + + + this.onChangeFrontMarker = function() { + this.renderer.updateFrontMarkers(); + }; + + this.onChangeBackMarker = function() { + this.renderer.updateBackMarkers(); + }; + + + this.onChangeBreakpoint = function() { + this.renderer.updateBreakpoints(); + }; + + this.onChangeAnnotation = function() { + this.renderer.setAnnotations(this.session.getAnnotations()); + }; + + + this.onChangeMode = function(e) { + this.renderer.updateText(); + this._emit("changeMode", e); + }; + + + this.onChangeWrapLimit = function() { + this.renderer.updateFull(); + }; + + this.onChangeWrapMode = function() { + this.renderer.onResize(true); + }; + + + this.onChangeFold = function() { + this.$updateHighlightActiveLine(); + this.renderer.updateFull(); + }; + this.getSelectedText = function() { + return this.session.getTextRange(this.getSelectionRange()); + }; + this.getCopyText = function() { + var text = this.getSelectedText(); + this._signal("copy", text); + return text; + }; + this.onCopy = function() { + this.commands.exec("copy", this); + }; + this.onCut = function() { + this.commands.exec("cut", this); + }; + this.onPaste = function(text, event) { + var e = {text: text, event: event}; + this.commands.exec("paste", this, e); + }; + + this.$handlePaste = function(e) { + if (typeof e == "string") + e = {text: e}; + this._signal("paste", e); + var text = e.text; + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { + this.insert(text); + } else { + var lines = text.split(/\r\n|\r|\n/); + var ranges = this.selection.rangeList.ranges; + + if (lines.length > ranges.length || lines.length < 2 || !lines[1]) + return this.commands.exec("insertstring", this, text); + + for (var i = ranges.length; i--;) { + var range = ranges[i]; + if (!range.isEmpty()) + this.session.remove(range); + + this.session.insert(range.start, lines[i]); + } + } + }; + + this.execCommand = function(command, args) { + return this.commands.exec(command, this, args); + }; + this.insert = function(text, pasted) { + var session = this.session; + var mode = session.getMode(); + var cursor = this.getCursorPosition(); + + if (this.getBehavioursEnabled() && !pasted) { + var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text); + if (transform) { + if (text !== transform.text) { + this.session.mergeUndoDeltas = false; + this.$mergeNextCommand = false; + } + text = transform.text; + + } + } + + if (text == "\t") + text = this.session.getTabString(); + if (!this.selection.isEmpty()) { + var range = this.getSelectionRange(); + cursor = this.session.remove(range); + this.clearSelection(); + } + else if (this.session.getOverwrite() && text.indexOf("\n") == -1) { + var range = new Range.fromPoints(cursor, cursor); + range.end.column += text.length; + this.session.remove(range); + } + + if (text == "\n" || text == "\r\n") { + var line = session.getLine(cursor.row); + if (cursor.column > line.search(/\S|$/)) { + var d = line.substr(cursor.column).search(/\S|$/); + session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d); + } + } + this.clearSelection(); + + var start = cursor.column; + var lineState = session.getState(cursor.row); + var line = session.getLine(cursor.row); + var shouldOutdent = mode.checkOutdent(lineState, line, text); + var end = session.insert(cursor, text); + + if (transform && transform.selection) { + if (transform.selection.length == 2) { // Transform relative to the current column + this.selection.setSelectionRange( + new Range(cursor.row, start + transform.selection[0], + cursor.row, start + transform.selection[1])); + } else { // Transform relative to the current row. + this.selection.setSelectionRange( + new Range(cursor.row + transform.selection[0], + transform.selection[1], + cursor.row + transform.selection[2], + transform.selection[3])); + } + } + + if (session.getDocument().isNewLine(text)) { + var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); + + session.insert({row: cursor.row+1, column: 0}, lineIndent); + } + if (shouldOutdent) + mode.autoOutdent(lineState, session, cursor.row); + }; + + this.onTextInput = function(text) { + this.keyBinding.onTextInput(text); + }; + + this.onCommandKey = function(e, hashId, keyCode) { + this.keyBinding.onCommandKey(e, hashId, keyCode); + }; + this.setOverwrite = function(overwrite) { + this.session.setOverwrite(overwrite); + }; + this.getOverwrite = function() { + return this.session.getOverwrite(); + }; + this.toggleOverwrite = function() { + this.session.toggleOverwrite(); + }; + this.setScrollSpeed = function(speed) { + this.setOption("scrollSpeed", speed); + }; + this.getScrollSpeed = function() { + return this.getOption("scrollSpeed"); + }; + this.setDragDelay = function(dragDelay) { + this.setOption("dragDelay", dragDelay); + }; + this.getDragDelay = function() { + return this.getOption("dragDelay"); + }; + this.setSelectionStyle = function(val) { + this.setOption("selectionStyle", val); + }; + this.getSelectionStyle = function() { + return this.getOption("selectionStyle"); + }; + this.setHighlightActiveLine = function(shouldHighlight) { + this.setOption("highlightActiveLine", shouldHighlight); + }; + this.getHighlightActiveLine = function() { + return this.getOption("highlightActiveLine"); + }; + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + this.setHighlightSelectedWord = function(shouldHighlight) { + this.setOption("highlightSelectedWord", shouldHighlight); + }; + this.getHighlightSelectedWord = function() { + return this.$highlightSelectedWord; + }; + + this.setAnimatedScroll = function(shouldAnimate){ + this.renderer.setAnimatedScroll(shouldAnimate); + }; + + this.getAnimatedScroll = function(){ + return this.renderer.getAnimatedScroll(); + }; + this.setShowInvisibles = function(showInvisibles) { + this.renderer.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.renderer.getShowInvisibles(); + }; + + this.setDisplayIndentGuides = function(display) { + this.renderer.setDisplayIndentGuides(display); + }; + + this.getDisplayIndentGuides = function() { + return this.renderer.getDisplayIndentGuides(); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.renderer.setShowPrintMargin(showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.renderer.getShowPrintMargin(); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.renderer.setPrintMarginColumn(showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.renderer.getPrintMarginColumn(); + }; + this.setReadOnly = function(readOnly) { + this.setOption("readOnly", readOnly); + }; + this.getReadOnly = function() { + return this.getOption("readOnly"); + }; + this.setBehavioursEnabled = function (enabled) { + this.setOption("behavioursEnabled", enabled); + }; + this.getBehavioursEnabled = function () { + return this.getOption("behavioursEnabled"); + }; + this.setWrapBehavioursEnabled = function (enabled) { + this.setOption("wrapBehavioursEnabled", enabled); + }; + this.getWrapBehavioursEnabled = function () { + return this.getOption("wrapBehavioursEnabled"); + }; + this.setShowFoldWidgets = function(show) { + this.setOption("showFoldWidgets", show); + + }; + this.getShowFoldWidgets = function() { + return this.getOption("showFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(fade) { + this.setOption("fadeFoldWidgets", fade); + }; + + this.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + this.remove = function(dir) { + if (this.selection.isEmpty()){ + if (dir == "left") + this.selection.selectLeft(); + else + this.selection.selectRight(); + } + + var range = this.getSelectionRange(); + if (this.getBehavioursEnabled()) { + var session = this.session; + var state = session.getState(range.start.row); + var new_range = session.getMode().transformAction(state, 'deletion', this, session, range); + + if (range.end.column === 0) { + var text = session.getTextRange(range); + if (text[text.length - 1] == "\n") { + var line = session.getLine(range.end.row); + if (/^\s+$/.test(line)) { + range.end.column = line.length; + } + } + } + if (new_range) + range = new_range; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.removeWordRight = function() { + if (this.selection.isEmpty()) + this.selection.selectWordRight(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeWordLeft = function() { + if (this.selection.isEmpty()) + this.selection.selectWordLeft(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineStart = function() { + if (this.selection.isEmpty()) + this.selection.selectLineStart(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineEnd = function() { + if (this.selection.isEmpty()) + this.selection.selectLineEnd(); + + var range = this.getSelectionRange(); + if (range.start.column == range.end.column && range.start.row == range.end.row) { + range.end.column = 0; + range.end.row++; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.splitLine = function() { + if (!this.selection.isEmpty()) { + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + } + + var cursor = this.getCursorPosition(); + this.insert("\n"); + this.moveCursorToPosition(cursor); + }; + this.transposeLetters = function() { + if (!this.selection.isEmpty()) { + return; + } + + var cursor = this.getCursorPosition(); + var column = cursor.column; + if (column === 0) + return; + + var line = this.session.getLine(cursor.row); + var swap, range; + if (column < line.length) { + swap = line.charAt(column) + line.charAt(column-1); + range = new Range(cursor.row, column-1, cursor.row, column+1); + } + else { + swap = line.charAt(column-1) + line.charAt(column-2); + range = new Range(cursor.row, column-2, cursor.row, column); + } + this.session.replace(range, swap); + this.session.selection.moveToPosition(range.end); + }; + this.toLowerCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toLowerCase()); + this.selection.setSelectionRange(originalRange); + }; + this.toUpperCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toUpperCase()); + this.selection.setSelectionRange(originalRange); + }; + this.indent = function() { + var session = this.session; + var range = this.getSelectionRange(); + + if (range.start.row < range.end.row) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } else if (range.start.column < range.end.column) { + var text = session.getTextRange(range); + if (!/^\s+$/.test(text)) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } + } + + var line = session.getLine(range.start.row); + var position = range.start; + var size = session.getTabSize(); + var column = session.documentToScreenColumn(position.row, position.column); + + if (this.session.getUseSoftTabs()) { + var count = (size - column % size); + var indentString = lang.stringRepeat(" ", count); + } else { + var count = column % size; + while (line[range.start.column - 1] == " " && count) { + range.start.column--; + count--; + } + this.selection.setSelectionRange(range); + indentString = "\t"; + } + return this.insert(indentString); + }; + this.blockIndent = function() { + var rows = this.$getSelectedRows(); + this.session.indentRows(rows.first, rows.last, "\t"); + }; + this.blockOutdent = function() { + var selection = this.session.getSelection(); + this.session.outdentRows(selection.getRange()); + }; + this.sortLines = function() { + var rows = this.$getSelectedRows(); + var session = this.session; + + var lines = []; + for (var i = rows.first; i <= rows.last; i++) + lines.push(session.getLine(i)); + + lines.sort(function(a, b) { + if (a.toLowerCase() < b.toLowerCase()) return -1; + if (a.toLowerCase() > b.toLowerCase()) return 1; + return 0; + }); + + var deleteRange = new Range(0, 0, 0, 0); + for (var i = rows.first; i <= rows.last; i++) { + var line = session.getLine(i); + deleteRange.start.row = i; + deleteRange.end.row = i; + deleteRange.end.column = line.length; + session.replace(deleteRange, lines[i-rows.first]); + } + }; + this.toggleCommentLines = function() { + var state = this.session.getState(this.getCursorPosition().row); + var rows = this.$getSelectedRows(); + this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last); + }; + + this.toggleBlockComment = function() { + var cursor = this.getCursorPosition(); + var state = this.session.getState(cursor.row); + var range = this.getSelectionRange(); + this.session.getMode().toggleBlockComment(state, this.session, range, cursor); + }; + this.getNumberAt = function(row, column) { + var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g; + _numberRx.lastIndex = 0; + + var s = this.session.getLine(row); + while (_numberRx.lastIndex < column) { + var m = _numberRx.exec(s); + if(m.index <= column && m.index+m[0].length >= column){ + var number = { + value: m[0], + start: m.index, + end: m.index+m[0].length + }; + return number; + } + } + return null; + }; + this.modifyNumber = function(amount) { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + var charRange = new Range(row, column-1, row, column); + + var c = this.session.getTextRange(charRange); + if (!isNaN(parseFloat(c)) && isFinite(c)) { + var nr = this.getNumberAt(row, column); + if (nr) { + var fp = nr.value.indexOf(".") >= 0 ? nr.start + nr.value.indexOf(".") + 1 : nr.end; + var decimals = nr.start + nr.value.length - fp; + + var t = parseFloat(nr.value); + t *= Math.pow(10, decimals); + + + if(fp !== nr.end && column < fp){ + amount *= Math.pow(10, nr.end - column - 1); + } else { + amount *= Math.pow(10, nr.end - column); + } + + t += amount; + t /= Math.pow(10, decimals); + var nnr = t.toFixed(decimals); + var replaceRange = new Range(row, nr.start, row, nr.end); + this.session.replace(replaceRange, nnr); + this.moveCursorTo(row, Math.max(nr.start +1, column + nnr.length - nr.value.length)); + + } + } + }; + this.removeLines = function() { + var rows = this.$getSelectedRows(); + this.session.removeFullLines(rows.first, rows.last); + this.clearSelection(); + }; + + this.duplicateSelection = function() { + var sel = this.selection; + var doc = this.session; + var range = sel.getRange(); + var reverse = sel.isBackwards(); + if (range.isEmpty()) { + var row = range.start.row; + doc.duplicateLines(row, row); + } else { + var point = reverse ? range.start : range.end; + var endPoint = doc.insert(point, doc.getTextRange(range), false); + range.start = point; + range.end = endPoint; + + sel.setSelectionRange(range, reverse); + } + }; + this.moveLinesDown = function() { + this.$moveLines(1, false); + }; + this.moveLinesUp = function() { + this.$moveLines(-1, false); + }; + this.moveText = function(range, toPosition, copy) { + return this.session.moveText(range, toPosition, copy); + }; + this.copyLinesUp = function() { + this.$moveLines(-1, true); + }; + this.copyLinesDown = function() { + this.$moveLines(1, true); + }; + this.$moveLines = function(dir, copy) { + var rows, moved; + var selection = this.selection; + if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) { + var range = selection.toOrientedRange(); + rows = this.$getSelectedRows(range); + moved = this.session.$moveLines(rows.first, rows.last, copy ? 0 : dir); + if (copy && dir == -1) moved = 0; + range.moveBy(moved, 0); + selection.fromOrientedRange(range); + } else { + var ranges = selection.rangeList.ranges; + selection.rangeList.detach(this.session); + this.inVirtualSelectionMode = true; + + var diff = 0; + var totalDiff = 0; + var l = ranges.length; + for (var i = 0; i < l; i++) { + var rangeIndex = i; + ranges[i].moveBy(diff, 0); + rows = this.$getSelectedRows(ranges[i]); + var first = rows.first; + var last = rows.last; + while (++i < l) { + if (totalDiff) ranges[i].moveBy(totalDiff, 0); + var subRows = this.$getSelectedRows(ranges[i]); + if (copy && subRows.first != last) + break; + else if (!copy && subRows.first > last + 1) + break; + last = subRows.last; + } + i--; + diff = this.session.$moveLines(first, last, copy ? 0 : dir); + if (copy && dir == -1) rangeIndex = i + 1; + while (rangeIndex <= i) { + ranges[rangeIndex].moveBy(diff, 0); + rangeIndex++; + } + if (!copy) diff = 0; + totalDiff += diff; + } + + selection.fromOrientedRange(selection.ranges[0]); + selection.rangeList.attach(this.session); + this.inVirtualSelectionMode = false; + } + }; + this.$getSelectedRows = function(range) { + range = (range || this.getSelectionRange()).collapseRows(); + + return { + first: this.session.getRowFoldStart(range.start.row), + last: this.session.getRowFoldEnd(range.end.row) + }; + }; + + this.onCompositionStart = function(text) { + this.renderer.showComposition(this.getCursorPosition()); + }; + + this.onCompositionUpdate = function(text) { + this.renderer.setCompositionText(text); + }; + + this.onCompositionEnd = function() { + this.renderer.hideComposition(); + }; + this.getFirstVisibleRow = function() { + return this.renderer.getFirstVisibleRow(); + }; + this.getLastVisibleRow = function() { + return this.renderer.getLastVisibleRow(); + }; + this.isRowVisible = function(row) { + return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); + }; + this.isRowFullyVisible = function(row) { + return (row >= this.renderer.getFirstFullyVisibleRow() && row <= this.renderer.getLastFullyVisibleRow()); + }; + this.$getVisibleRowCount = function() { + return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1; + }; + + this.$moveByPage = function(dir, select) { + var renderer = this.renderer; + var config = this.renderer.layerConfig; + var rows = dir * Math.floor(config.height / config.lineHeight); + + this.$blockScrolling++; + if (select === true) { + this.selection.$moveSelection(function(){ + this.moveCursorBy(rows, 0); + }); + } else if (select === false) { + this.selection.moveCursorBy(rows, 0); + this.selection.clearSelection(); + } + this.$blockScrolling--; + + var scrollTop = renderer.scrollTop; + + renderer.scrollBy(0, rows * config.lineHeight); + if (select != null) + renderer.scrollCursorIntoView(null, 0.5); + + renderer.animateScrolling(scrollTop); + }; + this.selectPageDown = function() { + this.$moveByPage(1, true); + }; + this.selectPageUp = function() { + this.$moveByPage(-1, true); + }; + this.gotoPageDown = function() { + this.$moveByPage(1, false); + }; + this.gotoPageUp = function() { + this.$moveByPage(-1, false); + }; + this.scrollPageDown = function() { + this.$moveByPage(1); + }; + this.scrollPageUp = function() { + this.$moveByPage(-1); + }; + this.scrollToRow = function(row) { + this.renderer.scrollToRow(row); + }; + this.scrollToLine = function(line, center, animate, callback) { + this.renderer.scrollToLine(line, center, animate, callback); + }; + this.centerSelection = function() { + var range = this.getSelectionRange(); + var pos = { + row: Math.floor(range.start.row + (range.end.row - range.start.row) / 2), + column: Math.floor(range.start.column + (range.end.column - range.start.column) / 2) + }; + this.renderer.alignCursor(pos, 0.5); + }; + this.getCursorPosition = function() { + return this.selection.getCursor(); + }; + this.getCursorPositionScreen = function() { + return this.session.documentToScreenPosition(this.getCursorPosition()); + }; + this.getSelectionRange = function() { + return this.selection.getRange(); + }; + this.selectAll = function() { + this.$blockScrolling += 1; + this.selection.selectAll(); + this.$blockScrolling -= 1; + }; + this.clearSelection = function() { + this.selection.clearSelection(); + }; + this.moveCursorTo = function(row, column) { + this.selection.moveCursorTo(row, column); + }; + this.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); + }; + this.jumpToMatching = function(select, expand) { + var cursor = this.getCursorPosition(); + var iterator = new TokenIterator(this.session, cursor.row, cursor.column); + var prevToken = iterator.getCurrentToken(); + var token = prevToken || iterator.stepForward(); + + if (!token) return; + var matchType; + var found = false; + var depth = {}; + var i = cursor.column - token.start; + var bracketType; + var brackets = { + ")": "(", + "(": "(", + "]": "[", + "[": "[", + "{": "{", + "}": "{" + }; + + do { + if (token.value.match(/[{}()\[\]]/g)) { + for (; i < token.value.length && !found; i++) { + if (!brackets[token.value[i]]) { + continue; + } + + bracketType = brackets[token.value[i]] + '.' + token.type.replace("rparen", "lparen"); + + if (isNaN(depth[bracketType])) { + depth[bracketType] = 0; + } + + switch (token.value[i]) { + case '(': + case '[': + case '{': + depth[bracketType]++; + break; + case ')': + case ']': + case '}': + depth[bracketType]--; + + if (depth[bracketType] === -1) { + matchType = 'bracket'; + found = true; + } + break; + } + } + } + else if (token && token.type.indexOf('tag-name') !== -1) { + if (isNaN(depth[token.value])) { + depth[token.value] = 0; + } + + if (prevToken.value === '<') { + depth[token.value]++; + } + else if (prevToken.value === '= 0; --i) { + if(this.$tryReplace(ranges[i], replacement)) { + replaced++; + } + } + + this.selection.setSelectionRange(selection); + this.$blockScrolling -= 1; + + return replaced; + }; + + this.$tryReplace = function(range, replacement) { + var input = this.session.getTextRange(range); + replacement = this.$search.replace(input, replacement); + if (replacement !== null) { + range.end = this.session.replace(range, replacement); + return range; + } else { + return null; + } + }; + this.getLastSearchOptions = function() { + return this.$search.getOptions(); + }; + this.find = function(needle, options, animate) { + if (!options) + options = {}; + + if (typeof needle == "string" || needle instanceof RegExp) + options.needle = needle; + else if (typeof needle == "object") + oop.mixin(options, needle); + + var range = this.selection.getRange(); + if (options.needle == null) { + needle = this.session.getTextRange(range) + || this.$search.$options.needle; + if (!needle) { + range = this.session.getWordRange(range.start.row, range.start.column); + needle = this.session.getTextRange(range); + } + this.$search.set({needle: needle}); + } + + this.$search.set(options); + if (!options.start) + this.$search.set({start: range}); + + var newRange = this.$search.find(this.session); + if (options.preventScroll) + return newRange; + if (newRange) { + this.revealRange(newRange, animate); + return newRange; + } + if (options.backwards) + range.start = range.end; + else + range.end = range.start; + this.selection.setRange(range); + }; + this.findNext = function(options, animate) { + this.find({skipCurrent: true, backwards: false}, options, animate); + }; + this.findPrevious = function(options, animate) { + this.find(options, {skipCurrent: true, backwards: true}, animate); + }; + + this.revealRange = function(range, animate) { + this.$blockScrolling += 1; + this.session.unfold(range); + this.selection.setSelectionRange(range); + this.$blockScrolling -= 1; + + var scrollTop = this.renderer.scrollTop; + this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5); + if (animate !== false) + this.renderer.animateScrolling(scrollTop); + }; + this.undo = function() { + this.$blockScrolling++; + this.session.getUndoManager().undo(); + this.$blockScrolling--; + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.redo = function() { + this.$blockScrolling++; + this.session.getUndoManager().redo(); + this.$blockScrolling--; + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.destroy = function() { + this.renderer.destroy(); + this._signal("destroy", this); + if (this.session) { + this.session.destroy(); + } + }; + this.setAutoScrollEditorIntoView = function(enable) { + if (!enable) + return; + var rect; + var self = this; + var shouldScroll = false; + if (!this.$scrollAnchor) + this.$scrollAnchor = document.createElement("div"); + var scrollAnchor = this.$scrollAnchor; + scrollAnchor.style.cssText = "position:absolute"; + this.container.insertBefore(scrollAnchor, this.container.firstChild); + var onChangeSelection = this.on("changeSelection", function() { + shouldScroll = true; + }); + var onBeforeRender = this.renderer.on("beforeRender", function() { + if (shouldScroll) + rect = self.renderer.container.getBoundingClientRect(); + }); + var onAfterRender = this.renderer.on("afterRender", function() { + if (shouldScroll && rect && (self.isFocused() + || self.searchBox && self.searchBox.isFocused()) + ) { + var renderer = self.renderer; + var pos = renderer.$cursorLayer.$pixelPos; + var config = renderer.layerConfig; + var top = pos.top - config.offset; + if (pos.top >= 0 && top + rect.top < 0) { + shouldScroll = true; + } else if (pos.top < config.height && + pos.top + rect.top + config.lineHeight > window.innerHeight) { + shouldScroll = false; + } else { + shouldScroll = null; + } + if (shouldScroll != null) { + scrollAnchor.style.top = top + "px"; + scrollAnchor.style.left = pos.left + "px"; + scrollAnchor.style.height = config.lineHeight + "px"; + scrollAnchor.scrollIntoView(shouldScroll); + } + shouldScroll = rect = null; + } + }); + this.setAutoScrollEditorIntoView = function(enable) { + if (enable) + return; + delete this.setAutoScrollEditorIntoView; + this.off("changeSelection", onChangeSelection); + this.renderer.off("afterRender", onAfterRender); + this.renderer.off("beforeRender", onBeforeRender); + }; + }; + + + this.$resetCursorStyle = function() { + var style = this.$cursorStyle || "ace"; + var cursorLayer = this.renderer.$cursorLayer; + if (!cursorLayer) + return; + cursorLayer.setSmoothBlinking(/smooth/.test(style)); + cursorLayer.isBlinking = !this.$readOnly && style != "wide"; + dom.setCssClass(cursorLayer.element, "ace_slim-cursors", /slim/.test(style)); + }; + + }).call(Editor.prototype); + + + + config.defineOptions(Editor.prototype, "editor", { + selectionStyle: { + set: function(style) { + this.onSelectionChange(); + this._signal("changeSelectionStyle", {data: style}); + }, + initialValue: "line" + }, + highlightActiveLine: { + set: function() {this.$updateHighlightActiveLine();}, + initialValue: true + }, + highlightSelectedWord: { + set: function(shouldHighlight) {this.$onSelectionChange();}, + initialValue: true + }, + readOnly: { + set: function(readOnly) { + this.$resetCursorStyle(); + }, + initialValue: false + }, + cursorStyle: { + set: function(val) { this.$resetCursorStyle(); }, + values: ["ace", "slim", "smooth", "wide"], + initialValue: "ace" + }, + mergeUndoDeltas: { + values: [false, true, "always"], + initialValue: true + }, + behavioursEnabled: {initialValue: true}, + wrapBehavioursEnabled: {initialValue: true}, + autoScrollEditorIntoView: { + set: function(val) {this.setAutoScrollEditorIntoView(val);} + }, + keyboardHandler: { + set: function(val) { this.setKeyboardHandler(val); }, + get: function() { return this.keybindingId; }, + handlesSet: true + }, + + hScrollBarAlwaysVisible: "renderer", + vScrollBarAlwaysVisible: "renderer", + highlightGutterLine: "renderer", + animatedScroll: "renderer", + showInvisibles: "renderer", + showPrintMargin: "renderer", + printMarginColumn: "renderer", + printMargin: "renderer", + fadeFoldWidgets: "renderer", + showFoldWidgets: "renderer", + showLineNumbers: "renderer", + showGutter: "renderer", + displayIndentGuides: "renderer", + fontSize: "renderer", + fontFamily: "renderer", + maxLines: "renderer", + minLines: "renderer", + scrollPastEnd: "renderer", + fixedWidthGutter: "renderer", + theme: "renderer", + + scrollSpeed: "$mouseHandler", + dragDelay: "$mouseHandler", + dragEnabled: "$mouseHandler", + focusTimout: "$mouseHandler", + tooltipFollowsMouse: "$mouseHandler", + + firstLineNumber: "session", + overwrite: "session", + newLineMode: "session", + useWorker: "session", + useSoftTabs: "session", + tabSize: "session", + wrap: "session", + indentedSoftWrap: "session", + foldStyle: "session", + mode: "session" + }); + + exports.Editor = Editor; + }); + + ace.define("ace/undomanager",["require","exports","module"], function(acequire, exports, module) { + "use strict"; + var UndoManager = function() { + this.reset(); + }; + + (function() { + this.execute = function(options) { + var deltaSets = options.args[0]; + this.$doc = options.args[1]; + if (options.merge && this.hasUndo()){ + this.dirtyCounter--; + deltaSets = this.$undoStack.pop().concat(deltaSets); + } + this.$undoStack.push(deltaSets); + this.$redoStack = []; + if (this.dirtyCounter < 0) { + this.dirtyCounter = NaN; + } + this.dirtyCounter++; + }; + this.undo = function(dontSelect) { + var deltaSets = this.$undoStack.pop(); + var undoSelectionRange = null; + if (deltaSets) { + undoSelectionRange = this.$doc.undoChanges(deltaSets, dontSelect); + this.$redoStack.push(deltaSets); + this.dirtyCounter--; + } + + return undoSelectionRange; + }; + this.redo = function(dontSelect) { + var deltaSets = this.$redoStack.pop(); + var redoSelectionRange = null; + if (deltaSets) { + redoSelectionRange = + this.$doc.redoChanges(this.$deserializeDeltas(deltaSets), dontSelect); + this.$undoStack.push(deltaSets); + this.dirtyCounter++; + } + return redoSelectionRange; + }; + this.reset = function() { + this.$undoStack = []; + this.$redoStack = []; + this.dirtyCounter = 0; + }; + this.hasUndo = function() { + return this.$undoStack.length > 0; + }; + this.hasRedo = function() { + return this.$redoStack.length > 0; + }; + this.markClean = function() { + this.dirtyCounter = 0; + }; + this.isClean = function() { + return this.dirtyCounter === 0; + }; + this.$serializeDeltas = function(deltaSets) { + return cloneDeltaSetsObj(deltaSets, $serializeDelta); + }; + this.$deserializeDeltas = function(deltaSets) { + return cloneDeltaSetsObj(deltaSets, $deserializeDelta); + }; + + function $serializeDelta(delta){ + return { + action: delta.action, + start: delta.start, + end: delta.end, + lines: delta.lines.length == 1 ? null : delta.lines, + text: delta.lines.length == 1 ? delta.lines[0] : null + }; + } + + function $deserializeDelta(delta) { + return { + action: delta.action, + start: delta.start, + end: delta.end, + lines: delta.lines || [delta.text] + }; + } + + function cloneDeltaSetsObj(deltaSets_old, fnGetModifiedDelta) { + var deltaSets_new = new Array(deltaSets_old.length); + for (var i = 0; i < deltaSets_old.length; i++) { + var deltaSet_old = deltaSets_old[i]; + var deltaSet_new = { group: deltaSet_old.group, deltas: new Array(deltaSet_old.length)}; + + for (var j = 0; j < deltaSet_old.deltas.length; j++) { + var delta_old = deltaSet_old.deltas[j]; + deltaSet_new.deltas[j] = fnGetModifiedDelta(delta_old); + } + + deltaSets_new[i] = deltaSet_new; + } + return deltaSets_new; + } + + }).call(UndoManager.prototype); + + exports.UndoManager = UndoManager; + }); + + ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var dom = acequire("../lib/dom"); + var oop = acequire("../lib/oop"); + var lang = acequire("../lib/lang"); + var EventEmitter = acequire("../lib/event_emitter").EventEmitter; + + var Gutter = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_gutter-layer"; + parentEl.appendChild(this.element); + this.setShowFoldWidgets(this.$showFoldWidgets); + + this.gutterWidth = 0; + + this.$annotations = []; + this.$updateAnnotations = this.$updateAnnotations.bind(this); + + this.$cells = []; + }; + + (function() { + + oop.implement(this, EventEmitter); + + this.setSession = function(session) { + if (this.session) + this.session.removeEventListener("change", this.$updateAnnotations); + this.session = session; + if (session) + session.on("change", this.$updateAnnotations); + }; + + this.addGutterDecoration = function(row, className){ + if (window.console) + console.warn && console.warn("deprecated use session.addGutterDecoration"); + this.session.addGutterDecoration(row, className); + }; + + this.removeGutterDecoration = function(row, className){ + if (window.console) + console.warn && console.warn("deprecated use session.removeGutterDecoration"); + this.session.removeGutterDecoration(row, className); + }; + + this.setAnnotations = function(annotations) { + this.$annotations = []; + for (var i = 0; i < annotations.length; i++) { + var annotation = annotations[i]; + var row = annotation.row; + var rowInfo = this.$annotations[row]; + if (!rowInfo) + rowInfo = this.$annotations[row] = {text: []}; + + var annoText = annotation.text; + annoText = annoText ? lang.escapeHTML(annoText) : annotation.html || ""; + + if (rowInfo.text.indexOf(annoText) === -1) + rowInfo.text.push(annoText); + + var type = annotation.type; + if (type == "error") + rowInfo.className = " ace_error"; + else if (type == "warning" && rowInfo.className != " ace_error") + rowInfo.className = " ace_warning"; + else if (type == "info" && (!rowInfo.className)) + rowInfo.className = " ace_info"; + } + }; + + this.$updateAnnotations = function (delta) { + if (!this.$annotations.length) + return; + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + if (len === 0) { + } else if (delta.action == 'remove') { + this.$annotations.splice(firstRow, len + 1, null); + } else { + var args = new Array(len + 1); + args.unshift(firstRow, 1); + this.$annotations.splice.apply(this.$annotations, args); + } + }; + + this.update = function(config) { + var session = this.session; + var firstRow = config.firstRow; + var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + session.getLength() - 1); + var fold = session.getNextFoldLine(firstRow); + var foldStart = fold ? fold.start.row : Infinity; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var firstLineNumber = session.$firstLineNumber; + var lastLineNumber = 0; + + var gutterRenderer = session.gutterRenderer || this.$renderer; + + var cell = null; + var index = -1; + var row = firstRow; + while (true) { + if (row > foldStart) { + row = fold.end.row + 1; + fold = session.getNextFoldLine(row, fold); + foldStart = fold ? fold.start.row : Infinity; + } + if (row > lastRow) { + while (this.$cells.length > index + 1) { + cell = this.$cells.pop(); + this.element.removeChild(cell.element); + } + break; + } + + cell = this.$cells[++index]; + if (!cell) { + cell = {element: null, textNode: null, foldWidget: null}; + cell.element = dom.createElement("div"); + cell.textNode = document.createTextNode(''); + cell.element.appendChild(cell.textNode); + this.element.appendChild(cell.element); + this.$cells[index] = cell; + } + + var className = "ace_gutter-cell "; + if (breakpoints[row]) + className += breakpoints[row]; + if (decorations[row]) + className += decorations[row]; + if (this.$annotations[row]) + className += this.$annotations[row].className; + if (cell.element.className != className) + cell.element.className = className; + + var height = session.getRowLength(row) * config.lineHeight + "px"; + if (height != cell.element.style.height) + cell.element.style.height = height; + + if (foldWidgets) { + var c = foldWidgets[row]; + if (c == null) + c = foldWidgets[row] = session.getFoldWidget(row); + } + + if (c) { + if (!cell.foldWidget) { + cell.foldWidget = dom.createElement("span"); + cell.element.appendChild(cell.foldWidget); + } + var className = "ace_fold-widget ace_" + c; + if (c == "start" && row == foldStart && row < fold.end.row) + className += " ace_closed"; + else + className += " ace_open"; + if (cell.foldWidget.className != className) + cell.foldWidget.className = className; + + var height = config.lineHeight + "px"; + if (cell.foldWidget.style.height != height) + cell.foldWidget.style.height = height; + } else { + if (cell.foldWidget) { + cell.element.removeChild(cell.foldWidget); + cell.foldWidget = null; + } + } + + var text = lastLineNumber = gutterRenderer + ? gutterRenderer.getText(session, row) + : row + firstLineNumber; + if (text !== cell.textNode.data) + cell.textNode.data = text; + + row++; + } + + this.element.style.height = config.minHeight + "px"; + + if (this.$fixedWidth || session.$useWrapMode) + lastLineNumber = session.getLength() + firstLineNumber; + + var gutterWidth = gutterRenderer + ? gutterRenderer.getWidth(session, lastLineNumber, config) + : lastLineNumber.toString().length * config.characterWidth; + + var padding = this.$padding || this.$computePadding(); + gutterWidth += padding.left + padding.right; + if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { + this.gutterWidth = gutterWidth; + this.element.style.width = Math.ceil(this.gutterWidth) + "px"; + this._emit("changeGutterWidth", gutterWidth); + } + }; + + this.$fixedWidth = false; + + this.$showLineNumbers = true; + this.$renderer = ""; + this.setShowLineNumbers = function(show) { + this.$renderer = !show && { + getWidth: function() {return "";}, + getText: function() {return "";} + }; + }; + + this.getShowLineNumbers = function() { + return this.$showLineNumbers; + }; + + this.$showFoldWidgets = true; + this.setShowFoldWidgets = function(show) { + if (show) + dom.addCssClass(this.element, "ace_folding-enabled"); + else + dom.removeCssClass(this.element, "ace_folding-enabled"); + + this.$showFoldWidgets = show; + this.$padding = null; + }; + + this.getShowFoldWidgets = function() { + return this.$showFoldWidgets; + }; + + this.$computePadding = function() { + if (!this.element.firstChild) + return {left: 0, right: 0}; + var style = dom.computedStyle(this.element.firstChild); + this.$padding = {}; + this.$padding.left = parseInt(style.paddingLeft) + 1 || 0; + this.$padding.right = parseInt(style.paddingRight) || 0; + return this.$padding; + }; + + this.getRegion = function(point) { + var padding = this.$padding || this.$computePadding(); + var rect = this.element.getBoundingClientRect(); + if (point.x < padding.left + rect.left) + return "markers"; + if (this.$showFoldWidgets && point.x > rect.right - padding.right) + return "foldWidgets"; + }; + + }).call(Gutter.prototype); + + exports.Gutter = Gutter; + + }); + + ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../range").Range; + var dom = acequire("../lib/dom"); + + var Marker = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_marker-layer"; + parentEl.appendChild(this.element); + }; + + (function() { + + this.$padding = 0; + + this.setPadding = function(padding) { + this.$padding = padding; + }; + this.setSession = function(session) { + this.session = session; + }; + + this.setMarkers = function(markers) { + this.markers = markers; + }; + + this.update = function(config) { + if (!config) return; + + this.config = config; + + + var html = []; + for (var key in this.markers) { + var marker = this.markers[key]; + + if (!marker.range) { + marker.update(html, this, this.session, config); + continue; + } + + var range = marker.range.clipRows(config.firstRow, config.lastRow); + if (range.isEmpty()) continue; + + range = range.toScreenRange(this.session); + if (marker.renderer) { + var top = this.$getTop(range.start.row, config); + var left = this.$padding + (this.session.$bidiHandler.isBidiRow(range.start.row) + ? this.session.$bidiHandler.getPosLeft(range.start.column) + : range.start.column * config.characterWidth); + marker.renderer(html, range, left, top, config); + } else if (marker.type == "fullLine") { + this.drawFullLineMarker(html, range, marker.clazz, config); + } else if (marker.type == "screenLine") { + this.drawScreenLineMarker(html, range, marker.clazz, config); + } else if (range.isMultiLine()) { + if (marker.type == "text") + this.drawTextMarker(html, range, marker.clazz, config); + else + this.drawMultiLineMarker(html, range, marker.clazz, config); + } else { + if (this.session.$bidiHandler.isBidiRow(range.start.row)) { + this.drawBidiSingleLineMarker(html, range, marker.clazz + " ace_start" + " ace_br15", config); + } else { + this.drawSingleLineMarker(html, range, marker.clazz + " ace_start" + " ace_br15", config); + } + } + } + this.element.innerHTML = html.join(""); + }; + + this.$getTop = function(row, layerConfig) { + return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight; + }; + + function getBorderClass(tl, tr, br, bl) { + return (tl ? 1 : 0) | (tr ? 2 : 0) | (br ? 4 : 0) | (bl ? 8 : 0); + } + this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig, extraStyle) { + var session = this.session; + var start = range.start.row; + var end = range.end.row; + var row = start; + var prev = 0; + var curr = 0; + var next = session.getScreenLastRowColumn(row); + var clazzModified = null; + var lineRange = new Range(row, range.start.column, row, curr); + for (; row <= end; row++) { + lineRange.start.row = lineRange.end.row = row; + lineRange.start.column = row == start ? range.start.column : session.getRowWrapIndent(row); + lineRange.end.column = next; + prev = curr; + curr = next; + next = row + 1 < end ? session.getScreenLastRowColumn(row + 1) : row == end ? 0 : range.end.column; + clazzModified = clazz + (row == start ? " ace_start" : "") + " ace_br" + + getBorderClass(row == start || row == start + 1 && range.start.column, prev < curr, curr > next, row == end); + + if (this.session.$bidiHandler.isBidiRow(row)) { + this.drawBidiSingleLineMarker(stringBuilder, lineRange, clazzModified, + layerConfig, row == end ? 0 : 1, extraStyle); + } else { + this.drawSingleLineMarker(stringBuilder, lineRange, clazzModified, + layerConfig, row == end ? 0 : 1, extraStyle); + } + } + }; + this.drawMultiLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var padding = this.$padding; + var height, top, left; + extraStyle = extraStyle || ""; + if (this.session.$bidiHandler.isBidiRow(range.start.row)) { + var range1 = range.clone(); + range1.end.row = range1.start.row; + range1.end.column = this.session.getLine(range1.start.row).length; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br1 ace_start", config, null, extraStyle); + } else { + height = config.lineHeight; + top = this.$getTop(range.start.row, config); + left = padding + range.start.column * config.characterWidth; + stringBuilder.push( + "
      " + ); + } + if (this.session.$bidiHandler.isBidiRow(range.end.row)) { + var range1 = range.clone(); + range1.start.row = range1.end.row; + range1.start.column = 0; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br12", config, null, extraStyle); + } else { + var width = range.end.column * config.characterWidth; + height = config.lineHeight; + top = this.$getTop(range.end.row, config); + stringBuilder.push( + "
      " + ); + } + height = (range.end.row - range.start.row - 1) * config.lineHeight; + if (height <= 0) + return; + top = this.$getTop(range.start.row + 1, config); + + var radiusClass = (range.start.column ? 1 : 0) | (range.end.column ? 0 : 8); + + stringBuilder.push( + "
      " + ); + }; + this.drawSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + var height = config.lineHeight; + var width = (range.end.column + (extraLength || 0) - range.start.column) * config.characterWidth; + + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + + stringBuilder.push( + "
      " + ); + }; + this.drawBidiSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + var height = config.lineHeight, top = this.$getTop(range.start.row, config), padding = this.$padding; + var selections = this.session.$bidiHandler.getSelections(range.start.column, range.end.column); + + selections.forEach(function(selection) { + stringBuilder.push( + "
      " + ); + }); + }; + + this.drawFullLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + if (range.start.row != range.end.row) + height += this.$getTop(range.end.row, config) - top; + + stringBuilder.push( + "
      " + ); + }; + + this.drawScreenLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + + stringBuilder.push( + "
      " + ); + }; + + }).call(Marker.prototype); + + exports.Marker = Marker; + + }); + + ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../lib/oop"); + var dom = acequire("../lib/dom"); + var lang = acequire("../lib/lang"); + var useragent = acequire("../lib/useragent"); + var EventEmitter = acequire("../lib/event_emitter").EventEmitter; + + var Text = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_text-layer"; + parentEl.appendChild(this.element); + this.$updateEolChar = this.$updateEolChar.bind(this); + }; + + (function() { + + oop.implement(this, EventEmitter); + + this.EOF_CHAR = "\xB6"; + this.EOL_CHAR_LF = "\xAC"; + this.EOL_CHAR_CRLF = "\xa4"; + this.EOL_CHAR = this.EOL_CHAR_LF; + this.TAB_CHAR = "\u2014"; //"\u21E5"; + this.SPACE_CHAR = "\xB7"; + this.$padding = 0; + + this.$updateEolChar = function() { + var EOL_CHAR = this.session.doc.getNewLineCharacter() == "\n" + ? this.EOL_CHAR_LF + : this.EOL_CHAR_CRLF; + if (this.EOL_CHAR != EOL_CHAR) { + this.EOL_CHAR = EOL_CHAR; + return true; + } + }; + + this.setPadding = function(padding) { + this.$padding = padding; + this.element.style.padding = "0 " + padding + "px"; + }; + + this.getLineHeight = function() { + return this.$fontMetrics.$characterSize.height || 0; + }; + + this.getCharacterWidth = function() { + return this.$fontMetrics.$characterSize.width || 0; + }; + + this.$setFontMetrics = function(measure) { + this.$fontMetrics = measure; + this.$fontMetrics.on("changeCharacterSize", function(e) { + this._signal("changeCharacterSize", e); + }.bind(this)); + this.$pollSizeChanges(); + }; + + this.checkForSizeChanges = function() { + this.$fontMetrics.checkForSizeChanges(); + }; + this.$pollSizeChanges = function() { + return this.$pollSizeChangesTimer = this.$fontMetrics.$pollSizeChanges(); + }; + this.setSession = function(session) { + this.session = session; + if (session) + this.$computeTabString(); + }; + + this.showInvisibles = false; + this.setShowInvisibles = function(showInvisibles) { + if (this.showInvisibles == showInvisibles) + return false; + + this.showInvisibles = showInvisibles; + this.$computeTabString(); + return true; + }; + + this.displayIndentGuides = true; + this.setDisplayIndentGuides = function(display) { + if (this.displayIndentGuides == display) + return false; + + this.displayIndentGuides = display; + this.$computeTabString(); + return true; + }; + + this.$tabStrings = []; + this.onChangeTabSize = + this.$computeTabString = function() { + var tabSize = this.session.getTabSize(); + this.tabSize = tabSize; + var tabStr = this.$tabStrings = [0]; + for (var i = 1; i < tabSize + 1; i++) { + if (this.showInvisibles) { + tabStr.push("" + + lang.stringRepeat(this.TAB_CHAR, i) + + ""); + } else { + tabStr.push(lang.stringRepeat(" ", i)); + } + } + if (this.displayIndentGuides) { + this.$indentGuideRe = /\s\S| \t|\t |\s$/; + var className = "ace_indent-guide"; + var spaceClass = ""; + var tabClass = ""; + if (this.showInvisibles) { + className += " ace_invisible"; + spaceClass = " ace_invisible_space"; + tabClass = " ace_invisible_tab"; + var spaceContent = lang.stringRepeat(this.SPACE_CHAR, this.tabSize); + var tabContent = lang.stringRepeat(this.TAB_CHAR, this.tabSize); + } else{ + var spaceContent = lang.stringRepeat(" ", this.tabSize); + var tabContent = spaceContent; + } + + this.$tabStrings[" "] = "" + spaceContent + ""; + this.$tabStrings["\t"] = "" + tabContent + ""; + } + }; + + this.updateLines = function(config, firstRow, lastRow) { + if (this.config.lastRow != config.lastRow || + this.config.firstRow != config.firstRow) { + this.scrollLines(config); + } + this.config = config; + + var first = Math.max(firstRow, config.firstRow); + var last = Math.min(lastRow, config.lastRow); + + var lineElements = this.element.childNodes; + var lineElementsIdx = 0; + + for (var row = config.firstRow; row < first; row++) { + var foldLine = this.session.getFoldLine(row); + if (foldLine) { + if (foldLine.containsRow(first)) { + first = foldLine.start.row; + break; + } else { + row = foldLine.end.row; + } + } + lineElementsIdx ++; + } + + var row = first; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row :Infinity; + } + if (row > last) + break; + + var lineElement = lineElements[lineElementsIdx++]; + if (lineElement) { + var html = []; + this.$renderLine( + html, row, !this.$useLineGroups(), row == foldStart ? foldLine : false + ); + lineElement.style.height = config.lineHeight * this.session.getRowLength(row) + "px"; + lineElement.innerHTML = html.join(""); + } + row++; + } + }; + + this.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + + if (!oldConfig || oldConfig.lastRow < config.firstRow) + return this.update(config); + + if (config.lastRow < oldConfig.firstRow) + return this.update(config); + + var el = this.element; + if (oldConfig.firstRow < config.firstRow) + for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--) + el.removeChild(el.firstChild); + + if (oldConfig.lastRow > config.lastRow) + for (var row=this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row>0; row--) + el.removeChild(el.lastChild); + + if (config.firstRow < oldConfig.firstRow) { + var fragment = this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1); + if (el.firstChild) + el.insertBefore(fragment, el.firstChild); + else + el.appendChild(fragment); + } + + if (config.lastRow > oldConfig.lastRow) { + var fragment = this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow); + el.appendChild(fragment); + } + }; + + this.$renderLinesFragment = function(config, firstRow, lastRow) { + var fragment = this.element.ownerDocument.createDocumentFragment(); + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) + break; + + var container = dom.createElement("div"); + + var html = []; + this.$renderLine(html, row, false, row == foldStart ? foldLine : false); + container.innerHTML = html.join(""); + if (this.$useLineGroups()) { + container.className = 'ace_line_group'; + fragment.appendChild(container); + container.style.height = config.lineHeight * this.session.getRowLength(row) + "px"; + + } else { + while(container.firstChild) + fragment.appendChild(container.firstChild); + } + + row++; + } + return fragment; + }; + + this.update = function(config) { + this.config = config; + + var html = []; + var firstRow = config.firstRow, lastRow = config.lastRow; + + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row :Infinity; + } + if (row > lastRow) + break; + + if (this.$useLineGroups()) + html.push("
      "); + + this.$renderLine(html, row, false, row == foldStart ? foldLine : false); + + if (this.$useLineGroups()) + html.push("
      "); // end the line group + + row++; + } + this.element.innerHTML = html.join(""); + }; + + this.$textToken = { + "text": true, + "rparen": true, + "lparen": true + }; + + this.$renderToken = function(stringBuilder, screenColumn, token, value) { + var self = this; + var replaceReg = /\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g; + var replaceFunc = function(c, a, b, tabIdx, idx4) { + if (a) { + return self.showInvisibles + ? "" + lang.stringRepeat(self.SPACE_CHAR, c.length) + "" + : c; + } else if (c == "&") { + return "&"; + } else if (c == "<") { + return "<"; + } else if (c == ">") { + return ">"; + } else if (c == "\t") { + var tabSize = self.session.getScreenTabSize(screenColumn + tabIdx); + screenColumn += tabSize - 1; + return self.$tabStrings[tabSize]; + } else if (c == "\u3000") { + var classToUse = self.showInvisibles ? "ace_cjk ace_invisible ace_invisible_space" : "ace_cjk"; + var space = self.showInvisibles ? self.SPACE_CHAR : ""; + screenColumn += 1; + return "" + space + ""; + } else if (b) { + return "" + self.SPACE_CHAR + ""; + } else { + screenColumn += 1; + return "" + c + ""; + } + }; + + var output = value.replace(replaceReg, replaceFunc); + + if (!this.$textToken[token.type]) { + var classes = "ace_" + token.type.replace(/\./g, " ace_"); + var style = ""; + if (token.type == "fold") + style = " style='width:" + (token.value.length * this.config.characterWidth) + "px;' "; + stringBuilder.push("", output, ""); + } + else { + stringBuilder.push(output); + } + return screenColumn + value.length; + }; + + this.renderIndentGuide = function(stringBuilder, value, max) { + var cols = value.search(this.$indentGuideRe); + if (cols <= 0 || cols >= max) + return value; + if (value[0] == " ") { + cols -= cols % this.tabSize; + stringBuilder.push(lang.stringRepeat(this.$tabStrings[" "], cols/this.tabSize)); + return value.substr(cols); + } else if (value[0] == "\t") { + stringBuilder.push(lang.stringRepeat(this.$tabStrings["\t"], cols)); + return value.substr(cols); + } + return value; + }; + + this.$renderWrappedLine = function(stringBuilder, tokens, splits, onlyContents) { + var chars = 0; + var split = 0; + var splitChars = splits[0]; + var screenColumn = 0; + + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + var value = token.value; + if (i == 0 && this.displayIndentGuides) { + chars = value.length; + value = this.renderIndentGuide(stringBuilder, value, splitChars); + if (!value) + continue; + chars -= value.length; + } + + if (chars + value.length < splitChars) { + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); + chars += value.length; + } else { + while (chars + value.length >= splitChars) { + screenColumn = this.$renderToken( + stringBuilder, screenColumn, + token, value.substring(0, splitChars - chars) + ); + value = value.substring(splitChars - chars); + chars = splitChars; + + if (!onlyContents) { + stringBuilder.push("
      ", + "
      " + ); + } + + stringBuilder.push(lang.stringRepeat("\xa0", splits.indent)); + + split ++; + screenColumn = 0; + splitChars = splits[split] || Number.MAX_VALUE; + } + if (value.length != 0) { + chars += value.length; + screenColumn = this.$renderToken( + stringBuilder, screenColumn, token, value + ); + } + } + } + }; + + this.$renderSimpleLine = function(stringBuilder, tokens) { + var screenColumn = 0; + var token = tokens[0]; + var value = token.value; + if (this.displayIndentGuides) + value = this.renderIndentGuide(stringBuilder, value); + if (value) + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); + for (var i = 1; i < tokens.length; i++) { + token = tokens[i]; + value = token.value; + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); + } + }; + this.$renderLine = function(stringBuilder, row, onlyContents, foldLine) { + if (!foldLine && foldLine != false) + foldLine = this.session.getFoldLine(row); + + if (foldLine) + var tokens = this.$getFoldLineTokens(row, foldLine); + else + var tokens = this.session.getTokens(row); + + + if (!onlyContents) { + stringBuilder.push( + "
      " + ); + } + + if (tokens.length) { + var splits = this.session.getRowSplitData(row); + if (splits && splits.length) + this.$renderWrappedLine(stringBuilder, tokens, splits, onlyContents); + else + this.$renderSimpleLine(stringBuilder, tokens); + } + + if (this.showInvisibles) { + if (foldLine) + row = foldLine.end.row; + + stringBuilder.push( + "", + row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR, + "" + ); + } + if (!onlyContents) + stringBuilder.push("
      "); + }; + + this.$getFoldLineTokens = function(row, foldLine) { + var session = this.session; + var renderTokens = []; + + function addTokens(tokens, from, to) { + var idx = 0, col = 0; + while ((col + tokens[idx].value.length) < from) { + col += tokens[idx].value.length; + idx++; + + if (idx == tokens.length) + return; + } + if (col != from) { + var value = tokens[idx].value.substring(from - col); + if (value.length > (to - from)) + value = value.substring(0, to - from); + + renderTokens.push({ + type: tokens[idx].type, + value: value + }); + + col = from + value.length; + idx += 1; + } + + while (col < to && idx < tokens.length) { + var value = tokens[idx].value; + if (value.length + col > to) { + renderTokens.push({ + type: tokens[idx].type, + value: value.substring(0, to - col) + }); + } else + renderTokens.push(tokens[idx]); + col += value.length; + idx += 1; + } + } + + var tokens = session.getTokens(row); + foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) { + if (placeholder != null) { + renderTokens.push({ + type: "fold", + value: placeholder + }); + } else { + if (isNewRow) + tokens = session.getTokens(row); + + if (tokens.length) + addTokens(tokens, lastColumn, column); + } + }, foldLine.end.row, this.session.getLine(foldLine.end.row).length); + + return renderTokens; + }; + + this.$useLineGroups = function() { + return this.session.getUseWrapMode(); + }; + + this.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + if (this.$measureNode) + this.$measureNode.parentNode.removeChild(this.$measureNode); + delete this.$measureNode; + }; + + }).call(Text.prototype); + + exports.Text = Text; + + }); + + ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) { + "use strict"; + + var dom = acequire("../lib/dom"); + var isIE8; + + var Cursor = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_cursor-layer"; + parentEl.appendChild(this.element); + + if (isIE8 === undefined) + isIE8 = !("opacity" in this.element.style); + + this.isVisible = false; + this.isBlinking = true; + this.blinkInterval = 1000; + this.smoothBlinking = false; + + this.cursors = []; + this.cursor = this.addCursor(); + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.$updateCursors = (isIE8 + ? this.$updateVisibility + : this.$updateOpacity).bind(this); + }; + + (function() { + + this.$updateVisibility = function(val) { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + cursors[i].style.visibility = val ? "" : "hidden"; + }; + this.$updateOpacity = function(val) { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + cursors[i].style.opacity = val ? "" : "0"; + }; + + + this.$padding = 0; + this.setPadding = function(padding) { + this.$padding = padding; + }; + + this.setSession = function(session) { + this.session = session; + }; + + this.setBlinking = function(blinking) { + if (blinking != this.isBlinking){ + this.isBlinking = blinking; + this.restartTimer(); + } + }; + + this.setBlinkInterval = function(blinkInterval) { + if (blinkInterval != this.blinkInterval){ + this.blinkInterval = blinkInterval; + this.restartTimer(); + } + }; + + this.setSmoothBlinking = function(smoothBlinking) { + if (smoothBlinking != this.smoothBlinking && !isIE8) { + this.smoothBlinking = smoothBlinking; + dom.setCssClass(this.element, "ace_smooth-blinking", smoothBlinking); + this.$updateCursors(true); + this.$updateCursors = (this.$updateOpacity).bind(this); + this.restartTimer(); + } + }; + + this.addCursor = function() { + var el = dom.createElement("div"); + el.className = "ace_cursor"; + this.element.appendChild(el); + this.cursors.push(el); + return el; + }; + + this.removeCursor = function() { + if (this.cursors.length > 1) { + var el = this.cursors.pop(); + el.parentNode.removeChild(el); + return el; + } + }; + + this.hideCursor = function() { + this.isVisible = false; + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.showCursor = function() { + this.isVisible = true; + dom.removeCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.restartTimer = function() { + var update = this.$updateCursors; + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + if (this.smoothBlinking) { + dom.removeCssClass(this.element, "ace_smooth-blinking"); + } + + update(true); + + if (!this.isBlinking || !this.blinkInterval || !this.isVisible) + return; + + if (this.smoothBlinking) { + setTimeout(function(){ + dom.addCssClass(this.element, "ace_smooth-blinking"); + }.bind(this)); + } + + var blink = function(){ + this.timeoutId = setTimeout(function() { + update(false); + }, 0.6 * this.blinkInterval); + }.bind(this); + + this.intervalId = setInterval(function() { + update(true); + blink(); + }, this.blinkInterval); + + blink(); + }; + + this.getPixelPosition = function(position, onScreen) { + if (!this.config || !this.session) + return {left : 0, top : 0}; + + if (!position) + position = this.session.selection.getCursor(); + var pos = this.session.documentToScreenPosition(position); + var cursorLeft = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, position.row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : pos.column * this.config.characterWidth); + + var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) * + this.config.lineHeight; + + return {left : cursorLeft, top : cursorTop}; + }; + + this.update = function(config) { + this.config = config; + + var selections = this.session.$selectionMarkers; + var i = 0, cursorIndex = 0; + + if (selections === undefined || selections.length === 0){ + selections = [{cursor: null}]; + } + + for (var i = 0, n = selections.length; i < n; i++) { + var pixelPos = this.getPixelPosition(selections[i].cursor, true); + if ((pixelPos.top > config.height + config.offset || + pixelPos.top < 0) && i > 1) { + continue; + } + + var style = (this.cursors[cursorIndex++] || this.addCursor()).style; + + if (!this.drawCursor) { + style.left = pixelPos.left + "px"; + style.top = pixelPos.top + "px"; + style.width = config.characterWidth + "px"; + style.height = config.lineHeight + "px"; + } else { + this.drawCursor(style, pixelPos, config, selections[i], this.session); + } + } + while (this.cursors.length > cursorIndex) + this.removeCursor(); + + var overwrite = this.session.getOverwrite(); + this.$setOverwrite(overwrite); + this.$pixelPos = pixelPos; + this.restartTimer(); + }; + + this.drawCursor = null; + + this.$setOverwrite = function(overwrite) { + if (overwrite != this.overwrite) { + this.overwrite = overwrite; + if (overwrite) + dom.addCssClass(this.element, "ace_overwrite-cursors"); + else + dom.removeCssClass(this.element, "ace_overwrite-cursors"); + } + }; + + this.destroy = function() { + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + }; + + }).call(Cursor.prototype); + + exports.Cursor = Cursor; + + }); + + ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var dom = acequire("./lib/dom"); + var event = acequire("./lib/event"); + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var MAX_SCROLL_H = 0x8000; + var ScrollBar = function(parent) { + this.element = dom.createElement("div"); + this.element.className = "ace_scrollbar ace_scrollbar" + this.classSuffix; + + this.inner = dom.createElement("div"); + this.inner.className = "ace_scrollbar-inner"; + this.element.appendChild(this.inner); + + parent.appendChild(this.element); + + this.setVisible(false); + this.skipEvent = false; + + event.addListener(this.element, "scroll", this.onScroll.bind(this)); + event.addListener(this.element, "mousedown", event.preventDefault); + }; + + (function() { + oop.implement(this, EventEmitter); + + this.setVisible = function(isVisible) { + this.element.style.display = isVisible ? "" : "none"; + this.isVisible = isVisible; + this.coeff = 1; + }; + }).call(ScrollBar.prototype); + var VScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollTop = 0; + this.scrollHeight = 0; + renderer.$scrollbarWidth = + this.width = dom.scrollbarWidth(parent.ownerDocument); + this.inner.style.width = + this.element.style.width = (this.width || 15) + 5 + "px"; + this.$minWidth = 0; + }; + + oop.inherits(VScrollBar, ScrollBar); + + (function() { + + this.classSuffix = '-v'; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollTop = this.element.scrollTop; + if (this.coeff != 1) { + var h = this.element.clientHeight / this.scrollHeight; + this.scrollTop = this.scrollTop * (1 - h) / (this.coeff - h); + } + this._emit("scroll", {data: this.scrollTop}); + } + this.skipEvent = false; + }; + this.getWidth = function() { + return Math.max(this.isVisible ? this.width : 0, this.$minWidth || 0); + }; + this.setHeight = function(height) { + this.element.style.height = height + "px"; + }; + this.setInnerHeight = + this.setScrollHeight = function(height) { + this.scrollHeight = height; + if (height > MAX_SCROLL_H) { + this.coeff = MAX_SCROLL_H / height; + height = MAX_SCROLL_H; + } else if (this.coeff != 1) { + this.coeff = 1; + } + this.inner.style.height = height + "px"; + }; + this.setScrollTop = function(scrollTop) { + if (this.scrollTop != scrollTop) { + this.skipEvent = true; + this.scrollTop = scrollTop; + this.element.scrollTop = scrollTop * this.coeff; + } + }; + + }).call(VScrollBar.prototype); + var HScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollLeft = 0; + this.height = renderer.$scrollbarWidth; + this.inner.style.height = + this.element.style.height = (this.height || 15) + 5 + "px"; + }; + + oop.inherits(HScrollBar, ScrollBar); + + (function() { + + this.classSuffix = '-h'; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollLeft = this.element.scrollLeft; + this._emit("scroll", {data: this.scrollLeft}); + } + this.skipEvent = false; + }; + this.getHeight = function() { + return this.isVisible ? this.height : 0; + }; + this.setWidth = function(width) { + this.element.style.width = width + "px"; + }; + this.setInnerWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollLeft = function(scrollLeft) { + if (this.scrollLeft != scrollLeft) { + this.skipEvent = true; + this.scrollLeft = this.element.scrollLeft = scrollLeft; + } + }; + + }).call(HScrollBar.prototype); + + + exports.ScrollBar = VScrollBar; // backward compatibility + exports.ScrollBarV = VScrollBar; // backward compatibility + exports.ScrollBarH = HScrollBar; // backward compatibility + + exports.VScrollBar = VScrollBar; + exports.HScrollBar = HScrollBar; + }); + + ace.define("ace/renderloop",["require","exports","module","ace/lib/event"], function(acequire, exports, module) { + "use strict"; + + var event = acequire("./lib/event"); + + + var RenderLoop = function(onRender, win) { + this.onRender = onRender; + this.pending = false; + this.changes = 0; + this.window = win || window; + }; + + (function() { + + + this.schedule = function(change) { + this.changes = this.changes | change; + if (!this.pending && this.changes) { + this.pending = true; + var _self = this; + event.nextFrame(function() { + _self.pending = false; + var changes; + while (changes = _self.changes) { + _self.changes = 0; + _self.onRender(changes); + } + }, this.window); + } + }; + + }).call(RenderLoop.prototype); + + exports.RenderLoop = RenderLoop; + }); + + ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"], function(acequire, exports, module) { + + var oop = acequire("../lib/oop"); + var dom = acequire("../lib/dom"); + var lang = acequire("../lib/lang"); + var useragent = acequire("../lib/useragent"); + var EventEmitter = acequire("../lib/event_emitter").EventEmitter; + + var CHAR_COUNT = 0; + + var FontMetrics = exports.FontMetrics = function(parentEl) { + this.el = dom.createElement("div"); + this.$setMeasureNodeStyles(this.el.style, true); + + this.$main = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$main.style); + + this.$measureNode = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$measureNode.style); + + + this.el.appendChild(this.$main); + this.el.appendChild(this.$measureNode); + parentEl.appendChild(this.el); + + if (!CHAR_COUNT) + this.$testFractionalRect(); + this.$measureNode.innerHTML = lang.stringRepeat("X", CHAR_COUNT); + + this.$characterSize = {width: 0, height: 0}; + this.checkForSizeChanges(); + }; + + (function() { + + oop.implement(this, EventEmitter); + + this.$characterSize = {width: 0, height: 0}; + + this.$testFractionalRect = function() { + var el = dom.createElement("div"); + this.$setMeasureNodeStyles(el.style); + el.style.width = "0.2px"; + document.documentElement.appendChild(el); + var w = el.getBoundingClientRect().width; + if (w > 0 && w < 1) + CHAR_COUNT = 50; + else + CHAR_COUNT = 100; + el.parentNode.removeChild(el); + }; + + this.$setMeasureNodeStyles = function(style, isRoot) { + style.width = style.height = "auto"; + style.left = style.top = "0px"; + style.visibility = "hidden"; + style.position = "absolute"; + style.whiteSpace = "pre"; + + if (useragent.isIE < 8) { + style["font-family"] = "inherit"; + } else { + style.font = "inherit"; + } + style.overflow = isRoot ? "hidden" : "visible"; + }; + + this.checkForSizeChanges = function() { + var size = this.$measureSizes(); + if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) { + this.$measureNode.style.fontWeight = "bold"; + var boldSize = this.$measureSizes(); + this.$measureNode.style.fontWeight = ""; + this.$characterSize = size; + this.charSizes = Object.create(null); + this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height; + this._emit("changeCharacterSize", {data: size}); + } + }; + + this.$pollSizeChanges = function() { + if (this.$pollSizeChangesTimer) + return this.$pollSizeChangesTimer; + var self = this; + return this.$pollSizeChangesTimer = setInterval(function() { + self.checkForSizeChanges(); + }, 500); + }; + + this.setPolling = function(val) { + if (val) { + this.$pollSizeChanges(); + } else if (this.$pollSizeChangesTimer) { + clearInterval(this.$pollSizeChangesTimer); + this.$pollSizeChangesTimer = 0; + } + }; + + this.$measureSizes = function() { + if (CHAR_COUNT === 50) { + var rect = null; + try { + rect = this.$measureNode.getBoundingClientRect(); + } catch(e) { + rect = {width: 0, height:0 }; + } + var size = { + height: rect.height, + width: rect.width / CHAR_COUNT + }; + } else { + var size = { + height: this.$measureNode.clientHeight, + width: this.$measureNode.clientWidth / CHAR_COUNT + }; + } + if (size.width === 0 || size.height === 0) + return null; + return size; + }; + + this.$measureCharWidth = function(ch) { + this.$main.innerHTML = lang.stringRepeat(ch, CHAR_COUNT); + var rect = this.$main.getBoundingClientRect(); + return rect.width / CHAR_COUNT; + }; + + this.getCharacterWidth = function(ch) { + var w = this.charSizes[ch]; + if (w === undefined) { + w = this.charSizes[ch] = this.$measureCharWidth(ch) / this.$characterSize.width; + } + return w; + }; + + this.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + if (this.el && this.el.parentNode) + this.el.parentNode.removeChild(this.el); + }; + + }).call(FontMetrics.prototype); + + }); + + ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var dom = acequire("./lib/dom"); + var config = acequire("./config"); + var useragent = acequire("./lib/useragent"); + var GutterLayer = acequire("./layer/gutter").Gutter; + var MarkerLayer = acequire("./layer/marker").Marker; + var TextLayer = acequire("./layer/text").Text; + var CursorLayer = acequire("./layer/cursor").Cursor; + var HScrollBar = acequire("./scrollbar").HScrollBar; + var VScrollBar = acequire("./scrollbar").VScrollBar; + var RenderLoop = acequire("./renderloop").RenderLoop; + var FontMetrics = acequire("./layer/font_metrics").FontMetrics; + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var editorCss = ".ace_editor {\ + position: relative;\ + overflow: hidden;\ + font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;\ + direction: ltr;\ + text-align: left;\ + -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\ + }\ + .ace_scroller {\ + position: absolute;\ + overflow: hidden;\ + top: 0;\ + bottom: 0;\ + background-color: inherit;\ + -ms-user-select: none;\ + -moz-user-select: none;\ + -webkit-user-select: none;\ + user-select: none;\ + cursor: text;\ + }\ + .ace_content {\ + position: absolute;\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + min-width: 100%;\ + }\ + .ace_dragging .ace_scroller:before{\ + position: absolute;\ + top: 0;\ + left: 0;\ + right: 0;\ + bottom: 0;\ + content: '';\ + background: rgba(250, 250, 250, 0.01);\ + z-index: 1000;\ + }\ + .ace_dragging.ace_dark .ace_scroller:before{\ + background: rgba(0, 0, 0, 0.01);\ + }\ + .ace_selecting, .ace_selecting * {\ + cursor: text !important;\ + }\ + .ace_gutter {\ + position: absolute;\ + overflow : hidden;\ + width: auto;\ + top: 0;\ + bottom: 0;\ + left: 0;\ + cursor: default;\ + z-index: 4;\ + -ms-user-select: none;\ + -moz-user-select: none;\ + -webkit-user-select: none;\ + user-select: none;\ + }\ + .ace_gutter-active-line {\ + position: absolute;\ + left: 0;\ + right: 0;\ + }\ + .ace_scroller.ace_scroll-left {\ + box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;\ + }\ + .ace_gutter-cell {\ + padding-left: 19px;\ + padding-right: 6px;\ + background-repeat: no-repeat;\ + }\ + .ace_gutter-cell.ace_error {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==\");\ + background-repeat: no-repeat;\ + background-position: 2px center;\ + }\ + .ace_gutter-cell.ace_warning {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==\");\ + background-position: 2px center;\ + }\ + .ace_gutter-cell.ace_info {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=\");\ + background-position: 2px center;\ + }\ + .ace_dark .ace_gutter-cell.ace_info {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC\");\ + }\ + .ace_scrollbar {\ + position: absolute;\ + right: 0;\ + bottom: 0;\ + z-index: 6;\ + }\ + .ace_scrollbar-inner {\ + position: absolute;\ + cursor: text;\ + left: 0;\ + top: 0;\ + }\ + .ace_scrollbar-v{\ + overflow-x: hidden;\ + overflow-y: scroll;\ + top: 0;\ + }\ + .ace_scrollbar-h {\ + overflow-x: scroll;\ + overflow-y: hidden;\ + left: 0;\ + }\ + .ace_print-margin {\ + position: absolute;\ + height: 100%;\ + }\ + .ace_text-input {\ + position: absolute;\ + z-index: 0;\ + width: 0.5em;\ + height: 1em;\ + opacity: 0;\ + background: transparent;\ + -moz-appearance: none;\ + appearance: none;\ + border: none;\ + resize: none;\ + outline: none;\ + overflow: hidden;\ + font: inherit;\ + padding: 0 1px;\ + margin: 0 -1px;\ + text-indent: -1em;\ + -ms-user-select: text;\ + -moz-user-select: text;\ + -webkit-user-select: text;\ + user-select: text;\ + white-space: pre!important;\ + }\ + .ace_text-input.ace_composition {\ + background: inherit;\ + color: inherit;\ + z-index: 1000;\ + opacity: 1;\ + text-indent: 0;\ + }\ + .ace_layer {\ + z-index: 1;\ + position: absolute;\ + overflow: hidden;\ + word-wrap: normal;\ + white-space: pre;\ + height: 100%;\ + width: 100%;\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + pointer-events: none;\ + }\ + .ace_gutter-layer {\ + position: relative;\ + width: auto;\ + text-align: right;\ + pointer-events: auto;\ + }\ + .ace_text-layer {\ + font: inherit !important;\ + }\ + .ace_cjk {\ + display: inline-block;\ + text-align: center;\ + }\ + .ace_cursor-layer {\ + z-index: 4;\ + }\ + .ace_cursor {\ + z-index: 4;\ + position: absolute;\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + border-left: 2px solid;\ + transform: translatez(0);\ + }\ + .ace_multiselect .ace_cursor {\ + border-left-width: 1px;\ + }\ + .ace_slim-cursors .ace_cursor {\ + border-left-width: 1px;\ + }\ + .ace_overwrite-cursors .ace_cursor {\ + border-left-width: 0;\ + border-bottom: 1px solid;\ + }\ + .ace_hidden-cursors .ace_cursor {\ + opacity: 0.2;\ + }\ + .ace_smooth-blinking .ace_cursor {\ + -webkit-transition: opacity 0.18s;\ + transition: opacity 0.18s;\ + }\ + .ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\ + position: absolute;\ + z-index: 3;\ + }\ + .ace_marker-layer .ace_selection {\ + position: absolute;\ + z-index: 5;\ + }\ + .ace_marker-layer .ace_bracket {\ + position: absolute;\ + z-index: 6;\ + }\ + .ace_marker-layer .ace_active-line {\ + position: absolute;\ + z-index: 2;\ + }\ + .ace_marker-layer .ace_selected-word {\ + position: absolute;\ + z-index: 4;\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + }\ + .ace_line .ace_fold {\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + display: inline-block;\ + height: 11px;\ + margin-top: -2px;\ + vertical-align: middle;\ + background-image:\ + url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\ + url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=\");\ + background-repeat: no-repeat, repeat-x;\ + background-position: center center, top left;\ + color: transparent;\ + border: 1px solid black;\ + border-radius: 2px;\ + cursor: pointer;\ + pointer-events: auto;\ + }\ + .ace_dark .ace_fold {\ + }\ + .ace_fold:hover{\ + background-image:\ + url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\ + url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC\");\ + }\ + .ace_tooltip {\ + background-color: #FFF;\ + background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));\ + background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));\ + border: 1px solid gray;\ + border-radius: 1px;\ + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\ + color: black;\ + max-width: 100%;\ + padding: 3px 4px;\ + position: fixed;\ + z-index: 999999;\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + cursor: default;\ + white-space: pre;\ + word-wrap: break-word;\ + line-height: normal;\ + font-style: normal;\ + font-weight: normal;\ + letter-spacing: normal;\ + pointer-events: none;\ + }\ + .ace_folding-enabled > .ace_gutter-cell {\ + padding-right: 13px;\ + }\ + .ace_fold-widget {\ + -moz-box-sizing: border-box;\ + -webkit-box-sizing: border-box;\ + box-sizing: border-box;\ + margin: 0 -12px 0 1px;\ + display: none;\ + width: 11px;\ + vertical-align: top;\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==\");\ + background-repeat: no-repeat;\ + background-position: center;\ + border-radius: 3px;\ + border: 1px solid transparent;\ + cursor: pointer;\ + }\ + .ace_folding-enabled .ace_fold-widget {\ + display: inline-block; \ + }\ + .ace_fold-widget.ace_end {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==\");\ + }\ + .ace_fold-widget.ace_closed {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==\");\ + }\ + .ace_fold-widget:hover {\ + border: 1px solid rgba(0, 0, 0, 0.3);\ + background-color: rgba(255, 255, 255, 0.2);\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\ + }\ + .ace_fold-widget:active {\ + border: 1px solid rgba(0, 0, 0, 0.4);\ + background-color: rgba(0, 0, 0, 0.05);\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\ + }\ + .ace_dark .ace_fold-widget {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");\ + }\ + .ace_dark .ace_fold-widget.ace_end {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");\ + }\ + .ace_dark .ace_fold-widget.ace_closed {\ + background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");\ + }\ + .ace_dark .ace_fold-widget:hover {\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ + background-color: rgba(255, 255, 255, 0.1);\ + }\ + .ace_dark .ace_fold-widget:active {\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ + }\ + .ace_fold-widget.ace_invalid {\ + background-color: #FFB4B4;\ + border-color: #DE5555;\ + }\ + .ace_fade-fold-widgets .ace_fold-widget {\ + -webkit-transition: opacity 0.4s ease 0.05s;\ + transition: opacity 0.4s ease 0.05s;\ + opacity: 0;\ + }\ + .ace_fade-fold-widgets:hover .ace_fold-widget {\ + -webkit-transition: opacity 0.05s ease 0.05s;\ + transition: opacity 0.05s ease 0.05s;\ + opacity:1;\ + }\ + .ace_underline {\ + text-decoration: underline;\ + }\ + .ace_bold {\ + font-weight: bold;\ + }\ + .ace_nobold .ace_bold {\ + font-weight: normal;\ + }\ + .ace_italic {\ + font-style: italic;\ + }\ + .ace_error-marker {\ + background-color: rgba(255, 0, 0,0.2);\ + position: absolute;\ + z-index: 9;\ + }\ + .ace_highlight-marker {\ + background-color: rgba(255, 255, 0,0.2);\ + position: absolute;\ + z-index: 8;\ + }\ + .ace_br1 {border-top-left-radius : 3px;}\ + .ace_br2 {border-top-right-radius : 3px;}\ + .ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}\ + .ace_br4 {border-bottom-right-radius: 3px;}\ + .ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}\ + .ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}\ + .ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}\ + .ace_br8 {border-bottom-left-radius : 3px;}\ + .ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}\ + .ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}\ + .ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_text-input-ios {\ + position: absolute !important;\ + top: -100000px !important;\ + left: -100000px !important;\ + }\ + "; + + dom.importCssString(editorCss, "ace_editor.css"); + + var VirtualRenderer = function(container, theme) { + var _self = this; + + this.container = container || dom.createElement("div"); + this.$keepTextAreaAtCursor = !useragent.isOldIE; + + dom.addCssClass(this.container, "ace_editor"); + + this.setTheme(theme); + + this.$gutter = dom.createElement("div"); + this.$gutter.className = "ace_gutter"; + this.container.appendChild(this.$gutter); + this.$gutter.setAttribute("aria-hidden", true); + + this.scroller = dom.createElement("div"); + this.scroller.className = "ace_scroller"; + this.container.appendChild(this.scroller); + + this.content = dom.createElement("div"); + this.content.className = "ace_content"; + this.scroller.appendChild(this.content); + + this.$gutterLayer = new GutterLayer(this.$gutter); + this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this)); + + this.$markerBack = new MarkerLayer(this.content); + + var textLayer = this.$textLayer = new TextLayer(this.content); + this.canvas = textLayer.element; + + this.$markerFront = new MarkerLayer(this.content); + + this.$cursorLayer = new CursorLayer(this.content); + this.$horizScroll = false; + this.$vScroll = false; + + this.scrollBar = + this.scrollBarV = new VScrollBar(this.container, this); + this.scrollBarH = new HScrollBar(this.container, this); + this.scrollBarV.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + + this.scrollTop = 0; + this.scrollLeft = 0; + + this.cursorPos = { + row : 0, + column : 0 + }; + + this.$fontMetrics = new FontMetrics(this.container); + this.$textLayer.$setFontMetrics(this.$fontMetrics); + this.$textLayer.addEventListener("changeCharacterSize", function(e) { + _self.updateCharacterSize(); + _self.onResize(true, _self.gutterWidth, _self.$size.width, _self.$size.height); + _self._signal("changeCharacterSize", e); + }); + + this.$size = { + width: 0, + height: 0, + scrollerHeight: 0, + scrollerWidth: 0, + $dirty: true + }; + + this.layerConfig = { + width : 1, + padding : 0, + firstRow : 0, + firstRowScreen: 0, + lastRow : 0, + lineHeight : 0, + characterWidth : 0, + minHeight : 1, + maxHeight : 1, + offset : 0, + height : 1, + gutterOffset: 1 + }; + + this.scrollMargin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + + this.$loop = new RenderLoop( + this.$renderChanges.bind(this), + this.container.ownerDocument.defaultView + ); + this.$loop.schedule(this.CHANGE_FULL); + + this.updateCharacterSize(); + this.setPadding(4); + config.resetOptions(this); + config._emit("renderer", this); + }; + + (function() { + + this.CHANGE_CURSOR = 1; + this.CHANGE_MARKER = 2; + this.CHANGE_GUTTER = 4; + this.CHANGE_SCROLL = 8; + this.CHANGE_LINES = 16; + this.CHANGE_TEXT = 32; + this.CHANGE_SIZE = 64; + this.CHANGE_MARKER_BACK = 128; + this.CHANGE_MARKER_FRONT = 256; + this.CHANGE_FULL = 512; + this.CHANGE_H_SCROLL = 1024; + + oop.implement(this, EventEmitter); + + this.updateCharacterSize = function() { + if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) { + this.$allowBoldFonts = this.$textLayer.allowBoldFonts; + this.setStyle("ace_nobold", !this.$allowBoldFonts); + } + + this.layerConfig.characterWidth = + this.characterWidth = this.$textLayer.getCharacterWidth(); + this.layerConfig.lineHeight = + this.lineHeight = this.$textLayer.getLineHeight(); + this.$updatePrintMargin(); + }; + this.setSession = function(session) { + if (this.session) + this.session.doc.off("changeNewLineMode", this.onChangeNewLineMode); + + this.session = session; + if (session && this.scrollMargin.top && session.getScrollTop() <= 0) + session.setScrollTop(-this.scrollMargin.top); + + this.$cursorLayer.setSession(session); + this.$markerBack.setSession(session); + this.$markerFront.setSession(session); + this.$gutterLayer.setSession(session); + this.$textLayer.setSession(session); + if (!session) + return; + + this.$loop.schedule(this.CHANGE_FULL); + this.session.$setFontMetrics(this.$fontMetrics); + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + + this.onChangeNewLineMode = this.onChangeNewLineMode.bind(this); + this.onChangeNewLineMode(); + this.session.doc.on("changeNewLineMode", this.onChangeNewLineMode); + }; + this.updateLines = function(firstRow, lastRow, force) { + if (lastRow === undefined) + lastRow = Infinity; + + if (!this.$changedLines) { + this.$changedLines = { + firstRow: firstRow, + lastRow: lastRow + }; + } + else { + if (this.$changedLines.firstRow > firstRow) + this.$changedLines.firstRow = firstRow; + + if (this.$changedLines.lastRow < lastRow) + this.$changedLines.lastRow = lastRow; + } + if (this.$changedLines.lastRow < this.layerConfig.firstRow) { + if (force) + this.$changedLines.lastRow = this.layerConfig.lastRow; + else + return; + } + if (this.$changedLines.firstRow > this.layerConfig.lastRow) + return; + this.$loop.schedule(this.CHANGE_LINES); + }; + + this.onChangeNewLineMode = function() { + this.$loop.schedule(this.CHANGE_TEXT); + this.$textLayer.$updateEolChar(); + this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR); + }; + + this.onChangeTabSize = function() { + this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER); + this.$textLayer.onChangeTabSize(); + }; + this.updateText = function() { + this.$loop.schedule(this.CHANGE_TEXT); + }; + this.updateFull = function(force) { + if (force) + this.$renderChanges(this.CHANGE_FULL, true); + else + this.$loop.schedule(this.CHANGE_FULL); + }; + this.updateFontSize = function() { + this.$textLayer.checkForSizeChanges(); + }; + + this.$changes = 0; + this.$updateSizeAsync = function() { + if (this.$loop.pending) + this.$size.$dirty = true; + else + this.onResize(); + }; + this.onResize = function(force, gutterWidth, width, height) { + if (this.resizing > 2) + return; + else if (this.resizing > 0) + this.resizing++; + else + this.resizing = force ? 1 : 0; + var el = this.container; + if (!height) + height = el.clientHeight || el.scrollHeight; + if (!width) + width = el.clientWidth || el.scrollWidth; + var changes = this.$updateCachedSize(force, gutterWidth, width, height); + + + if (!this.$size.scrollerHeight || (!width && !height)) + return this.resizing = 0; + + if (force) + this.$gutterLayer.$padding = null; + + if (force) + this.$renderChanges(changes | this.$changes, true); + else + this.$loop.schedule(changes | this.$changes); + + if (this.resizing) + this.resizing = 0; + this.scrollBarV.scrollLeft = this.scrollBarV.scrollTop = null; + }; + + this.$updateCachedSize = function(force, gutterWidth, width, height) { + height -= (this.$extraHeight || 0); + var changes = 0; + var size = this.$size; + var oldSize = { + width: size.width, + height: size.height, + scrollerHeight: size.scrollerHeight, + scrollerWidth: size.scrollerWidth + }; + if (height && (force || size.height != height)) { + size.height = height; + changes |= this.CHANGE_SIZE; + + size.scrollerHeight = size.height; + if (this.$horizScroll) + size.scrollerHeight -= this.scrollBarH.getHeight(); + this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px"; + + changes = changes | this.CHANGE_SCROLL; + } + + if (width && (force || size.width != width)) { + changes |= this.CHANGE_SIZE; + size.width = width; + + if (gutterWidth == null) + gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0; + + this.gutterWidth = gutterWidth; + + this.scrollBarH.element.style.left = + this.scroller.style.left = gutterWidth + "px"; + size.scrollerWidth = Math.max(0, width - gutterWidth - this.scrollBarV.getWidth()); + + this.scrollBarH.element.style.right = + this.scroller.style.right = this.scrollBarV.getWidth() + "px"; + this.scroller.style.bottom = this.scrollBarH.getHeight() + "px"; + + if (this.session && this.session.getUseWrapMode() && this.adjustWrapLimit() || force) + changes |= this.CHANGE_FULL; + } + + size.$dirty = !width || !height; + + if (changes) + this._signal("resize", oldSize); + + return changes; + }; + + this.onGutterResize = function() { + var gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0; + if (gutterWidth != this.gutterWidth) + this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height); + + if (this.session.getUseWrapMode() && this.adjustWrapLimit()) { + this.$loop.schedule(this.CHANGE_FULL); + } else if (this.$size.$dirty) { + this.$loop.schedule(this.CHANGE_FULL); + } else { + this.$computeLayerConfig(); + this.$loop.schedule(this.CHANGE_MARKER); + } + }; + this.adjustWrapLimit = function() { + var availableWidth = this.$size.scrollerWidth - this.$padding * 2; + var limit = Math.floor(availableWidth / this.characterWidth); + return this.session.adjustWrapLimit(limit, this.$showPrintMargin && this.$printMarginColumn); + }; + this.setAnimatedScroll = function(shouldAnimate){ + this.setOption("animatedScroll", shouldAnimate); + }; + this.getAnimatedScroll = function() { + return this.$animatedScroll; + }; + this.setShowInvisibles = function(showInvisibles) { + this.setOption("showInvisibles", showInvisibles); + this.session.$bidiHandler.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.getOption("showInvisibles"); + }; + this.getDisplayIndentGuides = function() { + return this.getOption("displayIndentGuides"); + }; + + this.setDisplayIndentGuides = function(display) { + this.setOption("displayIndentGuides", display); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.setOption("showPrintMargin", showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.getOption("showPrintMargin"); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.setOption("printMarginColumn", showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.getOption("printMarginColumn"); + }; + this.getShowGutter = function(){ + return this.getOption("showGutter"); + }; + this.setShowGutter = function(show){ + return this.setOption("showGutter", show); + }; + + this.getFadeFoldWidgets = function(){ + return this.getOption("fadeFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(show) { + this.setOption("fadeFoldWidgets", show); + }; + + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + + this.$updateGutterLineHighlight = function() { + var pos = this.$cursorLayer.$pixelPos; + var height = this.layerConfig.lineHeight; + if (this.session.getUseWrapMode()) { + var cursor = this.session.selection.getCursor(); + cursor.column = 0; + pos = this.$cursorLayer.getPixelPosition(cursor, true); + height *= this.session.getRowLength(cursor.row); + } + this.$gutterLineHighlight.style.top = pos.top - this.layerConfig.offset + "px"; + this.$gutterLineHighlight.style.height = height + "px"; + }; + + this.$updatePrintMargin = function() { + if (!this.$showPrintMargin && !this.$printMarginEl) + return; + + if (!this.$printMarginEl) { + var containerEl = dom.createElement("div"); + containerEl.className = "ace_layer ace_print-margin-layer"; + this.$printMarginEl = dom.createElement("div"); + this.$printMarginEl.className = "ace_print-margin"; + containerEl.appendChild(this.$printMarginEl); + this.content.insertBefore(containerEl, this.content.firstChild); + } + + var style = this.$printMarginEl.style; + style.left = ((this.characterWidth * this.$printMarginColumn) + this.$padding) + "px"; + style.visibility = this.$showPrintMargin ? "visible" : "hidden"; + + if (this.session && this.session.$wrap == -1) + this.adjustWrapLimit(); + }; + this.getContainerElement = function() { + return this.container; + }; + this.getMouseEventTarget = function() { + return this.scroller; + }; + this.getTextAreaContainer = function() { + return this.container; + }; + this.$moveTextAreaToCursor = function() { + if (!this.$keepTextAreaAtCursor) + return; + var config = this.layerConfig; + var posTop = this.$cursorLayer.$pixelPos.top; + var posLeft = this.$cursorLayer.$pixelPos.left; + posTop -= config.offset; + + var style = this.textarea.style; + var h = this.lineHeight; + if (posTop < 0 || posTop > config.height - h) { + style.top = style.left = "0"; + return; + } + + var w = this.characterWidth; + if (this.$composition) { + var val = this.textarea.value.replace(/^\x01+/, ""); + w *= (this.session.$getStringScreenWidth(val)[0]+2); + h += 2; + } + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + + posLeft += this.gutterWidth; + style.height = h + "px"; + style.width = w + "px"; + style.left = Math.min(posLeft, this.$size.scrollerWidth - w) + "px"; + style.top = Math.min(posTop, this.$size.height - h) + "px"; + }; + this.getFirstVisibleRow = function() { + return this.layerConfig.firstRow; + }; + this.getFirstFullyVisibleRow = function() { + return this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1); + }; + this.getLastFullyVisibleRow = function() { + var config = this.layerConfig; + var lastRow = config.lastRow; + var top = this.session.documentToScreenRow(lastRow, 0) * config.lineHeight; + if (top - this.session.getScrollTop() > config.height - config.lineHeight) + return lastRow - 1; + return lastRow; + }; + this.getLastVisibleRow = function() { + return this.layerConfig.lastRow; + }; + + this.$padding = null; + this.setPadding = function(padding) { + this.$padding = padding; + this.$textLayer.setPadding(padding); + this.$cursorLayer.setPadding(padding); + this.$markerFront.setPadding(padding); + this.$markerBack.setPadding(padding); + this.$loop.schedule(this.CHANGE_FULL); + this.$updatePrintMargin(); + }; + + this.setScrollMargin = function(top, bottom, left, right) { + var sm = this.scrollMargin; + sm.top = top|0; + sm.bottom = bottom|0; + sm.right = right|0; + sm.left = left|0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + if (sm.top && this.scrollTop <= 0 && this.session) + this.session.setScrollTop(-sm.top); + this.updateFull(); + }; + this.getHScrollBarAlwaysVisible = function() { + return this.$hScrollBarAlwaysVisible; + }; + this.setHScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("hScrollBarAlwaysVisible", alwaysVisible); + }; + this.getVScrollBarAlwaysVisible = function() { + return this.$vScrollBarAlwaysVisible; + }; + this.setVScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("vScrollBarAlwaysVisible", alwaysVisible); + }; + + this.$updateScrollBarV = function() { + var scrollHeight = this.layerConfig.maxHeight; + var scrollerHeight = this.$size.scrollerHeight; + if (!this.$maxLines && this.$scrollPastEnd) { + scrollHeight -= (scrollerHeight - this.lineHeight) * this.$scrollPastEnd; + if (this.scrollTop > scrollHeight - scrollerHeight) { + scrollHeight = this.scrollTop + scrollerHeight; + this.scrollBarV.scrollTop = null; + } + } + this.scrollBarV.setScrollHeight(scrollHeight + this.scrollMargin.v); + this.scrollBarV.setScrollTop(this.scrollTop + this.scrollMargin.top); + }; + this.$updateScrollBarH = function() { + this.scrollBarH.setScrollWidth(this.layerConfig.width + 2 * this.$padding + this.scrollMargin.h); + this.scrollBarH.setScrollLeft(this.scrollLeft + this.scrollMargin.left); + }; + + this.$frozen = false; + this.freeze = function() { + this.$frozen = true; + }; + + this.unfreeze = function() { + this.$frozen = false; + }; + + this.$renderChanges = function(changes, force) { + if (this.$changes) { + changes |= this.$changes; + this.$changes = 0; + } + if ((!this.session || !this.container.offsetWidth || this.$frozen) || (!changes && !force)) { + this.$changes |= changes; + return; + } + if (this.$size.$dirty) { + this.$changes |= changes; + return this.onResize(true); + } + if (!this.lineHeight) { + this.$textLayer.checkForSizeChanges(); + } + + this._signal("beforeRender"); + + if (this.session && this.session.$bidiHandler) + this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics); + + var config = this.layerConfig; + if (changes & this.CHANGE_FULL || + changes & this.CHANGE_SIZE || + changes & this.CHANGE_TEXT || + changes & this.CHANGE_LINES || + changes & this.CHANGE_SCROLL || + changes & this.CHANGE_H_SCROLL + ) { + changes |= this.$computeLayerConfig(); + if (config.firstRow != this.layerConfig.firstRow && config.firstRowScreen == this.layerConfig.firstRowScreen) { + var st = this.scrollTop + (config.firstRow - this.layerConfig.firstRow) * this.lineHeight; + if (st > 0) { + this.scrollTop = st; + changes = changes | this.CHANGE_SCROLL; + changes |= this.$computeLayerConfig(); + } + } + config = this.layerConfig; + this.$updateScrollBarV(); + if (changes & this.CHANGE_H_SCROLL) + this.$updateScrollBarH(); + this.$gutterLayer.element.style.marginTop = (-config.offset) + "px"; + this.content.style.marginTop = (-config.offset) + "px"; + this.content.style.width = config.width + 2 * this.$padding + "px"; + this.content.style.height = config.minHeight + "px"; + } + if (changes & this.CHANGE_H_SCROLL) { + this.content.style.marginLeft = -this.scrollLeft + "px"; + this.scroller.className = this.scrollLeft <= 0 ? "ace_scroller" : "ace_scroller ace_scroll-left"; + } + if (changes & this.CHANGE_FULL) { + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this.$highlightGutterLine && this.$updateGutterLineHighlight(); + this._signal("afterRender"); + return; + } + if (changes & this.CHANGE_SCROLL) { + if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES) + this.$textLayer.update(config); + else + this.$textLayer.scrollLines(config); + + if (this.$showGutter) + this.$gutterLayer.update(config); + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$highlightGutterLine && this.$updateGutterLineHighlight(); + this.$moveTextAreaToCursor(); + this._signal("afterRender"); + return; + } + + if (changes & this.CHANGE_TEXT) { + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + } + else if (changes & this.CHANGE_LINES) { + if (this.$updateLines() || (changes & this.CHANGE_GUTTER) && this.$showGutter) + this.$gutterLayer.update(config); + } + else if (changes & this.CHANGE_TEXT || changes & this.CHANGE_GUTTER) { + if (this.$showGutter) + this.$gutterLayer.update(config); + } + + if (changes & this.CHANGE_CURSOR) { + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this.$highlightGutterLine && this.$updateGutterLineHighlight(); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) { + this.$markerFront.update(config); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) { + this.$markerBack.update(config); + } + + this._signal("afterRender"); + }; + + + this.$autosize = function() { + var height = this.session.getScreenLength() * this.lineHeight; + var maxHeight = this.$maxLines * this.lineHeight; + var desiredHeight = Math.min(maxHeight, + Math.max((this.$minLines || 1) * this.lineHeight, height) + ) + this.scrollMargin.v + (this.$extraHeight || 0); + if (this.$horizScroll) + desiredHeight += this.scrollBarH.getHeight(); + if (this.$maxPixelHeight && desiredHeight > this.$maxPixelHeight) + desiredHeight = this.$maxPixelHeight; + var vScroll = height > maxHeight; + + if (desiredHeight != this.desiredHeight || + this.$size.height != this.desiredHeight || vScroll != this.$vScroll) { + if (vScroll != this.$vScroll) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var w = this.container.clientWidth; + this.container.style.height = desiredHeight + "px"; + this.$updateCachedSize(true, this.$gutterWidth, w, desiredHeight); + this.desiredHeight = desiredHeight; + + this._signal("autosize"); + } + }; + + this.$computeLayerConfig = function() { + var session = this.session; + var size = this.$size; + + var hideScrollbars = size.height <= 2 * this.lineHeight; + var screenLines = this.session.getScreenLength(); + var maxHeight = screenLines * this.lineHeight; + + var longestLine = this.$getLongestLine(); + + var horizScroll = !hideScrollbars && (this.$hScrollBarAlwaysVisible || + size.scrollerWidth - longestLine - 2 * this.$padding < 0); + + var hScrollChanged = this.$horizScroll !== horizScroll; + if (hScrollChanged) { + this.$horizScroll = horizScroll; + this.scrollBarH.setVisible(horizScroll); + } + var vScrollBefore = this.$vScroll; // autosize can change vscroll value in which case we need to update longestLine + if (this.$maxLines && this.lineHeight > 1) + this.$autosize(); + + var offset = this.scrollTop % this.lineHeight; + var minHeight = size.scrollerHeight + this.lineHeight; + + var scrollPastEnd = !this.$maxLines && this.$scrollPastEnd + ? (size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd + : 0; + maxHeight += scrollPastEnd; + + var sm = this.scrollMargin; + this.session.setScrollTop(Math.max(-sm.top, + Math.min(this.scrollTop, maxHeight - size.scrollerHeight + sm.bottom))); + + this.session.setScrollLeft(Math.max(-sm.left, Math.min(this.scrollLeft, + longestLine + 2 * this.$padding - size.scrollerWidth + sm.right))); + + var vScroll = !hideScrollbars && (this.$vScrollBarAlwaysVisible || + size.scrollerHeight - maxHeight + scrollPastEnd < 0 || this.scrollTop > sm.top); + var vScrollChanged = vScrollBefore !== vScroll; + if (vScrollChanged) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var lineCount = Math.ceil(minHeight / this.lineHeight) - 1; + var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight)); + var lastRow = firstRow + lineCount; + var firstRowScreen, firstRowHeight; + var lineHeight = this.lineHeight; + firstRow = session.screenToDocumentRow(firstRow, 0); + var foldLine = session.getFoldLine(firstRow); + if (foldLine) { + firstRow = foldLine.start.row; + } + + firstRowScreen = session.documentToScreenRow(firstRow, 0); + firstRowHeight = session.getRowLength(firstRow) * lineHeight; + + lastRow = Math.min(session.screenToDocumentRow(lastRow, 0), session.getLength() - 1); + minHeight = size.scrollerHeight + session.getRowLength(lastRow) * lineHeight + + firstRowHeight; + + offset = this.scrollTop - firstRowScreen * lineHeight; + + var changes = 0; + if (this.layerConfig.width != longestLine) + changes = this.CHANGE_H_SCROLL; + if (hScrollChanged || vScrollChanged) { + changes = this.$updateCachedSize(true, this.gutterWidth, size.width, size.height); + this._signal("scrollbarVisibilityChanged"); + if (vScrollChanged) + longestLine = this.$getLongestLine(); + } + + this.layerConfig = { + width : longestLine, + padding : this.$padding, + firstRow : firstRow, + firstRowScreen: firstRowScreen, + lastRow : lastRow, + lineHeight : lineHeight, + characterWidth : this.characterWidth, + minHeight : minHeight, + maxHeight : maxHeight, + offset : offset, + gutterOffset : lineHeight ? Math.max(0, Math.ceil((offset + size.height - size.scrollerHeight) / lineHeight)) : 0, + height : this.$size.scrollerHeight + }; + + return changes; + }; + + this.$updateLines = function() { + if (!this.$changedLines) return; + var firstRow = this.$changedLines.firstRow; + var lastRow = this.$changedLines.lastRow; + this.$changedLines = null; + + var layerConfig = this.layerConfig; + + if (firstRow > layerConfig.lastRow + 1) { return; } + if (lastRow < layerConfig.firstRow) { return; } + if (lastRow === Infinity) { + if (this.$showGutter) + this.$gutterLayer.update(layerConfig); + this.$textLayer.update(layerConfig); + return; + } + this.$textLayer.updateLines(layerConfig, firstRow, lastRow); + return true; + }; + + this.$getLongestLine = function() { + var charCount = this.session.getScreenWidth(); + if (this.showInvisibles && !this.session.$useWrapMode) + charCount += 1; + + return Math.max(this.$size.scrollerWidth - 2 * this.$padding, Math.round(charCount * this.characterWidth)); + }; + this.updateFrontMarkers = function() { + this.$markerFront.setMarkers(this.session.getMarkers(true)); + this.$loop.schedule(this.CHANGE_MARKER_FRONT); + }; + this.updateBackMarkers = function() { + this.$markerBack.setMarkers(this.session.getMarkers()); + this.$loop.schedule(this.CHANGE_MARKER_BACK); + }; + this.addGutterDecoration = function(row, className){ + this.$gutterLayer.addGutterDecoration(row, className); + }; + this.removeGutterDecoration = function(row, className){ + this.$gutterLayer.removeGutterDecoration(row, className); + }; + this.updateBreakpoints = function(rows) { + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.setAnnotations = function(annotations) { + this.$gutterLayer.setAnnotations(annotations); + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.updateCursor = function() { + this.$loop.schedule(this.CHANGE_CURSOR); + }; + this.hideCursor = function() { + this.$cursorLayer.hideCursor(); + }; + this.showCursor = function() { + this.$cursorLayer.showCursor(); + }; + + this.scrollSelectionIntoView = function(anchor, lead, offset) { + this.scrollCursorIntoView(anchor, offset); + this.scrollCursorIntoView(lead, offset); + }; + this.scrollCursorIntoView = function(cursor, offset, $viewMargin) { + if (this.$size.scrollerHeight === 0) + return; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + + var left = pos.left; + var top = pos.top; + + var topMargin = $viewMargin && $viewMargin.top || 0; + var bottomMargin = $viewMargin && $viewMargin.bottom || 0; + + var scrollTop = this.$scrollAnimation ? this.session.getScrollTop() : this.scrollTop; + + if (scrollTop + topMargin > top) { + if (offset && scrollTop + topMargin > top + this.lineHeight) + top -= offset * this.$size.scrollerHeight; + if (top === 0) + top = -this.scrollMargin.top; + this.session.setScrollTop(top); + } else if (scrollTop + this.$size.scrollerHeight - bottomMargin < top + this.lineHeight) { + if (offset && scrollTop + this.$size.scrollerHeight - bottomMargin < top - this.lineHeight) + top += offset * this.$size.scrollerHeight; + this.session.setScrollTop(top + this.lineHeight - this.$size.scrollerHeight); + } + + var scrollLeft = this.scrollLeft; + + if (scrollLeft > left) { + if (left < this.$padding + 2 * this.layerConfig.characterWidth) + left = -this.scrollMargin.left; + this.session.setScrollLeft(left); + } else if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) { + this.session.setScrollLeft(Math.round(left + this.characterWidth - this.$size.scrollerWidth)); + } else if (scrollLeft <= this.$padding && left - scrollLeft < this.characterWidth) { + this.session.setScrollLeft(0); + } + }; + this.getScrollTop = function() { + return this.session.getScrollTop(); + }; + this.getScrollLeft = function() { + return this.session.getScrollLeft(); + }; + this.getScrollTopRow = function() { + return this.scrollTop / this.lineHeight; + }; + this.getScrollBottomRow = function() { + return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1); + }; + this.scrollToRow = function(row) { + this.session.setScrollTop(row * this.lineHeight); + }; + + this.alignCursor = function(cursor, alignment) { + if (typeof cursor == "number") + cursor = {row: cursor, column: 0}; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + var h = this.$size.scrollerHeight - this.lineHeight; + var offset = pos.top - h * (alignment || 0); + + this.session.setScrollTop(offset); + return offset; + }; + + this.STEPS = 8; + this.$calcSteps = function(fromValue, toValue){ + var i = 0; + var l = this.STEPS; + var steps = []; + + var func = function(t, x_min, dx) { + return dx * (Math.pow(t - 1, 3) + 1) + x_min; + }; + + for (i = 0; i < l; ++i) + steps.push(func(i / this.STEPS, fromValue, toValue - fromValue)); + + return steps; + }; + this.scrollToLine = function(line, center, animate, callback) { + var pos = this.$cursorLayer.getPixelPosition({row: line, column: 0}); + var offset = pos.top; + if (center) + offset -= this.$size.scrollerHeight / 2; + + var initialScroll = this.scrollTop; + this.session.setScrollTop(offset); + if (animate !== false) + this.animateScrolling(initialScroll, callback); + }; + + this.animateScrolling = function(fromValue, callback) { + var toValue = this.scrollTop; + if (!this.$animatedScroll) + return; + var _self = this; + + if (fromValue == toValue) + return; + + if (this.$scrollAnimation) { + var oldSteps = this.$scrollAnimation.steps; + if (oldSteps.length) { + fromValue = oldSteps[0]; + if (fromValue == toValue) + return; + } + } + + var steps = _self.$calcSteps(fromValue, toValue); + this.$scrollAnimation = {from: fromValue, to: toValue, steps: steps}; + + clearInterval(this.$timer); + + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + this.$timer = setInterval(function() { + if (steps.length) { + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + } else if (toValue != null) { + _self.session.$scrollTop = -1; + _self.session.setScrollTop(toValue); + toValue = null; + } else { + _self.$timer = clearInterval(_self.$timer); + _self.$scrollAnimation = null; + callback && callback(); + } + }, 10); + }; + this.scrollToY = function(scrollTop) { + if (this.scrollTop !== scrollTop) { + this.$loop.schedule(this.CHANGE_SCROLL); + this.scrollTop = scrollTop; + } + }; + this.scrollToX = function(scrollLeft) { + if (this.scrollLeft !== scrollLeft) + this.scrollLeft = scrollLeft; + this.$loop.schedule(this.CHANGE_H_SCROLL); + }; + this.scrollTo = function(x, y) { + this.session.setScrollTop(y); + this.session.setScrollLeft(y); + }; + this.scrollBy = function(deltaX, deltaY) { + deltaY && this.session.setScrollTop(this.session.getScrollTop() + deltaY); + deltaX && this.session.setScrollLeft(this.session.getScrollLeft() + deltaX); + }; + this.isScrollableBy = function(deltaX, deltaY) { + if (deltaY < 0 && this.session.getScrollTop() >= 1 - this.scrollMargin.top) + return true; + if (deltaY > 0 && this.session.getScrollTop() + this.$size.scrollerHeight + - this.layerConfig.maxHeight < -1 + this.scrollMargin.bottom) + return true; + if (deltaX < 0 && this.session.getScrollLeft() >= 1 - this.scrollMargin.left) + return true; + if (deltaX > 0 && this.session.getScrollLeft() + this.$size.scrollerWidth + - this.layerConfig.width < -1 + this.scrollMargin.right) + return true; + }; + + this.pixelToScreenCoordinates = function(x, y) { + var canvasPos = this.scroller.getBoundingClientRect(); + + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight); + var col = Math.round(offset); + + return {row: row, column: col, side: offset - col > 0 ? 1 : -1, offsetX: offsetX}; + }; + + this.screenToTextCoordinates = function(x, y) { + var canvasPos = this.scroller.getBoundingClientRect(); + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + + var col = Math.round(offsetX / this.characterWidth); + + var row = (y + this.scrollTop - canvasPos.top) / this.lineHeight; + + return this.session.screenToDocumentPosition(row, Math.max(col, 0), offsetX); + }; + this.textToScreenCoordinates = function(row, column) { + var canvasPos = this.scroller.getBoundingClientRect(); + var pos = this.session.documentToScreenPosition(row, column); + + var x = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : Math.round(pos.column * this.characterWidth)); + + var y = pos.row * this.lineHeight; + + return { + pageX: canvasPos.left + x - this.scrollLeft, + pageY: canvasPos.top + y - this.scrollTop + }; + }; + this.visualizeFocus = function() { + dom.addCssClass(this.container, "ace_focus"); + }; + this.visualizeBlur = function() { + dom.removeCssClass(this.container, "ace_focus"); + }; + this.showComposition = function(position) { + if (!this.$composition) + this.$composition = { + keepTextAreaAtCursor: this.$keepTextAreaAtCursor, + cssText: this.textarea.style.cssText + }; + + this.$keepTextAreaAtCursor = true; + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); + }; + this.setCompositionText = function(text) { + this.$moveTextAreaToCursor(); + }; + this.hideComposition = function() { + if (!this.$composition) + return; + + dom.removeCssClass(this.textarea, "ace_composition"); + this.$keepTextAreaAtCursor = this.$composition.keepTextAreaAtCursor; + this.textarea.style.cssText = this.$composition.cssText; + this.$composition = null; + }; + this.setTheme = function(theme, cb) { + var _self = this; + this.$themeId = theme; + _self._dispatchEvent('themeChange',{theme:theme}); + + if (!theme || typeof theme == "string") { + var moduleName = theme || this.$options.theme.initialValue; + config.loadModule(["theme", moduleName], afterLoad); + } else { + afterLoad(theme); + } + + function afterLoad(module) { + if (_self.$themeId != theme) + return cb && cb(); + if (!module || !module.cssClass) + throw new Error("couldn't load module " + theme + " or it didn't call define"); + dom.importCssString( + module.cssText, + module.cssClass, + _self.container.ownerDocument + ); + + if (_self.theme) + dom.removeCssClass(_self.container, _self.theme.cssClass); + + var padding = "padding" in module ? module.padding + : "padding" in (_self.theme || {}) ? 4 : _self.$padding; + if (_self.$padding && padding != _self.$padding) + _self.setPadding(padding); + _self.$theme = module.cssClass; + + _self.theme = module; + dom.addCssClass(_self.container, module.cssClass); + dom.setCssClass(_self.container, "ace_dark", module.isDark); + if (_self.$size) { + _self.$size.width = 0; + _self.$updateSizeAsync(); + } + + _self._dispatchEvent('themeLoaded', {theme:module}); + cb && cb(); + } + }; + this.getTheme = function() { + return this.$themeId; + }; + this.setStyle = function(style, include) { + dom.setCssClass(this.container, style, include !== false); + }; + this.unsetStyle = function(style) { + dom.removeCssClass(this.container, style); + }; + + this.setCursorStyle = function(style) { + if (this.scroller.style.cursor != style) + this.scroller.style.cursor = style; + }; + this.setMouseCursor = function(cursorStyle) { + this.scroller.style.cursor = cursorStyle; + }; + this.destroy = function() { + this.$textLayer.destroy(); + this.$cursorLayer.destroy(); + }; + + }).call(VirtualRenderer.prototype); + + + config.defineOptions(VirtualRenderer.prototype, "renderer", { + animatedScroll: {initialValue: false}, + showInvisibles: { + set: function(value) { + if (this.$textLayer.setShowInvisibles(value)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: false + }, + showPrintMargin: { + set: function() { this.$updatePrintMargin(); }, + initialValue: true + }, + printMarginColumn: { + set: function() { this.$updatePrintMargin(); }, + initialValue: 80 + }, + printMargin: { + set: function(val) { + if (typeof val == "number") + this.$printMarginColumn = val; + this.$showPrintMargin = !!val; + this.$updatePrintMargin(); + }, + get: function() { + return this.$showPrintMargin && this.$printMarginColumn; + } + }, + showGutter: { + set: function(show){ + this.$gutter.style.display = show ? "block" : "none"; + this.$loop.schedule(this.CHANGE_FULL); + this.onGutterResize(); + }, + initialValue: true + }, + fadeFoldWidgets: { + set: function(show) { + dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show); + }, + initialValue: false + }, + showFoldWidgets: { + set: function(show) {this.$gutterLayer.setShowFoldWidgets(show);}, + initialValue: true + }, + showLineNumbers: { + set: function(show) { + this.$gutterLayer.setShowLineNumbers(show); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + displayIndentGuides: { + set: function(show) { + if (this.$textLayer.setDisplayIndentGuides(show)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: true + }, + highlightGutterLine: { + set: function(shouldHighlight) { + if (!this.$gutterLineHighlight) { + this.$gutterLineHighlight = dom.createElement("div"); + this.$gutterLineHighlight.className = "ace_gutter-active-line"; + this.$gutter.appendChild(this.$gutterLineHighlight); + return; + } + + this.$gutterLineHighlight.style.display = shouldHighlight ? "" : "none"; + if (this.$cursorLayer.$pixelPos) + this.$updateGutterLineHighlight(); + }, + initialValue: false, + value: true + }, + hScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$hScrollBarAlwaysVisible || !this.$horizScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + vScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$vScrollBarAlwaysVisible || !this.$vScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + fontSize: { + set: function(size) { + if (typeof size == "number") + size = size + "px"; + this.container.style.fontSize = size; + this.updateFontSize(); + }, + initialValue: 12 + }, + fontFamily: { + set: function(name) { + this.container.style.fontFamily = name; + this.updateFontSize(); + } + }, + maxLines: { + set: function(val) { + this.updateFull(); + } + }, + minLines: { + set: function(val) { + this.updateFull(); + } + }, + maxPixelHeight: { + set: function(val) { + this.updateFull(); + }, + initialValue: 0 + }, + scrollPastEnd: { + set: function(val) { + val = +val || 0; + if (this.$scrollPastEnd == val) + return; + this.$scrollPastEnd = val; + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: 0, + handlesSet: true + }, + fixedWidthGutter: { + set: function(val) { + this.$gutterLayer.$fixedWidth = !!val; + this.$loop.schedule(this.CHANGE_GUTTER); + } + }, + theme: { + set: function(val) { this.setTheme(val); }, + get: function() { return this.$themeId || this.theme; }, + initialValue: "./theme/textmate", + handlesSet: true + } + }); + + exports.VirtualRenderer = VirtualRenderer; + }); + + ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../lib/oop"); + var net = acequire("../lib/net"); + var EventEmitter = acequire("../lib/event_emitter").EventEmitter; + var config = acequire("../config"); + + function $workerBlob(workerUrl, mod) { + var script = mod.src;"importScripts('" + net.qualifyURL(workerUrl) + "');"; + try { + return new Blob([script], {"type": "application/javascript"}); + } catch (e) { // Backwards-compatibility + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; + var blobBuilder = new BlobBuilder(); + blobBuilder.append(script); + return blobBuilder.getBlob("application/javascript"); + } + } + + function createWorker(workerUrl, mod) { + var blob = $workerBlob(workerUrl, mod); + var URL = window.URL || window.webkitURL; + var blobURL = URL.createObjectURL(blob); + return new Worker(blobURL); + } + + var WorkerClient = function(topLevelNamespaces, mod, classname, workerUrl, importScripts) { + this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); + this.changeListener = this.changeListener.bind(this); + this.onMessage = this.onMessage.bind(this); + if (acequire.nameToUrl && !acequire.toUrl) + acequire.toUrl = acequire.nameToUrl; + + if (config.get("packaged") || !acequire.toUrl) { + workerUrl = workerUrl || config.moduleUrl(mod.id, "worker"); + } else { + var normalizePath = this.$normalizePath; + workerUrl = workerUrl || normalizePath(acequire.toUrl("ace/worker/worker.js", null, "_")); + + var tlns = {}; + topLevelNamespaces.forEach(function(ns) { + tlns[ns] = normalizePath(acequire.toUrl(ns, null, "_").replace(/(\.js)?(\?.*)?$/, "")); + }); + } + + this.$worker = createWorker(workerUrl, mod); + if (importScripts) { + this.send("importScripts", importScripts); + } + this.$worker.postMessage({ + init : true, + tlns : tlns, + module : mod.id, + classname : classname + }); + + this.callbackId = 1; + this.callbacks = {}; + + this.$worker.onmessage = this.onMessage; + }; + + (function(){ + + oop.implement(this, EventEmitter); + + this.onMessage = function(e) { + var msg = e.data; + switch (msg.type) { + case "event": + this._signal(msg.name, {data: msg.data}); + break; + case "call": + var callback = this.callbacks[msg.id]; + if (callback) { + callback(msg.data); + delete this.callbacks[msg.id]; + } + break; + case "error": + this.reportError(msg.data); + break; + case "log": + window.console && console.log && console.log.apply(console, msg.data); + break; + } + }; + + this.reportError = function(err) { + window.console && console.error && console.error(err); + }; + + this.$normalizePath = function(path) { + return net.qualifyURL(path); + }; + + this.terminate = function() { + this._signal("terminate", {}); + this.deltaQueue = null; + this.$worker.terminate(); + this.$worker = null; + if (this.$doc) + this.$doc.off("change", this.changeListener); + this.$doc = null; + }; + + this.send = function(cmd, args) { + this.$worker.postMessage({command: cmd, args: args}); + }; + + this.call = function(cmd, args, callback) { + if (callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + } + this.send(cmd, args); + }; + + this.emit = function(event, data) { + try { + this.$worker.postMessage({event: event, data: {data: data.data}}); + } + catch(ex) { + console.error(ex.stack); + } + }; + + this.attachToDocument = function(doc) { + if (this.$doc) + this.terminate(); + + this.$doc = doc; + this.call("setValue", [doc.getValue()]); + doc.on("change", this.changeListener); + }; + + this.changeListener = function(delta) { + if (!this.deltaQueue) { + this.deltaQueue = []; + setTimeout(this.$sendDeltaQueue, 0); + } + if (delta.action == "insert") + this.deltaQueue.push(delta.start, delta.lines); + else + this.deltaQueue.push(delta.start, delta.end); + }; + + this.$sendDeltaQueue = function() { + var q = this.deltaQueue; + if (!q) return; + this.deltaQueue = null; + if (q.length > 50 && q.length > this.$doc.getLength() >> 1) { + this.call("setValue", [this.$doc.getValue()]); + } else + this.emit("change", {data: q}); + }; + + }).call(WorkerClient.prototype); + + + var UIWorkerClient = function(topLevelNamespaces, mod, classname) { + this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); + this.changeListener = this.changeListener.bind(this); + this.callbackId = 1; + this.callbacks = {}; + this.messageBuffer = []; + + var main = null; + var emitSync = false; + var sender = Object.create(EventEmitter); + var _self = this; + + this.$worker = {}; + this.$worker.terminate = function() {}; + this.$worker.postMessage = function(e) { + _self.messageBuffer.push(e); + if (main) { + if (emitSync) + setTimeout(processNext); + else + processNext(); + } + }; + this.setEmitSync = function(val) { emitSync = val; }; + + var processNext = function() { + var msg = _self.messageBuffer.shift(); + if (msg.command) + main[msg.command].apply(main, msg.args); + else if (msg.event) + sender._signal(msg.event, msg.data); + }; + + sender.postMessage = function(msg) { + _self.onMessage({data: msg}); + }; + sender.callback = function(data, callbackId) { + this.postMessage({type: "call", id: callbackId, data: data}); + }; + sender.emit = function(name, data) { + this.postMessage({type: "event", name: name, data: data}); + }; + + config.loadModule(["worker", mod], function(Main) { + main = new Main[classname](sender); + while (_self.messageBuffer.length) + processNext(); + }); + }; + + UIWorkerClient.prototype = WorkerClient.prototype; + + exports.UIWorkerClient = UIWorkerClient; + exports.WorkerClient = WorkerClient; + exports.createWorker = createWorker; + + + }); + + ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("./range").Range; + var EventEmitter = acequire("./lib/event_emitter").EventEmitter; + var oop = acequire("./lib/oop"); + + var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) { + var _self = this; + this.length = length; + this.session = session; + this.doc = session.getDocument(); + this.mainClass = mainClass; + this.othersClass = othersClass; + this.$onUpdate = this.onUpdate.bind(this); + this.doc.on("change", this.$onUpdate); + this.$others = others; + + this.$onCursorChange = function() { + setTimeout(function() { + _self.onCursorChange(); + }); + }; + + this.$pos = pos; + var undoStack = session.getUndoManager().$undoStack || session.getUndoManager().$undostack || {length: -1}; + this.$undoStackDepth = undoStack.length; + this.setup(); + + session.selection.on("changeCursor", this.$onCursorChange); + }; + + (function() { + + oop.implement(this, EventEmitter); + this.setup = function() { + var _self = this; + var doc = this.doc; + var session = this.session; + + this.selectionBefore = session.selection.toJSON(); + if (session.selection.inMultiSelectMode) + session.selection.toSingleRange(); + + this.pos = doc.createAnchor(this.$pos.row, this.$pos.column); + var pos = this.pos; + pos.$insertRight = true; + pos.detach(); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + this.length), this.mainClass, null, false); + this.others = []; + this.$others.forEach(function(other) { + var anchor = doc.createAnchor(other.row, other.column); + anchor.$insertRight = true; + anchor.detach(); + _self.others.push(anchor); + }); + session.setUndoSelect(false); + }; + this.showOtherMarkers = function() { + if (this.othersActive) return; + var session = this.session; + var _self = this; + this.othersActive = true; + this.others.forEach(function(anchor) { + anchor.markerId = session.addMarker(new Range(anchor.row, anchor.column, anchor.row, anchor.column+_self.length), _self.othersClass, null, false); + }); + }; + this.hideOtherMarkers = function() { + if (!this.othersActive) return; + this.othersActive = false; + for (var i = 0; i < this.others.length; i++) { + this.session.removeMarker(this.others[i].markerId); + } + }; + this.onUpdate = function(delta) { + if (this.$updating) + return this.updateAnchors(delta); + + var range = delta; + if (range.start.row !== range.end.row) return; + if (range.start.row !== this.pos.row) return; + this.$updating = true; + var lengthDiff = delta.action === "insert" ? range.end.column - range.start.column : range.start.column - range.end.column; + var inMainRange = range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1; + var distanceFromStart = range.start.column - this.pos.column; + + this.updateAnchors(delta); + + if (inMainRange) + this.length += lengthDiff; + + if (inMainRange && !this.session.$fromUndo) { + if (delta.action === 'insert') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + this.doc.insertMergedLines(newPos, delta.lines); + } + } else if (delta.action === 'remove') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart}; + this.doc.remove(new Range(newPos.row, newPos.column, newPos.row, newPos.column - lengthDiff)); + } + } + } + + this.$updating = false; + this.updateMarkers(); + }; + + this.updateAnchors = function(delta) { + this.pos.onChange(delta); + for (var i = this.others.length; i--;) + this.others[i].onChange(delta); + this.updateMarkers(); + }; + + this.updateMarkers = function() { + if (this.$updating) + return; + var _self = this; + var session = this.session; + var updateMarker = function(pos, className) { + session.removeMarker(pos.markerId); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column+_self.length), className, null, false); + }; + updateMarker(this.pos, this.mainClass); + for (var i = this.others.length; i--;) + updateMarker(this.others[i], this.othersClass); + }; + + this.onCursorChange = function(event) { + if (this.$updating || !this.session) return; + var pos = this.session.selection.getCursor(); + if (pos.row === this.pos.row && pos.column >= this.pos.column && pos.column <= this.pos.column + this.length) { + this.showOtherMarkers(); + this._emit("cursorEnter", event); + } else { + this.hideOtherMarkers(); + this._emit("cursorLeave", event); + } + }; + this.detach = function() { + this.session.removeMarker(this.pos && this.pos.markerId); + this.hideOtherMarkers(); + this.doc.removeEventListener("change", this.$onUpdate); + this.session.selection.removeEventListener("changeCursor", this.$onCursorChange); + this.session.setUndoSelect(true); + this.session = null; + }; + this.cancel = function() { + if (this.$undoStackDepth === -1) + return; + var undoManager = this.session.getUndoManager(); + var undosRequired = (undoManager.$undoStack || undoManager.$undostack).length - this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(true); + } + if (this.selectionBefore) + this.session.selection.fromJSON(this.selectionBefore); + }; + }).call(PlaceHolder.prototype); + + + exports.PlaceHolder = PlaceHolder; + }); + + ace.define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event","ace/lib/useragent"], function(acequire, exports, module) { + + var event = acequire("../lib/event"); + var useragent = acequire("../lib/useragent"); + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + + function onMouseDown(e) { + var ev = e.domEvent; + var alt = ev.altKey; + var shift = ev.shiftKey; + var ctrl = ev.ctrlKey; + var accel = e.getAccelKey(); + var button = e.getButton(); + + if (ctrl && useragent.isMac) + button = ev.button; + + if (e.editor.inMultiSelectMode && button == 2) { + e.editor.textInput.onContextMenu(e.domEvent); + return; + } + + if (!ctrl && !alt && !accel) { + if (button === 0 && e.editor.inMultiSelectMode) + e.editor.exitMultiSelectMode(); + return; + } + + if (button !== 0) + return; + + var editor = e.editor; + var selection = editor.selection; + var isMultiSelect = editor.inMultiSelectMode; + var pos = e.getDocumentPosition(); + var cursor = selection.getCursor(); + var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + + var mouseX = e.x, mouseY = e.y; + var onMouseSelection = function(e) { + mouseX = e.clientX; + mouseY = e.clientY; + }; + + var session = editor.session; + var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var screenCursor = screenAnchor; + + var selectionMode; + if (editor.$mouseHandler.$enableJumpToDef) { + if (ctrl && alt || accel && alt) + selectionMode = shift ? "block" : "add"; + else if (alt && editor.$blockSelectEnabled) + selectionMode = "block"; + } else { + if (accel && !alt) { + selectionMode = "add"; + if (!isMultiSelect && shift) + return; + } else if (alt && editor.$blockSelectEnabled) { + selectionMode = "block"; + } + } + + if (selectionMode && useragent.isMac && ev.ctrlKey) { + editor.$mouseHandler.cancelContextMenu(); + } + + if (selectionMode == "add") { + if (!isMultiSelect && inSelection) + return; // dragging + + if (!isMultiSelect) { + var range = selection.toOrientedRange(); + editor.addSelectionMarker(range); + } + + var oldRange = selection.rangeList.rangeAtPoint(pos); + + + editor.$blockScrolling++; + editor.inVirtualSelectionMode = true; + + if (shift) { + oldRange = null; + range = selection.ranges[0] || range; + editor.removeSelectionMarker(range); + } + editor.once("mouseup", function() { + var tmpSel = selection.toOrientedRange(); + + if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) + selection.substractPoint(tmpSel.cursor); + else { + if (shift) { + selection.substractPoint(range.cursor); + } else if (range) { + editor.removeSelectionMarker(range); + selection.addRange(range); + } + selection.addRange(tmpSel); + } + editor.$blockScrolling--; + editor.inVirtualSelectionMode = false; + }); + + } else if (selectionMode == "block") { + e.stop(); + editor.inVirtualSelectionMode = true; + var initialRange; + var rectSel = []; + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column, newCursor.offsetX); + + if (isSamePoint(screenCursor, newCursor) && isSamePoint(cursor, selection.lead)) + return; + screenCursor = newCursor; + + editor.$blockScrolling++; + editor.selection.moveToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + if (editor.$mouseHandler.$clickSelection && rectSel.length == 1 && rectSel[0].isEmpty()) + rectSel[0] = editor.$mouseHandler.$clickSelection.clone(); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + editor.$blockScrolling--; + }; + editor.$blockScrolling++; + if (isMultiSelect && !accel) { + selection.toSingleRange(); + } else if (!isMultiSelect && accel) { + initialRange = selection.toOrientedRange(); + editor.addSelectionMarker(initialRange); + } + + if (shift) + screenAnchor = session.documentToScreenPosition(selection.lead); + else + selection.moveToPosition(pos); + editor.$blockScrolling--; + + screenCursor = {row: -1, column: -1}; + + var onMouseSelectionEnd = function(e) { + clearInterval(timerId); + editor.removeSelectionMarkers(rectSel); + if (!rectSel.length) + rectSel = [selection.toOrientedRange()]; + editor.$blockScrolling++; + if (initialRange) { + editor.removeSelectionMarker(initialRange); + selection.toSingleRange(initialRange); + } + for (var i = 0; i < rectSel.length; i++) + selection.addRange(rectSel[i]); + editor.inVirtualSelectionMode = false; + editor.$mouseHandler.$clickSelection = null; + editor.$blockScrolling--; + }; + + var onSelectionInterval = blockSelect; + + event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); + var timerId = setInterval(function() {onSelectionInterval();}, 20); + + return e.preventDefault(); + } + } + + + exports.onMouseDown = onMouseDown; + + }); + + ace.define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"], function(acequire, exports, module) { + exports.defaultCommands = [{ + name: "addCursorAbove", + exec: function(editor) { editor.selectMoreLines(-1); }, + bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorBelow", + exec: function(editor) { editor.selectMoreLines(1); }, + bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorAboveSkipCurrent", + exec: function(editor) { editor.selectMoreLines(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorBelowSkipCurrent", + exec: function(editor) { editor.selectMoreLines(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectMoreBefore", + exec: function(editor) { editor.selectMore(-1); }, + bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectMoreAfter", + exec: function(editor) { editor.selectMore(1); }, + bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectNextBefore", + exec: function(editor) { editor.selectMore(-1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectNextAfter", + exec: function(editor) { editor.selectMore(1, true); }, + bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"}, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "splitIntoLines", + exec: function(editor) { editor.multiSelect.splitIntoLines(); }, + bindKey: {win: "Ctrl-Alt-L", mac: "Ctrl-Alt-L"}, + readOnly: true + }, { + name: "alignCursors", + exec: function(editor) { editor.alignCursors(); }, + bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}, + scrollIntoView: "cursor" + }, { + name: "findAll", + exec: function(editor) { editor.findAll(); }, + bindKey: {win: "Ctrl-Alt-K", mac: "Ctrl-Alt-G"}, + scrollIntoView: "cursor", + readOnly: true + }]; + exports.multiSelectCommands = [{ + name: "singleSelection", + bindKey: "esc", + exec: function(editor) { editor.exitMultiSelectMode(); }, + scrollIntoView: "cursor", + readOnly: true, + isAvailable: function(editor) {return editor && editor.inMultiSelectMode;} + }]; + + var HashHandler = acequire("../keyboard/hash_handler").HashHandler; + exports.keyboardHandler = new HashHandler(exports.multiSelectCommands); + + }); + + ace.define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"], function(acequire, exports, module) { + + var RangeList = acequire("./range_list").RangeList; + var Range = acequire("./range").Range; + var Selection = acequire("./selection").Selection; + var onMouseDown = acequire("./mouse/multi_select_handler").onMouseDown; + var event = acequire("./lib/event"); + var lang = acequire("./lib/lang"); + var commands = acequire("./commands/multi_select_commands"); + exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands); + var Search = acequire("./search").Search; + var search = new Search(); + + function find(session, needle, dir) { + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session); + } + var EditSession = acequire("./edit_session").EditSession; + (function() { + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; + }).call(EditSession.prototype); + (function() { + this.ranges = null; + this.rangeList = null; + this.addRange = function(range, $blockChangeEvents) { + if (!range) + return; + + if (!this.inMultiSelectMode && this.rangeCount === 0) { + var oldRange = this.toOrientedRange(); + this.rangeList.add(oldRange); + this.rangeList.add(range); + if (this.rangeList.ranges.length != 2) { + this.rangeList.removeAll(); + return $blockChangeEvents || this.fromOrientedRange(range); + } + this.rangeList.removeAll(); + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + + if (!range.cursor) + range.cursor = range.end; + + var removed = this.rangeList.add(range); + + this.$onAddRange(range); + + if (removed.length) + this.$onRemoveRange(removed); + + if (this.rangeCount > 1 && !this.inMultiSelectMode) { + this._signal("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + + return $blockChangeEvents || this.fromOrientedRange(range); + }; + + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) + this.$onRemoveRange(removed); + + range && this.fromOrientedRange(range); + }; + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0]; + } + }; + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + if (removed.length) + this.$onRemoveRange(removed); + else if(this.ranges[0]) + this.fromOrientedRange(this.ranges[0]); + }; + + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this._signal("addRange", {range: range}); + }; + + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + var lastRange = this.rangeList.ranges.pop(); + removed.push(lastRange); + this.rangeCount = 0; + } + + for (var i = removed.length; i--; ) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + + this._signal("removeRange", {ranges: removed}); + + if (this.rangeCount === 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._signal("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + + lastRange = lastRange || this.ranges[0]; + if (lastRange && !lastRange.isEqual(this.getRange())) + this.fromOrientedRange(lastRange); + }; + this.$initRangeList = function() { + if (this.rangeList) + return; + + this.rangeList = new RangeList(); + this.ranges = []; + this.rangeCount = 0; + }; + this.getAllRanges = function() { + return this.rangeCount ? this.rangeList.ranges.concat() : [this.getRange()]; + }; + + this.splitIntoLines = function () { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var range = this.getRange(); + var isBackwards = this.isBackwards(); + var startRow = range.start.row; + var endRow = range.end.row; + if (startRow == endRow) { + if (isBackwards) + var start = range.end, end = range.start; + else + var start = range.start, end = range.end; + + this.addRange(Range.fromPoints(end, end)); + this.addRange(Range.fromPoints(start, start)); + return; + } + + var rectSel = []; + var r = this.getLineRange(startRow, true); + r.start.column = range.start.column; + rectSel.push(r); + + for (var i = startRow + 1; i < endRow; i++) + rectSel.push(this.getLineRange(i, true)); + + r = this.getLineRange(endRow, true); + r.end.column = range.end.column; + rectSel.push(r); + + rectSel.forEach(this.addRange, this); + } + }; + this.toggleBlockSelection = function () { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var cursor = this.session.documentToScreenPosition(this.selectionLead); + var anchor = this.session.documentToScreenPosition(this.selectionAnchor); + + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { + var rectSel = []; + + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + var startOffsetX = screenCursor.offsetX; + var endOffsetX = screenAnchor.offsetX; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + var startOffsetX = screenAnchor.offsetX; + var endOffsetX = screenCursor.offsetX; + } + + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + + if (startColumn < 0) + startColumn = 0; + if (startRow < 0) + startRow = 0; + + if (startRow == endRow) + includeEmptyLines = true; + + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints( + this.session.screenToDocumentPosition(row, startColumn, startOffsetX), + this.session.screenToDocumentPosition(row, endColumn, endOffsetX) + ); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) + break; + var docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + + if (yBackwards) + rectSel.reverse(); + + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) + end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) + start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) + rectSel.splice(i, 1); + } + } + + return rectSel; + }; + }).call(Selection.prototype); + var Editor = acequire("./editor").Editor; + (function() { + this.updateSelectionMarkers = function() { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) + orientedRange.cursor = orientedRange.end; + + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); + + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; + this.removeSelectionMarker = function(range) { + if (!range.marker) + return; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) + this.session.$selectionMarkers.splice(index, 1); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; + + this.removeSelectionMarkers = function(ranges) { + var markerList = this.session.$selectionMarkers; + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.marker) + continue; + this.session.removeMarker(range.marker); + var index = markerList.indexOf(range); + if (index != -1) + markerList.splice(index, 1); + } + this.session.selectionMarkerCount = markerList.length; + }; + + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) + return; + this.inMultiSelectMode = true; + + this.setStyle("ace_multiselect"); + this.keyBinding.addKeyboardHandler(commands.keyboardHandler); + this.commands.setDefaultHandler("exec", this.$onMultiSelectExec); + + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) + return; + this.inMultiSelectMode = false; + + this.unsetStyle("ace_multiselect"); + this.keyBinding.removeKeyboardHandler(commands.keyboardHandler); + + this.commands.removeDefaultHandler("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + this._emit("changeSelection"); + }; + + this.$onMultiSelectExec = function(e) { + var command = e.command; + var editor = e.editor; + if (!editor.multiSelect) + return; + if (!command.multiSelectAction) { + var result = command.exec(editor, e.args || {}); + editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); + editor.multiSelect.mergeOverlappingRanges(); + } else if (command.multiSelectAction == "forEach") { + result = editor.forEachSelection(command, e.args); + } else if (command.multiSelectAction == "forEachLine") { + result = editor.forEachSelection(command, e.args, true); + } else if (command.multiSelectAction == "single") { + editor.exitMultiSelectMode(); + result = command.exec(editor, e.args || {}); + } else { + result = command.multiSelectAction(editor, e.args || {}); + } + return result; + }; + this.forEachSelection = function(cmd, args, options) { + if (this.inVirtualSelectionMode) + return; + var keepOrder = options && options.keepOrder; + var $byLines = options == true || options && options.$byLines; + var session = this.session; + var selection = this.selection; + var rangeList = selection.rangeList; + var ranges = (keepOrder ? selection : rangeList).ranges; + var result; + + if (!ranges.length) + return cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + + var reg = selection._eventRegistry; + selection._eventRegistry = {}; + + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = ranges.length; i--;) { + if ($byLines) { + while (i > 0 && ranges[i].start.row == ranges[i - 1].end.row) + i--; + } + tmpSel.fromOrientedRange(ranges[i]); + tmpSel.index = i; + this.selection = session.selection = tmpSel; + var cmdResult = cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + if (!result && cmdResult !== undefined) + result = cmdResult; + tmpSel.toOrientedRange(ranges[i]); + } + tmpSel.detach(); + + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); + + var anim = this.renderer.$scrollAnimation; + this.onCursorChange(); + this.onSelectionChange(); + if (anim && anim.from == anim.to) + this.renderer.animateScrolling(anim.from); + + return result; + }; + this.exitMultiSelectMode = function() { + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) + return; + this.multiSelect.toSingleRange(); + }; + + this.getSelectedText = function() { + var text = ""; + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var ranges = this.multiSelect.rangeList.ranges; + var buf = []; + for (var i = 0; i < ranges.length; i++) { + buf.push(this.session.getTextRange(ranges[i])); + } + var nl = this.session.getDocument().getNewLineCharacter(); + text = buf.join(nl); + if (text.length == (buf.length - 1) * nl.length) + text = ""; + } else if (!this.selection.isEmpty()) { + text = this.session.getTextRange(this.getSelectionRange()); + } + return text; + }; + + this.$checkMultiselectChange = function(e, anchor) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var range = this.multiSelect.ranges[0]; + if (this.multiSelect.isEmpty() && anchor == this.multiSelect.anchor) + return; + var pos = anchor == this.multiSelect.anchor + ? range.cursor == range.start ? range.end : range.start + : range.cursor; + if (pos.row != anchor.row + || this.session.$clipPositionToDocument(pos.row, pos.column).column != anchor.column) + this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()); + } + }; + this.findAll = function(needle, options, additive) { + options = options || {}; + options.needle = needle || options.needle; + if (options.needle == undefined) { + var range = this.selection.isEmpty() + ? this.selection.getWordRange() + : this.selection.getRange(); + options.needle = this.session.getTextRange(range); + } + this.$search.set(options); + + var ranges = this.$search.findAll(this.session); + if (!ranges.length) + return 0; + + this.$blockScrolling += 1; + var selection = this.multiSelect; + + if (!additive) + selection.toSingleRange(ranges[0]); + + for (var i = ranges.length; i--; ) + selection.addRange(ranges[i], true); + if (range && selection.rangeList.rangeAtPoint(range.start)) + selection.addRange(range, true); + + this.$blockScrolling -= 1; + + return ranges.length; + }; + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; + + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; + + var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); + + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); + var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); + } else { + var anchor = lead; + } + + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } + + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + if (skip) + var toRemove = range.cursor; + } + + this.selection.addRange(newRange); + if (toRemove) + this.selection.substractPoint(toRemove); + }; + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; + + for (var i = all.length; i--; ) { + var range = all[i]; + if (range.isEmpty()) { + var tmp = session.getWordRange(range.start.row, range.start.column); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + range.end.row = tmp.end.row; + range.end.column = tmp.end.column; + } + } + sel.mergeOverlappingRanges(); + + var words = []; + for (var i = all.length; i--; ) { + var range = all[i]; + words.unshift(session.getTextRange(range)); + } + + if (dir < 0) + words.unshift(words.pop()); + else + words.push(words.shift()); + + for (var i = all.length; i--; ) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + }; + this.selectMore = function(dir, skip, stopAtFirst) { + var session = this.session; + var sel = session.multiSelect; + + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + range = session.getWordRange(range.start.row, range.start.column); + range.cursor = dir == -1 ? range.start : range.end; + this.multiSelect.addRange(range); + if (stopAtFirst) + return; + } + var needle = session.getTextRange(range); + + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.$blockScrolling += 1; + this.session.unfold(newRange); + this.multiSelect.addRange(newRange); + this.$blockScrolling -= 1; + this.renderer.scrollCursorIntoView(null, 0.5); + } + if (skip) + this.multiSelect.substractPoint(range.cursor); + }; + this.alignCursors = function() { + var session = this.session; + var sel = session.multiSelect; + var ranges = sel.ranges; + var row = -1; + var sameRowRanges = ranges.filter(function(r) { + if (r.cursor.row == row) + return true; + row = r.cursor.row; + }); + + if (!ranges.length || sameRowRanges.length == ranges.length - 1) { + var range = this.selection.getRange(); + var fr = range.start.row, lr = range.end.row; + var guessRange = fr == lr; + if (guessRange) { + var max = this.session.getLength(); + var line; + do { + line = this.session.getLine(lr); + } while (/[=:]/.test(line) && ++lr < max); + do { + line = this.session.getLine(fr); + } while (/[=:]/.test(line) && --fr > 0); + + if (fr < 0) fr = 0; + if (lr >= max) lr = max - 1; + } + var lines = this.session.removeFullLines(fr, lr); + lines = this.$reAlignText(lines, guessRange); + this.session.insert({row: fr, column: 0}, lines.join("\n") + "\n"); + if (!guessRange) { + range.start.column = 0; + range.end.column = lines[lines.length - 1].length; + } + this.selection.setRange(range); + } else { + sameRowRanges.forEach(function(r) { + sel.substractPoint(r.cursor); + }); + + var maxCol = 0; + var minSpace = Infinity; + var spaceOffsets = ranges.map(function(r) { + var p = r.cursor; + var line = session.getLine(p.row); + var spaceOffset = line.substr(p.column).search(/\S/g); + if (spaceOffset == -1) + spaceOffset = 0; + + if (p.column > maxCol) + maxCol = p.column; + if (spaceOffset < minSpace) + minSpace = spaceOffset; + return spaceOffset; + }); + ranges.forEach(function(r, i) { + var p = r.cursor; + var l = maxCol - p.column; + var d = spaceOffsets[i] - minSpace; + if (l > d) + session.insert(p, lang.stringRepeat(" ", l - d)); + else + session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); + + r.start.column = r.end.column = maxCol; + r.start.row = r.end.row = p.row; + r.cursor = r.end; + }); + sel.fromOrientedRange(ranges[0]); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + } + }; + + this.$reAlignText = function(lines, forceLeft) { + var isLeftAligned = true, isRightAligned = true; + var startW, textW, endW; + + return lines.map(function(line) { + var m = line.match(/(\s*)(.*?)(\s*)([=:].*)/); + if (!m) + return [line]; + + if (startW == null) { + startW = m[1].length; + textW = m[2].length; + endW = m[3].length; + return m; + } + + if (startW + textW + endW != m[1].length + m[2].length + m[3].length) + isRightAligned = false; + if (startW != m[1].length) + isLeftAligned = false; + + if (startW > m[1].length) + startW = m[1].length; + if (textW < m[2].length) + textW = m[2].length; + if (endW > m[3].length) + endW = m[3].length; + + return m; + }).map(forceLeft ? alignLeft : + isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign); + + function spaces(n) { + return lang.stringRepeat(" ", n); + } + + function alignLeft(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(textW - m[2].length + endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function alignRight(m) { + return !m[2] ? m[0] : spaces(startW + textW - m[2].length) + m[2] + + spaces(endW, " ") + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function unAlign(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + }; + }).call(Editor.prototype); + + + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + exports.onSessionChange = function(e) { + var session = e.session; + if (session && !session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } + this.multiSelect = session && session.multiSelect; + + var oldSession = e.oldSession; + if (oldSession) { + oldSession.multiSelect.off("addRange", this.$onAddRange); + oldSession.multiSelect.off("removeRange", this.$onRemoveRange); + oldSession.multiSelect.off("multiSelect", this.$onMultiSelect); + oldSession.multiSelect.off("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.lead.off("change", this.$checkMultiselectChange); + oldSession.multiSelect.anchor.off("change", this.$checkMultiselectChange); + } + + if (session) { + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + session.multiSelect.lead.on("change", this.$checkMultiselectChange); + session.multiSelect.anchor.on("change", this.$checkMultiselectChange); + } + + if (session && this.inMultiSelectMode != session.selection.inMultiSelectMode) { + if (session.selection.inMultiSelectMode) + this.$onMultiSelect(); + else + this.$onSingleSelect(); + } + }; + function MultiSelect(editor) { + if (editor.$multiselectOnSessionChange) + return; + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + editor.$multiselectOnSessionChange = exports.onSessionChange.bind(editor); + editor.$checkMultiselectChange = editor.$checkMultiselectChange.bind(editor); + + editor.$multiselectOnSessionChange(editor); + editor.on("changeSession", editor.$multiselectOnSessionChange); + + editor.on("mousedown", onMouseDown); + editor.commands.addCommands(commands.defaultCommands); + + addAltCursorListeners(editor); + } + + function addAltCursorListeners(editor){ + var el = editor.textInput.getElement(); + var altCursor = false; + event.addListener(el, "keydown", function(e) { + var altDown = e.keyCode == 18 && !(e.ctrlKey || e.shiftKey || e.metaKey); + if (editor.$blockSelectEnabled && altDown) { + if (!altCursor) { + editor.renderer.setMouseCursor("crosshair"); + altCursor = true; + } + } else if (altCursor) { + reset(); + } + }); + + event.addListener(el, "keyup", reset); + event.addListener(el, "blur", reset); + function reset(e) { + if (altCursor) { + editor.renderer.setMouseCursor(""); + altCursor = false; + } + } + } + + exports.MultiSelect = MultiSelect; + + + acequire("./config").defineOptions(Editor.prototype, "editor", { + enableMultiselect: { + set: function(val) { + MultiSelect(this); + if (val) { + this.on("changeSession", this.$multiselectOnSessionChange); + this.on("mousedown", onMouseDown); + } else { + this.off("changeSession", this.$multiselectOnSessionChange); + this.off("mousedown", onMouseDown); + } + }, + value: true + }, + enableBlockSelect: { + set: function(val) { + this.$blockSelectEnabled = val; + }, + value: true + } + }); + + + + }); + + ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../../range").Range; + + var FoldMode = exports.FoldMode = function() {}; + + (function() { + + this.foldingStartMarker = null; + this.foldingStopMarker = null; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + if (this.foldingStartMarker.test(line)) + return "start"; + if (foldStyle == "markbeginend" + && this.foldingStopMarker + && this.foldingStopMarker.test(line)) + return "end"; + return ""; + }; + + this.getFoldWidgetRange = function(session, foldStyle, row) { + return null; + }; + + this.indentationBlock = function(session, row, column) { + var re = /\S/; + var line = session.getLine(row); + var startLevel = line.search(re); + if (startLevel == -1) + return; + + var startColumn = column || line.length; + var maxRow = session.getLength(); + var startRow = row; + var endRow = row; + + while (++row < maxRow) { + var level = session.getLine(row).search(re); + + if (level == -1) + continue; + + if (level <= startLevel) + break; + + endRow = row; + } + + if (endRow > startRow) { + var endColumn = session.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }; + + this.openingBracketBlock = function(session, bracket, row, column, typeRe) { + var start = {row: row, column: column + 1}; + var end = session.$findClosingBracket(bracket, start, typeRe); + if (!end) + return; + + var fw = session.foldWidgets[end.row]; + if (fw == null) + fw = session.getFoldWidget(end.row); + + if (fw == "start" && end.row > start.row) { + end.row --; + end.column = session.getLine(end.row).length; + } + return Range.fromPoints(start, end); + }; + + this.closingBracketBlock = function(session, bracket, row, column, typeRe) { + var end = {row: row, column: column}; + var start = session.$findOpeningBracket(bracket, end); + + if (!start) + return; + + start.column++; + end.column--; + + return Range.fromPoints(start, end); + }; + }).call(FoldMode.prototype); + + }); + + ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) { + "use strict"; + + exports.isDark = false; + exports.cssClass = "ace-tm"; + exports.cssText = ".ace-tm .ace_gutter {\ + background: #f0f0f0;\ + color: #333;\ + }\ + .ace-tm .ace_print-margin {\ + width: 1px;\ + background: #e8e8e8;\ + }\ + .ace-tm .ace_fold {\ + background-color: #6B72E6;\ + }\ + .ace-tm {\ + background-color: #FFFFFF;\ + color: black;\ + }\ + .ace-tm .ace_cursor {\ + color: black;\ + }\ + .ace-tm .ace_invisible {\ + color: rgb(191, 191, 191);\ + }\ + .ace-tm .ace_storage,\ + .ace-tm .ace_keyword {\ + color: blue;\ + }\ + .ace-tm .ace_constant {\ + color: rgb(197, 6, 11);\ + }\ + .ace-tm .ace_constant.ace_buildin {\ + color: rgb(88, 72, 246);\ + }\ + .ace-tm .ace_constant.ace_language {\ + color: rgb(88, 92, 246);\ + }\ + .ace-tm .ace_constant.ace_library {\ + color: rgb(6, 150, 14);\ + }\ + .ace-tm .ace_invalid {\ + background-color: rgba(255, 0, 0, 0.1);\ + color: red;\ + }\ + .ace-tm .ace_support.ace_function {\ + color: rgb(60, 76, 114);\ + }\ + .ace-tm .ace_support.ace_constant {\ + color: rgb(6, 150, 14);\ + }\ + .ace-tm .ace_support.ace_type,\ + .ace-tm .ace_support.ace_class {\ + color: rgb(109, 121, 222);\ + }\ + .ace-tm .ace_keyword.ace_operator {\ + color: rgb(104, 118, 135);\ + }\ + .ace-tm .ace_string {\ + color: rgb(3, 106, 7);\ + }\ + .ace-tm .ace_comment {\ + color: rgb(76, 136, 107);\ + }\ + .ace-tm .ace_comment.ace_doc {\ + color: rgb(0, 102, 255);\ + }\ + .ace-tm .ace_comment.ace_doc.ace_tag {\ + color: rgb(128, 159, 191);\ + }\ + .ace-tm .ace_constant.ace_numeric {\ + color: rgb(0, 0, 205);\ + }\ + .ace-tm .ace_variable {\ + color: rgb(49, 132, 149);\ + }\ + .ace-tm .ace_xml-pe {\ + color: rgb(104, 104, 91);\ + }\ + .ace-tm .ace_entity.ace_name.ace_function {\ + color: #0000A2;\ + }\ + .ace-tm .ace_heading {\ + color: rgb(12, 7, 255);\ + }\ + .ace-tm .ace_list {\ + color:rgb(185, 6, 144);\ + }\ + .ace-tm .ace_meta.ace_tag {\ + color:rgb(0, 22, 142);\ + }\ + .ace-tm .ace_string.ace_regex {\ + color: rgb(255, 0, 0)\ + }\ + .ace-tm .ace_marker-layer .ace_selection {\ + background: rgb(181, 213, 255);\ + }\ + .ace-tm.ace_multiselect .ace_selection.ace_start {\ + box-shadow: 0 0 3px 0px white;\ + }\ + .ace-tm .ace_marker-layer .ace_step {\ + background: rgb(252, 255, 0);\ + }\ + .ace-tm .ace_marker-layer .ace_stack {\ + background: rgb(164, 229, 101);\ + }\ + .ace-tm .ace_marker-layer .ace_bracket {\ + margin: -1px 0 0 -1px;\ + border: 1px solid rgb(192, 192, 192);\ + }\ + .ace-tm .ace_marker-layer .ace_active-line {\ + background: rgba(0, 0, 0, 0.07);\ + }\ + .ace-tm .ace_gutter-active-line {\ + background-color : #dcdcdc;\ + }\ + .ace-tm .ace_marker-layer .ace_selected-word {\ + background: rgb(250, 250, 255);\ + border: 1px solid rgb(200, 200, 250);\ + }\ + .ace-tm .ace_indent-guide {\ + background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ + }\ + "; + + var dom = acequire("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); + }); + + ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("./lib/oop"); + var dom = acequire("./lib/dom"); + var Range = acequire("./range").Range; + + + function LineWidgets(session) { + this.session = session; + this.session.widgetManager = this; + this.session.getRowLength = this.getRowLength; + this.session.$getWidgetScreenLength = this.$getWidgetScreenLength; + this.updateOnChange = this.updateOnChange.bind(this); + this.renderWidgets = this.renderWidgets.bind(this); + this.measureWidgets = this.measureWidgets.bind(this); + this.session._changedWidgets = []; + this.$onChangeEditor = this.$onChangeEditor.bind(this); + + this.session.on("change", this.updateOnChange); + this.session.on("changeFold", this.updateOnFold); + this.session.on("changeEditor", this.$onChangeEditor); + } + + (function() { + this.getRowLength = function(row) { + var h; + if (this.lineWidgets) + h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + else + h = 0; + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1 + h; + } else { + return this.$wrapData[row].length + 1 + h; + } + }; + + this.$getWidgetScreenLength = function() { + var screenRows = 0; + this.lineWidgets.forEach(function(w){ + if (w && w.rowCount && !w.hidden) + screenRows += w.rowCount; + }); + return screenRows; + }; + + this.$onChangeEditor = function(e) { + this.attach(e.editor); + }; + + this.attach = function(editor) { + if (editor && editor.widgetManager && editor.widgetManager != this) + editor.widgetManager.detach(); + + if (this.editor == editor) + return; + + this.detach(); + this.editor = editor; + + if (editor) { + editor.widgetManager = this; + editor.renderer.on("beforeRender", this.measureWidgets); + editor.renderer.on("afterRender", this.renderWidgets); + } + }; + this.detach = function(e) { + var editor = this.editor; + if (!editor) + return; + + this.editor = null; + editor.widgetManager = null; + + editor.renderer.off("beforeRender", this.measureWidgets); + editor.renderer.off("afterRender", this.renderWidgets); + var lineWidgets = this.session.lineWidgets; + lineWidgets && lineWidgets.forEach(function(w) { + if (w && w.el && w.el.parentNode) { + w._inDocument = false; + w.el.parentNode.removeChild(w.el); + } + }); + }; + + this.updateOnFold = function(e, session) { + var lineWidgets = session.lineWidgets; + if (!lineWidgets || !e.action) + return; + var fold = e.data; + var start = fold.start.row; + var end = fold.end.row; + var hide = e.action == "add"; + for (var i = start + 1; i < end; i++) { + if (lineWidgets[i]) + lineWidgets[i].hidden = hide; + } + if (lineWidgets[end]) { + if (hide) { + if (!lineWidgets[start]) + lineWidgets[start] = lineWidgets[end]; + else + lineWidgets[end].hidden = hide; + } else { + if (lineWidgets[start] == lineWidgets[end]) + lineWidgets[start] = undefined; + lineWidgets[end].hidden = hide; + } + } + }; + + this.updateOnChange = function(delta) { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + } else if (delta.action == 'remove') { + var removed = lineWidgets.splice(startRow + 1, len); + removed.forEach(function(w) { + w && this.removeLineWidget(w); + }, this); + this.$updateRows(); + } else { + var args = new Array(len); + args.unshift(startRow, 0); + lineWidgets.splice.apply(lineWidgets, args); + this.$updateRows(); + } + }; + + this.$updateRows = function() { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + var noWidgets = true; + lineWidgets.forEach(function(w, i) { + if (w) { + noWidgets = false; + w.row = i; + while (w.$oldWidget) { + w.$oldWidget.row = i; + w = w.$oldWidget; + } + } + }); + if (noWidgets) + this.session.lineWidgets = null; + }; + + this.addLineWidget = function(w) { + if (!this.session.lineWidgets) + this.session.lineWidgets = new Array(this.session.getLength()); + + var old = this.session.lineWidgets[w.row]; + if (old) { + w.$oldWidget = old; + if (old.el && old.el.parentNode) { + old.el.parentNode.removeChild(old.el); + old._inDocument = false; + } + } + + this.session.lineWidgets[w.row] = w; + + w.session = this.session; + + var renderer = this.editor.renderer; + if (w.html && !w.el) { + w.el = dom.createElement("div"); + w.el.innerHTML = w.html; + } + if (w.el) { + dom.addCssClass(w.el, "ace_lineWidgetContainer"); + w.el.style.position = "absolute"; + w.el.style.zIndex = 5; + renderer.container.appendChild(w.el); + w._inDocument = true; + } + + if (!w.coverGutter) { + w.el.style.zIndex = 3; + } + if (w.pixelHeight == null) { + w.pixelHeight = w.el.offsetHeight; + } + if (w.rowCount == null) { + w.rowCount = w.pixelHeight / renderer.layerConfig.lineHeight; + } + + var fold = this.session.getFoldAt(w.row, 0); + w.$fold = fold; + if (fold) { + var lineWidgets = this.session.lineWidgets; + if (w.row == fold.end.row && !lineWidgets[fold.start.row]) + lineWidgets[fold.start.row] = w; + else + w.hidden = true; + } + + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + + this.$updateRows(); + this.renderWidgets(null, renderer); + this.onWidgetChanged(w); + return w; + }; + + this.removeLineWidget = function(w) { + w._inDocument = false; + w.session = null; + if (w.el && w.el.parentNode) + w.el.parentNode.removeChild(w.el); + if (w.editor && w.editor.destroy) try { + w.editor.destroy(); + } catch(e){} + if (this.session.lineWidgets) { + var w1 = this.session.lineWidgets[w.row]; + if (w1 == w) { + this.session.lineWidgets[w.row] = w.$oldWidget; + if (w.$oldWidget) + this.onWidgetChanged(w.$oldWidget); + } else { + while (w1) { + if (w1.$oldWidget == w) { + w1.$oldWidget = w.$oldWidget; + break; + } + w1 = w1.$oldWidget; + } + } + } + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + this.$updateRows(); + }; + + this.getWidgetsAtRow = function(row) { + var lineWidgets = this.session.lineWidgets; + var w = lineWidgets && lineWidgets[row]; + var list = []; + while (w) { + list.push(w); + w = w.$oldWidget; + } + return list; + }; + + this.onWidgetChanged = function(w) { + this.session._changedWidgets.push(w); + this.editor && this.editor.renderer.updateFull(); + }; + + this.measureWidgets = function(e, renderer) { + var changedWidgets = this.session._changedWidgets; + var config = renderer.layerConfig; + + if (!changedWidgets || !changedWidgets.length) return; + var min = Infinity; + for (var i = 0; i < changedWidgets.length; i++) { + var w = changedWidgets[i]; + if (!w || !w.el) continue; + if (w.session != this.session) continue; + if (!w._inDocument) { + if (this.session.lineWidgets[w.row] != w) + continue; + w._inDocument = true; + renderer.container.appendChild(w.el); + } + + w.h = w.el.offsetHeight; + + if (!w.fixedWidth) { + w.w = w.el.offsetWidth; + w.screenWidth = Math.ceil(w.w / config.characterWidth); + } + + var rowCount = w.h / config.lineHeight; + if (w.coverLine) { + rowCount -= this.session.getRowLineCount(w.row); + if (rowCount < 0) + rowCount = 0; + } + if (w.rowCount != rowCount) { + w.rowCount = rowCount; + if (w.row < min) + min = w.row; + } + } + if (min != Infinity) { + this.session._emit("changeFold", {data:{start:{row: min}}}); + this.session.lineWidgetWidth = null; + } + this.session._changedWidgets = []; + }; + + this.renderWidgets = function(e, renderer) { + var config = renderer.layerConfig; + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) + return; + var first = Math.min(this.firstRow, config.firstRow); + var last = Math.max(this.lastRow, config.lastRow, lineWidgets.length); + + while (first > 0 && !lineWidgets[first]) + first--; + + this.firstRow = config.firstRow; + this.lastRow = config.lastRow; + + renderer.$cursorLayer.config = config; + for (var i = first; i <= last; i++) { + var w = lineWidgets[i]; + if (!w || !w.el) continue; + if (w.hidden) { + w.el.style.top = -100 - (w.pixelHeight || 0) + "px"; + continue; + } + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + var top = renderer.$cursorLayer.getPixelPosition({row: i, column:0}, true).top; + if (!w.coverLine) + top += config.lineHeight * this.session.getRowLineCount(w.row); + w.el.style.top = top - config.offset + "px"; + + var left = w.coverGutter ? 0 : renderer.gutterWidth; + if (!w.fixedWidth) + left -= renderer.scrollLeft; + w.el.style.left = left + "px"; + + if (w.fullWidth && w.screenWidth) { + w.el.style.minWidth = config.width + 2 * config.padding + "px"; + } + + if (w.fixedWidth) { + w.el.style.right = renderer.scrollBar.getWidth() + "px"; + } else { + w.el.style.right = ""; + } + } + }; + + }).call(LineWidgets.prototype); + + + exports.LineWidgets = LineWidgets; + + }); + + ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"], function(acequire, exports, module) { + "use strict"; + var LineWidgets = acequire("../line_widgets").LineWidgets; + var dom = acequire("../lib/dom"); + var Range = acequire("../range").Range; + + function binarySearch(array, needle, comparator) { + var first = 0; + var last = array.length - 1; + + while (first <= last) { + var mid = (first + last) >> 1; + var c = comparator(needle, array[mid]); + if (c > 0) + first = mid + 1; + else if (c < 0) + last = mid - 1; + else + return mid; + } + return -(first + 1); + } + + function findAnnotations(session, row, dir) { + var annotations = session.getAnnotations().sort(Range.comparePoints); + if (!annotations.length) + return; + + var i = binarySearch(annotations, {row: row, column: -1}, Range.comparePoints); + if (i < 0) + i = -i - 1; + + if (i >= annotations.length) + i = dir > 0 ? 0 : annotations.length - 1; + else if (i === 0 && dir < 0) + i = annotations.length - 1; + + var annotation = annotations[i]; + if (!annotation || !dir) + return; + + if (annotation.row === row) { + do { + annotation = annotations[i += dir]; + } while (annotation && annotation.row === row); + if (!annotation) + return annotations.slice(); + } + + + var matched = []; + row = annotation.row; + do { + matched[dir < 0 ? "unshift" : "push"](annotation); + annotation = annotations[i += dir]; + } while (annotation && annotation.row == row); + return matched.length && matched; + } + + exports.showErrorMarker = function(editor, dir) { + var session = editor.session; + if (!session.widgetManager) { + session.widgetManager = new LineWidgets(session); + session.widgetManager.attach(editor); + } + + var pos = editor.getCursorPosition(); + var row = pos.row; + var oldWidget = session.widgetManager.getWidgetsAtRow(row).filter(function(w) { + return w.type == "errorMarker"; + })[0]; + if (oldWidget) { + oldWidget.destroy(); + } else { + row -= dir; + } + var annotations = findAnnotations(session, row, dir); + var gutterAnno; + if (annotations) { + var annotation = annotations[0]; + pos.column = (annotation.pos && typeof annotation.column != "number" + ? annotation.pos.sc + : annotation.column) || 0; + pos.row = annotation.row; + gutterAnno = editor.renderer.$gutterLayer.$annotations[pos.row]; + } else if (oldWidget) { + return; + } else { + gutterAnno = { + text: ["Looks good!"], + className: "ace_ok" + }; + } + editor.session.unfold(pos.row); + editor.selection.moveToPosition(pos); + + var w = { + row: pos.row, + fixedWidth: true, + coverGutter: true, + el: dom.createElement("div"), + type: "errorMarker" + }; + var el = w.el.appendChild(dom.createElement("div")); + var arrow = w.el.appendChild(dom.createElement("div")); + arrow.className = "error_widget_arrow " + gutterAnno.className; + + var left = editor.renderer.$cursorLayer + .getPixelPosition(pos).left; + arrow.style.left = left + editor.renderer.gutterWidth - 5 + "px"; + + w.el.className = "error_widget_wrapper"; + el.className = "error_widget " + gutterAnno.className; + el.innerHTML = gutterAnno.text.join("
      "); + + el.appendChild(dom.createElement("div")); + + var kb = function(_, hashId, keyString) { + if (hashId === 0 && (keyString === "esc" || keyString === "return")) { + w.destroy(); + return {command: "null"}; + } + }; + + w.destroy = function() { + if (editor.$mouseHandler.isMousePressed) + return; + editor.keyBinding.removeKeyboardHandler(kb); + session.widgetManager.removeLineWidget(w); + editor.off("changeSelection", w.destroy); + editor.off("changeSession", w.destroy); + editor.off("mouseup", w.destroy); + editor.off("change", w.destroy); + }; + + editor.keyBinding.addKeyboardHandler(kb); + editor.on("changeSelection", w.destroy); + editor.on("changeSession", w.destroy); + editor.on("mouseup", w.destroy); + editor.on("change", w.destroy); + + editor.session.widgetManager.addLineWidget(w); + + w.el.onmousedown = editor.focus.bind(editor); + + editor.renderer.scrollCursorIntoView(null, 0.5, {bottom: w.el.offsetHeight}); + }; + + + dom.importCssString("\ + .error_widget_wrapper {\ + background: inherit;\ + color: inherit;\ + border:none\ + }\ + .error_widget {\ + border-top: solid 2px;\ + border-bottom: solid 2px;\ + margin: 5px 0;\ + padding: 10px 40px;\ + white-space: pre-wrap;\ + }\ + .error_widget.ace_error, .error_widget_arrow.ace_error{\ + border-color: #ff5a5a\ + }\ + .error_widget.ace_warning, .error_widget_arrow.ace_warning{\ + border-color: #F1D817\ + }\ + .error_widget.ace_info, .error_widget_arrow.ace_info{\ + border-color: #5a5a5a\ + }\ + .error_widget.ace_ok, .error_widget_arrow.ace_ok{\ + border-color: #5aaa5a\ + }\ + .error_widget_arrow {\ + position: absolute;\ + border: solid 5px;\ + border-top-color: transparent!important;\ + border-right-color: transparent!important;\ + border-left-color: transparent!important;\ + top: -5px;\ + }\ + ", ""); + + }); + + ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"], function(acequire, exports, module) { + "use strict"; + + acequire("./lib/fixoldbrowsers"); + + var dom = acequire("./lib/dom"); + var event = acequire("./lib/event"); + + var Editor = acequire("./editor").Editor; + var EditSession = acequire("./edit_session").EditSession; + var UndoManager = acequire("./undomanager").UndoManager; + var Renderer = acequire("./virtual_renderer").VirtualRenderer; + acequire("./worker/worker_client"); + acequire("./keyboard/hash_handler"); + acequire("./placeholder"); + acequire("./multi_select"); + acequire("./mode/folding/fold_mode"); + acequire("./theme/textmate"); + acequire("./ext/error_marker"); + + exports.config = acequire("./config"); + exports.acequire = acequire; + + if (true) + exports.define = __webpack_require__(67); + exports.edit = function(el) { + if (typeof el == "string") { + var _id = el; + el = document.getElementById(_id); + if (!el) + throw new Error("ace.edit can't find div #" + _id); + } + + if (el && el.env && el.env.editor instanceof Editor) + return el.env.editor; + + var value = ""; + if (el && /input|textarea/i.test(el.tagName)) { + var oldNode = el; + value = oldNode.value; + el = dom.createElement("pre"); + oldNode.parentNode.replaceChild(el, oldNode); + } else if (el) { + value = dom.getInnerText(el); + el.innerHTML = ""; + } + + var doc = exports.createEditSession(value); + + var editor = new Editor(new Renderer(el)); + editor.setSession(doc); + + var env = { + document: doc, + editor: editor, + onResize: editor.resize.bind(editor, null) + }; + if (oldNode) env.textarea = oldNode; + event.addListener(window, "resize", env.onResize); + editor.on("destroy", function() { + event.removeListener(window, "resize", env.onResize); + env.editor.container.env = null; // prevent memory leak on old ie + }); + editor.container.env = editor.env = env; + return editor; + }; + exports.createEditSession = function(text, mode) { + var doc = new EditSession(text, mode); + doc.setUndoManager(new UndoManager()); + return doc; + }; + exports.EditSession = EditSession; + exports.UndoManager = UndoManager; + exports.version = "1.2.9"; + }); + (function() { + ace.acequire(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = ace.define; + } + if (!window.ace) + window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) + window.ace[key] = a[key]; + }); + })(); + + module.exports = window.ace.acequire("ace/ace"); + +/***/ }, +/* 67 */ +/***/ function(module, exports) { + + module.exports = function() { throw new Error("define cannot be used indirect"); }; + + +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + + ace.define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../lib/oop"); + var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules; + + var JsonHighlightRules = function() { + this.$rules = { + "start" : [ + { + token : "variable", // single line + regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)' + }, { + token : "string", // single line + regex : '"', + next : "string" + }, { + token : "constant.numeric", // hex + regex : "0[xX][0-9a-fA-F]+\\b" + }, { + token : "constant.numeric", // float + regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" + }, { + token : "constant.language.boolean", + regex : "(?:true|false)\\b" + }, { + token : "text", // single quoted strings are not allowed + regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" + }, { + token : "comment", // comments are not allowed, but who cares? + regex : "\\/\\/.*$" + }, { + token : "comment.start", // comments are not allowed, but who cares? + regex : "\\/\\*", + next : "comment" + }, { + token : "paren.lparen", + regex : "[[({]" + }, { + token : "paren.rparen", + regex : "[\\])}]" + }, { + token : "text", + regex : "\\s+" + } + ], + "string" : [ + { + token : "constant.language.escape", + regex : /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/ + }, { + token : "string", + regex : '"|$', + next : "start" + }, { + defaultToken : "string" + } + ], + "comment" : [ + { + token : "comment.end", // comments are not allowed, but who cares? + regex : "\\*\\/", + next : "start" + }, { + defaultToken: "comment" + } + ] + }; + + }; + + oop.inherits(JsonHighlightRules, TextHighlightRules); + + exports.JsonHighlightRules = JsonHighlightRules; + }); + + ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(acequire, exports, module) { + "use strict"; + + var Range = acequire("../range").Range; + + var MatchingBraceOutdent = function() {}; + + (function() { + + this.checkOutdent = function(line, input) { + if (! /^\s+$/.test(line)) + return false; + + return /^\s*\}/.test(input); + }; + + this.autoOutdent = function(doc, row) { + var line = doc.getLine(row); + var match = line.match(/^(\s*\})/); + + if (!match) return 0; + + var column = match[1].length; + var openBracePos = doc.findMatchingBracket({row: row, column: column}); + + if (!openBracePos || openBracePos.row == row) return 0; + + var indent = this.$getIndent(doc.getLine(openBracePos.row)); + doc.replace(new Range(row, 0, row, column-1), indent); + }; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + + }).call(MatchingBraceOutdent.prototype); + + exports.MatchingBraceOutdent = MatchingBraceOutdent; + }); + + ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../../lib/oop"); + var Range = acequire("../../range").Range; + var BaseFoldMode = acequire("./fold_mode").FoldMode; + + var FoldMode = exports.FoldMode = function(commentRegex) { + if (commentRegex) { + this.foldingStartMarker = new RegExp( + this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start) + ); + this.foldingStopMarker = new RegExp( + this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end) + ); + } + }; + oop.inherits(FoldMode, BaseFoldMode); + + (function() { + + this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/; + this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/; + this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/; + this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/; + this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/; + this._getFoldWidgetBase = this.getFoldWidget; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + + if (this.singleLineBlockCommentRe.test(line)) { + if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line)) + return ""; + } + + var fw = this._getFoldWidgetBase(session, foldStyle, row); + + if (!fw && this.startRegionRe.test(line)) + return "start"; // lineCommentRegionStart + + return fw; + }; + + this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) { + var line = session.getLine(row); + + if (this.startRegionRe.test(line)) + return this.getCommentRegionBlock(session, line, row); + + var match = line.match(this.foldingStartMarker); + if (match) { + var i = match.index; + + if (match[1]) + return this.openingBracketBlock(session, match[1], row, i); + + var range = session.getCommentFoldRange(row, i + match[0].length, 1); + + if (range && !range.isMultiLine()) { + if (forceMultiline) { + range = this.getSectionRange(session, row); + } else if (foldStyle != "all") + range = null; + } + + return range; + } + + if (foldStyle === "markbegin") + return; + + var match = line.match(this.foldingStopMarker); + if (match) { + var i = match.index + match[0].length; + + if (match[1]) + return this.closingBracketBlock(session, match[1], row, i); + + return session.getCommentFoldRange(row, i, -1); + } + }; + + this.getSectionRange = function(session, row) { + var line = session.getLine(row); + var startIndent = line.search(/\S/); + var startRow = row; + var startColumn = line.length; + row = row + 1; + var endRow = row; + var maxRow = session.getLength(); + while (++row < maxRow) { + line = session.getLine(row); + var indent = line.search(/\S/); + if (indent === -1) + continue; + if (startIndent > indent) + break; + var subRange = this.getFoldWidgetRange(session, "all", row); + + if (subRange) { + if (subRange.start.row <= startRow) { + break; + } else if (subRange.isMultiLine()) { + row = subRange.end.row; + } else if (startIndent == indent) { + break; + } + } + endRow = row; + } + + return new Range(startRow, startColumn, endRow, session.getLine(endRow).length); + }; + this.getCommentRegionBlock = function(session, line, row) { + var startColumn = line.search(/\s*$/); + var maxRow = session.getLength(); + var startRow = row; + + var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/; + var depth = 1; + while (++row < maxRow) { + line = session.getLine(row); + var m = re.exec(line); + if (!m) continue; + if (m[1]) depth--; + else depth++; + + if (!depth) break; + } + + var endRow = row; + if (endRow > startRow) { + return new Range(startRow, startColumn, endRow, line.length); + } + }; + + }).call(FoldMode.prototype); + + }); + + ace.define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"], function(acequire, exports, module) { + "use strict"; + + var oop = acequire("../lib/oop"); + var TextMode = acequire("./text").Mode; + var HighlightRules = acequire("./json_highlight_rules").JsonHighlightRules; + var MatchingBraceOutdent = acequire("./matching_brace_outdent").MatchingBraceOutdent; + var CstyleBehaviour = acequire("./behaviour/cstyle").CstyleBehaviour; + var CStyleFoldMode = acequire("./folding/cstyle").FoldMode; + var WorkerClient = acequire("../worker/worker_client").WorkerClient; + + var Mode = function() { + this.HighlightRules = HighlightRules; + this.$outdent = new MatchingBraceOutdent(); + this.$behaviour = new CstyleBehaviour(); + this.foldingRules = new CStyleFoldMode(); + }; + oop.inherits(Mode, TextMode); + + (function() { + + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + if (state == "start") { + var match = line.match(/^.*[\{\(\[]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; + + this.checkOutdent = function(state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function(state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; + + this.createWorker = function(session) { + var worker = new WorkerClient(["ace"], __webpack_require__(69), "JsonWorker"); + worker.attachToDocument(session.getDocument()); + + worker.on("annotate", function(e) { + session.setAnnotations(e.data); + }); + + worker.on("terminate", function() { + session.clearAnnotations(); + }); + + return worker; + }; + + + this.$id = "ace/mode/json"; + }).call(Mode.prototype); + + exports.Mode = Mode; + }); + + +/***/ }, +/* 69 */ +/***/ function(module, exports) { + + module.exports.id = 'ace/mode/json_worker'; + module.exports.src = "\"no use strict\";!function(window){function resolveModuleId(id,paths){for(var testPath=id,tail=\"\";testPath;){var alias=paths[testPath];if(\"string\"==typeof alias)return alias+tail;if(alias)return alias.location.replace(/\\/*$/,\"/\")+(tail||alias.main||alias.name);if(alias===!1)return\"\";var i=testPath.lastIndexOf(\"/\");if(-1===i)break;tail=testPath.substr(i)+tail,testPath=testPath.slice(0,i)}return id}if(!(void 0!==window.window&&window.document||window.acequire&&window.define)){window.console||(window.console=function(){var msgs=Array.prototype.slice.call(arguments,0);postMessage({type:\"log\",data:msgs})},window.console.error=window.console.warn=window.console.log=window.console.trace=window.console),window.window=window,window.ace=window,window.onerror=function(message,file,line,col,err){postMessage({type:\"error\",data:{message:message,data:err.data,file:file,line:line,col:col,stack:err.stack}})},window.normalizeModule=function(parentId,moduleName){if(-1!==moduleName.indexOf(\"!\")){var chunks=moduleName.split(\"!\");return window.normalizeModule(parentId,chunks[0])+\"!\"+window.normalizeModule(parentId,chunks[1])}if(\".\"==moduleName.charAt(0)){var base=parentId.split(\"/\").slice(0,-1).join(\"/\");for(moduleName=(base?base+\"/\":\"\")+moduleName;-1!==moduleName.indexOf(\".\")&&previous!=moduleName;){var previous=moduleName;moduleName=moduleName.replace(/^\\.\\//,\"\").replace(/\\/\\.\\//,\"/\").replace(/[^\\/]+\\/\\.\\.\\//,\"\")}}return moduleName},window.acequire=function acequire(parentId,id){if(id||(id=parentId,parentId=null),!id.charAt)throw Error(\"worker.js acequire() accepts only (parentId, id) as arguments\");id=window.normalizeModule(parentId,id);var module=window.acequire.modules[id];if(module)return module.initialized||(module.initialized=!0,module.exports=module.factory().exports),module.exports;if(!window.acequire.tlns)return console.log(\"unable to load \"+id);var path=resolveModuleId(id,window.acequire.tlns);return\".js\"!=path.slice(-3)&&(path+=\".js\"),window.acequire.id=id,window.acequire.modules[id]={},importScripts(path),window.acequire(parentId,id)},window.acequire.modules={},window.acequire.tlns={},window.define=function(id,deps,factory){if(2==arguments.length?(factory=deps,\"string\"!=typeof id&&(deps=id,id=window.acequire.id)):1==arguments.length&&(factory=id,deps=[],id=window.acequire.id),\"function\"!=typeof factory)return window.acequire.modules[id]={exports:factory,initialized:!0},void 0;deps.length||(deps=[\"require\",\"exports\",\"module\"]);var req=function(childId){return window.acequire(id,childId)};window.acequire.modules[id]={exports:{},factory:function(){var module=this,returnExports=factory.apply(this,deps.map(function(dep){switch(dep){case\"require\":return req;case\"exports\":return module.exports;case\"module\":return module;default:return req(dep)}}));return returnExports&&(module.exports=returnExports),module}}},window.define.amd={},acequire.tlns={},window.initBaseUrls=function(topLevelNamespaces){for(var i in topLevelNamespaces)acequire.tlns[i]=topLevelNamespaces[i]},window.initSender=function(){var EventEmitter=window.acequire(\"ace/lib/event_emitter\").EventEmitter,oop=window.acequire(\"ace/lib/oop\"),Sender=function(){};return function(){oop.implement(this,EventEmitter),this.callback=function(data,callbackId){postMessage({type:\"call\",id:callbackId,data:data})},this.emit=function(name,data){postMessage({type:\"event\",name:name,data:data})}}.call(Sender.prototype),new Sender};var main=window.main=null,sender=window.sender=null;window.onmessage=function(e){var msg=e.data;if(msg.event&&sender)sender._signal(msg.event,msg.data);else if(msg.command)if(main[msg.command])main[msg.command].apply(main,msg.args);else{if(!window[msg.command])throw Error(\"Unknown command:\"+msg.command);window[msg.command].apply(window,msg.args)}else if(msg.init){window.initBaseUrls(msg.tlns),acequire(\"ace/lib/es5-shim\"),sender=window.sender=window.initSender();var clazz=acequire(msg.module)[msg.classname];main=window.main=new clazz(sender)}}}}(this),ace.define(\"ace/lib/oop\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.inherits=function(ctor,superCtor){ctor.super_=superCtor,ctor.prototype=Object.create(superCtor.prototype,{constructor:{value:ctor,enumerable:!1,writable:!0,configurable:!0}})},exports.mixin=function(obj,mixin){for(var key in mixin)obj[key]=mixin[key];return obj},exports.implement=function(proto,mixin){exports.mixin(proto,mixin)}}),ace.define(\"ace/range\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";var comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},Range=function(startRow,startColumn,endRow,endColumn){this.start={row:startRow,column:startColumn},this.end={row:endRow,column:endColumn}};(function(){this.isEqual=function(range){return this.start.row===range.start.row&&this.end.row===range.end.row&&this.start.column===range.start.column&&this.end.column===range.end.column},this.toString=function(){return\"Range: [\"+this.start.row+\"/\"+this.start.column+\"] -> [\"+this.end.row+\"/\"+this.end.column+\"]\"},this.contains=function(row,column){return 0==this.compare(row,column)},this.compareRange=function(range){var cmp,end=range.end,start=range.start;return cmp=this.compare(end.row,end.column),1==cmp?(cmp=this.compare(start.row,start.column),1==cmp?2:0==cmp?1:0):-1==cmp?-2:(cmp=this.compare(start.row,start.column),-1==cmp?-1:1==cmp?42:0)},this.comparePoint=function(p){return this.compare(p.row,p.column)},this.containsRange=function(range){return 0==this.comparePoint(range.start)&&0==this.comparePoint(range.end)},this.intersects=function(range){var cmp=this.compareRange(range);return-1==cmp||0==cmp||1==cmp},this.isEnd=function(row,column){return this.end.row==row&&this.end.column==column},this.isStart=function(row,column){return this.start.row==row&&this.start.column==column},this.setStart=function(row,column){\"object\"==typeof row?(this.start.column=row.column,this.start.row=row.row):(this.start.row=row,this.start.column=column)},this.setEnd=function(row,column){\"object\"==typeof row?(this.end.column=row.column,this.end.row=row.row):(this.end.row=row,this.end.column=column)},this.inside=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)||this.isStart(row,column)?!1:!0:!1},this.insideStart=function(row,column){return 0==this.compare(row,column)?this.isEnd(row,column)?!1:!0:!1},this.insideEnd=function(row,column){return 0==this.compare(row,column)?this.isStart(row,column)?!1:!0:!1},this.compare=function(row,column){return this.isMultiLine()||row!==this.start.row?this.start.row>row?-1:row>this.end.row?1:this.start.row===row?column>=this.start.column?0:-1:this.end.row===row?this.end.column>=column?0:1:0:this.start.column>column?-1:column>this.end.column?1:0},this.compareStart=function(row,column){return this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.compareEnd=function(row,column){return this.end.row==row&&this.end.column==column?1:this.compare(row,column)},this.compareInside=function(row,column){return this.end.row==row&&this.end.column==column?1:this.start.row==row&&this.start.column==column?-1:this.compare(row,column)},this.clipRows=function(firstRow,lastRow){if(this.end.row>lastRow)var end={row:lastRow+1,column:0};else if(firstRow>this.end.row)var end={row:firstRow,column:0};if(this.start.row>lastRow)var start={row:lastRow+1,column:0};else if(firstRow>this.start.row)var start={row:firstRow,column:0};return Range.fromPoints(start||this.start,end||this.end)},this.extend=function(row,column){var cmp=this.compare(row,column);if(0==cmp)return this;if(-1==cmp)var start={row:row,column:column};else var end={row:row,column:column};return Range.fromPoints(start||this.start,end||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return Range.fromPoints(this.start,this.end)},this.collapseRows=function(){return 0==this.end.column?new Range(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new Range(this.start.row,0,this.end.row,0)},this.toScreenRange=function(session){var screenPosStart=session.documentToScreenPosition(this.start),screenPosEnd=session.documentToScreenPosition(this.end);return new Range(screenPosStart.row,screenPosStart.column,screenPosEnd.row,screenPosEnd.column)},this.moveBy=function(row,column){this.start.row+=row,this.start.column+=column,this.end.row+=row,this.end.column+=column}}).call(Range.prototype),Range.fromPoints=function(start,end){return new Range(start.row,start.column,end.row,end.column)},Range.comparePoints=comparePoints,Range.comparePoints=function(p1,p2){return p1.row-p2.row||p1.column-p2.column},exports.Range=Range}),ace.define(\"ace/apply_delta\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.applyDelta=function(docLines,delta){var row=delta.start.row,startColumn=delta.start.column,line=docLines[row]||\"\";switch(delta.action){case\"insert\":var lines=delta.lines;if(1===lines.length)docLines[row]=line.substring(0,startColumn)+delta.lines[0]+line.substring(startColumn);else{var args=[row,1].concat(delta.lines);docLines.splice.apply(docLines,args),docLines[row]=line.substring(0,startColumn)+docLines[row],docLines[row+delta.lines.length-1]+=line.substring(startColumn)}break;case\"remove\":var endColumn=delta.end.column,endRow=delta.end.row;row===endRow?docLines[row]=line.substring(0,startColumn)+line.substring(endColumn):docLines.splice(row,endRow-row+1,line.substring(0,startColumn)+docLines[endRow].substring(endColumn))}}}),ace.define(\"ace/lib/event_emitter\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";var EventEmitter={},stopPropagation=function(){this.propagationStopped=!0},preventDefault=function(){this.defaultPrevented=!0};EventEmitter._emit=EventEmitter._dispatchEvent=function(eventName,e){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var listeners=this._eventRegistry[eventName]||[],defaultHandler=this._defaultHandlers[eventName];if(listeners.length||defaultHandler){\"object\"==typeof e&&e||(e={}),e.type||(e.type=eventName),e.stopPropagation||(e.stopPropagation=stopPropagation),e.preventDefault||(e.preventDefault=preventDefault),listeners=listeners.slice();for(var i=0;listeners.length>i&&(listeners[i](e,this),!e.propagationStopped);i++);return defaultHandler&&!e.defaultPrevented?defaultHandler(e,this):void 0}},EventEmitter._signal=function(eventName,e){var listeners=(this._eventRegistry||{})[eventName];if(listeners){listeners=listeners.slice();for(var i=0;listeners.length>i;i++)listeners[i](e,this)}},EventEmitter.once=function(eventName,callback){var _self=this;callback&&this.addEventListener(eventName,function newCallback(){_self.removeEventListener(eventName,newCallback),callback.apply(null,arguments)})},EventEmitter.setDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers||(handlers=this._defaultHandlers={_disabled_:{}}),handlers[eventName]){var old=handlers[eventName],disabled=handlers._disabled_[eventName];disabled||(handlers._disabled_[eventName]=disabled=[]),disabled.push(old);var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}handlers[eventName]=callback},EventEmitter.removeDefaultHandler=function(eventName,callback){var handlers=this._defaultHandlers;if(handlers){var disabled=handlers._disabled_[eventName];if(handlers[eventName]==callback)handlers[eventName],disabled&&this.setDefaultHandler(eventName,disabled.pop());else if(disabled){var i=disabled.indexOf(callback);-1!=i&&disabled.splice(i,1)}}},EventEmitter.on=EventEmitter.addEventListener=function(eventName,callback,capturing){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];return listeners||(listeners=this._eventRegistry[eventName]=[]),-1==listeners.indexOf(callback)&&listeners[capturing?\"unshift\":\"push\"](callback),callback},EventEmitter.off=EventEmitter.removeListener=EventEmitter.removeEventListener=function(eventName,callback){this._eventRegistry=this._eventRegistry||{};var listeners=this._eventRegistry[eventName];if(listeners){var index=listeners.indexOf(callback);-1!==index&&listeners.splice(index,1)}},EventEmitter.removeAllListeners=function(eventName){this._eventRegistry&&(this._eventRegistry[eventName]=[])},exports.EventEmitter=EventEmitter}),ace.define(\"ace/anchor\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/event_emitter\"],function(acequire,exports){\"use strict\";var oop=acequire(\"./lib/oop\"),EventEmitter=acequire(\"./lib/event_emitter\").EventEmitter,Anchor=exports.Anchor=function(doc,row,column){this.$onChange=this.onChange.bind(this),this.attach(doc),column===void 0?this.setPosition(row.row,row.column):this.setPosition(row,column)};(function(){function $pointsInOrder(point1,point2,equalPointsInOrder){var bColIsAfter=equalPointsInOrder?point1.column<=point2.column:point1.columnthis.row)){var point=$getTransformedPoint(delta,{row:this.row,column:this.column},this.$insertRight);this.setPosition(point.row,point.column,!0)}},this.setPosition=function(row,column,noClip){var pos;if(pos=noClip?{row:row,column:column}:this.$clipPositionToDocument(row,column),this.row!=pos.row||this.column!=pos.column){var old={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._signal(\"change\",{old:old,value:pos})}},this.detach=function(){this.document.removeEventListener(\"change\",this.$onChange)},this.attach=function(doc){this.document=doc||this.document,this.document.on(\"change\",this.$onChange)},this.$clipPositionToDocument=function(row,column){var pos={};return row>=this.document.getLength()?(pos.row=Math.max(0,this.document.getLength()-1),pos.column=this.document.getLine(pos.row).length):0>row?(pos.row=0,pos.column=0):(pos.row=row,pos.column=Math.min(this.document.getLine(pos.row).length,Math.max(0,column))),0>column&&(pos.column=0),pos}}).call(Anchor.prototype)}),ace.define(\"ace/document\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/apply_delta\",\"ace/lib/event_emitter\",\"ace/range\",\"ace/anchor\"],function(acequire,exports){\"use strict\";var oop=acequire(\"./lib/oop\"),applyDelta=acequire(\"./apply_delta\").applyDelta,EventEmitter=acequire(\"./lib/event_emitter\").EventEmitter,Range=acequire(\"./range\").Range,Anchor=acequire(\"./anchor\").Anchor,Document=function(textOrLines){this.$lines=[\"\"],0===textOrLines.length?this.$lines=[\"\"]:Array.isArray(textOrLines)?this.insertMergedLines({row:0,column:0},textOrLines):this.insert({row:0,column:0},textOrLines)};(function(){oop.implement(this,EventEmitter),this.setValue=function(text){var len=this.getLength()-1;this.remove(new Range(0,0,len,this.getLine(len).length)),this.insert({row:0,column:0},text)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(row,column){return new Anchor(this,row,column)},this.$split=0===\"aaa\".split(/a/).length?function(text){return text.replace(/\\r\\n|\\r/g,\"\\n\").split(\"\\n\")}:function(text){return text.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(text){var match=text.match(/^.*?(\\r\\n|\\r|\\n)/m);this.$autoNewLine=match?match[1]:\"\\n\",this._signal(\"changeNewLineMode\")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case\"windows\":return\"\\r\\n\";case\"unix\":return\"\\n\";default:return this.$autoNewLine||\"\\n\"}},this.$autoNewLine=\"\",this.$newLineMode=\"auto\",this.setNewLineMode=function(newLineMode){this.$newLineMode!==newLineMode&&(this.$newLineMode=newLineMode,this._signal(\"changeNewLineMode\"))},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(text){return\"\\r\\n\"==text||\"\\r\"==text||\"\\n\"==text},this.getLine=function(row){return this.$lines[row]||\"\"},this.getLines=function(firstRow,lastRow){return this.$lines.slice(firstRow,lastRow+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(range){return this.getLinesForRange(range).join(this.getNewLineCharacter())},this.getLinesForRange=function(range){var lines;if(range.start.row===range.end.row)lines=[this.getLine(range.start.row).substring(range.start.column,range.end.column)];else{lines=this.getLines(range.start.row,range.end.row),lines[0]=(lines[0]||\"\").substring(range.start.column);var l=lines.length-1;range.end.row-range.start.row==l&&(lines[l]=lines[l].substring(0,range.end.column))}return lines},this.insertLines=function(row,lines){return console.warn(\"Use of document.insertLines is deprecated. Use the insertFullLines method instead.\"),this.insertFullLines(row,lines)},this.removeLines=function(firstRow,lastRow){return console.warn(\"Use of document.removeLines is deprecated. Use the removeFullLines method instead.\"),this.removeFullLines(firstRow,lastRow)},this.insertNewLine=function(position){return console.warn(\"Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead.\"),this.insertMergedLines(position,[\"\",\"\"])},this.insert=function(position,text){return 1>=this.getLength()&&this.$detectNewLine(text),this.insertMergedLines(position,this.$split(text))},this.insertInLine=function(position,text){var start=this.clippedPos(position.row,position.column),end=this.pos(position.row,position.column+text.length);return this.applyDelta({start:start,end:end,action:\"insert\",lines:[text]},!0),this.clonePos(end)},this.clippedPos=function(row,column){var length=this.getLength();void 0===row?row=length:0>row?row=0:row>=length&&(row=length-1,column=void 0);var line=this.getLine(row);return void 0==column&&(column=line.length),column=Math.min(Math.max(column,0),line.length),{row:row,column:column}},this.clonePos=function(pos){return{row:pos.row,column:pos.column}},this.pos=function(row,column){return{row:row,column:column}},this.$clipPosition=function(position){var length=this.getLength();return position.row>=length?(position.row=Math.max(0,length-1),position.column=this.getLine(length-1).length):(position.row=Math.max(0,position.row),position.column=Math.min(Math.max(position.column,0),this.getLine(position.row).length)),position},this.insertFullLines=function(row,lines){row=Math.min(Math.max(row,0),this.getLength());var column=0;this.getLength()>row?(lines=lines.concat([\"\"]),column=0):(lines=[\"\"].concat(lines),row--,column=this.$lines[row].length),this.insertMergedLines({row:row,column:column},lines)},this.insertMergedLines=function(position,lines){var start=this.clippedPos(position.row,position.column),end={row:start.row+lines.length-1,column:(1==lines.length?start.column:0)+lines[lines.length-1].length};return this.applyDelta({start:start,end:end,action:\"insert\",lines:lines}),this.clonePos(end)},this.remove=function(range){var start=this.clippedPos(range.start.row,range.start.column),end=this.clippedPos(range.end.row,range.end.column);return this.applyDelta({start:start,end:end,action:\"remove\",lines:this.getLinesForRange({start:start,end:end})}),this.clonePos(start)},this.removeInLine=function(row,startColumn,endColumn){var start=this.clippedPos(row,startColumn),end=this.clippedPos(row,endColumn);return this.applyDelta({start:start,end:end,action:\"remove\",lines:this.getLinesForRange({start:start,end:end})},!0),this.clonePos(start)},this.removeFullLines=function(firstRow,lastRow){firstRow=Math.min(Math.max(0,firstRow),this.getLength()-1),lastRow=Math.min(Math.max(0,lastRow),this.getLength()-1);var deleteFirstNewLine=lastRow==this.getLength()-1&&firstRow>0,deleteLastNewLine=this.getLength()-1>lastRow,startRow=deleteFirstNewLine?firstRow-1:firstRow,startCol=deleteFirstNewLine?this.getLine(startRow).length:0,endRow=deleteLastNewLine?lastRow+1:lastRow,endCol=deleteLastNewLine?0:this.getLine(endRow).length,range=new Range(startRow,startCol,endRow,endCol),deletedLines=this.$lines.slice(firstRow,lastRow+1);return this.applyDelta({start:range.start,end:range.end,action:\"remove\",lines:this.getLinesForRange(range)}),deletedLines},this.removeNewLine=function(row){this.getLength()-1>row&&row>=0&&this.applyDelta({start:this.pos(row,this.getLine(row).length),end:this.pos(row+1,0),action:\"remove\",lines:[\"\",\"\"]})},this.replace=function(range,text){if(range instanceof Range||(range=Range.fromPoints(range.start,range.end)),0===text.length&&range.isEmpty())return range.start;if(text==this.getTextRange(range))return range.end;this.remove(range);var end;return end=text?this.insert(range.start,text):range.start},this.applyDeltas=function(deltas){for(var i=0;deltas.length>i;i++)this.applyDelta(deltas[i])},this.revertDeltas=function(deltas){for(var i=deltas.length-1;i>=0;i--)this.revertDelta(deltas[i])},this.applyDelta=function(delta,doNotValidate){var isInsert=\"insert\"==delta.action;(isInsert?1>=delta.lines.length&&!delta.lines[0]:!Range.comparePoints(delta.start,delta.end))||(isInsert&&delta.lines.length>2e4&&this.$splitAndapplyLargeDelta(delta,2e4),applyDelta(this.$lines,delta,doNotValidate),this._signal(\"change\",delta))},this.$splitAndapplyLargeDelta=function(delta,MAX){for(var lines=delta.lines,l=lines.length,row=delta.start.row,column=delta.start.column,from=0,to=0;;){from=to,to+=MAX-1;var chunk=lines.slice(from,to);if(to>l){delta.lines=chunk,delta.start.row=row+from,delta.start.column=column;break}chunk.push(\"\"),this.applyDelta({start:this.pos(row+from,column),end:this.pos(row+to,column=0),action:delta.action,lines:chunk},!0)}},this.revertDelta=function(delta){this.applyDelta({start:this.clonePos(delta.start),end:this.clonePos(delta.end),action:\"insert\"==delta.action?\"remove\":\"insert\",lines:delta.lines.slice()})},this.indexToPosition=function(index,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,i=startRow||0,l=lines.length;l>i;i++)if(index-=lines[i].length+newlineLength,0>index)return{row:i,column:index+lines[i].length+newlineLength};return{row:l-1,column:lines[l-1].length}},this.positionToIndex=function(pos,startRow){for(var lines=this.$lines||this.getAllLines(),newlineLength=this.getNewLineCharacter().length,index=0,row=Math.min(pos.row,lines.length),i=startRow||0;row>i;++i)index+=lines[i].length+newlineLength;return index+pos.column}}).call(Document.prototype),exports.Document=Document}),ace.define(\"ace/lib/lang\",[\"require\",\"exports\",\"module\"],function(acequire,exports){\"use strict\";exports.last=function(a){return a[a.length-1]},exports.stringReverse=function(string){return string.split(\"\").reverse().join(\"\")},exports.stringRepeat=function(string,count){for(var result=\"\";count>0;)1&count&&(result+=string),(count>>=1)&&(string+=string);return result};var trimBeginRegexp=/^\\s\\s*/,trimEndRegexp=/\\s\\s*$/;exports.stringTrimLeft=function(string){return string.replace(trimBeginRegexp,\"\")},exports.stringTrimRight=function(string){return string.replace(trimEndRegexp,\"\")},exports.copyObject=function(obj){var copy={};for(var key in obj)copy[key]=obj[key];return copy},exports.copyArray=function(array){for(var copy=[],i=0,l=array.length;l>i;i++)copy[i]=array[i]&&\"object\"==typeof array[i]?this.copyObject(array[i]):array[i];return copy},exports.deepCopy=function deepCopy(obj){if(\"object\"!=typeof obj||!obj)return obj;var copy;if(Array.isArray(obj)){copy=[];for(var key=0;obj.length>key;key++)copy[key]=deepCopy(obj[key]);return copy}if(\"[object Object]\"!==Object.prototype.toString.call(obj))return obj;copy={};for(var key in obj)copy[key]=deepCopy(obj[key]);return copy},exports.arrayToMap=function(arr){for(var map={},i=0;arr.length>i;i++)map[arr[i]]=1;return map},exports.createMap=function(props){var map=Object.create(null);for(var i in props)map[i]=props[i];return map},exports.arrayRemove=function(array,value){for(var i=0;array.length>=i;i++)value===array[i]&&array.splice(i,1)},exports.escapeRegExp=function(str){return str.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,\"\\\\$1\")},exports.escapeHTML=function(str){return str.replace(/&/g,\"&\").replace(/\"/g,\""\").replace(/'/g,\"'\").replace(/i;i+=2){if(Array.isArray(data[i+1]))var d={action:\"insert\",start:data[i],lines:data[i+1]};else var d={action:\"remove\",start:data[i],end:data[i+1]};doc.applyDelta(d,!0)}return _self.$timeout?deferredUpdate.schedule(_self.$timeout):(_self.onUpdate(),void 0)})};(function(){this.$timeout=500,this.setTimeout=function(timeout){this.$timeout=timeout},this.setValue=function(value){this.doc.setValue(value),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(callbackId){this.sender.callback(this.doc.getValue(),callbackId)},this.onUpdate=function(){},this.isPending=function(){return this.deferredUpdate.isPending()}}).call(Mirror.prototype)}),ace.define(\"ace/mode/json/json_parse\",[\"require\",\"exports\",\"module\"],function(){\"use strict\";var at,ch,text,value,escapee={'\"':'\"',\"\\\\\":\"\\\\\",\"/\":\"/\",b:\"\\b\",f:\"\\f\",n:\"\\n\",r:\"\\r\",t:\"\t\"},error=function(m){throw{name:\"SyntaxError\",message:m,at:at,text:text}},next=function(c){return c&&c!==ch&&error(\"Expected '\"+c+\"' instead of '\"+ch+\"'\"),ch=text.charAt(at),at+=1,ch},number=function(){var number,string=\"\";for(\"-\"===ch&&(string=\"-\",next(\"-\"));ch>=\"0\"&&\"9\">=ch;)string+=ch,next();if(\".\"===ch)for(string+=\".\";next()&&ch>=\"0\"&&\"9\">=ch;)string+=ch;if(\"e\"===ch||\"E\"===ch)for(string+=ch,next(),(\"-\"===ch||\"+\"===ch)&&(string+=ch,next());ch>=\"0\"&&\"9\">=ch;)string+=ch,next();return number=+string,isNaN(number)?(error(\"Bad number\"),void 0):number},string=function(){var hex,i,uffff,string=\"\";if('\"'===ch)for(;next();){if('\"'===ch)return next(),string;if(\"\\\\\"===ch)if(next(),\"u\"===ch){for(uffff=0,i=0;4>i&&(hex=parseInt(next(),16),isFinite(hex));i+=1)uffff=16*uffff+hex;string+=String.fromCharCode(uffff)}else{if(\"string\"!=typeof escapee[ch])break;string+=escapee[ch]}else string+=ch}error(\"Bad string\")},white=function(){for(;ch&&\" \">=ch;)next()},word=function(){switch(ch){case\"t\":return next(\"t\"),next(\"r\"),next(\"u\"),next(\"e\"),!0;case\"f\":return next(\"f\"),next(\"a\"),next(\"l\"),next(\"s\"),next(\"e\"),!1;case\"n\":return next(\"n\"),next(\"u\"),next(\"l\"),next(\"l\"),null}error(\"Unexpected '\"+ch+\"'\")},array=function(){var array=[];if(\"[\"===ch){if(next(\"[\"),white(),\"]\"===ch)return next(\"]\"),array;for(;ch;){if(array.push(value()),white(),\"]\"===ch)return next(\"]\"),array;next(\",\"),white()}}error(\"Bad array\")},object=function(){var key,object={};if(\"{\"===ch){if(next(\"{\"),white(),\"}\"===ch)return next(\"}\"),object;for(;ch;){if(key=string(),white(),next(\":\"),Object.hasOwnProperty.call(object,key)&&error('Duplicate key \"'+key+'\"'),object[key]=value(),white(),\"}\"===ch)return next(\"}\"),object;next(\",\"),white()}}error(\"Bad object\")};return value=function(){switch(white(),ch){case\"{\":return object();case\"[\":return array();case'\"':return string();case\"-\":return number();default:return ch>=\"0\"&&\"9\">=ch?number():word()}},function(source,reviver){var result;return text=source,at=0,ch=\" \",result=value(),white(),ch&&error(\"Syntax error\"),\"function\"==typeof reviver?function walk(holder,key){var k,v,value=holder[key];if(value&&\"object\"==typeof value)for(k in value)Object.hasOwnProperty.call(value,k)&&(v=walk(value,k),void 0!==v?value[k]=v:delete value[k]);return reviver.call(holder,key,value)}({\"\":result},\"\"):result}}),ace.define(\"ace/mode/json_worker\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/worker/mirror\",\"ace/mode/json/json_parse\"],function(acequire,exports){\"use strict\";var oop=acequire(\"../lib/oop\"),Mirror=acequire(\"../worker/mirror\").Mirror,parse=acequire(\"./json/json_parse\"),JsonWorker=exports.JsonWorker=function(sender){Mirror.call(this,sender),this.setTimeout(200)};oop.inherits(JsonWorker,Mirror),function(){this.onUpdate=function(){var value=this.doc.getValue(),errors=[];try{value&&parse(value)}catch(e){var pos=this.doc.indexToPosition(e.at-1);errors.push({row:pos.row,column:pos.column,text:e.message,type:\"error\"})}this.sender.emit(\"annotate\",errors)}}.call(JsonWorker.prototype)}),ace.define(\"ace/lib/es5-shim\",[\"require\",\"exports\",\"module\"],function(){function Empty(){}function doesDefinePropertyWork(object){try{return Object.defineProperty(object,\"sentinel\",{}),\"sentinel\"in object}catch(exception){}}function toInteger(n){return n=+n,n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n))),n}Function.prototype.bind||(Function.prototype.bind=function(that){var target=this;if(\"function\"!=typeof target)throw new TypeError(\"Function.prototype.bind called on incompatible \"+target);var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var result=target.apply(this,args.concat(slice.call(arguments)));return Object(result)===result?result:this}return target.apply(that,args.concat(slice.call(arguments)))};return target.prototype&&(Empty.prototype=target.prototype,bound.prototype=new Empty,Empty.prototype=null),bound});var defineGetter,defineSetter,lookupGetter,lookupSetter,supportsAccessors,call=Function.prototype.call,prototypeOfArray=Array.prototype,prototypeOfObject=Object.prototype,slice=prototypeOfArray.slice,_toString=call.bind(prototypeOfObject.toString),owns=call.bind(prototypeOfObject.hasOwnProperty);if((supportsAccessors=owns(prototypeOfObject,\"__defineGetter__\"))&&(defineGetter=call.bind(prototypeOfObject.__defineGetter__),defineSetter=call.bind(prototypeOfObject.__defineSetter__),lookupGetter=call.bind(prototypeOfObject.__lookupGetter__),lookupSetter=call.bind(prototypeOfObject.__lookupSetter__)),2!=[1,2].splice(0).length)if(function(){function makeArray(l){var a=Array(l+2);return a[0]=a[1]=0,a}var lengthBefore,array=[];return array.splice.apply(array,makeArray(20)),array.splice.apply(array,makeArray(26)),lengthBefore=array.length,array.splice(5,0,\"XXX\"),lengthBefore+1==array.length,lengthBefore+1==array.length?!0:void 0\n}()){var array_splice=Array.prototype.splice;Array.prototype.splice=function(start,deleteCount){return arguments.length?array_splice.apply(this,[void 0===start?0:start,void 0===deleteCount?this.length-start:deleteCount].concat(slice.call(arguments,2))):[]}}else Array.prototype.splice=function(pos,removeCount){var length=this.length;pos>0?pos>length&&(pos=length):void 0==pos?pos=0:0>pos&&(pos=Math.max(length+pos,0)),length>pos+removeCount||(removeCount=length-pos);var removed=this.slice(pos,pos+removeCount),insert=slice.call(arguments,2),add=insert.length;if(pos===length)add&&this.push.apply(this,insert);else{var remove=Math.min(removeCount,length-pos),tailOldPos=pos+remove,tailNewPos=tailOldPos+add-remove,tailCount=length-tailOldPos,lengthAfterRemove=length-remove;if(tailOldPos>tailNewPos)for(var i=0;tailCount>i;++i)this[tailNewPos+i]=this[tailOldPos+i];else if(tailNewPos>tailOldPos)for(i=tailCount;i--;)this[tailNewPos+i]=this[tailOldPos+i];if(add&&pos===lengthAfterRemove)this.length=lengthAfterRemove,this.push.apply(this,insert);else for(this.length=lengthAfterRemove+add,i=0;add>i;++i)this[pos+i]=insert[i]}return removed};Array.isArray||(Array.isArray=function(obj){return\"[object Array]\"==_toString(obj)});var boxedString=Object(\"a\"),splitString=\"a\"!=boxedString[0]||!(0 in boxedString);if(Array.prototype.forEach||(Array.prototype.forEach=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,thisp=arguments[1],i=-1,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError;for(;length>++i;)i in self&&fun.call(thisp,self[i],i,object)}),Array.prototype.map||(Array.prototype.map=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,result=Array(length),thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)i in self&&(result[i]=fun.call(thisp,self[i],i,object));return result}),Array.prototype.filter||(Array.prototype.filter=function(fun){var value,object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,result=[],thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)i in self&&(value=self[i],fun.call(thisp,value,i,object)&&result.push(value));return result}),Array.prototype.every||(Array.prototype.every=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)if(i in self&&!fun.call(thisp,self[i],i,object))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0,thisp=arguments[1];if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");for(var i=0;length>i;i++)if(i in self&&fun.call(thisp,self[i],i,object))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");if(!length&&1==arguments.length)throw new TypeError(\"reduce of empty array with no initial value\");var result,i=0;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i++];break}if(++i>=length)throw new TypeError(\"reduce of empty array with no initial value\")}for(;length>i;i++)i in self&&(result=fun.call(void 0,result,self[i],i,object));return result}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(fun){var object=toObject(this),self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):object,length=self.length>>>0;if(\"[object Function]\"!=_toString(fun))throw new TypeError(fun+\" is not a function\");if(!length&&1==arguments.length)throw new TypeError(\"reduceRight of empty array with no initial value\");var result,i=length-1;if(arguments.length>=2)result=arguments[1];else for(;;){if(i in self){result=self[i--];break}if(0>--i)throw new TypeError(\"reduceRight of empty array with no initial value\")}do i in this&&(result=fun.call(void 0,result,self[i],i,object));while(i--);return result}),Array.prototype.indexOf&&-1==[0,1].indexOf(1,2)||(Array.prototype.indexOf=function(sought){var self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):toObject(this),length=self.length>>>0;if(!length)return-1;var i=0;for(arguments.length>1&&(i=toInteger(arguments[1])),i=i>=0?i:Math.max(0,length+i);length>i;i++)if(i in self&&self[i]===sought)return i;return-1}),Array.prototype.lastIndexOf&&-1==[0,1].lastIndexOf(0,-3)||(Array.prototype.lastIndexOf=function(sought){var self=splitString&&\"[object String]\"==_toString(this)?this.split(\"\"):toObject(this),length=self.length>>>0;if(!length)return-1;var i=length-1;for(arguments.length>1&&(i=Math.min(i,toInteger(arguments[1]))),i=i>=0?i:length-Math.abs(i);i>=0;i--)if(i in self&&sought===self[i])return i;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(object){return object.__proto__||(object.constructor?object.constructor.prototype:prototypeOfObject)}),!Object.getOwnPropertyDescriptor){var ERR_NON_OBJECT=\"Object.getOwnPropertyDescriptor called on a non-object: \";Object.getOwnPropertyDescriptor=function(object,property){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT+object);if(owns(object,property)){var descriptor,getter,setter;if(descriptor={enumerable:!0,configurable:!0},supportsAccessors){var prototype=object.__proto__;object.__proto__=prototypeOfObject;var getter=lookupGetter(object,property),setter=lookupSetter(object,property);if(object.__proto__=prototype,getter||setter)return getter&&(descriptor.get=getter),setter&&(descriptor.set=setter),descriptor}return descriptor.value=object[property],descriptor}}}if(Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(object){return Object.keys(object)}),!Object.create){var createEmpty;createEmpty=null===Object.prototype.__proto__?function(){return{__proto__:null}}:function(){var empty={};for(var i in empty)empty[i]=null;return empty.constructor=empty.hasOwnProperty=empty.propertyIsEnumerable=empty.isPrototypeOf=empty.toLocaleString=empty.toString=empty.valueOf=empty.__proto__=null,empty},Object.create=function(prototype,properties){var object;if(null===prototype)object=createEmpty();else{if(\"object\"!=typeof prototype)throw new TypeError(\"typeof prototype[\"+typeof prototype+\"] != 'object'\");var Type=function(){};Type.prototype=prototype,object=new Type,object.__proto__=prototype}return void 0!==properties&&Object.defineProperties(object,properties),object}}if(Object.defineProperty){var definePropertyWorksOnObject=doesDefinePropertyWork({}),definePropertyWorksOnDom=\"undefined\"==typeof document||doesDefinePropertyWork(document.createElement(\"div\"));if(!definePropertyWorksOnObject||!definePropertyWorksOnDom)var definePropertyFallback=Object.defineProperty}if(!Object.defineProperty||definePropertyFallback){var ERR_NON_OBJECT_DESCRIPTOR=\"Property description must be an object: \",ERR_NON_OBJECT_TARGET=\"Object.defineProperty called on non-object: \",ERR_ACCESSORS_NOT_SUPPORTED=\"getters & setters can not be defined on this javascript engine\";Object.defineProperty=function(object,property,descriptor){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(ERR_NON_OBJECT_TARGET+object);if(\"object\"!=typeof descriptor&&\"function\"!=typeof descriptor||null===descriptor)throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR+descriptor);if(definePropertyFallback)try{return definePropertyFallback.call(Object,object,property,descriptor)}catch(exception){}if(owns(descriptor,\"value\"))if(supportsAccessors&&(lookupGetter(object,property)||lookupSetter(object,property))){var prototype=object.__proto__;object.__proto__=prototypeOfObject,delete object[property],object[property]=descriptor.value,object.__proto__=prototype}else object[property]=descriptor.value;else{if(!supportsAccessors)throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);owns(descriptor,\"get\")&&defineGetter(object,property,descriptor.get),owns(descriptor,\"set\")&&defineSetter(object,property,descriptor.set)}return object}}Object.defineProperties||(Object.defineProperties=function(object,properties){for(var property in properties)owns(properties,property)&&Object.defineProperty(object,property,properties[property]);return object}),Object.seal||(Object.seal=function(object){return object}),Object.freeze||(Object.freeze=function(object){return object});try{Object.freeze(function(){})}catch(exception){Object.freeze=function(freezeObject){return function(object){return\"function\"==typeof object?object:freezeObject(object)}}(Object.freeze)}if(Object.preventExtensions||(Object.preventExtensions=function(object){return object}),Object.isSealed||(Object.isSealed=function(){return!1}),Object.isFrozen||(Object.isFrozen=function(){return!1}),Object.isExtensible||(Object.isExtensible=function(object){if(Object(object)===object)throw new TypeError;for(var name=\"\";owns(object,name);)name+=\"?\";object[name]=!0;var returnValue=owns(object,name);return delete object[name],returnValue}),!Object.keys){var hasDontEnumBug=!0,dontEnums=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"],dontEnumsLength=dontEnums.length;for(var key in{toString:null})hasDontEnumBug=!1;Object.keys=function(object){if(\"object\"!=typeof object&&\"function\"!=typeof object||null===object)throw new TypeError(\"Object.keys called on a non-object\");var keys=[];for(var name in object)owns(object,name)&&keys.push(name);if(hasDontEnumBug)for(var i=0,ii=dontEnumsLength;ii>i;i++){var dontEnum=dontEnums[i];owns(object,dontEnum)&&keys.push(dontEnum)}return keys}}Date.now||(Date.now=function(){return(new Date).getTime()});var ws=\"\t\\n\u000b\\f\\r   ᠎              \\u2028\\u2029\";if(!String.prototype.trim||ws.trim()){ws=\"[\"+ws+\"]\";var trimBeginRegexp=RegExp(\"^\"+ws+ws+\"*\"),trimEndRegexp=RegExp(ws+ws+\"*$\");String.prototype.trim=function(){return(this+\"\").replace(trimBeginRegexp,\"\").replace(trimEndRegexp,\"\")}}var toObject=function(o){if(null==o)throw new TypeError(\"can't convert \"+o+\" to object\");return Object(o)}});"; + +/***/ }, +/* 70 */ +/***/ function(module, exports) { + + ace.define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"], function(acequire, exports, module) { + "use strict"; + + var dom = acequire("../lib/dom"); + var lang = acequire("../lib/lang"); + var event = acequire("../lib/event"); + var searchboxCss = "\ + .ace_search {\ + background-color: #ddd;\ + color: #666;\ + border: 1px solid #cbcbcb;\ + border-top: 0 none;\ + overflow: hidden;\ + margin: 0;\ + padding: 4px 6px 0 4px;\ + position: absolute;\ + top: 0;\ + z-index: 99;\ + white-space: normal;\ + }\ + .ace_search.left {\ + border-left: 0 none;\ + border-radius: 0px 0px 5px 0px;\ + left: 0;\ + }\ + .ace_search.right {\ + border-radius: 0px 0px 0px 5px;\ + border-right: 0 none;\ + right: 0;\ + }\ + .ace_search_form, .ace_replace_form {\ + margin: 0 20px 4px 0;\ + overflow: hidden;\ + line-height: 1.9;\ + }\ + .ace_replace_form {\ + margin-right: 0;\ + }\ + .ace_search_form.ace_nomatch {\ + outline: 1px solid red;\ + }\ + .ace_search_field {\ + border-radius: 3px 0 0 3px;\ + background-color: white;\ + color: black;\ + border: 1px solid #cbcbcb;\ + border-right: 0 none;\ + box-sizing: border-box!important;\ + outline: 0;\ + padding: 0;\ + font-size: inherit;\ + margin: 0;\ + line-height: inherit;\ + padding: 0 6px;\ + min-width: 17em;\ + vertical-align: top;\ + }\ + .ace_searchbtn {\ + border: 1px solid #cbcbcb;\ + line-height: inherit;\ + display: inline-block;\ + padding: 0 6px;\ + background: #fff;\ + border-right: 0 none;\ + border-left: 1px solid #dcdcdc;\ + cursor: pointer;\ + margin: 0;\ + position: relative;\ + box-sizing: content-box!important;\ + color: #666;\ + }\ + .ace_searchbtn:last-child {\ + border-radius: 0 3px 3px 0;\ + border-right: 1px solid #cbcbcb;\ + }\ + .ace_searchbtn:disabled {\ + background: none;\ + cursor: default;\ + }\ + .ace_searchbtn:hover {\ + background-color: #eef1f6;\ + }\ + .ace_searchbtn.prev, .ace_searchbtn.next {\ + padding: 0px 0.7em\ + }\ + .ace_searchbtn.prev:after, .ace_searchbtn.next:after {\ + content: \"\";\ + border: solid 2px #888;\ + width: 0.5em;\ + height: 0.5em;\ + border-width: 2px 0 0 2px;\ + display:inline-block;\ + transform: rotate(-45deg);\ + }\ + .ace_searchbtn.next:after {\ + border-width: 0 2px 2px 0 ;\ + }\ + .ace_searchbtn_close {\ + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\ + border-radius: 50%;\ + border: 0 none;\ + color: #656565;\ + cursor: pointer;\ + font: 16px/16px Arial;\ + padding: 0;\ + height: 14px;\ + width: 14px;\ + top: 9px;\ + right: 7px;\ + position: absolute;\ + }\ + .ace_searchbtn_close:hover {\ + background-color: #656565;\ + background-position: 50% 100%;\ + color: white;\ + }\ + .ace_button {\ + margin-left: 2px;\ + cursor: pointer;\ + -webkit-user-select: none;\ + -moz-user-select: none;\ + -o-user-select: none;\ + -ms-user-select: none;\ + user-select: none;\ + overflow: hidden;\ + opacity: 0.7;\ + border: 1px solid rgba(100,100,100,0.23);\ + padding: 1px;\ + box-sizing: border-box!important;\ + color: black;\ + }\ + .ace_button:hover {\ + background-color: #eee;\ + opacity:1;\ + }\ + .ace_button:active {\ + background-color: #ddd;\ + }\ + .ace_button.checked {\ + border-color: #3399ff;\ + opacity:1;\ + }\ + .ace_search_options{\ + margin-bottom: 3px;\ + text-align: right;\ + -webkit-user-select: none;\ + -moz-user-select: none;\ + -o-user-select: none;\ + -ms-user-select: none;\ + user-select: none;\ + clear: both;\ + }\ + .ace_search_counter {\ + float: left;\ + font-family: arial;\ + padding: 0 8px;\ + }"; + var HashHandler = acequire("../keyboard/hash_handler").HashHandler; + var keyUtil = acequire("../lib/keys"); + + var MAX_COUNT = 999; + + dom.importCssString(searchboxCss, "ace_searchbox"); + + var html = ''.replace(/> +/g, ">"); + + var SearchBox = function(editor, range, showReplaceForm) { + var div = dom.createElement("div"); + div.innerHTML = html; + this.element = div.firstChild; + + this.setSession = this.setSession.bind(this); + + this.$init(); + this.setEditor(editor); + }; + + (function() { + this.setEditor = function(editor) { + editor.searchBox = this; + editor.renderer.scroller.appendChild(this.element); + this.editor = editor; + }; + + this.setSession = function(e) { + this.searchRange = null; + this.$syncOptions(true); + }; + + this.$initElements = function(sb) { + this.searchBox = sb.querySelector(".ace_search_form"); + this.replaceBox = sb.querySelector(".ace_replace_form"); + this.searchOption = sb.querySelector("[action=searchInSelection]"); + this.replaceOption = sb.querySelector("[action=toggleReplace]"); + this.regExpOption = sb.querySelector("[action=toggleRegexpMode]"); + this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]"); + this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]"); + this.searchInput = this.searchBox.querySelector(".ace_search_field"); + this.replaceInput = this.replaceBox.querySelector(".ace_search_field"); + this.searchCounter = sb.querySelector(".ace_search_counter"); + }; + + this.$init = function() { + var sb = this.element; + + this.$initElements(sb); + + var _this = this; + event.addListener(sb, "mousedown", function(e) { + setTimeout(function(){ + _this.activeInput.focus(); + }, 0); + event.stopPropagation(e); + }); + event.addListener(sb, "click", function(e) { + var t = e.target || e.srcElement; + var action = t.getAttribute("action"); + if (action && _this[action]) + _this[action](); + else if (_this.$searchBarKb.commands[action]) + _this.$searchBarKb.commands[action].exec(_this); + event.stopPropagation(e); + }); + + event.addCommandKeyListener(sb, function(e, hashId, keyCode) { + var keyString = keyUtil.keyCodeToString(keyCode); + var command = _this.$searchBarKb.findKeyCommand(hashId, keyString); + if (command && command.exec) { + command.exec(_this); + event.stopEvent(e); + } + }); + + this.$onChange = lang.delayedCall(function() { + _this.find(false, false); + }); + + event.addListener(this.searchInput, "input", function() { + _this.$onChange.schedule(20); + }); + event.addListener(this.searchInput, "focus", function() { + _this.activeInput = _this.searchInput; + _this.searchInput.value && _this.highlight(); + }); + event.addListener(this.replaceInput, "focus", function() { + _this.activeInput = _this.replaceInput; + _this.searchInput.value && _this.highlight(); + }); + }; + this.$closeSearchBarKb = new HashHandler([{ + bindKey: "Esc", + name: "closeSearchBar", + exec: function(editor) { + editor.searchBox.hide(); + } + }]); + this.$searchBarKb = new HashHandler(); + this.$searchBarKb.bindKeys({ + "Ctrl-f|Command-f": function(sb) { + var isReplace = sb.isReplace = !sb.isReplace; + sb.replaceBox.style.display = isReplace ? "" : "none"; + sb.replaceOption.checked = false; + sb.$syncOptions(); + sb.searchInput.focus(); + }, + "Ctrl-H|Command-Option-F": function(sb) { + sb.replaceOption.checked = true; + sb.$syncOptions(); + sb.replaceInput.focus(); + }, + "Ctrl-G|Command-G": function(sb) { + sb.findNext(); + }, + "Ctrl-Shift-G|Command-Shift-G": function(sb) { + sb.findPrev(); + }, + "esc": function(sb) { + setTimeout(function() { sb.hide();}); + }, + "Return": function(sb) { + if (sb.activeInput == sb.replaceInput) + sb.replace(); + sb.findNext(); + }, + "Shift-Return": function(sb) { + if (sb.activeInput == sb.replaceInput) + sb.replace(); + sb.findPrev(); + }, + "Alt-Return": function(sb) { + if (sb.activeInput == sb.replaceInput) + sb.replaceAll(); + sb.findAll(); + }, + "Tab": function(sb) { + (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus(); + } + }); + + this.$searchBarKb.addCommands([{ + name: "toggleRegexpMode", + bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"}, + exec: function(sb) { + sb.regExpOption.checked = !sb.regExpOption.checked; + sb.$syncOptions(); + } + }, { + name: "toggleCaseSensitive", + bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"}, + exec: function(sb) { + sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked; + sb.$syncOptions(); + } + }, { + name: "toggleWholeWords", + bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"}, + exec: function(sb) { + sb.wholeWordOption.checked = !sb.wholeWordOption.checked; + sb.$syncOptions(); + } + }, { + name: "toggleReplace", + exec: function(sb) { + sb.replaceOption.checked = !sb.replaceOption.checked; + sb.$syncOptions(); + } + }, { + name: "searchInSelection", + exec: function(sb) { + sb.searchOption.checked = !sb.searchRange; + sb.setSearchRange(sb.searchOption.checked && sb.editor.getSelectionRange()); + sb.$syncOptions(); + } + }]); + + this.setSearchRange = function(range) { + this.searchRange = range; + if (range) { + this.searchRangeMarker = this.editor.session.addMarker(range, "ace_active-line"); + } else if (this.searchRangeMarker) { + this.editor.session.removeMarker(this.searchRangeMarker); + this.searchRangeMarker = null; + } + }; + + this.$syncOptions = function(preventScroll) { + dom.setCssClass(this.replaceOption, "checked", this.searchRange); + dom.setCssClass(this.searchOption, "checked", this.searchOption.checked); + this.replaceOption.textContent = this.replaceOption.checked ? "-" : "+"; + dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked); + dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked); + dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked); + this.replaceBox.style.display = this.replaceOption.checked ? "" : "none"; + this.find(false, false, preventScroll); + }; + + this.highlight = function(re) { + this.editor.session.highlight(re || this.editor.$search.$options.re); + this.editor.renderer.updateBackMarkers(); + }; + this.find = function(skipCurrent, backwards, preventScroll) { + var range = this.editor.find(this.searchInput.value, { + skipCurrent: skipCurrent, + backwards: backwards, + wrap: true, + regExp: this.regExpOption.checked, + caseSensitive: this.caseSensitiveOption.checked, + wholeWord: this.wholeWordOption.checked, + preventScroll: preventScroll, + range: this.searchRange + }); + var noMatch = !range && this.searchInput.value; + dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); + this.editor._emit("findSearchBox", { match: !noMatch }); + this.highlight(); + this.updateCounter(); + }; + this.updateCounter = function() { + var editor = this.editor; + var regex = editor.$search.$options.re; + var all = 0; + var before = 0; + if (regex) { + var value = this.searchRange + ? editor.session.getTextRange(this.searchRange) + : editor.getValue(); + + var offset = editor.session.doc.positionToIndex(editor.selection.anchor); + if (this.searchRange) + offset -= editor.session.doc.positionToIndex(this.searchRange.start); + + var last = regex.lastIndex = 0; + var m; + while ((m = regex.exec(value))) { + all++; + last = m.index; + if (last <= offset) + before++; + if (all > MAX_COUNT) + break; + if (!m[0]) { + regex.lastIndex = last += 1; + if (last >= value.length) + break; + } + } + } + this.searchCounter.textContent = before + " of " + (all > MAX_COUNT ? MAX_COUNT + "+" : all); + }; + this.findNext = function() { + this.find(true, false); + }; + this.findPrev = function() { + this.find(true, true); + }; + this.findAll = function(){ + var range = this.editor.findAll(this.searchInput.value, { + regExp: this.regExpOption.checked, + caseSensitive: this.caseSensitiveOption.checked, + wholeWord: this.wholeWordOption.checked + }); + var noMatch = !range && this.searchInput.value; + dom.setCssClass(this.searchBox, "ace_nomatch", noMatch); + this.editor._emit("findSearchBox", { match: !noMatch }); + this.highlight(); + this.hide(); + }; + this.replace = function() { + if (!this.editor.getReadOnly()) + this.editor.replace(this.replaceInput.value); + }; + this.replaceAndFindNext = function() { + if (!this.editor.getReadOnly()) { + this.editor.replace(this.replaceInput.value); + this.findNext(); + } + }; + this.replaceAll = function() { + if (!this.editor.getReadOnly()) + this.editor.replaceAll(this.replaceInput.value); + }; + + this.hide = function() { + this.active = false; + this.setSearchRange(null); + this.editor.off("changeSession", this.setSession); + + this.element.style.display = "none"; + this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb); + this.editor.focus(); + }; + this.show = function(value, isReplace) { + this.active = true; + this.editor.on("changeSession", this.setSession); + this.element.style.display = ""; + this.replaceOption.checked = isReplace; + + if (value) + this.searchInput.value = value; + + this.searchInput.focus(); + this.searchInput.select(); + + this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb); + + this.$syncOptions(true); + }; + + this.isFocused = function() { + var el = document.activeElement; + return el == this.searchInput || el == this.replaceInput; + }; + }).call(SearchBox.prototype); + + exports.SearchBox = SearchBox; + + exports.Search = function(editor, isReplace) { + var sb = editor.searchBox || new SearchBox(editor); + sb.show(editor.session.getTextRange(), isReplace); + }; + + }); + (function() { + ace.acequire(["ace/ext/searchbox"], function() {}); + })(); + + +/***/ }, +/* 71 */ +/***/ function(module, exports) { + + /* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + + ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) { + + exports.isDark = false; + exports.cssClass = "ace-jsoneditor"; + exports.cssText = ".ace-jsoneditor .ace_gutter {\ + background: #ebebeb;\ + color: #333\ + }\ + \ + .ace-jsoneditor.ace_editor {\ + font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;\ + line-height: 1.3;\ + background-color: #fff;\ + }\ + .ace-jsoneditor .ace_print-margin {\ + width: 1px;\ + background: #e8e8e8\ + }\ + .ace-jsoneditor .ace_scroller {\ + background-color: #FFFFFF\ + }\ + .ace-jsoneditor .ace_text-layer {\ + color: gray\ + }\ + .ace-jsoneditor .ace_variable {\ + color: #1a1a1a\ + }\ + .ace-jsoneditor .ace_cursor {\ + border-left: 2px solid #000000\ + }\ + .ace-jsoneditor .ace_overwrite-cursors .ace_cursor {\ + border-left: 0px;\ + border-bottom: 1px solid #000000\ + }\ + .ace-jsoneditor .ace_marker-layer .ace_selection {\ + background: lightgray\ + }\ + .ace-jsoneditor.ace_multiselect .ace_selection.ace_start {\ + box-shadow: 0 0 3px 0px #FFFFFF;\ + border-radius: 2px\ + }\ + .ace-jsoneditor .ace_marker-layer .ace_step {\ + background: rgb(255, 255, 0)\ + }\ + .ace-jsoneditor .ace_marker-layer .ace_bracket {\ + margin: -1px 0 0 -1px;\ + border: 1px solid #BFBFBF\ + }\ + .ace-jsoneditor .ace_marker-layer .ace_active-line {\ + background: #FFFBD1\ + }\ + .ace-jsoneditor .ace_gutter-active-line {\ + background-color : #dcdcdc\ + }\ + .ace-jsoneditor .ace_marker-layer .ace_selected-word {\ + border: 1px solid lightgray\ + }\ + .ace-jsoneditor .ace_invisible {\ + color: #BFBFBF\ + }\ + .ace-jsoneditor .ace_keyword,\ + .ace-jsoneditor .ace_meta,\ + .ace-jsoneditor .ace_support.ace_constant.ace_property-value {\ + color: #AF956F\ + }\ + .ace-jsoneditor .ace_keyword.ace_operator {\ + color: #484848\ + }\ + .ace-jsoneditor .ace_keyword.ace_other.ace_unit {\ + color: #96DC5F\ + }\ + .ace-jsoneditor .ace_constant.ace_language {\ + color: darkorange\ + }\ + .ace-jsoneditor .ace_constant.ace_numeric {\ + color: red\ + }\ + .ace-jsoneditor .ace_constant.ace_character.ace_entity {\ + color: #BF78CC\ + }\ + .ace-jsoneditor .ace_invalid {\ + color: #FFFFFF;\ + background-color: #FF002A;\ + }\ + .ace-jsoneditor .ace_fold {\ + background-color: #AF956F;\ + border-color: #000000\ + }\ + .ace-jsoneditor .ace_storage,\ + .ace-jsoneditor .ace_support.ace_class,\ + .ace-jsoneditor .ace_support.ace_function,\ + .ace-jsoneditor .ace_support.ace_other,\ + .ace-jsoneditor .ace_support.ace_type {\ + color: #C52727\ + }\ + .ace-jsoneditor .ace_string {\ + color: green\ + }\ + .ace-jsoneditor .ace_comment {\ + color: #BCC8BA\ + }\ + .ace-jsoneditor .ace_entity.ace_name.ace_tag,\ + .ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {\ + color: #606060\ + }\ + .ace-jsoneditor .ace_markup.ace_underline {\ + text-decoration: underline\ + }\ + .ace-jsoneditor .ace_indent-guide {\ + background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\ + }"; + + var dom = acequire("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); + }); + + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/main/webapp/modules/core/images/favicon.png b/main/webapp/modules/core/images/favicon.png index 33bbd2aa9..8527767b1 100644 Binary files a/main/webapp/modules/core/images/favicon.png and b/main/webapp/modules/core/images/favicon.png differ diff --git a/main/webapp/modules/core/images/logo-gem-126.png b/main/webapp/modules/core/images/logo-gem-126.png new file mode 100644 index 000000000..2e1216be5 Binary files /dev/null and b/main/webapp/modules/core/images/logo-gem-126.png differ diff --git a/main/webapp/modules/core/images/logo-gem-150.png b/main/webapp/modules/core/images/logo-gem-150.png deleted file mode 100644 index 178cdaf8c..000000000 Binary files a/main/webapp/modules/core/images/logo-gem-150.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-gem-40.png b/main/webapp/modules/core/images/logo-gem-40.png deleted file mode 100644 index 591f102d5..000000000 Binary files a/main/webapp/modules/core/images/logo-gem-40.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-googlerefine-30.png b/main/webapp/modules/core/images/logo-googlerefine-30.png deleted file mode 100644 index aad370c89..000000000 Binary files a/main/webapp/modules/core/images/logo-googlerefine-30.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-googlerefine-40.png b/main/webapp/modules/core/images/logo-googlerefine-40.png deleted file mode 100644 index 9ccf3a4a1..000000000 Binary files a/main/webapp/modules/core/images/logo-googlerefine-40.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-openrefine-30.png b/main/webapp/modules/core/images/logo-openrefine-30.png deleted file mode 100644 index b5b99df69..000000000 Binary files a/main/webapp/modules/core/images/logo-openrefine-30.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-openrefine-40.png b/main/webapp/modules/core/images/logo-openrefine-40.png deleted file mode 100644 index dba86bd9b..000000000 Binary files a/main/webapp/modules/core/images/logo-openrefine-40.png and /dev/null differ diff --git a/main/webapp/modules/core/images/logo-openrefine-550.png b/main/webapp/modules/core/images/logo-openrefine-550.png new file mode 100644 index 000000000..4fcbd6989 Binary files /dev/null and b/main/webapp/modules/core/images/logo-openrefine-550.png differ diff --git a/main/webapp/modules/core/index.vt b/main/webapp/modules/core/index.vt index f4e58f24c..6666cc19d 100644 --- a/main/webapp/modules/core/index.vt +++ b/main/webapp/modules/core/index.vt @@ -44,7 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/main/webapp/modules/core/scripts/views/data-table/data-table-view.js b/main/webapp/modules/core/scripts/views/data-table/data-table-view.js index 874c9858b..bbb6a3ef0 100644 --- a/main/webapp/modules/core/scripts/views/data-table/data-table-view.js +++ b/main/webapp/modules/core/scripts/views/data-table/data-table-view.js @@ -553,7 +553,7 @@ DataTableView.prototype._addSortingCriterion = function(criterion, alone) { this.update(); }; -/** below can be move to seperate file **/ +/** below can be move to separate file **/ var doTextTransformPrompt = function() { var frame = $( DOM.loadHTML("core", "scripts/views/data-table/text-transform-dialog.html") @@ -594,11 +594,21 @@ DataTableView.prototype._addSortingCriterion = function(criterion, alone) { params.repeatCount = elmts.repeatCountInput[0].value; }; }; - /** above can be move to seperate file **/ + /** above can be move to separate file **/ DataTableView.prototype._createMenuForAllColumns = function(elmt) { var self = this; var menu = [ + /** + { + label: $.i18n._('core-views')["table-schema-validate"], + id: "core/facets", + width: "200px", + click: function() { + doTextTransformPrompt(); + } + }, + */ { label: $.i18n._('core-views')["transform"], id: "core/facets", diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-edit-cells.js b/main/webapp/modules/core/scripts/views/data-table/menu-edit-cells.js index ec0343e67..8298bdc2e 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-edit-cells.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-edit-cells.js @@ -268,6 +268,11 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { id: "core/to-blank", label: $.i18n._('core-views')["blank-out"], click: function() { doTextTransform("null", "keep-original", false, ""); } + }, + { + id: "core/to-empty", + label: $.i18n._('core-views')["blank-out-empty"], + click: function() { doTextTransform("\"\"", "keep-original", false, ""); } } ] }, diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js index e43491e8c..3d6a25335 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js @@ -91,7 +91,9 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var doAddColumnByFetchingURLs = function() { var frame = $( DOM.loadHTML("core", "scripts/views/data-table/add-column-by-fetching-urls-dialog.html") - .replace("$EXPRESSION_PREVIEW_WIDGET$", ExpressionPreviewDialog.generateWidgetHtml())); + .replace("$EXPRESSION_PREVIEW_WIDGET$", ExpressionPreviewDialog.generateWidgetHtml()) + .replace("$HTTP_HEADERS_WIDGET$", HttpHeadersDialog.generateWidgetHtml()) + ); var elmts = DOM.bind(frame); elmts.dialogHeader.text($.i18n._('core-views')["add-col-fetch"]+" " + column.name); @@ -103,6 +105,17 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { elmts.or_views_setBlank.text($.i18n._('core-views')["set-blank"]); elmts.or_views_storeErr.text($.i18n._('core-views')["store-err"]); elmts.or_views_cacheResponses.text($.i18n._('core-views')["cache-responses"]); + elmts.or_views_httpHeaders.text($.i18n._('core-views')["http-headers"]); + elmts.or_views_httpHeadersShowHide.text($.i18n._('core-views')["show"]); + elmts.or_views_httpHeadersShowHide.click(function() { + $( ".set-httpheaders-container" ).toggle( "slow", function() { + if ($(this).is(':visible')) { + elmts.or_views_httpHeadersShowHide.text($.i18n._('core-views')["hide"]); + } else { + elmts.or_views_httpHeadersShowHide.text($.i18n._('core-views')["show"]); + } + }); + }); elmts.or_views_urlFetch.text($.i18n._('core-views')["url-fetch"]); elmts.okButton.html($.i18n._('core-buttons')["ok"]); elmts.cancelButton.text($.i18n._('core-buttons')["cancel"]); @@ -118,7 +131,8 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { o.values, null ); - + + elmts.cancelButton.click(dismiss); elmts.okButton.click(function() { var columnName = $.trim(elmts.columnNameInput[0].value); @@ -126,7 +140,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { alert($.i18n._('core-views')["warning-col-name"]); return; } - + Refine.postCoreProcess( "add-column-by-fetching-urls", { @@ -137,6 +151,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { delay: elmts.throttleDelayInput[0].value, onError: $('input[name="dialog-onerror-choice"]:checked')[0].value, cacheResponses: $('input[name="dialog-cache-responses"]')[0].checked, + httpHeaders: JSON.stringify(elmts.setHttpHeadersContainer.find("input").serializeArray()) }, null, { modelsChanged: true } diff --git a/main/webapp/modules/core/styles/common.less b/main/webapp/modules/core/styles/common.less index 9beccc81f..d3a7ce8e4 100644 --- a/main/webapp/modules/core/styles/common.less +++ b/main/webapp/modules/core/styles/common.less @@ -242,6 +242,12 @@ a img { opacity: 0.3; } +.toggle-text { + font-size: x-small; + border-bottom: 1px blue dotted; + left-margin: 1em +} + #header { height: 40px; margin: 10px; diff --git a/main/webapp/modules/core/styles/dialogs/expression-preview-dialog.less b/main/webapp/modules/core/styles/dialogs/expression-preview-dialog.less index ff74b1a6e..61469ca2d 100644 --- a/main/webapp/modules/core/styles/dialogs/expression-preview-dialog.less +++ b/main/webapp/modules/core/styles/dialogs/expression-preview-dialog.less @@ -90,6 +90,7 @@ td.expression-preview-heading { td.expression-preview-value { max-width: 250px !important; + white-space: pre-wrap; overflow-x: hidden; } diff --git a/main/webapp/modules/core/styles/index/open-project-ui.less b/main/webapp/modules/core/styles/index/open-project-ui.less index d6309f3ca..c4eba6e05 100644 --- a/main/webapp/modules/core/styles/index/open-project-ui.less +++ b/main/webapp/modules/core/styles/index/open-project-ui.less @@ -132,12 +132,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. word-wrap: break-word; } +#metadata-body > table > tbody > tr:nth-child(1) > th:nth-child(1) { + width: 4% + } + #metadata-body > table > tbody > tr:nth-child(1) > th:nth-child(2) { - width: 80% + width: 16% } #metadata-body > table > tbody > tr:nth-child(1) > th:nth-child(3) { - width: 4% + width: 80% } /* Project Tags */ diff --git a/main/webapp/modules/core/styles/project/facets.less b/main/webapp/modules/core/styles/project/facets.less index ac72a8888..3d31b32f9 100644 --- a/main/webapp/modules/core/styles/project/facets.less +++ b/main/webapp/modules/core/styles/project/facets.less @@ -147,6 +147,7 @@ a.facet-title-remove:hover { } .facet-choice { + white-space: pre-wrap; padding: 2px 5px; clear: both; } diff --git a/main/webapp/modules/core/styles/project/sidebar.less b/main/webapp/modules/core/styles/project/sidebar.less index 9d8c36d4b..6e66e2c3d 100644 --- a/main/webapp/modules/core/styles/project/sidebar.less +++ b/main/webapp/modules/core/styles/project/sidebar.less @@ -98,7 +98,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. padding: 20px 0 0 10px; font-size: 1.15em; font-weight: bold; - background: url(../../images/logo-gem-40.png) no-repeat right top; + background: url(../../images/logo-gem-126.png) no-repeat top right; + background-size: 43px; } .browsing-panel-help p, .history-panel-help p { diff --git a/main/webapp/modules/core/styles/reconciliation/extend-data-preview-dialog.less b/main/webapp/modules/core/styles/reconciliation/extend-data-preview-dialog.less index 069a70628..0785bd368 100644 --- a/main/webapp/modules/core/styles/reconciliation/extend-data-preview-dialog.less +++ b/main/webapp/modules/core/styles/reconciliation/extend-data-preview-dialog.less @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .extend-data-preview-dialog .preview-container { border: 1px solid #aaa; overflow: auto; + max-width: 600px; } .extend-data-preview-dialog .preview-container table { diff --git a/main/webapp/modules/core/styles/util/encoding.less b/main/webapp/modules/core/styles/util/encoding.less index 1c64fccad..8c1fd8bd7 100644 --- a/main/webapp/modules/core/styles/util/encoding.less +++ b/main/webapp/modules/core/styles/util/encoding.less @@ -23,8 +23,8 @@ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @@ -41,6 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .select-encoding-dialog-encoding-list > table > tbody > tr > th { padding: @padding_tight; } +.select-encoding-dialog-encoding-list > table > tbody > tr > td:first-child, +.select-encoding-dialog-encoding-list > table > tbody > tr > th:first-child { + width: 150px; + } .select-encoding-dialog-encoding-list a { white-space: pre; - } \ No newline at end of file + } diff --git a/main/webapp/modules/core/styles/views/data-table-view.less b/main/webapp/modules/core/styles/views/data-table-view.less index 35eff8fc8..c6885da3d 100644 --- a/main/webapp/modules/core/styles/views/data-table-view.less +++ b/main/webapp/modules/core/styles/views/data-table-view.less @@ -168,6 +168,7 @@ div.data-table-cell-content { line-height: 1.2; color: #222; position: relative; + white-space: pre-wrap; } div.data-table-cell-content-numeric { @@ -316,6 +317,21 @@ a.data-table-flag-off { color: @light_grey; } +.set-httpheaders-container { + display: none; + } + +.set-httpheaders-container label { + display: inline-block; + width: 15%; + text-align: left; + } + +.set-httpheaders-container input { + display: inline-block; + width: 50%; + } + ul.sorting-dialog-blank-error-positions { margin: 0; padding: 5px; diff --git a/server/.classpath b/server/.classpath deleted file mode 100644 index a521b9fad..000000000 --- a/server/.classpath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/server/.project b/server/.project deleted file mode 100644 index 4d749e1c2..000000000 --- a/server/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - grefine-server - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/server/lib/native/windows/jdatapath.dll b/server/lib/native/windows/jdatapath.dll old mode 100755 new mode 100644 diff --git a/start.sh b/start.sh old mode 100644 new mode 100755 diff --git a/stop.sh b/stop.sh old mode 100644 new mode 100755