404: Something's gone wrong :-(
+ +You've tried to visit a page that doesn't exist. Luckily this site + has other pages.
+If you were looking for something specific, try searching: +
+ + +diff --git a/.github/ISSUE_TEMPLATE/---1-report-an-issue.md b/.github/ISSUE_TEMPLATE/---1-report-an-issue.md deleted file mode 100644 index 52fb548a0..000000000 --- a/.github/ISSUE_TEMPLATE/---1-report-an-issue.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: "\U0001F41B Report an issue" -about: A feature is not working as expected. -title: '' -labels: '' -assignees: '' - ---- - -### New Issue Checklist - - -- [ ] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-SDK-Flutter/security/policy). -- [ ] I am not just asking a [question](https://github.com/parse-community/.github/blob/main/SUPPORT.md). -- [ ] I have searched through [existing issues](https://github.com/parse-community/Parse-SDK-Flutter/issues?q=is%3Aissue). -- [ ] I can reproduce the issue with the latest version of [Parse Server](https://github.com/parse-community/parse-server/releases) and the [Parse Flutter SDK](https://github.com/parse-community/Parse-SDK-Flutter/releases). - -### Issue Description - - -### Steps to reproduce - - -### Actual Outcome - - -### Expected Outcome - - -### Environment - - -Parse Flutter SDK -- SDK version: `FILL_THIS_OUT` -- Flutter version: `FILL_THIS_OUT` -- Dart version: `FILL_THIS_OUT` -- Operating system version: `FILL_THIS_OUT` - -Server -- Parse Server version: `FILL_THIS_OUT` - -### Logs - diff --git a/.github/ISSUE_TEMPLATE/---2-feature-request.md b/.github/ISSUE_TEMPLATE/---2-feature-request.md deleted file mode 100644 index 77c685a50..000000000 --- a/.github/ISSUE_TEMPLATE/---2-feature-request.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: "\U0001F4A1 Request a feature" -about: Suggest new functionality or an enhancement of existing functionality. -title: '' -labels: '' -assignees: '' - ---- - -### New Feature / Enhancement Checklist - - -- [ ] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-SDK-Flutter/security/policy). -- [ ] I am not just asking a [question](https://github.com/parse-community/.github/blob/main/SUPPORT.md). -- [ ] I have searched through [existing issues](https://github.com/parse-community/Parse-SDK-Flutter/issues?q=is%3Aissue). - -### Current Limitation - - -### Feature / Enhancement Description - - -### Example Use Case - - -### Alternatives / Workarounds - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index e5a8c3caa..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: 🙋🏽♀️ Getting help with code - url: https://stackoverflow.com/questions/tagged/parse-platform - about: Get help with code-level questions on Stack Overflow. - - name: 🙋 Getting general help - url: https://community.parseplatform.org - about: Get help with other questions on our Community Forum. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 50d80c826..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,20 +0,0 @@ - -## Pull Request - -- Report security issues [confidentially](https://github.com/parse-community/Parse-SDK-Flutter/security/policy). -- Any contribution is under this [license](https://github.com/parse-community/Parse-SDK-Flutter/blob/master/LICENSE). -- Link this pull request to an [issue](https://github.com/parse-community/Parse-SDK-Flutter/issues?q=is%3Aissue). - -## Issue - - -Closes: FILL_THIS_OUT - -## Approach - - -## Tasks - - -- [ ] Add tests -- [ ] Add changes to documentation (guides, repository pages, code comments) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 304d625c5..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: ci -on: - push: - branches: - - "master" - - "development" - paths-ignore: - - "**/README.md" - - "docs/**" - pull_request: - branches: - - "**" -jobs: - check-dart: - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - # Dart framework may contain breaking changes in minor version releases, not following semver. - - name: Dart 3.0, Ubuntu - os: ubuntu-latest - sdk: 3.0.0 - - name: Dart 3.0, macOS - os: macos-latest - sdk: 3.0.0 - - name: Dart 3.0, Windows - os: windows-latest - sdk: 3.0.0 - # Only the latest Dart framework version (above) is tested with all architectures. Previous - # Dart framework versions (below) are only tested with Ubuntu to reduce CI resource usage. - - name: Dart 2.19 - os: ubuntu-latest - sdk: 2.19.6 - - name: Dart 2.18 - os: ubuntu-latest - sdk: 2.18.7 - - name: Dart beta - os: ubuntu-latest - sdk: beta - fail-fast: false - name: Test ${{ matrix.name }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Setup dart - uses: dart-lang/setup-dart@v1.5.0 - with: - sdk: ${{ matrix.sdk }} - - name: Install dependencies - run: dart pub get --directory packages/dart - - name: Run build_runner - run: (cd packages/dart && dart run build_runner build --delete-conflicting-outputs) - - name: Analyze code - run: dart analyze packages/dart --fatal-infos - - name: Lint - run: dart format --output=none --set-exit-if-changed packages/dart - - name: Publish dry run - run: cd packages/dart && dart pub publish --dry-run - - name: Run tests - run: (cd packages/dart && dart test --coverage=coverage) - - name: Convert code coverage - # Needs to be adapted to collect the coverage at all platforms if platform specific code is added. - if: ${{ always() && matrix.os == 'ubuntu-latest' }} - working-directory: packages/dart - run: | - dart pub global activate coverage - dart pub global run coverage:format_coverage -i coverage/test -o coverage/lcov.info --lcov --packages=.dart_tool/package_config.json --report-on=lib - - name: Upload code coverage - uses: codecov/codecov-action@v2 - # Needs to be adapted to collect the coverage at all platforms if platform specific code is added. - if: ${{ always() && matrix.os == 'ubuntu-latest' }} - with: - files: packages/dart/coverage/lcov.info - fail_ci_if_error: true - check-flutter: - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - # Flutter framework may contain breaking changes in minor version releases, not following semver. - - name: Flutter 3.10, Ubuntu - os: ubuntu-latest - sdk: 3.10.x - - name: Flutter 3.10, macOS - os: macos-latest - sdk: 3.10.x - - name: Flutter 3.10, Windows - os: windows-latest - sdk: 3.10.x - # Only the latest Flutter framework version (above) is tested with all architectures. Previous - # Flutter framework versions (below) are only tested with Ubuntu to reduce CI resource usage. - - name: Flutter 3.7 - os: ubuntu-latest - sdk: 3.7.x - - name: Flutter 3.3 - os: ubuntu-latest - sdk: 3.3.x - - name: Flutter beta - os: ubuntu-latest - sdk: beta - fail-fast: false - name: Test ${{ matrix.name }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Setup flutter (beta) - if: ${{ matrix.sdk == 'beta' }} - uses: subosito/flutter-action@v2 - with: - channel: "beta" - cache: true - - name: Setup flutter - if: ${{ matrix.sdk != 'beta' }} - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ matrix.sdk }} - cache: true - - name: Install dependencies on Ubuntu and MacOS - if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' - run: | - (cd packages/dart && flutter pub get) - (cd packages/flutter && flutter pub get) - - name: Install dependencies on Windows - if: matrix.os == 'windows-latest' - run: | - cmd /c "cd packages\dart && flutter pub get" - cmd /c "cd packages\flutter && flutter pub get" - - name: Analyze code - run: flutter analyze packages/flutter --fatal-infos - - name: Lint - run: dart format --output=none --set-exit-if-changed packages/flutter - - name: Publish dry run - run: cd packages/flutter && dart pub publish --dry-run - - name: Run tests - run: (cd packages/flutter && flutter test --coverage) - - name: Convert code coverage - # Needs to be adapted to collect the coverage at all platforms if platform specific code is added. - if: ${{ always() && matrix.os == 'ubuntu-latest' }} - working-directory: packages/flutter - run: | - escapedPath="$(echo `pwd` | sed 's/\//\\\//g')" - sed "s/^SF:lib/SF:$escapedPath\/lib/g" coverage/lcov.info > coverage/lcov-full.info - - name: Upload code coverage - uses: codecov/codecov-action@v2 - # Needs to be adapted to collect the coverage at all platforms if platform specific code is added. - if: ${{ always() && matrix.os == 'ubuntu-latest' }} - with: - files: packages/flutter/coverage/lcov-full.info - fail_ci_if_error: true -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true diff --git a/.github/workflows/release-automated.yml b/.github/workflows/release-automated.yml deleted file mode 100644 index 71736b297..000000000 --- a/.github/workflows/release-automated.yml +++ /dev/null @@ -1,54 +0,0 @@ -# To authenticate on pub.dev to publish a release via GitHub Actions, a tag -# needs to be pushed, see: https://dart.dev/tools/pub/automated-publishing -# -# Publishing cannot currently be triggered manually via a "workflow_dispatch", -# see: https://dart.dev/go/publishing-from-github - -name: release-automated -on: - push: - tags: - - "dart-[0-9]+.[0-9]+.[0-9]+*" - - "flutter-[0-9]+.[0-9]+.[0-9]+*" -env: - package: ${{ startsWith(github.ref_name, 'dart') && 'dart' || 'flutter' }} -jobs: - release: - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - id-token: write # This is required for requesting the JWT - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.ref_name }} - - name: Setup dart - uses: dart-lang/setup-dart@v1 - with: - sdk: '3.0.0' # Set to 3.0.0 due to Parse Dart SDK compatibility, can be removed later on - - name: Setup flutter - if: env.package == 'flutter' - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - cache: true - - name: Install dart dependencies - run: dart pub get --directory 'packages/dart' - - name: Install flutter dependencies - if: env.package == 'flutter' - run: dart pub get --directory 'packages/flutter' - - name: Analyze dart - if: env.package == 'dart' - run: dart analyze --fatal-infos 'packages/${{ env.package }}' - - name: Analyze flutter - if: env.package == 'flutter' - run: flutter analyze --fatal-infos 'packages/${{ env.package }}' - - name: Lint - run: dart format --output=none --set-exit-if-changed 'packages/${{ env.package }}' - - name: Publish package (dry-run) - working-directory: 'packages/${{ env.package }}' - run: dart pub publish --dry-run - - name: Publish package - working-directory: 'packages/${{ env.package }}' - run: dart pub publish --force diff --git a/.github/workflows/release-manual-docs.yml b/.github/workflows/release-manual-docs.yml deleted file mode 100644 index f3b1ab09a..000000000 --- a/.github/workflows/release-manual-docs.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: release-manual-docs -on: - workflow_dispatch: - inputs: - tag: - default: '' - description: 'Version tag (dart-#.#.# or flutter-#.#.#):' -env: - package: ${{ startsWith(github.event.inputs.tag, 'dart') && 'dart' || 'flutter' }} -jobs: - docs-publish: - if: github.event.inputs.tag != '' - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.inputs.tag }} - - name: Setup dart - uses: dart-lang/setup-dart@v1 - - name: Generate Docs - run: | - dart doc ./packages/${{ env.package }}/ -o ./.api_docs/${{ env.package }}/ - - name: Deploy - uses: peaceiris/actions-gh-pages@v3.7.3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./.api_docs/ - destination_dir: ${{ env.package }} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 00bf6f00c..000000000 --- a/.gitignore +++ /dev/null @@ -1,124 +0,0 @@ -# Miscellaneous -*.class -*.lock -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/ - -# Flutter repo-specific -/bin/cache/ -/bin/internal/bootstrap.bat -/bin/internal/bootstrap.sh -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/devicelab/ABresults*.json -/dev/docs/doc/ -/dev/docs/flutter.docs.zip -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/dev/integration_tests/**/xcuserdata -/dev/integration_tests/**/Pods -/packages/flutter/coverage/ -version -analysis_benchmark.json - -# packages file containing multi-root paths -.packages.generated - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -**/generated_plugin_registrant.dart -.packages -.pub-cache/ -.pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds - -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/key.properties -*.jks - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/.last_build_id -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/ephemeral -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* - -# macOS -**/macos/Flutter/GeneratedPluginRegistrant.swift -**/macos/Flutter/ephemeral/ - -# Linux -**/linux/flutter/ephemeral - -# Coverage -coverage/ -lcov.info - -# Symbols -app.*.symbols - -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock - diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 5f35995ba..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# Contributing to Parse SDK for Flutter - -We want to make contributing to this project as easy and transparent as possible. - -## Pull Requests - -We actively welcome your pull requests. When we get one, we'll run some Parse-specific integration tests on it first. From here, we'll need to get a core member to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process. - -1. Fork the repo and create your branch from `master`. -2. Add unit tests for any new code you add. -3. If you've changed APIs, update the documentation. -4. Ensure the test suite passes. - -## Code of Conduct - -This project adheres to the [Contributor Covenant Code of Conduct](https://github.com/parse-community/parse-server/blob/master/CODE_OF_CONDUCT.md). By participating, you are expected to honor this code. - -## License - -By contributing to Parse SDK Flutter, you agree that your contributions will be licensed under its license. - -[stack-overflow]: http://stackoverflow.com/tags/parse.com -[bug-reports]: https://github.com/parse-community/parse-server diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0e303d0f1..000000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/NOTICE b/NOTICE deleted file mode 100644 index b23623122..000000000 --- a/NOTICE +++ /dev/null @@ -1,10 +0,0 @@ -Parse Dart / Flutter SDK - -Copyright 2021-present Parse Platform - -This product includes software developed at Parse Platform. -www.parseplatform.org - ---- - -As of October 19, 2021, the original author Phill Wiggins has transferred this code to the Parse Platform organization. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index bb2ab9756..000000000 --- a/README.md +++ /dev/null @@ -1,27 +0,0 @@ - - ---- - -[](https://github.com/parse-community/Parse-SDK-Flutter/actions?query=workflow%3Aci+branch%3Amaster) -[](https://app.codecov.io/gh/parse-community/Parse-SDK-Flutter/branch/master) - -[][open-collective-link] -[][open-collective-link] -[](https://github.com/parse-community/Parse-SDK-Flutter/blob/master/LICENSE) -[](https://community.parseplatform.org/c/parse-server) -[](https://twitter.com/intent/follow?screen_name=ParsePlatform) - ---- - -These libraries give you access to the powerful Parse Server backend from your Dart/Flutter app. For more information about Parse Platform and its features visit [parseplatform.org](https://parseplatform.org/). - -# Packages - -These packages are available in this repository: - -| Type | Name | Pub | -|---------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Dart | [parse_server_sdk](./packages/dart) | [](https://pub.dev/packages/parse_server_sdk) [](https://pub.dev/packages/parse_server_sdk/score) [](https://pub.dev/packages/parse_server_sdk/score) [](https://pub.dev/packages/parse_server_sdk/score) | -| Flutter | [parse_server_sdk_flutter](./packages/flutter) | [](https://pub.dev/packages/parse_server_sdk_flutter) [](https://pub.dev/packages/parse_server_sdk_flutter/score) [](https://pub.dev/packages/parse_server_sdk_flutter/score) [](https://pub.dev/packages/parse_server_sdk_flutter/score) | - -[open-collective-link]: https://opencollective.com/parse-server diff --git a/dart/__404error.html b/dart/__404error.html new file mode 100644 index 000000000..b55662109 --- /dev/null +++ b/dart/__404error.html @@ -0,0 +1,112 @@ + + +
+ + + + + +You've tried to visit a page that doesn't exist. Luckily this site + has other pages.
+If you were looking for something specific, try searching: +
+ + +Future<dynamic> clear();
+static Future<CoreStoreSembastImp> getInstance(String dbPath,
+ {DatabaseFactory? factory, String? password}) async {
+ password ??= 'flutter_sdk';
+ if (_instance == null) {
+ factory ??= !parseIsWeb ? databaseFactoryIo : databaseFactoryWeb;
+ assert(() {
+ if (parseIsWeb) {
+ print(
+ '***********************************************************************************************************');
+ print(
+ 'Warning: CoreStoreSembastImp of the Parse_Server_SDK does not encrypt the database on WEB.');
+ print(
+ '***********************************************************************************************************');
+ }
+ if (password == 'flutter_sdk') {
+ print(
+ '***********************************************************************************************************');
+ print(
+ 'Warning: CoreStoreSembastImp uses the default password. Specify a custom password for increased security.');
+ print(
+ '***********************************************************************************************************');
+ }
+ return true;
+ }());
+ final Database db = await factory.openDatabase(dbPath,
+ codec: !parseIsWeb ? getXXTeaSembastCodec(password: password) : null);
+ _instance =
+ CoreStoreSembastImp._internal(db, StoreRef<String, String>.main());
+ }
+
+ return _instance!;
+}
+@override
+Future<List<String>?> getStringList(String key) async {
+ final List<String>? storedItem = await get(key);
+ return storedItem;
+}
+LiveQuery({bool? debug, bool? autoSendSessionId}) {
+ _debug = isDebugEnabled(objectLevelDebug: debug);
+ _sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId;
+ client = LiveQueryClient._getInstance(
+ debug: _debug, autoSendSessionId: _sendSessionId);
+}
+late LiveQueryClient client;
+factory LiveQueryClient() => _getInstance();
+StreamController<String>? chanelStream;
+Future<dynamic> disconnect({bool userInitialized = false}) async {
+ parse_web_socket.WebSocket? webSocket = _webSocket;
+ if (webSocket != null &&
+ webSocket.readyState == parse_web_socket.WebSocket.open) {
+ if (_debug) {
+ print('$_printConstLiveQuery: Socket closed');
+ }
+ await webSocket.close();
+ _webSocket = null;
+ }
+ WebSocketChannel? channel = _channel;
+ if (channel != null) {
+ if (_debug) {
+ print('$_printConstLiveQuery: close');
+ }
+ await channel.sink.close();
+ _channel = null;
+ }
+ _requestSubscription.values.toList().forEach((Subscription subscription) {
+ subscription._enabled = false;
+ });
+ _connecting = false;
+ if (userInitialized) {
+ _clientEventStreamController.sink
+ .add(LiveQueryClientEvent.userDisconnected);
+ }
+}
+Stream<LiveQueryClientEvent> get getClientEventStream {
+ return _clientEventStream;
+}
+static LiveQueryClient get instance => _getInstance();
+int readyState() {
+ parse_web_socket.WebSocket? webSocket = _webSocket;
+ if (webSocket != null) {
+ return webSocket.readyState;
+ }
+ return parse_web_socket.WebSocket.connecting;
+}
+late LiveQueryReconnectingController reconnectingController;
+Future<Subscription<T>> subscribe<T extends ParseObject>(
+ QueryBuilder<T> query,
+ {T? copyObject}) async {
+ if (_webSocket == null) {
+ await _clientEventStream.any((LiveQueryClientEvent event) =>
+ event == LiveQueryClientEvent.connected);
+ }
+ final int requestId = _requestIdGenerator();
+ final Subscription<T> subscription =
+ Subscription<T>(query, requestId, copyObject: copyObject);
+ _requestSubscription[requestId] = subscription;
+ //After a client connects to the LiveQuery server,
+ //it can send a subscribe message to subscribe a ParseQuery.
+ _subscribeLiveQuery(subscription);
+ return subscription;
+}
+void unSubscribe<T extends ParseObject>(Subscription<T> subscription) {
+ //Mount message for Unsubscribe
+ final Map<String, dynamic> unsubscribeMessage = <String, dynamic>{
+ 'op': 'unsubscribe',
+ 'requestId': subscription.requestId,
+ };
+ WebSocketChannel? channel = _channel;
+ if (channel != null) {
+ if (_debug) {
+ print('$_printConstLiveQuery: UnsubscribeMessage: $unsubscribeMessage');
+ }
+ channel.sink.add(jsonEncode(unsubscribeMessage));
+ subscription._enabled = false;
+ _requestSubscription.remove(subscription.requestId);
+ }
+}
+A constant List of the values in this enum, in order of their declaration.
+A constant List of the values in this enum, in order of their declaration.
+LiveQueryReconnectingController(
+ this._reconnect,
+ this._eventStream,
+ this.debug,
+) {
+ final ParseConnectivityProvider? connectivityProvider =
+ ParseCoreData().connectivityProvider;
+ if (connectivityProvider != null) {
+ connectivityProvider.checkConnectivity().then(_connectivityChanged);
+ connectivityProvider.connectivityStream.listen(_connectivityChanged);
+ } else {
+ print(
+ 'LiveQuery does not work, if there is no ParseConnectivityProvider provided.');
+ }
+ _eventStream.listen((LiveQueryClientEvent event) {
+ switch (event) {
+ case LiveQueryClientEvent.connected:
+ _isConnected = true;
+ _retryState = 0;
+ _userDisconnected = false;
+ break;
+ case LiveQueryClientEvent.disconnected:
+ _isConnected = false;
+ _setReconnect();
+ break;
+ case LiveQueryClientEvent.userDisconnected:
+ _userDisconnected = true;
+ Timer? currentTimer = _currentTimer;
+ if (currentTimer != null) {
+ currentTimer.cancel();
+ _currentTimer = null;
+ }
+ break;
+ }
+
+ if (debug) {
+ print('$debugTag: $event');
+ }
+ });
+ ParseCoreData().appResumedStream?.listen((void _) => _setReconnect());
+}
+final bool debug;
+static const String debugTag = 'LiveQueryReconnectingController';
+bool hasParseBeenInitialized() => _hasBeenInitialized;
+Future<ParseResponse> healthCheck(
+ {bool? debug, ParseClient? client, bool? sendSessionIdByDefault}) async {
+ final bool debugLocal = isDebugEnabled(objectLevelDebug: debug);
+
+ final ParseClient clientLocal = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId:
+ sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId,
+ securityContext: ParseCoreData().securityContext);
+
+ const String className = 'parseBase';
+ const ParseApiRQ type = ParseApiRQ.healthCheck;
+
+ try {
+ final ParseNetworkResponse response = await clientLocal
+ .get('${ParseCoreData().serverUrl}$keyEndPointHealth');
+ return handleResponse<Parse>(null, response, type, debugLocal, className);
+ } on Exception catch (e) {
+ return handleException(e, type, debugLocal, className);
+ }
+}
+To initialize Parse Server in your application
+This should be initialized in MyApp() creation
+Parse().initialize(
+ "PARSE_APP_ID",
+ "https://parse.myaddress.com/parse/,+%20%20%20%20%20%20%20clientKey:"asd23rjh234r234r234r",
+ debug: true,
+ liveQuery: true);
+
+Future<Parse> initialize(
+ String appId,
+ String serverUrl, {
+ bool debug = false,
+ String? appName,
+ String? appVersion,
+ String? appPackageName,
+ String? locale,
+ String? liveQueryUrl,
+ String? clientKey,
+ String? masterKey,
+ String? sessionId,
+ bool autoSendSessionId = true,
+ SecurityContext? securityContext,
+ CoreStore? coreStore,
+ Map<String, ParseObjectConstructor>? registeredSubClassMap,
+ ParseUserConstructor? parseUserConstructor,
+ ParseFileConstructor? parseFileConstructor,
+ List<int>? liveListRetryIntervals,
+ ParseConnectivityProvider? connectivityProvider,
+ String? fileDirectory,
+ Stream<void>? appResumedStream,
+ ParseClientCreator? clientCreator,
+}) async {
+ final String url = removeTrailingSlash(serverUrl);
+
+ await ParseCoreData.init(
+ appId,
+ url,
+ debug: debug,
+ appName: appName,
+ appVersion: appVersion,
+ appPackageName: appPackageName,
+ locale: locale,
+ liveQueryUrl: liveQueryUrl,
+ masterKey: masterKey,
+ clientKey: clientKey,
+ sessionId: sessionId,
+ autoSendSessionId: autoSendSessionId,
+ securityContext: securityContext,
+ store: coreStore,
+ registeredSubClassMap: registeredSubClassMap,
+ parseUserConstructor: parseUserConstructor,
+ parseFileConstructor: parseFileConstructor,
+ liveListRetryIntervals: liveListRetryIntervals,
+ connectivityProvider: connectivityProvider,
+ fileDirectory: fileDirectory,
+ appResumedStream: appResumedStream,
+ clientCreator: clientCreator,
+ );
+
+ _hasBeenInitialized = true;
+
+ objectsExistForEventually = await checkObjectsExistForEventually();
+
+ if (objectsExistForEventually) {
+ ParseObject.submitEventually();
+ }
+
+ return this;
+}
+static bool objectsExistForEventually = false;
+ParseACL is used to control which users can access or modify a particular object +ParseObject can have its own ParseACL +You can grant read and write permissions separately to specific users + or you can grant permissions to "the public" so that, for example, any user could read a particular object but +only a particular set of users could write to that object
+false
, the user may still be able to access it if getPublicReadAccess returns
+true
or a role that the user belongs to has read access.
+
+
+false
, the user may still be able to write it if getPublicWriteAccess returns
+true
or a role that the user belongs to has write access.
+
+
+Creates an ACL where only the provided user has access.
+owner
The only user that can read or write objects governed by this ACL.
ParseACL({ParseUser? owner}) {
+ if (owner != null) {
+ setReadAccess(userId: owner.objectId!, allowed: true);
+ setWriteAccess(userId: owner.objectId!, allowed: true);
+ }
+}
+ParseACL fromJson(Map<String, dynamic> map) {
+ final ParseACL parseACL = ParseACL();
+
+ map.forEach((String userId, dynamic permission) {
+ if (permission['read'] != null) {
+ parseACL.setReadAccess(userId: userId, allowed: permission['read']);
+ }
+ if (permission['write'] != null) {
+ parseACL.setWriteAccess(userId: userId, allowed: permission['write']);
+ }
+ });
+ return parseACL;
+}
+Get whether the public is allowed to read this object.
+bool getPublicReadAccess() {
+ return getReadAccess(userId: _publicKEY);
+}
+Set whether the public is allowed to write this object.
+bool getPublicWriteAccess() {
+ return getWriteAccess(userId: _publicKEY);
+}
+Get whether the given user id is explicitly allowed to read this object. Even if this returns
+false
, the user may still be able to access it if getPublicReadAccess returns
+true
or a role that the user belongs to has read access.
bool getReadAccess({required String userId}) {
+ return _permissionsById[userId]?.readPermission ?? false;
+}
+Get whether the given user id is explicitly allowed to write this object. Even if this
+returns false
, the user may still be able to write it if getPublicWriteAccess returns
+true
or a role that the user belongs to has write access.
bool getWriteAccess({required String userId}) {
+ return _permissionsById[userId]?.writePermission ?? false;
+}
+Set whether the public is allowed to read this object.
+void setPublicReadAccess({required bool allowed}) {
+ setReadAccess(userId: _publicKEY, allowed: allowed);
+}
+Set whether the public is allowed to write this object.
+void setPublicWriteAccess({required bool allowed}) {
+ setWriteAccess(userId: _publicKEY, allowed: allowed);
+}
+Set whether the given user id is allowed to read this object.
+void setReadAccess({required String userId, bool allowed = true}) {
+ final bool writePermission = getWriteAccess(userId: userId);
+ _setPermissionsIfNonEmpty(
+ userId: userId,
+ readPermission: allowed,
+ writePermission: writePermission);
+}
+Set whether the given user id is allowed to write this object.
+void setWriteAccess({required String userId, bool allowed = true}) {
+ final bool readPermission = getReadAccess(userId: userId);
+ _setPermissionsIfNonEmpty(
+ userId: userId,
+ readPermission: readPermission,
+ writePermission: allowed);
+}
+Map<String, dynamic> toJson() {
+ final Map<String, dynamic> map = <String, dynamic>{};
+ _permissionsById.forEach((String user, _ACLPermissions permission) {
+ map[user] = permission.toJson();
+ });
+ return map;
+}
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() => json.encode(toJson());
+Used to define the API calls made in ParseObject logs
+A constant List of the values in this enum, in order of their declaration.
+T
associated with a given key
+
+
+ParseACL
governing this object.
+
+
+String
in JSON format
+
+
+void clearUnsavedChanges() {
+ _unsavedChanges.clear();
+ _notifyChildrenAboutClearUnsaved();
+}
+bool containsValue(Object value) {
+ for (final val in _getObjectData().values) {
+ if (val == value || (val is _Valuable && val.getValue() == value)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+Creates a copy of this class
+@protected
+dynamic copy() => fromJson(toJson());
+Returns DateTime createdAt
+DateTime? get createdAt {
+ if (get<dynamic>(keyVarCreatedAt) is String) {
+ final String? dateAsString = get<String>(keyVarCreatedAt);
+ return dateAsString != null ? _parseDateFormat.parse(dateAsString) : null;
+ } else {
+ return get<DateTime>(keyVarCreatedAt);
+ }
+}
+Get item from value storage
+Future<dynamic> fromPin(String objectId) async {
+ final CoreStore coreStore = ParseCoreData().getStore();
+ final String? itemFromStore = await coreStore.getString(objectId);
+
+ if (itemFromStore != null) {
+ return fromJson(json.decode(itemFromStore));
+ }
+ return null;
+}
+Get a value of type T
associated with a given key
Returns null or defaultValue
if provided.
T? get<T>(String key, {T? defaultValue}) {
+ if (_getObjectData().containsKey(key)) {
+ final result = _getObjectData()[key];
+
+ if (result is _Valuable) {
+ return result.getValue() as T?;
+ }
+
+ if (result is _ParseRelation) {
+ return (result
+ ..parent = (this as ParseObject)
+ ..key = key) as T?;
+ }
+
+ return result as T?;
+ } else {
+ return defaultValue;
+ }
+}
+Returns String objectId
+String? get objectId => get<String>(keyVarObjectId);
+set objectId(String? objectId) => set<String?>(keyVarObjectId, objectId);
+dynamic operator [](String key) {
+ return get<dynamic>(key);
+}
+void operator []=(String key, dynamic value) {
+ set<dynamic>(key, value);
+}
+refers to the Table Name in your Parse Server
+String parseClassName = 'ParseBase';
+Saves item to value storage
+Future<bool> pin() async {
+ if (objectId != null) {
+ await unpin();
+ final Map<String, dynamic>? objectMap = parseEncode(this, full: true);
+ final String json = jsonEncode(objectMap);
+ await ParseCoreData().getStore().setString(objectId!, json);
+ return true;
+ } else {
+ return false;
+ }
+}
+Saves in storage
+Future<void> saveInStorage(String key) async {
+ final String objectJson = json.encode(toJson(full: true));
+ await ParseCoreData().getStore().setString(key, objectJson);
+}
+Add a key-value pair to this object.
+It is recommended to name keys in camelCaseLikeThis
bool forceUpdate is always true, if unsure as to whether an item is +needed or not, set to false
+void set<T>(String key, T value, {bool forceUpdate = true}) {
+ if (_getObjectData()[key] == value && !forceUpdate) {
+ return;
+ }
+
+ _getObjectData()[key] = _ParseOperation.maybeMergeWithPrevious<T>(
+ newValue: value,
+ previousValue: _getObjectData()[key],
+ parent: this as ParseObject,
+ key: key,
+ );
+
+ _unsavedChanges[key] = _getObjectData()[key];
+}
+Set the ParseACL
governing this object.
void setACL<ParseACL>(ParseACL acl) {
+ set(keyVarAcl, acl);
+}
+Converts object to String
in JSON format
Map<String, dynamic> toJson({
+ bool full = false,
+ bool forApiRQ = false,
+ bool allowCustomObjectId = false,
+}) {
+ final Map<String, dynamic> map = <String, dynamic>{
+ keyVarClassName: parseClassName,
+ };
+
+ if (objectId != null) {
+ map[keyVarObjectId] = objectId;
+ }
+
+ if (createdAt != null) {
+ map[keyVarCreatedAt] = _parseDateFormat.format(createdAt!);
+ }
+
+ if (updatedAt != null) {
+ map[keyVarUpdatedAt] = _parseDateFormat.format(updatedAt!);
+ }
+
+ final target = forApiRQ ? _unsavedChanges : _getObjectData();
+ target.forEach((String key, dynamic value) {
+ if (!map.containsKey(key)) {
+ map[key] = parseEncode(value, full: full);
+ }
+
+ if (forApiRQ &&
+ value is _ParseRelation &&
+ !value.shouldIncludeInRequest()) {
+ map.remove(key);
+ }
+ });
+
+ if (forApiRQ) {
+ map.remove(keyVarCreatedAt);
+ map.remove(keyVarUpdatedAt);
+ map.remove(keyVarClassName);
+ //map.remove(keyVarAcl);
+
+ if (!allowCustomObjectId) {
+ map.remove(keyVarObjectId);
+ }
+ map.remove(keyParamSessionToken);
+ }
+
+ return map;
+}
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() => json.encode(toJson(full: true));
+Remove item from value storage
+Future<bool> unpin({String? key}) async {
+ if (objectId != null || key != null) {
+ await ParseCoreData().getStore().remove(key ?? objectId!);
+ return true;
+ }
+
+ return false;
+}
+Returns DateTime updatedAt
+DateTime? get updatedAt {
+ if (get<dynamic>(keyVarUpdatedAt) is String) {
+ final String? dateAsString = get<String>(keyVarUpdatedAt);
+ return dateAsString != null ? _parseDateFormat.parse(dateAsString) : null;
+ } else {
+ return get<DateTime>(keyVarUpdatedAt);
+ }
+}
+@Deprecated("Use ParseCoreData() instead.")
+ParseCoreData get data => ParseCoreData();
+Future<ParseNetworkResponse> delete(
+ String path, {
+ ParseNetworkOptions? options,
+});
+Future<ParseNetworkResponse> get(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+});
+Future<ParseNetworkByteResponse> getBytes(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+ dynamic cancelToken,
+});
+Future<ParseNetworkResponse> post(
+ String path, {
+ String? data,
+ ParseNetworkOptions? options,
+});
+Future<ParseNetworkResponse> postBytes(
+ String path, {
+ Stream<List<int>>? data,
+ ParseNetworkOptions? options,
+ ProgressCallback? onSendProgress,
+ dynamic cancelToken,
+});
+Future<ParseNetworkResponse> put(
+ String path, {
+ String? data,
+ ParseNetworkOptions? options,
+});
+typedef ParseClientCreator = ParseClient Function(
+ {required bool sendSessionId, SecurityContext? securityContext});
+Creates method which can be used to deep clone objects
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ Creates a new cloud function object
+{https://docs.parseplatform.org/cloudcode/guide/}
+ParseCloudFunction(
+ this.functionName, {
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId,
+}) : super(
+ functionName,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ debug: debug,
+ ) {
+ _path = '/functions/$functionName';
+}
+Executes a cloud function
+To add the parameters, create an object and call set(value to set)
+Future<ParseResponse> execute(
+ {Map<String, dynamic>? parameters, Map<String, String>? headers}) async {
+ final String uri = '${ParseCoreData().serverUrl}$_path';
+ if (parameters != null) {
+ _setObjectData(parameters);
+ }
+ try {
+ final ParseNetworkResponse result = await _client.post(uri,
+ options: ParseNetworkOptions(headers: headers),
+ data: json.encode(_getObjectData()));
+ return handleResponse<ParseCloudFunction>(
+ this, result, ParseApiRQ.execute, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.execute, _debug, parseClassName);
+ }
+}
+Executes a cloud function that returns a ParseObject type
+To add the parameters, create an object and call set(value to set)
+Future<ParseResponse> executeObjectFunction<T extends ParseObject>(
+ {Map<String, dynamic>? parameters, Map<String, String>? headers}) async {
+ final String uri = '${ParseCoreData().serverUrl}$_path';
+ if (parameters != null) {
+ _setObjectData(parameters);
+ }
+ try {
+ final ParseNetworkResponse result = await _client.post(uri,
+ options: ParseNetworkOptions(headers: headers),
+ data: json.encode(_getObjectData()));
+ return handleResponse<T>(this, result,
+ ParseApiRQ.executeObjectionFunction, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(
+ e, ParseApiRQ.executeObjectionFunction, _debug, parseClassName);
+ }
+}
+final String functionName;
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ Creates an instance of ParseConfig so that you can grab all configs from the server
+ParseConfig({
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId,
+}) : super(
+ 'config',
+ debug: debug,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ );
+Adds a new config
+Future<ParseResponse> addConfig(String key, dynamic value) async {
+ try {
+ final String uri = '${ParseCoreData().serverUrl}/config';
+ final String body =
+ '{"params":{"$key": ${json.encode(parseEncode(value))}}}';
+ final ParseNetworkResponse result = await _client.put(uri, data: body);
+ return handleResponse<ParseConfig>(
+ this, result, ParseApiRQ.addConfig, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.addConfig, _debug, parseClassName);
+ }
+}
+Gets all configs from the server
+Future<ParseResponse> getConfigs() async {
+ try {
+ final String uri = '${ParseCoreData().serverUrl}/config';
+ final ParseNetworkResponse result = await _client.get(uri);
+ return handleResponse<ParseConfig>(
+ this, result, ParseApiRQ.getConfigs, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.getConfigs, _debug, parseClassName);
+ }
+}
+Future<ParseConnectivityResult> checkConnectivity();
+Stream<ParseConnectivityResult> get connectivityStream;
+Connection status check result.
+WiFi: Device connected via Wi-Fi
+ + +Mobile: Device connected to cellular network
+ + +None: Device not connected to any network
+ + +A constant List of the values in this enum, in order of their declaration.
+Singleton class that defines all user keys and data
+factory ParseCoreData() => _instance;
+String? appName;
+String? appPackageName;
+Stream<void>? appResumedStream;
+String? appVersion;
+String applicationId;
+late bool autoSendSessionId;
+late ParseClientCreator clientCreator;
+String? clientKey;
+ParseConnectivityProvider? connectivityProvider;
+ParseFileBase createFile({String? url, String? name}) =>
+ _subClassHandler.createFile(name: name, url: url);
+ParseObject createObject(String classname) {
+ return _subClassHandler.createObject(classname);
+}
+ParseUser createParseUser(
+ String? username, String? password, String? emailAddress,
+ {String? sessionToken, bool? debug, ParseClient? client}) {
+ return _subClassHandler.createParseUser(username, password, emailAddress,
+ sessionToken: sessionToken, debug: debug, client: client);
+}
+late bool debug;
+String? fileDirectory;
+CoreStore getStore() {
+ return storage;
+}
+Creates an instance of Parse Server
+This class should not be user unless switching servers during the app, +which is odd. Should only be user by Parse.init
+static Future<void> init(
+ String appId,
+ String serverUrl, {
+ required bool debug,
+ String? appName,
+ String? appVersion,
+ String? appPackageName,
+ String? locale,
+ String? liveQueryUrl,
+ String? masterKey,
+ String? clientKey,
+ String? sessionId,
+ required bool autoSendSessionId,
+ SecurityContext? securityContext,
+ CoreStore? store,
+ Map<String, ParseObjectConstructor>? registeredSubClassMap,
+ ParseUserConstructor? parseUserConstructor,
+ ParseFileConstructor? parseFileConstructor,
+ List<int>? liveListRetryIntervals,
+ ParseConnectivityProvider? connectivityProvider,
+ String? fileDirectory,
+ Stream<void>? appResumedStream,
+ ParseClientCreator? clientCreator,
+}) async {
+ _instance = ParseCoreData._init(appId, serverUrl);
+
+ _instance.storage = store ?? CoreStoreMemoryImp();
+ _instance.debug = debug;
+ _instance.appName = appName;
+ _instance.appVersion = appVersion;
+ _instance.appPackageName = appPackageName;
+ _instance.locale = locale;
+ _instance.liveQueryURL = liveQueryUrl;
+ _instance.clientKey = clientKey;
+ _instance.masterKey = masterKey;
+ _instance.sessionId = sessionId;
+ _instance.autoSendSessionId = autoSendSessionId;
+ _instance.securityContext = securityContext;
+ _instance.liveListRetryIntervals = liveListRetryIntervals ??
+ (parseIsWeb
+ ? <int>[0, 500, 1000, 2000, 5000]
+ : <int>[0, 500, 1000, 2000, 5000, 10000]);
+ _instance._subClassHandler = ParseSubClassHandler(
+ registeredSubClassMap: registeredSubClassMap,
+ parseUserConstructor: parseUserConstructor,
+ parseFileConstructor: parseFileConstructor,
+ );
+ _instance.connectivityProvider = connectivityProvider;
+ _instance.fileDirectory = fileDirectory;
+ _instance.appResumedStream = appResumedStream;
+ _instance.clientCreator = clientCreator ??
+ (({required bool sendSessionId, SecurityContext? securityContext}) =>
+ ParseHTTPClient(
+ sendSessionId: sendSessionId,
+ securityContext: securityContext));
+}
+static ParseCoreData get instance => _instance;
+String? liveQueryURL;
+String? locale;
+String? masterKey;
+void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
+ _subClassHandler.registerFileSubClass(parseFileConstructor);
+}
+void registerSubClass(
+ String className, ParseObjectConstructor objectConstructor) {
+ _subClassHandler.registerSubClass(className, objectConstructor);
+}
+void registerUserSubClass(ParseUserConstructor parseUserConstructor) {
+ _subClassHandler.registerUserSubClass(parseUserConstructor);
+}
+SecurityContext? securityContext;
+String serverUrl;
+String? sessionId;
+Sets the current sessionId.
+This is generated when a users logs in, or calls currentUser to update +their keys
+void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+}
+late CoreStore storage;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() => '$applicationId $masterKey';
+ParseDioClient({bool sendSessionId = false, dynamic securityContext}) {
+ _client = _ParseDioClient(
+ sendSessionId: sendSessionId,
+ securityContext: securityContext,
+ );
+}
+@override
+Future<ParseNetworkResponse> delete(String path,
+ {ParseNetworkOptions? options}) async {
+ try {
+ final dio.Response<String> dioResponse = await _client.delete<String>(
+ path,
+ options: _Options(headers: options?.headers),
+ );
+
+ return ParseNetworkResponse(
+ data: dioResponse.data!,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ return ParseNetworkResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ }
+}
+@override
+Future<ParseNetworkResponse> get(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+}) async {
+ try {
+ final dio.Response<String> dioResponse = await _client.get<String>(
+ path,
+ options: _Options(headers: options?.headers),
+ );
+
+ return ParseNetworkResponse(
+ data: dioResponse.data!,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ return ParseNetworkResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ }
+}
+@override
+Future<ParseNetworkByteResponse> getBytes(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+ dynamic cancelToken,
+}) async {
+ try {
+ final dio.Response<List<int>> dioResponse = await _client.get<List<int>>(
+ path,
+ cancelToken: cancelToken,
+ onReceiveProgress: onReceiveProgress,
+ options: _Options(
+ headers: options?.headers, responseType: dio.ResponseType.bytes),
+ );
+ return ParseNetworkByteResponse(
+ bytes: dioResponse.data,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ if (error.response != null) {
+ return ParseNetworkByteResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ } else {
+ return _getOtherCaseErrorForParseNetworkResponse(
+ error.error.toString());
+ }
+ }
+}
+@override
+Future<ParseNetworkResponse> post(String path,
+ {String? data, ParseNetworkOptions? options}) async {
+ try {
+ final dio.Response<String> dioResponse = await _client.post<String>(
+ path,
+ data: data,
+ options: _Options(headers: options?.headers),
+ );
+
+ return ParseNetworkResponse(
+ data: dioResponse.data!,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ return ParseNetworkResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ }
+}
+@override
+Future<ParseNetworkResponse> postBytes(String path,
+ {Stream<List<int>>? data,
+ ParseNetworkOptions? options,
+ ProgressCallback? onSendProgress,
+ dynamic cancelToken}) async {
+ try {
+ final dio.Response<String> dioResponse = await _client.post<String>(
+ path,
+ data: data,
+ cancelToken: cancelToken,
+ options: _Options(headers: options?.headers),
+ onSendProgress: onSendProgress,
+ );
+
+ return ParseNetworkResponse(
+ data: dioResponse.data!,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ if (error.response != null) {
+ return ParseNetworkResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ } else {
+ return _getOtherCaseErrorForParseNetworkResponse(
+ error.error.toString());
+ }
+ }
+}
+@override
+Future<ParseNetworkResponse> put(String path,
+ {String? data, ParseNetworkOptions? options}) async {
+ try {
+ final dio.Response<String> dioResponse = await _client.put<String>(
+ path,
+ data: data,
+ options: _Options(headers: options?.headers),
+ );
+
+ return ParseNetworkResponse(
+ data: dioResponse.data!,
+ statusCode: dioResponse.statusCode!,
+ );
+ } on dio.DioException catch (error) {
+ return ParseNetworkResponse(
+ data: error.response?.data ?? _fallbackErrorData,
+ statusCode: error.response?.statusCode ?? ParseError.otherCause,
+ );
+ }
+}
+ParseException is used in ParseResult
to inform the user of the exception
ParseError(
+ {this.code = otherCause,
+ this.message = 'OtherCause',
+ this.exception,
+ bool debug = false}) {
+ type = _exceptions[code];
+ if (debug) {
+ print(toString());
+ }
+}
+Error code indicating that an an account being linked is already linked +to another user.
+static const int accountAlreadyLinked = 208;
+Error code indicating that there were multiple errors. Aggregate errors +have an "errors" property, which is an array of error objects with more +detail about each error that occurred.
+static const int aggregateError = 600;
+Error code indicating the result was not found in the cache.
+static const int cacheMiss = 120;
+final int code;
+Error code indicating that the feature you tried to access is only +available internally for testing purposes.
+static const int commandUnavailable = 108;
+Error code indicating the connection to the Parse servers failed.
+static const int connectionFailed = 100;
+Error code indicating that the request was a duplicate and has been discarded due to +idempotency rules.
+static const int duplicateRequest = 159;
+Error code indicating that a unique field was given a value that is +already taken.
+static const int duplicateValue = 137;
+Error code indicating that the email is missing, but must be specified.
+static const int emailMissing = 204;
+Error code indicating that a user with the specified email was not found.
+static const int emailNotFound = 205;
+Error code indicating that the email has already been taken.
+static const int emailTaken = 203;
+Error code indicating that an application quota was exceeded. Upgrade to +resolve.
+static const int exceededQuota = 140;
+final Exception? exception;
+Error code indicating an error deleting a file.
+static const int fileDeleteError = 153;
+Error code indicating an error deleting an unnamed file.
+static const int fileDeleteUnnamedError = 161;
+Error code indicating the client was unable to read an input file.
+static const int fileReadError = 601;
+Error code indicating an error saving a file.
+static const int fileSaveError = 130;
+Error code indicating a file that was too large.
+static const int fileTooLarge = 129;
+Error code indicating that a field was set to an inconsistent type.
+static const int incorrectType = 111;
+Error code indicating that something has gone wrong with the server.
+static const int internalServerError = 1;
+Error code indicating an invalid ACL was provided.
+static const int invalidAcl = 123;
+Error code indicating an invalid channel name. A channel name is either +an empty string (the broadcast channel) or contains only a-zA-Z0-9_ +characters and starts with a letter.
+static const int invalidChannelName = 112;
+Error code indicating a missing or invalid classname. Classnames are +case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the +only valid characters.
+static const int invalidClassName = 103;
+Error code indicating an invalid content length.
+static const int invalidContentLength = 128;
+Error code indicating that the email address was invalid.
+static const int invalidEmailAddress = 125;
+Error code indicating an invalid event name.
+static const int invalidEventName = 160;
+Error code indicating that an invalid filename was used for ParseFile. +A valid file name contains only a-zA-Z0-9_. characters and is between 1 +and 128 characters.
+static const int invalidFileName = 122;
+Error code indicating that invalid image data was provided.
+static const int invalidImageData = 143;
+Error code indicating that badly formed JSON was received upstream. This +either indicates you have done something unusual with modifying how +things encode to JSON, or the network is failing badly.
+static const int invalidJson = 107;
+Error code indicating an invalid key name. Keys are case-sensitive. They +must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+static const int invalidKeyName = 105;
+Error code indicating that a user with a linked (e.g. Facebook) account +has an invalid session.
+static const int invalidLinkedSession = 251;
+Error code indicating that an invalid key was used in a nested +JSONObject.
+static const int invalidNestedKey = 121;
+Error code indicating a malformed pointer. You should not see this unless +you have been mucking about changing internal Parse code.
+static const int invalidPointer = 106;
+Error code indicating an invalid push time.
+static const int invalidPushTimeError = 152;
+Error code indicating you tried to query with a datatype that doesn't +support it, like exact matching an array or object.
+static const int invalidQuery = 102;
+Error code indicating that a role's name is invalid.
+static const int invalidRoleName = 139;
+Error code indicating an invalid operation occured on schema
+static const int invalidSchemaOperation = 255;
+Error code indicating that the current session token is invalid.
+static const int invalidSessionToken = 209;
+Error code indicating that a field had an invalid value.
+static const int invalidValue = 162;
+Error code indicating that a user cannot be linked to an account because +that account's id could not be found.
+static const int linkedIdMissing = 250;
+final String message;
+Error code indicating an error enabling or verifying MFA
+static const int mfaError = 210;
+Error code indicating that a valid MFA token must be provided
+static const int mfaTokenRequired = 211;
+Error code indicating a missing content length.
+static const int missingContentLength = 127;
+Error code indicating a missing content type.
+static const int missingContentType = 126;
+Error code indicating an unspecified object id.
+static const int missingObjectId = 104;
+Error code indicating that a user can only be created through signup.
+static const int mustCreateUserThroughSignup = 207;
+You must call Parse().initialize before using the Parse library.
+static const int notInitialized = 109;
+Error code indicating the specified object doesn't exist.
+static const int objectNotFound = 101;
+Error code indicating that the object is too large.
+static const int objectTooLarge = 116;
+Error code indicating that the operation isn't allowed for clients.
+static const int operationForbidden = 119;
+Error code indicating some error other than those enumerated here.
+static const int otherCause = -1;
+Error code indicating that the password is missing or empty.
+static const int passwordMissing = 201;
+Error code indicating that push is misconfigured.
+static const int pushMisconfigured = 115;
+Error code indicating that the application has exceeded its request +limit.
+static const int requestLimitExceeded = 155;
+Error code indicating that a Cloud Code script failed.
+static const int scriptFailed = 141;
+Error code indicating that a user object without a valid session could +not be altered.
+static const int sessionMissing = 206;
+Error code indicating that the request timed out on the server. Typically +this indicates that the request is too expensive to run.
+static const int timeout = 124;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() {
+ String exceptionString = ' \n';
+ exceptionString += '----';
+ exceptionString += '\nParseException (Type: $type) :';
+ exceptionString += '\nCode: $code';
+ exceptionString += '\nMessage: $message';
+ exceptionString += '----';
+ return exceptionString;
+}
+String? type;
+Error code indicating an unsaved file.
+static const int unsavedFileError = 151;
+Error code indicating that a service being linked (e.g. Facebook or +Twitter) is unsupported.
+static const int unsupportedService = 252;
+Error code indicating that the username is missing or empty.
+static const int usernameMissing = 200;
+Error code indicating that the username has already been taken.
+static const int usernameTaken = 202;
+Error code indicating that a Cloud Code validation failed.
+static const int validationError = 142;
+Error code indicating a real error code is unavailable because +we had to use an XDomainRequest object to allow CORS requests in +Internet Explorer, which strips the body from HTTP responses that have +a non-2XX status code.
+static const int xDomainRequest = 602;
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ Creates a new file
+{https://docs.parseplatform.org/rest/guide/#files/}
+ParseFile(this.file,
+ {String? name,
+ String? url,
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId})
+ : super(
+ name: file != null ? path.basename(file.path) : name!,
+ url: url,
+ debug: debug,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ );
+Cancels the current request (upload or download of file).
+@override
+void cancel([dynamic reason]) {
+ _cancelToken?.cancel(reason);
+ _cancelToken = null;
+}
+@override
+Future<ParseFile> download({ProgressCallback? progressCallback}) async {
+ if (url == null) {
+ return this;
+ }
+
+ file = File('${ParseCoreData().fileDirectory}/$name');
+ await file!.create();
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+
+ final ParseNetworkByteResponse response = await _client.getBytes(
+ url!,
+ onReceiveProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ await file!.writeAsBytes(response.bytes!);
+
+ return this;
+}
+File? file;
+Future<ParseFile> loadStorage() async {
+ final File possibleFile = File('${ParseCoreData().fileDirectory}/$name');
+ // ignore: avoid_slow_async_io
+ final bool exists = await possibleFile.exists();
+
+ if (exists) {
+ file = possibleFile;
+ } else {
+ file = null;
+ }
+
+ return this;
+}
+Add Progress Callback
+@override
+void progressCallback(ProgressCallback progressCallback) {
+ _progressCallback = progressCallback;
+}
+Uploads a file to Parse Server
+@override
+Future<ParseResponse> upload({ProgressCallback? progressCallback}) async {
+ if (saved) {
+ //Creates a Fake Response to return the correct result
+ final Map<String, String> response = <String, String>{
+ 'url': url!,
+ 'name': name
+ };
+ return handleResponse<ParseFile>(
+ this,
+ ParseNetworkResponse(data: json.encode(response), statusCode: 201),
+ ParseApiRQ.upload,
+ _debug,
+ parseClassName);
+ }
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+
+ final Map<String, String> headers = <String, String>{
+ HttpHeaders.contentTypeHeader:
+ lookupMimeType(file!.path) ?? 'application/octet-stream',
+ HttpHeaders.contentLengthHeader: '${file!.lengthSync()}',
+ };
+
+ try {
+ final String uri = ParseCoreData().serverUrl + _path;
+ final ParseNetworkResponse response = await _client.postBytes(
+ uri,
+ options: ParseNetworkOptions(headers: headers),
+ data: file!.openRead(),
+ onSendProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ if (response.statusCode == 201) {
+ final Map<String, dynamic> map = json.decode(response.data);
+ url = map['url'].toString();
+ name = map['name'].toString();
+ }
+
+ return handleResponse<ParseFile>(
+ this, response, ParseApiRQ.upload, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.upload, _debug, parseClassName);
+ }
+}
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ Creates a new file
+{https://docs.parseplatform.org/rest/guide/#files/}
+ParseFileBase(
+ {required String name,
+ String? url,
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId})
+ : super(keyFileClassname,
+ debug: debug,
+ autoSendSessionId: autoSendSessionId,
+ client: client) {
+ _path = '/files/$name';
+ this.name = name;
+ if (url != null) this.url = url;
+}
+void cancel([dynamic reason]);
+Future<ParseFileBase> download({ProgressCallback? progressCallback});
+void progressCallback(ProgressCallback progressCallback);
+Uploads a file to Parse Server
+@override
+Future<ParseResponse> save({dynamic context}) async {
+ return upload();
+}
+bool get saved => url != null;
+Converts object to String
in JSON format
@override
+Map<String, dynamic> toJson({
+ bool full = false,
+ bool forApiRQ = false,
+ bool allowCustomObjectId = false,
+}) =>
+ <String, String?>{'__type': keyFile, 'name': name, 'url': url};
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() => json.encode(toJson(full: true));
+Uploads a file to Parse Server
+Future<ParseResponse> upload({ProgressCallback? progressCallback});
+String? get url => super.get<String>(keyVarURL);
+set url(http://github.com/String?%20url) => set<String?>(keyVarURL, url);
+typedef ParseFileConstructor = ParseFileBase Function(
+ {String? name, String? url});
+Creates a Parse Object of type GeoPoint
+ParseGeoPoint({this.latitude = 0.0, this.longitude = 0.0})
+ : assert(
+ latitude < 90, 'Latitude must be within the range (-90.0, 90.0).'),
+ assert(
+ latitude > -90, 'Latitude must be within the range (-90.0, 90.0).'),
+ assert(latitude < 180,
+ 'Longitude must be within the range (-180.0, 180.0).'),
+ assert(latitude > -180,
+ 'Longitude must be within the range (-180.0, 180.0).');
+double latitude, longitude;
+double latitude, longitude;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() {
+ return 'latitude: $latitude, longitude: $longitude';
+}
+ParseHTTPClient(
+ {bool sendSessionId = false, SecurityContext? securityContext}) {
+ _client = _ParseHTTPClient(
+ sendSessionId: sendSessionId,
+ securityContext: securityContext,
+ );
+}
+Map<String, String>? get additionalHeaders => _client.additionalHeaders;
+set additionalHeaders(Map<String, String>? additionalHeaders) =>
+ _client.additionalHeaders = additionalHeaders;
+@override
+Future<ParseNetworkResponse> delete(String path,
+ {ParseNetworkOptions? options}) async {
+ final http.Response response = await _client.delete(
+ Uri.parse(path),
+ headers: options?.headers,
+ );
+ return ParseNetworkResponse(
+ data: response.body, statusCode: response.statusCode);
+}
+@override
+Future<ParseNetworkResponse> get(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+}) async {
+ final http.Response response = await _client.get(
+ Uri.parse(path),
+ headers: options?.headers,
+ );
+ return ParseNetworkResponse(
+ data: response.body, statusCode: response.statusCode);
+}
+@override
+Future<ParseNetworkByteResponse> getBytes(
+ String path, {
+ ParseNetworkOptions? options,
+ ProgressCallback? onReceiveProgress,
+ dynamic cancelToken,
+}) async {
+ final http.Response response = await _client.get(
+ Uri.parse(path),
+ headers: options?.headers,
+ );
+ return ParseNetworkByteResponse(
+ bytes: response.bodyBytes, statusCode: response.statusCode);
+}
+@override
+Future<ParseNetworkResponse> post(
+ String path, {
+ String? data,
+ ParseNetworkOptions? options,
+}) async {
+ final http.Response response = await _client.post(
+ Uri.parse(path),
+ body: data,
+ headers: options?.headers,
+ );
+ return ParseNetworkResponse(
+ data: response.body, statusCode: response.statusCode);
+}
+@override
+Future<ParseNetworkResponse> postBytes(
+ String path, {
+ Stream<List<int>>? data,
+ ParseNetworkOptions? options,
+ ProgressCallback? onSendProgress,
+ dynamic cancelToken,
+}) async {
+ final http.Response response = await _client.post(
+ Uri.parse(path),
+ //Convert the stream to a list
+ body: await data?.fold<List<int>>(<int>[],
+ (List<int> previous, List<int> element) => previous..addAll(element)),
+ headers: options?.headers,
+ );
+ return ParseNetworkResponse(
+ data: response.body, statusCode: response.statusCode);
+}
+@override
+Future<ParseNetworkResponse> put(
+ String path, {
+ String? data,
+ ParseNetworkOptions? options,
+}) async {
+ final http.Response response = await _client.put(
+ Uri.parse(path),
+ body: data,
+ headers: options?.headers,
+ );
+ return ParseNetworkResponse(
+ data: response.body, statusCode: response.statusCode);
+}
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ ParseInstallation.forQuery() : super(keyClassUser);
+Creates an instance of ParseInstallation
+ParseInstallation({
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId,
+}) : super(
+ keyClassInstallation,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ debug: debug,
+ );
+Map<String, dynamic> get acl => super
+ .get<Map<String, dynamic>>(keyVarAcl, defaultValue: <String, dynamic>{})!;
+set acl(Map<String, dynamic> acl) =>
+ set<Map<String, dynamic>>(keyVarAcl, acl);
+String? get appIdentifier => super.get<String>(keyAppIdentifier);
+String? get appName => super.get<String>(keyAppName);
+String? get appVersion => super.get<String>(keyAppVersion);
+Creates a new object and saves it online
+ +@override
+Future<ParseResponse> create(
+ {bool allowCustomObjectId = false, dynamic context}) async {
+ final bool isCurrent = await ParseInstallation.isCurrent(this);
+ if (isCurrent) {
+ await _updateInstallation();
+ }
+
+ final ParseResponse parseResponse =
+ await _create(allowCustomObjectId: allowCustomObjectId);
+ if (parseResponse.success && isCurrent) {
+ clearUnsavedChanges();
+ await saveInStorage(keyParseStoreInstallation);
+ }
+ return parseResponse;
+}
+Gets the current installation from storage
+static Future<ParseInstallation> currentInstallation() async {
+ return (await _getFromLocalStore()) ?? (await _createInstallation());
+}
+String? get deviceToken => super.get<String>(keyDeviceToken);
+set deviceToken(String? deviceToken) =>
+ set<String?>(keyDeviceToken, deviceToken);
+String? get deviceType => super.get<String>(keyDeviceType);
+Returns an <List
Future<List<dynamic>> getSubscribedChannels() async {
+ print('getSubscribedChannels');
+ final ParseResponse apiResponse =
+ await ParseObject(keyClassInstallation).getObject(objectId!);
+
+ if (apiResponse.success) {
+ final ParseObject installation = apiResponse.result;
+ return Future<List<dynamic>>.value(installation
+ .get<List<dynamic>>('channels', defaultValue: <dynamic>[]));
+ } else {
+ return <String>[];
+ }
+}
+String? get installationId => super.get<String>(keyInstallationId);
+static Future<bool> isCurrent(ParseInstallation installation) async {
+ _currentInstallationId ??= (await _getFromLocalStore())?.installationId;
+ return _currentInstallationId != null &&
+ installation.installationId == _currentInstallationId;
+}
+String? get parseVersion => super.get<String>(keyParseVersion);
+Saves the current installation
+@override
+Future<ParseResponse> save({dynamic context}) async {
+ final bool isCurrent = await ParseInstallation.isCurrent(this);
+ if (isCurrent) {
+ await _updateInstallation();
+ }
+ //ParseResponse parseResponse = await super.save();
+ final ParseResponse parseResponse = await _save();
+ if (parseResponse.success && isCurrent) {
+ clearUnsavedChanges();
+ await saveInStorage(keyParseStoreInstallation);
+ }
+ return parseResponse;
+}
+Subscribes the device to a channel of push notifications.
+Future<void> subscribeToChannel(String value) async {
+ final List<dynamic> channel = <String>[value];
+ setAddAllUnique('channels', channel);
+ await save();
+}
+Unsubscribes the device to a channel of push notifications.
+Future<void> unsubscribeFromChannel(String value) async {
+ final List<dynamic> channel = <String>[value];
+ setRemove('channels', channel);
+ await save();
+}
+ParseLiveElement(T object, {bool loaded = false, List<String>? includeObject})
+ : super(object,
+ loaded: loaded,
+ updatedSubItems:
+ ParseLiveList._toIncludeMap(includeObject ?? <String>[])) {
+ _includes = ParseLiveList._toIncludeMap(includeObject ?? <String>[]);
+ queryBuilder = QueryBuilder<T>(object.clone(<String, dynamic>{}))
+ ..whereEqualTo(keyVarObjectId, object.objectId);
+ if (includeObject != null) {
+ queryBuilder.includeObject(includeObject);
+ }
+ _init(object, loaded: loaded, includeObject: includeObject);
+}
+@override
+void dispose() {
+ final Subscription<T>? subscription = _subscription;
+ if (subscription != null) {
+ LiveQuery().client.unSubscribe(subscription);
+ _subscription = null;
+ }
+ super.dispose();
+}
+late QueryBuilder<T> queryBuilder;
+is object1 listed after object2?
+bool? after(T object1, T object2) {
+ List<String> fields = <String>[];
+
+ if (_query.limiters.containsKey('order')) {
+ fields = _query.limiters['order'].toString().split(',');
+ }
+ fields.add(keyVarCreatedAt);
+ for (String key in fields) {
+ bool reverse = false;
+ if (key.startsWith('-')) {
+ reverse = true;
+ key = key.substring(1);
+ }
+ final dynamic val1 = object1.get<dynamic>(key);
+ final dynamic val2 = object2.get<dynamic>(key);
+
+ if (val1 == null && val2 == null) {
+ break;
+ }
+ if (val1 == null) {
+ return reverse;
+ }
+ if (val2 == null) {
+ return !reverse;
+ }
+
+ if (val1 is num && val2 is num) {
+ if (val1 < val2) {
+ return reverse;
+ }
+ if (val1 > val2) {
+ return !reverse;
+ }
+ } else if (val1 is String && val2 is String) {
+ if (val1.toString().compareTo(val2) < 0) {
+ return reverse;
+ }
+ if (val1.toString().compareTo(val2) > 0) {
+ return !reverse;
+ }
+ } else if (val1 is DateTime && val2 is DateTime) {
+ if (val1.isAfter(val2)) {
+ return !reverse;
+ }
+ if (val1.isBefore(val2)) {
+ return reverse;
+ }
+ }
+ }
+ return null;
+}
+static Future<ParseLiveList<T>> create<T extends ParseObject>(
+ QueryBuilder<T> query, {
+ bool? listenOnAllSubItems,
+ List<String>? listeningIncludes,
+ bool lazyLoading = true,
+ List<String>? preloadedColumns,
+}) {
+ final ParseLiveList<T> parseLiveList = ParseLiveList<T>._(
+ query,
+ listenOnAllSubItems == true
+ ? _toIncludeMap(
+ query.limiters['include']?.toString().split(',') ?? <String>[])
+ : _toIncludeMap(listeningIncludes ?? <String>[]),
+ lazyLoading,
+ preloadedColumns: preloadedColumns ?? const <String>[],
+ );
+
+ return parseLiveList._init().then((_) {
+ return parseLiveList;
+ });
+}
+void dispose() {
+ Subscription<T>? liveQuerySubscription = _liveQuerySubscription;
+ if (liveQuerySubscription != null) {
+ LiveQuery().client.unSubscribe(liveQuerySubscription);
+ _liveQuerySubscription = null;
+ }
+ StreamSubscription<LiveQueryClientEvent>? liveQueryClientEventSubscription =
+ _liveQueryClientEventSubscription;
+ if (liveQueryClientEventSubscription != null) {
+ liveQueryClientEventSubscription.cancel();
+ _liveQueryClientEventSubscription = null;
+ }
+ while (_list.isNotEmpty) {
+ _list.removeLast().dispose();
+ }
+}
+Stream<T> getAt(final int index) async* {
+ if (index < _list.length) {
+ if (!_list[index].loaded) {
+ final QueryBuilder<T> queryBuilder = QueryBuilder<T>.copy(_query)
+ ..whereEqualTo(
+ keyVarObjectId, _list[index].object.get<String>(keyVarObjectId))
+ ..setLimit(1);
+ final ParseResponse response = await queryBuilder.query<T>();
+ if (_list.isEmpty) {
+ yield* _createStreamError<T>(
+ ParseError(message: 'ParseLiveList: _list is empty'));
+ return;
+ }
+ if (response.success) {
+ _list[index].object = response.results?.first;
+ } else {
+ ParseError? error = response.error;
+ if (error != null) yield* _createStreamError<T>(error);
+ return;
+ }
+ }
+// just for testing
+// await Future<void>.delayed(const Duration(seconds: 2));
+ yield _list[index].object;
+ yield* _list[index].stream;
+ }
+}
+T? getLoadedAt(int index) {
+ if (index < _list.length && _list[index].loaded) {
+ return _list[index].object;
+ }
+ return null;
+}
+T? getPreLoadedAt(int index) {
+ if (index < _list.length) {
+ return _list[index].object;
+ }
+ return null;
+}
+int get nextID => _nextID++;
+int get size {
+ return _list.length;
+}
+Stream<ParseLiveListEvent<T>> get stream => _eventStreamController.stream;
+ParseLiveListAddEvent(int index, T object) : super(index, object);
+ParseLiveListDeleteEvent(int index, T object) : super(index, object);
+ParseLiveListElement(this._object,
+ {bool loaded = false, Map<String, dynamic>? updatedSubItems})
+ : _loaded = loaded {
+ _updatedSubItems =
+ _toSubscriptionMap(updatedSubItems ?? <String, dynamic>{});
+ if (_updatedSubItems.isNotEmpty) {
+ _liveQuery = LiveQuery();
+ _subscribe();
+ }
+}
+void dispose() {
+ _unsubscribe(_updatedSubItems);
+ _streamController.close();
+}
+bool get loaded => _loaded;
+T get object => _object.clone(_object.toJson(full: true));
+set object(T value) {
+ _loaded = true;
+ _object = value;
+ _unsubscribe(_updatedSubItems);
+ _subscribe();
+ _streamController.add(object);
+}
+Future<void> reconnected() async {
+ if (loaded) {
+ _subscriptionQueue.whenComplete(() async {
+ await _updateSubItems(_object, _updatedSubItems);
+// _streamController.add(_object?.clone(_object.toJson(full: true)));
+ });
+ }
+}
+Stream<T> get stream => _streamController.stream;
+ParseLiveListElementSnapshot(
+ {this.loadedData, this.error, this.preLoadedData});
+final ParseError? error;
+bool get failed => error != null;
+bool get hasData => loadedData != null;
+bool get hasPreLoadedData => preLoadedData != null;
+final T? loadedData;
+final T? preLoadedData;
+ParseLiveListEvent(this._index, this._object);
+int get index => _index;
+T get object => _object;
+ParseLiveListUpdateEvent(int index, T object) : super(index, object);
+ParseNetworkByteResponse({
+ this.bytes,
+ final String data = 'byte response',
+ final int statusCode = -1,
+}) : super(
+ data: data,
+ statusCode: statusCode,
+ );
+final String data;
+final int statusCode;
+A constant List of the values in this enum, in order of their declaration.
+ParseObject is a local representation of data that can be saved and +retrieved from the Parse cloud.
+The basic workflow for creating new data is to construct a new ParseObject, +use set(key, value) to fill it with data, and then use save to persist +to the cloud.
+The basic workflow for accessing existing data is to use a QueryBuilder +to specify which existing data to retrieve.
+objets
to a relation associated with a given key
+
+
+T
associated with a given key
+ objectId
+
+
+key
+
+
+objets
from a relation associated with a given key
+
+
+ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+
+
+elements
to the end of the array
+associated with a given key
+
+
+elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+
+
+element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+
+
+key
by the given amount
+
+
+key
by the given amount
+
+
+element
from an array
+associated with a given key
+
+
+elements
contained in a List from the
+array associated with a given key
+
+
+String
in JSON format
+ ParseObject.clone(String className) : this(className);
+Creates a new Parse Object
+className
, refers to the Table Name in your Parse Server
debug
, will overwrite the current default debug settings
client
, can be overwritten to create your own HTTP Client
ParseObject(
+ String className, {
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId,
+}) : super() {
+ parseClassName = className;
+ _path = '$keyEndPointClasses$className';
+ _aggregatepath = '$keyEndPointAggregate$className';
+
+ _debug = isDebugEnabled(objectLevelDebug: debug);
+ _client = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId:
+ autoSendSessionId ?? ParseCoreData().autoSendSessionId,
+ securityContext: ParseCoreData().securityContext);
+}
+Add multiple objets
to a relation associated with a given key
void addRelation(String key, List<ParseObject> objets) {
+ set(key, _ParseAddRelationOperation(objets.toSet()));
+}
+Creates a new object and saves it online
+ +Future<ParseResponse> create(
+ {bool allowCustomObjectId = false, dynamic context}) async {
+ try {
+ final Uri url = getSanitisedUri(_client, _path);
+ final String body = json.encode(toJson(
+ forApiRQ: true,
+ allowCustomObjectId: allowCustomObjectId,
+ ));
+
+ _saveChanges();
+
+ final Map<String, String> headers = {
+ keyHeaderContentType: keyHeaderContentTypeJson,
+ };
+
+ if (context != null) {
+ headers
+ .addAll({keyHeaderCloudContext: json.encode(parseEncode(context))});
+ }
+
+ final ParseNetworkResponse result = await _client.post(url.toString(),
+ data: body, options: ParseNetworkOptions(headers: headers));
+
+ final response = handleResponse<ParseObject>(
+ this, result, ParseApiRQ.create, _debug, parseClassName);
+
+ if (!response.success) {
+ _notifyChildrenAboutErrorSaving();
+ }
+
+ return response;
+ } on Exception catch (e) {
+ _notifyChildrenAboutErrorSaving();
+ return handleException(e, ParseApiRQ.create, _debug, parseClassName);
+ }
+}
+Deletes the current object locally and online
+Future<ParseResponse> delete<T extends ParseObject>({
+ String? id,
+ String? path,
+}) async {
+ assert(() {
+ final objId = objectId;
+ final isNotValidObjectId = objId == null || objId.isEmpty;
+ final isNotValidIdArg = id == null || id.isEmpty;
+
+ if (isNotValidObjectId && isNotValidIdArg) {
+ throw Exception(
+ "Can't delete a parse object while the objectId property "
+ "and id argument is null or empty",
+ );
+ }
+
+ return true;
+ }());
+
+ try {
+ path ??= _path;
+ id ??= objectId;
+ final Uri url = getSanitisedUri(_client, '$_path/$id');
+ final ParseNetworkResponse result = await _client.delete(url.toString());
+ return handleResponse<T>(
+ this, result, ParseApiRQ.delete, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.delete, _debug, parseClassName);
+ }
+}
+Future<ParseResponse> deleteEventually() async {
+ // save object
+ final ParseResponse response = await delete();
+
+ if (response.success) {
+ // return success response
+ return response;
+ } else {
+ // if have network connection error
+ if ((response.error?.message ?? "").contains(keyNetworkError)) {
+ // save this object in CoreStore
+ await _addThisObjectToParseCoreDataList(keyParseStoreDeletes);
+ } else {
+ return response;
+ }
+ }
+ return response;
+}
+Future<ParseResponse> distinct<T extends ParseObject>(String query) async {
+ try {
+ final Uri url = getSanitisedUri(_client, _aggregatepath, query: query);
+ final ParseNetworkResponse result = await _client.get(url.toString());
+ return handleResponse<T>(
+ this, result, ParseApiRQ.query, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.query, _debug, parseClassName);
+ }
+}
+Fetches this object with the data from the server.
+Call this whenever you want the state of the object to reflect exactly +what is on the server.
+include
, is a list of ParseObject
s keys to be included directly and
+not as a pointer.
Future<ParseObject> fetch({List<String>? include}) async {
+ if (objectId == null || objectId!.isEmpty) {
+ throw 'can not fetch without a objectId';
+ }
+
+ final ParseResponse response = await getObject(objectId!, include: include);
+
+ if (response.success && response.results != null) {
+ return response.results!.first;
+ } else {
+ return this;
+ }
+}
+Gets all objects from this table - Limited response at the moment
+Future<ParseResponse> getAll() async {
+ try {
+ final Uri url = getSanitisedUri(_client, _path);
+ final ParseNetworkResponse result = await _client.get(url.toString());
+ return handleResponse<ParseObject>(
+ this, result, ParseApiRQ.getAll, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.getAll, _debug, parseClassName);
+ }
+}
+Gets an object from the server using it's objectId
include
, is a list of ParseObjects keys to be included directly and
+not as a pointer.
Future<ParseResponse> getObject(
+ String objectId, {
+ List<String>? include,
+}) async {
+ try {
+ String? query;
+ if (include != null) {
+ query = 'include=${concatenateArray(include)}';
+ }
+
+ final Uri url =
+ getSanitisedUri(_client, '$_path/$objectId', query: query);
+
+ final ParseNetworkResponse result = await _client.get(url.toString());
+ return handleResponse<ParseObject>(
+ this, result, ParseApiRQ.get, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.get, _debug, parseClassName);
+ }
+}
+Get the instance of ParseRelation class associated with the given key
ParseRelation<T> getRelation<T extends ParseObject>(String key) {
+ final potentialRelation = _getObjectData()[key];
+
+ if (potentialRelation == null) {
+ final relation = ParseRelation<T>(parent: this, key: key);
+
+ set(key, relation);
+
+ return relation;
+ }
+
+ if (potentialRelation is _ParseRelation<T>) {
+ return potentialRelation
+ ..parent = this
+ ..key = key;
+ }
+
+ throw ParseRelationException(
+ 'The key $key is associated with a value ($potentialRelation) '
+ 'can not be a relation');
+}
+Can be used to create custom queries
+Future<ParseResponse> query<T extends ParseObject>(String query,
+ {ProgressCallback? progressCallback}) async {
+ try {
+ final Uri url = getSanitisedUri(_client, _path, query: query);
+ final ParseNetworkResponse result = await _client.get(
+ url.toString(),
+ onReceiveProgress: progressCallback,
+ );
+ return handleResponse<T>(
+ this, result, ParseApiRQ.query, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.query, _debug, parseClassName);
+ }
+}
+Remove multiple objets
from a relation associated with a given key
void removeRelation(String key, List<ParseObject> objets) {
+ set(key, _ParseRemoveRelationOperation(objets.toSet()));
+}
+Saves the current object online.
+If the object not saved yet, this will create it. Otherwise, +it will send the updated object to the server.
+This will save any nested(child) object in this object. So you do not need +to each one of them manually.
+Example of saving child and parent objects using save():
+final dietPlan = ParseObject('Diet_Plans')..set('Fat', 15);
+final plan = ParseObject('Plan')..set('planName', 'John.W');
+dietPlan.set('plan', plan);
+
+// the save function will create the nested(child) object first and then
+// attempts to save the parent object.
+//
+// using create in this situation will throw an error, because the child
+// object is not saved/created yet and you need to create it manually
+await dietPlan.save();
+
+print(plan.objectId); // DLde4rYA8C
+print(dietPlan.objectId); // RGd4fdEUB
+
+
+The same principle works with ParseRelation
+Its safe to call this function aging if an error occurred while saving.
+ +Future<ParseResponse> save({dynamic context}) async {
+ final ParseResponse childrenResponse = await _saveChildren(this, _client);
+ if (childrenResponse.success) {
+ ParseResponse? response;
+ if (objectId == null) {
+ response = await create(context: context);
+ } else if (_isDirty(false)) {
+ response = await update(context: context);
+ }
+
+ if (response != null) {
+ if (response.success) {
+ _savingChanges.clear();
+ } else {
+ _revertSavingChanges();
+ }
+ return response;
+ }
+ }
+ return childrenResponse;
+}
+Future<ParseResponse> saveEventually() async {
+ // save object
+ final ParseResponse response = await save();
+
+ if (response.success) {
+ // return success response
+ return response;
+ } else {
+ // if have network connection error
+ if ((response.error?.message ?? "").contains(keyNetworkError)) {
+ // save this object in CoreStore
+ await _addThisObjectToParseCoreDataList(keyParseStoreObjects);
+ } else {
+ return response;
+ }
+ }
+ return response;
+}
+Add an element
to the end of the array associated with a given key
void setAdd<T>(String key, T element) {
+ set(key, _ParseAddOperation([element]));
+}
+Add multiple elements
to the end of the array
+associated with a given key
void setAddAll(String key, List<dynamic> elements) {
+ set(key, _ParseAddOperation(elements));
+}
+Add multiple elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
void setAddAllUnique(String key, List<dynamic> elements) {
+ set(key, _ParseAddUniqueOperation(elements));
+}
+Add an element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
void setAddUnique(String key, dynamic element) {
+ set(key, _ParseAddUniqueOperation([element]));
+}
+Remove every instance of an element
from an array
+associated with a given key
void setRemove(String key, dynamic element) {
+ set(key, _ParseRemoveOperation([element]));
+}
+Removes all instances of the elements
contained in a List from the
+array associated with a given key
void setRemoveAll(String key, List<dynamic> elements) {
+ set(key, _ParseRemoveOperation(elements));
+}
+static Future<ParseResponse?> submitDeleteEventually(
+ {ParseClient? client, bool? autoSendSessionId}) async {
+ // preparation ParseCoreData
+ final CoreStore coreStore = ParseCoreData().getStore();
+
+ // delete
+ // get json parse saved objects
+ List<String>? listDeletes =
+ await coreStore.getStringList(keyParseStoreDeletes);
+
+ if (listDeletes != null) {
+ int firstLength = listDeletes.length;
+ List<String> elementsToRemove = [];
+ for (var element in listDeletes) {
+ // decode json
+ dynamic object = json.decode(element);
+
+ // crate parse object
+ ParseObject parseObject = ParseObject(object[keyVarClassName],
+ client: client, autoSendSessionId: autoSendSessionId)
+ .fromJson(object);
+
+ // delete parse object
+ ParseResponse deleteResponse = await parseObject.delete();
+
+ if (deleteResponse.success) {
+ // remove from list Deletes
+ elementsToRemove.add(element);
+ }
+ }
+
+ // Remove the elements after the loop
+ for (var elementToRemove in elementsToRemove) {
+ listDeletes.remove(elementToRemove);
+ }
+
+ // set new list deletes in coreStore
+ coreStore.setStringList(keyParseStoreDeletes, listDeletes);
+
+ bool success = true;
+ int statusCode = 200;
+
+ if (listDeletes.length == firstLength) {
+ // Nothing has been deleted
+ success = false;
+ statusCode = -1;
+ }
+
+ final ParseResponse response = ParseResponse()
+ ..success = success
+ ..results = <dynamic>[]
+ ..statusCode = statusCode;
+
+ return response;
+ }
+
+ return null;
+}
+static Future<void> submitEventually(
+ {ParseClient? client, bool? autoSendSessionId}) async {
+ await submitSaveEventually(
+ client: client, autoSendSessionId: autoSendSessionId);
+ await submitDeleteEventually(
+ client: client, autoSendSessionId: autoSendSessionId);
+
+ Parse.objectsExistForEventually = await checkObjectsExistForEventually();
+}
+static Future<ParseResponse?> submitSaveEventually(
+ {ParseClient? client, bool? autoSendSessionId}) async {
+ // get client
+ ParseClient localClient = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId:
+ autoSendSessionId ?? ParseCoreData().autoSendSessionId,
+ securityContext: ParseCoreData().securityContext);
+
+ // preparation ParseCoreData
+ final CoreStore coreStore = ParseCoreData().getStore();
+
+ // save
+ // get json parse saved objects
+ List<String>? listSaves =
+ await coreStore.getStringList(keyParseStoreObjects);
+
+ if (listSaves != null) {
+ List<ParseObject> parseObjectList = [];
+ for (var element in listSaves) {
+ // decode json
+ dynamic object = json.decode(element);
+ parseObjectList
+ .add(ParseObject(object[keyVarClassName]).fromJson(object));
+ }
+
+ // send parseObjects to server
+ ParseResponse response =
+ await ParseObject._saveChildren(parseObjectList, localClient);
+
+ // if success clear all objects
+ if (response.success) {
+ coreStore.setStringList(keyParseStoreObjects, []);
+ }
+
+ return response;
+ }
+
+ return null;
+}
+Can be used set an objects variable to undefined rather than null
+If object is not saved remotely, set offlineOnly to true to avoid api calls.
+Future<ParseResponse> unset(String key, {bool offlineOnly = false}) async {
+ final dynamic object = _objectData[key];
+ _objectData.remove(key);
+ _unsavedChanges.remove(key);
+ _savingChanges.remove(key);
+
+ if (offlineOnly) {
+ return ParseResponse()..success = true;
+ }
+
+ if (objectId == null) {
+ return ParseResponse()..success = false;
+ }
+
+ try {
+ final Uri url = getSanitisedUri(_client, '$_path/$objectId');
+
+ final String body = '{"$key":{"__op":"Delete"}}';
+
+ final ParseNetworkResponse result =
+ await _client.put(url.toString(), data: body);
+
+ final ParseResponse response = handleResponse<ParseObject>(
+ this, result, ParseApiRQ.unset, _debug, parseClassName);
+
+ if (response.success) {
+ return ParseResponse()..success = true;
+ } else {
+ _objectData[key] = object;
+ _unsavedChanges[key] = object;
+ _savingChanges[key] = object;
+
+ return response;
+ }
+ } on Exception catch (e) {
+ _objectData[key] = object;
+ _unsavedChanges[key] = object;
+ _savingChanges[key] = object;
+
+ return handleException(e, ParseApiRQ.unset, _debug, parseClassName);
+ }
+}
+Send the updated object to the server.
+Will only send the dirty (modified) data and not the entire object
+The object should hold an objectId in order to update it
+ +Future<ParseResponse> update({dynamic context}) async {
+ assert(
+ objectId != null && (objectId?.isNotEmpty ?? false),
+ "Can't update a parse object while the objectId property is null or empty",
+ );
+
+ try {
+ final Uri url = getSanitisedUri(_client, '$_path/$objectId');
+ final String body = json.encode(toJson(forApiRQ: true));
+
+ _saveChanges();
+
+ final Map<String, String> headers = {
+ keyHeaderContentType: keyHeaderContentTypeJson,
+ };
+
+ if (context != null) {
+ headers
+ .addAll({keyHeaderCloudContext: json.encode(parseEncode(context))});
+ }
+
+ final ParseNetworkResponse result = await _client.put(url.toString(),
+ data: body, options: ParseNetworkOptions(headers: headers));
+
+ final response = handleResponse<ParseObject>(
+ this, result, ParseApiRQ.save, _debug, parseClassName);
+
+ if (!response.success) {
+ _notifyChildrenAboutErrorSaving();
+ }
+
+ return response;
+ } on Exception catch (e) {
+ _notifyChildrenAboutErrorSaving();
+ return handleException(e, ParseApiRQ.save, _debug, parseClassName);
+ }
+}
+typedef ParseObjectConstructor = ParseObject Function();
+const ParseOperationException([this.message]);
+final String? message;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() {
+ if (message == null) return "ParseOperationException";
+ return "ParseOperationException: $message";
+}
+factory ParseRelation.fromJson(
+ Map<String, dynamic> map, {
+ ParseObject? parent,
+ String? key,
+}) {
+ return _ParseRelation.fromJson(map, parent: parent, key: key);
+}
+factory ParseRelation({
+ required ParseObject parent,
+ required String key,
+}) {
+ return _ParseRelation(parent: parent, key: key);
+}
+Add object to this relation
+void add(T parseObject);
+Add objects to this relation.
+void addAll(List<T> parseObjects);
+String getKey();
+ParseObject getParent();
+Gets a query that can be used to query the objects in this relation.
+Return a QueryBuilder that restricts the results to objects in this relation
+QueryBuilder getQuery();
+The className of the target objects.
+@Deprecated('use the targetClass getter')
+String get getTargetClass;
+Remove object from this relation
+void remove(T parseObject);
+Remove objects from this relation
+void removeAll(List<T> parseObjects);
+Will work only if the current target class is null, otherwise will throw +ParseRelationException with the message: +The target class can not be modified if it is already set
+set setTargetClass(String targetClass);
+The className of the target objects.
+String? get targetClass;
+const ParseRelationException([this.message]);
+final String? message;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() {
+ if (message == null) return "ParseRelationException";
+ return "ParseRelationException: $message";
+}
+ParseResponse({
+ this.error,
+});
+int count = 0;
+ParseError? error;
+If result is a singular result, i.e. getByObjectID
+This is now deprecated - Please use results. This will contain a list of +results, no need to check if its a list or a list of elements anymore.
+dynamic result;
+All results stored as a list - Even if only one response is returned
+// ignore: always_specify_types
+List? results;
+int statusCode = -1;
+bool success = false;
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ ParseSession({
+ bool? debug,
+ ParseClient? client,
+}) : super(
+ keyClassSession,
+ client: client,
+ debug: debug,
+ );
+@override
+ParseSession clone(Map<String, dynamic> map) {
+ return fromJson(map);
+}
+DateTime get expiresAt => super.get<DateTime>(keyVarExpiresAt)!;
+Future<ParseResponse> getCurrentSessionFromServer() async {
+ try {
+ const String path = '$keyEndPointSessions/me';
+ final Uri url = getSanitisedUri(_client, path);
+
+ final ParseNetworkResponse response = await _client.get(url.toString());
+
+ return handleResponse<ParseSession>(
+ this, response, ParseApiRQ.logout, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.logout, _debug, parseClassName);
+ }
+}
+String get installationId => super.get<String>(keyVarInstallationId)!;
+set installationId(String installationId) =>
+ set<String>(keyVarInstallationId, installationId);
+bool get restricted => super.get<bool>(keyVarRestricted)!;
+String get sessionToken => super.get<String>(keyVarSessionToken)!;
+ParseObject get user => super.get<ParseObject>(keyVarUser)!;
+ParseSubClassHandler(
+ {Map<String, ParseObjectConstructor>? registeredSubClassMap,
+ ParseUserConstructor? parseUserConstructor,
+ ParseFileConstructor? parseFileConstructor}) {
+ _subClassMap = registeredSubClassMap ?? <String, ParseObjectConstructor>{};
+ _parseUserConstructor = parseUserConstructor;
+ if (parseFileConstructor != null) {
+ _parseFileConstructor = parseFileConstructor;
+ }
+}
+ParseFileBase createFile({String? name, String? url}) =>
+ _parseFileConstructor(name: name, url: url);
+ParseObject createObject(String classname) {
+ if (classname == keyClassUser) {
+ return createParseUser(null, null, null);
+ }
+ if (_subClassMap.containsKey(classname)) {
+ return _subClassMap[classname]!();
+ }
+ return ParseObject(classname);
+}
+ParseUser createParseUser(
+ String? username, String? password, String? emailAddress,
+ {String? sessionToken, bool? debug, ParseClient? client}) {
+ return _parseUserConstructor != null
+ ? _parseUserConstructor!(username, password, emailAddress,
+ sessionToken: sessionToken, debug: debug, client: client)
+ : ParseUser(username, password, emailAddress,
+ sessionToken: sessionToken, debug: debug, client: client);
+}
+static ParseFileBase defaultParseFileConstructor(
+ {String? name, String? url}) {
+ if (parseIsWeb) {
+ return ParseWebFile(null, name: name!, url: url);
+ } else {
+ return ParseFile(null, name: name, url: url);
+ }
+}
+void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
+ _parseFileConstructor = parseFileConstructor;
+}
+void registerSubClass(
+ String className, ParseObjectConstructor objectConstructor) {
+ if (className != keyClassUser &&
+ className != keyClassInstallation &&
+ className != keyClassSession &&
+ className != keyFileClassname) {
+ _subClassMap[className] = objectConstructor;
+ }
+}
+void registerUserSubClass(ParseUserConstructor parseUserConstructor) {
+ _parseUserConstructor = parseUserConstructor;
+}
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to add 'X-Parse-Installation-Id' as an allowed header on your parse-server.
+
+
+objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to add 'X-Parse-Installation-Id' as an allowed header on your parse-server.
+
+
+ParseUser.forQuery() : super(keyClassUser);
+Creates an instance of ParseUser
+Users can set whether debug should be set on this class with a bool,
+they can also create their own custom version of ParseHttpClient
Creates a new user locally
+Requires String username, String password. String email address +is required as well to create a full new user object on ParseServer. Only +username and password is required to login
+ParseUser(
+ String? username,
+ String? password,
+ String? emailAddress, {
+ String? sessionToken,
+ bool? debug,
+ ParseClient? client,
+}) : super(
+ keyClassUser,
+ client: client,
+ autoSendSessionId: true,
+ debug: debug,
+ ) {
+ if (username != null) this.username = username;
+ if (emailAddress != null) this.emailAddress = emailAddress;
+ if (password != null) this.password = password;
+ if (sessionToken != null) this.sessionToken = sessionToken;
+}
+Map<String, dynamic> get acl => super
+ .get<Map<String, dynamic>>(keyVarAcl, defaultValue: <String, dynamic>{})!;
+set acl(Map<String, dynamic> acl) =>
+ set<Map<String, dynamic>>(keyVarAcl, acl);
+Gets a list of all users (limited return)
+static Future<ParseResponse> all({bool? debug, ParseClient? client}) async {
+ final ParseUser emptyUser = _getEmptyUser();
+
+ final bool debugLocal = isDebugEnabled(objectLevelDebug: debug);
+ final ParseClient clientLocal = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId: true,
+ securityContext: ParseCoreData().securityContext);
+
+ try {
+ final Uri url = getSanitisedUri(clientLocal, path);
+ final ParseNetworkResponse response =
+ await clientLocal.get(url.toString());
+ final ParseResponse parseResponse = handleResponse<ParseUser>(
+ emptyUser, response, ParseApiRQ.getAll, debugLocal, keyClassUser);
+ return parseResponse;
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.getAll, debugLocal, keyClassUser);
+ }
+}
+Map<String, dynamic>? get authData =>
+ super.get<Map<String, dynamic>>(keyVarAuthData);
+set authData(Map<String, dynamic>? authData) =>
+ set<Map<String, dynamic>?>(keyVarAuthData, authData);
+static ParseUser createUser(
+ [String? username, String? password, String? emailAddress]) {
+ return ParseCoreData.instance
+ .createParseUser(username, password, emailAddress);
+}
+Gets the current user from storage
+Current user is stored locally, but in case of a server update bool
+fromServer can be called and an updated version of the User
object will be
+returned
static Future<dynamic> currentUser({ParseCloneable? customUserObject}) async {
+ if (customUserObject != null) {
+ return await _getUserFromLocalStore(cloneable: customUserObject);
+ } else {
+ return await _getUserFromLocalStore();
+ }
+}
+Delete the local user data.
+Future<void> deleteLocalUserData() async {
+ await unpin(key: keyParseStoreUser);
+ _setObjectData(<String, dynamic>{});
+}
+Removes a user from Parse Server locally and online
+Future<ParseResponse?> destroy() async {
+ if (objectId != null) {
+ try {
+ final Uri url = getSanitisedUri(_client, '$_path/$objectId');
+ final ParseNetworkResponse response =
+ await _client.delete(url.toString());
+ return await _handleResponse(
+ this, response, ParseApiRQ.destroy, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.destroy, _debug, parseClassName);
+ }
+ }
+
+ return null;
+}
+String? get emailAddress => super.get<String>(keyVarEmail);
+set emailAddress(String? emailAddress) =>
+ set<String?>(keyVarEmail, emailAddress);
+bool? get emailVerified => super.get<bool>(keyEmailVerified);
+set emailVerified(bool? emailVerified) =>
+ set<bool?>(keyEmailVerified, emailVerified);
+void forgetLocalSession() {
+ ParseCoreData().sessionId = null;
+}
+Gets the current user from the server
+Current user is stored locally, but in case of a server update bool
+fromServer can be called and an updated version of the User
object will be
+returned.
NOTE: If using custom ParseUserObject create instance and user getUpdatedUser
+static Future<ParseResponse?> getCurrentUserFromServer(String token,
+ {bool? debug, ParseClient? client}) async {
+ final ParseUser user = _getEmptyUser();
+ user.sessionToken = token;
+ return user.getUpdatedUser(debug: debug, client: client);
+}
+Get the updated version of the user from the server
+Uses token to get the latest version of the user. Prefer this to getCurrentUserFromServer +if using custom ParseUser object
+Future<ParseResponse> getUpdatedUser(
+ {bool? debug, ParseClient? client}) async {
+ final bool debugLocal = isDebugEnabled(objectLevelDebug: debug);
+ final ParseClient clientLocal = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId: true,
+ securityContext: ParseCoreData().securityContext);
+
+ // We can't get the current user and session without a sessionId
+ if ((ParseCoreData().sessionId == null) && (sessionToken == null)) {
+ ///return null;
+ throw 'can not get the current user and session without a sessionId';
+ }
+
+ final Map<String, String> headers = <String, String>{};
+ if (sessionToken != null) {
+ headers[keyHeaderSessionToken] = sessionToken!;
+ }
+
+ try {
+ final Uri url = getSanitisedUri(clientLocal, keyEndPointUserName);
+ final ParseNetworkResponse response = await clientLocal.get(
+ url.toString(),
+ options: ParseNetworkOptions(headers: headers),
+ );
+ return await _handleResponse(
+ this, response, ParseApiRQ.currentUser, debugLocal, parseClassName);
+ } on Exception catch (e) {
+ return handleException(
+ e, ParseApiRQ.currentUser, debugLocal, parseClassName);
+ }
+}
+static const String keyEmailAddress = 'email';
+static const String keyEmailVerified = 'emailVerified';
+static const String keyUsername = 'username';
+Logs a user in via Parse
+Once a user is created using Parse.create
and a username and password is
+provided, call this method to login.
+Set doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to set 'X-Parse-Installation-Id' as an allowed header on your parse-server.
Future<ParseResponse> login({bool doNotSendInstallationID = false}) async {
+ forgetLocalSession();
+
+ try {
+ final Map<String, dynamic> queryParams = <String, String>{
+ keyVarUsername: username!,
+ keyVarPassword: password!
+ };
+ final String? installationId = await _getInstallationId();
+ final Uri url = getSanitisedUri(_client, keyEndPointLogin);
+ _saveChanges();
+ final ParseNetworkResponse response = await _client.post(
+ url.toString(),
+ data: jsonEncode(queryParams),
+ options: ParseNetworkOptions(headers: <String, String>{
+ keyHeaderRevocableSession: '1',
+ if (installationId != null && !doNotSendInstallationID)
+ keyHeaderInstallationId: installationId,
+ }),
+ );
+
+ return await _handleResponse(
+ this, response, ParseApiRQ.login, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.login, _debug, parseClassName);
+ }
+}
+Logs in a user anonymously
+Set doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to add 'X-Parse-Installation-Id' as an allowed header on your parse-server.
Future<ParseResponse> loginAnonymous(
+ {bool doNotSendInstallationID = false}) async {
+ forgetLocalSession();
+ try {
+ final Uri url = getSanitisedUri(_client, keyEndPointUsers);
+ const Uuid uuid = Uuid();
+ final String? installationId = await _getInstallationId();
+
+ final ParseNetworkResponse response = await _client.post(
+ url.toString(),
+ options: ParseNetworkOptions(headers: <String, String>{
+ keyHeaderRevocableSession: '1',
+ if (installationId != null && !doNotSendInstallationID)
+ keyHeaderInstallationId: installationId,
+ }),
+ data: jsonEncode(<String, dynamic>{
+ 'authData': <String, dynamic>{
+ 'anonymous': <String, dynamic>{'id': uuid.v4()}
+ }
+ }),
+ );
+
+ return await _handleResponse(
+ this, response, ParseApiRQ.loginAnonymous, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(
+ e, ParseApiRQ.loginAnonymous, _debug, parseClassName);
+ }
+}
+Logs in a user using a service
+Set doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to add 'X-Parse-Installation-Id' as an allowed header on your parse-server.
static Future<ParseResponse> loginWith(String provider, Object authData,
+ {bool doNotSendInstallationID = false,
+ String? username,
+ String? password,
+ String? email}) async {
+ final ParseUser user = ParseUser.createUser(username, password, email);
+ final ParseResponse response = await user._loginWith(provider, authData,
+ doNotSendInstallationID: doNotSendInstallationID);
+ return response;
+}
+Sends a request to delete the sessions token from the +server. Will also delete the local user data unless +deleteLocalUserData is false.
+Future<ParseResponse> logout({bool deleteLocalUserData = true}) async {
+ final String? sessionId = ParseCoreData().sessionId;
+
+ if (sessionId == null) {
+ return await _handleResponse(
+ this,
+ ParseNetworkResponse(data: "{}", statusCode: 200),
+ ParseApiRQ.logout,
+ _debug,
+ parseClassName);
+ }
+
+ forgetLocalSession();
+
+ if (deleteLocalUserData == true) {
+ await this.deleteLocalUserData();
+ }
+
+ try {
+ final Uri url = getSanitisedUri(_client, keyEndPointLogout);
+ final ParseNetworkResponse response = await _client.post(
+ url.toString(),
+ options: ParseNetworkOptions(
+ headers: <String, String>{keyHeaderSessionToken: sessionId}),
+ );
+
+ return await _handleResponse(
+ this, response, ParseApiRQ.logout, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.logout, _debug, parseClassName);
+ }
+}
+String? get password => _password;
+set password(String? password) {
+ if (_password != password) {
+ _password = password;
+ if (password != null) _unsavedChanges[keyVarPassword] = password;
+ }
+}
+static const String path = '$keyEndPointClasses$keyClassUser';
+Sends a password reset email to the users email address
+Future<ParseResponse> requestPasswordReset() async {
+ try {
+ final ParseNetworkResponse response = await _client.post(
+ '${ParseCoreData().serverUrl}$keyEndPointRequestPasswordReset',
+ data: json.encode(<String, dynamic>{keyVarEmail: emailAddress}),
+ );
+ return await _handleResponse(this, response,
+ ParseApiRQ.requestPasswordReset, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(
+ e, ParseApiRQ.requestPasswordReset, _debug, parseClassName);
+ }
+}
+Saves the current user
+If changes are made to the current user, call save to sync them with +Parse Server
+@override
+Future<ParseResponse> save({dynamic context}) async {
+ if (objectId == null) {
+ return await signUp();
+ } else {
+ final ParseResponse response = await super.save();
+ if (response.success) {
+ await _onResponseSuccess();
+ }
+ return response;
+ }
+}
+String? get sessionToken => super.get<String>(keyVarSessionToken);
+set sessionToken(String? sessionToken) =>
+ set<String?>(keyVarSessionToken, sessionToken);
+Registers a user on Parse Server
+After creating a new user via Parse.create
call this method to register
+that user on Parse
+By setting allowWithoutEmail
to true
, you can sign up without setting an email
+Set doNotSendInstallationID
to 'true' in order to prevent the SDK from sending the installationID to the Server.
+This option is especially useful if you are running you application on web and you don't have permission to add 'X-Parse-Installation-Id' as an allowed header on your parse-server.
Future<ParseResponse> signUp(
+ {bool allowWithoutEmail = false,
+ bool doNotSendInstallationID = false}) async {
+ forgetLocalSession();
+
+ try {
+ if (emailAddress == null) {
+ if (!allowWithoutEmail) {
+ assert(() {
+ print(
+ '`ParseUser().signUp()` failed, because the email is not set. If you want to allow signUp without a set email, you should run `ParseUser().signUp(allowWithoutEmail = true)`');
+ return true;
+ }());
+ throw '`signUp` failed, because `emailAddress` of ParseUser was not provided and `allowWithoutEmail` was `false`';
+ } else {
+ assert(() {
+ print(
+ 'It is recommended to only allow user signUp with an email set.');
+ return true;
+ }());
+ }
+ }
+
+ final Uri url = getSanitisedUri(_client, path);
+ final String body = json.encode(toJson(forApiRQ: true));
+ _saveChanges();
+ final String? installationId = await _getInstallationId();
+ final ParseNetworkResponse response = await _client.post(url.toString(),
+ options: ParseNetworkOptions(headers: <String, String>{
+ keyHeaderRevocableSession: '1',
+ if (installationId != null && !doNotSendInstallationID)
+ keyHeaderInstallationId: installationId,
+ }),
+ data: body);
+
+ return await _handleResponse(
+ this, response, ParseApiRQ.signUp, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.signUp, _debug, parseClassName);
+ }
+}
+Send the updated object to the server.
+Will only send the dirty (modified) data and not the entire object
+The object should hold an objectId in order to update it
+ +@override
+Future<ParseResponse> update({dynamic context}) async {
+ if (objectId == null) {
+ return await signUp();
+ } else {
+ final ParseResponse response = await super.update();
+ if (response.success) {
+ await _onResponseSuccess();
+ }
+ return response;
+ }
+}
+String? get username => super.get<String>(keyVarUsername);
+set username(String? username) => set<String?>(keyVarUsername, username);
+Sends a verification email to the users email address
+Future<ParseResponse> verificationEmailRequest() async {
+ try {
+ final ParseNetworkResponse response = await _client.post(
+ '${ParseCoreData().serverUrl}$keyEndPointVerificationEmail',
+ data: json.encode(<String, dynamic>{keyVarEmail: emailAddress}),
+ );
+ return await _handleResponse(this, response,
+ ParseApiRQ.verificationEmailRequest, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(
+ e, ParseApiRQ.verificationEmailRequest, _debug, parseClassName);
+ }
+}
+typedef ParseUserConstructor = ParseUser Function(
+ String? username, String? password, String? emailAddress,
+ {String? sessionToken, bool? debug, ParseClient? client});
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ ParseWebFile(this.file,
+ {required String name,
+ String? url,
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId})
+ : super(
+ name: name,
+ url: url,
+ debug: debug,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ );
+Cancels the current request (upload or download of file).
+@override
+void cancel([dynamic reason]) {
+ _cancelToken?.cancel(reason);
+ _cancelToken = null;
+}
+@override
+Future<ParseWebFile> download({ProgressCallback? progressCallback}) async {
+ if (url == null) {
+ return this;
+ }
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+
+ final ParseNetworkByteResponse response = await _client.getBytes(
+ url!,
+ onReceiveProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ file = response.bytes as Uint8List?;
+
+ return this;
+}
+Uint8List? file;
+Add Progress Callback
+@override
+void progressCallback(ProgressCallback progressCallback) {
+ _progressCallback = progressCallback;
+}
+Uploads a file to Parse Server
+@override
+Future<ParseResponse> upload({ProgressCallback? progressCallback}) async {
+ if (saved) {
+ //Creates a Fake Response to return the correct result
+ final Map<String, String> response = <String, String>{
+ 'url': url!,
+ 'name': name
+ };
+ return handleResponse<ParseWebFile>(
+ this,
+ ParseNetworkResponse(data: json.encode(response), statusCode: 201),
+ ParseApiRQ.upload,
+ _debug,
+ parseClassName);
+ }
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+
+ final Map<String, String> headers = <String, String>{
+ HttpHeaders.contentTypeHeader:
+ lookupMimeType(url ?? name) ?? 'application/octet-stream',
+ };
+ try {
+ final String uri = ParseCoreData().serverUrl + _path;
+ final ParseNetworkResponse response = await _client.postBytes(
+ uri,
+ options: ParseNetworkOptions(headers: headers),
+ data: Stream<List<int>>.fromIterable(<List<int>>[file!]),
+ onSendProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ if (response.statusCode == 201) {
+ final Map<String, dynamic> map = json.decode(response.data);
+ url = map['url'].toString();
+ name = map['name'].toString();
+ }
+ return handleResponse<ParseWebFile>(
+ this, response, ParseApiRQ.upload, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.upload, _debug, parseClassName);
+ }
+}
+objets
to a relation associated with a given key
+ T
associated with a given key
+ objectId
+ key
+ objets
from a relation associated with a given key
+ ParseACL
governing this object.
+ element
to the end of the array associated with a given key
+ elements
to the end of the array
+associated with a given key
+ elements
to the array associated with a given key
, only
+adding elements which are not already present in the array. The position
+of the insert is not guaranteed
+ element
to the array associated with a given key
, only if
+it is not already present in the array. The position of the insert is not
+guaranteed
+ key
by the given amount
+ key
by the given amount
+ element
from an array
+associated with a given key
+ elements
contained in a List from the
+array associated with a given key
+ String
in JSON format
+ Creates a new file base XFile
+{https://docs.parseplatform.org/rest/guide/#files/}
+ParseXFile(this.file,
+ {String? name,
+ String? url,
+ bool? debug,
+ ParseClient? client,
+ bool? autoSendSessionId})
+ : super(
+ name: file != null ? path.basename(file.path) : name!,
+ url: url,
+ debug: debug,
+ client: client,
+ autoSendSessionId: autoSendSessionId,
+ );
+Cancels the current request (upload or download of file).
+@override
+void cancel([dynamic reason]) {
+ _cancelToken?.cancel(reason);
+ _cancelToken = null;
+}
+@override
+Future<ParseXFile> download({ProgressCallback? progressCallback}) async {
+ if (url == null) {
+ return this;
+ }
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+
+ if (parseIsWeb) {
+ final ParseNetworkByteResponse response = await _client.getBytes(
+ url!,
+ onReceiveProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+
+ if (response.bytes != null) {
+ file = XFile.fromData(response.bytes as Uint8List);
+ }
+ } else {
+ file = XFile('${ParseCoreData().fileDirectory}/$name');
+ await File(file!.path).create();
+
+ final ParseNetworkByteResponse response = await _client.getBytes(
+ url!,
+ onReceiveProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ await File(file!.path).writeAsBytes(response.bytes!);
+ }
+
+ return this;
+}
+XFile? file;
+Future<ParseXFile> loadStorage() async {
+ // Web not need load storage.
+ if (parseIsWeb) {
+ return this;
+ }
+
+ final XFile possibleFile = XFile('${ParseCoreData().fileDirectory}/$name');
+ // ignore: avoid_slow_async_io
+ final bool exists = await File(possibleFile.path).exists();
+
+ if (exists) {
+ file = possibleFile;
+ } else {
+ file = null;
+ }
+
+ return this;
+}
+Add Progress Callback
+@override
+void progressCallback(ProgressCallback progressCallback) {
+ _progressCallback = progressCallback;
+}
+Uploads a file to Parse Server
+@override
+Future<ParseResponse> upload({ProgressCallback? progressCallback}) async {
+ if (saved) {
+ //Creates a Fake Response to return the correct result
+ final Map<String, String> response = <String, String>{
+ 'url': url!,
+ 'name': name
+ };
+ return handleResponse<ParseXFile>(
+ this,
+ ParseNetworkResponse(data: json.encode(response), statusCode: 201),
+ ParseApiRQ.upload,
+ _debug,
+ parseClassName);
+ }
+
+ progressCallback ??= _progressCallback;
+
+ _cancelToken = CancelToken();
+ Map<String, String> headers;
+ if (parseIsWeb) {
+ headers = <String, String>{
+ HttpHeaders.contentTypeHeader: file?.mimeType ??
+ lookupMimeType(url ?? file?.name ?? name,
+ headerBytes: await file?.readAsBytes()) ??
+ 'application/octet-stream',
+ };
+ } else {
+ headers = <String, String>{
+ HttpHeaders.contentTypeHeader: file?.mimeType ??
+ lookupMimeType(file!.path) ??
+ 'application/octet-stream',
+ HttpHeaders.contentLengthHeader: '${await file!.length()}',
+ };
+ }
+
+ try {
+ final String uri = ParseCoreData().serverUrl + _path;
+
+ Stream<List<int>>? data;
+ if (parseIsWeb) {
+ data = Stream<List<int>>.fromIterable(
+ <List<int>>[await file!.readAsBytes()]);
+ } else {
+ data = file!.openRead();
+ }
+
+ final ParseNetworkResponse response = await _client.postBytes(
+ uri,
+ options: ParseNetworkOptions(headers: headers),
+ data: data,
+ onSendProgress: progressCallback,
+ cancelToken: _cancelToken,
+ );
+ if (response.statusCode == 201) {
+ final Map<String, dynamic> map = json.decode(response.data);
+ url = map['url'].toString();
+ name = map['name'].toString();
+ }
+ return handleResponse<ParseXFile>(
+ this, response, ParseApiRQ.upload, _debug, parseClassName);
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.upload, _debug, parseClassName);
+ }
+}
+PathKey(this.key, {this.subscription});
+final String key;
+Subscription<ParseObject>? subscription;
+A string representation of this object.
+Some classes have a default textual representation,
+often paired with a static parse
function (like int.parse).
+These classes will provide the textual representation as
+their string representation.
Other classes have no meaningful textual representation
+that a program will care about.
+Such classes will typically override toString
to provide
+useful information when inspecting the object,
+mainly for debugging or logging.
@override
+String toString() {
+ return 'PathKey(key: $key, subscription: ${subscription?.requestId})';
+}
+Callback to listen the progress for sending/receiving data.
+count
is the length of the bytes have been sent/received.
total
is the content length of the response/request body.
+1.When receiving data:
+ total
is the request body length.
+2.When receiving data:
+ total
will be -1 if the size of the response body is not known in advance,
+ for example: response data is compressed with gzip or no content-length header.
typedef ProgressCallback = void Function(int count, int total);
+Class to create complex queries
+substring
+
+
+column
+ends with prefix
+
+
+column
's value
+to be equal to the provided value
+
+
+column
's value
+to be not equal to the provided value
+
+
+column
+starts with prefix
+
+
+QueryBuilder.and(this.object, List<QueryBuilder<T>> list) {
+ _constructorInitializer(query: '"\$and":[', list: list);
+}
+factory QueryBuilder.copy(QueryBuilder<T> query) {
+ QueryBuilder<T> copy = QueryBuilder(query.object);
+ copy.queries = query.queries
+ .map((MapEntry<String, dynamic> entry) =>
+ MapEntry<String, dynamic>(entry.key, entry.value.toString()))
+ .toList();
+ query.limiters.forEach((String key, dynamic value) =>
+ copy.limiters.putIfAbsent(key, () => value.toString()));
+ return copy;
+}
+Class to create complex queries
+QueryBuilder(this.object) : super();
+factory QueryBuilder.name(String classname) {
+ return QueryBuilder(ParseCoreData.instance.createObject(classname) as T);
+}
+QueryBuilder.nor(this.object, List<QueryBuilder<T>> list) {
+ _constructorInitializer(query: '"\$nor":[', list: list);
+}
+QueryBuilder.or(this.object, List<QueryBuilder<T>> list) {
+ _constructorInitializer(query: '"\$or":[', list: list);
+}
+Runs through all queries and adds them to a query string
+String buildQueries(List<MapEntry<String, dynamic>> queries) {
+ String queryBuilder = '';
+
+ for (final MapEntry<String, dynamic> item in queries) {
+ if (item == queries.first) {
+ queryBuilder += item.value;
+ } else {
+ queryBuilder += ',${item.value}';
+ }
+ }
+
+ return queryBuilder;
+}
+Builds the query for Parse
+String buildQuery() {
+ queries = _checkForMultipleColumnInstances(queries);
+ return 'where={${buildQueries(queries)}}${getLimiters(limiters)}${getExtraOptions(extraOptions)}';
+}
+Counts the number of objects that match this query
+Future<ParseResponse> count() async {
+ return object.query(_buildQueryCount());
+}
+Future<ParseResponse> distinct<U extends ParseObject>(
+ String className) async {
+ final String queryString = 'distinct=$className';
+ return object.distinct<U>(queryString);
+}
+Returns an object where the String column doesn't select
+void dontSelectKeys(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$dontSelect'));
+}
+Exclude specific fields from the returned query
+String keys not will return the columns of a result you want the data for
+void excludeKeys(List<String> keys) {
+ limiters['excludeKeys'] = concatenateArray(keys);
+}
+Find the objects that satisfy the query. +Returns an empty list if no objects are found.
+Future<List<T>> find() async {
+ ParseResponse parseResponse = await query();
+ if (parseResponse.success) {
+ return parseResponse.results?.map((e) => e as T).toList() ?? <T>[];
+ }
+ throw parseResponse.error ?? ParseError();
+}
+Find the first object that satisfies the query. +Returns null, if no object is found.
+Future<T?> first() async {
+ ParseResponse parseResponse =
+ await (QueryBuilder.copy(this)..setLimit(1)).query();
+ if (parseResponse.success) {
+ return parseResponse.results?.first;
+ }
+ throw parseResponse.error ?? ParseError();
+}
+Adds extra options to the query
+String getExtraOptions(Map<String, dynamic> map) {
+ String result = '';
+ map.forEach((String key, dynamic value) {
+ result = '$result&$key=$value';
+ });
+ return result;
+}
+Adds the limiters to the query, i.e. skip=10, limit=10
+String getLimiters(Map<String, dynamic> map) {
+ String result = '';
+ map.forEach((String key, dynamic value) {
+ result = '$result&$key=$value';
+ });
+ return result;
+}
+Adds the limiters to the query relational, i.e. skip=10, limit=10
+String getLimitersRelational(Map<String, dynamic> map) {
+ String result = '';
+ map.forEach((String key, dynamic value) {
+ if (result.isNotEmpty) {
+ result = '$result,"$key":$value';
+ } else {
+ result = '"$key":$value';
+ }
+ });
+ return result;
+}
+Define which keys in an object to return.
+String keys will only return the columns of a result you want the data for, +this is useful for large objects
+void keysToReturn(List<String> keys) {
+ limiters['keys'] = concatenateArray(keys);
+}
+T object;
+Sorts the results in ascending order.
+String order will be the column of the table that the results are +ordered by
+void orderByAscending(String order) {
+ if (!limiters.containsKey('order')) {
+ limiters['order'] = order;
+ } else {
+ limiters['order'] = limiters['order'] + ',' + order;
+ }
+}
+Sorts the results descending order.
+String order will be the column of the table that the results are +ordered by
+void orderByDescending(String order) {
+ if (!limiters.containsKey('order')) {
+ limiters['order'] = '-$order';
+ } else {
+ limiters['order'] = limiters['order'] + ',' + '-$order';
+ }
+}
+Finishes the query and calls the server
+Make sure to call this after defining your queries
+Future<ParseResponse> query<U extends ParseObject>(
+ {ProgressCallback? progressCallback}) async {
+ return object.query<U>(
+ buildQuery(),
+ progressCallback: progressCallback,
+ );
+}
+Returns an object where the String column has a regEx performed on, +this can include ^StringsWith, or ^EndsWith. This can be manipulated to the users desire
+void regEx(String column, String value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$regex'));
+}
+Returns an object where the String column contains select
+void selectKeys(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$select'));
+}
+Adds a limit to amount of results return from Parse
+void setLimit(int limit) {
+ limiters['limit'] = limit;
+}
+Used by ParseRelation getQuery()
+void setRedirectClassNameForKey(String key) {
+ extraOptions['redirectClassNameForKey'] = key;
+}
+Returns an object where the String column contains all
+void whereArrayContainsAll(String column, List<dynamic> value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$all'));
+}
+Returns an object where the String column is containedIn
+void whereContainedIn(String column, List<dynamic> value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$in'));
+}
+Add a constraint for finding String values that contain the provided
+substring
void whereContains(
+ String column,
+ String substring, {
+ bool caseSensitive = false,
+}) {
+ substring = Uri.encodeComponent(substring);
+
+ if (caseSensitive) {
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$regex": "$substring"}'));
+ } else {
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$regex": "$substring", "\$options": "i"}'));
+ }
+}
+Powerful search for containing whole words. This search is much quicker +than regex and can search for whole words including whether they are case +sensitive or not. This search can also order by the score of the search
+void whereContainsWholeWord(
+ String column,
+ String searchTerm, {
+ bool caseSensitive = false,
+ bool orderByScore = true,
+ bool diacriticSensitive = false,
+}) {
+ searchTerm = Uri.encodeComponent(searchTerm);
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$text":{"\$search":{"\$term": "$searchTerm", "\$caseSensitive": $caseSensitive , "\$diacriticSensitive": $diacriticSensitive }}}'));
+ if (orderByScore) {
+ orderByAscending('\$score');
+ keysToReturn(['\$score']);
+ }
+}
+Add a constraint to the query that requires a particular key's value does not match any value for a key in the results of another ParseQuery
+void whereDoesNotMatchKeyInQuery<E extends ParseObject>(
+ String column, String keyInQuery, QueryBuilder<E> query) {
+ if (query.queries.isEmpty) {
+ throw ArgumentError('query conditions is required');
+ }
+ if (limiters.containsKey('order')) {
+ throw ArgumentError('order is not allowed');
+ }
+ if (limiters.containsKey('include')) {
+ throw ArgumentError('include is not allowed');
+ }
+
+ final String inQuery =
+ query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);
+
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$dontSelect":$inQuery}'));
+}
+Add a constraint to the query that requires a particular key's value does not match another QueryBuilder
+void whereDoesNotMatchQuery<E extends ParseObject>(
+ String column, QueryBuilder<E> query) {
+ final String inQuery =
+ query._buildQueryRelational(query.object.parseClassName);
+
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$notInQuery":$inQuery}'));
+}
+Add a constraint for finding objects where the String value in column
+ends with prefix
void whereEndsWith(
+ String column,
+ String prefix, {
+ bool caseSensitive = false,
+}) {
+ prefix = Uri.encodeComponent(prefix);
+
+ if (caseSensitive) {
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$regex": "$prefix\$"}'));
+ } else {
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$regex": "$prefix\$", "\$options": "i"}'));
+ }
+}
+Add a constraint to the query that requires a particular column
's value
+to be equal to the provided value
void whereEqualTo(String column, dynamic value) {
+ if (value is String) {
+ value = Uri.encodeComponent(value);
+ }
+
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), _noOperatorNeeded));
+}
+Creates a query based on where
+void whereEquals(String where) {
+ limiters['where'] = where;
+}
+Returns an object where the String column contains a value greater +than value
+void whereGreaterThan(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$gt'));
+}
+Returns an object where the String column contains a value greater +than equal to value
+void whereGreaterThanOrEqualsTo(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$gte'));
+}
+Returns an object where the String column contains a value less than +value
+void whereLessThan(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$lt'));
+}
+Returns an object where the String column contains a value less or equal +to than value
+void whereLessThanOrEqualTo(String column, dynamic value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$lte'));
+}
+Add a constraint to the query that requires a particular key's value matches a value for a key in the results of another ParseQuery.
+void whereMatchesKeyInQuery<E extends ParseObject>(
+ String column, String keyInQuery, QueryBuilder<E> query) {
+ if (query.queries.isEmpty) {
+ throw ArgumentError('query conditions is required');
+ }
+ if (limiters.containsKey('order')) {
+ throw ArgumentError('order is not allowed');
+ }
+ if (limiters.containsKey('include')) {
+ throw ArgumentError('include is not allowed');
+ }
+
+ final String inQuery =
+ query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);
+
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$select":$inQuery}'));
+}
+Add a constraint to the query that requires a particular key's value match another QueryBuilder
+void whereMatchesQuery<E extends ParseObject>(
+ String column, QueryBuilder<E> query) {
+ final String inQuery =
+ query._buildQueryRelational(query.object.parseClassName);
+
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$inQuery":$inQuery}'));
+}
+Returns an objects with key point values near the point given
+void whereNear(String column, ParseGeoPoint point) {
+ final double latitude = point.latitude;
+ final double longitude = point.longitude;
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$nearSphere":{"__type":"GeoPoint","latitude":$latitude,"longitude":$longitude}}'));
+}
+Returns an object where the String column is notContainedIn
+void whereNotContainedIn(String column, List<dynamic> value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$nin'));
+}
+Add a constraint to the query that requires a particular column
's value
+to be not equal to the provided value
void whereNotEqualTo(String column, dynamic value) {
+ if (value is String) {
+ value = Uri.encodeComponent(value);
+ }
+
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$ne'));
+}
+Add a constraint to the query that requires a particular key's coordinates that contains a point
+void wherePolygonContains(String column, ParseGeoPoint point) {
+ final double latitude = point.latitude;
+ final double longitude = point.longitude;
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$geoIntersects":{"\$point":{"__type":"GeoPoint","latitude":$latitude,"longitude":$longitude}}}'));
+}
+Retrieves related objets where String column is a relation field to the class String className
+void whereRelatedTo(String column, String className, String objectId) {
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"\$relatedTo":{"object":{"__type":"Pointer","className":"$className","objectId":"$objectId"},"key":"$column"}'));
+}
+Add a constraint for finding objects where the String value in column
+starts with prefix
void whereStartsWith(
+ String column,
+ String prefix, {
+ bool caseSensitive = false,
+}) {
+ prefix = Uri.encodeComponent(prefix);
+
+ if (caseSensitive) {
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$regex": "^$prefix"}'));
+ } else {
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$regex": "^$prefix", "\$options": "i"}'));
+ }
+}
+Returns an object where the String column for the object has data correctly entered/saved
+void whereValueExists(String column, bool value) {
+ queries.add(_buildQueryWithColumnValueAndOperator(
+ MapEntry<String, dynamic>(column, value), '\$exists'));
+}
+Returns an object with key point values contained within a given rectangular geographic bounding box.
+void whereWithinGeoBox(
+ String column, ParseGeoPoint southwest, ParseGeoPoint northeast) {
+ final double latitudeS = southwest.latitude;
+ final double longitudeS = southwest.longitude;
+
+ final double latitudeN = northeast.latitude;
+ final double longitudeN = northeast.longitude;
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$within":{"\$box": [{"__type": "GeoPoint","latitude":$latitudeS,"longitude":$longitudeS},{"__type": "GeoPoint","latitude":$latitudeN,"longitude":$longitudeN}]}}'));
+}
+Returns an object with key point values near the point given and within the maximum distance given.
+void whereWithinKilometers(
+ String column, ParseGeoPoint point, double maxDistance) {
+ final double latitude = point.latitude;
+ final double longitude = point.longitude;
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$nearSphere":{"__type":"GeoPoint","latitude":$latitude,"longitude":$longitude},"\$maxDistanceInKilometers":$maxDistance}'));
+}
+Returns an object with key point values near the point given and within the maximum distance given.
+void whereWithinMiles(
+ String column, ParseGeoPoint point, double maxDistance) {
+ final double latitude = point.latitude;
+ final double longitude = point.longitude;
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$nearSphere":{"__type":"GeoPoint","latitude":$latitude,"longitude":$longitude},"\$maxDistanceInMiles":$maxDistance}'));
+}
+Return an object with key coordinates be contained within and on the bounds of a given polygon. +Supports closed and open (last point is connected to first) paths +Polygon must have at least 3 points
+void whereWithinPolygon(String column, List<ParseGeoPoint> points) {
+ if (points.length < 3) {
+ throw ArgumentError('Polygon must have at least 3 points');
+ }
+ Map<String, dynamic> dictionary = <String, dynamic>{};
+ dictionary['\$polygon'] = points.map((e) => e.toJson()).toList();
+
+ queries.add(MapEntry<String, dynamic>(
+ _singleQuery, '"$column":{"\$geoWithin":${jsonEncode(dictionary)}}'));
+}
+Returns an object with key point values near the point given and within the maximum distance given.
+void whereWithinRadians(
+ String column, ParseGeoPoint point, double maxDistance) {
+ final double latitude = point.latitude;
+ final double longitude = point.longitude;
+
+ queries.add(MapEntry<String, dynamic>(_singleQuery,
+ '"$column":{"\$nearSphere":{"__type":"GeoPoint","latitude":$latitude,"longitude":$longitude},"\$maxDistanceInRadians":$maxDistance}'));
+}
+Subscription(this.query, this.requestId, {T? copyObject}) {
+ _copyObject = copyObject;
+}
+T? get copyObject {
+ return _copyObject;
+}
+void on(LiveQueryEvent op, Function callback) {
+ eventCallbacks[_liveQueryEvent[op.index]] = callback;
+}
+QueryBuilder<T> query;
+int requestId;
+Future<ParseResponse> batchRequest(
+ List<dynamic> requests, List<ParseObject> objects,
+ {ParseClient? client, bool? debug}) async {
+ debug = isDebugEnabled(objectLevelDebug: debug);
+ client = client ??
+ ParseCoreData().clientCreator(
+ sendSessionId: ParseCoreData().autoSendSessionId,
+ securityContext: ParseCoreData().securityContext);
+ try {
+ final Uri url = getSanitisedUri(client, '/batch');
+ final String body = json.encode(<String, dynamic>{'requests': requests});
+ final ParseNetworkResponse result =
+ await client.post(url.toString(), data: body);
+
+ return handleResponse<ParseObject>(
+ objects, result, ParseApiRQ.batch, debug, 'parse_utils');
+ } on Exception catch (e) {
+ return handleException(e, ParseApiRQ.batch, debug, 'parse_utils');
+ }
+}
+Handles any errors returned in response
+ParseResponse buildErrorResponse(
+ ParseResponse response, ParseNetworkResponse apiResponse) {
+ final Map<String, dynamic> responseData = json.decode(apiResponse.data);
+
+ response.error = ParseError(
+ code: responseData[keyCode] ?? ParseError.otherCause,
+ message: responseData[keyError].toString(),
+ );
+
+ response.statusCode = responseData[keyCode] ?? ParseError.otherCause;
+
+ return response;
+}
+Handles exception instead of throwing an exception
+ParseResponse buildParseResponseWithException(Exception exception) {
+ if (exception is DioException) {
+ Map<String, dynamic> errorResponse = {};
+ try {
+ errorResponse = json.decode(exception.response?.data?.toString() ?? '{}');
+ } on FormatException catch (_) {}
+
+ final errorMessage =
+ errorResponse['error']?.toString() ?? exception.response?.statusMessage;
+
+ final errorCode =
+ int.tryParse(errorResponse['code']) ?? exception.response?.statusCode;
+
+ return ParseResponse(
+ error: ParseError(
+ message: errorMessage ?? exception.toString(),
+ exception: exception,
+ code: errorCode ?? ParseError.otherCause,
+ ));
+ }
+
+ return ParseResponse(
+ error: ParseError(message: exception.toString(), exception: exception));
+}
+Handles successful responses with no results
+ParseResponse buildSuccessResponseWithNoResults(
+ ParseResponse response, int code, String value) {
+ response.success = true;
+ response.statusCode = 200;
+ response.error = ParseError(code: code, message: value);
+ return response;
+}
+Future<void> checkForSubmitEventually() async {
+ if (_inSubmitEventually) return;
+
+ if (Parse.objectsExistForEventually) {
+ _inSubmitEventually = true;
+ await ParseObject.submitEventually();
+ _inSubmitEventually = false;
+ }
+}
+Future<bool> checkObjectsExistForEventually() async {
+ // preparation ParseCoreData
+ final CoreStore coreStore = ParseCoreData().getStore();
+
+ List<String>? listSaves = await coreStore.getStringList(keyParseStoreObjects);
+
+ if (listSaves != null) {
+ if (listSaves.isNotEmpty) {
+ return true;
+ }
+ }
+
+ List<String>? listDeletes =
+ await coreStore.getStringList(keyParseStoreDeletes);
+
+ if (listDeletes != null) {
+ if (listDeletes.isNotEmpty) {
+ return true;
+ }
+ }
+
+ return false;
+}
+Convert list of strings to a string with commas
+String concatenateArray(List<String> list) {
+ String output = '';
+
+ for (final String item in list) {
+ if (item == list.first) {
+ output += item;
+ } else {
+ output += ',$item';
+ }
+ }
+
+ return output;
+}
+Converts the object to the correct value for JSON,
+Strings are wrapped with "" but integers and others are not
+dynamic convertValueToCorrectType(dynamic value) {
+ /*if (value is String && !value.contains('__type')) {
+ return '\"$value\"';
+ }*/
+
+ if (value is DateTime || value is ParseObject) {
+ return parseEncode(value);
+ } else {
+ return value;
+ }
+}
+Custom encoder for DateTime
+dynamic dateTimeEncoder(dynamic item) {
+ if (item is DateTime) {
+ return _parseDateFormat.format(item);
+ }
+ return item;
+}
+Map<String, String> encodeObject(String className, String objectId) {
+ return <String, String>{
+ '__type': 'Pointer',
+ keyVarClassName: className,
+ keyVarObjectId: objectId
+ };
+}
+ParseResult
to inform the user of the exception
+Sanitises a url
+Uri getCustomUri(ParseClient client, String path,
+ {Map<String, dynamic>? queryParams, String? query}) {
+ final Uri tempUri = Uri.parse(ParseCoreData().serverUrl);
+
+ final Uri url = Uri(
+ scheme: tempUri.scheme,
+ host: tempUri.host,
+ port: tempUri.port,
+ path: path,
+ queryParameters: queryParams,
+ query: query);
+
+ return url;
+}
+Sanitises a url
+Uri getSanitisedUri(ParseClient client, String pathToAppend,
+ {Map<String, dynamic>? queryParams, String? query}) {
+ final Uri tempUri = Uri.parse(ParseCoreData().serverUrl);
+
+ final Uri url = Uri(
+ scheme: tempUri.scheme,
+ host: tempUri.host,
+ port: tempUri.port,
+ path: '${tempUri.path}$pathToAppend',
+ queryParameters: queryParams,
+ query: query);
+
+ return url;
+}
+Create a codec to use when opening an encrypted sembast database
+The usage is then
+// Initialize the encryption codec with a user password
+var codec = getXXTeaSembastCodec(password: '[your_user_password]');
+// Open the database with the codec
+Database db = await factory.openDatabase(dbPath, codec: codec);
+
+// ...your database is ready to use as encrypted
+
+SembastCodec getXXTeaSembastCodec({required String password}) =>
+ SembastCodec(signature: 'xxtea', codec: _XXTeaCodec(password));
+Handles an API response and logs data if bool debug is enabled
+@protected
+ParseResponse handleException(
+ Exception exception, ParseApiRQ type, bool debug, String className) {
+ final ParseResponse parseResponse =
+ buildParseResponseWithException(exception);
+
+ if (debug) {
+ logAPIResponse(className, type.toString(), parseResponse);
+ }
+
+ return parseResponse;
+}
+Handles an API response and logs data if bool debug is enabled
+@protected
+ParseResponse handleResponse<T>(dynamic object, ParseNetworkResponse response,
+ ParseApiRQ type, bool debug, String className) {
+ final ParseResponse parseResponse =
+ _ParseResponseBuilder().handleResponse<T>(object, response, type);
+
+ if (debug) {
+ logAPIResponse(className, type.toString(), parseResponse);
+ }
+
+ return parseResponse;
+}
+Checks whether debug is enabled
+Debug can be set in 2 places, one global param in the Parse.initialize, and +then can be overwritten class by class
+bool isDebugEnabled({bool? objectLevelDebug}) {
+ return objectLevelDebug ?? ParseCoreData().debug;
+}
+bool isSuccessButNoResults(ParseNetworkResponse apiResponse) {
+ final dynamic decodedResponse = jsonDecode(apiResponse.data);
+ List<dynamic>? results;
+ if (decodedResponse is Map<String, dynamic>) {
+ results = decodedResponse['results'];
+ } else if (decodedResponse is List<dynamic>) {
+ results = decodedResponse;
+ }
+
+ if (results == null) {
+ return false;
+ }
+
+ return results.isEmpty;
+}
+bool isUnsuccessfulResponse(ParseNetworkResponse apiResponse) =>
+ apiResponse.statusCode != 200 && apiResponse.statusCode != 201;
+const String keyAppIdentifier = 'appIdentifier';
+const String keyAppName = 'appName';
+const String keyAppVersion = 'appVersion';
+const String keyClassInstallation = '_Installation';
+const String keyClassMain = 'ParseMain';
+const String keyClassSession = '_Session';
+const String keyClassUser = '_User';
+const String keyCode = 'code';
+const String keyDeviceToken = 'deviceToken';
+const String keyDeviceType = 'deviceType';
+const String keyEndPointAggregate = '/aggregate/';
+const String keyEndPointClasses = '/classes/';
+const String keyEndPointHealth = '/health';
+const String keyEndPointInstallations = '/installations';
+const String keyEndPointLogin = '/login';
+const String keyEndPointLogout = '/logout';
+const String keyEndPointRequestPasswordReset = '/requestPasswordReset';
+const String keyEndPointSessions = '/sessions';
+const String keyEndPointUserName = '/users/me';
+const String keyEndPointUsers = '/users';
+const String keyEndPointVerificationEmail = '/verificationEmailRequest';
+const String keyError = 'error';
+const String keyFile = 'File';
+const String keyFileClassname = 'ParseFile';
+const String keyGeoPoint = 'GeoPoint';
+const String keyHeaderApplicationId = 'X-Parse-Application-Id';
+const String keyHeaderClientKey = 'X-Parse-Client-Key';
+const String keyHeaderCloudContext = 'X-Parse-Cloud-Context';
+const String keyHeaderContentType = 'content-type';
+const String keyHeaderContentTypeJson = 'application/json';
+const String keyHeaderInstallationId = 'X-Parse-Installation-Id';
+const String keyHeaderMasterKey = 'X-Parse-Master-Key';
+const String keyHeaderRevocableSession = 'X-Parse-Revocable-Session';
+const String keyHeaderSessionToken = 'X-Parse-Session-Token';
+const String keyHeaderUserAgent = 'user-agent';
+const String keyInstallationId = 'installationId';
+const String keyLatitude = 'latitude';
+const String keyLibraryName = 'Flutter Parse SDK';
+const String keyLocaleIdentifier = 'localeIdentifier';
+const String keyLongitude = 'longitude';
+const String keyNetworkError = 'NetworkError';
+const String keyParamSessionToken = 'sessionToken';
+const String keyParseStoreBase = 'flutter_parse_sdk_';
+const String keyParseStoreDeletes = '${keyParseStoreBase}deletes';
+const String keyParseStoreInstallation = '${keyParseStoreBase}installation';
+const String keyParseStoreObjects = '${keyParseStoreBase}objects';
+const String keyParseStoreUser = '${keyParseStoreBase}user';
+const String keyParseVersion = 'parseVersion';
+const String keyRelation = 'Relation';
+const String keySdkVersion = '6.4.0';
+const String keyTimeZone = 'timeZone';
+const String keyVarAcl = 'ACL';
+const String keyVarAuthData = 'authData';
+const String keyVarClassName = 'className';
+const String keyVarCreatedAt = 'createdAt';
+const String keyVarCreatedWith = 'createdWith';
+const String keyVarEmail = 'email';
+const String keyVarExpiresAt = 'expiresAt';
+const String keyVarInstallationId = 'installationId';
+const String keyVarName = 'name';
+const String keyVarObjectId = 'objectId';
+const String keyVarPassword = 'password';
+const String keyVarRestricted = 'restricted';
+const String keyVarSessionToken = 'sessionToken';
+const String keyVarURL = 'url';
+const String keyVarUpdatedAt = 'updatedAt';
+const String keyVarUser = 'user';
+const String keyVarUsername = 'username';
+void logAPIResponse(
+ String className, String type, ParseResponse parseResponse) {
+ const String spacer = ' \n';
+ String responseString = '';
+
+ responseString += '╭-- Parse Response';
+ responseString += '\nClass: $className';
+ responseString += '\nFunction: $type';
+
+ if (parseResponse.success) {
+ responseString += '\nStatus Code: ${parseResponse.statusCode}';
+ if (parseResponse.result != null) {
+ responseString += '\nPayload: ${parseResponse.result.toString()}';
+ } else {
+ responseString += '\nReponse: OK';
+ }
+ } else if (!parseResponse.success) {
+ responseString += '\nStatus Code: ${parseResponse.error!.code}';
+ responseString += '\nType: ${parseResponse.error!.type}';
+
+ final String errorOrException =
+ parseResponse.error!.exception != null ? 'Exception' : 'Error';
+
+ responseString += '\n$errorOrException: ${parseResponse.error!.message}';
+ }
+
+ responseString += '\n╰-- \n';
+ responseString += spacer;
+ print(responseString);
+}
+void logRequest(
+ String? appName, String className, String type, String uri, String body) {
+ String requestString = ' \n';
+ final String name = appName != null ? '$appName ' : '';
+ requestString += '----\n${name}API Request ($className : $type) :';
+ requestString += '\nUri: $uri';
+ requestString += '\nBody: $body';
+
+ requestString += '\n----\n';
+ print(requestString);
+}
+Decode any type value
+dynamic parseDecode(dynamic value) {
+ if (value is List) {
+ return _convertJSONArrayToList(value);
+ }
+
+ if (value is bool) {
+ return value;
+ }
+
+ if (value is int) {
+ return value.toInt();
+ }
+
+ if (value is double) {
+ return value.toDouble();
+ }
+
+ if (value is num) {
+ return value;
+ }
+
+ if (value is! Map) {
+ return value;
+ }
+
+ final Map<String, dynamic> map = value as Map<String, dynamic>;
+
+ if (!map.containsKey('__type') && !map.containsKey('className')) {
+ return _convertJSONObjectToMap(map);
+ }
+
+ /// Decoding from Api Response
+ if (map.containsKey('__type')) {
+ switch (map['__type']) {
+ case 'Date':
+ final String iso = map['iso'];
+ return _parseDateFormat.parse(iso);
+ case 'Bytes':
+ final String val = map['base64'];
+ return base64.decode(val);
+ case 'Pointer':
+ case 'Object':
+ final String className = map['className'];
+ return ParseCoreData.instance.createObject(className).fromJson(map);
+ case 'File':
+ return ParseCoreData.instance
+ .createFile(url: map['url'], name: map['name'])
+ .fromJson(map);
+ case 'GeoPoint':
+ final num latitude = map['latitude'] ?? 0.0;
+ final num longitude = map['longitude'] ?? 0.0;
+ return ParseGeoPoint(
+ latitude: latitude.toDouble(), longitude: longitude.toDouble());
+ case 'Relation':
+ return ParseRelation.fromJson(map);
+ }
+ }
+
+ /// Decoding from locally cached JSON
+ if (map.containsKey('className')) {
+ switch (map['className']) {
+ case 'GeoPoint':
+ final num latitude = map['latitude'] ?? 0.0;
+ final num longitude = map['longitude'] ?? 0.0;
+ return ParseGeoPoint(
+ latitude: latitude.toDouble(),
+ longitude: longitude.toDouble(),
+ );
+
+ case 'ParseArray':
+ return _ParseArray.fromFullJson(map);
+
+ case 'ParseNumber':
+ return _ParseNumber.fromFullJson(map);
+
+ case 'ParseRelation':
+ return _ParseRelation.fromFullJson(map);
+
+ default:
+ return ParseCoreData.instance
+ .createObject(map['className'])
+ .fromJson(map);
+ }
+ }
+
+ return null;
+}
+Custom json encoder for types related to parse
+dynamic parseEncode(dynamic value, {bool full = false}) {
+ if (value is Uint8List) {
+ return _encodeUint8List(value);
+ }
+
+ if (value is DateTime) {
+ return _encodeDate(value);
+ }
+
+ if (value is Iterable) {
+ return value.map<dynamic>((dynamic value) {
+ return parseEncode(value, full: full);
+ }).toList();
+ }
+
+ if (value is _ParseArray) {
+ return value.toJson(full: full);
+ }
+
+ if (value is _ParseNumber) {
+ return value.toJson(full: full);
+ }
+
+ if (value is _ParseOperation) {
+ return value.toJson(full: full);
+ }
+
+ if (value is Map) {
+ value.forEach((dynamic k, dynamic v) {
+ value[k] = parseEncode(v, full: full);
+ });
+ }
+
+ if (value is ParseGeoPoint) {
+ return value.toJson(full: full);
+ }
+
+ if (value is ParseFileBase) {
+ return value.toJson(full: full);
+ }
+
+ if (value is ParseRelation) {
+ return value.toJson(full: full);
+ }
+
+ if (value is ParseObject) {
+ if (full) {
+ return value.toJson(full: full);
+ } else {
+ return value.toPointer();
+ }
+ }
+
+ if (value is ParseACL) {
+ return value.toJson();
+ }
+
+ return value;
+}
+const bool parseIsWeb = identical(0, 0.0);
+List removeDuplicateParseObjectByObjectId(Iterable iterable) {
+ final list = iterable.toList();
+
+ final foldedGroupedByObjectId = list
+ .whereType<ParseObject>()
+ .where((e) => e.objectId != null)
+ .groupFoldBy(
+ (e) => e.objectId!,
+ (previous, element) => element,
+ );
+
+ list.removeWhere(
+ (e) {
+ return e is ParseObject &&
+ foldedGroupedByObjectId.keys.contains(e.objectId);
+ },
+ );
+
+ list.addAll(foldedGroupedByObjectId.values);
+
+ return list;
+}
+Removes unncessary /
+String removeTrailingSlash(String serverUrl) {
+ if (serverUrl.isNotEmpty &&
+ serverUrl.substring(serverUrl.length - 1) == '/') {
+ return serverUrl.substring(0, serverUrl.length - 1);
+ } else {
+ return serverUrl;
+ }
+}
+bool shouldReturnAsABaseResult(ParseApiRQ type) {
+ if (type == ParseApiRQ.healthCheck ||
+ type == ParseApiRQ.execute ||
+ type == ParseApiRQ.add ||
+ type == ParseApiRQ.addAll ||
+ type == ParseApiRQ.addUnique ||
+ type == ParseApiRQ.remove ||
+ type == ParseApiRQ.removeAll ||
+ type == ParseApiRQ.increment ||
+ type == ParseApiRQ.decrement ||
+ type == ParseApiRQ.getConfigs ||
+ type == ParseApiRQ.addConfig) {
+ return true;
+ } else {
+ return false;
+ }
+}
+This library gives you access to the powerful Parse Server backend from your Dart app. For more information on Parse Platform and its features, visit parseplatform.org.
+The Parse Dart SDK is continuously tested with the most recent release of the Dart framework to ensure compatibility. To give developers time to upgrade their app to a newer Dart framework, previous Dart framework releases are supported for at least 1 year after the release date of the next higher significant version.
+Version | +Latest Version | +End of Support | +Compatible | +
---|---|---|---|
Dart 3.1 | +3.1.2 | +Sep 2024 | +✅ Yes | +
Dart 3.0 | +3.0.7 | +May 2024 | +✅ Yes | +
Dart 2.19 | +2.19.6 | +Mar 2024 | +✅ Yes | +
To install, add the Parse Dart SDK as a dependency in your pubspec.yaml
file.
Find the full documentation in the Parse Dart SDK guide.
+We want to make contributing to this project as easy and transparent as possible. Please refer to the Contribution Guidelines.
+r)return n}return parseInt(a,b)},
+fS(a){return A.ll(a)},
+ll(a){var s,r,q,p
+if(a instanceof A.t)return A.T(A.bD(a),null)
+s=J.bd(a)
+if(s===B.M||s===B.O||t.o.b(a)){r=B.p(a)
+if(r!=="Object"&&r!=="")return r
+q=a.constructor
+if(typeof q=="function"){p=q.name
+if(typeof p=="string"&&p!=="Object"&&p!=="")return p}}return A.T(A.bD(a),null)},
+jr(a){if(a==null||typeof a=="number"||A.i2(a))return J.aD(a)
+if(typeof a=="string")return JSON.stringify(a)
+if(a instanceof A.aG)return a.j(0)
+if(a instanceof A.ci)return a.bd(!0)
+return"Instance of '"+A.fS(a)+"'"},
+lm(a,b,c){var s,r,q,p
+if(c<=500&&b===0&&c===a.length)return String.fromCharCode.apply(null,a)
+for(s=b,r="";s =0},
+no(a){if(/[[\]{}()*+?.\\^$|]/.test(a))return a.replace(/[[\]{}()*+?.\\^$|]/g,"\\$&")
+return a},
+kb(a){return a},
+nr(a,b,c,d){var s,r,q,p=new A.h7(b,a,0),o=t.F,n=0,m=""
+for(;p.n();){s=p.d
+if(s==null)s=o.a(s)
+r=s.b
+q=r.index
+m=m+A.p(A.kb(B.a.m(a,n,q)))+A.p(c.$1(s))
+n=q+r[0].length}p=m+A.p(A.kb(B.a.M(a,n)))
+return p.charCodeAt(0)==0?p:p},
+eG:function eG(a,b){this.a=a
+this.b=b},
+bG:function bG(){},
+bH:function bH(a,b,c){this.a=a
+this.b=b
+this.$ti=c},
+fW:function fW(a,b,c,d,e,f){var _=this
+_.a=a
+_.b=b
+_.c=c
+_.d=d
+_.e=e
+_.f=f},
+c2:function c2(){},
+de:function de(a,b,c){this.a=a
+this.b=b
+this.c=c},
+dX:function dX(a){this.a=a},
+fR:function fR(a){this.a=a},
+bM:function bM(a,b){this.a=a
+this.b=b},
+cn:function cn(a){this.a=a
+this.b=null},
+aG:function aG(){},
+cT:function cT(){},
+cU:function cU(){},
+dP:function dP(){},
+dK:function dK(){},
+bh:function bh(a,b){this.a=a
+this.b=b},
+ea:function ea(a){this.a=a},
+dF:function dF(a){this.a=a},
+b2:function b2(a){var _=this
+_.a=0
+_.f=_.e=_.d=_.c=_.b=null
+_.r=0
+_.$ti=a},
+fG:function fG(a){this.a=a},
+fJ:function fJ(a,b){var _=this
+_.a=a
+_.b=b
+_.d=_.c=null},
+an:function an(a,b){this.a=a
+this.$ti=b},
+dg:function dg(a,b){var _=this
+_.a=a
+_.b=b
+_.d=_.c=null},
+ia:function ia(a){this.a=a},
+ib:function ib(a){this.a=a},
+ic:function ic(a){this.a=a},
+ci:function ci(){},
+eF:function eF(){},
+fE:function fE(a,b){var _=this
+_.a=a
+_.b=b
+_.d=_.c=null},
+eu:function eu(a){this.b=a},
+h7:function h7(a,b,c){var _=this
+_.a=a
+_.b=b
+_.c=c
+_.d=null},
+mn(a){return a},
+lk(a){return new Int8Array(a)},
+aA(a,b,c){if(a>>>0!==a||a>=c)throw A.b(A.iX(b,a))},
+mk(a,b,c){var s
+if(!(a>>>0!==a))s=b>>>0!==b||a>b||b>c
+else s=!0
+if(s)throw A.b(A.n1(a,b,c))
+return b},
+dn:function dn(){},
+bZ:function bZ(){},
+dp:function dp(){},
+bo:function bo(){},
+bX:function bX(){},
+bY:function bY(){},
+dq:function dq(){},
+dr:function dr(){},
+ds:function ds(){},
+dt:function dt(){},
+du:function du(){},
+dv:function dv(){},
+dw:function dw(){},
+c_:function c_(){},
+c0:function c0(){},
+ce:function ce(){},
+cf:function cf(){},
+cg:function cg(){},
+ch:function ch(){},
+jt(a,b){var s=b.c
+return s==null?b.c=A.iK(a,b.y,!0):s},
+iE(a,b){var s=b.c
+return s==null?b.c=A.cs(a,"aJ",[b.y]):s},
+lp(a){var s=a.d
+if(s!=null)return s
+return a.d=new Map()},
+ju(a){var s=a.x
+if(s===6||s===7||s===8)return A.ju(a.y)
+return s===12||s===13},
+lo(a){return a.at},
+ff(a){return A.f0(v.typeUniverse,a,!1)},
+aS(a,b,a0,a1){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c=b.x
+switch(c){case 5:case 1:case 2:case 3:case 4:return b
+case 6:s=b.y
+r=A.aS(a,s,a0,a1)
+if(r===s)return b
+return A.jP(a,r,!0)
+case 7:s=b.y
+r=A.aS(a,s,a0,a1)
+if(r===s)return b
+return A.iK(a,r,!0)
+case 8:s=b.y
+r=A.aS(a,s,a0,a1)
+if(r===s)return b
+return A.jO(a,r,!0)
+case 9:q=b.z
+p=A.cB(a,q,a0,a1)
+if(p===q)return b
+return A.cs(a,b.y,p)
+case 10:o=b.y
+n=A.aS(a,o,a0,a1)
+m=b.z
+l=A.cB(a,m,a0,a1)
+if(n===o&&l===m)return b
+return A.iI(a,n,l)
+case 12:k=b.y
+j=A.aS(a,k,a0,a1)
+i=b.z
+h=A.mQ(a,i,a0,a1)
+if(j===k&&h===i)return b
+return A.jN(a,j,h)
+case 13:g=b.z
+a1+=g.length
+f=A.cB(a,g,a0,a1)
+o=b.y
+n=A.aS(a,o,a0,a1)
+if(f===g&&n===o)return b
+return A.iJ(a,n,f,!0)
+case 14:e=b.y
+if(e =0)p+=" "+r[q];++q}return p+"})"},
+k2(a3,a4,a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2=", "
+if(a5!=null){s=a5.length
+if(a4==null){a4=A.n([],t.s)
+r=null}else r=a4.length
+q=a4.length
+for(p=s;p>0;--p)a4.push("T"+(q+p))
+for(o=t.X,n=t._,m="<",l="",p=0;p >>0!==0?255:q}return o},
+eq:function eq(a,b){this.a=a
+this.b=b
+this.c=null},
+er:function er(a){this.a=a},
+h5:function h5(){},
+h4:function h4(){},
+fl:function fl(){},
+fm:function fm(){},
+cW:function cW(){},
+cY:function cY(){},
+fr:function fr(){},
+fx:function fx(){},
+fw:function fw(){},
+fH:function fH(){},
+fI:function fI(a){this.a=a},
+h2:function h2(){},
+h6:function h6(){},
+hP:function hP(a){this.b=0
+this.c=a},
+h3:function h3(a){this.a=a},
+hO:function hO(a){this.a=a
+this.b=16
+this.c=0},
+ik(a,b){var s=A.jq(a,b)
+if(s!=null)return s
+throw A.b(A.N(a,null,null))},
+l1(a,b){a=A.b(a)
+a.stack=b.j(0)
+throw a
+throw A.b("unreachable")},
+jm(a,b,c,d){var s,r=c?J.ld(a,d):J.lc(a,d)
+if(a!==0&&b!=null)for(s=0;s 9)k.$2("invalid character",s)}else{if(q===3)k.$2(m,s)
+o=A.ik(B.a.m(a,r,s),null)
+if(o>255)k.$2(l,r)
+n=q+1
+j[q]=o
+r=s+1
+q=n}}if(q!==3)k.$2(m,c)
+o=A.ik(B.a.m(a,r,c),null)
+if(o>255)k.$2(l,r)
+j[q]=o
+return j},
+jA(a,b,a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e=null,d=new A.h_(a),c=new A.h0(d,a)
+if(a.length<2)d.$2("address is too short",e)
+s=A.n([],t.t)
+for(r=b,q=r,p=!1,o=!1;r"}else{m=""
+r=null}o=a3.y
+h=a3.z
+g=h.a
+f=g.length
+e=h.b
+d=e.length
+c=h.c
+b=c.length
+a=A.T(o,a4)
+for(a0="",a1="",p=0;p0)p+="<"+A.cr(c)+">"
+s=a.eC.get(p)
+if(s!=null)return s
+r=new A.W(null,null)
+r.x=9
+r.y=b
+r.z=c
+if(c.length>0)r.c=c[0]
+r.at=p
+q=A.az(a,r)
+a.eC.set(p,q)
+return q},
+iI(a,b,c){var s,r,q,p,o,n
+if(b.x===10){s=b.y
+r=b.z.concat(c)}else{r=c
+s=b}q=s.at+(";<"+A.cr(r)+">")
+p=a.eC.get(q)
+if(p!=null)return p
+o=new A.W(null,null)
+o.x=10
+o.y=s
+o.z=r
+o.at=q
+n=A.az(a,o)
+a.eC.set(q,n)
+return n},
+lT(a,b,c){var s,r,q="+"+(b+"("+A.cr(c)+")"),p=a.eC.get(q)
+if(p!=null)return p
+s=new A.W(null,null)
+s.x=11
+s.y=b
+s.z=c
+s.at=q
+r=A.az(a,s)
+a.eC.set(q,r)
+return r},
+jN(a,b,c){var s,r,q,p,o,n=b.at,m=c.a,l=m.length,k=c.b,j=k.length,i=c.c,h=i.length,g="("+A.cr(m)
+if(j>0){s=l>0?",":""
+g+=s+"["+A.cr(k)+"]"}if(h>0){s=l>0?",":""
+g+=s+"{"+A.lN(i)+"}"}r=n+(g+")")
+q=a.eC.get(r)
+if(q!=null)return q
+p=new A.W(null,null)
+p.x=12
+p.y=b
+p.z=c
+p.at=r
+o=A.az(a,p)
+a.eC.set(r,o)
+return o},
+iJ(a,b,c,d){var s,r=b.at+("<"+A.cr(c)+">"),q=a.eC.get(r)
+if(q!=null)return q
+s=A.lP(a,b,c,r,d)
+a.eC.set(r,s)
+return s},
+lP(a,b,c,d,e){var s,r,q,p,o,n,m,l
+if(e){s=c.length
+r=A.hQ(s)
+for(q=0,p=0;p0){n=A.aS(a,b,r,0)
+m=A.cB(a,c,r,0)
+return A.iJ(a,n,m,c!==m)}}l=new A.W(null,null)
+l.x=13
+l.y=b
+l.z=c
+l.at=d
+return A.az(a,l)},
+jI(a,b,c,d){return{u:a,e:b,r:c,s:[],p:0,n:d}},
+jK(a){var s,r,q,p,o,n,m,l=a.r,k=a.s
+for(s=l.length,r=0;r=48&&q<=57)r=A.lG(r+1,q,l,k)
+else if((((q|32)>>>0)-97&65535)<26||q===95||q===36||q===124)r=A.jJ(a,r,l,k,!1)
+else if(q===46)r=A.jJ(a,r,l,k,!0)
+else{++r
+switch(q){case 44:break
+case 58:k.push(!1)
+break
+case 33:k.push(!0)
+break
+case 59:k.push(A.aR(a.u,a.e,k.pop()))
+break
+case 94:k.push(A.lS(a.u,k.pop()))
+break
+case 35:k.push(A.ct(a.u,5,"#"))
+break
+case 64:k.push(A.ct(a.u,2,"@"))
+break
+case 126:k.push(A.ct(a.u,3,"~"))
+break
+case 60:k.push(a.p)
+a.p=k.length
+break
+case 62:A.lI(a,k)
+break
+case 38:A.lH(a,k)
+break
+case 42:p=a.u
+k.push(A.jP(p,A.aR(p,a.e,k.pop()),a.n))
+break
+case 63:p=a.u
+k.push(A.iK(p,A.aR(p,a.e,k.pop()),a.n))
+break
+case 47:p=a.u
+k.push(A.jO(p,A.aR(p,a.e,k.pop()),a.n))
+break
+case 40:k.push(-3)
+k.push(a.p)
+a.p=k.length
+break
+case 41:A.lF(a,k)
+break
+case 91:k.push(a.p)
+a.p=k.length
+break
+case 93:o=k.splice(a.p)
+A.jL(a.u,a.e,o)
+a.p=k.pop()
+k.push(o)
+k.push(-1)
+break
+case 123:k.push(a.p)
+a.p=k.length
+break
+case 125:o=k.splice(a.p)
+A.lK(a.u,a.e,o)
+a.p=k.pop()
+k.push(o)
+k.push(-2)
+break
+case 43:n=l.indexOf("(",r)
+k.push(l.substring(r,n))
+k.push(-4)
+k.push(a.p)
+a.p=k.length
+r=n+1
+break
+default:throw"Bad character "+q}}}m=k.pop()
+return A.aR(a.u,a.e,m)},
+lG(a,b,c,d){var s,r,q=b-48
+for(s=c.length;a=48&&r<=57))break
+q=q*10+(r-48)}d.push(q)
+return a},
+jJ(a,b,c,d,e){var s,r,q,p,o,n,m=b+1
+for(s=c.length;m>>0)-97&65535)<26||r===95||r===36||r===124))q=r>=48&&r<=57
+else q=!0
+if(!q)break}}p=c.substring(b,m)
+if(e){s=a.u
+o=a.e
+if(o.x===10)o=o.y
+n=A.lX(s,o.y)[p]
+if(n==null)A.fh('No "'+p+'" in "'+A.lo(o)+'"')
+d.push(A.cu(s,o,n))}else d.push(p)
+return m},
+lI(a,b){var s,r=a.u,q=A.jH(a,b),p=b.pop()
+if(typeof p=="string")b.push(A.cs(r,p,q))
+else{s=A.aR(r,a.e,p)
+switch(s.x){case 12:b.push(A.iJ(r,s,q,a.n))
+break
+default:b.push(A.iI(r,s,q))
+break}}},
+lF(a,b){var s,r,q,p,o,n=null,m=a.u,l=b.pop()
+if(typeof l=="number")switch(l){case-1:s=b.pop()
+r=n
+break
+case-2:r=b.pop()
+s=n
+break
+default:b.push(l)
+r=n
+s=r
+break}else{b.push(l)
+r=n
+s=r}q=A.jH(a,b)
+l=b.pop()
+switch(l){case-3:l=b.pop()
+if(s==null)s=m.sEA
+if(r==null)r=m.sEA
+p=A.aR(m,a.e,l)
+o=new A.el()
+o.a=q
+o.b=s
+o.c=r
+b.push(A.jN(m,p,o))
+return
+case-4:b.push(A.lT(m,b.pop(),q))
+return
+default:throw A.b(A.cM("Unexpected state under `()`: "+A.p(l)))}},
+lH(a,b){var s=b.pop()
+if(0===s){b.push(A.ct(a.u,1,"0&"))
+return}if(1===s){b.push(A.ct(a.u,4,"1&"))
+return}throw A.b(A.cM("Unexpected extended operation "+A.p(s)))},
+jH(a,b){var s=b.splice(a.p)
+A.jL(a.u,a.e,s)
+a.p=b.pop()
+return s},
+aR(a,b,c){if(typeof c=="string")return A.cs(a,c,a.sEA)
+else if(typeof c=="number"){b.toString
+return A.lJ(a,b,c)}else return c},
+jL(a,b,c){var s,r=c.length
+for(s=0;s0?new Array(a):v.typeUniverse.sEA},
+W:function W(a,b){var _=this
+_.a=a
+_.b=b
+_.w=_.r=_.e=_.d=_.c=null
+_.x=0
+_.at=_.as=_.Q=_.z=_.y=null},
+el:function el(){this.c=this.b=this.a=null},
+hL:function hL(a){this.a=a},
+eh:function eh(){},
+cq:function cq(a){this.a=a},
+lw(){var s,r,q={}
+if(self.scheduleImmediate!=null)return A.mX()
+if(self.MutationObserver!=null&&self.document!=null){s=self.document.createElement("div")
+r=self.document.createElement("span")
+q.a=null
+new self.MutationObserver(A.bb(new A.h9(q),1)).observe(s,{childList:true})
+return new A.h8(q,s,r)}else if(self.setImmediate!=null)return A.mY()
+return A.mZ()},
+lx(a){self.scheduleImmediate(A.bb(new A.ha(a),0))},
+ly(a){self.setImmediate(A.bb(new A.hb(a),0))},
+lz(a){A.lL(0,a)},
+lL(a,b){var s=new A.hJ()
+s.bS(a,b)
+return s},
+mH(a){return new A.e4(new A.I($.C,a.k("I<0>")),a.k("e4<0>"))},
+mi(a,b){a.$2(0,null)
+b.b=!0
+return b.a},
+mf(a,b){A.mj(a,b)},
+mh(a,b){b.ak(0,a)},
+mg(a,b){b.am(A.ai(a),A.aU(a))},
+mj(a,b){var s,r,q=new A.hT(b),p=new A.hU(b)
+if(a instanceof A.I)a.bb(q,p,t.z)
+else{s=t.z
+if(a instanceof A.I)a.aX(q,p,s)
+else{r=new A.I($.C,t.aY)
+r.a=8
+r.c=a
+r.bb(q,p,s)}}},
+mU(a){var s=function(b,c){return function(d,e){while(true)try{b(d,e)
+break}catch(r){e=r
+d=c}}}(a,1)
+return $.C.by(new A.i6(s))},
+fj(a,b){var s=A.fe(a,"error",t.K)
+return new A.cN(s,b==null?A.j7(a):b)},
+j7(a){var s
+if(t.U.b(a)){s=a.gac()
+if(s!=null)return s}return B.J},
+jF(a,b){var s,r
+for(;s=a.a,(s&4)!==0;)a=a.c
+if((s&24)!==0){r=b.aK()
+b.ad(a)
+A.ca(b,r)}else{r=b.c
+b.b9(a)
+a.aJ(r)}},
+lB(a,b){var s,r,q={},p=q.a=a
+for(;s=p.a,(s&4)!==0;){p=p.c
+q.a=p}if((s&24)===0){r=b.c
+b.b9(p)
+q.a.aJ(r)
+return}if((s&16)===0&&b.c==null){b.ad(p)
+return}b.a^=2
+A.ba(null,null,b.b,new A.hk(q,b))},
+ca(a,b){var s,r,q,p,o,n,m,l,k,j,i,h,g={},f=g.a=a
+for(;!0;){s={}
+r=f.a
+q=(r&16)===0
+p=!q
+if(b==null){if(p&&(r&1)===0){f=f.c
+A.i3(f.a,f.b)}return}s.a=b
+o=b.a
+for(f=b;o!=null;f=o,o=n){f.a=null
+A.ca(g.a,f)
+s.a=o
+n=o.a}r=g.a
+m=r.c
+s.b=p
+s.c=m
+if(q){l=f.c
+l=(l&1)!==0||(l&15)===8}else l=!0
+if(l){k=f.b.b
+if(p){r=r.b===k
+r=!(r||r)}else r=!1
+if(r){A.i3(m.a,m.b)
+return}j=$.C
+if(j!==k)$.C=k
+else j=null
+f=f.c
+if((f&15)===8)new A.hr(s,g,p).$0()
+else if(q){if((f&1)!==0)new A.hq(s,m).$0()}else if((f&2)!==0)new A.hp(g,s).$0()
+if(j!=null)$.C=j
+f=s.c
+if(f instanceof A.I){r=s.a.$ti
+r=r.k("aJ<2>").b(f)||!r.z[1].b(f)}else r=!1
+if(r){i=s.a.b
+if((f.a&24)!==0){h=i.c
+i.c=null
+b=i.af(h)
+i.a=f.a&30|i.a&1
+i.c=f.c
+g.a=f
+continue}else A.jF(f,i)
+return}}i=s.a.b
+h=i.c
+i.c=null
+b=i.af(h)
+f=s.b
+r=s.c
+if(!f){i.a=8
+i.c=r}else{i.a=i.a&1|16
+i.c=r}g.a=i
+f=i}},
+mM(a,b){if(t.C.b(a))return b.by(a)
+if(t.w.b(a))return a
+throw A.b(A.is(a,"onError",u.c))},
+mJ(){var s,r
+for(s=$.bB;s!=null;s=$.bB){$.cA=null
+r=s.b
+$.bB=r
+if(r==null)$.cz=null
+s.a.$0()}},
+mP(){$.iT=!0
+try{A.mJ()}finally{$.cA=null
+$.iT=!1
+if($.bB!=null)$.j1().$1(A.kd())}},
+ka(a){var s=new A.e5(a),r=$.cz
+if(r==null){$.bB=$.cz=s
+if(!$.iT)$.j1().$1(A.kd())}else $.cz=r.b=s},
+mO(a){var s,r,q,p=$.bB
+if(p==null){A.ka(a)
+$.cA=$.cz
+return}s=new A.e5(a)
+r=$.cA
+if(r==null){s.b=p
+$.bB=$.cA=s}else{q=r.b
+s.b=q
+$.cA=r.b=s
+if(q==null)$.cz=s}},
+np(a){var s,r=null,q=$.C
+if(B.c===q){A.ba(r,r,B.c,a)
+return}s=!1
+if(s){A.ba(r,r,q,a)
+return}A.ba(r,r,q,q.bi(a))},
+o_(a){A.fe(a,"stream",t.K)
+return new A.eO()},
+i3(a,b){A.mO(new A.i4(a,b))},
+k6(a,b,c,d){var s,r=$.C
+if(r===c)return d.$0()
+$.C=c
+s=r
+try{r=d.$0()
+return r}finally{$.C=s}},
+k7(a,b,c,d,e){var s,r=$.C
+if(r===c)return d.$1(e)
+$.C=c
+s=r
+try{r=d.$1(e)
+return r}finally{$.C=s}},
+mN(a,b,c,d,e,f){var s,r=$.C
+if(r===c)return d.$2(e,f)
+$.C=c
+s=r
+try{r=d.$2(e,f)
+return r}finally{$.C=s}},
+ba(a,b,c,d){if(B.c!==c)d=c.bi(d)
+A.ka(d)},
+h9:function h9(a){this.a=a},
+h8:function h8(a,b,c){this.a=a
+this.b=b
+this.c=c},
+ha:function ha(a){this.a=a},
+hb:function hb(a){this.a=a},
+hJ:function hJ(){},
+hK:function hK(a,b){this.a=a
+this.b=b},
+e4:function e4(a,b){this.a=a
+this.b=!1
+this.$ti=b},
+hT:function hT(a){this.a=a},
+hU:function hU(a){this.a=a},
+i6:function i6(a){this.a=a},
+cN:function cN(a,b){this.a=a
+this.b=b},
+c7:function c7(){},
+b8:function b8(a,b){this.a=a
+this.$ti=b},
+bw:function bw(a,b,c,d,e){var _=this
+_.a=null
+_.b=a
+_.c=b
+_.d=c
+_.e=d
+_.$ti=e},
+I:function I(a,b){var _=this
+_.a=0
+_.b=a
+_.c=null
+_.$ti=b},
+hh:function hh(a,b){this.a=a
+this.b=b},
+ho:function ho(a,b){this.a=a
+this.b=b},
+hl:function hl(a){this.a=a},
+hm:function hm(a){this.a=a},
+hn:function hn(a,b,c){this.a=a
+this.b=b
+this.c=c},
+hk:function hk(a,b){this.a=a
+this.b=b},
+hj:function hj(a,b){this.a=a
+this.b=b},
+hi:function hi(a,b,c){this.a=a
+this.b=b
+this.c=c},
+hr:function hr(a,b,c){this.a=a
+this.b=b
+this.c=c},
+hs:function hs(a){this.a=a},
+hq:function hq(a,b){this.a=a
+this.b=b},
+hp:function hp(a,b){this.a=a
+this.b=b},
+e5:function e5(a){this.a=a
+this.b=null},
+eO:function eO(){},
+hS:function hS(){},
+i4:function i4(a,b){this.a=a
+this.b=b},
+hw:function hw(){},
+hx:function hx(a,b){this.a=a
+this.b=b},
+hy:function hy(a,b,c){this.a=a
+this.b=b
+this.c=c},
+jk(a,b,c){return A.n3(a,new A.b2(b.k("@<0>").G(c).k("b2<1,2>")))},
+dh(a,b){return new A.b2(a.k("@<0>").G(b).k("b2<1,2>"))},
+bT(a){return new A.cb(a.k("cb<0>"))},
+iG(){var s=Object.create(null)
+s["
>>4]&1<<(o&15))!==0)p+=A.ar(o)
+else p=d&&o===32?p+"+":p+"%"+n[o>>>4&15]+n[o&15]}return p.charCodeAt(0)==0?p:p},
+fs(a){if(typeof a=="number"||A.i2(a)||a==null)return J.aD(a)
+if(typeof a=="string")return JSON.stringify(a)
+return A.jr(a)},
+l2(a,b){A.fe(a,"error",t.K)
+A.fe(b,"stackTrace",t.l)
+A.l1(a,b)},
+cM(a){return new A.cL(a)},
+aE(a,b){return new A.Z(!1,null,b,a)},
+is(a,b,c){return new A.Z(!0,a,b,c)},
+ln(a,b){return new A.c3(null,null,!0,a,b,"Value not in range")},
+V(a,b,c,d,e){return new A.c3(b,c,!0,a,d,"Invalid value")},
+b3(a,b,c){if(0>a||a>c)throw A.b(A.V(a,0,c,"start",null))
+if(b!=null){if(a>b||b>c)throw A.b(A.V(b,a,c,"end",null))
+return b}return c},
+js(a,b){if(a<0)throw A.b(A.V(a,0,null,b,null))
+return a},
+E(a,b,c,d){return new A.db(b,!0,a,d,"Index out of range")},
+r(a){return new A.dZ(a)},
+jy(a){return new A.dW(a)},
+dJ(a){return new A.br(a)},
+aH(a){return new A.cX(a)},
+N(a,b,c){return new A.fv(a,b,c)},
+lb(a,b,c){var s,r
+if(A.iZ(a)){if(b==="("&&c===")")return"(...)"
+return b+"..."+c}s=A.n([],t.s)
+$.be.push(a)
+try{A.mG(a,s)}finally{$.be.pop()}r=A.jv(b,s,", ")+c
+return r.charCodeAt(0)==0?r:r},
+iw(a,b,c){var s,r
+if(A.iZ(a))return b+"..."+c
+s=new A.O(b)
+$.be.push(a)
+try{r=s
+r.a=A.jv(r.a,a,", ")}finally{$.be.pop()}s.a+=c
+r=s.a
+return r.charCodeAt(0)==0?r:r},
+mG(a,b){var s,r,q,p,o,n,m,l=a.gA(a),k=0,j=0
+while(!0){if(!(k<80||j<3))break
+if(!l.n())return
+s=A.p(l.gq(l))
+b.push(s)
+k+=s.length+2;++j}if(!l.n()){if(j<=5)return
+r=b.pop()
+q=b.pop()}else{p=l.gq(l);++j
+if(!l.n()){if(j<=4){b.push(A.p(p))
+return}r=A.p(p)
+q=b.pop()
+k+=r.length+2}else{o=l.gq(l);++j
+for(;l.n();p=o,o=n){n=l.gq(l);++j
+if(j>100){while(!0){if(!(k>75&&j>3))break
+k-=b.pop().length+2;--j}b.push("...")
+return}}q=A.p(p)
+r=A.p(o)
+k+=r.length+q.length+4}}if(j>b.length+2){k+=5
+m="..."}else m=null
+while(!0){if(!(k>80&&b.length>3))break
+k-=b.pop().length+2
+if(m==null){k+=5
+m="..."}}if(m!=null)b.push(m)
+b.push(q)
+b.push(r)},
+iB(a,b,c,d){var s
+if(B.k===c){s=B.e.gt(a)
+b=J.aj(b)
+return A.iF(A.aO(A.aO($.iq(),s),b))}if(B.k===d){s=B.e.gt(a)
+b=J.aj(b)
+c=J.aj(c)
+return A.iF(A.aO(A.aO(A.aO($.iq(),s),b),c))}s=B.e.gt(a)
+b=J.aj(b)
+c=J.aj(c)
+d=J.aj(d)
+d=A.iF(A.aO(A.aO(A.aO(A.aO($.iq(),s),b),c),d))
+return d},
+e0(a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3=null,a4=a5.length
+if(a4>=5){s=((a5.charCodeAt(4)^58)*3|a5.charCodeAt(0)^100|a5.charCodeAt(1)^97|a5.charCodeAt(2)^116|a5.charCodeAt(3)^97)>>>0
+if(s===0)return A.jz(a4>>4]&1<<(o&15))!==0){if(p&&65<=o&&90>=o){if(q==null)q=new A.O("")
+if(r>>4]&1<<(o&15))!==0)A.by(a,s,"Invalid character")
+else{if((o&64512)===55296&&s+1127||(B.t[r>>>4]&1<<(r&15))===0)break}return a},
+m_(a,b){var s,r,q
+for(s=0,r=0;r<2;++r){q=a.charCodeAt(b+r)
+if(48<=q&&q<=57)s=s*16+q-48
+else{q|=32
+if(97<=q&&q<=102)s=s*16+q-87
+else throw A.b(A.aE("Invalid URL encoding",null))}}return s},
+iP(a,b,c,d,e){var s,r,q,p,o=b
+while(!0){if(!(o
b)throw A.b(A.N(k,a,r))
+for(;p!==44;){j.push(r);++r
+for(o=-1;r=0)j.push(o)
+else{n=B.b.gao(j)
+if(p!==44||r!==n+7||!B.a.F(a,"base64",n+1))throw A.b(A.N("Expecting '='",a,r))
+break}}j.push(r)
+m=r+1
+if((j.length&1)===1)a=B.z.cH(0,a,m,s)
+else{l=A.jV(a,m,s,B.j,!0,!1)
+if(l!=null)a=B.a.Z(a,m,s,l)}return new A.fY(a,j,c)},
+mm(){var s,r,q,p,o,n="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~!$&'()*+,;=",m=".",l=":",k="/",j="\\",i="?",h="#",g="/\\",f=J.jh(22,t.bX)
+for(s=0;s<22;++s)f[s]=new Uint8Array(96)
+r=new A.hY(f)
+q=new A.hZ()
+p=new A.i_()
+o=r.$2(0,225)
+q.$3(o,n,1)
+q.$3(o,m,14)
+q.$3(o,l,34)
+q.$3(o,k,3)
+q.$3(o,j,227)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(14,225)
+q.$3(o,n,1)
+q.$3(o,m,15)
+q.$3(o,l,34)
+q.$3(o,g,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(15,225)
+q.$3(o,n,1)
+q.$3(o,"%",225)
+q.$3(o,l,34)
+q.$3(o,k,9)
+q.$3(o,j,233)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(1,225)
+q.$3(o,n,1)
+q.$3(o,l,34)
+q.$3(o,k,10)
+q.$3(o,j,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(2,235)
+q.$3(o,n,139)
+q.$3(o,k,131)
+q.$3(o,j,131)
+q.$3(o,m,146)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(3,235)
+q.$3(o,n,11)
+q.$3(o,k,68)
+q.$3(o,j,68)
+q.$3(o,m,18)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(4,229)
+q.$3(o,n,5)
+p.$3(o,"AZ",229)
+q.$3(o,l,102)
+q.$3(o,"@",68)
+q.$3(o,"[",232)
+q.$3(o,k,138)
+q.$3(o,j,138)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(5,229)
+q.$3(o,n,5)
+p.$3(o,"AZ",229)
+q.$3(o,l,102)
+q.$3(o,"@",68)
+q.$3(o,k,138)
+q.$3(o,j,138)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(6,231)
+p.$3(o,"19",7)
+q.$3(o,"@",68)
+q.$3(o,k,138)
+q.$3(o,j,138)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(7,231)
+p.$3(o,"09",7)
+q.$3(o,"@",68)
+q.$3(o,k,138)
+q.$3(o,j,138)
+q.$3(o,i,172)
+q.$3(o,h,205)
+q.$3(r.$2(8,8),"]",5)
+o=r.$2(9,235)
+q.$3(o,n,11)
+q.$3(o,m,16)
+q.$3(o,g,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(16,235)
+q.$3(o,n,11)
+q.$3(o,m,17)
+q.$3(o,g,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(17,235)
+q.$3(o,n,11)
+q.$3(o,k,9)
+q.$3(o,j,233)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(10,235)
+q.$3(o,n,11)
+q.$3(o,m,18)
+q.$3(o,k,10)
+q.$3(o,j,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(18,235)
+q.$3(o,n,11)
+q.$3(o,m,19)
+q.$3(o,g,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(19,235)
+q.$3(o,n,11)
+q.$3(o,g,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(11,235)
+q.$3(o,n,11)
+q.$3(o,k,10)
+q.$3(o,j,234)
+q.$3(o,i,172)
+q.$3(o,h,205)
+o=r.$2(12,236)
+q.$3(o,n,12)
+q.$3(o,i,12)
+q.$3(o,h,205)
+o=r.$2(13,237)
+q.$3(o,n,13)
+q.$3(o,i,13)
+p.$3(r.$2(20,245),"az",21)
+o=r.$2(21,245)
+p.$3(o,"az",21)
+p.$3(o,"09",21)
+q.$3(o,"+-.",21)
+return f},
+k9(a,b,c,d,e){var s,r,q,p,o=$.kG()
+for(s=b;s>>0},
+j(a){return"Closure '"+this.$_name+"' of "+("Instance of '"+A.fS(this.a)+"'")}}
+A.ea.prototype={
+j(a){return"Reading static variable '"+this.a+"' during its initialization"}}
+A.dF.prototype={
+j(a){return"RuntimeError: "+this.a}}
+A.b2.prototype={
+gh(a){return this.a},
+gD(a){return new A.an(this,A.J(this).k("an<1>"))},
+gbD(a){var s=A.J(this)
+return A.lj(new A.an(this,s.k("an<1>")),new A.fG(this),s.c,s.z[1])},
+a4(a,b){var s=this.b
+if(s==null)return!1
+return s[b]!=null},
+i(a,b){var s,r,q,p,o=null
+if(typeof b=="string"){s=this.b
+if(s==null)return o
+r=s[b]
+q=r==null?o:r.b
+return q}else if(typeof b=="number"&&(b&0x3fffffff)===b){p=this.c
+if(p==null)return o
+r=p[b]
+q=r==null?o:r.b
+return q}else return this.cE(b)},
+cE(a){var s,r,q=this.d
+if(q==null)return null
+s=q[this.bt(a)]
+r=this.bu(s,a)
+if(r<0)return null
+return s[r].b},
+l(a,b,c){var s,r,q,p,o,n,m=this
+if(typeof b=="string"){s=m.b
+m.aZ(s==null?m.b=m.aH():s,b,c)}else if(typeof b=="number"&&(b&0x3fffffff)===b){r=m.c
+m.aZ(r==null?m.c=m.aH():r,b,c)}else{q=m.d
+if(q==null)q=m.d=m.aH()
+p=m.bt(b)
+o=q[p]
+if(o==null)q[p]=[m.aI(b,c)]
+else{n=m.bu(o,b)
+if(n>=0)o[n].b=c
+else o.push(m.aI(b,c))}}},
+aj(a){var s=this
+if(s.a>0){s.b=s.c=s.d=s.e=s.f=null
+s.a=0
+s.b7()}},
+v(a,b){var s=this,r=s.e,q=s.r
+for(;r!=null;){b.$2(r.a,r.b)
+if(q!==s.r)throw A.b(A.aH(s))
+r=r.c}},
+aZ(a,b,c){var s=a[b]
+if(s==null)a[b]=this.aI(b,c)
+else s.b=c},
+b7(){this.r=this.r+1&1073741823},
+aI(a,b){var s,r=this,q=new A.fJ(a,b)
+if(r.e==null)r.e=r.f=q
+else{s=r.f
+s.toString
+q.d=s
+r.f=s.c=q}++r.a
+r.b7()
+return q},
+bt(a){return J.aj(a)&1073741823},
+bu(a,b){var s,r
+if(a==null)return-1
+s=a.length
+for(r=0;r"]=s
+delete s["0;){--q;--s
+j[q]=r[s]}}j=A.jn(j,!1,k)
+j.fixed$length=Array
+j.immutable$list=Array
+return j}}
+A.eF.prototype={
+b5(){return[this.a,this.b]},
+K(a,b){if(b==null)return!1
+return b instanceof A.eF&&this.$s===b.$s&&J.bE(this.a,b.a)&&J.bE(this.b,b.b)},
+gt(a){return A.iB(this.$s,this.a,this.b,B.k)}}
+A.fE.prototype={
+j(a){return"RegExp/"+this.a+"/"+this.b.flags},
+gc5(){var s=this,r=s.c
+if(r!=null)return r
+r=s.b
+return s.c=A.jj(s.a,r.multiline,!r.ignoreCase,r.unicode,r.dotAll,!0)},
+c1(a,b){var s,r=this.gc5()
+r.lastIndex=b
+s=r.exec(a)
+if(s==null)return null
+return new A.eu(s)}}
+A.eu.prototype={
+gcu(a){var s=this.b
+return s.index+s[0].length},
+i(a,b){return this.b[b]},
+$ifL:1,
+$iiC:1}
+A.h7.prototype={
+gq(a){var s=this.d
+return s==null?t.F.a(s):s},
+n(){var s,r,q,p,o,n=this,m=n.b
+if(m==null)return!1
+s=n.c
+r=m.length
+if(s<=r){q=n.a
+p=q.c1(m,s)
+if(p!=null){n.d=p
+o=p.gcu(p)
+if(p.b.index===o){if(q.b.unicode){s=n.c
+q=s+1
+if(q").G(b).k("ak<1,2>"))},
+cv(a,b,c,d){var s
+A.b3(b,c,this.gh(a))
+for(s=b;s>>0]=c},
+$S:10}
+A.eJ.prototype={
+gbo(){return this.c>0},
+gbq(){return this.c>0&&this.d+1>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.aa.prototype={
+gh(a){return a.length},
+$iaa:1}
+A.dB.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.as.prototype={$ias:1}
+A.dE.prototype={
+i(a,b){return A.aT(a.get(b))},
+v(a,b){var s,r=a.entries()
+for(;!0;){s=r.next()
+if(s.done)return
+b.$2(s.value[0],A.aT(s.value[1]))}},
+gD(a){var s=A.n([],t.s)
+this.v(a,new A.fT(s))
+return s},
+gh(a){return a.size},
+l(a,b,c){throw A.b(A.r("Not supported"))},
+$iy:1}
+A.fT.prototype={
+$2(a,b){return this.a.push(a)},
+$S:2}
+A.dG.prototype={
+gh(a){return a.length}}
+A.ab.prototype={$iab:1}
+A.dH.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.ac.prototype={$iac:1}
+A.dI.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.ad.prototype={
+gh(a){return a.length},
+$iad:1}
+A.dL.prototype={
+i(a,b){return a.getItem(A.bA(b))},
+l(a,b,c){a.setItem(b,c)},
+v(a,b){var s,r,q
+for(s=0;!0;++s){r=a.key(s)
+if(r==null)return
+q=a.getItem(r)
+q.toString
+b.$2(r,q)}},
+gD(a){var s=A.n([],t.s)
+this.v(a,new A.fV(s))
+return s},
+gh(a){return a.length},
+$iy:1}
+A.fV.prototype={
+$2(a,b){return this.a.push(a)},
+$S:5}
+A.X.prototype={$iX:1}
+A.c5.prototype={
+H(a,b,c,d){var s,r
+if("createContextualFragment" in window.Range.prototype)return this.aw(a,b,c,d)
+s=A.l0("
"+b+"
",c,d)
+r=document.createDocumentFragment()
+new A.M(r).N(0,new A.M(s))
+return r}}
+A.dN.prototype={
+H(a,b,c,d){var s,r
+if("createContextualFragment" in window.Range.prototype)return this.aw(a,b,c,d)
+s=document
+r=s.createDocumentFragment()
+s=new A.M(B.y.H(s.createElement("table"),b,c,d))
+s=new A.M(s.gV(s))
+new A.M(r).N(0,new A.M(s.gV(s)))
+return r}}
+A.dO.prototype={
+H(a,b,c,d){var s,r
+if("createContextualFragment" in window.Range.prototype)return this.aw(a,b,c,d)
+s=document
+r=s.createDocumentFragment()
+s=new A.M(B.y.H(s.createElement("table"),b,c,d))
+new A.M(r).N(0,new A.M(s.gV(s)))
+return r}}
+A.bs.prototype={
+ab(a,b,c){var s,r
+a.textContent=null
+s=a.content
+s.toString
+J.kH(s)
+r=this.H(a,b,c,null)
+a.content.appendChild(r)},
+aa(a,b){return this.ab(a,b,null)},
+$ibs:1}
+A.b5.prototype={$ib5:1}
+A.af.prototype={$iaf:1}
+A.Y.prototype={$iY:1}
+A.dQ.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.dR.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.dS.prototype={
+gh(a){return a.length}}
+A.ag.prototype={$iag:1}
+A.dT.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.dU.prototype={
+gh(a){return a.length}}
+A.S.prototype={}
+A.e1.prototype={
+j(a){return String(a)}}
+A.e2.prototype={
+gh(a){return a.length}}
+A.bv.prototype={$ibv:1}
+A.e8.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.c8.prototype={
+j(a){var s,r,q,p=a.left
+p.toString
+s=a.top
+s.toString
+r=a.width
+r.toString
+q=a.height
+q.toString
+return"Rectangle ("+A.p(p)+", "+A.p(s)+") "+A.p(r)+" x "+A.p(q)},
+K(a,b){var s,r
+if(b==null)return!1
+if(t.q.b(b)){s=a.left
+s.toString
+r=b.left
+r.toString
+if(s===r){s=a.top
+s.toString
+r=b.top
+r.toString
+if(s===r){s=a.width
+s.toString
+r=J.L(b)
+if(s===r.ga_(b)){s=a.height
+s.toString
+r=s===r.gY(b)
+s=r}else s=!1}else s=!1}else s=!1}else s=!1
+return s},
+gt(a){var s,r,q,p=a.left
+p.toString
+s=a.top
+s.toString
+r=a.width
+r.toString
+q=a.height
+q.toString
+return A.iB(p,s,r,q)},
+gb6(a){return a.height},
+gY(a){var s=a.height
+s.toString
+return s},
+gbf(a){return a.width},
+ga_(a){var s=a.width
+s.toString
+return s}}
+A.em.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.cd.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.eM.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.eS.prototype={
+gh(a){return a.length},
+i(a,b){var s=a.length
+if(b>>>0!==b||b>=s)throw A.b(A.E(b,s,a,null))
+return a[b]},
+l(a,b,c){throw A.b(A.r("Cannot assign element of immutable List."))},
+p(a,b){return a[b]},
+$if:1,
+$io:1,
+$ij:1}
+A.e6.prototype={
+v(a,b){var s,r,q,p,o,n
+for(s=this.gD(this),r=s.length,q=this.a,p=0;p0?q+"-":q)+o}return q.charCodeAt(0)==0?q:q}}
+A.hc.prototype={
+$2(a,b){if(B.a.B(a,"data-"))this.b.$2(this.a.bc(B.a.M(a,5)),b)},
+$S:5}
+A.hd.prototype={
+$2(a,b){if(B.a.B(a,"data-"))this.b.push(this.a.bc(B.a.M(a,5)))},
+$S:5}
+A.eg.prototype={
+R(){var s,r,q,p,o=A.bT(t.N)
+for(s=this.a.className.split(" "),r=s.length,q=0;q"),M:s("B