commit a1e1aa156fb6e0531fd2fcbccb73f8c5ea2b74c8 Author: Shawn Date: Mon Aug 29 20:38:28 2022 -0600 Initial public commit diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..a771f5a5 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,11 @@ +version: 2 +enable-beta-ecosystems: true +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 00000000..9704dacf --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,12 @@ +name: tests +on: + workflow_dispatch: + +jobs: + tests_self_hosted: + runs-on: [self-hosted, macos] + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - run: flutter test + - run: flutter test -d macos integration_test/smoke_test.dart diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..139e58b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +desktop.ini +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 00000000..0a999ee9 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: db747aa1331bd95bc9b3874c842261ca2d302cd5 + channel: stable + +project_type: app diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..1120070a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "flutter-wonders-app", + "request": "launch", + "type": "dart" + }, + { + "name": "flutter-wonders-app (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "flutter-wonders-app (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3a87d0f8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive", + "dart.lineLength": 120 +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5ae22f00 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2022 gskinner.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..949f5a11 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +[![Codemagic build status](https://api.codemagic.io/apps/62c5bf4e59f5e09fd24b0994/62c5bf4e59f5e09fd24b0993/status_badge.svg)](https://codemagic.io/apps/62c5bf4e59f5e09fd24b0994/62c5bf4e59f5e09fd24b0993/latest_build) +# Wonderous +

+ + +

+

+ wonderous-banner-800w +

+ +Navigate the intersection of history, art, and culture. Wonderous will educate and entertain as you uncover information about some of the most famous structures in the world. + +Built by [gskinner](https://gskinner.com/) in partnership with the Flutter team, Wonderous deliberately pushes visual fidelity, effects and transitions to showcase what Flutter is truly capable of on modern mobile hardware. + +In addition to forking and reviewing the [MIT licensed](LICENSE) code available here, you can check out more information on the [Wonderous Showcase Website](https://wonderous.app). + +# App Downloads + +To try the app you can download it from your favorite app store: +* [Google Play](https://play.google.com/store/apps/details?id=com.gskinner.flutter.wonders) +* [Apple App Store](https://apps.apple.com/us/app/wonderous/id1612491897) + +# Installation + +If you're new to Flutter the first thing you'll need is to follow the [setup instructions](https://flutter.dev/docs/get-started/install). + +Once Flutter is setup, you can use the latest `beta` channel: + * `flutter channel beta` + * `flutter upgrade` + + Once on `beta` you're ready to run the app on your local device or simulator: + * `flutter run -d ios` + * `flutter run -d android` + +### Enable Impeller + +For better performance on iOS you can enable "Impeller", which is Flutters new rendering engine. + +To enable, edit the `Info.plist` file and set `FLTEnableImpeller` to `true`: +``` +FLTEnableImpeller + +``` + +Then, switch to the `master` channel and build as normal: + * `flutter channel master` + * `flutter upgrade` + * `flutter run -d ios` + +Note: When Impeller is enabled builds to the Simulator will not work, you will need to test on a physical device instead. + +# About gskinner +We exist to build innovative digital experiences for smart clients, and we love how easy Flutter makes that experience. Don't hesitate to [stop by our site](https://gskinner.com/) to learn more about what we do. We'd love to hear from you! + +# License + +This application is released under the [MIT license](LICENSE). You can use the code for any purpose, including commercial projects. + +[![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..426f7214 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + always_declare_return_types: true + always_use_package_imports: true + prefer_const_constructors: false + prefer_single_quotes: true +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..ce65aea7 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +#key.properties +#**/*.keystore +#**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 00000000..98f8b419 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,89 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + + defaultConfig { + configurations.all { + resolutionStrategy { force 'androidx.core:core-ktx:1.6.0' } + } + applicationId "com.gskinner.flutter.wonders" + minSdkVersion 23 + targetSdkVersion flutter.targetSdkVersion + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + checkReleaseBuilds false + abortOnError false + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..20c5597d --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d3452eee --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 00000000..961c47da Binary files /dev/null and b/android/app/src/main/ic_launcher-playstore.png differ diff --git a/android/app/src/main/kotlin/com/gskinner/wonders/MainActivity.kt b/android/app/src/main/kotlin/com/gskinner/wonders/MainActivity.kt new file mode 100644 index 00000000..74eece23 --- /dev/null +++ b/android/app/src/main/kotlin/com/gskinner/wonders/MainActivity.kt @@ -0,0 +1,6 @@ +package com.gskinner.flutter.wonders + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-hdpi/branding.png b/android/app/src/main/res/drawable-hdpi/branding.png new file mode 100644 index 00000000..29e07556 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/branding.png differ diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png new file mode 100644 index 00000000..51cb6f1a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-mdpi/branding.png b/android/app/src/main/res/drawable-mdpi/branding.png new file mode 100644 index 00000000..1cfd8680 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/branding.png differ diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png new file mode 100644 index 00000000..1e856ca4 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 00000000..7619addd Binary files /dev/null and b/android/app/src/main/res/drawable-v21/background.png differ diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..3cc4948a --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/android/app/src/main/res/drawable-xhdpi/branding.png b/android/app/src/main/res/drawable-xhdpi/branding.png new file mode 100644 index 00000000..e78a19c1 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/branding.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png new file mode 100644 index 00000000..b062141b Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/branding.png b/android/app/src/main/res/drawable-xxhdpi/branding.png new file mode 100644 index 00000000..47c4350a Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/branding.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png new file mode 100644 index 00000000..3d682bf7 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/branding.png b/android/app/src/main/res/drawable-xxxhdpi/branding.png new file mode 100644 index 00000000..13b2266c Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/branding.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png new file mode 100644 index 00000000..cc3c2a45 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png new file mode 100644 index 00000000..7619addd Binary files /dev/null and b/android/app/src/main/res/drawable/background.png differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..3cc4948a --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..44a240e7 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 00000000..6ba71d69 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..a088490f Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png new file mode 100644 index 00000000..d5819168 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..b6e5a91e Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 00000000..22ee33bc Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..670af3dd Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 00000000..407d3ec2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..156432c9 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 00000000..23f918a3 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..3db14bb5 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..f5fdb6b7 --- /dev/null +++ b/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..d0e36a37 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #BEABA1 + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..0d151866 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..7f5659d6 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 00000000..4256f917 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..cc5527d7 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/fonts/B612Mono-Regular.ttf b/assets/fonts/B612Mono-Regular.ttf new file mode 100644 index 00000000..ed013f27 Binary files /dev/null and b/assets/fonts/B612Mono-Regular.ttf differ diff --git a/assets/fonts/CinzelDecorative-Black.ttf b/assets/fonts/CinzelDecorative-Black.ttf new file mode 100644 index 00000000..5b8f0e05 Binary files /dev/null and b/assets/fonts/CinzelDecorative-Black.ttf differ diff --git a/assets/fonts/CinzelDecorative-Bold.ttf b/assets/fonts/CinzelDecorative-Bold.ttf new file mode 100644 index 00000000..ad6bd2c5 Binary files /dev/null and b/assets/fonts/CinzelDecorative-Bold.ttf differ diff --git a/assets/fonts/CinzelDecorative-Regular.ttf b/assets/fonts/CinzelDecorative-Regular.ttf new file mode 100644 index 00000000..f2ae7a05 Binary files /dev/null and b/assets/fonts/CinzelDecorative-Regular.ttf differ diff --git a/assets/fonts/MaShanZheng-Regular.ttf b/assets/fonts/MaShanZheng-Regular.ttf new file mode 100644 index 00000000..2fab2824 Binary files /dev/null and b/assets/fonts/MaShanZheng-Regular.ttf differ diff --git a/assets/fonts/Raleway-Bold.ttf b/assets/fonts/Raleway-Bold.ttf new file mode 100644 index 00000000..87632314 Binary files /dev/null and b/assets/fonts/Raleway-Bold.ttf differ diff --git a/assets/fonts/Raleway-BoldItalic.ttf b/assets/fonts/Raleway-BoldItalic.ttf new file mode 100644 index 00000000..9c16440f Binary files /dev/null and b/assets/fonts/Raleway-BoldItalic.ttf differ diff --git a/assets/fonts/Raleway-ExtraBold.ttf b/assets/fonts/Raleway-ExtraBold.ttf new file mode 100644 index 00000000..64928cdf Binary files /dev/null and b/assets/fonts/Raleway-ExtraBold.ttf differ diff --git a/assets/fonts/Raleway-ExtraBoldItalic.ttf b/assets/fonts/Raleway-ExtraBoldItalic.ttf new file mode 100644 index 00000000..37283280 Binary files /dev/null and b/assets/fonts/Raleway-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Raleway-Italic.ttf b/assets/fonts/Raleway-Italic.ttf new file mode 100644 index 00000000..7bca5ad5 Binary files /dev/null and b/assets/fonts/Raleway-Italic.ttf differ diff --git a/assets/fonts/Raleway-Medium.ttf b/assets/fonts/Raleway-Medium.ttf new file mode 100644 index 00000000..5428c9c6 Binary files /dev/null and b/assets/fonts/Raleway-Medium.ttf differ diff --git a/assets/fonts/Raleway-MediumItalic.ttf b/assets/fonts/Raleway-MediumItalic.ttf new file mode 100644 index 00000000..ba0598a9 Binary files /dev/null and b/assets/fonts/Raleway-MediumItalic.ttf differ diff --git a/assets/fonts/Raleway-Regular.ttf b/assets/fonts/Raleway-Regular.ttf new file mode 100644 index 00000000..acb57156 Binary files /dev/null and b/assets/fonts/Raleway-Regular.ttf differ diff --git a/assets/fonts/TenorSans-Regular.ttf b/assets/fonts/TenorSans-Regular.ttf new file mode 100644 index 00000000..9ff6cf6f Binary files /dev/null and b/assets/fonts/TenorSans-Regular.ttf differ diff --git a/assets/fonts/YesevaOne-Regular.ttf b/assets/fonts/YesevaOne-Regular.ttf new file mode 100644 index 00000000..9817f2e6 Binary files /dev/null and b/assets/fonts/YesevaOne-Regular.ttf differ diff --git a/assets/images/_common/2.0x/adjust-search.png b/assets/images/_common/2.0x/adjust-search.png new file mode 100644 index 00000000..f412092c Binary files /dev/null and b/assets/images/_common/2.0x/adjust-search.png differ diff --git a/assets/images/_common/2.0x/arrow-indicator.png b/assets/images/_common/2.0x/arrow-indicator.png new file mode 100644 index 00000000..aceed820 Binary files /dev/null and b/assets/images/_common/2.0x/arrow-indicator.png differ diff --git a/assets/images/_common/2.0x/cloud-white.png b/assets/images/_common/2.0x/cloud-white.png new file mode 100644 index 00000000..23c7cfa9 Binary files /dev/null and b/assets/images/_common/2.0x/cloud-white.png differ diff --git a/assets/images/_common/2.0x/construction.png b/assets/images/_common/2.0x/construction.png new file mode 100644 index 00000000..8de0e4aa Binary files /dev/null and b/assets/images/_common/2.0x/construction.png differ diff --git a/assets/images/_common/2.0x/geography.png b/assets/images/_common/2.0x/geography.png new file mode 100644 index 00000000..6a4da256 Binary files /dev/null and b/assets/images/_common/2.0x/geography.png differ diff --git a/assets/images/_common/2.0x/history.png b/assets/images/_common/2.0x/history.png new file mode 100644 index 00000000..1b051b86 Binary files /dev/null and b/assets/images/_common/2.0x/history.png differ diff --git a/assets/images/_common/2.0x/intro-camel.jpg b/assets/images/_common/2.0x/intro-camel.jpg new file mode 100755 index 00000000..f078daf6 Binary files /dev/null and b/assets/images/_common/2.0x/intro-camel.jpg differ diff --git a/assets/images/_common/2.0x/intro-mask-1.png b/assets/images/_common/2.0x/intro-mask-1.png new file mode 100644 index 00000000..be9f7dad Binary files /dev/null and b/assets/images/_common/2.0x/intro-mask-1.png differ diff --git a/assets/images/_common/2.0x/intro-mask-2.png b/assets/images/_common/2.0x/intro-mask-2.png new file mode 100644 index 00000000..6245eb8a Binary files /dev/null and b/assets/images/_common/2.0x/intro-mask-2.png differ diff --git a/assets/images/_common/2.0x/intro-mask-3.png b/assets/images/_common/2.0x/intro-mask-3.png new file mode 100644 index 00000000..febb1ef6 Binary files /dev/null and b/assets/images/_common/2.0x/intro-mask-3.png differ diff --git a/assets/images/_common/2.0x/intro-petra.jpg b/assets/images/_common/2.0x/intro-petra.jpg new file mode 100755 index 00000000..32ecac44 Binary files /dev/null and b/assets/images/_common/2.0x/intro-petra.jpg differ diff --git a/assets/images/_common/2.0x/intro-statue.jpg b/assets/images/_common/2.0x/intro-statue.jpg new file mode 100755 index 00000000..5358f92c Binary files /dev/null and b/assets/images/_common/2.0x/intro-statue.jpg differ diff --git a/assets/images/_common/2.0x/location-pin.png b/assets/images/_common/2.0x/location-pin.png new file mode 100644 index 00000000..653c044d Binary files /dev/null and b/assets/images/_common/2.0x/location-pin.png differ diff --git a/assets/images/_common/2.0x/ribbon-end.png b/assets/images/_common/2.0x/ribbon-end.png new file mode 100644 index 00000000..314d9127 Binary files /dev/null and b/assets/images/_common/2.0x/ribbon-end.png differ diff --git a/assets/images/_common/2.0x/search-map.png b/assets/images/_common/2.0x/search-map.png new file mode 100644 index 00000000..f1044034 Binary files /dev/null and b/assets/images/_common/2.0x/search-map.png differ diff --git a/assets/images/_common/2.0x/tab-artifacts-active.png b/assets/images/_common/2.0x/tab-artifacts-active.png new file mode 100644 index 00000000..fdc97118 Binary files /dev/null and b/assets/images/_common/2.0x/tab-artifacts-active.png differ diff --git a/assets/images/_common/2.0x/tab-artifacts.png b/assets/images/_common/2.0x/tab-artifacts.png new file mode 100644 index 00000000..392685ed Binary files /dev/null and b/assets/images/_common/2.0x/tab-artifacts.png differ diff --git a/assets/images/_common/2.0x/tab-bubble-bar.png b/assets/images/_common/2.0x/tab-bubble-bar.png new file mode 100644 index 00000000..923e5f0e Binary files /dev/null and b/assets/images/_common/2.0x/tab-bubble-bar.png differ diff --git a/assets/images/_common/2.0x/tab-bubble.png b/assets/images/_common/2.0x/tab-bubble.png new file mode 100644 index 00000000..09b93c88 Binary files /dev/null and b/assets/images/_common/2.0x/tab-bubble.png differ diff --git a/assets/images/_common/2.0x/tab-editorial-active.png b/assets/images/_common/2.0x/tab-editorial-active.png new file mode 100644 index 00000000..7e2cf9fa Binary files /dev/null and b/assets/images/_common/2.0x/tab-editorial-active.png differ diff --git a/assets/images/_common/2.0x/tab-editorial.png b/assets/images/_common/2.0x/tab-editorial.png new file mode 100644 index 00000000..2f53103f Binary files /dev/null and b/assets/images/_common/2.0x/tab-editorial.png differ diff --git a/assets/images/_common/2.0x/tab-photos-active.png b/assets/images/_common/2.0x/tab-photos-active.png new file mode 100644 index 00000000..b91c6027 Binary files /dev/null and b/assets/images/_common/2.0x/tab-photos-active.png differ diff --git a/assets/images/_common/2.0x/tab-photos.png b/assets/images/_common/2.0x/tab-photos.png new file mode 100644 index 00000000..62b2cc49 Binary files /dev/null and b/assets/images/_common/2.0x/tab-photos.png differ diff --git a/assets/images/_common/2.0x/tab-timeline-active.png b/assets/images/_common/2.0x/tab-timeline-active.png new file mode 100644 index 00000000..25286f3e Binary files /dev/null and b/assets/images/_common/2.0x/tab-timeline-active.png differ diff --git a/assets/images/_common/2.0x/tab-timeline.png b/assets/images/_common/2.0x/tab-timeline.png new file mode 100644 index 00000000..dc03399b Binary files /dev/null and b/assets/images/_common/2.0x/tab-timeline.png differ diff --git a/assets/images/_common/3.0x/adjust-search.png b/assets/images/_common/3.0x/adjust-search.png new file mode 100644 index 00000000..c43aec40 Binary files /dev/null and b/assets/images/_common/3.0x/adjust-search.png differ diff --git a/assets/images/_common/3.0x/construction.png b/assets/images/_common/3.0x/construction.png new file mode 100644 index 00000000..f3726c68 Binary files /dev/null and b/assets/images/_common/3.0x/construction.png differ diff --git a/assets/images/_common/3.0x/geography.png b/assets/images/_common/3.0x/geography.png new file mode 100644 index 00000000..e1b78ec7 Binary files /dev/null and b/assets/images/_common/3.0x/geography.png differ diff --git a/assets/images/_common/3.0x/history.png b/assets/images/_common/3.0x/history.png new file mode 100644 index 00000000..589861b2 Binary files /dev/null and b/assets/images/_common/3.0x/history.png differ diff --git a/assets/images/_common/3.0x/intro-camel.jpg b/assets/images/_common/3.0x/intro-camel.jpg new file mode 100755 index 00000000..0e354282 Binary files /dev/null and b/assets/images/_common/3.0x/intro-camel.jpg differ diff --git a/assets/images/_common/3.0x/intro-mask-1.png b/assets/images/_common/3.0x/intro-mask-1.png new file mode 100644 index 00000000..7a570cb9 Binary files /dev/null and b/assets/images/_common/3.0x/intro-mask-1.png differ diff --git a/assets/images/_common/3.0x/intro-mask-2.png b/assets/images/_common/3.0x/intro-mask-2.png new file mode 100644 index 00000000..0184c856 Binary files /dev/null and b/assets/images/_common/3.0x/intro-mask-2.png differ diff --git a/assets/images/_common/3.0x/intro-mask-3.png b/assets/images/_common/3.0x/intro-mask-3.png new file mode 100644 index 00000000..7934b39e Binary files /dev/null and b/assets/images/_common/3.0x/intro-mask-3.png differ diff --git a/assets/images/_common/3.0x/intro-petra.jpg b/assets/images/_common/3.0x/intro-petra.jpg new file mode 100755 index 00000000..9694cd26 Binary files /dev/null and b/assets/images/_common/3.0x/intro-petra.jpg differ diff --git a/assets/images/_common/3.0x/intro-statue.jpg b/assets/images/_common/3.0x/intro-statue.jpg new file mode 100755 index 00000000..9fd0cf04 Binary files /dev/null and b/assets/images/_common/3.0x/intro-statue.jpg differ diff --git a/assets/images/_common/3.0x/ribbon-end.png b/assets/images/_common/3.0x/ribbon-end.png new file mode 100644 index 00000000..895ed979 Binary files /dev/null and b/assets/images/_common/3.0x/ribbon-end.png differ diff --git a/assets/images/_common/3.0x/search-map.png b/assets/images/_common/3.0x/search-map.png new file mode 100644 index 00000000..f46ec189 Binary files /dev/null and b/assets/images/_common/3.0x/search-map.png differ diff --git a/assets/images/_common/3.0x/tab-artifacts-active.png b/assets/images/_common/3.0x/tab-artifacts-active.png new file mode 100644 index 00000000..0274253e Binary files /dev/null and b/assets/images/_common/3.0x/tab-artifacts-active.png differ diff --git a/assets/images/_common/3.0x/tab-artifacts.png b/assets/images/_common/3.0x/tab-artifacts.png new file mode 100644 index 00000000..145836ec Binary files /dev/null and b/assets/images/_common/3.0x/tab-artifacts.png differ diff --git a/assets/images/_common/3.0x/tab-editorial-active.png b/assets/images/_common/3.0x/tab-editorial-active.png new file mode 100644 index 00000000..e623aa30 Binary files /dev/null and b/assets/images/_common/3.0x/tab-editorial-active.png differ diff --git a/assets/images/_common/3.0x/tab-editorial.png b/assets/images/_common/3.0x/tab-editorial.png new file mode 100644 index 00000000..3e921ca4 Binary files /dev/null and b/assets/images/_common/3.0x/tab-editorial.png differ diff --git a/assets/images/_common/3.0x/tab-photos-active.png b/assets/images/_common/3.0x/tab-photos-active.png new file mode 100644 index 00000000..9002ce5b Binary files /dev/null and b/assets/images/_common/3.0x/tab-photos-active.png differ diff --git a/assets/images/_common/3.0x/tab-photos.png b/assets/images/_common/3.0x/tab-photos.png new file mode 100644 index 00000000..408f6220 Binary files /dev/null and b/assets/images/_common/3.0x/tab-photos.png differ diff --git a/assets/images/_common/3.0x/tab-timeline-active.png b/assets/images/_common/3.0x/tab-timeline-active.png new file mode 100644 index 00000000..05461032 Binary files /dev/null and b/assets/images/_common/3.0x/tab-timeline-active.png differ diff --git a/assets/images/_common/3.0x/tab-timeline.png b/assets/images/_common/3.0x/tab-timeline.png new file mode 100644 index 00000000..20978c9c Binary files /dev/null and b/assets/images/_common/3.0x/tab-timeline.png differ diff --git a/assets/images/_common/adjust-search.png b/assets/images/_common/adjust-search.png new file mode 100644 index 00000000..ecd01400 Binary files /dev/null and b/assets/images/_common/adjust-search.png differ diff --git a/assets/images/_common/app-logo-plain.png b/assets/images/_common/app-logo-plain.png new file mode 100644 index 00000000..e933e9fb Binary files /dev/null and b/assets/images/_common/app-logo-plain.png differ diff --git a/assets/images/_common/app-logo.png b/assets/images/_common/app-logo.png new file mode 100644 index 00000000..8bf20a1e Binary files /dev/null and b/assets/images/_common/app-logo.png differ diff --git a/assets/images/_common/arrow-indicator.png b/assets/images/_common/arrow-indicator.png new file mode 100644 index 00000000..67be6c2c Binary files /dev/null and b/assets/images/_common/arrow-indicator.png differ diff --git a/assets/images/_common/cloud-white.png b/assets/images/_common/cloud-white.png new file mode 100644 index 00000000..db2f7592 Binary files /dev/null and b/assets/images/_common/cloud-white.png differ diff --git a/assets/images/_common/cloud-white.svg b/assets/images/_common/cloud-white.svg new file mode 100644 index 00000000..101e8469 --- /dev/null +++ b/assets/images/_common/cloud-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/_common/compass-full.svg b/assets/images/_common/compass-full.svg new file mode 100644 index 00000000..a9b70065 --- /dev/null +++ b/assets/images/_common/compass-full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/_common/compass-simple.svg b/assets/images/_common/compass-simple.svg new file mode 100644 index 00000000..9c47a76f --- /dev/null +++ b/assets/images/_common/compass-simple.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/_common/construction.png b/assets/images/_common/construction.png new file mode 100644 index 00000000..cf196d00 Binary files /dev/null and b/assets/images/_common/construction.png differ diff --git a/assets/images/_common/geography.png b/assets/images/_common/geography.png new file mode 100644 index 00000000..23408517 Binary files /dev/null and b/assets/images/_common/geography.png differ diff --git a/assets/images/_common/history.png b/assets/images/_common/history.png new file mode 100644 index 00000000..c7431d19 Binary files /dev/null and b/assets/images/_common/history.png differ diff --git a/assets/images/_common/icons/2.0x/icon-back.png b/assets/images/_common/icons/2.0x/icon-back.png new file mode 100644 index 00000000..c1f576b1 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-back.png differ diff --git a/assets/images/_common/icons/2.0x/icon-close-large.png b/assets/images/_common/icons/2.0x/icon-close-large.png new file mode 100644 index 00000000..f7c9c504 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-close-large.png differ diff --git a/assets/images/_common/icons/2.0x/icon-close.png b/assets/images/_common/icons/2.0x/icon-close.png new file mode 100644 index 00000000..c903341b Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-close.png differ diff --git a/assets/images/_common/icons/2.0x/icon-collection.png b/assets/images/_common/icons/2.0x/icon-collection.png new file mode 100644 index 00000000..36fe94b7 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-collection.png differ diff --git a/assets/images/_common/icons/2.0x/icon-download.png b/assets/images/_common/icons/2.0x/icon-download.png new file mode 100644 index 00000000..64832b34 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-download.png differ diff --git a/assets/images/_common/icons/2.0x/icon-expand.png b/assets/images/_common/icons/2.0x/icon-expand.png new file mode 100644 index 00000000..e9ea5f1a Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-expand.png differ diff --git a/assets/images/_common/icons/2.0x/icon-fullscreen-exit.png b/assets/images/_common/icons/2.0x/icon-fullscreen-exit.png new file mode 100644 index 00000000..5d4e00f9 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-fullscreen-exit.png differ diff --git a/assets/images/_common/icons/2.0x/icon-fullscreen.png b/assets/images/_common/icons/2.0x/icon-fullscreen.png new file mode 100644 index 00000000..b20f2abc Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-fullscreen.png differ diff --git a/assets/images/_common/icons/2.0x/icon-info.png b/assets/images/_common/icons/2.0x/icon-info.png new file mode 100644 index 00000000..5186fbf8 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-info.png differ diff --git a/assets/images/_common/icons/2.0x/icon-menu.png b/assets/images/_common/icons/2.0x/icon-menu.png new file mode 100644 index 00000000..93922f13 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-menu.png differ diff --git a/assets/images/_common/icons/2.0x/icon-next-large.png b/assets/images/_common/icons/2.0x/icon-next-large.png new file mode 100644 index 00000000..19a6a2ea Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-next-large.png differ diff --git a/assets/images/_common/icons/2.0x/icon-north.png b/assets/images/_common/icons/2.0x/icon-north.png new file mode 100644 index 00000000..fe018303 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-north.png differ diff --git a/assets/images/_common/icons/2.0x/icon-prev.png b/assets/images/_common/icons/2.0x/icon-prev.png new file mode 100644 index 00000000..939a27c4 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-prev.png differ diff --git a/assets/images/_common/icons/2.0x/icon-reset-location.png b/assets/images/_common/icons/2.0x/icon-reset-location.png new file mode 100644 index 00000000..222aac11 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-reset-location.png differ diff --git a/assets/images/_common/icons/2.0x/icon-search.png b/assets/images/_common/icons/2.0x/icon-search.png new file mode 100644 index 00000000..2ebd252d Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-search.png differ diff --git a/assets/images/_common/icons/2.0x/icon-share-android.png b/assets/images/_common/icons/2.0x/icon-share-android.png new file mode 100644 index 00000000..ec12e5bb Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-share-android.png differ diff --git a/assets/images/_common/icons/2.0x/icon-share-ios.png b/assets/images/_common/icons/2.0x/icon-share-ios.png new file mode 100644 index 00000000..7bb4650b Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-share-ios.png differ diff --git a/assets/images/_common/icons/2.0x/icon-timeline.png b/assets/images/_common/icons/2.0x/icon-timeline.png new file mode 100644 index 00000000..48f24519 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-timeline.png differ diff --git a/assets/images/_common/icons/2.0x/icon-wallpaper.png b/assets/images/_common/icons/2.0x/icon-wallpaper.png new file mode 100644 index 00000000..253590e0 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-wallpaper.png differ diff --git a/assets/images/_common/icons/2.0x/icon-zoom-in.png b/assets/images/_common/icons/2.0x/icon-zoom-in.png new file mode 100644 index 00000000..25396c25 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-zoom-in.png differ diff --git a/assets/images/_common/icons/2.0x/icon-zoom-out.png b/assets/images/_common/icons/2.0x/icon-zoom-out.png new file mode 100644 index 00000000..6d324828 Binary files /dev/null and b/assets/images/_common/icons/2.0x/icon-zoom-out.png differ diff --git a/assets/images/_common/icons/3.0x/icon-back.png b/assets/images/_common/icons/3.0x/icon-back.png new file mode 100644 index 00000000..01dba41e Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-back.png differ diff --git a/assets/images/_common/icons/3.0x/icon-close-large.png b/assets/images/_common/icons/3.0x/icon-close-large.png new file mode 100644 index 00000000..ba7d9661 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-close-large.png differ diff --git a/assets/images/_common/icons/3.0x/icon-close.png b/assets/images/_common/icons/3.0x/icon-close.png new file mode 100644 index 00000000..bc8c5ec2 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-close.png differ diff --git a/assets/images/_common/icons/3.0x/icon-collection.png b/assets/images/_common/icons/3.0x/icon-collection.png new file mode 100644 index 00000000..c686b68a Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-collection.png differ diff --git a/assets/images/_common/icons/3.0x/icon-download.png b/assets/images/_common/icons/3.0x/icon-download.png new file mode 100644 index 00000000..4246da3b Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-download.png differ diff --git a/assets/images/_common/icons/3.0x/icon-expand.png b/assets/images/_common/icons/3.0x/icon-expand.png new file mode 100644 index 00000000..aedb2efe Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-expand.png differ diff --git a/assets/images/_common/icons/3.0x/icon-fullscreen-exit.png b/assets/images/_common/icons/3.0x/icon-fullscreen-exit.png new file mode 100644 index 00000000..6df2240b Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-fullscreen-exit.png differ diff --git a/assets/images/_common/icons/3.0x/icon-fullscreen.png b/assets/images/_common/icons/3.0x/icon-fullscreen.png new file mode 100644 index 00000000..21526947 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-fullscreen.png differ diff --git a/assets/images/_common/icons/3.0x/icon-info.png b/assets/images/_common/icons/3.0x/icon-info.png new file mode 100644 index 00000000..d1ca3462 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-info.png differ diff --git a/assets/images/_common/icons/3.0x/icon-menu.png b/assets/images/_common/icons/3.0x/icon-menu.png new file mode 100644 index 00000000..931d3666 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-menu.png differ diff --git a/assets/images/_common/icons/3.0x/icon-next-large.png b/assets/images/_common/icons/3.0x/icon-next-large.png new file mode 100644 index 00000000..7edb7f95 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-next-large.png differ diff --git a/assets/images/_common/icons/3.0x/icon-north.png b/assets/images/_common/icons/3.0x/icon-north.png new file mode 100644 index 00000000..3ad6f4fd Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-north.png differ diff --git a/assets/images/_common/icons/3.0x/icon-prev.png b/assets/images/_common/icons/3.0x/icon-prev.png new file mode 100644 index 00000000..19985324 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-prev.png differ diff --git a/assets/images/_common/icons/3.0x/icon-reset-location.png b/assets/images/_common/icons/3.0x/icon-reset-location.png new file mode 100644 index 00000000..ae2c0de7 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-reset-location.png differ diff --git a/assets/images/_common/icons/3.0x/icon-search.png b/assets/images/_common/icons/3.0x/icon-search.png new file mode 100644 index 00000000..58fd207a Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-search.png differ diff --git a/assets/images/_common/icons/3.0x/icon-share-android.png b/assets/images/_common/icons/3.0x/icon-share-android.png new file mode 100644 index 00000000..e03f11ec Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-share-android.png differ diff --git a/assets/images/_common/icons/3.0x/icon-share-ios.png b/assets/images/_common/icons/3.0x/icon-share-ios.png new file mode 100644 index 00000000..18a21340 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-share-ios.png differ diff --git a/assets/images/_common/icons/3.0x/icon-timeline.png b/assets/images/_common/icons/3.0x/icon-timeline.png new file mode 100644 index 00000000..de08bb88 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-timeline.png differ diff --git a/assets/images/_common/icons/3.0x/icon-wallpaper.png b/assets/images/_common/icons/3.0x/icon-wallpaper.png new file mode 100644 index 00000000..7e4c179d Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-wallpaper.png differ diff --git a/assets/images/_common/icons/3.0x/icon-zoom-in.png b/assets/images/_common/icons/3.0x/icon-zoom-in.png new file mode 100644 index 00000000..91e9dda7 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-zoom-in.png differ diff --git a/assets/images/_common/icons/3.0x/icon-zoom-out.png b/assets/images/_common/icons/3.0x/icon-zoom-out.png new file mode 100644 index 00000000..bbf7c9e0 Binary files /dev/null and b/assets/images/_common/icons/3.0x/icon-zoom-out.png differ diff --git a/assets/images/_common/icons/icon-close-large.png b/assets/images/_common/icons/icon-close-large.png new file mode 100644 index 00000000..e9a75171 Binary files /dev/null and b/assets/images/_common/icons/icon-close-large.png differ diff --git a/assets/images/_common/icons/icon-close.png b/assets/images/_common/icons/icon-close.png new file mode 100644 index 00000000..7f757e5d Binary files /dev/null and b/assets/images/_common/icons/icon-close.png differ diff --git a/assets/images/_common/icons/icon-collection.png b/assets/images/_common/icons/icon-collection.png new file mode 100644 index 00000000..50962ff6 Binary files /dev/null and b/assets/images/_common/icons/icon-collection.png differ diff --git a/assets/images/_common/icons/icon-download.png b/assets/images/_common/icons/icon-download.png new file mode 100644 index 00000000..1eb40292 Binary files /dev/null and b/assets/images/_common/icons/icon-download.png differ diff --git a/assets/images/_common/icons/icon-expand.png b/assets/images/_common/icons/icon-expand.png new file mode 100644 index 00000000..b50523ed Binary files /dev/null and b/assets/images/_common/icons/icon-expand.png differ diff --git a/assets/images/_common/icons/icon-fullscreen-exit.png b/assets/images/_common/icons/icon-fullscreen-exit.png new file mode 100644 index 00000000..ce5bd014 Binary files /dev/null and b/assets/images/_common/icons/icon-fullscreen-exit.png differ diff --git a/assets/images/_common/icons/icon-fullscreen.png b/assets/images/_common/icons/icon-fullscreen.png new file mode 100644 index 00000000..0eefa7d2 Binary files /dev/null and b/assets/images/_common/icons/icon-fullscreen.png differ diff --git a/assets/images/_common/icons/icon-info.png b/assets/images/_common/icons/icon-info.png new file mode 100644 index 00000000..20f02d9f Binary files /dev/null and b/assets/images/_common/icons/icon-info.png differ diff --git a/assets/images/_common/icons/icon-menu.png b/assets/images/_common/icons/icon-menu.png new file mode 100644 index 00000000..717ca4df Binary files /dev/null and b/assets/images/_common/icons/icon-menu.png differ diff --git a/assets/images/_common/icons/icon-next-large.png b/assets/images/_common/icons/icon-next-large.png new file mode 100644 index 00000000..70ed6993 Binary files /dev/null and b/assets/images/_common/icons/icon-next-large.png differ diff --git a/assets/images/_common/icons/icon-north.png b/assets/images/_common/icons/icon-north.png new file mode 100644 index 00000000..9b59e738 Binary files /dev/null and b/assets/images/_common/icons/icon-north.png differ diff --git a/assets/images/_common/icons/icon-prev.png b/assets/images/_common/icons/icon-prev.png new file mode 100644 index 00000000..c40c0baf Binary files /dev/null and b/assets/images/_common/icons/icon-prev.png differ diff --git a/assets/images/_common/icons/icon-reset-location.png b/assets/images/_common/icons/icon-reset-location.png new file mode 100644 index 00000000..93776191 Binary files /dev/null and b/assets/images/_common/icons/icon-reset-location.png differ diff --git a/assets/images/_common/icons/icon-search.png b/assets/images/_common/icons/icon-search.png new file mode 100644 index 00000000..7c419469 Binary files /dev/null and b/assets/images/_common/icons/icon-search.png differ diff --git a/assets/images/_common/icons/icon-share-android.png b/assets/images/_common/icons/icon-share-android.png new file mode 100644 index 00000000..82165d3b Binary files /dev/null and b/assets/images/_common/icons/icon-share-android.png differ diff --git a/assets/images/_common/icons/icon-share-ios.png b/assets/images/_common/icons/icon-share-ios.png new file mode 100644 index 00000000..51a3c65a Binary files /dev/null and b/assets/images/_common/icons/icon-share-ios.png differ diff --git a/assets/images/_common/icons/icon-timeline.png b/assets/images/_common/icons/icon-timeline.png new file mode 100644 index 00000000..0eb0a82e Binary files /dev/null and b/assets/images/_common/icons/icon-timeline.png differ diff --git a/assets/images/_common/icons/icon-wallpaper.png b/assets/images/_common/icons/icon-wallpaper.png new file mode 100644 index 00000000..11b540c4 Binary files /dev/null and b/assets/images/_common/icons/icon-wallpaper.png differ diff --git a/assets/images/_common/icons/icon-zoom-in.png b/assets/images/_common/icons/icon-zoom-in.png new file mode 100644 index 00000000..497cb345 Binary files /dev/null and b/assets/images/_common/icons/icon-zoom-in.png differ diff --git a/assets/images/_common/icons/icon-zoom-out.png b/assets/images/_common/icons/icon-zoom-out.png new file mode 100644 index 00000000..8fb8894f Binary files /dev/null and b/assets/images/_common/icons/icon-zoom-out.png differ diff --git a/assets/images/_common/intro-camel.jpg b/assets/images/_common/intro-camel.jpg new file mode 100755 index 00000000..71e5cab0 Binary files /dev/null and b/assets/images/_common/intro-camel.jpg differ diff --git a/assets/images/_common/intro-mask-1.png b/assets/images/_common/intro-mask-1.png new file mode 100644 index 00000000..01a94317 Binary files /dev/null and b/assets/images/_common/intro-mask-1.png differ diff --git a/assets/images/_common/intro-mask-2.png b/assets/images/_common/intro-mask-2.png new file mode 100644 index 00000000..c33e7502 Binary files /dev/null and b/assets/images/_common/intro-mask-2.png differ diff --git a/assets/images/_common/intro-mask-3.png b/assets/images/_common/intro-mask-3.png new file mode 100644 index 00000000..d872a0b3 Binary files /dev/null and b/assets/images/_common/intro-mask-3.png differ diff --git a/assets/images/_common/intro-petra.jpg b/assets/images/_common/intro-petra.jpg new file mode 100755 index 00000000..5fd7d157 Binary files /dev/null and b/assets/images/_common/intro-petra.jpg differ diff --git a/assets/images/_common/intro-statue.jpg b/assets/images/_common/intro-statue.jpg new file mode 100755 index 00000000..4a8e7c2a Binary files /dev/null and b/assets/images/_common/intro-statue.jpg differ diff --git a/assets/images/_common/location-pin.png b/assets/images/_common/location-pin.png new file mode 100644 index 00000000..4b4cc87e Binary files /dev/null and b/assets/images/_common/location-pin.png differ diff --git a/assets/images/_common/particle-21x23.png b/assets/images/_common/particle-21x23.png new file mode 100644 index 00000000..ea7e6a82 Binary files /dev/null and b/assets/images/_common/particle-21x23.png differ diff --git a/assets/images/_common/ribbon-end.png b/assets/images/_common/ribbon-end.png new file mode 100644 index 00000000..b4ffbefa Binary files /dev/null and b/assets/images/_common/ribbon-end.png differ diff --git a/assets/images/_common/search-map.png b/assets/images/_common/search-map.png new file mode 100644 index 00000000..c10169af Binary files /dev/null and b/assets/images/_common/search-map.png differ diff --git a/assets/images/_common/tab-artifacts-active.png b/assets/images/_common/tab-artifacts-active.png new file mode 100644 index 00000000..51df028d Binary files /dev/null and b/assets/images/_common/tab-artifacts-active.png differ diff --git a/assets/images/_common/tab-artifacts.png b/assets/images/_common/tab-artifacts.png new file mode 100644 index 00000000..38266e7a Binary files /dev/null and b/assets/images/_common/tab-artifacts.png differ diff --git a/assets/images/_common/tab-bubble-bar.png b/assets/images/_common/tab-bubble-bar.png new file mode 100644 index 00000000..4b5b3b12 Binary files /dev/null and b/assets/images/_common/tab-bubble-bar.png differ diff --git a/assets/images/_common/tab-bubble.png b/assets/images/_common/tab-bubble.png new file mode 100644 index 00000000..12fd2e58 Binary files /dev/null and b/assets/images/_common/tab-bubble.png differ diff --git a/assets/images/_common/tab-editorial-active.png b/assets/images/_common/tab-editorial-active.png new file mode 100644 index 00000000..dba4de81 Binary files /dev/null and b/assets/images/_common/tab-editorial-active.png differ diff --git a/assets/images/_common/tab-editorial.png b/assets/images/_common/tab-editorial.png new file mode 100644 index 00000000..9d069341 Binary files /dev/null and b/assets/images/_common/tab-editorial.png differ diff --git a/assets/images/_common/tab-photos-active.png b/assets/images/_common/tab-photos-active.png new file mode 100644 index 00000000..7bb10f15 Binary files /dev/null and b/assets/images/_common/tab-photos-active.png differ diff --git a/assets/images/_common/tab-photos.png b/assets/images/_common/tab-photos.png new file mode 100644 index 00000000..00655731 Binary files /dev/null and b/assets/images/_common/tab-photos.png differ diff --git a/assets/images/_common/tab-timeline-active.png b/assets/images/_common/tab-timeline-active.png new file mode 100644 index 00000000..6e344997 Binary files /dev/null and b/assets/images/_common/tab-timeline-active.png differ diff --git a/assets/images/_common/tab-timeline.png b/assets/images/_common/tab-timeline.png new file mode 100644 index 00000000..b8501623 Binary files /dev/null and b/assets/images/_common/tab-timeline.png differ diff --git a/assets/images/_common/texture/roller-1-black.png b/assets/images/_common/texture/roller-1-black.png new file mode 100644 index 00000000..c2580292 Binary files /dev/null and b/assets/images/_common/texture/roller-1-black.png differ diff --git a/assets/images/_common/texture/roller-1-white.png b/assets/images/_common/texture/roller-1-white.png new file mode 100644 index 00000000..ae75d477 Binary files /dev/null and b/assets/images/_common/texture/roller-1-white.png differ diff --git a/assets/images/_common/texture/roller-2-black.png b/assets/images/_common/texture/roller-2-black.png new file mode 100644 index 00000000..d34e2ca8 Binary files /dev/null and b/assets/images/_common/texture/roller-2-black.png differ diff --git a/assets/images/_common/texture/roller-2-white.png b/assets/images/_common/texture/roller-2-white.png new file mode 100644 index 00000000..539dbb57 Binary files /dev/null and b/assets/images/_common/texture/roller-2-white.png differ diff --git a/assets/images/_common/texture/speckles-white.png b/assets/images/_common/texture/speckles-white.png new file mode 100644 index 00000000..e98c5a0c Binary files /dev/null and b/assets/images/_common/texture/speckles-white.png differ diff --git a/assets/images/chichen_itza/2.0x/chichen.png b/assets/images/chichen_itza/2.0x/chichen.png new file mode 100755 index 00000000..0142f47d Binary files /dev/null and b/assets/images/chichen_itza/2.0x/chichen.png differ diff --git a/assets/images/chichen_itza/2.0x/flattened.jpg b/assets/images/chichen_itza/2.0x/flattened.jpg new file mode 100644 index 00000000..fc71ffe3 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/flattened.jpg differ diff --git a/assets/images/chichen_itza/2.0x/foreground-left.png b/assets/images/chichen_itza/2.0x/foreground-left.png new file mode 100755 index 00000000..cccb133c Binary files /dev/null and b/assets/images/chichen_itza/2.0x/foreground-left.png differ diff --git a/assets/images/chichen_itza/2.0x/foreground-right.png b/assets/images/chichen_itza/2.0x/foreground-right.png new file mode 100755 index 00000000..61a4e8a7 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/foreground-right.png differ diff --git a/assets/images/chichen_itza/2.0x/photo-1.jpg b/assets/images/chichen_itza/2.0x/photo-1.jpg new file mode 100644 index 00000000..f4dbf118 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/photo-1.jpg differ diff --git a/assets/images/chichen_itza/2.0x/photo-2.jpg b/assets/images/chichen_itza/2.0x/photo-2.jpg new file mode 100644 index 00000000..8ee96d73 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/photo-2.jpg differ diff --git a/assets/images/chichen_itza/2.0x/photo-3.jpg b/assets/images/chichen_itza/2.0x/photo-3.jpg new file mode 100644 index 00000000..af76a953 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/photo-3.jpg differ diff --git a/assets/images/chichen_itza/2.0x/photo-4.jpg b/assets/images/chichen_itza/2.0x/photo-4.jpg new file mode 100644 index 00000000..33f157c8 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/photo-4.jpg differ diff --git a/assets/images/chichen_itza/2.0x/sun.png b/assets/images/chichen_itza/2.0x/sun.png new file mode 100755 index 00000000..b7ffa1c0 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/sun.png differ diff --git a/assets/images/chichen_itza/2.0x/top-left.png b/assets/images/chichen_itza/2.0x/top-left.png new file mode 100755 index 00000000..7c41b5f1 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/top-left.png differ diff --git a/assets/images/chichen_itza/2.0x/top-right.png b/assets/images/chichen_itza/2.0x/top-right.png new file mode 100755 index 00000000..73e4379a Binary files /dev/null and b/assets/images/chichen_itza/2.0x/top-right.png differ diff --git a/assets/images/chichen_itza/2.0x/wonder-button.png b/assets/images/chichen_itza/2.0x/wonder-button.png new file mode 100644 index 00000000..0d1510e1 Binary files /dev/null and b/assets/images/chichen_itza/2.0x/wonder-button.png differ diff --git a/assets/images/chichen_itza/3.0x/chichen.png b/assets/images/chichen_itza/3.0x/chichen.png new file mode 100755 index 00000000..ca6eb710 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/chichen.png differ diff --git a/assets/images/chichen_itza/3.0x/flattened.jpg b/assets/images/chichen_itza/3.0x/flattened.jpg new file mode 100644 index 00000000..7564e6a4 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/flattened.jpg differ diff --git a/assets/images/chichen_itza/3.0x/foreground-left.png b/assets/images/chichen_itza/3.0x/foreground-left.png new file mode 100755 index 00000000..01b993eb Binary files /dev/null and b/assets/images/chichen_itza/3.0x/foreground-left.png differ diff --git a/assets/images/chichen_itza/3.0x/foreground-right.png b/assets/images/chichen_itza/3.0x/foreground-right.png new file mode 100755 index 00000000..a4e142c3 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/foreground-right.png differ diff --git a/assets/images/chichen_itza/3.0x/photo-1.jpg b/assets/images/chichen_itza/3.0x/photo-1.jpg new file mode 100755 index 00000000..7c78905e Binary files /dev/null and b/assets/images/chichen_itza/3.0x/photo-1.jpg differ diff --git a/assets/images/chichen_itza/3.0x/photo-2.jpg b/assets/images/chichen_itza/3.0x/photo-2.jpg new file mode 100755 index 00000000..a6d041bd Binary files /dev/null and b/assets/images/chichen_itza/3.0x/photo-2.jpg differ diff --git a/assets/images/chichen_itza/3.0x/photo-3.jpg b/assets/images/chichen_itza/3.0x/photo-3.jpg new file mode 100755 index 00000000..562c7d61 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/photo-3.jpg differ diff --git a/assets/images/chichen_itza/3.0x/photo-4.jpg b/assets/images/chichen_itza/3.0x/photo-4.jpg new file mode 100755 index 00000000..c0753f02 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/photo-4.jpg differ diff --git a/assets/images/chichen_itza/3.0x/sun.png b/assets/images/chichen_itza/3.0x/sun.png new file mode 100755 index 00000000..b1241bd4 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/sun.png differ diff --git a/assets/images/chichen_itza/3.0x/top-left.png b/assets/images/chichen_itza/3.0x/top-left.png new file mode 100755 index 00000000..b9e67bf2 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/top-left.png differ diff --git a/assets/images/chichen_itza/3.0x/top-right.png b/assets/images/chichen_itza/3.0x/top-right.png new file mode 100755 index 00000000..f0988bab Binary files /dev/null and b/assets/images/chichen_itza/3.0x/top-right.png differ diff --git a/assets/images/chichen_itza/3.0x/wonder-button.png b/assets/images/chichen_itza/3.0x/wonder-button.png new file mode 100644 index 00000000..b6ca2c55 Binary files /dev/null and b/assets/images/chichen_itza/3.0x/wonder-button.png differ diff --git a/assets/images/chichen_itza/chichen.png b/assets/images/chichen_itza/chichen.png new file mode 100644 index 00000000..b87c4252 Binary files /dev/null and b/assets/images/chichen_itza/chichen.png differ diff --git a/assets/images/chichen_itza/flattened.jpg b/assets/images/chichen_itza/flattened.jpg new file mode 100644 index 00000000..1134d71d Binary files /dev/null and b/assets/images/chichen_itza/flattened.jpg differ diff --git a/assets/images/chichen_itza/foreground-left.png b/assets/images/chichen_itza/foreground-left.png new file mode 100644 index 00000000..d3ee55fc Binary files /dev/null and b/assets/images/chichen_itza/foreground-left.png differ diff --git a/assets/images/chichen_itza/foreground-right.png b/assets/images/chichen_itza/foreground-right.png new file mode 100644 index 00000000..c3af397a Binary files /dev/null and b/assets/images/chichen_itza/foreground-right.png differ diff --git a/assets/images/chichen_itza/photo-1.jpg b/assets/images/chichen_itza/photo-1.jpg new file mode 100644 index 00000000..10b7d862 Binary files /dev/null and b/assets/images/chichen_itza/photo-1.jpg differ diff --git a/assets/images/chichen_itza/photo-2.jpg b/assets/images/chichen_itza/photo-2.jpg new file mode 100644 index 00000000..e297a54c Binary files /dev/null and b/assets/images/chichen_itza/photo-2.jpg differ diff --git a/assets/images/chichen_itza/photo-3.jpg b/assets/images/chichen_itza/photo-3.jpg new file mode 100644 index 00000000..0442b3ac Binary files /dev/null and b/assets/images/chichen_itza/photo-3.jpg differ diff --git a/assets/images/chichen_itza/photo-4.jpg b/assets/images/chichen_itza/photo-4.jpg new file mode 100644 index 00000000..5265195b Binary files /dev/null and b/assets/images/chichen_itza/photo-4.jpg differ diff --git a/assets/images/chichen_itza/sun.png b/assets/images/chichen_itza/sun.png new file mode 100644 index 00000000..9bcf0261 Binary files /dev/null and b/assets/images/chichen_itza/sun.png differ diff --git a/assets/images/chichen_itza/top-left.png b/assets/images/chichen_itza/top-left.png new file mode 100644 index 00000000..338284dc Binary files /dev/null and b/assets/images/chichen_itza/top-left.png differ diff --git a/assets/images/chichen_itza/top-right.png b/assets/images/chichen_itza/top-right.png new file mode 100644 index 00000000..6182f6d8 Binary files /dev/null and b/assets/images/chichen_itza/top-right.png differ diff --git a/assets/images/chichen_itza/wonder-button.png b/assets/images/chichen_itza/wonder-button.png new file mode 100644 index 00000000..a2eea9c6 Binary files /dev/null and b/assets/images/chichen_itza/wonder-button.png differ diff --git a/assets/images/christ_the_redeemer/2.0x/flattened.jpg b/assets/images/christ_the_redeemer/2.0x/flattened.jpg new file mode 100644 index 00000000..2bc6aedf Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/flattened.jpg differ diff --git a/assets/images/christ_the_redeemer/2.0x/foreground-left.png b/assets/images/christ_the_redeemer/2.0x/foreground-left.png new file mode 100755 index 00000000..9497384e Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/foreground-left.png differ diff --git a/assets/images/christ_the_redeemer/2.0x/foreground-right.png b/assets/images/christ_the_redeemer/2.0x/foreground-right.png new file mode 100755 index 00000000..64372c6d Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/foreground-right.png differ diff --git a/assets/images/christ_the_redeemer/2.0x/photo-1.jpg b/assets/images/christ_the_redeemer/2.0x/photo-1.jpg new file mode 100644 index 00000000..3ca90f76 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/photo-1.jpg differ diff --git a/assets/images/christ_the_redeemer/2.0x/photo-2.jpg b/assets/images/christ_the_redeemer/2.0x/photo-2.jpg new file mode 100644 index 00000000..7d66b609 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/photo-2.jpg differ diff --git a/assets/images/christ_the_redeemer/2.0x/photo-3.jpg b/assets/images/christ_the_redeemer/2.0x/photo-3.jpg new file mode 100644 index 00000000..a933b138 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/photo-3.jpg differ diff --git a/assets/images/christ_the_redeemer/2.0x/photo-4.jpg b/assets/images/christ_the_redeemer/2.0x/photo-4.jpg new file mode 100644 index 00000000..b37ab936 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/photo-4.jpg differ diff --git a/assets/images/christ_the_redeemer/2.0x/redeemer.png b/assets/images/christ_the_redeemer/2.0x/redeemer.png new file mode 100755 index 00000000..08cb54f4 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/redeemer.png differ diff --git a/assets/images/christ_the_redeemer/2.0x/sun.png b/assets/images/christ_the_redeemer/2.0x/sun.png new file mode 100755 index 00000000..12e14595 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/sun.png differ diff --git a/assets/images/christ_the_redeemer/2.0x/wonder-button.png b/assets/images/christ_the_redeemer/2.0x/wonder-button.png new file mode 100644 index 00000000..39ae4c14 Binary files /dev/null and b/assets/images/christ_the_redeemer/2.0x/wonder-button.png differ diff --git a/assets/images/christ_the_redeemer/3.0x/flattened.jpg b/assets/images/christ_the_redeemer/3.0x/flattened.jpg new file mode 100644 index 00000000..1144ddf7 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/flattened.jpg differ diff --git a/assets/images/christ_the_redeemer/3.0x/foreground-left.png b/assets/images/christ_the_redeemer/3.0x/foreground-left.png new file mode 100755 index 00000000..d850bb9f Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/foreground-left.png differ diff --git a/assets/images/christ_the_redeemer/3.0x/foreground-right.png b/assets/images/christ_the_redeemer/3.0x/foreground-right.png new file mode 100755 index 00000000..f4cc167d Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/foreground-right.png differ diff --git a/assets/images/christ_the_redeemer/3.0x/photo-1.jpg b/assets/images/christ_the_redeemer/3.0x/photo-1.jpg new file mode 100644 index 00000000..06ef07e7 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/photo-1.jpg differ diff --git a/assets/images/christ_the_redeemer/3.0x/photo-2.jpg b/assets/images/christ_the_redeemer/3.0x/photo-2.jpg new file mode 100644 index 00000000..bbf812f7 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/photo-2.jpg differ diff --git a/assets/images/christ_the_redeemer/3.0x/photo-3.jpg b/assets/images/christ_the_redeemer/3.0x/photo-3.jpg new file mode 100644 index 00000000..5af6a278 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/photo-3.jpg differ diff --git a/assets/images/christ_the_redeemer/3.0x/photo-4.jpg b/assets/images/christ_the_redeemer/3.0x/photo-4.jpg new file mode 100644 index 00000000..0c10faa4 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/photo-4.jpg differ diff --git a/assets/images/christ_the_redeemer/3.0x/redeemer.png b/assets/images/christ_the_redeemer/3.0x/redeemer.png new file mode 100755 index 00000000..a791323d Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/redeemer.png differ diff --git a/assets/images/christ_the_redeemer/3.0x/sun.png b/assets/images/christ_the_redeemer/3.0x/sun.png new file mode 100755 index 00000000..ad9633d7 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/sun.png differ diff --git a/assets/images/christ_the_redeemer/3.0x/wonder-button.png b/assets/images/christ_the_redeemer/3.0x/wonder-button.png new file mode 100644 index 00000000..83675708 Binary files /dev/null and b/assets/images/christ_the_redeemer/3.0x/wonder-button.png differ diff --git a/assets/images/christ_the_redeemer/flattened.jpg b/assets/images/christ_the_redeemer/flattened.jpg new file mode 100644 index 00000000..0c4a0c29 Binary files /dev/null and b/assets/images/christ_the_redeemer/flattened.jpg differ diff --git a/assets/images/christ_the_redeemer/foreground-left.png b/assets/images/christ_the_redeemer/foreground-left.png new file mode 100755 index 00000000..b13d465c Binary files /dev/null and b/assets/images/christ_the_redeemer/foreground-left.png differ diff --git a/assets/images/christ_the_redeemer/foreground-right.png b/assets/images/christ_the_redeemer/foreground-right.png new file mode 100755 index 00000000..9bc03101 Binary files /dev/null and b/assets/images/christ_the_redeemer/foreground-right.png differ diff --git a/assets/images/christ_the_redeemer/photo-1.jpg b/assets/images/christ_the_redeemer/photo-1.jpg new file mode 100644 index 00000000..e8f67bb5 Binary files /dev/null and b/assets/images/christ_the_redeemer/photo-1.jpg differ diff --git a/assets/images/christ_the_redeemer/photo-2.jpg b/assets/images/christ_the_redeemer/photo-2.jpg new file mode 100644 index 00000000..ec729ad4 Binary files /dev/null and b/assets/images/christ_the_redeemer/photo-2.jpg differ diff --git a/assets/images/christ_the_redeemer/photo-3.jpg b/assets/images/christ_the_redeemer/photo-3.jpg new file mode 100644 index 00000000..ac01a24a Binary files /dev/null and b/assets/images/christ_the_redeemer/photo-3.jpg differ diff --git a/assets/images/christ_the_redeemer/photo-4.jpg b/assets/images/christ_the_redeemer/photo-4.jpg new file mode 100644 index 00000000..2f553814 Binary files /dev/null and b/assets/images/christ_the_redeemer/photo-4.jpg differ diff --git a/assets/images/christ_the_redeemer/redeemer.png b/assets/images/christ_the_redeemer/redeemer.png new file mode 100755 index 00000000..5e5195ad Binary files /dev/null and b/assets/images/christ_the_redeemer/redeemer.png differ diff --git a/assets/images/christ_the_redeemer/sun.png b/assets/images/christ_the_redeemer/sun.png new file mode 100755 index 00000000..d3996bde Binary files /dev/null and b/assets/images/christ_the_redeemer/sun.png differ diff --git a/assets/images/christ_the_redeemer/wonder-button.png b/assets/images/christ_the_redeemer/wonder-button.png new file mode 100644 index 00000000..ba35e540 Binary files /dev/null and b/assets/images/christ_the_redeemer/wonder-button.png differ diff --git a/assets/images/collectibles/2.0x/jewelry.png b/assets/images/collectibles/2.0x/jewelry.png new file mode 100644 index 00000000..d8116f2f Binary files /dev/null and b/assets/images/collectibles/2.0x/jewelry.png differ diff --git a/assets/images/collectibles/2.0x/picture.png b/assets/images/collectibles/2.0x/picture.png new file mode 100644 index 00000000..533f9151 Binary files /dev/null and b/assets/images/collectibles/2.0x/picture.png differ diff --git a/assets/images/collectibles/2.0x/scroll.png b/assets/images/collectibles/2.0x/scroll.png new file mode 100644 index 00000000..742268b9 Binary files /dev/null and b/assets/images/collectibles/2.0x/scroll.png differ diff --git a/assets/images/collectibles/2.0x/statue.png b/assets/images/collectibles/2.0x/statue.png new file mode 100644 index 00000000..32f76daa Binary files /dev/null and b/assets/images/collectibles/2.0x/statue.png differ diff --git a/assets/images/collectibles/2.0x/textile.png b/assets/images/collectibles/2.0x/textile.png new file mode 100644 index 00000000..7e27a91a Binary files /dev/null and b/assets/images/collectibles/2.0x/textile.png differ diff --git a/assets/images/collectibles/2.0x/vase.png b/assets/images/collectibles/2.0x/vase.png new file mode 100644 index 00000000..0b33dbcf Binary files /dev/null and b/assets/images/collectibles/2.0x/vase.png differ diff --git a/assets/images/collectibles/3.0x/jewelry.png b/assets/images/collectibles/3.0x/jewelry.png new file mode 100644 index 00000000..ac5e1e9c Binary files /dev/null and b/assets/images/collectibles/3.0x/jewelry.png differ diff --git a/assets/images/collectibles/3.0x/picture.png b/assets/images/collectibles/3.0x/picture.png new file mode 100644 index 00000000..c849200e Binary files /dev/null and b/assets/images/collectibles/3.0x/picture.png differ diff --git a/assets/images/collectibles/3.0x/scroll.png b/assets/images/collectibles/3.0x/scroll.png new file mode 100644 index 00000000..8dddbf13 Binary files /dev/null and b/assets/images/collectibles/3.0x/scroll.png differ diff --git a/assets/images/collectibles/3.0x/statue.png b/assets/images/collectibles/3.0x/statue.png new file mode 100644 index 00000000..d35f2017 Binary files /dev/null and b/assets/images/collectibles/3.0x/statue.png differ diff --git a/assets/images/collectibles/3.0x/textile.png b/assets/images/collectibles/3.0x/textile.png new file mode 100644 index 00000000..b1fa1df9 Binary files /dev/null and b/assets/images/collectibles/3.0x/textile.png differ diff --git a/assets/images/collectibles/3.0x/vase.png b/assets/images/collectibles/3.0x/vase.png new file mode 100644 index 00000000..f35a30d5 Binary files /dev/null and b/assets/images/collectibles/3.0x/vase.png differ diff --git a/assets/images/collectibles/jewelry.png b/assets/images/collectibles/jewelry.png new file mode 100644 index 00000000..85b3e094 Binary files /dev/null and b/assets/images/collectibles/jewelry.png differ diff --git a/assets/images/collectibles/picture.png b/assets/images/collectibles/picture.png new file mode 100644 index 00000000..d4743382 Binary files /dev/null and b/assets/images/collectibles/picture.png differ diff --git a/assets/images/collectibles/scroll.png b/assets/images/collectibles/scroll.png new file mode 100644 index 00000000..b3de1b85 Binary files /dev/null and b/assets/images/collectibles/scroll.png differ diff --git a/assets/images/collectibles/statue.png b/assets/images/collectibles/statue.png new file mode 100644 index 00000000..ff9b8f06 Binary files /dev/null and b/assets/images/collectibles/statue.png differ diff --git a/assets/images/collectibles/textile.png b/assets/images/collectibles/textile.png new file mode 100644 index 00000000..2a31003f Binary files /dev/null and b/assets/images/collectibles/textile.png differ diff --git a/assets/images/collectibles/vase.png b/assets/images/collectibles/vase.png new file mode 100644 index 00000000..000b40e2 Binary files /dev/null and b/assets/images/collectibles/vase.png differ diff --git a/assets/images/colosseum/2.0x/colosseum.png b/assets/images/colosseum/2.0x/colosseum.png new file mode 100755 index 00000000..e1104637 Binary files /dev/null and b/assets/images/colosseum/2.0x/colosseum.png differ diff --git a/assets/images/colosseum/2.0x/flattened.jpg b/assets/images/colosseum/2.0x/flattened.jpg new file mode 100644 index 00000000..e197706a Binary files /dev/null and b/assets/images/colosseum/2.0x/flattened.jpg differ diff --git a/assets/images/colosseum/2.0x/foreground-left.png b/assets/images/colosseum/2.0x/foreground-left.png new file mode 100755 index 00000000..6f594b32 Binary files /dev/null and b/assets/images/colosseum/2.0x/foreground-left.png differ diff --git a/assets/images/colosseum/2.0x/foreground-right.png b/assets/images/colosseum/2.0x/foreground-right.png new file mode 100755 index 00000000..30f373bd Binary files /dev/null and b/assets/images/colosseum/2.0x/foreground-right.png differ diff --git a/assets/images/colosseum/2.0x/photo-1.jpg b/assets/images/colosseum/2.0x/photo-1.jpg new file mode 100755 index 00000000..d3979b47 Binary files /dev/null and b/assets/images/colosseum/2.0x/photo-1.jpg differ diff --git a/assets/images/colosseum/2.0x/photo-2.jpg b/assets/images/colosseum/2.0x/photo-2.jpg new file mode 100755 index 00000000..8f23b54e Binary files /dev/null and b/assets/images/colosseum/2.0x/photo-2.jpg differ diff --git a/assets/images/colosseum/2.0x/photo-3.jpg b/assets/images/colosseum/2.0x/photo-3.jpg new file mode 100755 index 00000000..5b7fbfa6 Binary files /dev/null and b/assets/images/colosseum/2.0x/photo-3.jpg differ diff --git a/assets/images/colosseum/2.0x/photo-4.jpg b/assets/images/colosseum/2.0x/photo-4.jpg new file mode 100755 index 00000000..39278af2 Binary files /dev/null and b/assets/images/colosseum/2.0x/photo-4.jpg differ diff --git a/assets/images/colosseum/2.0x/sun.png b/assets/images/colosseum/2.0x/sun.png new file mode 100755 index 00000000..e0516456 Binary files /dev/null and b/assets/images/colosseum/2.0x/sun.png differ diff --git a/assets/images/colosseum/2.0x/wonder-button.png b/assets/images/colosseum/2.0x/wonder-button.png new file mode 100644 index 00000000..b384be0e Binary files /dev/null and b/assets/images/colosseum/2.0x/wonder-button.png differ diff --git a/assets/images/colosseum/3.0x/colosseum.png b/assets/images/colosseum/3.0x/colosseum.png new file mode 100755 index 00000000..fef4ba2d Binary files /dev/null and b/assets/images/colosseum/3.0x/colosseum.png differ diff --git a/assets/images/colosseum/3.0x/flattened.jpg b/assets/images/colosseum/3.0x/flattened.jpg new file mode 100644 index 00000000..110a9a30 Binary files /dev/null and b/assets/images/colosseum/3.0x/flattened.jpg differ diff --git a/assets/images/colosseum/3.0x/foreground-left.png b/assets/images/colosseum/3.0x/foreground-left.png new file mode 100755 index 00000000..4b35b268 Binary files /dev/null and b/assets/images/colosseum/3.0x/foreground-left.png differ diff --git a/assets/images/colosseum/3.0x/foreground-right.png b/assets/images/colosseum/3.0x/foreground-right.png new file mode 100755 index 00000000..cdf34bda Binary files /dev/null and b/assets/images/colosseum/3.0x/foreground-right.png differ diff --git a/assets/images/colosseum/3.0x/photo-1.jpg b/assets/images/colosseum/3.0x/photo-1.jpg new file mode 100755 index 00000000..2585f5ce Binary files /dev/null and b/assets/images/colosseum/3.0x/photo-1.jpg differ diff --git a/assets/images/colosseum/3.0x/photo-2.jpg b/assets/images/colosseum/3.0x/photo-2.jpg new file mode 100755 index 00000000..ecbc60cb Binary files /dev/null and b/assets/images/colosseum/3.0x/photo-2.jpg differ diff --git a/assets/images/colosseum/3.0x/photo-3.jpg b/assets/images/colosseum/3.0x/photo-3.jpg new file mode 100755 index 00000000..f3f07a6c Binary files /dev/null and b/assets/images/colosseum/3.0x/photo-3.jpg differ diff --git a/assets/images/colosseum/3.0x/photo-4.jpg b/assets/images/colosseum/3.0x/photo-4.jpg new file mode 100755 index 00000000..d7b0a778 Binary files /dev/null and b/assets/images/colosseum/3.0x/photo-4.jpg differ diff --git a/assets/images/colosseum/3.0x/sun.png b/assets/images/colosseum/3.0x/sun.png new file mode 100755 index 00000000..0e404ed9 Binary files /dev/null and b/assets/images/colosseum/3.0x/sun.png differ diff --git a/assets/images/colosseum/3.0x/wonder-button.png b/assets/images/colosseum/3.0x/wonder-button.png new file mode 100644 index 00000000..a2e84d46 Binary files /dev/null and b/assets/images/colosseum/3.0x/wonder-button.png differ diff --git a/assets/images/colosseum/colosseum.png b/assets/images/colosseum/colosseum.png new file mode 100755 index 00000000..4433792f Binary files /dev/null and b/assets/images/colosseum/colosseum.png differ diff --git a/assets/images/colosseum/flattened.jpg b/assets/images/colosseum/flattened.jpg new file mode 100644 index 00000000..a6227d4a Binary files /dev/null and b/assets/images/colosseum/flattened.jpg differ diff --git a/assets/images/colosseum/foreground-left.png b/assets/images/colosseum/foreground-left.png new file mode 100755 index 00000000..3a11128f Binary files /dev/null and b/assets/images/colosseum/foreground-left.png differ diff --git a/assets/images/colosseum/foreground-right.png b/assets/images/colosseum/foreground-right.png new file mode 100755 index 00000000..73358209 Binary files /dev/null and b/assets/images/colosseum/foreground-right.png differ diff --git a/assets/images/colosseum/photo-1.jpg b/assets/images/colosseum/photo-1.jpg new file mode 100755 index 00000000..9a243f9a Binary files /dev/null and b/assets/images/colosseum/photo-1.jpg differ diff --git a/assets/images/colosseum/photo-2.jpg b/assets/images/colosseum/photo-2.jpg new file mode 100755 index 00000000..33e32d0c Binary files /dev/null and b/assets/images/colosseum/photo-2.jpg differ diff --git a/assets/images/colosseum/photo-3.jpg b/assets/images/colosseum/photo-3.jpg new file mode 100755 index 00000000..9e3f1bb4 Binary files /dev/null and b/assets/images/colosseum/photo-3.jpg differ diff --git a/assets/images/colosseum/photo-4.jpg b/assets/images/colosseum/photo-4.jpg new file mode 100755 index 00000000..b1df0820 Binary files /dev/null and b/assets/images/colosseum/photo-4.jpg differ diff --git a/assets/images/colosseum/sun.png b/assets/images/colosseum/sun.png new file mode 100755 index 00000000..cc0835aa Binary files /dev/null and b/assets/images/colosseum/sun.png differ diff --git a/assets/images/colosseum/wonder-button.png b/assets/images/colosseum/wonder-button.png new file mode 100644 index 00000000..b30c98d2 Binary files /dev/null and b/assets/images/colosseum/wonder-button.png differ diff --git a/assets/images/great_wall_of_china/2.0x/flattened.jpg b/assets/images/great_wall_of_china/2.0x/flattened.jpg new file mode 100644 index 00000000..107819bc Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/flattened.jpg differ diff --git a/assets/images/great_wall_of_china/2.0x/foreground-left.png b/assets/images/great_wall_of_china/2.0x/foreground-left.png new file mode 100755 index 00000000..abc69101 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/foreground-left.png differ diff --git a/assets/images/great_wall_of_china/2.0x/foreground-right.png b/assets/images/great_wall_of_china/2.0x/foreground-right.png new file mode 100755 index 00000000..8bda4bfc Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/foreground-right.png differ diff --git a/assets/images/great_wall_of_china/2.0x/great-wall.png b/assets/images/great_wall_of_china/2.0x/great-wall.png new file mode 100755 index 00000000..502d4ba9 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/great-wall.png differ diff --git a/assets/images/great_wall_of_china/2.0x/photo-1.jpg b/assets/images/great_wall_of_china/2.0x/photo-1.jpg new file mode 100644 index 00000000..961902f7 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/photo-1.jpg differ diff --git a/assets/images/great_wall_of_china/2.0x/photo-2.jpg b/assets/images/great_wall_of_china/2.0x/photo-2.jpg new file mode 100644 index 00000000..0ee86f4d Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/photo-2.jpg differ diff --git a/assets/images/great_wall_of_china/2.0x/photo-3.jpg b/assets/images/great_wall_of_china/2.0x/photo-3.jpg new file mode 100644 index 00000000..5158295d Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/photo-3.jpg differ diff --git a/assets/images/great_wall_of_china/2.0x/photo-4.jpg b/assets/images/great_wall_of_china/2.0x/photo-4.jpg new file mode 100644 index 00000000..476e0259 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/photo-4.jpg differ diff --git a/assets/images/great_wall_of_china/2.0x/sun.png b/assets/images/great_wall_of_china/2.0x/sun.png new file mode 100755 index 00000000..9ee2c756 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/sun.png differ diff --git a/assets/images/great_wall_of_china/2.0x/wonder-button.png b/assets/images/great_wall_of_china/2.0x/wonder-button.png new file mode 100644 index 00000000..3ecd6417 Binary files /dev/null and b/assets/images/great_wall_of_china/2.0x/wonder-button.png differ diff --git a/assets/images/great_wall_of_china/3.0x/flattened.jpg b/assets/images/great_wall_of_china/3.0x/flattened.jpg new file mode 100644 index 00000000..b2e25497 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/flattened.jpg differ diff --git a/assets/images/great_wall_of_china/3.0x/foreground-left.png b/assets/images/great_wall_of_china/3.0x/foreground-left.png new file mode 100755 index 00000000..603da24b Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/foreground-left.png differ diff --git a/assets/images/great_wall_of_china/3.0x/foreground-right.png b/assets/images/great_wall_of_china/3.0x/foreground-right.png new file mode 100755 index 00000000..d10730d7 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/foreground-right.png differ diff --git a/assets/images/great_wall_of_china/3.0x/great-wall.png b/assets/images/great_wall_of_china/3.0x/great-wall.png new file mode 100755 index 00000000..34d371d4 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/great-wall.png differ diff --git a/assets/images/great_wall_of_china/3.0x/photo-1.jpg b/assets/images/great_wall_of_china/3.0x/photo-1.jpg new file mode 100644 index 00000000..a86de532 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/photo-1.jpg differ diff --git a/assets/images/great_wall_of_china/3.0x/photo-2.jpg b/assets/images/great_wall_of_china/3.0x/photo-2.jpg new file mode 100644 index 00000000..cf360201 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/photo-2.jpg differ diff --git a/assets/images/great_wall_of_china/3.0x/photo-3.jpg b/assets/images/great_wall_of_china/3.0x/photo-3.jpg new file mode 100644 index 00000000..8366b75c Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/photo-3.jpg differ diff --git a/assets/images/great_wall_of_china/3.0x/photo-4.jpg b/assets/images/great_wall_of_china/3.0x/photo-4.jpg new file mode 100644 index 00000000..465f2657 Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/photo-4.jpg differ diff --git a/assets/images/great_wall_of_china/3.0x/sun.png b/assets/images/great_wall_of_china/3.0x/sun.png new file mode 100755 index 00000000..dae31b0f Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/sun.png differ diff --git a/assets/images/great_wall_of_china/3.0x/wonder-button.png b/assets/images/great_wall_of_china/3.0x/wonder-button.png new file mode 100644 index 00000000..9f51364a Binary files /dev/null and b/assets/images/great_wall_of_china/3.0x/wonder-button.png differ diff --git a/assets/images/great_wall_of_china/flattened.jpg b/assets/images/great_wall_of_china/flattened.jpg new file mode 100644 index 00000000..d67f0225 Binary files /dev/null and b/assets/images/great_wall_of_china/flattened.jpg differ diff --git a/assets/images/great_wall_of_china/foreground-left.png b/assets/images/great_wall_of_china/foreground-left.png new file mode 100755 index 00000000..1aacdf61 Binary files /dev/null and b/assets/images/great_wall_of_china/foreground-left.png differ diff --git a/assets/images/great_wall_of_china/foreground-right.png b/assets/images/great_wall_of_china/foreground-right.png new file mode 100755 index 00000000..fe4ebdc8 Binary files /dev/null and b/assets/images/great_wall_of_china/foreground-right.png differ diff --git a/assets/images/great_wall_of_china/great-wall.png b/assets/images/great_wall_of_china/great-wall.png new file mode 100755 index 00000000..abcd8e9c Binary files /dev/null and b/assets/images/great_wall_of_china/great-wall.png differ diff --git a/assets/images/great_wall_of_china/photo-1.jpg b/assets/images/great_wall_of_china/photo-1.jpg new file mode 100644 index 00000000..3c11fcbd Binary files /dev/null and b/assets/images/great_wall_of_china/photo-1.jpg differ diff --git a/assets/images/great_wall_of_china/photo-2.jpg b/assets/images/great_wall_of_china/photo-2.jpg new file mode 100644 index 00000000..f9c05847 Binary files /dev/null and b/assets/images/great_wall_of_china/photo-2.jpg differ diff --git a/assets/images/great_wall_of_china/photo-3.jpg b/assets/images/great_wall_of_china/photo-3.jpg new file mode 100644 index 00000000..d8a527e7 Binary files /dev/null and b/assets/images/great_wall_of_china/photo-3.jpg differ diff --git a/assets/images/great_wall_of_china/photo-4.jpg b/assets/images/great_wall_of_china/photo-4.jpg new file mode 100644 index 00000000..3e65e100 Binary files /dev/null and b/assets/images/great_wall_of_china/photo-4.jpg differ diff --git a/assets/images/great_wall_of_china/sun.png b/assets/images/great_wall_of_china/sun.png new file mode 100755 index 00000000..bfeafd82 Binary files /dev/null and b/assets/images/great_wall_of_china/sun.png differ diff --git a/assets/images/great_wall_of_china/wonder-button.png b/assets/images/great_wall_of_china/wonder-button.png new file mode 100644 index 00000000..135c0d49 Binary files /dev/null and b/assets/images/great_wall_of_china/wonder-button.png differ diff --git a/assets/images/machu_picchu/2.0x/flattened.jpg b/assets/images/machu_picchu/2.0x/flattened.jpg new file mode 100644 index 00000000..fed096fb Binary files /dev/null and b/assets/images/machu_picchu/2.0x/flattened.jpg differ diff --git a/assets/images/machu_picchu/2.0x/foreground-back.png b/assets/images/machu_picchu/2.0x/foreground-back.png new file mode 100755 index 00000000..148f4a58 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/foreground-back.png differ diff --git a/assets/images/machu_picchu/2.0x/foreground-front.png b/assets/images/machu_picchu/2.0x/foreground-front.png new file mode 100755 index 00000000..efb22e57 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/foreground-front.png differ diff --git a/assets/images/machu_picchu/2.0x/machu-picchu.png b/assets/images/machu_picchu/2.0x/machu-picchu.png new file mode 100755 index 00000000..5d5da675 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/machu-picchu.png differ diff --git a/assets/images/machu_picchu/2.0x/photo-1.jpg b/assets/images/machu_picchu/2.0x/photo-1.jpg new file mode 100755 index 00000000..6c83894b Binary files /dev/null and b/assets/images/machu_picchu/2.0x/photo-1.jpg differ diff --git a/assets/images/machu_picchu/2.0x/photo-2.jpg b/assets/images/machu_picchu/2.0x/photo-2.jpg new file mode 100755 index 00000000..eda831a0 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/photo-2.jpg differ diff --git a/assets/images/machu_picchu/2.0x/photo-3.jpg b/assets/images/machu_picchu/2.0x/photo-3.jpg new file mode 100755 index 00000000..f717bc4a Binary files /dev/null and b/assets/images/machu_picchu/2.0x/photo-3.jpg differ diff --git a/assets/images/machu_picchu/2.0x/photo-4.jpg b/assets/images/machu_picchu/2.0x/photo-4.jpg new file mode 100755 index 00000000..267686e3 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/photo-4.jpg differ diff --git a/assets/images/machu_picchu/2.0x/sun.png b/assets/images/machu_picchu/2.0x/sun.png new file mode 100755 index 00000000..30e0f2b7 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/sun.png differ diff --git a/assets/images/machu_picchu/2.0x/wonder-button.png b/assets/images/machu_picchu/2.0x/wonder-button.png new file mode 100644 index 00000000..f770a111 Binary files /dev/null and b/assets/images/machu_picchu/2.0x/wonder-button.png differ diff --git a/assets/images/machu_picchu/3.0x/flattened.jpg b/assets/images/machu_picchu/3.0x/flattened.jpg new file mode 100644 index 00000000..05d816cd Binary files /dev/null and b/assets/images/machu_picchu/3.0x/flattened.jpg differ diff --git a/assets/images/machu_picchu/3.0x/foreground-back.png b/assets/images/machu_picchu/3.0x/foreground-back.png new file mode 100755 index 00000000..7666f278 Binary files /dev/null and b/assets/images/machu_picchu/3.0x/foreground-back.png differ diff --git a/assets/images/machu_picchu/3.0x/foreground-front.png b/assets/images/machu_picchu/3.0x/foreground-front.png new file mode 100755 index 00000000..71eaa56d Binary files /dev/null and b/assets/images/machu_picchu/3.0x/foreground-front.png differ diff --git a/assets/images/machu_picchu/3.0x/machu-picchu.png b/assets/images/machu_picchu/3.0x/machu-picchu.png new file mode 100755 index 00000000..1f588f17 Binary files /dev/null and b/assets/images/machu_picchu/3.0x/machu-picchu.png differ diff --git a/assets/images/machu_picchu/3.0x/photo-1.jpg b/assets/images/machu_picchu/3.0x/photo-1.jpg new file mode 100755 index 00000000..8a39000b Binary files /dev/null and b/assets/images/machu_picchu/3.0x/photo-1.jpg differ diff --git a/assets/images/machu_picchu/3.0x/photo-2.jpg b/assets/images/machu_picchu/3.0x/photo-2.jpg new file mode 100755 index 00000000..991dce6b Binary files /dev/null and b/assets/images/machu_picchu/3.0x/photo-2.jpg differ diff --git a/assets/images/machu_picchu/3.0x/photo-3.jpg b/assets/images/machu_picchu/3.0x/photo-3.jpg new file mode 100755 index 00000000..4bba5fba Binary files /dev/null and b/assets/images/machu_picchu/3.0x/photo-3.jpg differ diff --git a/assets/images/machu_picchu/3.0x/photo-4.jpg b/assets/images/machu_picchu/3.0x/photo-4.jpg new file mode 100755 index 00000000..07de2e6a Binary files /dev/null and b/assets/images/machu_picchu/3.0x/photo-4.jpg differ diff --git a/assets/images/machu_picchu/3.0x/sun.png b/assets/images/machu_picchu/3.0x/sun.png new file mode 100755 index 00000000..bc3ed152 Binary files /dev/null and b/assets/images/machu_picchu/3.0x/sun.png differ diff --git a/assets/images/machu_picchu/3.0x/wonder-button.png b/assets/images/machu_picchu/3.0x/wonder-button.png new file mode 100644 index 00000000..d609811f Binary files /dev/null and b/assets/images/machu_picchu/3.0x/wonder-button.png differ diff --git a/assets/images/machu_picchu/flattened.jpg b/assets/images/machu_picchu/flattened.jpg new file mode 100644 index 00000000..be8ed72d Binary files /dev/null and b/assets/images/machu_picchu/flattened.jpg differ diff --git a/assets/images/machu_picchu/foreground-back.png b/assets/images/machu_picchu/foreground-back.png new file mode 100755 index 00000000..31e26865 Binary files /dev/null and b/assets/images/machu_picchu/foreground-back.png differ diff --git a/assets/images/machu_picchu/foreground-front.png b/assets/images/machu_picchu/foreground-front.png new file mode 100755 index 00000000..b5a30465 Binary files /dev/null and b/assets/images/machu_picchu/foreground-front.png differ diff --git a/assets/images/machu_picchu/machu-picchu.png b/assets/images/machu_picchu/machu-picchu.png new file mode 100755 index 00000000..7e4ac825 Binary files /dev/null and b/assets/images/machu_picchu/machu-picchu.png differ diff --git a/assets/images/machu_picchu/photo-1.jpg b/assets/images/machu_picchu/photo-1.jpg new file mode 100755 index 00000000..925df476 Binary files /dev/null and b/assets/images/machu_picchu/photo-1.jpg differ diff --git a/assets/images/machu_picchu/photo-2.jpg b/assets/images/machu_picchu/photo-2.jpg new file mode 100755 index 00000000..191b7613 Binary files /dev/null and b/assets/images/machu_picchu/photo-2.jpg differ diff --git a/assets/images/machu_picchu/photo-3.jpg b/assets/images/machu_picchu/photo-3.jpg new file mode 100755 index 00000000..15ce1fb9 Binary files /dev/null and b/assets/images/machu_picchu/photo-3.jpg differ diff --git a/assets/images/machu_picchu/photo-4.jpg b/assets/images/machu_picchu/photo-4.jpg new file mode 100755 index 00000000..24338ccf Binary files /dev/null and b/assets/images/machu_picchu/photo-4.jpg differ diff --git a/assets/images/machu_picchu/sun.png b/assets/images/machu_picchu/sun.png new file mode 100755 index 00000000..44e0814c Binary files /dev/null and b/assets/images/machu_picchu/sun.png differ diff --git a/assets/images/machu_picchu/wonder-button.png b/assets/images/machu_picchu/wonder-button.png new file mode 100644 index 00000000..7907c8b8 Binary files /dev/null and b/assets/images/machu_picchu/wonder-button.png differ diff --git a/assets/images/petra/2.0x/candles.png b/assets/images/petra/2.0x/candles.png new file mode 100755 index 00000000..20878c18 Binary files /dev/null and b/assets/images/petra/2.0x/candles.png differ diff --git a/assets/images/petra/2.0x/flattened.jpg b/assets/images/petra/2.0x/flattened.jpg new file mode 100644 index 00000000..8358b7b5 Binary files /dev/null and b/assets/images/petra/2.0x/flattened.jpg differ diff --git a/assets/images/petra/2.0x/foreground-left.png b/assets/images/petra/2.0x/foreground-left.png new file mode 100644 index 00000000..01f251fc Binary files /dev/null and b/assets/images/petra/2.0x/foreground-left.png differ diff --git a/assets/images/petra/2.0x/foreground-right.png b/assets/images/petra/2.0x/foreground-right.png new file mode 100644 index 00000000..6a9f6833 Binary files /dev/null and b/assets/images/petra/2.0x/foreground-right.png differ diff --git a/assets/images/petra/2.0x/moon.png b/assets/images/petra/2.0x/moon.png new file mode 100755 index 00000000..21a7464a Binary files /dev/null and b/assets/images/petra/2.0x/moon.png differ diff --git a/assets/images/petra/2.0x/petra.png b/assets/images/petra/2.0x/petra.png new file mode 100644 index 00000000..0a5fa7e0 Binary files /dev/null and b/assets/images/petra/2.0x/petra.png differ diff --git a/assets/images/petra/2.0x/photo-1.jpg b/assets/images/petra/2.0x/photo-1.jpg new file mode 100755 index 00000000..a0ea8fd4 Binary files /dev/null and b/assets/images/petra/2.0x/photo-1.jpg differ diff --git a/assets/images/petra/2.0x/photo-2.jpg b/assets/images/petra/2.0x/photo-2.jpg new file mode 100755 index 00000000..c5f107ec Binary files /dev/null and b/assets/images/petra/2.0x/photo-2.jpg differ diff --git a/assets/images/petra/2.0x/photo-3.jpg b/assets/images/petra/2.0x/photo-3.jpg new file mode 100755 index 00000000..fae39eee Binary files /dev/null and b/assets/images/petra/2.0x/photo-3.jpg differ diff --git a/assets/images/petra/2.0x/photo-4.jpg b/assets/images/petra/2.0x/photo-4.jpg new file mode 100755 index 00000000..b3bd34da Binary files /dev/null and b/assets/images/petra/2.0x/photo-4.jpg differ diff --git a/assets/images/petra/2.0x/wonder-button.png b/assets/images/petra/2.0x/wonder-button.png new file mode 100644 index 00000000..b99cfe74 Binary files /dev/null and b/assets/images/petra/2.0x/wonder-button.png differ diff --git a/assets/images/petra/3.0x/candles.png b/assets/images/petra/3.0x/candles.png new file mode 100755 index 00000000..4ef493f4 Binary files /dev/null and b/assets/images/petra/3.0x/candles.png differ diff --git a/assets/images/petra/3.0x/flattened.jpg b/assets/images/petra/3.0x/flattened.jpg new file mode 100644 index 00000000..1b0b80ae Binary files /dev/null and b/assets/images/petra/3.0x/flattened.jpg differ diff --git a/assets/images/petra/3.0x/foreground-left.png b/assets/images/petra/3.0x/foreground-left.png new file mode 100644 index 00000000..803ef67c Binary files /dev/null and b/assets/images/petra/3.0x/foreground-left.png differ diff --git a/assets/images/petra/3.0x/foreground-right.png b/assets/images/petra/3.0x/foreground-right.png new file mode 100644 index 00000000..9dac57ab Binary files /dev/null and b/assets/images/petra/3.0x/foreground-right.png differ diff --git a/assets/images/petra/3.0x/moon.png b/assets/images/petra/3.0x/moon.png new file mode 100755 index 00000000..5a8fc452 Binary files /dev/null and b/assets/images/petra/3.0x/moon.png differ diff --git a/assets/images/petra/3.0x/petra.png b/assets/images/petra/3.0x/petra.png new file mode 100644 index 00000000..6b41938f Binary files /dev/null and b/assets/images/petra/3.0x/petra.png differ diff --git a/assets/images/petra/3.0x/photo-1.jpg b/assets/images/petra/3.0x/photo-1.jpg new file mode 100755 index 00000000..87f105ca Binary files /dev/null and b/assets/images/petra/3.0x/photo-1.jpg differ diff --git a/assets/images/petra/3.0x/photo-2.jpg b/assets/images/petra/3.0x/photo-2.jpg new file mode 100755 index 00000000..3b2b3e6c Binary files /dev/null and b/assets/images/petra/3.0x/photo-2.jpg differ diff --git a/assets/images/petra/3.0x/photo-3.jpg b/assets/images/petra/3.0x/photo-3.jpg new file mode 100755 index 00000000..967008fb Binary files /dev/null and b/assets/images/petra/3.0x/photo-3.jpg differ diff --git a/assets/images/petra/3.0x/photo-4.jpg b/assets/images/petra/3.0x/photo-4.jpg new file mode 100755 index 00000000..15581caa Binary files /dev/null and b/assets/images/petra/3.0x/photo-4.jpg differ diff --git a/assets/images/petra/3.0x/wonder-button.png b/assets/images/petra/3.0x/wonder-button.png new file mode 100644 index 00000000..6c0d79e1 Binary files /dev/null and b/assets/images/petra/3.0x/wonder-button.png differ diff --git a/assets/images/petra/candles.png b/assets/images/petra/candles.png new file mode 100755 index 00000000..24af0997 Binary files /dev/null and b/assets/images/petra/candles.png differ diff --git a/assets/images/petra/flattened.jpg b/assets/images/petra/flattened.jpg new file mode 100644 index 00000000..6c7a5eb0 Binary files /dev/null and b/assets/images/petra/flattened.jpg differ diff --git a/assets/images/petra/foreground-left.png b/assets/images/petra/foreground-left.png new file mode 100644 index 00000000..1ea8b56b Binary files /dev/null and b/assets/images/petra/foreground-left.png differ diff --git a/assets/images/petra/foreground-right.png b/assets/images/petra/foreground-right.png new file mode 100644 index 00000000..364a19f3 Binary files /dev/null and b/assets/images/petra/foreground-right.png differ diff --git a/assets/images/petra/moon.png b/assets/images/petra/moon.png new file mode 100755 index 00000000..dda8d8b0 Binary files /dev/null and b/assets/images/petra/moon.png differ diff --git a/assets/images/petra/petra.png b/assets/images/petra/petra.png new file mode 100644 index 00000000..fb4f1f58 Binary files /dev/null and b/assets/images/petra/petra.png differ diff --git a/assets/images/petra/photo-1.jpg b/assets/images/petra/photo-1.jpg new file mode 100755 index 00000000..68e2a528 Binary files /dev/null and b/assets/images/petra/photo-1.jpg differ diff --git a/assets/images/petra/photo-2.jpg b/assets/images/petra/photo-2.jpg new file mode 100755 index 00000000..91f4fc54 Binary files /dev/null and b/assets/images/petra/photo-2.jpg differ diff --git a/assets/images/petra/photo-3.jpg b/assets/images/petra/photo-3.jpg new file mode 100755 index 00000000..2b1d3fbd Binary files /dev/null and b/assets/images/petra/photo-3.jpg differ diff --git a/assets/images/petra/photo-4.jpg b/assets/images/petra/photo-4.jpg new file mode 100755 index 00000000..501d527a Binary files /dev/null and b/assets/images/petra/photo-4.jpg differ diff --git a/assets/images/petra/wonder-button.png b/assets/images/petra/wonder-button.png new file mode 100644 index 00000000..bed40bd3 Binary files /dev/null and b/assets/images/petra/wonder-button.png differ diff --git a/assets/images/pyramids/2.0x/flattened.jpg b/assets/images/pyramids/2.0x/flattened.jpg new file mode 100644 index 00000000..ac320503 Binary files /dev/null and b/assets/images/pyramids/2.0x/flattened.jpg differ diff --git a/assets/images/pyramids/2.0x/foreground-back.png b/assets/images/pyramids/2.0x/foreground-back.png new file mode 100755 index 00000000..f2cdb1c4 Binary files /dev/null and b/assets/images/pyramids/2.0x/foreground-back.png differ diff --git a/assets/images/pyramids/2.0x/foreground-front.png b/assets/images/pyramids/2.0x/foreground-front.png new file mode 100755 index 00000000..5c55685d Binary files /dev/null and b/assets/images/pyramids/2.0x/foreground-front.png differ diff --git a/assets/images/pyramids/2.0x/moon.png b/assets/images/pyramids/2.0x/moon.png new file mode 100755 index 00000000..9067e590 Binary files /dev/null and b/assets/images/pyramids/2.0x/moon.png differ diff --git a/assets/images/pyramids/2.0x/photo-1.jpg b/assets/images/pyramids/2.0x/photo-1.jpg new file mode 100644 index 00000000..61772772 Binary files /dev/null and b/assets/images/pyramids/2.0x/photo-1.jpg differ diff --git a/assets/images/pyramids/2.0x/photo-2.jpg b/assets/images/pyramids/2.0x/photo-2.jpg new file mode 100644 index 00000000..dead34e7 Binary files /dev/null and b/assets/images/pyramids/2.0x/photo-2.jpg differ diff --git a/assets/images/pyramids/2.0x/photo-3.jpg b/assets/images/pyramids/2.0x/photo-3.jpg new file mode 100644 index 00000000..972ac34e Binary files /dev/null and b/assets/images/pyramids/2.0x/photo-3.jpg differ diff --git a/assets/images/pyramids/2.0x/photo-4.jpg b/assets/images/pyramids/2.0x/photo-4.jpg new file mode 100644 index 00000000..2e8bafd3 Binary files /dev/null and b/assets/images/pyramids/2.0x/photo-4.jpg differ diff --git a/assets/images/pyramids/2.0x/pyramids.png b/assets/images/pyramids/2.0x/pyramids.png new file mode 100755 index 00000000..9c4f77b0 Binary files /dev/null and b/assets/images/pyramids/2.0x/pyramids.png differ diff --git a/assets/images/pyramids/2.0x/wonder-button.png b/assets/images/pyramids/2.0x/wonder-button.png new file mode 100644 index 00000000..49624a0c Binary files /dev/null and b/assets/images/pyramids/2.0x/wonder-button.png differ diff --git a/assets/images/pyramids/3.0x/flattened.jpg b/assets/images/pyramids/3.0x/flattened.jpg new file mode 100644 index 00000000..51d27cad Binary files /dev/null and b/assets/images/pyramids/3.0x/flattened.jpg differ diff --git a/assets/images/pyramids/3.0x/foreground-back.png b/assets/images/pyramids/3.0x/foreground-back.png new file mode 100755 index 00000000..e57f5900 Binary files /dev/null and b/assets/images/pyramids/3.0x/foreground-back.png differ diff --git a/assets/images/pyramids/3.0x/foreground-front.png b/assets/images/pyramids/3.0x/foreground-front.png new file mode 100755 index 00000000..5fd4678e Binary files /dev/null and b/assets/images/pyramids/3.0x/foreground-front.png differ diff --git a/assets/images/pyramids/3.0x/moon.png b/assets/images/pyramids/3.0x/moon.png new file mode 100755 index 00000000..330738d9 Binary files /dev/null and b/assets/images/pyramids/3.0x/moon.png differ diff --git a/assets/images/pyramids/3.0x/photo-1.jpg b/assets/images/pyramids/3.0x/photo-1.jpg new file mode 100644 index 00000000..89ab35b0 Binary files /dev/null and b/assets/images/pyramids/3.0x/photo-1.jpg differ diff --git a/assets/images/pyramids/3.0x/photo-2.jpg b/assets/images/pyramids/3.0x/photo-2.jpg new file mode 100644 index 00000000..f066fbbd Binary files /dev/null and b/assets/images/pyramids/3.0x/photo-2.jpg differ diff --git a/assets/images/pyramids/3.0x/photo-3.jpg b/assets/images/pyramids/3.0x/photo-3.jpg new file mode 100644 index 00000000..a7024821 Binary files /dev/null and b/assets/images/pyramids/3.0x/photo-3.jpg differ diff --git a/assets/images/pyramids/3.0x/photo-4.jpg b/assets/images/pyramids/3.0x/photo-4.jpg new file mode 100644 index 00000000..6c3fd34e Binary files /dev/null and b/assets/images/pyramids/3.0x/photo-4.jpg differ diff --git a/assets/images/pyramids/3.0x/pyramids.png b/assets/images/pyramids/3.0x/pyramids.png new file mode 100755 index 00000000..3adf771a Binary files /dev/null and b/assets/images/pyramids/3.0x/pyramids.png differ diff --git a/assets/images/pyramids/3.0x/wonder-button.png b/assets/images/pyramids/3.0x/wonder-button.png new file mode 100644 index 00000000..dfa1c5b5 Binary files /dev/null and b/assets/images/pyramids/3.0x/wonder-button.png differ diff --git a/assets/images/pyramids/flattened.jpg b/assets/images/pyramids/flattened.jpg new file mode 100644 index 00000000..3c1f56a4 Binary files /dev/null and b/assets/images/pyramids/flattened.jpg differ diff --git a/assets/images/pyramids/foreground-back.png b/assets/images/pyramids/foreground-back.png new file mode 100755 index 00000000..5ad424c9 Binary files /dev/null and b/assets/images/pyramids/foreground-back.png differ diff --git a/assets/images/pyramids/foreground-front.png b/assets/images/pyramids/foreground-front.png new file mode 100755 index 00000000..289cab92 Binary files /dev/null and b/assets/images/pyramids/foreground-front.png differ diff --git a/assets/images/pyramids/moon.png b/assets/images/pyramids/moon.png new file mode 100755 index 00000000..49948fb8 Binary files /dev/null and b/assets/images/pyramids/moon.png differ diff --git a/assets/images/pyramids/photo-1.jpg b/assets/images/pyramids/photo-1.jpg new file mode 100644 index 00000000..7ea96219 Binary files /dev/null and b/assets/images/pyramids/photo-1.jpg differ diff --git a/assets/images/pyramids/photo-2.jpg b/assets/images/pyramids/photo-2.jpg new file mode 100644 index 00000000..0b47f94d Binary files /dev/null and b/assets/images/pyramids/photo-2.jpg differ diff --git a/assets/images/pyramids/photo-3.jpg b/assets/images/pyramids/photo-3.jpg new file mode 100644 index 00000000..1baeb934 Binary files /dev/null and b/assets/images/pyramids/photo-3.jpg differ diff --git a/assets/images/pyramids/photo-4.jpg b/assets/images/pyramids/photo-4.jpg new file mode 100644 index 00000000..24b8770a Binary files /dev/null and b/assets/images/pyramids/photo-4.jpg differ diff --git a/assets/images/pyramids/pyramids.png b/assets/images/pyramids/pyramids.png new file mode 100755 index 00000000..d1d7fe9a Binary files /dev/null and b/assets/images/pyramids/pyramids.png differ diff --git a/assets/images/pyramids/wonder-button.png b/assets/images/pyramids/wonder-button.png new file mode 100644 index 00000000..e11d6916 Binary files /dev/null and b/assets/images/pyramids/wonder-button.png differ diff --git a/assets/images/taj_mahal/2.0x/flattened.jpg b/assets/images/taj_mahal/2.0x/flattened.jpg new file mode 100644 index 00000000..491381b1 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/flattened.jpg differ diff --git a/assets/images/taj_mahal/2.0x/foreground-left.png b/assets/images/taj_mahal/2.0x/foreground-left.png new file mode 100755 index 00000000..41df9726 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/foreground-left.png differ diff --git a/assets/images/taj_mahal/2.0x/foreground-right.png b/assets/images/taj_mahal/2.0x/foreground-right.png new file mode 100755 index 00000000..2fb96a70 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/foreground-right.png differ diff --git a/assets/images/taj_mahal/2.0x/photo-1.jpg b/assets/images/taj_mahal/2.0x/photo-1.jpg new file mode 100644 index 00000000..6489215f Binary files /dev/null and b/assets/images/taj_mahal/2.0x/photo-1.jpg differ diff --git a/assets/images/taj_mahal/2.0x/photo-2.jpg b/assets/images/taj_mahal/2.0x/photo-2.jpg new file mode 100644 index 00000000..22fee3d7 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/photo-2.jpg differ diff --git a/assets/images/taj_mahal/2.0x/photo-3.jpg b/assets/images/taj_mahal/2.0x/photo-3.jpg new file mode 100644 index 00000000..4d2c1622 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/photo-3.jpg differ diff --git a/assets/images/taj_mahal/2.0x/photo-4.jpg b/assets/images/taj_mahal/2.0x/photo-4.jpg new file mode 100644 index 00000000..a5f71f75 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/photo-4.jpg differ diff --git a/assets/images/taj_mahal/2.0x/pool.png b/assets/images/taj_mahal/2.0x/pool.png new file mode 100755 index 00000000..b3b997e3 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/pool.png differ diff --git a/assets/images/taj_mahal/2.0x/sun.png b/assets/images/taj_mahal/2.0x/sun.png new file mode 100755 index 00000000..aafe0c3d Binary files /dev/null and b/assets/images/taj_mahal/2.0x/sun.png differ diff --git a/assets/images/taj_mahal/2.0x/taj-mahal.png b/assets/images/taj_mahal/2.0x/taj-mahal.png new file mode 100755 index 00000000..19175141 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/taj-mahal.png differ diff --git a/assets/images/taj_mahal/2.0x/wonder-button.png b/assets/images/taj_mahal/2.0x/wonder-button.png new file mode 100644 index 00000000..87bb4025 Binary files /dev/null and b/assets/images/taj_mahal/2.0x/wonder-button.png differ diff --git a/assets/images/taj_mahal/3.0x/flattened.jpg b/assets/images/taj_mahal/3.0x/flattened.jpg new file mode 100644 index 00000000..559b2169 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/flattened.jpg differ diff --git a/assets/images/taj_mahal/3.0x/foreground-left.png b/assets/images/taj_mahal/3.0x/foreground-left.png new file mode 100755 index 00000000..c9c2aca0 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/foreground-left.png differ diff --git a/assets/images/taj_mahal/3.0x/foreground-right.png b/assets/images/taj_mahal/3.0x/foreground-right.png new file mode 100755 index 00000000..0a81e249 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/foreground-right.png differ diff --git a/assets/images/taj_mahal/3.0x/photo-1.jpg b/assets/images/taj_mahal/3.0x/photo-1.jpg new file mode 100755 index 00000000..d74f4cb5 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/photo-1.jpg differ diff --git a/assets/images/taj_mahal/3.0x/photo-2.jpg b/assets/images/taj_mahal/3.0x/photo-2.jpg new file mode 100755 index 00000000..88fcf279 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/photo-2.jpg differ diff --git a/assets/images/taj_mahal/3.0x/photo-3.jpg b/assets/images/taj_mahal/3.0x/photo-3.jpg new file mode 100755 index 00000000..3407918c Binary files /dev/null and b/assets/images/taj_mahal/3.0x/photo-3.jpg differ diff --git a/assets/images/taj_mahal/3.0x/photo-4.jpg b/assets/images/taj_mahal/3.0x/photo-4.jpg new file mode 100755 index 00000000..81f58885 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/photo-4.jpg differ diff --git a/assets/images/taj_mahal/3.0x/pool.png b/assets/images/taj_mahal/3.0x/pool.png new file mode 100755 index 00000000..db823de4 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/pool.png differ diff --git a/assets/images/taj_mahal/3.0x/sun.png b/assets/images/taj_mahal/3.0x/sun.png new file mode 100755 index 00000000..d6337f6c Binary files /dev/null and b/assets/images/taj_mahal/3.0x/sun.png differ diff --git a/assets/images/taj_mahal/3.0x/taj-mahal.png b/assets/images/taj_mahal/3.0x/taj-mahal.png new file mode 100755 index 00000000..c66ecb79 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/taj-mahal.png differ diff --git a/assets/images/taj_mahal/3.0x/wonder-button.png b/assets/images/taj_mahal/3.0x/wonder-button.png new file mode 100644 index 00000000..85a418f0 Binary files /dev/null and b/assets/images/taj_mahal/3.0x/wonder-button.png differ diff --git a/assets/images/taj_mahal/flattened.jpg b/assets/images/taj_mahal/flattened.jpg new file mode 100644 index 00000000..39b752b4 Binary files /dev/null and b/assets/images/taj_mahal/flattened.jpg differ diff --git a/assets/images/taj_mahal/foreground-left.png b/assets/images/taj_mahal/foreground-left.png new file mode 100755 index 00000000..42388139 Binary files /dev/null and b/assets/images/taj_mahal/foreground-left.png differ diff --git a/assets/images/taj_mahal/foreground-right.png b/assets/images/taj_mahal/foreground-right.png new file mode 100755 index 00000000..f10ff261 Binary files /dev/null and b/assets/images/taj_mahal/foreground-right.png differ diff --git a/assets/images/taj_mahal/photo-1.jpg b/assets/images/taj_mahal/photo-1.jpg new file mode 100644 index 00000000..0c060da1 Binary files /dev/null and b/assets/images/taj_mahal/photo-1.jpg differ diff --git a/assets/images/taj_mahal/photo-2.jpg b/assets/images/taj_mahal/photo-2.jpg new file mode 100644 index 00000000..b3ce452c Binary files /dev/null and b/assets/images/taj_mahal/photo-2.jpg differ diff --git a/assets/images/taj_mahal/photo-3.jpg b/assets/images/taj_mahal/photo-3.jpg new file mode 100644 index 00000000..ff3db48b Binary files /dev/null and b/assets/images/taj_mahal/photo-3.jpg differ diff --git a/assets/images/taj_mahal/photo-4.jpg b/assets/images/taj_mahal/photo-4.jpg new file mode 100644 index 00000000..2fdc5e7e Binary files /dev/null and b/assets/images/taj_mahal/photo-4.jpg differ diff --git a/assets/images/taj_mahal/pool.png b/assets/images/taj_mahal/pool.png new file mode 100755 index 00000000..ad544f5e Binary files /dev/null and b/assets/images/taj_mahal/pool.png differ diff --git a/assets/images/taj_mahal/sun.png b/assets/images/taj_mahal/sun.png new file mode 100755 index 00000000..a51f4bfe Binary files /dev/null and b/assets/images/taj_mahal/sun.png differ diff --git a/assets/images/taj_mahal/taj-mahal.png b/assets/images/taj_mahal/taj-mahal.png new file mode 100755 index 00000000..ef85fd98 Binary files /dev/null and b/assets/images/taj_mahal/taj-mahal.png differ diff --git a/assets/images/taj_mahal/wonder-button.png b/assets/images/taj_mahal/wonder-button.png new file mode 100644 index 00000000..9c161fe4 Binary files /dev/null and b/assets/images/taj_mahal/wonder-button.png differ diff --git a/assets/marketing/app-icon-1024.png b/assets/marketing/app-icon-1024.png new file mode 100644 index 00000000..e87d2478 Binary files /dev/null and b/assets/marketing/app-icon-1024.png differ diff --git a/assets/marketing/app-icon-512.png b/assets/marketing/app-icon-512.png new file mode 100644 index 00000000..af9dee0a Binary files /dev/null and b/assets/marketing/app-icon-512.png differ diff --git a/assets/marketing/feature_1024_500.jpg b/assets/marketing/feature_1024_500.jpg new file mode 100644 index 00000000..32fde6fe Binary files /dev/null and b/assets/marketing/feature_1024_500.jpg differ diff --git a/assets/marketing/logo-wonderous.svg b/assets/marketing/logo-wonderous.svg new file mode 100644 index 00000000..4d4637dd --- /dev/null +++ b/assets/marketing/logo-wonderous.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/flutter_native_splash.yaml b/flutter_native_splash.yaml new file mode 100644 index 00000000..3d3ba08c --- /dev/null +++ b/flutter_native_splash.yaml @@ -0,0 +1,128 @@ +flutter_native_splash: + # This package generates native code to customize Flutter's default white native splash screen + # with background color and splash image. + # Customize the parameters below, and run the following command in the terminal: + # flutter pub run flutter_native_splash:create + # To restore Flutter's default white splash screen, run the following command in the terminal: + # flutter pub run flutter_native_splash:remove + + # color or background_image is the only required parameter. Use color to set the background + # of your splash screen to a solid color. Use background_image to set the background of your + # splash screen to a png image. This is useful for gradients. The image will be stretch to the + # size of the app. Only one parameter can be used, color and background_image cannot both be set. + color: "#272625" + #background_image: "assets/background.png" + + # Optional parameters are listed below. To enable a parameter, uncomment the line by removing + # the leading # character. + + # The image parameter allows you to specify an image used in the splash screen. It must be a + # png file and should be sized for 4x pixel density. + image: assets/images/_common/app-logo.png + + # The branding property allows you to specify an image used as branding in the splash screen. + # It must be a png file. It is supported for Android < v12, iOS and the Web. + #branding: + + # To position the branding image at the bottom of the screen you can use bottom, bottomRight, + # and bottomLeft. The default values is bottom if not specified or specified something else. + #branding_mode: bottom + + # The color_dark, background_image_dark, image_dark, branding_dark are parameters that set the background + # and image when the device is in dark mode. If they are not specified, the app will use the + # parameters from above. If the image_dark parameter is specified, color_dark or + # background_image_dark must be specified. color_dark and background_image_dark cannot both be + # set. + #color_dark: "#042a49" + #background_image_dark: "assets/dark-background.png" + #image_dark: assets/splash-invert.png + #branding_dark: assets/dart_dark.png + + # Android 12 handles the splash screen differently than previous versions. Please visit + # https://developer.android.com/guide/topics/ui/splash-screen + # Following are Android 12 specific parameter. + android_12: + # The image parameter sets the splash screen icon image. If this parameter is not specified, + # the app's launcher icon will be used instead. + # Please note that the splash screen will be clipped to a circle on the center of the screen. + # App icon with an icon background: This should be 960×960 pixels, and fit within a circle + # 640 pixels in diameter. + # App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle + # 768 pixels in diameter. + #image: assets/android12splash.png + + # Splash screen background color. + #color: "#42a5f5" + + # App icon background color. + #icon_background_color: "#111111" + + # The image_dark parameter and icon_background_color_dark set the image and icon background + # color when the device is in dark mode. If they are not specified, the app will use the + # parameters from above. + #image_dark: assets/android12splash-invert.png + #color_dark: "#042a49" + #icon_background_color_dark: "#eeeeee" + + # The android, ios and web parameters can be used to disable generating a splash screen on a given + # platform. + #android: false + #ios: false + #web: false + + # Platform specific images can be specified with the following parameters, which will override + # the respective image parameter. You may specify all, selected, or none of these parameters: + #image_andriod: assets/splash-andriod.png + #image_dark_android: assets/splash-invert-android.png + #image_ios: assets/splash-ios.png + #image_dark_ios: assets/splash-invert-ios.png + #image_web: assets/splash-web.png + #image_dark_web: assets/splash-invert-web.png + #background_image_android: "assets/background-android.png" + #background_image_dark_android: "assets/dark-background-android.png" + #background_image_ios: "assets/background-ios.png" + #background_image_dark_ios: "assets/dark-background-ios.png" + #background_image_web: "assets/background-web.png" + #background_image_dark_web: "assets/dark-background-web.png" + #branding_andriod: assets/brand-android.png + #branding_dark_android: assets/dart_dark-android.png + #branding_ios: assets/brand-ios.png + #branding_dark_ios: assets/dart_dark-ios.png + + # The position of the splash image can be set with android_gravity, ios_content_mode, and + # web_image_mode parameters. All default to center. + # + # android_gravity can be one of the following Android Gravity (see + # https://developer.android.com/reference/android/view/Gravity): bottom, center, + # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, + # fill_vertical, left, right, start, or top. + #android_gravity: center + # + # ios_content_mode can be one of the following iOS UIView.ContentMode (see + # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, + # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, + # bottomLeft, or bottomRight. + #ios_content_mode: center + # + # web_image_mode can be one of the following modes: center, contain, stretch, and cover. + #web_image_mode: center + + # The screen orientation can be set in Android with the android_screen_orientation parameter. + # Valid parameters can be found here: + # https://developer.android.com/guide/topics/manifest/activity-element#screen + #android_screen_orientation: sensorLandscape + + # To hide the notification bar, use the fullscreen parameter. Has no effect in web since web + # has no notification bar. Defaults to false. + # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. + # To show the notification bar, add the following code to your Flutter app: + # WidgetsFlutterBinding.ensureInitialized(); + # SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]); + #fullscreen: true + + # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) + # with the info_plist_files parameter. Remove only the # characters in the three lines below, + # do not remove any spaces: + #info_plist_files: + # - 'ios/Runner/Info-Debug.plist' + # - 'ios/Runner/Info-Release.plist' \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/.ruby-version b/ios/.ruby-version new file mode 100644 index 00000000..a603bb50 --- /dev/null +++ b/ios/.ruby-version @@ -0,0 +1 @@ +2.7.5 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9625e105 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..ec97fc6f --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..c4855bfe --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..88359b22 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..3f3bb808 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,95 @@ +PODS: + - Flutter (1.0.0) + - flutter_inappwebview (0.0.1): + - Flutter + - flutter_inappwebview/Core (= 0.0.1) + - OrderedSet (~> 5.0) + - flutter_inappwebview/Core (0.0.1): + - Flutter + - OrderedSet (~> 5.0) + - flutter_native_splash (0.0.1): + - Flutter + - google_maps_flutter_ios (0.0.1): + - Flutter + - GoogleMaps + - GoogleMaps (6.2.1): + - GoogleMaps/Maps (= 6.2.1) + - GoogleMaps/Base (6.2.1) + - GoogleMaps/Maps (6.2.1): + - GoogleMaps/Base + - image_gallery_saver (1.5.0): + - Flutter + - integration_test (0.0.1): + - Flutter + - OrderedSet (5.0.0) + - package_info_plus (0.4.5): + - Flutter + - path_provider_ios (0.0.1): + - Flutter + - share_plus (0.0.1): + - Flutter + - shared_preferences_ios (0.0.1): + - Flutter + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`) + - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) + - integration_test (from `.symlinks/plugins/integration_test/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - GoogleMaps + - OrderedSet + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + flutter_inappwebview: + :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_native_splash: + :path: ".symlinks/plugins/flutter_native_splash/ios" + google_maps_flutter_ios: + :path: ".symlinks/plugins/google_maps_flutter_ios/ios" + image_gallery_saver: + :path: ".symlinks/plugins/image_gallery_saver/ios" + integration_test: + :path: ".symlinks/plugins/integration_test/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 + flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef + google_maps_flutter_ios: 66201f392bf62d500f07670a30488a247b9bb5b9 + GoogleMaps: 20d7b12be49a14287f797e88e0e31bc4156aaeb4 + image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 + integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 + OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 + shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 + +COCOAPODS: 1.11.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3df8d421 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,747 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 323DE3CFA8490EAB3C4E249C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A44ACC5DE81A9C3E5BDA151 /* Pods_Runner.framework */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E214FC8827C5A18E005F78FB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1475293CB8660AC785DF56AB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 4A44ACC5DE81A9C3E5BDA151 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 76D33FFF3E2F37E583228A7C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D54A22D234A57151F14B56DF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + E214FC8227C5A18D005F78FB /* wondersUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = wondersUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E2415C5B27D1451D00925491 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 323DE3CFA8490EAB3C4E249C /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E214FC7F27C5A18D005F78FB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5073AC1D92C10773F20D12A2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4A44ACC5DE81A9C3E5BDA151 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 5073AC1D92C10773F20D12A2 /* Frameworks */, + E090BB04291350D10AF9DE4E /* Pods */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + E214FC8227C5A18D005F78FB /* wondersUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + E2415C5B27D1451D00925491 /* Runner.entitlements */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + E090BB04291350D10AF9DE4E /* Pods */ = { + isa = PBXGroup; + children = ( + D54A22D234A57151F14B56DF /* Pods-Runner.debug.xcconfig */, + 76D33FFF3E2F37E583228A7C /* Pods-Runner.release.xcconfig */, + 1475293CB8660AC785DF56AB /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 1AEEFB7EEBB3E2D2CCB7D8D4 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 9DAF500633B345EE15CA82E0 /* [CP] Embed Pods Frameworks */, + B727DC94BF66FDBF036EDFA6 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + E214FC8127C5A18D005F78FB /* wondersUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E214FC8A27C5A18E005F78FB /* Build configuration list for PBXNativeTarget "wondersUITests" */; + buildPhases = ( + E214FC7E27C5A18D005F78FB /* Sources */, + E214FC7F27C5A18D005F78FB /* Frameworks */, + E214FC8027C5A18D005F78FB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E214FC8927C5A18E005F78FB /* PBXTargetDependency */, + ); + name = wondersUITests; + productName = wondersUITests; + productReference = E214FC8227C5A18D005F78FB /* wondersUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + KnownAssetTags = ( + New, + ); + LastSwiftUpdateCheck = 1320; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + E214FC8127C5A18D005F78FB = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + E214FC8127C5A18D005F78FB /* wondersUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E214FC8027C5A18D005F78FB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1AEEFB7EEBB3E2D2CCB7D8D4 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + 9DAF500633B345EE15CA82E0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B727DC94BF66FDBF036EDFA6 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E214FC7E27C5A18D005F78FB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E214FC8927C5A18E005F78FB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = E214FC8827C5A18E005F78FB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SYSTEM_FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.flutter.wonders; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SYSTEM_FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SYSTEM_FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.flutter.wonders; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.flutter.wonders; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + E214FC8B27C5A18E005F78FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.wonders.wondersUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + E214FC8C27C5A18E005F78FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.wonders.wondersUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; + E214FC8D27C5A18E005F78FB /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_TEAM = S3TL5AY6Y3; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.gskinner.wonders.wondersUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Profile; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E214FC8A27C5A18E005F78FB /* Build configuration list for PBXNativeTarget "wondersUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E214FC8B27C5A18E005F78FB /* Debug */, + E214FC8C27C5A18E005F78FB /* Release */, + E214FC8D27C5A18E005F78FB /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..31c6917c --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..71eef49a --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,15 @@ +import UIKit +import Flutter +import GoogleMaps + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GMSServices.provideAPIKey("AIzaSyDectGpXKc04T6U5MyccC7L5DVl0xbUFY4") + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} \ No newline at end of file diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..cd5b38ab Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..32899496 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..bb93149e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..5a339141 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..ba651531 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..ee9f36dc Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..97632531 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..bb93149e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..2e334f9f Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..aab535b9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 00000000..ee5d302c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 00000000..4bd5ba49 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 00000000..c32cc15d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 00000000..14a65033 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..aab535b9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..9749a918 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 00000000..44a240e7 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 00000000..670af3dd Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..3ce0239c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..c4304f40 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..dfa9fc70 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 00000000..9f447e1b --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 00000000..7619addd Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..00cabce8 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "LaunchImage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "LaunchImage@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "LaunchImage@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..1e856ca4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..b062141b Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..3d682bf7 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..6079d439 --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 00000000..975838b6 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,58 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Wonderous + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + zh_CN + + CFBundleName + wonders + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + FLTEnableImpeller + + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + Show a google maps view. + NSPhotoLibraryAddUsageDescription + Save wallpapers to the gallery! + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 00000000..903def2a --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 00000000..4e6692e5 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/lib/_tools/artifact_search_helper.dart b/lib/_tools/artifact_search_helper.dart new file mode 100644 index 00000000..4acd9644 --- /dev/null +++ b/lib/_tools/artifact_search_helper.dart @@ -0,0 +1,440 @@ +// ignore_for_file: unused_element + +import 'dart:async'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:path_provider/path_provider.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:http/http.dart' as http; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +final int minYear = wondersLogic.timelineStartYear; +final int maxYear = wondersLogic.timelineEndYear; +const int maxRequests = 32; + +class ArtifactSearchHelper extends StatefulWidget { + const ArtifactSearchHelper({Key? key}) : super(key: key); + + @override + State createState() => _ArtifactSearchHelperState(); +} + +class _ArtifactSearchHelperState extends State { + String selectedWonder = 'All'; + int maxIds = 500, maxPriority = 200; + bool checkImages = true; + + List wonderQueue = []; + WonderData? wonder; + + List queryQueue = []; + bool priority = false; + + List idQueue = []; + HashSet idSet = HashSet(); + HashMap> errors = HashMap(); + List entries = []; + + http.Client _http = http.Client(); + int activeRequestCount = 0; + List log = []; + Stopwatch timer = Stopwatch(); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: Padding( + padding: EdgeInsets.all(32.0), + child: _buildContent(context), + ), + ), + ); + } + + @override + void dispose() { + _http.close(); + super.dispose(); + } + + void _run() { + // reset: + errors.clear(); + log.clear(); + timer + ..reset() + ..start(); + + if (selectedWonder == 'All') { + wonderQueue = wondersLogic.all.toList(); + } else { + wonderQueue = [wondersLogic.all.firstWhere((o) => o.title == selectedWonder)]; + } + _log('Loading data for ${wonderQueue.length} wonders'); + _http = http.Client(); + _nextWonder(); + } + + void _nextWonder() { + // reset: + idQueue.clear(); + idSet.clear(); + entries.clear(); + activeRequestCount = 0; + + if (wonderQueue.isEmpty) { + return _complete(); + } + wonder = wonderQueue.removeAt(0); + _log('\n${wonder!.title}'); + queryQueue = queries[wonder!.type]!.toList(); + _nextQuery(); + } + + Future _nextQuery() async { + if (queryQueue.isEmpty) { + return _runIds(); + } + String query = queryQueue.removeAt(0); + priority = query[0] == '!'; + if (priority) query = query.substring(1); + _log('${priority ? '*' : '-'} $query'); + + Uri uri = Uri.parse(_baseQueryUri + query); + http.Response response = await _http.get(uri); + Map json = jsonDecode(response.body) as Map; + List ids = json['objectIDs']; + + int count = priority ? maxPriority : maxIds; + count = min(ids.length, min(count, maxIds - idQueue.length)); + int foundCount = 0; + + for (int i = 0; i < ids.length && foundCount < count; i++) { + if (idSet.add(ids[i] as int)) ++foundCount; + } + idQueue = idSet.toList(); + + _log(' - ${ids.length} artifacts found, added $foundCount'); + + _nextQuery(); + } + + void _runIds() { + int count = min(maxRequests, idQueue.length); + _log('- Loading data for ${idQueue.length} artifacts'); + if (count == 0) { + _completeIds(); + return; + } + while (count-- > 0) { + _nextId(); + } + } + + Future _nextId() async { + if (idQueue.isEmpty) return; + activeRequestCount++; + int id = idQueue.removeLast(); + Uri uri = Uri.parse(_baseArtifactUri + id.toString()); + http.Response response = await _http.get(uri); + if (response.statusCode != 200) { + _logError(id, 'bad status code ${response.statusCode}'); + } else { + Map? json = jsonDecode(response.body) as Map?; + await _parseId(id, json); + } + + _completeId(); + } + + Future _parseId(int id, Map? json) async { + // catch all error conditions: + if (json == null) return _logError(id, 'could not parse json'); + if ((json['title'] ?? '') == '') return _logError(id, 'missing title'); + if (!json.containsKey('objectBeginDate') || !json.containsKey('objectBeginDate')) { + return _logError(id, 'missing years'); + } + //if (!json.containsKey('isPublicDomain') || !json['isPublicDomain']) return _logError(id, 'not public domain') + + final int year = ((json['objectBeginDate'] as int) + (json['objectEndDate'] as int)) ~/ 2; + if (year < minYear || year > maxYear) return _logError(id, 'year is out of range'); + + String? imageUrlSmall = json['primaryImageSmall']; + if (imageUrlSmall == null) return _logError(id, 'no small image url'); + if (!imageUrlSmall.startsWith(SearchData.baseImagePath)) { + return _logError(id, 'unexpected image uri: "$imageUrlSmall"'); + } + String imagePath = imageUrlSmall.substring(SearchData.baseImagePath.length); + imagePath = imagePath.replaceFirst('/web-large/', '/mobile-large/'); + + double? aspectRatio = 0; + if (checkImages) aspectRatio = await _getAspectRatio(imageUrlSmall); + if (aspectRatio == null) return _logError(id, 'image failed to load'); + + SearchData entry = SearchData( + year, + id, + _escape(json['title']), + _getKeywords(json), + imagePath, + aspectRatio, + ); + + entries.add(entry); + } + + Future _getAspectRatio(String imagePath) async { + Completer completer = Completer(); + NetworkImage image = NetworkImage(imagePath); + ImageStream stream = image.resolve(ImageConfiguration()); + stream.addListener(ImageStreamListener( + (info, _) => completer.complete(info.image.width / info.image.height), + onError: (_, __) => completer.complete(null), + )); + return completer.future; + } + + String _getKeywords(Map json) { + String str = '${json['objectName'] ?? ''}|${json['medium'] ?? ''}|${json['classification'] ?? ''}'; + return _escape(str.toLowerCase()); + } + + String _escape(String str) => str.replaceAll("'", "\\'").replaceAll('\r', ' ').replaceAll('\n', ' '); + + void _completeId() { + --activeRequestCount; + if (idQueue.isNotEmpty) { + _nextId(); + } else if (activeRequestCount == 0) { + _completeIds(); + } + } + + Future _completeIds() async { + _log('- Created ${entries.length} entries'); + + entries.shuffle(); + + // build output: + String entryStr = ''; + for (int i = 0; i < entries.length; i++) { + entryStr += ' ${entries[i].write()},\n'; + } + + String output = '// ${wonder!.title} (${entries.length})\nList _searchData = const [\n$entryStr];'; + + String suggestions = _getSuggestions(entries); + + Directory dir = await getApplicationDocumentsDirectory(); + String type = wonder!.type.toString().split('.').last; + String path = '${dir.path}/$type.dart'; + File file = File(path); + await file.writeAsString('$suggestions\n\n$output'); + _log('- Wrote file: $type.dart'); + debugPrint(path); + _nextWonder(); + } + + void _complete() { + _log('\n----------\nCompleted with ${errors.length} unique errors in ${timer.elapsed.inSeconds} seconds.'); + String errorStr = ''; + errors.forEach((key, value) { + errorStr += '$key (${value.length})\n'; + }); + _log(errorStr); + timer.stop(); + _http.close(); + } + + void _log(String str) { + log.add(str); + setState(() {}); + } + + void _logError(int id, String str) { + if (!errors.containsKey(str)) errors[str] = []; + errors[str]!.add(id); + } + + void _runSuggestions() { + if (selectedWonder == 'All') { + debugPrint('select a single wonder'); + } else { + WonderData wonder = wondersLogic.all.firstWhere((o) => o.title == selectedWonder); + debugPrint(_getSuggestions(wonder.searchData)); + } + } + + String _getSuggestions(List data) { + HashMap counts = HashMap(); + HashSet ignore = HashSet(); + + // iterate through all items, and count the number of times keywords show up + // but don't count multiple times for a single item + for (int i = 0; i < data.length; i++) { + ignore.clear(); + ignore.addAll([ + 'and', + 'the', + 'with', + 'from', + 'for', + 'form', + 'probably', + 'back', + 'front', + 'under', + 'his', + 'one', + 'two', + 'three', + 'four', + 'part', + 'called', + 'over' + ]); + SearchData o = data[i]; + RegExp re = RegExp(r'\b\w{3,}\b'); + List matches = re.allMatches(o.title).toList() + re.allMatches(o.keywords).toList(); + for (int j = 0; j < matches.length; j++) { + String match = matches[j].group(0)!.toLowerCase(); + if (ignore.contains(match)) continue; + ignore.add(match); + counts[match] = (counts[match] ?? 0) + 1; + } + } + + String str = 'List _searchSuggestions = const ['; + + int minCount = min(10, max(3, data.length / 60)).round(); + int suggestionCount = 0; + counts.forEach((key, value) { + if (value >= minCount) { + str += "'$key', "; + suggestionCount++; + } + }); + _log('- extracted $suggestionCount keyword suggestions'); + return '// Search suggestions ($suggestionCount)\n$str];'; + } + + Widget _buildContent(BuildContext context) { + return Row( + children: [ + // input: + SizedBox( + width: 200, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Wonder to run:'), + _buildWonderPicker(context), + Gap(16), + Text('Max items:'), + TextFormField( + initialValue: maxIds.toString(), + onChanged: (s) => setState(() => maxIds = int.parse(s)), + ), + Gap(16), + Text('Max priority items:'), + TextFormField( + initialValue: maxPriority.toString(), + onChanged: (s) => setState(() => maxPriority = int.parse(s)), + ), + Gap(16), + CheckboxListTile( + title: Text('check images'), value: checkImages, onChanged: (b) => setState(() => checkImages = b!)), + Gap(32), + MaterialButton(onPressed: () => _run(), child: Text('RUN')), + ]), + ), + Gap(40), + + // output: + Expanded( + child: ListView( + children: log.map((o) => Text(o)).toList(growable: false), + )), + ], + ); + } + + Widget _buildWonderPicker(BuildContext context) { + List items = wondersLogic.all.map((o) => o.title).toList(); + items.insert(0, 'All'); + + return DropdownButton( + value: selectedWonder, + icon: const Icon(Icons.arrow_downward), + elevation: 16, + style: const TextStyle(color: Colors.deepPurple), + underline: Container( + height: 2, + color: Colors.deepPurpleAccent, + ), + onChanged: (String? newValue) { + setState(() { + selectedWonder = newValue!; + }); + }, + items: items.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ); + } +} + +const String _baseArtifactUri = 'https://collectionapi.metmuseum.org/public/collection/v1/objects/'; + +// ! as first char indicates a priority query +const String _baseQueryUri = 'https://collectionapi.metmuseum.org/public/collection/v1/search?hasImage=true&'; +const Map> queries = { + WonderType.chichenItza: [ + // 550 1550 + 'artistOrCulture=true&q=maya', // 137 + 'geoLocation=North and Central America&q=maya', // 193 + ], + WonderType.christRedeemer: [ + // 1800 1950 + 'geoLocation=Brazil&q=brazil', // 69 + ], + WonderType.colosseum: [ + // 1 500 + //'!geoLocation=Rome&q=Rome', + //'geoLocation=Roman Empire&q=roman', // 408 + //'!q=colosseum', + 'artistOrCulture=true&q=roman', // 6068 + //'!dateBegin=-&dateEnd=500&geoLocation=Roman Empire&q=imperial rome', // 408 + ], + WonderType.greatWall: [ + // -700 1650 + '!dateBegin=-700&dateEnd=1650&artistOrCulture=true&q=china', // 4540 + 'geolocation=china&artistOrCulture=true&q=china', // 14181 + ], + WonderType.machuPicchu: [ + // 1400 1600 + '!artistOrCulture=true&geoLocation=Peru&q=quechua', + 'geoLocation=South%20America&q=inca', // 344 + ], + WonderType.petra: [ + // -500 500 + '!artistOrCulture=true&q=nabataean', // 50 + '!geoLocation=Levant&q=levant', // 346 + 'geoLocation=Asia&q=Arabia', + ], + WonderType.pyramidsGiza: [ + // -2600 -2500 + '!dateBegin=-2650&dateEnd=-2450&geoLocation=Egypt&q=egypt', // 205 + 'geoLocation=Egypt&q=egypt', // 16668 + ], + WonderType.tajMahal: [ + // 1600 1700 + '!geoLocation=India&q=mughal', // 399, + 'geoLocation=India&q=India', + ], +}; diff --git a/lib/_tools/unsplash_download_service.dart b/lib/_tools/unsplash_download_service.dart new file mode 100644 index 00000000..c03e1c27 --- /dev/null +++ b/lib/_tools/unsplash_download_service.dart @@ -0,0 +1,69 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:http/http.dart' show get; +import 'package:path_provider/path_provider.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/unsplash_service.dart'; +import 'package:wonders/logic/wonders_logic.dart'; + +class UnsplashDownloadService { + static final UnsplashService _unsplash = UnsplashService(); + static final WondersLogic _wondersLogic = WondersLogic(); + + /// Downloads one image in various sizes + static Future downloadImageSet(String id) async { + final photo = await _unsplash.loadInfo(id); + int saveCount = 0; + if (photo != null) { + final rootDir = await getApplicationDocumentsDirectory(); + final imagesDir = '${rootDir.path}/unsplash_images'; + await Directory(imagesDir).create(recursive: true); + debugPrint('Downloading image set $id to $imagesDir'); + final sizes = [32, 400, 800, 2000, 1600, 4000]; + for (var size in sizes) { + final url = photo.getUnsplashUrl(size); + final imgResponse = await get(Uri.parse(url)); + File file = File('$imagesDir/$id-$size.jpg'); + file.writeAsBytesSync(imgResponse.bodyBytes); + //print('file saved @ ${file.path}'); + saveCount++; + } + } + return saveCount; + } + + /// Downloads all images for a single collection, in various sizes + static Future downloadCollectionImages(WonderData data) async { + final collection = await _unsplash.loadCollectionPhotos(data.unsplashCollectionId) ?? []; + debugPrint('download: ${collection.length} images for ${data.title}'); + int downloadCount = 0; + for (var p in collection) { + downloadCount += await downloadImageSet(p); + } + debugPrint('${data.title} complete, downloads = $downloadCount'); + } + + /// Downloads all images for all collections + static Future downloadAllCollections() async { + /// Note: intentionally not in parallel so as to not annoy the unsplash servers + for (var w in _wondersLogic.all) { + await downloadCollectionImages(w); + } + } + + /// Generates a map we can use to look up collection images without needing to copy all 200 ids by hand. + /// Spits the results into the log, developers can copy and paste them from there into a dart file somewhere. + static Future printPhotosByCollectionIdMap() async { + /// Note: intentionally not in parallel so as to not annoy the unsplash servers + Map> imageListByCollectionId = {}; + for (var w in _wondersLogic.all) { + final collection = await _unsplash.loadCollectionPhotos(w.unsplashCollectionId) ?? []; + imageListByCollectionId[w.unsplashCollectionId] = collection; + } + debugPrint(''' + final photosByCollectionId = ${jsonEncode(imageListByCollectionId).replaceAll('"', '\'')}; + '''); + } +} diff --git a/lib/assets.dart b/lib/assets.dart new file mode 100644 index 00000000..aa5ad64d --- /dev/null +++ b/lib/assets.dart @@ -0,0 +1,72 @@ +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/platform_info.dart'; + +/// Loads bitmap assets into memory that may be required later +class AppBitmaps { + static late final BitmapDescriptor mapMarker; + + static Future init() async { + mapMarker = await BitmapDescriptor.fromAssetImage( + ImageConfiguration(devicePixelRatio: PlatformInfo.pixelRatio), + '${ImagePaths.common}/location-pin.png', + ); + } +} + +/// Consolidates raster image paths used across the app +class ImagePaths { + static String root = 'assets/images'; + static String common = 'assets/images/_common'; + static String cloud = '$common/cloud-white.png'; + + static String collectibles = '$root/collectibles'; + static String particle = '$common/particle-21x23.png'; + static String ribbonEnd = '$common/ribbon-end.png'; + + static String textures = '$common/texture'; + static String icons = '$common/icons'; + static String speckles = '$textures/speckles-white.png'; + static String roller1 = '$textures/roller-1-white.png'; + static String roller2 = '$textures/roller-2-white.png'; + + static String appLogo = '$common/app-logo.png'; + static String appLogoPlain = '$common/app-logo-plain.png'; +} + +/// Consolidates SCG image paths in their own class, hints to the UI to use an SvgPicture to render +class SvgPaths { + static String compassFull = '${ImagePaths.common}/compass-full.svg'; + static String compassSimple = '${ImagePaths.common}/compass-simple.svg'; +} + +/// For wonder specific assets, add an extension to [WonderType] for easy lookup +extension WonderAssetExtensions on WonderType { + String get assetPath { + switch (this) { + case WonderType.pyramidsGiza: + return '${ImagePaths.root}/pyramids'; + case WonderType.greatWall: + return '${ImagePaths.root}/great_wall_of_china'; + case WonderType.petra: + return '${ImagePaths.root}/petra'; + case WonderType.colosseum: + return '${ImagePaths.root}/colosseum'; + case WonderType.chichenItza: + return '${ImagePaths.root}/chichen_itza'; + case WonderType.machuPicchu: + return '${ImagePaths.root}/machu_picchu'; + case WonderType.tajMahal: + return '${ImagePaths.root}/taj_mahal'; + case WonderType.christRedeemer: + return '${ImagePaths.root}/christ_the_redeemer'; + } + } + + String get homeBtn => '$assetPath/wonder-button.png'; + String get photo1 => '$assetPath/photo-1.jpg'; + String get photo2 => '$assetPath/photo-2.jpg'; + String get photo3 => '$assetPath/photo-3.jpg'; + String get photo4 => '$assetPath/photo-4.jpg'; + String get flattened => '$assetPath/flattened.jpg'; +} diff --git a/lib/common_libs.dart b/lib/common_libs.dart new file mode 100644 index 00000000..c9578c57 --- /dev/null +++ b/lib/common_libs.dart @@ -0,0 +1,29 @@ +/// Consolidate shared imports that are common across the app. + +export 'dart:math'; + +export 'package:collection/collection.dart'; +export 'package:extra_alignments/extra_alignments.dart'; +export 'package:flextras/flextras.dart'; +export 'package:flutter/material.dart'; +export 'package:flutter/services.dart'; +export 'package:gap/gap.dart'; +export 'package:get_it/get_it.dart'; +export 'package:get_it_mixin/get_it_mixin.dart'; +export 'package:go_router/go_router.dart'; +export 'package:provider/provider.dart'; +export 'package:rnd/rnd.dart'; +export 'package:simple_rich_text/simple_rich_text.dart'; +export 'package:sized_context/sized_context.dart'; +export 'package:wonders/assets.dart'; +export 'package:wonders/logic/app_logic.dart'; +export 'package:wonders/logic/data/wonder_type.dart'; +export 'package:wonders/logic/settings_logic.dart'; +export 'package:wonders/main.dart'; +export 'package:wonders/router.dart'; +export 'package:wonders/styles/styles.dart'; +export 'package:wonders/ui/common/controls/buttons.dart'; +export 'package:wonders/ui/common/controls/circle_buttons.dart'; +export 'package:wonders/ui/common/controls/scroll_decorator.dart'; +export 'package:wonders/ui/common/controls/app_image.dart'; +export 'package:flutter_animate/flutter_animate.dart'; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 00000000..59babdcc --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,407 @@ +{ + "appName": "Wonderous", + "animatedArrowSemanticSwipe": "Explore details about {title}.", + "appBarTitleFactsHistory": "Facts and History", + "appBarTitleConstruction": "Construction", + "appBarTitleLocation": "Location Info", + "bottomScrubberSemanticScrubber": "scrubber", + "bottomScrubberSemanticTimeline": "Timeline Scrubber, drag horizontally to navigate the timeline.", + "collectionLabelDiscovered": "{percentage}% discovered", + "collectionLabelCount": "{count} of {total}", + "collectionButtonReset": "Reset Collection", + "eventsListButtonOpenGlobal": "Open global timeline", + "newlyDiscoveredSemanticNew": "{count} new item{plural} to explore. Scroll to new item", + "newlyDiscoveredLabelNew": "{count} new item{plural} to explore", + "resultsPopupEnglishContent": "This content is provided by the Metropolitan Museum of Art Collection API, and is only available in English.", + "resultsSemanticDismiss": "dismiss message", + "scrollingContentSemanticYoutube": "Youtube thumbnail", + "scrollingContentSemanticOpen": "Open fullscreen maps view", + "searchInputTitleSuggestions": "Suggestions", + "searchInputHintSearch": "Search (ex. type or material)", + "searchInputSemanticClear": "clear search", + "timelineSemanticDate": "{fromDate} to {endDate}", + "titleLabelDate": "{fromDate} to {endDate}", + "appModalsButtonOk": "Ok", + "appModalsButtonCancel": "Cancel", + "appPageDefaultTitlePage": "page", + "appPageSemanticSwipe": "{pageTitle} {count} of {total}.", + "artifactsTitleArtifacts": "ARTIFACTS", + "semanticsPrevious": "Previous {title}", + "semanticsNext": "Next {title}", + "artifactsSemanticsPrevious": "Previous artifact", + "artifactsSemanticsNext": "Next artifact", + "artifactsSemanticArtifact": "Artifact", + "artifactsButtonBrowse": "Browse all artifacts", + "artifactDetailsLabelDate": "Date", + "artifactDetailsLabelPeriod": "Period", + "artifactDetailsLabelGeography": "Geography", + "artifactDetailsLabelMedium": "Medium", + "artifactDetailsLabelDimension": "Dimension", + "artifactDetailsLabelClassification": "Classification", + "artifactDetailsSemanticThumbnail": "thumbnail image", + "artifactDetailsErrorNotFound": "Unable to find info for artifact {artifactId} ", + "artifactsSearchTitleBrowse": "Browse Artifacts", + "artifactsSearchLabelNotFound": "No artifacts found", + "artifactsSearchButtonToggle": "Toggle Timeframe", + "artifactsSearchSemanticTimeframe": "timeframe", + "artifactsSearchLabelFound": "{numFound} artifacts found, {numResults} in ", + "artifactsSearchLabelAdjust": "Adjust your", + "artifactsSearchLabelSearch": "search terms", + "artifactsSearchLabelTimeframe": "timeframe", + "circleButtonsSemanticClose": "close", + "circleButtonsSemanticBack": "back", + "collectibleFoundTitleArtifactDiscovered": "Artifact Discovered", + "collectibleFoundButtonViewCollection": "view in my collection", + "collectibleItemSemanticCollectible": "collectible item", + "collectionTitleCollection": "Collection", + "collectionPopupResetConfirm": "Are you sure you want to reset your collection?", + "eightWaySemanticSwipeDetector": "eight-way swipe detector", + "expandingTimeSelectorSemanticSelector": "time range selector", + "fullscreenImageViewerSemanticFull": "full screen image, no description available", + "homeMenuButtonExplore": "Explore the timeline", + "homeMenuButtonView": "View your collection", + "homeMenuButtonAbout": "About this app", + "homeMenuAboutWonderous": "Wonderous is a visual showcase of eight wonders of the world. ", + "homeMenuAboutBuilt": "Built with {flutterUrl} by the team at {gskinnerUrl}.", + "homeMenuAboutLearn": "Learn more at {wonderousUrl}.", + "homeMenuAboutSource": "To see the source code for this app, please visit the {githubUrl}.", + "homeMenuAboutRepo": "Wonderous github repo", + "homeMenuAboutFlutter": "Flutter", + "homeMenuAboutGskinner": "gskinner", + "homeMenuAboutApp": "wonderous.app", + "homeMenuAboutPublic": "Public-domain artworks from {metUrl}.", + "homeMenuAboutMet": "The Metropolitan Museum of Art, New York", + "homeMenuAboutPhotography": "Photography from {unsplashUrl}.", + "homeMenuAboutUnsplash": "Unsplash", + "introTitleJourney": "Journey to the past", + "introDescriptionNavigate": "Navigate the intersection of time, art, and culture.", + "introTitleExplore": "Explore places", + "introDescriptionUncover": "Uncover remarkable human-made structures from around the world.", + "introTitleDiscover": "Discover artifacts", + "introDescriptionLearn": "Learn about cultures throughout time by examining things they left behind.", + "introSemanticNavigate": "Navigate", + "introSemanticSwipeLeft": "Swipe left to continue", + "introSemanticEnterApp": "Enter the app", + "introSemanticWonderous": "Wonderous", + "labelledToggleSemanticToggle": "toggle", + "photoGallerySemanticCollectible": "collectible!", + "photoGallerySemanticFocus": "Photo {photoIndex} of {photoTotal}. Tap to focus.", + "photoGallerySemanticFullscreen": "Photo {photoIndex} of {photoTotal}. Tap to open fullscreen view.", + "eraPrehistory": "Prehistory", + "eraClassical": "Classical Era", + "eraEarlyModern": "Early Modern Era", + "eraModern": "Modern Era", + "yearBCE": "BCE", + "yearCE": "CE", + "yearFormat": "{date} {era}", + "year": "Year", + "timelineLabelConstruction": "Construction of {title} begins.", + "timelineTitleGlobalTimeline": "Global Timeline", + "wallpaperModalSave": "Save this poster to your photo gallery?", + "wallpaperModalSaving": "Saving Image. Please wait...", + "wallpaperModalSaveComplete": "Save complete!", + "wallpaperSemanticSharePhoto": "share photo", + "wallpaperSemanticTakePhoto": "take photo", + "wallpaperCheckboxShowTitle": "Show Title", + "wonderDetailsTabLabelInformation": "information and history", + "wonderDetailsTabLabelImages": "photo gallery", + "wonderDetailsTabLabelArtifacts": "artifacts", + "wonderDetailsTabLabelEvents": "events", + "wonderDetailsTabSemanticBack": "back to wonder selection", + "homeSemanticOpenMain": "Open main menu", + "homeSemanticWonder": "wonder", + "chichenItzaTitle": "Chichen Itza", + "chichenItzaSubTitle": "The Great Mayan City", + "chichenItzaRegionTitle": "Yucatan, Mexico", + "chichenItzaArtifactCulture": "Maya", + "chichenItzaArtifactGeolocation": "North and Central America", + "chichenItzaPullQuote1Top": "The Beauty Between", + "chichenItzaPullQuote1Bottom": "the Heavens and the Underworld", + "chichenItzaPullQuote2": "The Maya and Toltec vision of the world and the universe is revealed in their stone monuments and artistic works.", + "chichenItzaPullQuote2Author": "UNESCO", + "chichenItzaCallout1": "The site exhibits a multitude of architectural styles, reminiscent of styles seen in central Mexico and of the Puuc and Chenes styles of the Northern Maya lowlands.", + "chichenItzaCallout2": "The city comprised an area of at least 1.9 sq miles (5 sq km) of densely clustered architecture.", + "chichenItzaVideoCaption": "“Ancient Maya 101 | National Geographic.” Youtube, uploaded by National Geographic.", + "chichenItzaMapCaption": "Map showing location of Chichen Itza in Yucatán State, Mexico.", + "chichenItzaHistoryInfo1": "Chichen Itza was a powerful regional capital controlling north and central Yucatán. The earliest hieroglyphic date discovered at Chichen Itza is equivalent to 832 CE, while the last known date was recorded in the Osario temple in 998 CE.\nDominating the North Platform of Chichen Itza is the famous Temple of Kukulcán. The temple was identified by the first Spaniards to see it, as El Castillo (\"the castle\"), and it regularly is referred to as such. The temple was identified by the first Spaniards to see it, as El Castillo (\"the castle\"), and it regularly is referred to as such.", + "chichenItzaHistoryInfo2": "The city was thought to have the most diverse population in the Maya world, a factor that could have contributed to this architectural variety.", + "chichenItzaConstructionInfo1": "The structures of Chichen Itza were built from precisely chiseled limestone blocks that fit together perfectly without the mortar. Many of these stone buildings were originally painted in red, green, blue and purple colors depending on the availability of the pigments.\nThe stepped pyramid El Castillo stands about 98 feet (30 m) high and consists of a series of nine square terraces, each approximately 8.4 feet (2.57 m) high, with a 20 foot (6 m) high temple upon the summit.", + "chichenItzaConstructionInfo2": "It was built upon broken terrain, which was artificially leveled to support structures such as the Castillo pyramid. Important buildings within the center were connected by a dense network of paved roads called sacbeob.", + "chichenItzaLocationInfo1": "Chichen Itza is located in the eastern portion of Yucatán state in Mexico. Nearby, four large sinkholes, called cenotes, could have provided plentiful water year round at Chichen, making it attractive for settlement.", + "chichenItzaLocationInfo2": "Of these cenotes, the \"Cenote Sagrado\" or Sacred Cenote, was used for the sacrifice of precious objects and human beings as a form of worship to the Maya rain god Chaac.", + "chichenItza600ce": "Chichen Itza rises to regional prominence toward the end of the Early Classic period", + "chichenItza832ce": "The earliest hieroglyphic date discovered at Chichen Itza", + "chichenItza998ce": "Last known date recorded in the Osario temple", + "chichenItza1100ce": "Chichen Itza declines as a regional center", + "chichenItza1527ce": "Invaded by Spanish Conquistador Francisco de Montejo", + "chichenItza1535ce": "All Spanish are driven from the Yucatán Peninsula", + "chichenItzaCollectible1Title": "Pendant", + "chichenItzaCollectible1Icon": "jewelry", + "chichenItzaCollectible2Title": "Bird Ornament", + "chichenItzaCollectible2Icon": "jewelry", + "chichenItzaCollectible3Title": "La Prison, à Chichen-Itza", + "chichenItzaCollectible3Icon": "picture", + "christRedeemerTitle": "Christ the Redeemer", + "christRedeemerSubTitle": "A symbol of peace", + "christRedeemerRegionTitle": "Rio de Janeiro, Brazil", + "christRedeemerArtifactGeolocation": "Brazil", + "christRedeemerPullQuote1Top": "A Perfect Union Between", + "christRedeemerPullQuote1Bottom": "Nature and Architecture", + "christRedeemerPullQuote2": "The statue looms large on the landscape, but it hides as much as it reveals about the diverse religious life of Brazilians.", + "christRedeemerPullQuote2Author": "Thomas Tweed", + "christRedeemerCallout1": "The statue of Christ the Redeemer with open arms, a symbol of peace, was chosen.", + "christRedeemerCallout2": "Construction took nine years, from 1922 to 1931, and cost the equivalent of US$250,000 (equivalent to $3,600,000 in 2020) and the monument opened on October 12, 1931.", + "christRedeemerVideoCaption": "“The Majestic Statue of Christ the Redeemer - Seven Wonders of the Modern World - See U in History.” Youtube, uploaded by See U in History / Mythology.", + "christRedeemerMapCaption": "Map showing location of Christ the Redeemer in Rio de Janeiro, Brazil.", + "christRedeemerHistoryInfo1": "The placement of a Christian monument on Mount Corcovado was first suggested in the mid-1850s to honor Princess Isabel, regent of Brazil and the daughter of Emperor Pedro II, but the project was not approved.\nIn 1889 the country became a republic, and owing to the separation of church and state the proposed statue was dismissed.", + "christRedeemerHistoryInfo2": "The Catholic Circle of Rio made a second proposal for a landmark statue on the mountain in 1920. The group organized an event called Semana do Monumento (\"Monument Week\") to attract donations and collect signatures to support the building of the statue. The organization was motivated by what they perceived as \"Godlessness\" in the society.\nThe designs considered for the \"Statue of the Christ\" included a representation of the Christian cross, a statue of Jesus with a globe in his hands, and a pedestal symbolizing the world.", + "christRedeemerConstructionInfo1": "Artist Carlos Oswald and local engineer Heitor da Silva Costa designed the statue. French sculptor Paul Landowski created the work. In 1922, Landowski commissioned fellow Parisian Romanian sculptor Gheorghe Leonida, who studied sculpture at the Fine Arts Conservatory in Bucharest and in Italy.", + "christRedeemerConstructionInfo2": "A group of engineers and technicians studied Landowski's submissions and felt building the structure of reinforced concrete instead of steel was more suitable for the cross-shaped statue. The concrete making up the base was supplied from Limhamn, Sweden. The outer layers are soapstone, chosen for its enduring qualities and ease of use.", + "christRedeemerLocationInfo1": "Corcovado, which means \"hunchback\" in Portuguese, is a mountain in central Rio de Janeiro, Brazil. It is a 2,329 foot (710 m) granite peak located in the Tijuca Forest, a national park.", + "christRedeemerLocationInfo2": "Corcovado hill lies just west of the city center but is wholly within the city limits and visible from great distances.", + "christRedeemer1850ce": "Plans for the statue were first proposed by Pedro Maria Boss upon Mount Corcovado. This was never approved, however.", + "christRedeemer1921ce": "A new plan was proposed by the Roman Catholic archdiocese, and after the citizens of Rio de Janeiro petitioned the president, it was finally approved.", + "christRedeemer1922ce": "The foundation of the statue was ceremoniously laid out to commemorate Brazil’s independence from Portugal.", + "christRedeemer1926ce": "Construction officially began after the initial design was chosen via a competition and amended by Brazilian artists and engineers.", + "christRedeemer1931ce": "Construction of the statue was completed, standing 98’ tall with a 92’ wide arm span.", + "christRedeemer2006ce": "A chapel was consecrated at the statue’s base to Our Lady of Aparecida to mark the statue’s 75th anniversary.", + "christRedeemerCollectible1Title": "Engraved Horn", + "christRedeemerCollectible1Icon": "statue", + "christRedeemerCollectible2Title": "Fixed fan", + "christRedeemerCollectible2Icon": "jewelry", + "christRedeemerCollectible3Title": "Handkerchiefs (one of two)", + "christRedeemerCollectible3Icon": "textile", + "colosseumTitle": "Colosseum", + "colosseumSubTitle": "The icon of Rome", + "colosseumRegionTitle": "Rome, Italy", + "colosseumArtifactCulture": "Roman", + "colosseumArtifactGeolocation": "Roman Empire", + "colosseumPullQuote1Top": "Still the Largest Standing", + "colosseumPullQuote1Bottom": "Amphitheater in the World Today", + "colosseumPullQuote2": "When falls the Coliseum, Rome shall fall; And when Rome falls - the World.", + "colosseumPullQuote2Author": "Lord Byron", + "colosseumCallout1": "It was used for gladiatorial contests and public spectacles including animal hunts, executions, reenactments of famous battles, and dramas based on Roman mythology, and mock sea battles.", + "colosseumCallout2": "It is the largest ancient amphitheater ever built, and is still the largest standing amphitheater in the world today, despite its age.", + "colosseumVideoCaption": "“Ancient Rome 101 | National Geographic.” Youtube, uploaded by National Geographic.", + "colosseumMapCaption": "Map showing location of Colosseum in Rome, Italy,", + "colosseumHistoryInfo1": "The Colosseum is an oval amphitheater in the center of the city of Rome, Italy. Unlike Roman theaters that were built into hillsides, the Colosseum is an entirely free-standing structure.", + "colosseumHistoryInfo2": "The building ceased to be used for entertainment in the early medieval era. By the late 6th century a small chapel had been built into the structure of the amphitheater, and the arena was converted into a cemetery. \nThe numerous vaulted spaces in the arcades under the seating were converted into housing and workshops, and are recorded as still being rented out as late as the 12th century.", + "colosseumConstructionInfo1": "Construction began under the emperor Vespasian (r. 69-79 CE) in 72 and was completed in 80 CE under his successor and heir, Titus (r. 79-81). Further modifications were made during the reign of Domitian (r. 81-96).\nThe Colosseum is built of travertine limestone, tuff (volcanic rock), and brick-faced concrete. The outer wall is estimated to have required over 3.5 million cubic feet of travertine stone which were set without mortar; they were held together by 300 tons of iron clamps.", + "colosseumConstructionInfo2": "It could hold an estimated 50,000 to 80,000 spectators at various points in its history, having an average audience of some 65,000.", + "colosseumLocationInfo1": "Following the Great Fire of Rome in 64 CE, Emperor Nero seized much of the destroyed area to build his grandiose Domus Aurea (\"Golden House\"). A severe embarrassment to Nero's successors, parts of this extravagant palace and grounds, encompassing 1 sq mile, were filled with earth and built over.", + "colosseumLocationInfo2": "On the site of the lake, in the middle of the palace grounds, Emperor Vespasian would build the Colosseum as part of a Roman resurgence.", + "colosseum70ce": "Colosseum construction was started during the Vespasian reign overtop what used to be a private lake for the previous four emperors. This was done in an attempt to revitalize Rome from their tyrannical reign.", + "colosseum82ce": "The uppermost floor was built, and the structure was officially completed by Domitian.", + "colosseum1140ce": "The arena was repurposed as a fortress for the Frangipane and Annibaldi families. It was also at one point used as a church.", + "colosseum1490ce": "Pope Alexander VI permitted the site to be used as a quarry, for both storing and salvaging building materials.", + "colosseum1829ce": "Preservation of the colosseum officially began, after more than a millennia of dilapidation and vandalism. Pope Pius VIII was notably devoted to this project.", + "colosseum1990ce": "A restoration project was undertaken to ensure the colosseum remained a major tourist attraction for Rome. It currently stands as one of the greatest sources of tourism revenue in Italy.", + "colosseumCollectible1Title": "Glass hexagonal amphoriskos", + "colosseumCollectible1Icon": "vase", + "colosseumCollectible2Title": "Bronze plaque of Mithras slaying the bull", + "colosseumCollectible2Icon": "statue", + "colosseumCollectible3Title": "Interno del Colosseo", + "colosseumCollectible3Icon": "picture", + "greatWallTitle": "The Great Wall", + "greatWallSubTitle": "Longest structure on Earth", + "greatWallRegionTitle": "China", + "greatWallArtifactCulture": "Chinese", + "greatWallArtifactGeolocation": "China", + "greatWallPullQuote1Top": "The Longest Man-Made", + "greatWallPullQuote1Bottom": "Structure in the World", + "greatWallPullQuote2": "Its historic and strategic importance is matched only by its architectural significance.", + "greatWallPullQuote2Author": "UNESCO", + "greatWallCallout1": "The best-known sections of the wall were built by the Ming dynasty (1368-1644).", + "greatWallCallout2": "During the Ming dynasty, however, bricks were heavily used in many areas of the wall, as were materials such as tiles, lime, and stone.", + "greatWallVideoCaption": "“See China’s Iconic Great Wall From Above | National Geographic.” Youtube, uploaded by National Geographic.", + "greatWallMapCaption": "Map showing location of Great Wall of China in northern China.", + "greatWallHistoryInfo1": "The Great Wall of China is a series of fortifications that were built across the historical northern borders of ancient Chinese states and Imperial China as protection against various nomadic groups from the Eurasian Steppe. The total length of all sections ever built is over 13,000 miles.", + "greatWallHistoryInfo2": "Several walls were built from as early as the 7th century BCE, with selective stretches later joined together by Qin Shi Huang (220-206 BCE), the first emperor of China. Little of the Qin wall remains. \nLater on, many successive dynasties built and maintained multiple stretches of border walls.", + "greatWallConstructionInfo1": "Transporting the large quantity of materials required for construction was difficult, so builders always tried to use local resources. Stones from the mountains were used over mountain ranges, while rammed earth was used for construction in the plains. Most of the ancient walls have eroded away over the centuries.", + "greatWallConstructionInfo2": "Stones cut into rectangular shapes were used for the foundation, inner and outer brims, and gateways of the wall. \nUnder the rule of the Qing dynasty, China's borders extended beyond the walls and Mongolia was annexed into the empire, so construction was discontinued.", + "greatWallLocationInfo1": "The frontier walls built by different dynasties have multiple courses. Collectively, they stretch from Liaodong in the east to Lop Lake in the west, from the present-day Sino-Russian border in the north to Tao River in the south; along an arc that roughly delineates the edge of the Mongolian steppe.", + "greatWallLocationInfo2": "Apart from defense, other purposes of the Great Wall have included border controls, allowing the imposition of duties on goods transported along the Silk Road, regulation or encouragement of trade and the control of immigration and emigration.", + "greatWall700bce": "First landmark of the Great Wall began originally as a square wall surrounding the state of Chu. Over the years, additional walls would be built and added to it to expand and connect territory.", + "greatWall214bce": "The first Qin Emperor unifies China and links the wall of the surrounding states of Qin, Yan, and Zhao into the Great Wall of China, taking 10 years to build with hundreds of thousands of laborers.", + "greatWall121bce": "A 20-year construction project was started by the Han emperor to build east and west sections of the wall, including beacons, towers, and castles. Not just for defense, but also to control trade routes like the Silk Road.", + "greatWall556ce": "The Bei Qi kingdom also launched several construction projects, utilizing over 1.8 million workers to repair and extend sections of the wall, adding to its length and even building a second inner wall around Shanxi.", + "greatWall618ce": "The Great Wall was repaired during the Sui Dynasty and used to defend against Tujue attacks. Before and after the Sui Dynasty, the wall saw very little use and fell into disrepair.", + "greatWall1487ce": "Hongzhi Emperor split the walls into north and south lines, eventually shaping it into how it is today. Since then, it has gradually fallen into disrepair and remains mostly unused.", + "greatWallCollectible1Title": "Biographies of Lian Po and Lin Xiangru", + "greatWallCollectible1Icon": "scroll", + "greatWallCollectible2Title": "Jar with Dragon", + "greatWallCollectible2Icon": "vase", + "greatWallCollectible3Title": "Panel with Peonies and Butterfly", + "greatWallCollectible3Icon": "textile", + "machuPicchuTitle": "Machu Picchu", + "machuPicchuSubTitle": "Citadel of the Inca", + "machuPicchuRegionTitle": "Cusco Region, Peru", + "machuPicchuArtifactCulture": "Inca", + "machuPicchuArtifactGeolocation": "South America", + "machuPicchuPullQuote1Top": "Few Romances Can Ever Surpass", + "machuPicchuPullQuote1Bottom": "That of the Granite Citadel", + "machuPicchuPullQuote1Author": "Hiram Bingham", + "machuPicchuPullQuote2": "In the variety of its charms and the power of its spell, I know of no other place in the world which can compare with it.", + "machuPicchuPullQuote2Author": "Hiram Bingham", + "machuPicchuCallout1": "During its use as a royal estate, it is estimated that about 750 people lived there, with most serving as support staff who lived there permanently.", + "machuPicchuCallout2": "The Incas were masters of this technique, called ashlar, in which blocks of stone are cut to fit together tightly without mortar.", + "machuPicchuVideoCaption": "“Machu Picchu 101 | National Geographic.” Youtube, uploaded by National Geographic.", + "machuPicchuMapCaption": "Map showing location of Machu Picchu in the Eastern Cordillera of southern Peru.", + "machuPicchuHistoryInfo1": "Machu Picchu is a 15th-century Inca citadel located in the Eastern Cordillera of southern Peru on a 2,430-meter (7,970 ft) mountain ridge. Construction appears to date from two great Inca rulers, Pachacutec Inca Yupanqui (1438–1471 CE) and Túpac Inca Yupanqui (1472–1493 CE).", + "machuPicchuHistoryInfo2": "There is a consensus among archeologists that Pachacutec ordered the construction of the royal estate for his use as a retreat, most likely after a successful military campaign.\nRather it was used for 80 years before being abandoned, seemingly because of the Spanish conquests in other parts of the Inca Empire.", + "machuPicchuConstructionInfo1": "The central buildings use the classical Inca architectural style of polished dry-stone walls of regular shape. \nInca walls have many stabilizing features: doors and windows are trapezoidal, narrowing from bottom to top; corners usually are rounded; inside corners often incline slightly into the rooms, and outside corners were often tied together by \"L\"-shaped blocks.", + "machuPicchuConstructionInfo2": "This precision construction method made the structures at Machu Picchu resistant to seismic activity.\nThe site itself may have been intentionally built on fault lines to afford better drainage and a ready supply of fractured stone.", + "machuPicchuLocationInfo1": "Machu Picchu is situated above a bow of the Urubamba River, which surrounds the site on three sides, where cliffs drop vertically for 1,480 feet (450 m) to the river at their base. The location of the city was a military secret, and its deep precipices and steep mountains provided natural defenses.", + "machuPicchuLocationInfo2": "The Inca Bridge, an Inca grass rope bridge, across the Urubamba River in the Pongo de Mainique, provided a secret entrance for the Inca army. Another Inca bridge was built to the west of Machu Picchu, the tree-trunk bridge, at a location where a gap occurs in the cliff that measures 20 feet (6 m).", + "machuPicchu1438ce": "Speculated to be built and occupied by Inca ruler Pachacuti Inca Yupanqui.", + "machuPicchu1572ce": "The last Inca rulers used the site as a bastion to rebel against Spanish rule until they were ultimately wiped out.", + "machuPicchu1867ce": "Speculated to have been originally discovered by German explorer Augusto Berns, but his findings were never effectively publicized.", + "machuPicchu1911ce": "Introduced to the world by Hiram Bingham of Yale University, who was led there by locals after disclosing he was searching for Vilcabamba, the ’lost city of the Incas’.", + "machuPicchu1964ce": "Surrounding sites were excavated thoroughly by Gene Savoy, who found a much more suitable candidate for Vilcabamba in the ruin known as Espíritu Pampa.", + "machuPicchu1997ce": "Since its rediscovery, growing numbers of tourists have visited the Machu Picchu each year, with numbers exceeding 1.4 million in 2017.", + "machuPicchuCollectible1Title": "Eight-Pointed Star Tunic", + "machuPicchuCollectible1Icon": "textile", + "machuPicchuCollectible2Title": "Camelid figurine", + "machuPicchuCollectible2Icon": "statue", + "machuPicchuCollectible3Title": "Double Bowl", + "machuPicchuCollectible3Icon": "vase", + "petraTitle": "Petra", + "petraSubTitle": "The Lost City", + "petraRegionTitle": "Ma’an, Jordan", + "petraArtifactCulture": "Nabataean", + "petraArtifactGeolocation": "Levant", + "petraPullQuote1Top": "A Rose-Red City", + "petraPullQuote1Bottom": "Half as Old as Time", + "petraPullQuote1Author": "John William Burgon", + "petraPullQuote2": "Petra is a brilliant display of man’s artistry in turning barren rock into a majestic wonder.", + "petraPullQuote2Author": "Edward Dawson", + "petraCallout1": "They were particularly skillful in harvesting rainwater, agriculture and stone carving.", + "petraCallout2": "Perhaps a more prominent resemblance to Hellenistic style in Petra comes with its Treasury.", + "petraVideoCaption": "“Stunning Stone Monuments of Petra | National Geographic.” Youtube, uploaded by National Geographic.", + "petraMapCaption": "Map showing location of Petra in Ma’an Governorate, Jordan.", + "petraHistoryInfo1": "The area around Petra has been inhabited from as early as 7000 BCE, and the Nabataeans might have settled in what would become the capital city of their kingdom as early as the 4th century BCE.\nThe trading business gained the Nabataeans considerable revenue and Petra became the focus of their wealth. The Nabataeans were accustomed to living in the barren deserts, unlike their enemies, and were able to repel attacks by taking advantage of the area's mountainous terrain.", + "petraHistoryInfo2": "Petra flourished in the 1st century CE, when its famous Al-Khazneh structure - believed to be the mausoleum of Nabataean king Aretas IV - was constructed, and its population peaked at an estimated 20,000 inhabitants.\nAccess to the city is through a 3/4 mile-long (1.2 km) gorge called the Siq, which leads directly to the Khazneh.", + "petraConstructionInfo1": "Famous for its rock-cut architecture and water conduit system, Petra is also called the \"Red Rose City\" because of the color of the stone from which it is carved.\nAnother thing Petra is known for is its Hellenistic (“Greek”) architecture. These influences can be seen in many of the facades at Petra and are a reflection of the cultures that the Nabataens traded with.", + "petraConstructionInfo2": "The facade of the Treasury features a broken pediment with a central tholos (“dome”) inside, and two obelisks appear to form into the rock of Petra at the top. Near the bottom of the Treasury we see twin Greek Gods: Pollux, Castor, and Dioscuri, who protect travelers on their journeys. \nNear the top of the Treasury, two victories are seen standing on each side of a female figure on the tholos. This female figure is believed to be the Isis-Tyche, Isis being the Egyptian Goddess and Tyche being the Greek Goddess of good fortune.", + "petraLocationInfo1": "Petra is located in southern Jordan. It is adjacent to the mountain of Jabal Al-Madbah, in a basin surrounded by mountains forming the eastern flank of the Arabah valley running from the Dead Sea to the Gulf of Aqaba.", + "petraLocationInfo2": "The area around Petra has been inhabited from as early as 7000 BC, and the Nabataeans might have settled in what would become the capital city of their kingdom as early as the 4th century BC.\nArchaeological work has only discovered evidence of Nabataean presence dating back to the second century BC, by which time Petra had become their capital. The Nabataeans were nomadic Arabs who invested in Petra's proximity to the incense trade routes by establishing it as a major regional trading hub.", + "petra1200bce": "First Edomites occupied the area and established a foothold.", + "petra106bce": "Became part of the Roman province Arabia", + "petra551ce": "After being damaged by earthquakes, habitation of the city all but ceased.", + "petra1812ce": "Rediscovered by the Swiss traveler Johann Ludwig Burckhardt.", + "petra1958ce": "Excavations led on the site by the British School of Archaeology and the American Center of Oriental Research.", + "petra1989ce": "Appeared in the film Indiana Jones and The Last Crusade.", + "petraCollectible1Title": "Camel and riders", + "petraCollectible1Icon": "statue", + "petraCollectible2Title": "Vessel", + "petraCollectible2Icon": "vase", + "petraCollectible3Title": "Open bowl", + "petraCollectible3Icon": "vase", + "pyramidsGizaTitle": "Pyramids of Giza", + "pyramidsGizaSubTitle": "The ancient wonder", + "pyramidsGizaRegionTitle": "Cairo, Egypt", + "pyramidsGizaArtifactCulture": "Egyptian", + "pyramidsGizaArtifactGeolocation": "Egypt", + "pyramidsGizaPullQuote1Top": "The Tallest Structures on Earth", + "pyramidsGizaPullQuote1Bottom": "Until the Advent of Modern Skyscrapers", + "pyramidsGizaPullQuote2": "From the heights of these pyramids, forty centuries look down on us.", + "pyramidsGizaPullQuote2Author": "Napoleon Bonaparte", + "pyramidsGizaCallout1": "It is theorized the pyramid not only served as a tomb for the pharaoh, but also as a storage pit for various items he would need in the afterlife.", + "pyramidsGizaCallout2": "The Great Pyramid consists of an estimated 2.3 million blocks. Approximately 5.5 million tonnes of limestone, 8,000 tonnes of granite, and 500,000 tonnes of mortar were used in the construction.", + "pyramidsGizaVideoCaption": "“The Great Pyramids of Giza | Egypt’s Ancient Mysteries | National Geographic UK.” Youtube, uploaded by National Geographic UK.", + "pyramidsGizaMapCaption": "Map showing location of Giza Pyramids in Greater Cairo, Egypt.", + "pyramidsGizaHistoryInfo1": "The Giza pyramid complex, also called the Giza necropolis, is the site on the Giza Plateau in Greater Cairo, Egypt that includes the Great Pyramid of Giza, the Pyramid of Khafre, and the Pyramid of Menkaure, along with their associated pyramid complexes and the Great Sphinx of Giza. All were built during the Fourth Dynasty of the Old Kingdom of Ancient Egypt, between 2600 and 2500 BCE.", + "pyramidsGizaHistoryInfo2": "The pyramids of Giza and others are thought to have been constructed to house the remains of the deceased pharaohs who ruled over Ancient Egypt. A portion of the pharaoh's spirit called his ka was believed to remain with his corpse. Proper care of the remains was necessary in order for the former Pharaoh to perform his new duties as king of the dead.", + "pyramidsGizaConstructionInfo1": "Most construction theories are based on the idea that the pyramids were built by moving huge stones from a quarry and dragging and lifting them into place. In building the pyramids, the architects might have developed their techniques over time.\nThey would select a site on a relatively flat area of bedrock — not sand — which provided a stable foundation. After carefully surveying the site and laying down the first level of stones, they constructed the pyramids in horizontal levels, one on top of the other.", + "pyramidsGizaConstructionInfo2": "For the Great Pyramid, most of the stone for the interior seems to have been quarried immediately to the south of the construction site. The smooth exterior of the pyramid was made of a fine grade of white limestone that was quarried across the Nile.\nTo ensure that the pyramid remained symmetrical, the exterior casing stones all had to be equal in height and width. Workers might have marked all the blocks to indicate the angle of the pyramid wall and trimmed the surfaces carefully so that the blocks fit together. During construction, the outer surface of the stone was smooth limestone; excess stone has eroded as time has passed.", + "pyramidsGizaLocationInfo1": "The site is at the edges of the Western Desert, approximately 5.6 miles (9 km) west of the Nile River in the city of Giza, and about 8 miles (13 km) southwest of the city center of Cairo.", + "pyramidsGizaLocationInfo2": "Currently, the pyramids are located in the northwestern side of the Western Desert, and it is considered to be one of the best recognizable and the most visited tourist attractions of the planet.", + "pyramidsGiza2575bce": "Construction of the 3 pyramids began for three kings of the 4th dynasty; Khufu, Khafre, and Menkaure.", + "pyramidsGiza2465bce": "Construction began on the smaller surrounding structures called Mastabas for royalty of the 5th and 6th dynasties.", + "pyramidsGiza443bce": "Greek Author Herodotus speculated that the pyramids were built in the span of 20 years with over 100,000 slave labourers. This assumption would last for over 1500 years", + "pyramidsGiza1925ce": "Tomb of Queen Hetepheres was discovered, containing furniture and jewelry. One of the last remaining treasure-filled tombs after many years of looting and plundering.", + "pyramidsGiza1979ce": "Designated a UNESCO World Heritage Site to prevent any more unauthorized plundering and vandalism.", + "pyramidsGiza1990ce": "Discovery of labouror’s districts suggest that the workers building the pyramids were not slaves, and an ingenious building method proved a relatively small work-force was required to build such immense structures.", + "pyramidsGizaCollectible1Title": "Two papyrus fragments", + "pyramidsGizaCollectible1Icon": "scroll", + "pyramidsGizaCollectible2Title": "Fragmentary Face of King Khafre", + "pyramidsGizaCollectible2Icon": "statue", + "pyramidsGizaCollectible3Title": "Jewelry Elements", + "pyramidsGizaCollectible3Icon": "jewelry", + "tajMahalTitle": "Taj Mahal", + "tajMahalSubTitle": "Heaven on Earth", + "tajMahalRegionTitle": "Agra, India", + "tajMahalArtifactCulture": "Mughal", + "tajMahalArtifactGeolocation": "India", + "tajMahalPullQuote1Top": "Not just a Monument,", + "tajMahalPullQuote1Bottom": "but a Symbol of Love.", + "tajMahalPullQuote1Author": "Suman Pokhrel", + "tajMahalPullQuote2": "The Taj Mahal rises above the banks of the river like a solitary tear suspended on the cheek of time.", + "tajMahalPullQuote2Author": "Rabindranath Tagore", + "tajMahalCallout1": "The Taj Mahal is distinguished as the finest example of Mughal architecture, a blend of Indian, Persian, and Islamic styles.", + "tajMahalCallout2": "It took the efforts of 22,000 laborers, painters, embroidery artists and stonecutters to shape the Taj Mahal.", + "tajMahalVideoCaption": "“India’s Taj Mahal Is an Enduring Monument to Love | National Geographic.” Youtube, uploaded by National Geographic.", + "tajMahalMapCaption": "Map showing location of Taj Mahal in Uttar Pradesh, India.", + "tajMahalHistoryInfo1": "The Taj Mahal is an ivory-white marble mausoleum on the right bank of the river Yamuna in the Indian city of Agra. It was commissioned in 1632 CE by the Mughal emperor Shah Jahan (r. 1628-1658) to house the tomb of his favorite wife, Mumtaz Mahal; it also houses the tomb of Shah Jahan himself.", + "tajMahalHistoryInfo2": "The tomb is the centerpiece of a 42-acre (17-hectare) complex, which include twin mosque buildings (placed symmetrically on either side of the mausoleum), a guest house, and is set in formal gardens bounded on three sides by walls.", + "tajMahalConstructionInfo1": "The Taj Mahal was constructed using materials from all over India and Asia. It is believed over 1,000 elephants were used to transport building materials.\nThe translucent white marble was brought from Rajasthan, the jasper from Punjab, jade and crystal from China. The turquoise was from Tibet and the lapis from Afghanistan, while the sapphire came from Sri Lanka. In all, twenty-eight types of precious and semi-precious stones were inlaid into the white marble.", + "tajMahalConstructionInfo2": "An area of roughly 3 acres was excavated, filled with dirt to reduce seepage, and leveled at 160 ft above riverbank. In the tomb area, wells were dug and filled with stone and rubble to form the footings of the tomb.\nThe plinth and tomb took roughly 12 years to complete. The remaining parts of the complex took an additional 10 years.", + "tajMahalLocationInfo1": "India's most famed building, it is situated in the eastern part of the city on the southern bank of the Yamuna River, nearly 1 mile east of the Agra Fort, also on the right bank of the Yamuna.", + "tajMahalLocationInfo2": "The Taj Mahal is built on a parcel of land to the south of the walled city of Agra. Shah Jahan presented Maharaja Jai Singh with a large palace in the center of Agra in exchange for the land.", + "tajMahal1631ce": "Built by Mughal Emperor Shah Jahān to immortalize his deceased wife.", + "tajMahal1647ce": "Construction completed. The project involved over 20,000 workers and spanned 42 acres.", + "tajMahal1658ce": "There were plans for a second mausoleum for his own remains, but Shah Jahān was imprisoned by his son for the rest of his life in Agra Fort, and this never came to pass.", + "tajMahal1901ce": "Lord Curzon and the British Viceroy of India carried out a major restoration to the monument after over 350 years of decay and corrosion due to factory pollution and exhaust.", + "tajMahal1984ce": "To protect the structure from Sikh militants and some Hindu nationalist groups, night viewing was banned to tourists. This ban would last 20 years.", + "tajMahal1998ce": "Restoration and research program put into action to help preserve the monument.", + "tajMahalCollectible1Title": "Dagger with Scabbard", + "tajMahalCollectible1Icon": "jewelry", + "tajMahalCollectible2Title": "The House of Bijapur", + "tajMahalCollectible2Icon": "picture", + "tajMahalCollectible3Title": "Panel of Nasta'liq Calligraphy", + "tajMahalCollectible3Icon": "scroll", + "timelineEvent2900bce": "First known use of papyrus by Egyptians", + "timelineEvent2700bce": "The Old Kingdom begins in Egypt", + "timelineEvent2600bce": "Emergence of Mayan culture in the Yucatán Peninsula", + "timelineEvent2560bce": "King Khufu completes the Great Pyramid of Giza", + "timelineEvent2500bce": "The mammoth goes extinct", + "timelineEvent2200bce": "Completion of Stonehenge", + "timelineEvent2000bce": "Domestication of the horse", + "timelineEvent1800bce": "Alphabetic writing emerges", + "timelineEvent890bce": "Home writes the Iliad and the Odyssey", + "timelineEvent776bce": "First recorded Ancient Olympic Games", + "timelineEvent753bce": "Founding of Rome", + "timelineEvent447bce": "Building of the Parthenon at Athens started", + "timelineEvent427bce": "Birth of Greek Philosopher Plato", + "timelineEvent322bce": "Death of Aristotle (61), the first genuine scientist", + "timelineEvent200bce": "Paper is invented in the Han Dynasty", + "timelineEvent44bce": "Death of Julius Caesar; beginning of the Roman Empire", + "timelineEvent4bce": "Birth of Jesus Christ", + "timelineEvent43ce": "The Roman Empire enters Great Britain for the first time", + "timelineEvent79ce": "Destruction of Pompeii by the volcano Vesuvius", + "timelineEvent455ce": "End of the Roman Empire", + "timelineEvent500ce": "Tikal becomes the first great Maya city", + "timelineEvent632ce": "Death of Muhammad (61), founder of Islam", + "timelineEvent793ce": "The Vikings first invade Britain", + "timelineEvent800ce": "Gunpowder is invented in China", + "timelineEvent1001ce": "Leif Erikson settles during the winter in present-day eastern Canada", + "timelineEvent1077ce": "The Construction of the Tower of London begins", + "timelineEvent1117ce": "The University of Oxford is founded", + "timelineEvent1199ce": "Europeans first use compasses", + "timelineEvent1227ce": "Death of Genghis Khan (65)", + "timelineEvent1337ce": "The Hundred Years' War begins as England and France struggle for dominance.", + "timelineEvent1347ce": "The first of many concurrences of the Black Death plague, believed to have wiped out as many as 50% of Europe's population by its end", + "timelineEvent1428ce": "Birth of the Aztec Empire in Mexico", + "timelineEvent1439ce": "Johannes Gutenberg invents the printing press", + "timelineEvent1492ce": "Christopher Columbus reaches the New World", + "timelineEvent1760ce": "The industrial revolution begins", + "timelineEvent1763ce": "Development of the Watt steam engine", + "timelineEvent1783ce": "End of the American War of Independence from the British Empire", + "timelineEvent1789ce": "The French Revolution begins", + "timelineEvent1914ce": "World War I", + "timelineEvent1929ce": "Black Tuesday signals the beginning of the Great Depression", + "timelineEvent1939ce": "World War II", + "timelineEvent1957ce": "launch of Sputnik 1 by the Soviet Union", + "timelineEvent1969ce": "Apollo 11 mission lands on the moon" +} + \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb new file mode 100644 index 00000000..b3e8fd0e --- /dev/null +++ b/lib/l10n/app_zh.arb @@ -0,0 +1,407 @@ +{ + "appName": "Wonderous", + "animatedArrowSemanticSwipe": "查看关于{title}的详细信息。", + "appBarTitleFactsHistory": "历史与细节", + "appBarTitleConstruction": "建造", + "appBarTitleLocation": "地理位置", + "bottomScrubberSemanticScrubber": "scrubber", + "bottomScrubberSemanticTimeline": "Timeline Scrubber,水平拖动可在历史年表中导航。", + "collectionLabelDiscovered": "已发现 {percentage}% ", + "collectionLabelCount": "{total} 之 {count}", + "collectionButtonReset": "重置集合", + "eventsListButtonOpenGlobal": "打开世界历史年表", + "newlyDiscoveredSemanticNew": "{count}要探索的新项目。滚动到新项目", + "newlyDiscoveredLabelNew": "{count}要探索的新项目", + "resultsPopupEnglishContent": "本内容由大都会艺术博物馆收藏API提供,仅提供英文版本。", + "resultsSemanticDismiss": "解除信息", + "scrollingContentSemanticYoutube": "Youtube 视频缩略图", + "scrollingContentSemanticOpen": "打开全屏地图", + "searchInputTitleSuggestions": "建议", + "searchInputHintSearch": "搜索", + "searchInputSemanticClear": "删除搜索", + "timelineSemanticDate": "{fromDate} 至 {endDate}", + "titleLabelDate": "{fromDate} ~ {endDate}", + "appModalsButtonOk": "确定", + "appModalsButtonCancel": "取消", + "appPageDefaultTitlePage": "页", + "appPageSemanticSwipe": "{pageTitle} {total} 之 {count}.", + "artifactsTitleArtifacts": "文物", + "semanticsPrevious": "之前的文物{title}", + "semanticsNext": "下一个文物{title}", + "artifactsSemanticsPrevious": "之前的文物", + "artifactsSemanticsNext": "下一个文物", + "artifactsSemanticArtifact": "文物", + "artifactsButtonBrowse": "游览所有文物", + "artifactDetailsLabelDate": "日期", + "artifactDetailsLabelPeriod": "年代", + "artifactDetailsLabelGeography": "地理位置", + "artifactDetailsLabelMedium": "类型", + "artifactDetailsLabelDimension": "尺寸", + "artifactDetailsLabelClassification": "级别", + "artifactDetailsSemanticThumbnail": "缩略图", + "artifactDetailsErrorNotFound": "无法搜到工件 {artifactId} 的信息", + "artifactsSearchTitleBrowse": "游览文物", + "artifactsSearchLabelNotFound": "未能搜到文物", + "artifactsSearchButtonToggle": "切换日期范围", + "artifactsSearchSemanticTimeframe": "日期范围", + "artifactsSearchLabelFound": "{numFound}文物搜到,{numResults}在", + "artifactsSearchLabelAdjust": "调整您的", + "artifactsSearchLabelSearch": "搜索词", + "artifactsSearchLabelTimeframe": "日期范围", + "circleButtonsSemanticClose": "关闭", + "circleButtonsSemanticBack": "返回", + "collectibleFoundTitleArtifactDiscovered": "已发现的文物", + "collectibleFoundButtonViewCollection": "在收藏里观看文物", + "collectibleItemSemanticCollectible": "收藏物品", + "collectionTitleCollection": "收藏", + "collectionPopupResetConfirm": "您确定要重置您的收藏吗?", + "eightWaySemanticSwipeDetector": "八方滑动识别器", + "expandingTimeSelectorSemanticSelector": "日期范围选择器", + "fullscreenImageViewerSemanticFull": "全屏图像,没有描述信息", + "homeMenuButtonExplore": "探索历史年表", + "homeMenuButtonView": "观看您的收藏", + "homeMenuButtonAbout": "关于这个应用程序", + "homeMenuAboutWonderous": "Wonderous是世界八大奇迹的视觉展示。", + "homeMenuAboutBuilt": "由 {gskinnerUrl} 团队使用 {flutterUrl} 构建。", + "homeMenuAboutLearn": "在 {wonderousUrl} 上了解更多信息。", + "homeMenuAboutSource": "要查看这个应用程序的源代码,请访问 {githubUrl}。", + "homeMenuAboutRepo": "Wonderous github 储存库", + "homeMenuAboutFlutter": "Flutter", + "homeMenuAboutGskinner": "gskinner", + "homeMenuAboutApp": "wonderous.app", + "homeMenuAboutPublic": "来自{metUrl}的公共领域艺术品。", + "homeMenuAboutMet": "纽约大都会艺术博物馆", + "homeMenuAboutPhotography": "来自{unsplashUrl}的照片。", + "homeMenuAboutUnsplash": "Unsplash", + "introTitleJourney": "穿越过去之旅", + "introDescriptionNavigate": "浏览时光、艺术和文化的交汇处。", + "introTitleExplore": "探索名胜古迹", + "introDescriptionUncover": "发掘世界各地非凡的人造建筑。", + "introTitleDiscover": "发现文物", + "introDescriptionLearn": "研究历史文物,了解历史文化", + "introSemanticNavigate": "浏览", + "introSemanticSwipeLeft": "向左滑动以继续浏览", + "introSemanticEnterApp": "进入应用程序", + "introSemanticWonderous": "Wonderous", + "labelledToggleSemanticToggle": "切换", + "photoGallerySemanticCollectible": "收藏品!", + "photoGallerySemanticFocus": "{photoTotal} 张照片中的第 {photoIndex} 张。点击聚焦。", + "photoGallerySemanticFullscreen": "{photoTotal} 张照片中的第 {photoIndex} 张。点击以打开全屏视图。", + "eraPrehistory": "史前时代", + "eraClassical": "古典时代", + "eraEarlyModern": "早期现代", + "eraModern": "现代", + "yearBCE": "公元前", + "yearCE": "公元后", + "yearFormat": "{era}{date}年", + "year": "年", + "timelineLabelConstruction": "{title}的建造开始。", + "timelineTitleGlobalTimeline": "世界历史年表", + "wallpaperModalSave": "将此海报保存到您的照片库?", + "wallpaperModalSaving": "请稍等, 保存图像中...", + "wallpaperModalSaveComplete": "保存完成!", + "wallpaperSemanticSharePhoto": "分享照片", + "wallpaperSemanticTakePhoto": "拍照", + "wallpaperCheckboxShowTitle": "显示标题", + "wonderDetailsTabLabelInformation": "信息和历史", + "wonderDetailsTabLabelImages": "照片库", + "wonderDetailsTabLabelArtifacts": "文物", + "wonderDetailsTabLabelEvents": "事件", + "wonderDetailsTabSemanticBack": "回到 Wonder 选择", + "homeSemanticOpenMain": "打开主菜单", + "homeSemanticWonder": "Wonder", + "chichenItzaTitle": "奇琴伊察", + "chichenItzaSubTitle": "伟大的玛雅城市", + "chichenItzaRegionTitle": "墨西哥尤卡坦半岛", + "chichenItzaArtifactCulture": "玛雅", + "chichenItzaArtifactGeolocation": "北美洲、中美洲", + "chichenItzaPullQuote1Top": "天堂和地狱", + "chichenItzaPullQuote1Bottom": "之间的美观", + "chichenItzaPullQuote2": "从当地的石制遗迹和艺术作品中,我们可以看出玛雅人和托尔特克人的世界观和宇宙观。", + "chichenItzaPullQuote2Author": "联合国教科文组织", + "chichenItzaCallout1": "奇琴伊察的遗址展示了多种建筑风格,其中包括墨西哥中部的风格以及玛雅北部低地的Puuc和Chenes风格。", + "chichenItzaCallout2": "这座城市由至少1.9平方英里(5平方公里)密集的建筑组成。", + "chichenItzaVideoCaption": "《古玛雅101 | 国家地理》Youtube,由国家地理上传。", + "chichenItzaMapCaption": "显示奇琴伊察位于墨西哥尤卡坦州的地图。", + "chichenItzaHistoryInfo1": "奇琴伊察是玛雅文明的一座强大的地区首都,控制着从尤卡坦州中部到北海岸的一片土地。最早的象形文字在奇琴伊察发现的日期相当于公元832年,而最后一个已知的日期记录在998年的奥萨里奥神庙中。卡斯蒂略金字塔坐落在奇琴伊察北部平台。第一批看到这座金字塔的西班牙人将其命名为“卡斯蒂略” (意为“城堡”),人们至今仍以这个名字称呼它。", + "chichenItzaHistoryInfo2": "该市可能是玛雅世界中人口最多样化的地区,这可能是导致该地点建筑风格多样化的一个因素。", + "chichenItzaConstructionInfo1": "奇琴伊察的建筑结构是由精确凿刻的石灰石块建造而成的,它们之间完美地结合在一起,不需要砂浆。这些石头建筑中许多最初是用红色,绿色,蓝色和紫色绘制的,根据该区域最容易获得的颜料来选择颜料。卡斯蒂略阶梯金字塔高约98英尺(30米),由9个方形阶梯组成,每个阶梯高约8.4英尺(2.57米),顶部有一座20英尺(6米)高的神庙。", + "chichenItzaConstructionInfo2": "这座城市是建立在破碎的地形上的,为了建立主要的建筑群,人为地将其夷为平地,对Castillo金字塔以及相关建筑物的平坦度上进行了最大的努力。这些重要的建筑物通过密集的铺装过道相互连接,称为萨科布。", + "chichenItzaLocationInfo1": "奇琴伊察位于墨西哥尤卡坦州的东部。它附近有四个可见的自然沉孔,称为 cenotes ,这可能会为奇琴伊察全年提供充足的水源,使其成为定居点的吸引力。", + "chichenItzaLocationInfo2": "在这些cenote (天然井) 中,“ Cenote Sagrado” 或 \"神圣天然井\" 被用来祭祀珍贵的物品和人类,作为对玛雅雨神Chaac的一种崇拜。", + "chichenItza600ce": "奇琴伊察在早期古典时期末期成为当地的重要城市", + "chichenItza832ce": "最早的象形文字在奇琴伊察发现", + "chichenItza998ce": "最后在奥萨里奥神庙记录的日期", + "chichenItza1100ce": "奇琴伊察作为地区中心的衰落", + "chichenItza1527ce": "西班牙征服者弗朗西斯科·德·蒙特霍入侵", + "chichenItza1535ce": "西班牙人被赶出尤卡坦半岛", + "chichenItzaCollectible1Title": "坠饰", + "chichenItzaCollectible1Icon": "首饰", + "chichenItzaCollectible2Title": "鸟装饰", + "chichenItzaCollectible2Icon": "首饰", + "chichenItzaCollectible3Title": "La Prison, à Chichen-Itza", + "chichenItzaCollectible3Icon": "图片", + "christRedeemerTitle": "救世基督像", + "christRedeemerSubTitle": "和平的象征", + "christRedeemerRegionTitle": "巴西,里约热内卢", + "christRedeemerArtifactGeolocation": "巴西", + "christRedeemerPullQuote1Top": "自然与建筑的", + "christRedeemerPullQuote1Bottom": "完美结合", + "christRedeemerPullQuote2": "一座耸立在山景中的雕像即揭示又隐藏了巴西人多样的宗教生活。", + "christRedeemerPullQuote2Author": "汤玛斯·特维德", + "christRedeemerCallout1": "救世主基督的雕像被选中,张开双臂,象征和平。", + "christRedeemerCallout2": "建筑耗时9年,从1922年到1931年,花费了相当于25万美元(相当于2020年的360万美元),于1931年10月12日开放。", + "christRedeemerVideoCaption": "“雄伟的救世基督雕像——现代世界七大奇迹——我们在历史中见。” Youtube,由See U in History / Mythology (我们在历史/神话中见)上传。", + "christRedeemerMapCaption": "显示救世基督像位于巴西里约热内卢的地图。", + "christRedeemerHistoryInfo1": "19世纪50年代中期,为了纪念巴西末代皇帝佩德罗二世的女儿伊莎贝尔公主,人们首次提议在科尔科瓦多山上建一座基督教纪念碑,但该项目未获批准。1889年巴西成为共和国,由于政教分离,提议的雕像被驳回。", + "christRedeemerHistoryInfo2": "第二次“在山上建立一个雕像”的提议是里约热内卢天主教会在1920年所提出。天主教会组织了一个叫做“纪念像周”(Semana do Monumento)的活动来吸引捐款和收集签名,支持雕像的建造。这个组织的动机是他们在社会中所观察到的“无神论”。基督雕像的设计要求包括:代表基督教的十字架,一座手持地球仪的耶稣基督像,和一个象征世界的基座。", + "christRedeemerConstructionInfo1": "雕像由艺术家卡洛斯·奥斯瓦尔德和当地工程师海托·达·席尔瓦·科斯卡设计,由法国雕塑家保罗·兰多斯基执行雕塑。1922年,弗兰多斯基委托在巴黎的罗马尼亚雕塑家盖奥赫·莱奥尼达,后者曾在布加勒斯特和意大利的美术学院学习雕塑。", + "christRedeemerConstructionInfo2": "一组工程师和技师团研究了兰多斯基的设计方案,并决定以钢筋混凝土代替钢材,以便更适合十字架形状的雕像。雕像基座的混凝土来自瑞典的利姆。科斯卡和兰多斯基决定以滑石作为雕像的外层材料,因为它经久耐用,易于使用。", + "christRedeemerLocationInfo1": "科尔科瓦多,在葡萄牙语中是“驼背”的意思,是巴西里约热内卢中部的一座山。它是一座2329英尺(710米)高的花岗岩山峰,位于Tijuca森林国家公园。", + "christRedeemerLocationInfo2": "科尔科瓦多山位于市中心以西,但完全在市区范围内,从很远的地方就可以看到。", + "christRedeemer1850ce": "天主教祭司佩德罗·玛丽亚·博斯提议在科尔科瓦多山上建一座基督教纪念碑,但该项目未获批准。", + "christRedeemer1921ce": "罗马天主教总教区提出了一项新计划。在里约热内卢的市民向总统请愿后,该计划最终获得批准。", + "christRedeemer1922ce": "为了纪念巴西从葡萄牙独立,雕像的地基被隆重布置", + "christRedeemer1926ce": "在巴西艺术家和工程师通过竞赛选择并修改了最初的设计后,建筑正式开始施工。", + "christRedeemer1931ce": "雕像的建造完成,高98英尺,臂展92英尺。", + "christRedeemer2006ce": "为了纪念雕像的75周年,雕像基座上的一座小教堂被用来供奉阿帕雷西达圣母。", + "christRedeemerCollectible1Title": "雕刻牛角", + "christRedeemerCollectible1Icon": "雕像", + "christRedeemerCollectible2Title": "固定扇子", + "christRedeemerCollectible2Icon": "首饰", + "christRedeemerCollectible3Title": "手帕(两个当中的一个)", + "christRedeemerCollectible3Icon": "织物", + "colosseumTitle": "罗马斗兽场", + "colosseumSubTitle": "罗马的象征", + "colosseumRegionTitle": "意大利, 罗马市", + "colosseumArtifactCulture": "古罗马", + "colosseumArtifactGeolocation": "罗马帝国", + "colosseumPullQuote1Top": "至今仍是世上最大的", + "colosseumPullQuote1Bottom": "站立式圆形剧场", + "colosseumPullQuote2": "罗马斗兽场倒下,罗马也会倒下; 罗马灭亡,世界也会灭亡。", + "colosseumPullQuote2Author": "拜伦", + "colosseumCallout1": "罗马斗兽场用来进行角斗士的比赛、动物狩猎、海战表演、处决、重要战役的历史重演、以及演出以罗马神话为基础的戏剧。", + "colosseumCallout2": "罗马斗兽场是迄今为止建造的最大的古代圆形剧场,至今仍是世界上最大的站立式圆形剧场。", + "colosseumVideoCaption": "\"古罗马101 | 国家地理。\" Youtube,由国家地理上传。", + "colosseumMapCaption": "显示罗马斗兽场位于意大利罗马市的地图。", + "colosseumHistoryInfo1": "罗马斗兽场是位于意大利罗马市中心的椭圆形圆形剧场。与建在山坡上的罗马剧院不同,罗马斗兽场是一个完全独立的结构。", + "colosseumHistoryInfo2": "罗马斗兽场在中世纪前期已不再用在娱乐用途。到了6世纪晚期,圆形剧场里建起了一座小教堂,斗兽场也被改建为墓地。在座位下方的拱廊里,无数的拱形空间被改造成了住房和作坊,据记载,直到12世纪仍在出租。", + "colosseumConstructionInfo1": "罗马斗兽场的建造始于公元72年,在皇帝韦斯巴芗 (公元69-79年) 的统治下,并在他的继任者提图斯 (公元79-81年) 的统治下于公元80年完成。在图密善统治时期 (公元81-96年) 进行了进一步的修改。罗马斗兽场由石灰华(洞石)、凝灰岩 (火山岩) 和砖面混凝土建造而成。它的外墙是用超过350万立方英尺的石灰华石头建造的,这些石头没有用灰浆凝结,而是用300吨的铁夹子固定在一起。", + "colosseumConstructionInfo2": "罗马斗兽场在历史上不同时期预计可容纳5万至8万名观众,平均观众约为6.5万名。", + "colosseumLocationInfo1": "在公元64年的罗马大火之后,尼禄皇帝利用大部分被摧毁的地区建造了他的宏伟的Domus Aurea(“黄金屋”)。这座奢华的宫殿给尼禄的继任者带来了极大的尴尬。大约1平方英里的宫殿场地后来被填满了泥土,并盖上了新的建筑。", + "colosseumLocationInfo2": "在湖的原址上,在宫殿的中央,韦斯巴芗皇帝将建造了罗马斗兽场,以代表罗马的复兴。", + "colosseum70ce": "罗马斗兽场在韦斯巴芗统治时期开始建造,位于前四位皇帝的私人湖泊之上。这样做是为了使罗马从他们的暴虐统治中复兴。", + "colosseum82ce": "在图密善的统治下,最上层建成,场地整个结构正式完成。", + "colosseum1140ce": "Franangipane 和 Annibaldi 贵族家族将斗兽场改造成了堡垒。斗兽场也曾一度被用作教堂。", + "colosseum1490ce": "教皇亚历山大六世允许斗兽场用作采石场,用于储存和回收建筑材料。", + "colosseum1829ce": "在经历了一千多年的破败和破坏之后,罗马斗兽场正式开始了保护工作。教皇庇护八世特别致力于这项工程。", + "colosseum1990ce": "罗马斗兽场进行了修复工程,以确保它仍然是罗马的主要旅游景点。目前,它是意大利最大的旅游收入来源之一。", + "colosseumCollectible1Title": "玻璃六角双耳瓶", + "colosseumCollectible1Icon": "花瓶", + "colosseumCollectible2Title": "密特拉神杀牛的青铜牌匾", + "colosseumCollectible2Icon": "雕像", + "colosseumCollectible3Title": "\"Interno del Colosseo\" (斗兽场内)", + "colosseumCollectible3Icon": "图片", + "greatWallTitle": "万里长城", + "greatWallSubTitle": "地球上最长的建筑", + "greatWallRegionTitle": "中国", + "greatWallArtifactCulture": "华夏", + "greatWallArtifactGeolocation": "中国", + "greatWallPullQuote1Top": "世界上最长的", + "greatWallPullQuote1Bottom": "人造建筑", + "greatWallPullQuote2": "长城在建筑学上的价值,足以与其在历史和战略上的重要性相媲美。", + "greatWallPullQuote2Author": "联合国教育、科学及文化组织", + "greatWallCallout1": "长城最著名的部分主要建于明朝 (1368-1644)。", + "greatWallCallout2": "然而,在明朝时期,砖石在长城的许多地方都被大量使用,同时使用的还有瓦片、石灰和石头等材料。", + "greatWallVideoCaption": "“从空中看长城 | 国家地理频道。” Youtube,由《国家地理》上传", + "greatWallMapCaption": "显示长城位于中国北方的地图。", + "greatWallHistoryInfo1": "长城是中国古代一系列军事防御建筑的统称,它跨越了古代中原的北部边界,用来防御不同时期来自欧亚草原游牧部落的侵袭。长城的总长度超过13000英里。", + "greatWallHistoryInfo2": "早在公元前7世纪,就有几道墙被修建,后来中国的第一位皇帝秦始皇 (公元前220-206年) 将它们连接起来。如今,秦长城只留下断断续续的遗迹。后来,边境墙被许多朝代连续重建、维护和加强。", + "greatWallConstructionInfo1": "运输大量的建筑材料非常困难,所以建筑人士总是试图利用当地的资源。山上的石头被用在山脉上的建筑,而夯土则用于平原上的建筑。", + "greatWallConstructionInfo2": "城墙的基础、内外边缘和入口是用被切割成矩形的石头来建造的。在清朝的统治下,中国的边界延伸到城墙之外,蒙古被并入大清国,因此,工程停止了。", + "greatWallLocationInfo1": "不同朝代修筑的边墙创造了不同的路线。整体来说,长城的建筑沿着蒙古大草原边缘的弧线,东起辽东,西至罗布湖,北起今中俄边境,南至洮河。", + "greatWallLocationInfo2": "除了防御游牧部落的入侵之外,长城的其他用途还包括边境控制、对丝绸之路运输的货物征收关税、监管或鼓励贸易、以及控制移民和出境。", + "greatWall700bce": "长城的第一个标志性建筑最初是围绕楚国的方形城墙。多年来,为了扩大和连接领土,人们修建了更多的墙。", + "greatWall214bce": "秦始皇统一中国后,将秦国、燕国、赵国的城墙连接起来,形成了中国长城,耗时10年,耗费数十万劳工。", + "greatWall121bce": "汉朝皇帝开始了一项为期20年的建设工程,建造长城的东西部分,包括烽火台、塔楼和城堡。目的不仅是为了防御,也是为了控制丝绸之路以及类似的贸易路线。", + "greatWall556ce": "北齐国启动了几项建设工程,使用180多万工人来修复和延长长城的部分,增加它的长度,甚至在山西周围建造了第二道内墙。", + "greatWall618ce": "长城在隋朝经过修复,用来防御突厥人的攻击。隋朝前后,长城很少被使用,年久失修。", + "greatWall1487ce": "明代弘治皇帝将城墙分成南北线,塑造成现代的外观。从那时起,长城开始逐渐年久失修。", + "greatWallCollectible1Title": "廉颇蔺相如列传", + "greatWallCollectible1Icon": "卷轴", + "greatWallCollectible2Title": "龙缸", + "greatWallCollectible2Icon": "花瓶", + "greatWallCollectible3Title": "牡丹蝴蝶纹绣片", + "greatWallCollectible3Icon": "织物", + "machuPicchuTitle": "马丘比丘", + "machuPicchuSubTitle": "印加城塞", + "machuPicchuRegionTitle": "秘鲁库斯科地区", + "machuPicchuArtifactCulture": "印加", + "machuPicchuArtifactGeolocation": "南美洲", + "machuPicchuPullQuote1Top": "很少有能超越这座", + "machuPicchuPullQuote1Bottom": "石头城塞的浪漫", + "machuPicchuPullQuote1Author": "海勒姆·宾厄姆", + "machuPicchuPullQuote2": "就它的魅力和魔力而言,世界上没有任何地方可以与它相比。", + "machuPicchuPullQuote2Author": "海勒姆·宾厄姆", + "machuPicchuCallout1": "在马丘比丘被用作皇家庄园期间,据估计约有750人居住在那里,其中大多数是长期居住的后勤人员。", + "machuPicchuCallout2": "印加人精通一种被称为“琢石”的技术,这种技术不需要砂浆就能将一块块切割好的石头拼在一起。", + "machuPicchuVideoCaption": "《马丘比丘101 | 国家地理》Youtube,由国家地理上传。", + "machuPicchuMapCaption": "显示马丘比丘位于秘鲁南部东科迪勒拉山脉的地图。", + "machuPicchuHistoryInfo1": "马丘比丘是一座建于15世纪的印加帝国城市遗迹,位于秘鲁南部的东科迪勒拉山脉,高耸在海拔2,430米(7,970英尺)的山脊上。马丘比丘建造在两个伟大的印加统治者帕查庫特克·尤潘基(1438-1471) 和图帕克·印卡·尤潘基(1472-1493)时期。", + "machuPicchuHistoryInfo2": "考古学家一致认为帕恰库特很可能是在赢得一场军事战役之后,下令建造这座皇家庄园作为私人休养地。但这座庄园只使用了80年就被废弃了,可能是因为西班牙人征服了印加帝国的其他地区。", + "machuPicchuConstructionInfo1": "中心建筑采用了经典的印加建筑风格,抛光的规则形状的干石墙。印加的墙壁有许多稳定的特点:门窗是梯形的,从下到上变窄;角通常是圆形的;内角通常略微向房间内倾斜,外角通常用“L”形的木块连接在一起。", + "machuPicchuConstructionInfo2": "这种精确的建造方法使马丘比丘的结构能够抵抗地震活动。这个地点本身可能是有意建在断层线上的,这样可以提供更好的排水系统和现成的碎石供应。", + "machuPicchuLocationInfo1": "马丘比丘位于乌鲁班巴河之上,乌鲁班巴河三面围绕着遗址,悬崖从1480英尺(450米)垂直下降到河流的底部。这座城市的位置是一个军事机密,它的深崖和陡峭的山脉提供了天然的防御。", + "machuPicchuLocationInfo2": "印加桥,一座横跨乌鲁班巴河的印加草绳桥,为印加军队提供了秘密入口。另一座印加桥建在马丘比丘以西的树干桥上,在悬崖上有一个20英尺(6米)的缺口。", + "machuPicchu1438ce": "马丘比丘由印加统治者帕查庫特克·印卡·尤潘基建造。", + "machuPicchu1572ce": "加的末代统治者将此地作为反抗西班牙统治的堡垒,直到他们最终被消灭。", + "machuPicchu1867ce": "据推测,马丘比丘最初由德国探险家奥古斯托·伯恩斯重新发现,但他的发现从未被公开。", + "machuPicchu1911ce": "耶鲁大学的海勒姆·宾厄姆在寻找“失落的印加之城”比尔卡班巴的过程中,在当地人的带领下来到马丘比丘,并将它介绍给全世界。", + "machuPicchu1964ce": "美国探险家吉恩·萨沃伊(Gene Savoy)彻底挖掘了周围的遗址,他在名为Espíritu Pampa的废墟中发现了一个更适合比尔卡班巴的遗址。", + "machuPicchu1997ce": "自马丘比丘被重新发现以来,每年都有越来越多的游客前来参观,2017年的游客人数超过了140万。", + "machuPicchuCollectible1Title": "8点星束腰外衣", + "machuPicchuCollectible1Icon": "织物", + "machuPicchuCollectible2Title": "骆驼科小雕像", + "machuPicchuCollectible2Icon": "雕像", + "machuPicchuCollectible3Title": "双层碗", + "machuPicchuCollectible3Icon": "花瓶", + "petraTitle": "佩特拉", + "petraSubTitle": "失落的石城", + "petraRegionTitle": "约旦马安省", + "petraArtifactCulture": "纳巴泰", + "petraArtifactGeolocation": "黎凡特", + "petraPullQuote1Top": "一座玫瑰红的城市", + "petraPullQuote1Bottom": "其历史有人类历史的一半", + "petraPullQuote1Author": "约翰·威廉·伯根", + "petraPullQuote2": "佩特拉展现了人类将贫瘠的岩石变成宏伟奇迹的能力。", + "petraPullQuote2Author": "爱德华·道森", + "petraCallout1": "他们尤其擅长收集雨水、农业和石雕。", + "petraCallout2": "佩特拉的宝库与希腊风格有显著的相似性。", + "petraVideoCaption": "\"佩特拉惊人的石碑 | 国家地理。\" Youtube,由国家地理上传。", + "petraMapCaption": "显示佩特拉位于约旦马安省的地图。", + "petraHistoryInfo1": "佩特拉周边地区早在公元前7000年就有人居住,纳巴泰人可能早在公元前4世纪就定居在他们王国的首都。贸易业务为纳巴泰人带来了可观的收入,佩特拉成为他们财富的焦点。纳巴泰人与他们的敌人不同,他们习惯了生活在贫瘠的沙漠中,并能够利用该地区的山区地形击退攻击。", + "petraHistoryInfo2": "佩特拉在公元1世纪繁荣发展,当时人口达到顶峰,估计有两万居民。大约在同一时间,它著名的卡兹尼神殿被建造,据信是纳巴泰国王阿雷塔斯四世的陵墓。进入这座城市要穿过一个称为Siq的3/4英里长(1.2公里)的峡谷,直接通往卡兹尼神殿。", + "petraConstructionInfo1": "佩特拉因其岩石雕刻的建筑和水管系统而闻名,被称为“红玫瑰城”。佩特拉也因其希腊风格的建筑而闻名。这种影响可以在佩特拉的许多建筑中看到,并反映了纳巴泰人与之交易的多元文化。", + "petraConstructionInfo2": "宝库的正面是一个破碎的三角楣,里面有一个中央圆顶,两个方尖碑在顶部似乎形成了佩特拉的岩石。在宝库底部附近,我们看到了双胞胎希腊神波鲁克斯和卡斯特,在旅途中保护旅行者。在靠近宝库顶部的地方,可以看到两个胜利雕像分别站在梭罗上的女性雕像的两侧。这个女性形象被认为是伊西斯-堤喀,伊西斯是埃及女神,而堤喀是希腊的好运女神。", + "petraLocationInfo1": "佩特拉位于约旦南部。它毗邻Jabal Al-Madbah山,在一个被山脉包围的盆地中,形成了从死海到亚喀巴湾的阿拉伯山谷的东侧。", + "petraLocationInfo2": "佩特拉周边地区早在公元前7000年就有人居住,纳巴泰人可能早在公元前4世纪就定居在他们王国的首都。考古工作只发现了纳巴泰人始于公元前2世纪的证据,那时佩特拉已经成为他们的首都。纳巴泰人是游牧的阿拉伯人,他们把佩特拉建成了一个主要的区域贸易中心,在靠近香贸易路线的地方投资。", + "petra1200bce": "以东人首先占领了这一地区。", + "petra106bce": "佩特拉成为罗马阿拉伯省的一部分。", + "petra551ce": "在被地震破坏后,佩特拉不再有人居住。", + "petra1812ce": "佩特拉被瑞士旅行家约翰·路德维希·伯克哈德重新发现。", + "petra1958ce": "在英国考古学院和美国东方研究中心的领导下,佩特拉的发掘工作开始。", + "petra1989ce": "在好莱坞电影《夺宝奇兵3:圣战奇兵》出现。", + "petraCollectible1Title": "骆驼和骆驼骑手", + "petraCollectible1Icon": "雕像", + "petraCollectible2Title": "器皿", + "petraCollectible2Icon": "瓶", + "petraCollectible3Title": "碗", + "petraCollectible3Icon": "瓶", + "pyramidsGizaTitle": "吉薩金字塔", + "pyramidsGizaSubTitle": "古代的奇迹", + "pyramidsGizaRegionTitle": "埃及开罗", + "pyramidsGizaArtifactCulture": "埃及", + "pyramidsGizaArtifactGeolocation": "埃及", + "pyramidsGizaPullQuote1Top": "在现代摩天大楼出现之前,", + "pyramidsGizaPullQuote1Bottom": "地球上最高的建筑", + "pyramidsGizaPullQuote2": "四十个世纪从这些金字塔的顶端,俯瞰着我们。", + "pyramidsGizaPullQuote2Author": "拿破仑", + "pyramidsGizaCallout1": "金字塔不仅是法老的坟墓,也是他死后各种物品的储存坑。", + "pyramidsGizaCallout2": "据估计,大金字塔由230万块石块组成。大约550万吨石灰石、8000吨花岗岩和50万吨砂浆被用于建造。", + "pyramidsGizaVideoCaption": "“吉萨大金字塔 |古代埃及的奥秘 |英国国家地理频道。” Youtube,由英国国家地理上传。", + "pyramidsGizaMapCaption": "显示吉萨金字塔位于埃及大开罗地区的地图。", + "pyramidsGizaHistoryInfo1": "吉萨金字塔群,又名吉萨墓地,位于埃及大开罗地区的吉萨高原,包括吉萨大金字塔、卡夫拉金字塔、孟卡拉金字塔,以及与其相关的金字塔群和吉萨狮身人面像。这些建筑都建于埃及古王国第四王朝时期,即公元前2600年至公元前2500年之间。", + "pyramidsGizaHistoryInfo2": "吉萨金字塔和其他金字塔被认为是用来存放古埃及法老遗体的。埃及人相信,法老的灵魂中有一部分被称为他的ka,在他死后与他的尸体一起存在。为了让前法老履行他作为亡灵之王的新职责,妥善保管遗体是必要的。", + "pyramidsGizaConstructionInfo1": "大多数理论认为,金字塔是通过从采石场搬运巨大的石头,然后拖吊到合适的地方而建成的。在建造金字塔的过程中,建筑师可能随着时间的推移而发展了他们的技术。他们没有选择沙子作为地基,而是选择了一块基岩相对平坦的地方,这样可以提供一个稳定的地基。在仔细勘察了遗址并铺好了第一层石头之后,建筑者们用水平的方式建造金字塔,一层接着一层。", + "pyramidsGizaConstructionInfo2": "大金字塔内部的大部分石头似乎都是在建筑工地的南面开采的。金字塔光滑的外表是由尼罗河上开采的优质白色石灰石制成的。为了确保金字塔保持对称,外部外壳的石头都必须在高度和宽度相等。工人们可能已经在所有的石块上做了标记,以表明金字塔墙壁的角度,并仔细地修整表面,以便将石块拼在一起。在施工过程中,石头的外表面是光滑的石灰石;随着时间的推移,多余的石头已被侵蚀。", + "pyramidsGizaLocationInfo1": "金字塔遗址位于西部沙漠的边缘,位于尼罗河以西约5.6英里(9公里)的吉萨市,距开罗市中心西南约8英里(13公里)。", + "pyramidsGizaLocationInfo2": "金字塔位于西部沙漠的西北侧,它被认为是世界上辨识度最高、参观人数最多的旅游景点之一。", + "pyramidsGiza2575bce": "埃及人开始为第四王朝的三位法老胡夫、卡夫拉、孟卡拉建造三座金字塔。", + "pyramidsGiza2465bce": "埃及人开始为第五和第六王朝的皇室建造 Mastabas,也就是金字塔周围更小的结构。", + "pyramidsGiza443bce": "希腊作家希罗多德推测,金字塔是在20年的时间内建成的,共有10万多名奴隶劳工。这种假设持续了1500多年。", + "pyramidsGiza1925ce": "王后Hetepheres的坟墓被发现,里面有家具和珠宝。经过多年的掠夺后,这座古墓是最后剩下的装满宝藏的墓穴之一。", + "pyramidsGiza1979ce": "金字塔被联合国教科文组织指定为世界遗产,以防止任何未经授权的掠夺和破坏行为。", + "pyramidsGiza1990ce": "劳工区的发现表明建造金字塔的工人并不是奴隶。他们巧妙的建造方法证明,建造如此巨大的建筑只需要相对较小的劳动力。", + "pyramidsGizaCollectible1Title": "两张莎草纸", + "pyramidsGizaCollectible1Icon": "卷轴", + "pyramidsGizaCollectible2Title": "卡夫拉法老的脸 (碎片)", + "pyramidsGizaCollectible2Icon": "雕像", + "pyramidsGizaCollectible3Title": "首饰", + "pyramidsGizaCollectible3Icon": "珠宝", + "tajMahalTitle": "泰姬陵", + "tajMahalSubTitle": "人间天堂", + "tajMahalRegionTitle": "印度阿格拉", + "tajMahalArtifactCulture": "莫卧儿", + "tajMahalArtifactGeolocation": "印度阿格拉", + "tajMahalPullQuote1Top": "不仅仅是一座遗址,", + "tajMahalPullQuote1Bottom": "更是爱的象征。", + "tajMahalPullQuote1Author": "苏曼·波克雷尔", + "tajMahalPullQuote2": "耸立在河岸上的泰姬陵,像永恒面颊上的一滴眼泪。", + "tajMahalPullQuote2Author": "泰戈尔", + "tajMahalCallout1": "泰姬陵是莫卧儿王朝建筑中最杰出的典范,融合了印度、波斯和伊斯兰风格。", + "tajMahalCallout2": "泰姬陵的建造花费了2.2万名工人、画家、刺绣艺术家和石匠的心血。", + "tajMahalVideoCaption": "“印度的泰姬陵是永恒的爱情纪念碑 | 国家地理。” Youtube,由国家地理上传。", + "tajMahalMapCaption": "显示泰姬陵位于印度北方邦的地图。", + "tajMahalHistoryInfo1": "泰姬陵是位于印度北方邦阿格拉市亚穆纳河右岸的一座用白色大理石建造的陵墓。它是莫卧儿王朝皇帝沙贾汗为了纪念他最喜爱的妻子—已故皇后姬蔓·芭奴而兴建的陵墓,沙贾汗本人身故后亦合葬于此。", + "tajMahalHistoryInfo2": "这座陵墓是一个42英亩(17公顷)的建筑群的中心,其中包括两座清真寺建筑(对称地安置在陵墓的两侧),一个招待所,坐落在三面城墙围成的花园中。", + "tajMahalConstructionInfo1": "泰姬陵的建造材料来自印度和亚洲各地。据信有超过1000头大象被用来运输建筑材料。半透明的白色大理石来自拉贾斯坦邦,碧玉来自旁遮普,玉石和水晶来自中国。绿松石来自西藏,青金石来自阿富汗,而蓝宝石则来自斯里兰卡。这座建筑的白色大理石上总共镶嵌了28种宝石和半宝石。", + "tajMahalConstructionInfo2": "挖掘出大约三英亩的土地,填满泥土以减少渗漏,并将其平整在河岸上方160英尺的地方。在墓区,人们挖井,填满石头和碎石,形成坟墓的基座。基座和坟墓花了大约12年的时间才完成。该建筑群的其余部分又花了10年时间完成。", + "tajMahalLocationInfo1": "它是印度最著名的建筑,位于阿格拉市东部亚穆纳河南岸,阿格拉堡以东近1英里。", + "tajMahalLocationInfo2": "泰姬陵建在阿格拉市南部的一块土地上。沙贾汗将阿格拉市中心的一座大宫殿赠送给印度王公贾伊辛格,以换取这块土地。", + "tajMahal1631ce": "莫卧儿王朝皇帝沙贾汗为纪念亡妻而建造。", + "tajMahal1647ce": "施工完成。该项目涉及两万多名工人,占地42英亩。", + "tajMahal1658ce": "尽管沙贾汗计划为自己的遗体建造第二座陵墓,但他的余生被儿子囚禁在阿格拉堡,第二座陵墓从未实现。", + "tajMahal1901ce": "在经历了350多年的工厂污染和废气的腐蚀后,柯松勋爵和英国印度总督对这座遗址进行了一次重大修复。", + "tajMahal1984ce": "为了保护这座建筑不受锡克教武装分子和一些印度教民族主义团体的影响,游客被禁止观看夜景。这项禁令将持续20年。", + "tajMahal1998ce": "修复和研究计划付诸行动,以帮助保护遗址。", + "tajMahalCollectible1Title": "有鞘的匕首", + "tajMahalCollectible1Icon": "首饰", + "tajMahalCollectible2Title": "比贾普尔家族之画", + "tajMahalCollectible2Icon": "图片", + "tajMahalCollectible3Title": "纳斯塔利克书法展板", + "tajMahalCollectible3Icon": "卷轴", + "timelineEvent2900bce": "埃及人第一次使用莎草纸", + "timelineEvent2700bce": "埃及古王国开始", + "timelineEvent2600bce": "玛雅文化出现在尤卡坦半岛", + "timelineEvent2560bce": "吉萨大金字塔在胡夫法老统治时期完成", + "timelineEvent2500bce": "猛犸象灭绝", + "timelineEvent2200bce": "巨石阵完成", + "timelineEvent2000bce": "马的驯化成功", + "timelineEvent1800bce": "字母文字的出现", + "timelineEvent890bce": "荷马完成《伊利亚特》和《奥德赛》", + "timelineEvent776bce": "最早记载的古代奥运会", + "timelineEvent753bce": "罗马的创始", + "timelineEvent447bce": "雅典的帕台农神庙开始建造", + "timelineEvent427bce": "希腊哲学家柏拉图的诞生", + "timelineEvent322bce": "亚里士多德去世(61岁),历史上第一位真正的科学家", + "timelineEvent200bce": "纸在中国汉代发明", + "timelineEvent44bce": "凯撒之死; 罗马帝国建立", + "timelineEvent4bce": "耶稣基督的诞生", + "timelineEvent43ce": "罗马帝国征服不列颠", + "timelineEvent79ce": "庞贝城被维苏威火山摧毁", + "timelineEvent455ce": "罗马帝国的衰落", + "timelineEvent500ce": "蒂卡尔成为第一座伟大的玛雅都市", + "timelineEvent632ce": "伊斯兰教创始人穆罕默德 (61岁) 逝世", + "timelineEvent793ce": "维京人第一次入侵不列颠", + "timelineEvent800ce": "纸在中国唐代发明", + "timelineEvent1001ce": "莱夫·埃里克松于公元1001年冬天定居在今天的加拿大东部", + "timelineEvent1077ce": "伦敦塔开始建造", + "timelineEvent1117ce": "牛津大学成立", + "timelineEvent1199ce": "欧洲人最早使用指南针", + "timelineEvent1227ce": "成吉思汗去世(65岁)", + "timelineEvent1337ce": "英法两国为争夺统治权展开了百年战争", + "timelineEvent1347ce": "黑死病的第一例;到最后,这场瘟疫将使欧洲50%的人口灭绝", + "timelineEvent1428ce": "阿兹特克帝国在墨西哥的诞生", + "timelineEvent1439ce": "约翰内斯·古腾堡发明了印刷机", + "timelineEvent1492ce": "克里斯托弗·哥伦布到达了新大陆", + "timelineEvent1760ce": "工业革命的开始", + "timelineEvent1763ce": "瓦特蒸汽机的发明", + "timelineEvent1783ce": "美国独立战争结束,脱离大英帝国", + "timelineEvent1789ce": "法国革命开始", + "timelineEvent1914ce": "第一次世界大战", + "timelineEvent1929ce": "黑色星期二,大萧条的开始", + "timelineEvent1939ce": "第二次世界大战", + "timelineEvent1957ce": "苏联发射斯普特尼克1号", + "timelineEvent1969ce": "阿波罗11号在月球着陆" +} + \ No newline at end of file diff --git a/lib/logic/app_logic.dart b/lib/logic/app_logic.dart new file mode 100644 index 00000000..2213093e --- /dev/null +++ b/lib/logic/app_logic.dart @@ -0,0 +1,69 @@ +import 'dart:async'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/utils/page_routes.dart'; + +class AppLogic { + /// Indicates to the rest of the app that bootstrap has not completed. + /// The router will use this to prevent redirects while bootstrapping. + bool isBootstrapComplete = false; + + /// Initialize the app and all main actors. + /// Loads settings, sets up services etc. + Future bootstrap() async { + // Default error handler + FlutterError.onError = _handleFlutterError; + + // Load any bitmaps the views might need + await AppBitmaps.init(); + + // Default to only allowing portrait mode + setDeviceOrientation(Axis.vertical); + + // Localizations load + await localeLogic.load(); + // Data load + await timelineLogic.init(); + // Settings load + await settingsLogic.load(); + // Collectibles init + await collectiblesLogic.load(); + + // flag bootStrap as complete + isBootstrapComplete = true; + + // load initial view (replace empty initial view) + if (settingsLogic.hasCompletedOnboarding.value) { + appRouter.go(ScreenPaths.home); + } else { + appRouter.go(ScreenPaths.intro); + } + } + + void setDeviceOrientation(Axis? axis) { + final orientations = []; + if (axis == null || axis == Axis.vertical) { + orientations.addAll([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + } + if (axis == null || axis == Axis.horizontal) { + orientations.addAll([ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); + } + SystemChrome.setPreferredOrientations(orientations); + } + + void _handleFlutterError(FlutterErrorDetails details) { + FlutterError.dumpErrorToConsole(details); + } + + Future showFullscreenDialogRoute(BuildContext context, Widget child) async { + return await Navigator.of(context).push( + PageRoutes.dialog(child, $styles.times.pageTransition), + ); + } +} diff --git a/lib/logic/collectibles_logic.dart b/lib/logic/collectibles_logic.dart new file mode 100644 index 00000000..f2ec907b --- /dev/null +++ b/lib/logic/collectibles_logic.dart @@ -0,0 +1,59 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/save_load_mixin.dart'; +import 'package:wonders/logic/data/collectible_data.dart'; + +class CollectiblesLogic with ThrottledSaveLoadMixin { + @override + String get fileName => 'collectibles.dat'; + + /// Holds all collectibles that the views should care about + final List all = collectiblesData; + + /// Current state for each collectible + final statesById = ValueNotifier>({}); + + CollectibleData? fromId(String? id) => id == null ? null : all.firstWhereOrNull((o) => o.id == id); + + List forWonder(WonderType wonder) { + return all.where((o) => o.wonder == wonder).toList(growable: false); + } + + void updateState(String id, int state) { + Map states = Map.of(statesById.value); + states[id] = state; + statesById.value = states; + scheduleSave(); + } + + bool isLost(WonderType wonderType, int i) { + final datas = forWonder(wonderType); + final states = statesById.value; + if (datas.length > i && states.containsKey(datas[i].id)) { + return states[datas[i].id] == CollectibleState.lost; + } + return true; + } + + void reset() { + Map states = {}; + for (int i = 0; i < all.length; i++) { + states[all[i].id] = CollectibleState.lost; + } + statesById.value = states; + debugPrint('collection reset'); + scheduleSave(); + } + + @override + void copyFromJson(Map value) { + Map states = {}; + for (int i = 0; i < all.length; i++) { + String id = all[i].id; + states[id] = value[id] ?? CollectibleState.lost; + } + statesById.value = states; + } + + @override + Map toJson() => statesById.value; +} diff --git a/lib/logic/common/color_utils.dart b/lib/logic/common/color_utils.dart new file mode 100644 index 00000000..68e194aa --- /dev/null +++ b/lib/logic/common/color_utils.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; + +class ColorUtils { + static Color shiftHsl(Color c, [double amt = 0]) { + var hslc = HSLColor.fromColor(c); + return hslc.withLightness((hslc.lightness + amt).clamp(0.0, 1.0)).toColor(); + } + + static Color parseHex(String value) => Color(int.parse(value.substring(1, 7), radix: 16) + 0xFF000000); + + static Color blend(Color dst, Color src, double opacity) { + return Color.fromARGB( + 255, + (dst.red.toDouble() * (1.0 - opacity) + src.red.toDouble() * opacity).toInt(), + (dst.green.toDouble() * (1.0 - opacity) + src.green.toDouble() * opacity).toInt(), + (dst.blue.toDouble() * (1.0 - opacity) + src.blue.toDouble() * opacity).toInt(), + ); + } +} diff --git a/lib/logic/common/debouncer.dart b/lib/logic/common/debouncer.dart new file mode 100644 index 00000000..ef467880 --- /dev/null +++ b/lib/logic/common/debouncer.dart @@ -0,0 +1,29 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class Debouncer { + Debouncer(this.interval); + final Duration interval; + + VoidCallback? _action; + Timer? _timer; + + void call(VoidCallback action) { + // Let the latest action override whatever was there before + _action = action; + // Always cancel and restart the timer + _timer?.cancel(); + _timer = Timer(interval, _callAction); + } + + void _callAction() { + _action?.call(); // If we have an action queued up, complete it. + _timer = null; + } + + void reset() { + _action = null; + _timer = null; + } +} diff --git a/lib/logic/common/http_client.dart b/lib/logic/common/http_client.dart new file mode 100644 index 00000000..c4b228ff --- /dev/null +++ b/lib/logic/common/http_client.dart @@ -0,0 +1,103 @@ +import 'dart:convert'; +import 'dart:developer' as dev; + +import 'package:http/http.dart' as http; +import 'package:wonders/logic/common/rest_utils.dart'; +import 'package:wonders/logic/common/string_utils.dart'; + +enum NetErrorType { + none, + disconnected, + timedOut, + denied, +} + +enum MethodType { get, post, put, patch, delete, head } + +typedef HttpRequest = Future Function(); + +class HttpClient { + static Future get(String url, {Map? headers}) async { + return await _request(() async { + return await http.get(Uri.parse(url), headers: headers); + }); + } + + static Future send(String url, + {Map? urlParams, + MethodType method = MethodType.get, + Map? headers, + dynamic body, + Encoding? encoding}) async { + HttpResponse? response; + + if (urlParams != null) { + Map urlParams = {}; + urlParams.forEach((key, value) { + urlParams[key] = value.toString(); + }); + + url += RestUtils.encodeParams(urlParams); + } + + response = await HttpClient.get(url, headers: headers); + return response; + } + + static Future _request(HttpRequest request) async { + http.Response response; + try { + response = await request(); + } on Exception catch (e) { + dev.log('Network call failed: ${e.toString()}'); + response = http.Response('ERROR: Could not get a response', 404); + } + return HttpResponse(response); + } +} + +class HttpResponse { + final http.Response? raw; + + NetErrorType? errorType; + + bool get success => errorType == NetErrorType.none; + + String? get body => raw?.body; + + Map? get headers => raw?.headers; + + int get statusCode => raw?.statusCode ?? -1; + + HttpResponse(this.raw) { + //No response at all, there must have been a connection error + if (raw == null) { + errorType = NetErrorType.disconnected; + } else if (raw!.statusCode >= 200 && raw!.statusCode <= 299) { + errorType = NetErrorType.none; + } else if (raw!.statusCode >= 500 && raw!.statusCode < 600) { + errorType = NetErrorType.timedOut; + } else if (raw!.statusCode >= 400 && raw!.statusCode < 500) { + errorType = NetErrorType.denied; + } + } +} + +class ServiceResult { + final HttpResponse response; + + R? content; + + bool get parseError => content == null; + bool get success => response.success && !parseError; + + ServiceResult(this.response, R Function(Map) parser) { + if (StringUtils.isNotEmpty(response.body) && response.success) { + try { + content = parser.call(jsonDecode(response.body!)); + } on FormatException catch (e) { + dev.log('ParseError: ${e.message}'); + } + } + } +} diff --git a/lib/logic/common/json_prefs_file.dart b/lib/logic/common/json_prefs_file.dart new file mode 100644 index 00000000..7c43b0fc --- /dev/null +++ b/lib/logic/common/json_prefs_file.dart @@ -0,0 +1,19 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; + +class JsonPrefsFile { + JsonPrefsFile(this.name); + final String name; + + Future> load() async { + final p = (await SharedPreferences.getInstance()).getString(name); + //print('loaded: $p'); + return Map.from(jsonDecode(p ?? '{}')); + } + + Future save(Map data) async { + //print('saving $data'); + await (await SharedPreferences.getInstance()).setString(name, jsonEncode(data)); + } +} diff --git a/lib/logic/common/platform_info.dart b/lib/logic/common/platform_info.dart new file mode 100644 index 00000000..b3d0fb8d --- /dev/null +++ b/lib/logic/common/platform_info.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; +import 'package:wonders/common_libs.dart'; + +class PlatformInfo { + static const _desktopPlatforms = [TargetPlatform.macOS, TargetPlatform.windows, TargetPlatform.linux]; + static const _mobilePlatforms = [TargetPlatform.android, TargetPlatform.iOS]; + + static bool get isDesktop => _desktopPlatforms.contains(defaultTargetPlatform); + static bool get isMobile => _mobilePlatforms.contains(defaultTargetPlatform); + + static double get pixelRatio => WidgetsBinding.instance.window.devicePixelRatio; + + static bool get isWindows => defaultTargetPlatform == TargetPlatform.windows; + static bool get isLinux => defaultTargetPlatform == TargetPlatform.linux; + static bool get isMacOS => defaultTargetPlatform == TargetPlatform.macOS; + static bool get isAndroid => defaultTargetPlatform == TargetPlatform.android; + static bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS; + + static Future get isConnected async => await InternetConnectionChecker().hasConnection; + static Future get isDisconnected async => (await isConnected) == false; +} diff --git a/lib/logic/common/rest_utils.dart b/lib/logic/common/rest_utils.dart new file mode 100644 index 00000000..3739c7d7 --- /dev/null +++ b/lib/logic/common/rest_utils.dart @@ -0,0 +1,12 @@ +class RestUtils { + static String encodeParams(Map params) { + var s = ''; + params.forEach((key, value) { + if (value.isNotEmpty && value != 'null') { + var urlEncode = Uri.encodeComponent(value); + s += '${s == '' ? '?' : '&'}$key=$urlEncode'; + } + }); + return s; + } +} diff --git a/lib/logic/common/retry_image.dart b/lib/logic/common/retry_image.dart new file mode 100644 index 00000000..5f5f0cae --- /dev/null +++ b/lib/logic/common/retry_image.dart @@ -0,0 +1,123 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; + +/// An image provider that retries if loading the bytes failed. +/// +/// Useful for network image requests that may transiently fail. +@immutable +class RetryImage extends ImageProvider { + /// Creates an object that uses [imageProvider] to fetch and decode an image, + /// and retries if fetching fails. + const RetryImage(this.imageProvider, {this.scale = 1.0, this.maxRetries = 4}); + + /// A wrapped image provider to use. + final ImageProvider imageProvider; + + /// The maximum number of times to retry. + final int maxRetries; + + /// The scale to place in the [ImageInfo] object of the image. + /// + /// Must be the same as the scale argument provided to [imageProvider], if + /// any. + final double scale; + + @override + Future obtainKey(ImageConfiguration configuration) { + Completer? completer; + // If the imageProvider.obtainKey future is synchronous, then we will be able to fill in result with + // a value before completer is initialized below. + SynchronousFuture? result; + imageProvider.obtainKey(configuration).then((Object key) { + if (completer == null) { + // This future has completed synchronously (completer was never assigned), + // so we can directly create the synchronous result to return. + result = SynchronousFuture(key); + } else { + // This future did not synchronously complete. + completer.complete(key); + } + }); + if (result != null) { + return result!; + } + // If the code reaches here, it means the imageProvider.obtainKey was not + // completed sync, so we initialize the completer for completion later. + completer = Completer(); + return completer.future; + } + + ImageStreamCompleter _commonLoad(ImageStreamCompleter Function() loader) { + final _DelegatingImageStreamCompleter completer = _DelegatingImageStreamCompleter(); + ImageStreamCompleter completerToWrap = loader(); + late ImageStreamListener listener; + + Duration duration = const Duration(milliseconds: 250); + int count = 1; + listener = ImageStreamListener( + (ImageInfo image, bool synchronousCall) { + completer.addImage(image); + }, + onChunk: completer._reportChunkEvent, + onError: (Object exception, StackTrace? stackTrace) { + completerToWrap.removeListener(listener); + if (count > maxRetries) { + completer.reportError(exception: exception, stack: stackTrace); + return; + } + Future.delayed(duration).then((void v) { + duration *= 2; + completerToWrap = loader(); + count += 1; + completerToWrap.addListener(listener); + }); + }, + ); + completerToWrap.addListener(listener); + + completer.addOnLastListenerRemovedCallback(() { + completerToWrap.removeListener(listener); + }); + + return completer; + } + + @override + // ignore: deprecated_member_use + ImageStreamCompleter load(Object key, DecoderCallback decode) { + // ignore: deprecated_member_use + return _commonLoad(() => imageProvider.load(key, decode)); + } + + @override + ImageStreamCompleter loadBuffer(Object key, DecoderBufferCallback decode) { + return _commonLoad(() => imageProvider.loadBuffer(key, decode)); + } + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is RetryImage && other.imageProvider == other.imageProvider && other.scale == scale; + } + + @override + int get hashCode => Object.hash(imageProvider, scale); + + @override + String toString() => + '${objectRuntimeType(this, 'RetryImage')}(imageProvider: $imageProvider, maxRetries: $maxRetries, scale: $scale)'; +} + +class _DelegatingImageStreamCompleter extends ImageStreamCompleter { + void addImage(ImageInfo info) { + setImage(info); + } + + void _reportChunkEvent(ImageChunkEvent event) { + reportImageChunkEvent(event); + } +} diff --git a/lib/logic/common/save_load_mixin.dart b/lib/logic/common/save_load_mixin.dart new file mode 100644 index 00000000..affd5f25 --- /dev/null +++ b/lib/logic/common/save_load_mixin.dart @@ -0,0 +1,33 @@ +import 'package:flutter/foundation.dart'; +import 'package:wonders/logic/common/json_prefs_file.dart'; +import 'package:wonders/logic/common/throttler.dart'; + +mixin ThrottledSaveLoadMixin { + late final _file = JsonPrefsFile(fileName); + final _throttle = Throttler(const Duration(seconds: 2)); + + Future load() async { + final results = await _file.load(); + try { + copyFromJson(results); + } on Exception catch (e) { + debugPrint(e.toString()); + } + } + + Future save() async { + debugPrint('Saving...'); + try { + await _file.save(toJson()); + } on Exception catch (e) { + debugPrint(e.toString()); + } + } + + Future scheduleSave() async => _throttle.call(save); + + /// Serialization + String get fileName; + Map toJson(); + void copyFromJson(Map value); +} diff --git a/lib/logic/common/string_utils.dart b/lib/logic/common/string_utils.dart new file mode 100644 index 00000000..564b852e --- /dev/null +++ b/lib/logic/common/string_utils.dart @@ -0,0 +1,80 @@ +import 'package:wonders/common_libs.dart'; + +class StringUtils { + static bool isEmpty(String? s) { + return s == null || s.trim().isEmpty; + } + + static bool isNotEmpty(String? s) => !isEmpty(s); + + static bool isLink(String str) => + str.contains(RegExp(r'^(https?:\/\/)?([\w\d_-]+)\.([\w\d_\.-]+)\/?\??([^#\n\r]*)?#?([^\n\r]*)')); + + static String truncateWithEllipsis(int cutoff, String myString) { + return (myString.length <= cutoff) ? myString : '${myString.substring(0, cutoff)}...'; + } + + static String printMap(Map? map) { + String str = ''; + map?.forEach((key, value) => str += '$key: ${value.toString}, '); + return str; + } + + static Size measure(String text, TextStyle style, {int maxLines = 1, TextDirection direction = TextDirection.ltr}) { + final TextPainter textPainter = + TextPainter(text: TextSpan(text: text, style: style), maxLines: maxLines, textDirection: direction) + ..layout(minWidth: 0, maxWidth: double.infinity); + return textPainter.size; + } + + static double measureLongest(List items, TextStyle style, [maxItems]) { + double l = 0; + if (maxItems != null && maxItems < items.length) { + items.length = maxItems; + } + for (var item in items) { + double m = StringUtils.measure(item, style).width; + if (m > l) l = m; + } + return l; + } + + /// Gracefully handles null values, and skips the suffix when null + static String safeGet(String value, [String? suffix]) { + return value + (value.isNotEmpty ? suffix ?? '' : ''); + } + + static String formatYr(int yr) { + if (yr == 0) yr = 1; + return supplant( + $strings.yearFormat, + { + '{date}': yr.abs().toString(), + '{era}': getYrSuffix(yr), + }, + ); + } + + static String getYrSuffix(int yr) => yr < 0 ? $strings.yearBCE : $strings.yearCE; + + static String getEra(int yr) { + if (yr <= -600) return $strings.eraPrehistory; + if (yr <= 476) return $strings.eraClassical; + if (yr <= 1450) return $strings.eraEarlyModern; + return $strings.eraModern; + } + + static String capitalize(String value) { + return '${value[0].toUpperCase()}${value.substring(1).toLowerCase()}'; + } + + static String supplant(String value, Map supplants) { + return value.replaceAllMapped(RegExp(r'\{\w+\}'), (match) { + final placeholder = match.group(0) ?? ''; + if (supplants.containsKey(placeholder)) { + return supplants[placeholder]!; + } + return placeholder; + }); + } +} diff --git a/lib/logic/common/throttler.dart b/lib/logic/common/throttler.dart new file mode 100644 index 00000000..65fd4ee1 --- /dev/null +++ b/lib/logic/common/throttler.dart @@ -0,0 +1,35 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class Throttler { + Throttler(this.interval); + final Duration interval; + + VoidCallback? _action; + Timer? _timer; + + void call(VoidCallback action, {bool immediateCall = true}) { + // Let the latest action override whatever was there before + _action = action; + // If no timer is running, we want to start one + if (_timer == null) { + // If immediateCall is true, we handle the action now + if (immediateCall) { + _callAction(); + } + // Start a timer that will temporarily throttle subsequent calls, and eventually make a call to whatever _action is (if anything) + _timer = Timer(interval, _callAction); + } + } + + void _callAction() { + _action?.call(); // If we have an action queued up, complete it. + _timer = null; + } + + void reset() { + _action = null; + _timer = null; + } +} diff --git a/lib/logic/data/artifact_data.dart b/lib/logic/data/artifact_data.dart new file mode 100644 index 00000000..1a7bc95e --- /dev/null +++ b/lib/logic/data/artifact_data.dart @@ -0,0 +1,31 @@ +class ArtifactData { + ArtifactData({ + required this.objectId, + required this.title, + required this.image, + required this.date, + required this.period, + required this.country, + required this.medium, + required this.dimension, + required this.classification, + required this.culture, + required this.objectType, + required this.objectBeginYear, + required this.objectEndYear, + }); + final String objectId; // Artifact ID, used to identify through MET server calls. + final String title; // Artifact title / name + final String image; // Artifact primary image URL (can have multiple) + final int objectBeginYear; // Artifact creation year start. + final int objectEndYear; // Artifact creation year end. + final String objectType; // Type of thing (coin, basic, cup etc) + + final String date; // Date of creation + final String period; // Time period of creation + final String country; // Country of origin + final String medium; // Art medium + final String dimension; // Width and height of physical artifact + final String classification; // Type of artifact + final String culture; // Culture of artifact +} diff --git a/lib/logic/data/collectible_data.dart b/lib/logic/data/collectible_data.dart new file mode 100644 index 00000000..06ea731c --- /dev/null +++ b/lib/logic/data/collectible_data.dart @@ -0,0 +1,246 @@ +import 'package:wonders/common_libs.dart'; + +class CollectibleState { + static const int lost = 0; + static const int discovered = 1; + static const int explored = 2; +} + +class CollectibleData { + CollectibleData({ + required this.title, + required this.imageUrl, + required this.imageUrlSmall, + required this.iconName, + required this.artifactId, + required this.wonder, + }) { + icon = AssetImage('${ImagePaths.collectibles}/$iconName.png'); + } + + final String title; + final String imageUrl; + final String imageUrlSmall; + final String iconName; + + late final ImageProvider icon; + + final String artifactId; + final WonderType wonder; + + String get id => artifactId; + String get subtitle => wondersLogic.getData(wonder).artifactCulture; +} + +// Note: look up a human readable page with: +// https://www.metmuseum.org/art/collection/search/503940 +// where 503940 is the ID. +List collectiblesData = [ + // chichenItza + CollectibleData( + title: 'Pendant', + wonder: WonderType.chichenItza, + artifactId: '701645', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-25104-001.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/mobile-large/DP-25104-001.jpg', + iconName: 'jewelry', + ), + CollectibleData( + title: 'Bird Ornament', + wonder: WonderType.chichenItza, + artifactId: '310555', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-23474-001.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/mobile-large/DP-23474-001.jpg', + iconName: 'jewelry', + ), + CollectibleData( + title: 'La Prison, à Chichen-Itza', + wonder: WonderType.chichenItza, + artifactId: '286467', + imageUrl: 'https://images.metmuseum.org/CRDImages/ph/original/DP132063.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ph/mobile-large/DP132063.jpg', + iconName: 'picture', + ), + + // christRedeemer + CollectibleData( + title: 'Engraved Horn', + wonder: WonderType.christRedeemer, + artifactId: '501302', + imageUrl: 'https://images.metmuseum.org/CRDImages/mi/original/MUS550A2.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/mi/mobile-large/MUS550A2.jpg', + iconName: 'statue', + ), + CollectibleData( + title: 'Fixed fan', + wonder: WonderType.christRedeemer, + artifactId: '157985', + imageUrl: 'https://images.metmuseum.org/CRDImages/ci/original/48.60_front_CP4.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ci/mobile-large/48.60_front_CP4.jpg', + iconName: 'jewelry', + ), + CollectibleData( + title: 'Handkerchiefs (one of two)', + wonder: WonderType.christRedeemer, + artifactId: '227759', + imageUrl: 'https://images.metmuseum.org/CRDImages/ad/original/DP2896.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ad/mobile-large/DP2896.jpg', + iconName: 'textile', + ), + + // colosseum + CollectibleData( + title: 'Glass hexagonal amphoriskos', + wonder: WonderType.colosseum, + artifactId: '245376', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP124005.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/mobile-large/DP124005.jpg', + iconName: 'vase', + ), + CollectibleData( + title: 'Bronze plaque of Mithras slaying the bull', + wonder: WonderType.colosseum, + artifactId: '256570', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP119236.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/mobile-large/DP119236.jpg', + iconName: 'statue', + ), + CollectibleData( + title: 'Interno del Colosseo', + wonder: WonderType.colosseum, + artifactId: '286136', + imageUrl: 'https://images.metmuseum.org/CRDImages/ph/original/DP138036.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ph/mobile-large/DP138036.jpg', + iconName: 'picture', + ), + + // greatWall + CollectibleData( + title: 'Biographies of Lian Po and Lin Xiangru', + wonder: WonderType.greatWall, + artifactId: '39918', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP153769.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/mobile-large/DP153769.jpg', + iconName: 'scroll', + ), + CollectibleData( + title: 'Jar with Dragon', + wonder: WonderType.greatWall, + artifactId: '39666', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DT5083.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/mobile-large/DT5083.jpg', + iconName: 'vase', + ), + CollectibleData( + title: 'Panel with Peonies and Butterfly', + wonder: WonderType.greatWall, + artifactId: '39735', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DT834.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/mobile-large/DT834.jpg', + iconName: 'textile', + ), + + // machuPicchu + CollectibleData( + title: 'Eight-Pointed Star Tunic', + wonder: WonderType.machuPicchu, + artifactId: '308120', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/ra33.149.100.R.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/mobile-large/ra33.149.100.R.jpg', + iconName: 'textile', + ), + CollectibleData( + title: 'Camelid figurine', + wonder: WonderType.machuPicchu, + artifactId: '309960', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-13440-031.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/mobile-large/DP-13440-031.jpg', + iconName: 'statue', + ), + CollectibleData( + title: 'Double Bowl', + wonder: WonderType.machuPicchu, + artifactId: '313341', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-24356-001.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/mobile-large/DP-24356-001.jpg', + iconName: 'vase', + ), + + // petra + CollectibleData( + title: 'Camel and riders', + wonder: WonderType.petra, + artifactId: '322592', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/DP-14352-001.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/mobile-large/DP-14352-001.jpg', + iconName: 'statue', + ), + CollectibleData( + title: 'Vessel', + wonder: WonderType.petra, + artifactId: '325918', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/hb67_246_37.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/mobile-large/hb67_246_37.jpg', + iconName: 'vase', + ), + CollectibleData( + title: 'Open bowl', + wonder: WonderType.petra, + artifactId: '326243', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/DT904.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/mobile-large/DT904.jpg', + iconName: 'vase', + ), + + // pyramidsGiza + CollectibleData( + title: 'Two papyrus fragments', + wonder: WonderType.pyramidsGiza, + artifactId: '546510', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/09.180.537A_recto_0083.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/mobile-large/09.180.537A_recto_0083.jpg', + iconName: 'scroll', + ), + CollectibleData( + title: 'Fragmentary Face of King Khafre', + wonder: WonderType.pyramidsGiza, + artifactId: '543896', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DT11751.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/mobile-large/DT11751.jpg', + iconName: 'statue', + ), + CollectibleData( + title: 'Jewelry Elements', + wonder: WonderType.pyramidsGiza, + artifactId: '545728', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DP327402.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/mobile-large/DP327402.jpg', + iconName: 'jewelry', + ), + + // tajMahal + CollectibleData( + title: 'Dagger with Scabbard', + wonder: WonderType.tajMahal, + artifactId: '24907', + imageUrl: 'https://images.metmuseum.org/CRDImages/aa/original/DP157706.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/aa/mobile-large/DP157706.jpg', + iconName: 'jewelry', + ), + CollectibleData( + title: 'The House of Bijapur', + wonder: WonderType.tajMahal, + artifactId: '453183', + imageUrl: 'https://images.metmuseum.org/CRDImages/is/original/DP231353.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/is/mobile-large/DP231353.jpg', + iconName: 'picture', + ), + CollectibleData( + title: 'Panel of Nasta\'liq Calligraphy', + wonder: WonderType.tajMahal, + artifactId: '453983', + imageUrl: 'https://images.metmuseum.org/CRDImages/is/original/DP299944.jpg', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/is/mobile-large/DP299944.jpg', + iconName: 'scroll', + ), +]; diff --git a/lib/logic/data/highlight_data.dart b/lib/logic/data/highlight_data.dart new file mode 100644 index 00000000..5bda91d8 --- /dev/null +++ b/lib/logic/data/highlight_data.dart @@ -0,0 +1,476 @@ +import 'package:wonders/common_libs.dart'; + +class HighlightData { + HighlightData({ + required this.title, + required this.imageUrl, + required this.imageUrlSmall, + required this.culture, + required this.artifactId, + required this.wonder, + required this.date, + }); + + static HighlightData? fromId(String? id) => id == null ? null : _highlights.firstWhereOrNull((o) => o.id == id); + static List forWonder(WonderType wonder) => + _highlights.where((o) => o.wonder == wonder).toList(growable: false); + static List get all => _highlights; + + final String title; + final String imageUrl; + final String imageUrlSmall; + final String culture; + final String date; + + late final ImageProvider icon; + + final String artifactId; + final WonderType wonder; + + String get id => artifactId; + String get subtitle => wondersLogic.getData(wonder).artifactCulture; +} + +// Note: look up a human readable page with: +// https://www.metmuseum.org/art/collection/search/503940 +// where 503940 is the ID. +List _highlights = [ + // chichenItza + HighlightData( + title: 'Double Whistle', + wonder: WonderType.chichenItza, + artifactId: '503940', + culture: 'Mayan', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/mi/web-large/DT4624a.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/mi/original/DT4624a.jpg', + date: '7th–9th century', + ), + HighlightData( + title: 'Seated Female Figure', + wonder: WonderType.chichenItza, + artifactId: '312595', + culture: 'Maya', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP-12659-001.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-12659-001.jpg', + date: '6th–9th century', + ), + HighlightData( + title: 'Censer Support', + wonder: WonderType.chichenItza, + artifactId: '310551', + culture: 'Maya', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP102949.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP102949.jpg', + date: 'mid-7th–9th century', + ), + HighlightData( + title: 'Tripod Plate', + wonder: WonderType.chichenItza, + artifactId: '316304', + culture: 'Maya', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP219258.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP219258.jpg', + date: '9th–10th century', + ), + HighlightData( + title: 'Costumed Figure', + wonder: WonderType.chichenItza, + artifactId: '313151', + culture: 'Maya', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/1979.206.953_a.JPG', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/1979.206.953_a.JPG', + date: '7th–8th century', + ), + HighlightData( + title: 'Mirror-Bearer', + wonder: WonderType.chichenItza, + artifactId: '313256', + culture: 'Maya', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP-24340-001.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-24340-001.jpg', + date: '6th century', + ), + +// christRedeemer + HighlightData( + title: '[Studio Portrait: Male Street Vendor Holding Box of Flowers, Brazil]', + wonder: WonderType.christRedeemer, + artifactId: '764815', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ph/web-large/DP-15801-131.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ph/original/DP-15801-131.jpg', + date: '1864–66', + ), + HighlightData( + title: 'Rattle', + wonder: WonderType.christRedeemer, + artifactId: '502019', + culture: 'Native American (Brazilian)', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/mi/web-large/midp89.4.1453.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/mi/original/midp89.4.1453.jpg', + date: '19th century', + ), + HighlightData( + title: '[Studio Portrait: Two Males Wearing Hats and Ponchos, Brazil]', + wonder: WonderType.christRedeemer, + artifactId: '764814', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ph/web-large/DP-15801-129.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ph/original/DP-15801-129.jpg', + date: '1864–66', + ), + HighlightData( + title: '[Studio Portrait: Female Street Vendor Seated Wearing Turban, Brazil]', + wonder: WonderType.christRedeemer, + artifactId: '764816', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ph/web-large/DP-15801-133.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ph/original/DP-15801-133.jpg', + date: '1864–66', + ), + HighlightData( + title: 'Pluriarc', + wonder: WonderType.christRedeemer, + artifactId: '501319', + culture: 'African American (Brazil - Afro-Brazilian?)', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/mi/web-large/midp89.4.703.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/mi/original/midp89.4.703.jpg', + date: 'late 19th century', + ), + +// colosseum + HighlightData( + title: 'Marble portrait of a young woman', + wonder: WonderType.colosseum, + artifactId: '251350', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP331280.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP331280.jpg', + date: 'A.D. 150–175', + ), + HighlightData( + title: 'Silver mirror', + wonder: WonderType.colosseum, + artifactId: '255960', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP145605.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP145605.jpg', + date: '4th century A.D.', + ), + HighlightData( + title: 'Marble portrait of the emperor Augustus', + wonder: WonderType.colosseum, + artifactId: '247993', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP337220.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP337220.jpg', + date: 'ca. A.D. 14–37', + ), + HighlightData( + title: 'Terracotta medallion', + wonder: WonderType.colosseum, + artifactId: '250464', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP105842.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP105842.jpg', + date: 'late 2nd–early 3rd century A.D.', + ), + HighlightData( + title: 'Marble head and torso of Athena', + wonder: WonderType.colosseum, + artifactId: '251476', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP357289.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP357289.jpg', + date: '1st–2nd century A.D.', + ), + HighlightData( + title: 'Silver mirror', + wonder: WonderType.colosseum, + artifactId: '255960', + culture: 'Roman', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/gr/web-large/DP145605.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/gr/original/DP145605.jpg', + date: '4th century A.D.', + ), + +// greatWall + HighlightData( + title: 'Cape', + wonder: WonderType.greatWall, + artifactId: '79091', + culture: 'French', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ci/web-large/DT2183.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ci/original/DT2183.jpg', + date: 'second half 16th century', + ), + HighlightData( + title: 'Censer in the form of a mythical beast', + wonder: WonderType.greatWall, + artifactId: '781812', + culture: 'China', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP-17100-001.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP-17100-001.jpg', + date: 'early 17th century', + ), + HighlightData( + title: 'Dish with peafowls and peonies', + wonder: WonderType.greatWall, + artifactId: '40213', + culture: 'China', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP704217.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP704217.jpg', + date: 'early 15th century', + ), + HighlightData( + title: 'Base for a mandala', + wonder: WonderType.greatWall, + artifactId: '40765', + culture: 'China', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP229015.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP229015.jpg', + date: '15th century', + ), + HighlightData( + title: 'Bodhisattva Manjushri as Tikshna-Manjushri (Minjie Wenshu)', + wonder: WonderType.greatWall, + artifactId: '57612', + culture: 'China', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP164061.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP164061.jpg', + date: '', + ), + HighlightData( + title: 'Tripod incense burner with lid', + wonder: WonderType.greatWall, + artifactId: '666573', + culture: 'China', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP356342.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP356342.jpg', + date: 'early 15th century', + ), + +// machuPicchu + HighlightData( + title: 'Face Beaker', + wonder: WonderType.machuPicchu, + artifactId: '313295', + culture: 'Inca', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DT9410.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DT9410.jpg', + date: '14th–early 16th century', + ), + HighlightData( + title: 'Feathered Bag', + wonder: WonderType.machuPicchu, + artifactId: '316926', + culture: 'Inca', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP158704.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP158704.jpg', + date: '15th–early 16th century', + ), + HighlightData( + title: 'Female Figurine', + wonder: WonderType.machuPicchu, + artifactId: '309944', + culture: 'Inca', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP-13440-023.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-13440-023.jpg', + date: '1400–1533', + ), + HighlightData( + title: 'Stirrup Spout Bottle with Felines', + wonder: WonderType.machuPicchu, + artifactId: '309436', + culture: 'Moche', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/67.92.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/67.92.jpg', + date: '4th–7th century', + ), + HighlightData( + title: 'Camelid figurine', + wonder: WonderType.machuPicchu, + artifactId: '309960', + culture: 'Inca', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP-13440-031.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP-13440-031.jpg', + date: '1400–1533', + ), + HighlightData( + title: 'Temple Model', + wonder: WonderType.machuPicchu, + artifactId: '316873', + culture: 'Aztec', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/ao/web-large/DP341942.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/ao/original/DP341942.jpg', + date: '1400–1521', + ), + +// petra + HighlightData( + title: 'Unguentarium', + wonder: WonderType.petra, + artifactId: '325900', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_19.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_19.jpg', + date: 'ca. 1st century A.D.', + ), + HighlightData( + title: 'Cooking pot', + wonder: WonderType.petra, + artifactId: '325902', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_21.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_21.jpg', + date: 'ca. 1st century A.D.', + ), + HighlightData( + title: 'Lamp', + wonder: WonderType.petra, + artifactId: '325919', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_38.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_38.jpg', + date: 'ca. 1st century A.D.', + ), + HighlightData( + title: 'Bowl', + wonder: WonderType.petra, + artifactId: '325884', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_3.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_3.jpg', + date: 'ca. 1st century A.D.', + ), + HighlightData( + title: 'Small lamp', + wonder: WonderType.petra, + artifactId: '325887', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_6.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_6.jpg', + date: 'ca. 1st century A.D.', + ), + HighlightData( + title: 'Male figurine', + wonder: WonderType.petra, + artifactId: '325891', + culture: 'Nabataean', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/an/web-large/ME67_246_10.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/an/original/ME67_246_10.jpg', + date: 'ca. 1st century A.D.', + ), + +// pyramidsGiza + HighlightData( + title: 'Guardian Figure', + wonder: WonderType.pyramidsGiza, + artifactId: '543864', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/DP330260.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DP330260.jpg', + date: 'ca. 1919–1885 B.C.', + ), + HighlightData( + title: 'Relief fragment', + wonder: WonderType.pyramidsGiza, + artifactId: '546488', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/LC-34_1_183_EGDP033257.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/LC-34_1_183_EGDP033257.jpg', + date: 'ca. 1981–1640 B.C.', + ), + HighlightData( + title: 'Ring with Uninscribed Scarab', + wonder: WonderType.pyramidsGiza, + artifactId: '557137', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/15.3.205_EGDP015425.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/15.3.205_EGDP015425.jpg', + date: 'ca. 1850–1640 B.C.', + ), + HighlightData( + title: 'Nikare as a scribe', + wonder: WonderType.pyramidsGiza, + artifactId: '543900', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/DP240451.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DP240451.jpg', + date: 'ca. 2420–2389 B.C. or later', + ), + HighlightData( + title: 'Seated Statue of King Menkaure', + wonder: WonderType.pyramidsGiza, + artifactId: '543935', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/DP109397.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DP109397.jpg', + date: 'ca. 2490–2472 B.C.', + ), + HighlightData( + title: 'Floral collar from Tutankhamun\'s Embalming Cache', + wonder: WonderType.pyramidsGiza, + artifactId: '544782', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/eg/web-large/DP225343.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/eg/original/DP225343.jpg', + date: 'ca. 1336–1327 B.C.', + ), + +// tajMahal + HighlightData( + title: 'Mango-Shaped Flask', + wonder: WonderType.tajMahal, + artifactId: '453341', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/is/web-large/DP240307.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/is/original/DP240307.jpg', + date: 'mid-17th century', + ), + HighlightData( + title: 'Base for a Water Pipe (Huqqa) with Irises', + wonder: WonderType.tajMahal, + artifactId: '453243', + culture: '', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/is/web-large/DP214317.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/is/original/DP214317.jpg', + date: 'late 17th century', + ), + HighlightData( + title: 'Plate', + wonder: WonderType.tajMahal, + artifactId: '73309', + culture: 'India (Gujarat)', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP138506.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP138506.jpg', + date: 'mid-16th–17th century', + ), + HighlightData( + title: 'Helmet', + wonder: WonderType.tajMahal, + artifactId: '24932', + culture: 'Indian, Mughal', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/aa/web-large/1988.147_007mar2015.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/aa/original/1988.147_007mar2015.jpg', + date: '18th century', + ), + HighlightData( + title: 'Jewelled plate', + wonder: WonderType.tajMahal, + artifactId: '56230', + culture: 'India', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/as/web-large/DP-14153-029.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/as/original/DP-14153-029.jpg', + date: '18th–19th century', + ), + HighlightData( + title: 'Shirt of Mail and Plate of Emperor Shah Jahan (reigned 1624–58)', + wonder: WonderType.tajMahal, + artifactId: '35633', + culture: 'Indian', + imageUrlSmall: 'https://images.metmuseum.org/CRDImages/aa/web-large/DP219616.jpg', + imageUrl: 'https://images.metmuseum.org/CRDImages/aa/original/DP219616.jpg', + date: 'dated A.H. 1042/A.D. 1632–33', + ), +]; diff --git a/lib/logic/data/timeline_data.dart b/lib/logic/data/timeline_data.dart new file mode 100644 index 00000000..40a57c9a --- /dev/null +++ b/lib/logic/data/timeline_data.dart @@ -0,0 +1,55 @@ +import 'package:wonders/common_libs.dart'; + +class TimelineEvent { + TimelineEvent(this.year, this.description); + final int year; + final String description; +} + +class GlobalEventsData { + final globalEvents = [ + TimelineEvent(-2900, $strings.timelineEvent2900bce), + TimelineEvent(-2700, $strings.timelineEvent2700bce), + TimelineEvent(-2600, $strings.timelineEvent2600bce), + TimelineEvent(-2560, $strings.timelineEvent2560bce), + TimelineEvent(-2500, $strings.timelineEvent2500bce), + TimelineEvent(-2200, $strings.timelineEvent2200bce), + TimelineEvent(-2000, $strings.timelineEvent2000bce), + TimelineEvent(-1800, $strings.timelineEvent1800bce), + TimelineEvent(-890, $strings.timelineEvent890bce), + TimelineEvent(-776, $strings.timelineEvent776bce), + TimelineEvent(-753, $strings.timelineEvent753bce), + TimelineEvent(-447, $strings.timelineEvent447bce), + TimelineEvent(-427, $strings.timelineEvent427bce), + TimelineEvent(-322, $strings.timelineEvent322bce), + TimelineEvent(-200, $strings.timelineEvent200bce), + TimelineEvent(-44, $strings.timelineEvent44bce), + TimelineEvent(-4, $strings.timelineEvent4bce), + TimelineEvent(43, $strings.timelineEvent43ce), + TimelineEvent(79, $strings.timelineEvent79ce), + TimelineEvent(455, $strings.timelineEvent455ce), + TimelineEvent(500, $strings.timelineEvent500ce), + TimelineEvent(632, $strings.timelineEvent632ce), + TimelineEvent(793, $strings.timelineEvent793ce), + TimelineEvent(800, $strings.timelineEvent800ce), + TimelineEvent(1001, $strings.timelineEvent1001ce), + TimelineEvent(1077, $strings.timelineEvent1077ce), + TimelineEvent(1117, $strings.timelineEvent1117ce), + TimelineEvent(1199, $strings.timelineEvent1199ce), + TimelineEvent(1227, $strings.timelineEvent1227ce), + TimelineEvent(1337, $strings.timelineEvent1337ce), + TimelineEvent(1347, $strings.timelineEvent1347ce), + TimelineEvent(1428, $strings.timelineEvent1428ce), + TimelineEvent(1439, $strings.timelineEvent1439ce), + TimelineEvent(1492, $strings.timelineEvent1492ce), + TimelineEvent(1760, $strings.timelineEvent1760ce), + TimelineEvent(1763, $strings.timelineEvent1763ce), + TimelineEvent(1783, $strings.timelineEvent1783ce), + TimelineEvent(1789, $strings.timelineEvent1789ce), + TimelineEvent(1914, $strings.timelineEvent1914ce), + TimelineEvent(1929, $strings.timelineEvent1929ce), + TimelineEvent(1939, $strings.timelineEvent1939ce), + TimelineEvent(1957, $strings.timelineEvent1957ce), + TimelineEvent(1969, $strings.timelineEvent1969ce), + ]; +} diff --git a/lib/logic/data/unsplash_photo_data.dart b/lib/logic/data/unsplash_photo_data.dart new file mode 100644 index 00000000..8943c172 --- /dev/null +++ b/lib/logic/data/unsplash_photo_data.dart @@ -0,0 +1,246 @@ +import 'package:wonders/_tools/unsplash_download_service.dart'; +import 'package:wonders/logic/common/platform_info.dart'; + +enum UnsplashPhotoSize { med, large, xl } + +class UnsplashPhotoData { + UnsplashPhotoData({ + required this.id, + required this.url, + }); + final String id; + final String url; + + String getUnsplashUrl(int size) => '$url?q=90&fm=jpg&w=$size&fit=max'; + + static String getSelfHostedUrl(String id, UnsplashPhotoSize targetSize) { + late int size; + switch (targetSize) { + case UnsplashPhotoSize.med: + size = 400; + break; + case UnsplashPhotoSize.large: + size = 800; + break; + case UnsplashPhotoSize.xl: + size = 1200; + break; + } + if (PlatformInfo.pixelRatio >= 1.5) { + size *= 2; + } + return 'https://wonderous.info/unsplash/$id-$size.jpg'; + } + + /// List of image ids by collection. This can be generated with the [UnsplashDownloadService].generateUnsplashCollectionsClass(). + static final photosByCollectionId = { + 'SUK0tuMnLLw': [ + 'T-A0pEYn_0w', + 'wO3o3ibEfxw', + '5GSXOxJiF70', + 'SHTEX8-4knw', + 'zhmpjgrYM4U', + 'qT2toWR8x2M', + '3u1UUdUW0dM', + 'ZTuNK61OFjE', + 'SAC9IP2L4ww', + 'YuRSdBq2Pks', + '7g4afdiVhF8', + '3XMsTfVnesg', + 'CdCeLu7Zi_U', + '_sSK8shpE_0', + 'J0tUqWIIrYY', + 'gBPNJ-dq9C8', + 'VPavA7BBxK0', + 'UfK0P6WygEE', + '8L7mOETNgHA', + 'zYix52PYbXk', + '_iklK8oQKPs', + 'Yt0GMo9DcTg', + '9h9bzwTzZHc', + 'MeBRK0Ypo9M' + ], + '684IRta86_c': [ + 'gwraJHxsMd0', + '8tMxz9MRJHc', + 'ywdQtrOmjmg', + 'XtZNcroGK3E', + 'TgfdQeODkVY', + 'B3-lUYDbyDo', + 'sAB4BWrQ4Y4', + 'cKICrMrWHqk', + '_0Niuvr92LU', + 'SHDG46hSVfg', + '28s5r-zA6Lw', + 'ZpN1lhola0s', + 'BpSLyvZIW2E', + 'Hqk4zmLXUuw', + 'E2NtGFv9lX0', + 'IosnvXR94qI', + 'w-oUzSufu8A', + 'FddqGrvwoyE', + 'Axl4311WzQU', + 'BCEexmxL9EQ', + 'mwV6PsB-7Bk', + 'VmVk172z034', + '0pG9XKXCj4s', + 'eU4pipU_8HA' + ], + 'dPgX5iK8Ufo': [ + '0jcM54HBO4c', + 'fcaeEfnh818', + 'bQFxKVLV6SY', + 'eTIG213bO4U', + '8yYYyxdcyrY', + 'zeH77USuASc', + 'okvoZRLXOsw', + '21AiwkRfAnY', + '-e0u9SAFeP4', + 'BVj6AnXs3TA', + 'd2kxugivOJo', + 'RSvKa3Jg3Z8', + 'cG2JQnPfmAw', + 'o-bc8wuY29c', + 'xec7srO4U5c', + '4nJOD8wtMS4', + 'JoJuM-n20zo', + 'CErddu-JwKw', + 'GTLJklnjn-E', + 'DSm50vbc9F0', + 'ka-HD2cZYec', + '8tTh9isJoos', + 'OkiDIla7K8Q', + 'xaIEq6owYvM' + ], + 'VPdti8Kjq9o': [ + 'rhRk8J33Bcc', + 'INA89gCRuNo', + 'K-TOsIh8FxE', + 'grlIoctRp1o', + 'mCLvY_GsdW8', + 'GHA2X73SRL8', + 'BLGigyFXbPo', + 'j-1UlgFdpx8', + 'iIINwmkBizU', + 'PWdWPYF0_RY', + 'kdlTfNzJBx0', + 'LUdSrJw-BII', + '3mzn2xpA2YA', + 'nAb-SFzL1GM', + 's87bBFZviAU', + 'sKYsYzEG2Lg', + '3b4e9D731ME', + 'haLyuhP6oLE', + 'lUO-BjCiZEA', + 'TWOrGJ5REuY', + 'VFRTXGw1VjU', + 'ckotRXopwRM', + 'CzKI2-nejUk', + 'Q4g0Q-eVVEg' + ], + 'Kg_h04xvZEo': [ + 'eq4OpDuGN7w', + 'cSKa2PDcU-Q', + 'MLfwSItwSpg', + '1xnuIi-zcTQ', + '20Nfb3kTnsY', + 'Wbu_scb-9HQ', + '0FMRVVrMCyc', + 'Q36BvLGdOAg', + 'RyGG5z6SUZ8', + 'ZQxxar2ovS0', + 'siy5LCp84AY', + 'chc2vP_7_kY', + 'i56swU7BDbQ', + '6aZBfbzx5Ms', + 'VW8YW3Xlc0k', + 'MZayf0ZVY-A', + 'fDxfe1_5VyY', + 'E13mcj-2TLE', + 'sGPfjjAOX1o', + 'd0VxLuvjUJA', + 'lzwT4n05p20', + 'OXL47qN9brQ', + 'vhKZvNFmpPU', + '2s1chnvuMQ4' + ], + 'wUhgZTyUnl8': [ + 'UiUtIG0xLPM', + 'qytSCIVTTc4', + 'Ewm_GNF15E4', + 'Tzooq3pm5P8', + 'laNV6F7Sk3M', + 'kNbq1SHfLd8', + 'aqWoohHDRYw', + 'bLxbRF-CFrU', + 'WMkuiKU6uBM', + 'yam1KMv0SgQ', + 'VddWWTaTnpQ', + 'oDZyswNe6jg', + 'Tpo2Fq8BtCk', + 'e83VIgq_hgI', + 'Q_OTD6Hs56c', + 'EKNe678ktEY', + 't1HTTqB2TtE', + 'bPfIUoXX_X4', + '3NWi-SGUjHA', + 'oKvTO3WdxKY', + 'PO7CGnoDFUI', + '7bBaW4UPk7A', + 'iyYMqhLwDQE', + 'BtYpDZNIeFc' + ], + 'qWQJbDvCMW8': [ + 'I4PEltFE6v8', + '5JHj33-s604', + 'gKn3GCu55W4', + '1MglIsDOypY', + 'pvOzKAmMtaU', + 'EfWHX2plrww', + 'cXnisQvxpbU', + 'z5cCf20gOE8', + 'AVgjb2KpWr4', + 'ucGxk8JLmWw', + '_-X-y-NaQNs', + 'eVD08ZVuJmY', + 'QNJdCsluIoI', + 'irUJJCORE1c', + 'FgYSaefAUTs', + 'OdDzYng7lTI', + 'Un067U25aOc', + 'iYPIx7VIh5g', + 'Ivwyqtw3PzU', + 'c8F1hJ_UTrk', + 'Pm1hCH1X2r0', + 'sYMgkKkHpGI', + '5_Bu25SV6X8', + 'py8omnp-hko' + ], + 'CSEvB5Tza9E': [ + '-tnS9O1ubn8', + 'CxXpMYIk_64', + 'IBILLYRAW1o', + 'tjjrLV2x0-I', + 'YWbQKxSwMtE', + 'QxPNDh0_5EQ', + 'bylApKzvY3g', + 'oCkZ6WicY8Q', + 'H3ugdzHeh2I', + 'Mkh6SOuL-Z0', + 'TY7b_S4mLwk', + 'MmoaflYGwuI', + '27a_s3DdbVc', + 'Y4QPolVdgx8', + 'J0Z37Hx5f-I', + 'f3hx6y0NzrQ', + 'IYKL2uhgsnU', + 'M66ux8WJ-9M', + 'FXXuJ9S-KkQ', + 'zRacUCRY_Sw', + 'boyXZfqpwpU', + 'rxv2qwYPe6s', + 'zpAwgjaI3lI', + 'MoonoldXeqs' + ] + }; +} diff --git a/lib/logic/data/wonder_data.dart b/lib/logic/data/wonder_data.dart new file mode 100644 index 00000000..1d021455 --- /dev/null +++ b/lib/logic/data/wonder_data.dart @@ -0,0 +1,86 @@ +import 'package:equatable/equatable.dart'; +import 'package:wonders/logic/data/wonder_type.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +class WonderData extends Equatable { + const WonderData({ + required this.type, + required this.title, + required this.subTitle, + required this.regionTitle, + this.startYr = 0, + this.endYr = 0, + this.artifactStartYr = 0, + this.artifactEndYr = 0, + this.artifactCulture = '', + this.artifactGeolocation = '', + this.lat = 0, + this.lng = 0, + this.imageIds = const [], + required this.unsplashCollectionId, + required this.pullQuote1Top, + required this.pullQuote1Bottom, + required this.pullQuote1Author, + this.pullQuote2 = '', + this.pullQuote2Author = '', + this.callout1 = '', + this.callout2 = '', + this.facts = const [], + required this.historyInfo1, + required this.historyInfo2, + required this.constructionInfo1, + required this.constructionInfo2, + required this.locationInfo1, + required this.locationInfo2, + required this.videoId, + this.videoCaption = '', + this.mapCaption = '', + required this.events, + this.highlightArtifacts = const [], + this.hiddenArtifacts = const [], + this.searchData = const [], + this.searchSuggestions = const [], + }); + + final WonderType type; + final String title; + final String subTitle; + final String regionTitle; + final String historyInfo1; + final String historyInfo2; + final String constructionInfo1; + final String constructionInfo2; + final String locationInfo1; + final String locationInfo2; + final String pullQuote1Top; + final String pullQuote1Bottom; + final String pullQuote1Author; + final String pullQuote2; + final String pullQuote2Author; + final String callout1; + final String callout2; + final String unsplashCollectionId; + final String videoId; + final String videoCaption; + final String mapCaption; + final List imageIds; + final List facts; + final int startYr; + final int endYr; + final int artifactStartYr; + final int artifactEndYr; + final String artifactCulture; + final String artifactGeolocation; + final double lat; + final double lng; + final List highlightArtifacts; // IDs used to assemble HighlightsData, should not be used directly + final List hiddenArtifacts; // IDs used to assemble CollectibleData, should not be used directly + final Map events; + final List searchData; + final List searchSuggestions; + + String get titleWithBreaks => title.replaceFirst(' ', '\n'); + + @override + List get props => [type, title, historyInfo1, imageIds, facts]; +} diff --git a/lib/logic/data/wonder_type.dart b/lib/logic/data/wonder_type.dart new file mode 100644 index 00000000..d4d860dc --- /dev/null +++ b/lib/logic/data/wonder_type.dart @@ -0,0 +1,10 @@ +enum WonderType { + chichenItza, + christRedeemer, + colosseum, + greatWall, + machuPicchu, + petra, + pyramidsGiza, + tajMahal, +} diff --git a/lib/logic/data/wonders_data/chichen_itza_data.dart b/lib/logic/data/wonders_data/chichen_itza_data.dart new file mode 100644 index 00000000..7c041fcc --- /dev/null +++ b/lib/logic/data/wonders_data/chichen_itza_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/chichen_itza_search_data.dart'; + +class ChichenItzaData extends WonderData { + ChichenItzaData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.chichenItza, + title: $strings.chichenItzaTitle, + subTitle: $strings.chichenItzaSubTitle, + regionTitle: $strings.chichenItzaRegionTitle, + videoId: 'Q6eBJjdca14', + startYr: 550, + endYr: 1550, + artifactStartYr: 500, + artifactEndYr: 1600, + artifactCulture: $strings.chichenItzaArtifactCulture, + artifactGeolocation: $strings.chichenItzaArtifactGeolocation, + lat: 20.68346184201756, + lng: -88.56769676930931, + unsplashCollectionId: 'SUK0tuMnLLw', + pullQuote1Top: $strings.chichenItzaPullQuote1Top, + pullQuote1Bottom: $strings.chichenItzaPullQuote1Bottom, + pullQuote1Author: '', + pullQuote2: $strings.chichenItzaPullQuote2, + pullQuote2Author: $strings.chichenItzaPullQuote2Author, + callout1: $strings.chichenItzaCallout1, + callout2: $strings.chichenItzaCallout2, + videoCaption: $strings.chichenItzaVideoCaption, + mapCaption: $strings.chichenItzaMapCaption, + historyInfo1: $strings.chichenItzaHistoryInfo1, + historyInfo2: $strings.chichenItzaHistoryInfo2, + constructionInfo1: $strings.chichenItzaConstructionInfo1, + constructionInfo2: $strings.chichenItzaConstructionInfo2, + locationInfo1: $strings.chichenItzaLocationInfo1, + locationInfo2: $strings.chichenItzaLocationInfo2, + highlightArtifacts: const [ + '503940', + '312595', + '310551', + '316304', + '313151', + '313256', + ], + hiddenArtifacts: const [ + '701645', + '310555', + '286467', + ], + events: { + 600: $strings.chichenItza600ce, + 832: $strings.chichenItza832ce, + 998: $strings.chichenItza998ce, + 1100: $strings.chichenItza1100ce, + 1527: $strings.chichenItza1527ce, + 1535: $strings.chichenItza1535ce, + }, + ); +} \ No newline at end of file diff --git a/lib/logic/data/wonders_data/christ_redeemer_data.dart b/lib/logic/data/wonders_data/christ_redeemer_data.dart new file mode 100644 index 00000000..50562d54 --- /dev/null +++ b/lib/logic/data/wonders_data/christ_redeemer_data.dart @@ -0,0 +1,62 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/christ_redeemer_search_data.dart'; + +class ChristRedeemerData extends WonderData { + ChristRedeemerData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.christRedeemer, + title: $strings.christRedeemerTitle, + subTitle: $strings.christRedeemerSubTitle, + regionTitle: $strings.christRedeemerRegionTitle, + videoId: 'k_615AauSds', + startYr: 1922, + endYr: 1931, + artifactStartYr: 1600, + artifactEndYr: 2100, + artifactCulture: '', + artifactGeolocation: $strings.christRedeemerArtifactGeolocation, + lat: -22.95238891944396, + lng: -43.21045520611561, + unsplashCollectionId: 'dPgX5iK8Ufo', + pullQuote1Top: $strings.christRedeemerPullQuote1Top, + pullQuote1Bottom: $strings.christRedeemerPullQuote1Bottom, + pullQuote1Author: '', + pullQuote2: $strings.christRedeemerPullQuote2, + pullQuote2Author: $strings.christRedeemerPullQuote2Author, + callout1: $strings.christRedeemerCallout1, + callout2: $strings.christRedeemerCallout2, + videoCaption: $strings.christRedeemerVideoCaption, + mapCaption: $strings.christRedeemerMapCaption, + historyInfo1: $strings.christRedeemerHistoryInfo1, + historyInfo2: $strings.christRedeemerHistoryInfo2, + constructionInfo1: $strings.christRedeemerConstructionInfo1, + constructionInfo2: $strings.christRedeemerConstructionInfo2, + locationInfo1: $strings.christRedeemerLocationInfo1, + locationInfo2: $strings.christRedeemerLocationInfo2, + highlightArtifacts: const [ + '501319', + '764815', + '502019', + '764814', + '764816', + ], + hiddenArtifacts: const [ + '501302', + '157985', + '227759', + ], + events: { + 1850: $strings.christRedeemer1850ce, + 1921: $strings.christRedeemer1921ce, + 1922: $strings.christRedeemer1922ce, + 1926: $strings.christRedeemer1926ce, + 1931: $strings.christRedeemer1931ce, + 2006: $strings.christRedeemer2006ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/colosseum_data.dart b/lib/logic/data/wonders_data/colosseum_data.dart new file mode 100644 index 00000000..4baa56e1 --- /dev/null +++ b/lib/logic/data/wonders_data/colosseum_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/colosseum_search_data.dart'; + +class ColosseumData extends WonderData { + ColosseumData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.colosseum, + title: $strings.colosseumTitle, + subTitle: $strings.colosseumSubTitle, + regionTitle: $strings.colosseumRegionTitle, + videoId: 'GXoEpNjgKzg', + startYr: 70, + endYr: 80, + artifactStartYr: 0, + artifactEndYr: 500, + artifactCulture: $strings.colosseumArtifactCulture, + artifactGeolocation: $strings.colosseumArtifactGeolocation, + lat: 41.890242126393495, + lng: 12.492349361871392, + unsplashCollectionId: 'VPdti8Kjq9o', + pullQuote1Top: $strings.colosseumPullQuote1Top, + pullQuote1Bottom: $strings.colosseumPullQuote1Bottom, + pullQuote1Author: '', + pullQuote2: $strings.colosseumPullQuote2, + pullQuote2Author: $strings.colosseumPullQuote2Author, + callout1: $strings.colosseumCallout1, + callout2: $strings.colosseumCallout2, + videoCaption: $strings.colosseumVideoCaption, + mapCaption: $strings.colosseumMapCaption, + historyInfo1: $strings.colosseumHistoryInfo1, + historyInfo2: $strings.colosseumHistoryInfo2, + constructionInfo1: $strings.colosseumConstructionInfo1, + constructionInfo2: $strings.colosseumConstructionInfo2, + locationInfo1: $strings.colosseumLocationInfo1, + locationInfo2: $strings.colosseumLocationInfo2, + highlightArtifacts: const [ + '251350', + '255960', + '247993', + '250464', + '251476', + '255960', + ], + hiddenArtifacts: const [ + '245376', + '256570', + '286136', + ], + events: { + 70: $strings.colosseum70ce, + 82: $strings.colosseum82ce, + 1140: $strings.colosseum1140ce, + 1490: $strings.colosseum1490ce, + 1829: $strings.colosseum1829ce, + 1990: $strings.colosseum1990ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/great_wall_data.dart b/lib/logic/data/wonders_data/great_wall_data.dart new file mode 100644 index 00000000..961aaa95 --- /dev/null +++ b/lib/logic/data/wonders_data/great_wall_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/great_wall_search_data.dart'; + +class GreatWallData extends WonderData { + GreatWallData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.greatWall, + title: $strings.greatWallTitle, + subTitle: $strings.greatWallSubTitle, + regionTitle: $strings.greatWallRegionTitle, + videoId: 'do1Go22Wu8o', + startYr: -700, + endYr: 1644, + artifactStartYr: -700, + artifactEndYr: 1650, + artifactCulture: $strings.greatWallArtifactCulture, + artifactGeolocation: $strings.greatWallArtifactGeolocation, + lat: 40.43199751120627, + lng: 116.57040708482984, + unsplashCollectionId: 'Kg_h04xvZEo', + pullQuote1Top: $strings.greatWallPullQuote1Top, + pullQuote1Bottom: $strings.greatWallPullQuote1Bottom, + pullQuote1Author: '', //No key because it doesn't generate for empty values + pullQuote2: $strings.greatWallPullQuote2, + pullQuote2Author: $strings.greatWallPullQuote2Author, + callout1: $strings.greatWallCallout1, + callout2: $strings.greatWallCallout2, + videoCaption: $strings.greatWallVideoCaption, + mapCaption: $strings.greatWallMapCaption, + historyInfo1: $strings.greatWallHistoryInfo1, + historyInfo2: $strings.greatWallHistoryInfo2, + constructionInfo1: $strings.greatWallConstructionInfo1, + constructionInfo2: $strings.greatWallConstructionInfo2, + locationInfo1: $strings.greatWallLocationInfo1, + locationInfo2: $strings.greatWallLocationInfo2, + highlightArtifacts: const [ + '79091', + '781812', + '40213', + '40765', + '57612', + '666573', + ], + hiddenArtifacts: const [ + '39918', + '39666', + '39735', + ], + events: { + -700: $strings.greatWall700bce, + -214: $strings.greatWall214bce, + -121: $strings.greatWall121bce, + 556: $strings.greatWall556ce, + 618: $strings.greatWall618ce, + 1487: $strings.greatWall1487ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/machu_picchu_data.dart b/lib/logic/data/wonders_data/machu_picchu_data.dart new file mode 100644 index 00000000..f7897ba8 --- /dev/null +++ b/lib/logic/data/wonders_data/machu_picchu_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/machu_picchu_search_data.dart'; + +class MachuPicchuData extends WonderData { + MachuPicchuData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.machuPicchu, + title: $strings.machuPicchuTitle, + subTitle: $strings.machuPicchuSubTitle, + regionTitle: $strings.machuPicchuRegionTitle, + videoId: 'cnMa-Sm9H4k', + startYr: 1450, + endYr: 1572, + artifactStartYr: 1200, + artifactEndYr: 1700, + artifactCulture: $strings.machuPicchuArtifactCulture, + artifactGeolocation: $strings.machuPicchuArtifactGeolocation, + lat: -13.162690683637758, + lng: -72.54500778824891, + unsplashCollectionId: 'wUhgZTyUnl8', + pullQuote1Top: $strings.machuPicchuPullQuote1Top, + pullQuote1Bottom: $strings.machuPicchuPullQuote1Bottom, + pullQuote1Author: $strings.machuPicchuPullQuote1Author, + pullQuote2: $strings.machuPicchuPullQuote2, + pullQuote2Author: $strings.machuPicchuPullQuote2Author, + callout1: $strings.machuPicchuCallout1, + callout2: $strings.machuPicchuCallout2, + videoCaption: $strings.machuPicchuVideoCaption, + mapCaption: $strings.machuPicchuMapCaption, + historyInfo1: $strings.machuPicchuHistoryInfo1, + historyInfo2: $strings.machuPicchuHistoryInfo2, + constructionInfo1: $strings.machuPicchuConstructionInfo1, + constructionInfo2: $strings.machuPicchuConstructionInfo2, + locationInfo1: $strings.machuPicchuLocationInfo1, + locationInfo2: $strings.machuPicchuLocationInfo2, + highlightArtifacts: const [ + '313295', + '316926', + '309944', + '309436', + '309960', + '316873', + ], + hiddenArtifacts: const [ + '308120', + '309960', + '313341', + ], + events: { + 1438: $strings.machuPicchu1438ce, + 1572: $strings.machuPicchu1572ce, + 1867: $strings.machuPicchu1867ce, + 1911: $strings.machuPicchu1911ce, + 1964: $strings.machuPicchu1964ce, + 1997: $strings.machuPicchu1997ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/petra_data.dart b/lib/logic/data/wonders_data/petra_data.dart new file mode 100644 index 00000000..a3d4afc4 --- /dev/null +++ b/lib/logic/data/wonders_data/petra_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/petra_search_data.dart'; + +class PetraData extends WonderData { + PetraData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.petra, + title: $strings.petraTitle, + subTitle: $strings.petraSubTitle, + regionTitle: $strings.petraRegionTitle, + videoId: 'ezDiSkOU0wc', + startYr: -312, + endYr: 100, + artifactStartYr: -500, + artifactEndYr: 500, + artifactCulture: $strings.petraArtifactCulture, + artifactGeolocation: $strings.petraArtifactGeolocation, + lat: 30.328830750209903, + lng: 35.44398203484667, + unsplashCollectionId: 'qWQJbDvCMW8', + pullQuote1Top: $strings.petraPullQuote1Top, + pullQuote1Bottom: $strings.petraPullQuote1Bottom, + pullQuote1Author: $strings.petraPullQuote1Author, + pullQuote2: $strings.petraPullQuote2, + pullQuote2Author: $strings.petraPullQuote2Author, + callout1: $strings.petraCallout1, + callout2: $strings.petraCallout2, + videoCaption: $strings.petraVideoCaption, + mapCaption: $strings.petraMapCaption, + historyInfo1: $strings.petraHistoryInfo1, + historyInfo2: $strings.petraHistoryInfo2, + constructionInfo1: $strings.petraConstructionInfo1, + constructionInfo2: $strings.petraConstructionInfo2, + locationInfo1: $strings.petraLocationInfo1, + locationInfo2: $strings.petraLocationInfo2, + highlightArtifacts: const [ + '325900', + '325902', + '325919', + '325884', + '325887', + '325891', + ], + hiddenArtifacts: const [ + '322592', + '325918', + '326243', + ], + events: { + -1200: $strings.petra1200bce, + -106: $strings.petra106bce, + 551: $strings.petra551ce, + 1812: $strings.petra1812ce, + 1958: $strings.petra1958ce, + 1989: $strings.petra1989ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/pyramids_giza_data.dart b/lib/logic/data/wonders_data/pyramids_giza_data.dart new file mode 100644 index 00000000..6bc61c3f --- /dev/null +++ b/lib/logic/data/wonders_data/pyramids_giza_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/pyramids_giza_search_data.dart'; + +class PyramidsGizaData extends WonderData { + PyramidsGizaData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.pyramidsGiza, + title: $strings.pyramidsGizaTitle, + subTitle: $strings.pyramidsGizaSubTitle, + regionTitle: $strings.pyramidsGizaRegionTitle, + videoId: 'lJKX3Y7Vqvs', + startYr: -2600, + endYr: -2500, + artifactStartYr: -2800, + artifactEndYr: -2300, + artifactCulture: $strings.pyramidsGizaArtifactCulture, + artifactGeolocation: $strings.pyramidsGizaArtifactGeolocation, + lat: 29.9792, + lng: 31.1342, + unsplashCollectionId: 'CSEvB5Tza9E', + pullQuote1Top: $strings.pyramidsGizaPullQuote1Top, + pullQuote1Bottom: $strings.pyramidsGizaPullQuote1Bottom, + pullQuote1Author: '', + pullQuote2: $strings.pyramidsGizaPullQuote2, + pullQuote2Author: $strings.pyramidsGizaPullQuote2Author, + callout1: $strings.pyramidsGizaCallout1, + callout2: $strings.pyramidsGizaCallout2, + videoCaption: $strings.pyramidsGizaVideoCaption, + mapCaption: $strings.pyramidsGizaMapCaption, + historyInfo1: $strings.pyramidsGizaHistoryInfo1, + historyInfo2: $strings.pyramidsGizaHistoryInfo2, + constructionInfo1: $strings.pyramidsGizaConstructionInfo1, + constructionInfo2: $strings.pyramidsGizaConstructionInfo2, + locationInfo1: $strings.pyramidsGizaLocationInfo1, + locationInfo2: $strings.pyramidsGizaLocationInfo2, + highlightArtifacts: const [ + '543864', + '546488', + '557137', + '543900', + '543935', + '544782', + ], + hiddenArtifacts: const [ + '546510', + '543896', + '545728', + ], + events: { + -2575: $strings.pyramidsGiza2575bce, + -2465: $strings.pyramidsGiza2465bce, + -443: $strings.pyramidsGiza443bce, + 1925: $strings.pyramidsGiza1925ce, + 1979: $strings.pyramidsGiza1979ce, + 1990: $strings.pyramidsGiza1990ce, + }, + ); +} diff --git a/lib/logic/data/wonders_data/search/chichen_itza_search_data.dart b/lib/logic/data/wonders_data/search/chichen_itza_search_data.dart new file mode 100644 index 00000000..487bc9aa --- /dev/null +++ b/lib/logic/data/wonders_data/search/chichen_itza_search_data.dart @@ -0,0 +1,199 @@ +part of '../chichen_itza_data.dart'; + +// Search suggestions (59) +List _searchSuggestions = const ['stamp', 'yoke', 'hacha', 'male', 'female', 'serpentine', 'andesite', 'figure', 'cylindrical', 'chisel', 'earflare', 'relief', 'model', 'plate', 'ornament', 'red', 'censer', 'jade', 'slip', 'gold', 'implements', 'musical', 'jar', 'stone', 'sculpture', 'pendant', 'rattle', 'monkey', 'seated', 'mythological', 'beads', 'ceramics', 'ornaments', 'pigment', 'metal', 'instruments', 'head', 'deity', 'ceramic', 'shell', 'containers', 'flint', 'bird', 'double', 'bead', 'ochre', 'carved', 'face', 'jadeite', 'tripod', 'tubular', 'vessel', 'celt', 'bowl', 'eagle', 'paint', 'limestone', 'scene', 'lid', ]; + +// Chichen Itza (191) +List _searchData = const [ + SearchData(550, 317120, 'Earflare', 'earflare|jade|stone-ornaments', 'ao/mobile-large/VS1994_35_582.JPG', 1.18), + SearchData(700, 313336, 'Handle (?)', 'tube|bone|bone/ivory-sculpture', 'ao/mobile-large/vs1979_206_1144.jpg', 0.28), + SearchData(1400, 317635, 'Gouge', 'chisel|stone|stone-implements', 'ao/mobile-large/vs1994_35_771.jpg', 3.40), + SearchData(1250, 318229, 'Metate Fragment', 'metate fragment|stone|stone-sculpture', 'ao/mobile-large/X.180.10.JPG', 1.52), + SearchData(1400, 317303, 'Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/VS1994_35_447.JPG', 1.43), + SearchData(1100, 313348, 'Monkey Vessel', 'vessel|onyx marble, pyrite, shell|stone-containers', 'ao/mobile-large/DP-25032-001.jpg', 0.80), + SearchData(1400, 317267, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_401.jpg', 0.58), + SearchData(200, 312581, 'House Model', 'house model|ceramic|ceramics-sculpture', 'ao/mobile-large/DP-23907-001.jpg', 0.78), + SearchData(1350, 312599, 'Pedestal Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP102175.jpg', 1.03), + SearchData(850, 309404, 'Monumental Figure', 'figure|limestone|stone-sculpture', 'ao/mobile-large/DP104844.jpg', 0.44), + SearchData(750, 501839, 'Pottery Rattle', 'pottery rattle|clay|idiophone-shaken-rattle', 'mi/mobile-large/DP-23770-001.jpg', 0.80), + SearchData(1400, 317227, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_361.jpg', 0.62), + SearchData(749, 503940, 'Double Whistle', 'double whistle|pottery, paint|aerophone-whistle flute', 'mi/mobile-large/DT4624a.jpg', 0.78), + SearchData(400, 309713, 'Yoke-Form Vessel', 'vessel with lid|ceramic|ceramics-containers', 'ao/mobile-large/DT11169.jpg', 0.80), + SearchData(1400, 317234, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_368.jpg', 0.87), + SearchData(-650, 313138, 'Seated Bench Figure', 'figure|serpentine|stone-sculpture', 'ao/mobile-large/DT9963.jpg', 0.80), + SearchData(1400, 317210, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_343.jpg', 0.79), + SearchData(700, 310651, 'Vessel, Palace Scene', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/1978.412.202_a.JPG', 0.77), + SearchData(1400, 317211, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_344.jpg', 0.68), + SearchData(1400, 317248, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_382.jpg', 0.44), + SearchData(-1000, 314946, 'Bird Vessel', 'vessel|ceramic, red ochre|ceramics-containers', 'ao/mobile-large/DP23080.jpg', 0.83), + SearchData(700, 313315, 'Vessel with Deity Figures', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/1979.206.1122_a.JPG', 0.81), + SearchData(1400, 317099, 'Stone Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/hz1994_35_334.jpg', 0.87), + SearchData(1000, 313091, 'Ceremonial Metate', 'metate|diabase|stone-implements', 'ao/mobile-large/DP104835.jpg', 1.00), + SearchData(1400, 317314, 'Eccentric Flint', 'flint|flint|stone-implements', 'ao/mobile-large/hz1994_35_458.jpg', 0.22), + SearchData(700, 319633, 'Cylindrical Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-13003-003.jpg', 0.75), + SearchData(1400, 317291, 'Adze', 'adze|stone|stone-implements', 'ao/mobile-large/VS1994_35_435.JPG', 0.63), + SearchData(-1000, 312589, 'Relief-Carved Bowl', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP296005.jpg', 1.10), + SearchData(1550, 310589, 'Figure Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DT10182.jpg', 0.79), + SearchData(0, 317600, 'Temple Model', 'temple model|ceramic|ceramics-sculpture', 'ao/mobile-large/DP-23904-001.jpg', 1.09), + SearchData(1400, 317241, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_375.jpg', 0.86), + SearchData(775, 310551, 'Censer Support', 'censer support|ceramic|ceramics-sculpture', 'ao/mobile-large/DP102949.jpg', 0.67), + SearchData(400, 316299, 'Pair of Earflare Frontals', 'earflare frontals|jade (jadeite)|stone-ornaments', 'ao/mobile-large/DP-14786-043.jpg', 1.39), + SearchData(1410, 321343, 'Serpent Labret with Articulated Tongue', 'labret|gold|metal-ornaments', 'ao/mobile-large/DP-478-020.jpg', 1.61), + SearchData(750, 310474, 'Hacha', 'hacha|stone, pigment|stone-sculpture', 'ao/mobile-large/DP-17617-001.jpg', 0.76), + SearchData(700, 310468, 'Standing Male Figure', 'male figure|ceramic|ceramics-sculpture', 'ao/mobile-large/1978.412.6_1.jpg', 0.87), + SearchData(1400, 317101, 'Stone Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/hz1994_35_336.jpg', 0.67), + SearchData(-650, 309987, 'Duck-Face Ornament', 'ornament|jadeite, pigment|stone-ornaments', 'ao/mobile-large/DP-12761-009.jpg', 1.09), + SearchData(1400, 317222, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_356.jpg', 0.67), + SearchData(1400, 317244, 'Stone Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/hz1994_35_378.jpg', 0.56), + SearchData(700, 314311, 'Cylindrical Vessel', 'vessel|ceramic, slip, pigment|stone-containers', 'ao/mobile-large/DP-575-001.jpg', 0.75), + SearchData(900, 309901, 'Pedestal Bowl', 'bowl|marble|stone-containers', 'ao/mobile-large/DP-17794-001.jpg', 0.80), + SearchData(1400, 317233, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_367.jpg', 0.88), + SearchData(1399, 318679, 'Monkey stamp', 'stamp|ceramic|ceramics-implements', 'ao/mobile-large/vs00_5_1165.jpg', 0.70), + SearchData(750, 313386, '"Smiling" Figure', 'male figure|ceramic|ceramics-sculpture', 'ao/mobile-large/DP104829.jpg', 0.89), + SearchData(1360, 309861, 'Deity Censer (Xantil)', 'censer|ceramic, pigment|ceramics-sculpture', 'ao/mobile-large/DT1256.jpg', 0.80), + SearchData(800, 310540, 'Column, Costumed Figure', 'column|limestone|stone-sculpture', 'ao/mobile-large/DP-22128-001.jpg', 0.64), + SearchData(700, 314217, 'Vessel, Mythological Scene', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-571-001.jpg', 0.74), + SearchData(600, 310607, 'Vessel, Seated Deities', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-13003-005.jpg', 0.75), + SearchData(800, 312593, 'Hacha, Head', 'hacha|andesite|stone-sculpture', 'ao/mobile-large/1979.206.371.jpg', 1.22), + SearchData(750, 312915, 'Whistle with the Maize God emerging from a flower', 'figure|ceramic, pigment|ceramics-sculpture', 'ao/mobile-large/DT9945.jpg', 0.80), + SearchData(350, 318345, 'Censer, Seated King', 'censer|ceramic|ceramics-containers', 'ao/mobile-large/DT4512.jpg', 0.80), + SearchData(-650, 310467, 'Celt with Incised Profile', 'celt|jade (jadeite)|stone-implements', 'ao/mobile-large/DP-23470-001.jpg', 0.72), + SearchData(0, 314523, 'Cylindrical Vessel', 'vessel|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP101926.jpg', 0.98), + SearchData(1049, 319238, 'Female figure', 'figure|stone|stone-sculpture', 'ao/mobile-large/vs00_5_6.jpg', 0.51), + SearchData(1420, 307744, 'Stamp, Monkey', 'stamp|ceramic|ceramics-implements', 'ao/mobile-large/00.5.1164.jpg', 0.80), + SearchData(1400, 317100, 'Stone Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/hz1994_35_335.jpg', 0.94), + SearchData(1400, 317217, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_350.jpg', 0.77), + SearchData(1100, 307599, 'Eagle Relief', 'panel|andesite/dacite, paint|stone-sculpture', 'ao/mobile-large/DP-20487-001.jpg', 1.11), + SearchData(1400, 317214, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_347.jpg', 0.66), + SearchData(1159, 310268, 'Eagle Pendant', 'pendant|gold|metal-ornaments', 'ao/mobile-large/DT5062.jpg', 0.80), + SearchData(-1000, 310556, 'Bottle', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/hb_1978.412.104.jpg', 0.76), + SearchData(1420, 307748, 'Stamp, Monkey', 'stamp|ceramic|ceramics-implements', 'ao/mobile-large/00.5.1177_b.jpg', 0.67), + SearchData(1400, 317247, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_381.jpg', 0.63), + SearchData(500, 317885, 'Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/1998.317.4.JPG', 1.17), + SearchData(700, 312596, 'Seated Female Figure', 'female figure|ceramic|ceramics-musical instruments', 'ao/mobile-large/1979.206.374.JPG', 0.72), + SearchData(1400, 317216, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_349.jpg', 0.62), + SearchData(25, 318346, 'Spouted Jar', 'jar|indurated limestone|stone-containers', 'ao/mobile-large/DT4518.jpg', 1.25), + SearchData(1400, 317102, 'Stone Chisel', 'chisel|stone|stone-implements', 'ao/mobile-large/hz1994_35_337.jpg', 0.75), + SearchData(700, 313149, 'Canine Ornament', 'ornament|shell (spondylus)|shell-ornaments', 'ao/mobile-large/DP-25094-001.jpg', 1.50), + SearchData(750, 315883, 'Bowl, Mythological Scene', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP-578-001.jpg', 1.32), + SearchData(1400, 317215, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_348.jpg', 0.71), + SearchData(1400, 317213, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_346.jpg', 0.63), + SearchData(800, 316813, 'Jar, Ritual Scenes', 'jar|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/1993.441_a.JPG', 0.83), + SearchData(500, 316645, 'Deity Head Pendant', 'pendant|jade|stone-ornaments', 'ao/mobile-large/vs1991_362_2.jpg', 0.73), + SearchData(500, 309557, 'Closed Yoke', 'yoke|stone|stone-sculpture', 'ao/mobile-large/DP-17792-001.jpg', 1.53), + SearchData(-950, 318473, 'Blackware Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP705410.jpg', 0.82), + SearchData(1400, 317107, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_342.jpg', 0.73), + SearchData(-300, 313316, 'Bench Figure', 'male figure|greenstone (muscovite-paragonite phyllite)|stone-sculpture', 'ao/mobile-large/DP23089.jpg', 0.78), + SearchData(1400, 317226, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_360.jpg', 0.58), + SearchData(500, 313235, 'Hacha in the Shape of Bound Hands', 'hacha|stone|stone-sculpture', 'ao/mobile-large/DP104825.jpg', 0.95), + SearchData(750, 312643, 'Yoke', 'yoke|serpentine|stone-sculpture', 'ao/mobile-large/DP-23864-001.jpg', 1.46), + SearchData(1400, 317359, 'Tubular Bead', 'bead|stone|beads-ornaments', 'ao/mobile-large/VS1994_35_509.JPG', 0.49), + SearchData(550, 318444, 'Grouped Pigment Jars', 'jars|ceramic, pigment|ceramics-sculpture', 'ao/mobile-large/DP-23881-001.jpg', 1.37), + SearchData(400, 313262, 'Deity Figure', 'figure|jade (pyroxene jadeite)|stone-sculpture', 'ao/mobile-large/DP-23472-001.jpg', 0.76), + SearchData(1400, 317270, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_404.jpg', 0.47), + SearchData(1400, 317301, 'Celt-Form Pendant', 'pendant|stone|stone-ornaments', 'ao/mobile-large/VS1994_35_445.JPG', 0.93), + SearchData(499, 313330, 'Bird Pendant', 'pendant|jadeite|stone-ornaments', 'ao/mobile-large/DP-17791-001.jpg', 0.80), + SearchData(600, 310527, 'Stela Fragment with Glyphs', 'stela fragment|stone|stone-sculpture', 'ao/mobile-large/DT10173.jpg', 1.03), + SearchData(1100, 307598, 'Eagle Relief', 'panel|andesite or dacite, paint|stone-sculpture', 'ao/mobile-large/DP-17616-001.jpg', 1.08), + SearchData(1400, 317350, 'Bead', 'bead|stone|beads-ornaments', 'ao/mobile-large/hz1994_35_500.jpg', 0.97), + SearchData(1400, 317352, 'Bead', 'bead|stone|beads-ornaments', 'ao/mobile-large/hz1994_35_502.jpg', 1.01), + SearchData(400, 312586, 'Tripod Vessel', 'vessel|ceramic, red ochre|ceramics-containers', 'ao/mobile-large/DP302441.jpg', 1.10), + SearchData(500, 314557, 'Seated Figure Censer (Incensario)', 'censer|ceramic|ceramics-containers', 'ao/mobile-large/DT7407.jpg', 0.80), + SearchData(750, 316274, 'Plate with Trumpeter', 'plate|ceramic|ceramics-containers', 'ao/mobile-large/DP-24261-001.jpg', 1.02), + SearchData(650, 313254, 'Animal Head Hacha', 'hacha|stone|stone-sculpture', 'ao/mobile-large/DP-23868-001.jpg', 1.13), + SearchData(550, 317121, 'Earflare', 'earflare|jade|stone-ornaments', 'ao/mobile-large/VS1994_35_583.JPG', 1.21), + SearchData(800, 320147, 'Figure', 'figure|ceramic|ceramics-sculpture', 'ao/mobile-large/DP221679.jpg', 0.75), + SearchData(1400, 317237, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_371.jpg', 0.79), + SearchData(50, 319227, 'House Model', 'house model|ceramic|ceramics-sculpture', 'ao/mobile-large/DP-23908-001.jpg', 0.90), + SearchData(700, 317712, 'Head Pendant', 'pendant|shell|shell-ornaments', 'ao/mobile-large/262187.jpg', 0.64), + SearchData(1400, 318653, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_352.jpg', 0.84), + SearchData(1400, 317297, 'Celt-Form Pendant', 'pendant|stone|stone-ornaments', 'ao/mobile-large/VS1994_35_441.JPG', 0.61), + SearchData(749, 718242, 'Tripod Plate, Mythological Scene', 'plate|ceramic with red, cream, and black slip|ceramics', 'ao/mobile-large/DP-23101-003.jpg', 1.02), + SearchData(800, 320205, 'Figure', 'figure|ceramic, hematite|ceramics-sculpture', 'ao/mobile-large/DP220954.jpg', 0.75), + SearchData(550, 317429, 'Earflare Set', 'earflare set|jade|stone-ornaments', 'ao/mobile-large/VS1994_35_590.JPG', 1.80), + SearchData(1400, 317265, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_399.jpg', 0.64), + SearchData(1400, 317235, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_369.jpg', 0.87), + SearchData(700, 313241, 'Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/1979.206.1048_a.JPG', 0.93), + SearchData(750, 313335, 'Crocodile Rattle', 'rattle|ceramic|ceramics-musical instruments', 'ao/mobile-large/DT9416.jpg', 1.25), + SearchData(1475, 307634, 'Cihuateotl', 'figure|stone, pigment|stone-sculpture', 'ao/mobile-large/DT5116.jpg', 0.80), + SearchData(550, 317760, 'Pair of Carved Ornaments', 'earflare frontals|shell|shell-ornaments', 'ao/mobile-large/DP-14786-044.jpg', 1.29), + SearchData(-1050, 316302, 'Bowl (Tecomate)', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP23126.jpg', 0.93), + SearchData(550, 318405, 'Carved Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DT4631.jpg', 1.25), + SearchData(1400, 317597, 'Polishing Stone (?)', 'polishing stone|stone|stone-implements', 'ao/mobile-large/vs1994_35_764.jpg', 0.96), + SearchData(700, 310364, 'Vessel, Mythological Scene', 'vessel|ceramic, pigment|ceramics-containers', 'ao/mobile-large/DP-576-001.jpg', 0.74), + SearchData(300, 314827, 'Tripod Bird Bowl', 'bowl with lid|ceramic|ceramics-containers', 'ao/mobile-large/DT4867.jpg', 0.80), + SearchData(750, 312162, 'Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/1979.205.7.JPG', 1.50), + SearchData(1400, 317266, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_400.jpg', 0.60), + SearchData(300, 312161, 'Bottle (Florero)', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/DP101922.jpg', 0.90), + SearchData(700, 319586, 'Pendant with Seated Lord', 'pendant|jadeite|stone-ornaments', 'ao/mobile-large/DP131715.jpg', 0.75), + SearchData(450, 310542, 'Double-Chambered Vessel', 'vessel with lid|ceramic|ceramics-containers', 'ao/mobile-large/DP-23468-001.jpg', 0.77), + SearchData(1400, 317103, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_338.jpg', 0.95), + SearchData(700, 319009, 'Covered Bowl', 'bowl with lid|ceramic|ceramics-containers', 'ao/mobile-large/DP-23884-001.jpg', 0.93), + SearchData(1460, 316873, 'Temple Model', 'temple model|ceramic|ceramics-sculpture', 'ao/mobile-large/DP341942.jpg', 0.75), + SearchData(700, 310475, 'Head from a Figure', 'head|ceramic|ceramics-sculpture', 'ao/mobile-large/1978.412.19.JPG', 0.72), + SearchData(800, 316267, 'Ball Player', 'male figure|ceramic|ceramics-sculpture', 'ao/mobile-large/DP-23878-001.jpg', 0.67), + SearchData(150, 314524, 'Cylindrical Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/h1_1982.207.7.jpg', 0.71), + SearchData(-650, 313337, 'Ritual Spoon Pendant', 'pendant|jadeite|stone-ornaments', 'ao/mobile-large/DP-25108-001.jpg', 0.46), + SearchData(699, 53939, 'Head Pendant', 'pendant|jade (jadeite)|jade', 'as/mobile-large/DP-14791-008.jpg', 0.75), + SearchData(750, 313151, 'Costumed Figure', 'male figure|ceramic, pigment|ceramics-musical instruments', 'ao/mobile-large/1979.206.953_a.JPG', 0.67), + SearchData(1400, 317305, 'Celt', 'chisel|stone|stone-implements', 'ao/mobile-large/VS1994_35_449.JPG', 1.46), + SearchData(699, 662967, 'Codex-Style Vase with Mythological Scene', 'vase|ceramic|ceramics-vessels', 'ao/mobile-large/DP348021.jpg', 0.75), + SearchData(-650, 749344, 'Kneeling Bearded Figure', 'figure|serpentine|stone-sculpture', 'ao/mobile-large/DP15497-009-.jpg', 0.80), + SearchData(-1000, 316301, 'Bowl (Tecomate)', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP-23910-001.jpg', 1.16), + SearchData(1399, 705547, 'Necklace with Beads in the Shape of Jaguar Teeth', 'necklace|gold|metalwork-gold', 'ao/mobile-large/DP-14865-001.jpg', 1.33), + SearchData(-600, 310513, 'Pendant', 'pendant|jade|stone-ornaments', 'ao/mobile-large/vs1978_412_57.jpg', 1.66), + SearchData(800, 317619, 'Turkey Vessel', 'vessel|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/hz1998_71.jpg', 1.31), + SearchData(750, 313325, 'Ceremonial Handle (?)', 'handle|jade (jadeite/omphacite)|stone-sculpture', 'ao/mobile-large/DP102172corrected.jpg', 0.87), + SearchData(-1000, 313319, 'Small Yoke with Face', 'yoke|stone|stone-sculpture', 'ao/mobile-large/DP-24242-001.jpg', 1.22), + SearchData(700, 313342, 'Figure with Helmet Mask', 'male figure|ceramic, pigment|ceramics-sculpture', 'ao/mobile-large/1979.206.1150_a1.JPG', 1.07), + SearchData(1400, 317106, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_341.jpg', 1.00), + SearchData(1400, 315730, 'Footed Vessel', 'bowl|calcite|stone-containers', 'ao/mobile-large/1989.314.25.JPG', 0.81), + SearchData(772, 313240, 'Relief with Enthroned Ruler', 'relief|limestone, paint|stone-sculpture', 'ao/mobile-large/DP104826.jpg', 0.99), + SearchData(-1000, 318464, 'Duck-Head Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP705381.jpg', 0.98), + SearchData(650, 701645, 'Pendant', 'pendant|jadeite, pigment|stone-ornaments', 'ao/mobile-large/DP-25104-001.jpg', 0.63), + SearchData(-750, 317697, 'Eagle Transformation Figure', 'figure|albitite, cinnabar|stone-sculpture', 'ao/mobile-large/DP-25107-001.jpg', 0.58), + SearchData(550, 313256, 'Mirror-Bearer', 'male figure|wood, red hematite|wood-sculpture', 'ao/mobile-large/DP-24340-001.jpg', 0.73), + SearchData(1400, 317295, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/VS1994_35_439.JPG', 0.46), + SearchData(750, 318463, 'Pendant with Figure and Double-Headed Crocodilian', 'pendant|shell|shell-ornaments', 'ao/mobile-large/DP-25095-001.jpg', 1.33), + SearchData(700, 310555, 'Bird Ornament', 'ornament|shell|shell-ornaments', 'ao/mobile-large/DP-23474-001.jpg', 1.09), + SearchData(1400, 317225, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_359.jpg', 0.67), + SearchData(1400, 317275, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_409.jpg', 2.77), + SearchData(700, 319873, 'Deity Face Pendant', 'pendant|jadeite/omphacite, iron ochre|stone-ornaments', 'ao/mobile-large/DP148420.jpg', 0.75), + SearchData(700, 316711, 'Vessel with Seated Lord', 'vessel|ceramic, stucco|ceramics-containers', 'ao/mobile-large/DP-24262-001.jpg', 0.86), + SearchData(700, 312595, 'Seated Female Figure', 'female figure|ceramic|ceramics-musical instruments', 'ao/mobile-large/DP-12659-001.jpg', 0.75), + SearchData(750, 318628, 'Cylinder Vessel', 'vessel|ceramic, slip, stucco|ceramics-containers', 'ao/mobile-large/1979.205.8.JPG', 0.85), + SearchData(1300, 313257, 'Crocodile-Head Figure Pendant', 'pendant|gold (cast alloy), pyrite inlay|metal-ornaments', 'ao/mobile-large/VS19792061064.JPG', 0.70), + SearchData(1400, 317263, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_397.jpg', 0.70), + SearchData(737, 318662, 'Vessel, Throne Scene', 'vessel|ceramic, pigment|ceramics-containers', 'ao/mobile-large/DT4514.jpg', 0.80), + SearchData(150, 316300, 'Cylindrical Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP242327.jpg', 1.03), + SearchData(-99, 314522, 'Tetrapod Jar', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP23127.jpg', 1.03), + SearchData(1400, 317320, 'Blade', 'blade|flint|stone-implements', 'ao/mobile-large/hz1994_35_464.jpg', 0.43), + SearchData(750, 312645, 'Palma with textile motif', 'palma|stone, pigment|stone-sculpture', 'ao/mobile-large/DP104834.jpg', 0.83), + SearchData(550, 314832, 'Shell Ornament', 'pendant|jade|stone-ornaments', 'ao/mobile-large/vs1985_216_2.jpg', 2.35), + SearchData(1400, 317246, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_380.jpg', 0.60), + SearchData(50, 677436, 'Human Figure', 'figure|clay|ceramics-sculpture', 'ao/mobile-large/LC-300_5434.jpg', 1.34), + SearchData(1843, 591853, 'Incidents of Travel in Yucatan', '|illustrated book|', 'li/mobile-large/liUDs83v2.R.jpg', 1.53), + SearchData(1470, 321292, 'Head from a figure, Xochipilli-Macuilxochitl', 'head|obsidian|glass-sculpture', 'ao/mobile-large/TR.451.2.2012_a.jpg', 1.60), + SearchData(300, 313035, 'Ornament with Maya Glyph', 'ornament|jadeite|stone-ornaments', 'ao/mobile-large/VS1979206858A.JPG', 1.23), + SearchData(900, 316304, 'Tripod Plate', 'plate|ceramic|ceramics-containers', 'ao/mobile-large/DP219258.jpg', 0.81), + SearchData(700, 310644, 'Scepter with Profile Figures', 'scepter|flint|stone-sculpture', 'ao/mobile-large/DT10197.jpg', 0.79), + SearchData(1400, 317358, 'Tubular Bead', 'bead|stone|beads-ornaments', 'ao/mobile-large/VS1994_35_508.JPG', 2.59), + SearchData(1000, 310480, 'Head of a Rain God', 'head|fossiliferous limestone|stone-sculpture', 'ao/mobile-large/DP102948.jpg', 0.99), + SearchData(800, 318678, 'Standing Figure', 'figure|ceramic|ceramics-sculpture', 'ao/mobile-large/vs00_5_176.jpg', 0.96), + SearchData(749, 761272, 'Vessel with Water Bird and Hieroglyphic Text', 'vessel|ceramic|ceramics-vessels', 'ao/mobile-large/DP-15497-005.jpg', 1.21), + SearchData(-650, 316288, 'Celt', 'celt|jade|stone-ornaments', 'ao/mobile-large/1989.314.4.jpg', 0.67), + SearchData(700, 312818, 'Ornamental Figure', 'ornament|shell|shell-ornaments', 'ao/mobile-large/vs1979_206_622.jpg', 0.38), + SearchData(900, 307443, 'Fragmentary Relief', 'relief|stone|stone-sculpture', 'ao/mobile-large/DP-20489-002.jpg', 1.43), + SearchData(300, 315884, 'Lidded Vessel', 'bowl with lid|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/1987.450.2ab1.JPG', 1.30), + SearchData(1400, 317365, 'Tubular Bead', 'bead|stone|beads-ornaments', 'ao/mobile-large/VS1994_35_515.JPG', 3.53), + SearchData(-650, 310495, 'Yoke', 'yoke|stone, pigment|stone-sculpture', 'ao/mobile-large/DT10169.jpg', 0.80), + SearchData(700, 314305, 'Bowl', 'bowl|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/1983.505_a.JPG', 1.50), + SearchData(550, 317430, 'Earflare Set', 'earflare set|jade|stone-ornaments', 'ao/mobile-large/VS1994_35_591.JPG', 1.79), + SearchData(800, 320206, 'Figure', 'figure|ceramic|ceramics-sculpture', 'ao/mobile-large/DP220955.jpg', 0.75), + SearchData(500, 310354, 'Bowl with Pouring Lip', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/1978.309.1.JPG', 1.12), + SearchData(1400, 317212, 'Celt', 'celt|stone|stone-implements', 'ao/mobile-large/hz1994_35_345.jpg', 0.64), + SearchData(750, 312804, 'Figure Rattle', 'rattle|ceramic, pigment|ceramics-musical instruments', 'ao/mobile-large/1979.206.608.JPG', 0.70), + SearchData(900, 313242, 'Bowl', 'bowl|ceramic|ceramics-containers', 'ao/mobile-large/1979.206.1049_a.JPG', 1.23), + SearchData(550, 315035, 'Head Pendant', 'pendant|stone|stone-ornaments', 'ao/mobile-large/vs1986_483.jpg', 0.57), + SearchData(200, 314518, 'Tetrapod Bowl', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-13003-001.jpg', 1.25), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/christ_redeemer_search_data.dart b/lib/logic/data/wonders_data/search/christ_redeemer_search_data.dart new file mode 100644 index 00000000..a8b4bb4c --- /dev/null +++ b/lib/logic/data/wonders_data/search/christ_redeemer_search_data.dart @@ -0,0 +1,24 @@ +part of '../christ_redeemer_data.dart'; + +// Search suggestions (11) +List _searchSuggestions = const ['ivory', 'blown', 'cane', 'hole', 'vibrated', 'aerophone', 'blow', 'fan', 'flute', 'bamboo', 'lip', ]; + +// Christ the Redeemer (16) +List _searchData = const [ + SearchData(1850, 501336, 'Flute', 'flute|bamboo|aerophone-blow hole-end-blown flute (vertical)', 'mi/mobile-large/midp89.4.720 (2).jpg', 1.78), + SearchData(1875, 501319, 'Pluriarc', 'pluriarc||chordophone-musical bow', 'mi/mobile-large/midp89.4.703.jpg', 0.60), + SearchData(1875, 501302, 'Engraved Horn', 'engraved horn|horn|aerophone-lip vibrated-horn', 'mi/mobile-large/MUS550A2.jpg', 0.58), + SearchData(1850, 502019, 'Rattle', 'rattle|fruit or nut shells, fiber rope, cord|idiophone-shaken-rattle', 'mi/mobile-large/midp89.4.1453.jpg', 1.78), + SearchData(950, 319556, 'Plate', 'plate|ceramic|ceramics-containers', 'ao/mobile-large/2005.461_a.jpg', 1.50), + SearchData(1850, 502107, 'Caracasha', 'caracasha|bamboo, basketry, oxhorn, fiber, rubber or resin|aerophone-lip vibrated-trumpet / trombone', 'mi/mobile-large/MUS563A21.jpg', 0.40), + SearchData(1850, 227759, 'Handkerchiefs (one of two)', 'handkerchief||', 'ad/mobile-large/DP2896.jpg', 0.95), + SearchData(1850, 501338, 'Poo-Do-Parana (flute)', 'poo-do-parana (flute)|bamboo, dried grass|aerophone-blow hole-side-blown flute (transverse)', 'mi/mobile-large/midp89.4.722.jpg', 2.26), + SearchData(1850, 227758, 'Handkerchiefs (one of two)', 'handkerchief||', 'ad/mobile-large/DP2927.jpg', 1.01), + SearchData(1850, 501337, 'Flute', 'flute|cane|aerophone-blow hole-end-blown flute (vertical)', 'mi/mobile-large/midp89.4.721.jpg', 1.78), + SearchData(1850, 501335, 'Flute', 'flute|bamboo or cane|aerophone-blow hole-side-blown flute (transverse)', 'mi/mobile-large/midp89.4.719.jpg', 1.78), + SearchData(1887, 122578, 'Fan', 'fan|feathers, ivory|', 'ci/mobile-large/CI43.45.1.jpg', 1.09), + SearchData(1850, 502182, 'Caracasha', 'caracasha|gourd, cane or bamboo, basketry, fibercord, rubber or resin|aerophone-lip vibrated-trumpet / trombone', 'mi/mobile-large/MUS563A19.jpg', 5.25), + SearchData(1850, 501334, 'Whistle', 'whistle|wood|aerophone-whistle flute-whistle flute', 'mi/mobile-large/MUS563A5.jpg', 0.16), + SearchData(1870, 157985, 'Fixed fan', 'fixed fan|ivory, feather|', 'ci/mobile-large/48.60_front_CP4.jpg', 0.79), + SearchData(1887, 122579, 'Fan', 'fan|feathers, ivory|', 'ci/mobile-large/CI43.45.2.jpg', 1.01), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/colosseum_search_data.dart b/lib/logic/data/wonders_data/search/colosseum_search_data.dart new file mode 100644 index 00000000..07c8dd48 --- /dev/null +++ b/lib/logic/data/wonders_data/search/colosseum_search_data.dart @@ -0,0 +1,506 @@ +part of '../colosseum_data.dart'; + +// Search suggestions (70) +List _searchSuggestions = const ['skyphos', 'marble', 'wall', 'mosaic', 'man', 'ornament', 'copper', 'sarcophagus', 'miscellaneous', 'pentelic', 'grotesque', 'handle', 'silver', 'ring', 'terracotta', 'stone', 'sculpture', 'phallic', 'urn', 'panel', 'cinerary', 'vase', 'beaker', 'oil', 'champlev', 'ivory', 'fresco', 'funerary', 'glass', 'cup', 'torso', 'fragment', 'lid', 'decoration', 'statuette', 'amulet', 'bronzes', 'woman', 'drinking', 'relief', 'bronze', 'emperor', 'villa', 'bottle', 'brooch', 'fragmentary', 'shaped', 'ribbed', 'paintings', 'gold', 'jar', 'inscription', 'pendant', 'lamp', 'spoon', 'bone', 'head', 'enamel', 'stucco', 'painting', 'flask', 'bust', 'statue', 'imperial', 'jug', 'terracottas', 'bowl', 'enamels', 'vases', 'portrait', ]; + +// Colosseum (498) +List _searchData = const [ + SearchData(149, 251450, 'Marble left hand holding a small box', 'hand|marble|stone sculpture', 'gr/mobile-large/DP231316.jpg', 1.33), + SearchData(125, 250559, 'Bronze bust of a young satyr', 'bust of a satyr, chariot attachment|bronze, silver, copper|bronzes', 'gr/mobile-large/DP20474.jpg', 1.00), + SearchData(349, 249692, 'Glass pendant in the form of a dolphin', 'pendant in the form of a dolphin|glass|glass', 'gr/mobile-large/DP121034.jpg', 0.75), + SearchData(49, 257866, 'Marble cinerary urn in the form of a tree stump with leaves and grapes', 'urn|marble|stone sculpture', 'gr/mobile-large/DP-24425-001.jpg', 0.75), + SearchData(40, 245846, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112919.jpg', 1.00), + SearchData(34, 245285, 'Glass bowl', 'bowl, patella|glass|glass', 'gr/mobile-large/DP105510.jpg', 1.00), + SearchData(25, 245364, 'Glass beaker', 'beaker|glass|glass', 'gr/mobile-large/DP120991.jpg', 1.00), + SearchData(100, 246907, 'Bronze batillum (incense shovel)', 'shovel|bronze|bronzes', 'gr/mobile-large/DP20384.jpg', 1.09), + SearchData(100, 250790, 'Fragmentary bronze statuette of boy wearing a mantle', 'statuette of a boy, upper part|bronze|bronzes', 'gr/mobile-large/sf1919257.jpg', 0.99), + SearchData(200, 250096, 'Three-handled jug with relief medallions', 'jug|terracotta|vases', 'gr/mobile-large/DP107074.jpg', 1.00), + SearchData(49, 257878, 'A pair of glass drinking cups', 'cups, pair|glass|glass', 'gr/mobile-large/DP275294.jpg', 1.33), + SearchData(199, 250092, 'Terracotta flask', 'vase in the form of a pomegranate|terracotta|vases', 'gr/mobile-large/DP117521.jpg', 1.00), + SearchData(60, 241579, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-19673-006.jpg', 0.83), + SearchData(149, 250258, 'Terracotta flask', 'vase|terracotta, glass|vases', 'gr/mobile-large/DP107317.jpg', 1.00), + SearchData(300, 466583, 'Sarcophagus Lid with Last Judgement', 'sarcophagus lid|marble|sculpture-stone', 'md/mobile-large/DT271481.jpg', 5.41), + SearchData(124, 257876, 'Glass serving dish', 'tray, cast|glass, greenish colorless|glass', 'gr/mobile-large/DP286319.jpg', 1.50), + SearchData(25, 245648, 'Glass mosaic bottle', 'perfume bottle, mosaic|glass|glass', 'gr/mobile-large/DP120995.jpg', 1.00), + SearchData(25, 257829, 'Glass gold-band mosaic lid fragment', 'lid fragment, gold-band mosaic|glass|glass', 'gr/mobile-large/DP145727.jpg', 1.33), + SearchData(-5, 250946, 'Wall painting: Polyphemus and Galatea in a landscape, from the imperial villa at Boscotrecase', 'wall painting: polyphemus and galatea in a landscape|fresco|miscellaneous-paintings', 'gr/mobile-large/DP138763.jpg', 0.67), + SearchData(149, 250100, 'Terracotta beaker with barbotine decoration', 'bowl|terracotta|vases', 'gr/mobile-large/DP107680.jpg', 1.00), + SearchData(199, 250245, 'Terracotta barrel-shaped jar', 'jar, barrel-shaped|terracotta|vases', 'gr/mobile-large/DP107354.jpg', 1.00), + SearchData(200, 473287, 'Disk Brooch', 'brooch|copper alloy, millefiori enamel|enamels-champlevé', 'md/mobile-large/TR46922000OBV.jpg', 0.88), + SearchData(12, 257842, 'Glass network mosaic bowl fragment', 'fragment, mosaic network bowl|glass|glass', 'gr/mobile-large/DP145738.jpg', 0.75), + SearchData(25, 249470, 'Glass jug', 'ennion jug|glass|glass', 'gr/mobile-large/DP121998.jpg', 0.75), + SearchData(100, 256657, 'Bronze statuette of a ram', 'statuette of a ram|bronze|bronzes', 'gr/mobile-large/DP2058.jpg', 1.00), + SearchData(39, 248851, 'Marble portrait bust of the emperor Gaius, known as Caligula', 'portrait bust of the emperor caligula|marble|stone sculpture', 'gr/mobile-large/DP337262.jpg', 0.75), + SearchData(-15, 250064, 'Terracotta cup', 'cup|terracotta|vases', 'gr/mobile-large/DP1293.jpg', 1.00), + SearchData(100, 249232, 'Couch and footstool with bone carvings and glass inlays', 'couch and footstool with bone carvings and glass inlay|wood, bone, glass|miscellaneous-bone, ivory', 'gr/mobile-large/DP138722.jpg', 1.63), + SearchData(100, 246914, 'Bronze simpulum (ladle)', 'ladle|bronze|bronzes', 'gr/mobile-large/DP20381.jpg', 0.99), + SearchData(199, 251506, 'Bone hairpin with bust of a woman', 'pin|bone|miscellaneous-bone, ivory', 'gr/mobile-large/DP121062.jpg', 0.75), + SearchData(50, 246732, 'Terracotta jug with barbotine decoration', 'jug|terracotta|vases', 'gr/mobile-large/DP109351.jpg', 1.00), + SearchData(175, 256570, 'Bronze plaque of Mithras slaying the bull', 'plaque of mithras killing the bull|bronze|bronzes', 'gr/mobile-large/DP119236.jpg', 0.84), + SearchData(50, 257884, 'Marble two-sided relief', 'relief with masks|marble|stone sculpture', 'gr/mobile-large/DP285251.jpg', 1.33), + SearchData(-15, 245787, 'Glass garland bowl', 'bowl|glass|glass', 'gr/mobile-large/DP122006.jpg', 0.74), + SearchData(20, 257641, 'Marble statue of a member of the imperial family', 'statue of a young man, half-draped|marble|stone sculpture', 'gr/mobile-large/DP-1353-002.jpg', 0.75), + SearchData(150, 255251, 'Bronze statuette of a mouse', 'statuette of a mouse from a lamp|bronze|bronzes', 'gr/mobile-large/DP20805.jpg', 1.01), + SearchData(50, 250112, 'Terracotta jug', 'vase|terracotta|vases', 'gr/mobile-large/DP107684.jpg', 1.00), + SearchData(149, 250098, 'Terracotta jar with barbotine decoration', 'bowl|terracotta|vases', 'gr/mobile-large/DP1282.jpg', 1.00), + SearchData(137, 245170, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP121441.jpg', 1.00), + SearchData(25, 241632, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-19673-001.jpg', 1.21), + SearchData(67, 250497, 'Terracotta mortarium fragment', 'mortarium fragment|terracotta|vases', 'gr/mobile-large/DP231269.jpg', 0.75), + SearchData(199, 466288, 'Brooch in the form of a Stag', 'brooch|copper alloy, champleve enamel|metalwork-copper alloy', 'md/mobile-large/sf1996-212s1.jpg', 1.20), + SearchData(-50, 250915, 'Silver skyphos (drinking cup)', 'skyphos|silver|gold and silver', 'gr/mobile-large/GR874.jpg', 1.08), + SearchData(100, 246906, 'Bronze batillum (incense shovel)', 'shovel|bronze|bronzes', 'gr/mobile-large/DP20376.jpg', 2.03), + SearchData(199, 249033, 'Ivory needle', 'needle|ivory|miscellaneous-bone, ivory', 'gr/mobile-large/DP121009.jpg', 0.75), + SearchData(50, 251447, 'Marble calyx-krater with reliefs of maidens and dancing maenads', 'calyx-krater|marble, pentelic|stone sculpture', 'gr/mobile-large/DT4541.jpg', 0.80), + SearchData(149, 250646, 'Marble column with base and capital', 'column|marble|stone sculpture', 'gr/mobile-large/DP-14287-095.jpg', 0.34), + SearchData(299, 248959, 'Glass strigil (scraper)', 'strigil|glass|glass', 'gr/mobile-large/DP107016.jpg', 1.00), + SearchData(-50, 250919, 'Silver cochlear (spoon)', 'spoon, cochlear|silver|gold and silver', 'gr/mobile-large/DT277656.jpg', 0.80), + SearchData(349, 245716, 'Glass beaker or lamp', 'beaker or lamp|glass|glass', 'gr/mobile-large/DP117585.jpg', 1.00), + SearchData(100, 250563, 'Bronze statuette of an actor', 'statuette of an actor|bronze|bronzes', 'gr/mobile-large/DP20478.jpg', 0.86), + SearchData(-100, 254781, 'Terracotta wine amphora', 'amphora, pointed|terracotta|vases', 'gr/mobile-large/DP121458.jpg', 0.55), + SearchData(100, 254968, 'Gold funerary wreath', 'wreath, funerary|gold|gold and silver', 'gr/mobile-large/SF5759.jpg', 1.33), + SearchData(50, 245667, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP120996.jpg', 1.00), + SearchData(25, 257855, 'Marble funerary altar', 'funerary altar of anthus|marble|stone sculpture', 'gr/mobile-large/DP-16750-002.jpg', 1.33), + SearchData(362, 249674, 'Glass pendant shaped like a jug', 'pendant in the form of a vase|glass|glass', 'gr/mobile-large/DP121030.jpg', 0.75), + SearchData(199, 256774, 'Marble funerary inscription', 'plaque with funerary inscription|marble|stone sculpture', 'gr/mobile-large/DP132690.jpg', 1.28), + SearchData(349, 246976, 'Bronze lampstand', 'lampstand|bronze|bronzes', 'gr/mobile-large/DP20382.jpg', 0.90), + SearchData(149, 259142, 'Terracotta mold fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP121117.jpg', 1.00), + SearchData(25, 249048, 'Terracotta atramentarium (inkwell)', 'inkwell|terracotta|vases', 'gr/mobile-large/DP107085.jpg', 1.00), + SearchData(100, 246704, 'Bronze strigil', 'strigil|bronze|bronzes', 'gr/mobile-large/DP133623.jpg', 0.75), + SearchData(50, 255093, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20798.jpg', 1.00), + SearchData(25, 241979, 'Marble inscribed block fragment', 'inscription, fragment|marble, blue gray|cesnola inscriptions', 'gr/mobile-large/DP132681.jpg', 1.42), + SearchData(10, 245827, 'Glass gold-band mosaic scyphus (drinking cup)', 'skyphos, gold-band mosaic|glass|glass', 'gr/mobile-large/DP118055.jpg', 1.33), + SearchData(115, 250106, 'Terracotta jug', 'jug|terracotta|vases', 'gr/mobile-large/DP107681.jpg', 1.00), + SearchData(-50, 250917, 'Silver simpulum (ladle)', 'kyathos|silver|gold and silver', 'gr/mobile-large/h1_20.49.2-12.jpg', 1.02), + SearchData(87, 245341, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP107095.jpg', 1.00), + SearchData(349, 245682, 'Glass beaker', 'beaker|glass|glass', 'gr/mobile-large/DP117586.jpg', 1.00), + SearchData(55, 250075, 'Terracotta bowl', 'bowl|terracotta|vases', 'gr/mobile-large/DP107068.jpg', 1.00), + SearchData(100, 257867, 'Bronze balsamarium decorated with lion-skins and herms', 'balsamarium with lion-skins and herms|bronze|bronzes', 'gr/mobile-large/DP-24426-003.jpg', 0.72), + SearchData(40, 253337, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP114270.jpg', 1.00), + SearchData(-45, 247008, 'Wall painting from the west wall of Room L of the Villa of P. Fannius Synistor at Boscoreale', 'wall painting|fresco|miscellaneous-paintings', 'gr/mobile-large/DP141474.jpg', 1.35), + SearchData(232, 258540, 'Marble portrait bust of Severus Alexander', 'portrait bust of severus alexander|marble|stone sculpture', 'gr/mobile-large/DP345023.jpg', 0.75), + SearchData(47, 634951, 'Porphyry vessel with bearded masks', 'urn, cinerary|porphyry|miscellaneous-stone vases', 'gr/mobile-large/DP329148.jpg', 1.31), + SearchData(199, 250104, 'Terracotta jar with barbotine decoration', 'urn|terracotta|vases', 'gr/mobile-large/DP121126.jpg', 1.00), + SearchData(22, 250120, 'Terracotta modiolus (drinking cup)', 'cup, "modiolus"|terracotta|vases', 'gr/mobile-large/DP107076.jpg', 1.00), + SearchData(140, 257864, 'Marble head of Zeus Ammon', 'head of zeus ammon|marble|stone sculpture', 'gr/mobile-large/DP265183.jpg', 0.87), + SearchData(100, 256652, 'Bronze statuette of a bull', 'statuette of a bull|bronze|bronzes', 'gr/mobile-large/sfx2184.jpg', 1.18), + SearchData(100, 251478, 'Bronze stamp', 'stamp, inscribed|bronze|bronzes', 'gr/mobile-large/DP20597.jpg', 1.15), + SearchData(199, 245369, 'Glass flask decorated with intersecting circles', 'flask|glass|glass', 'gr/mobile-large/DP107637.jpg', 1.00), + SearchData(12, 245719, 'Glass bowl', 'bowl|glass|glass', 'gr/mobile-large/DP121412.jpg', 1.00), + SearchData(100, 256661, 'Bronze statuette of bull', 'statuette of a bull|bronze|bronzes', 'gr/mobile-large/sfx2197.jpg', 1.33), + SearchData(249, 463712, 'Fragment of a Glass Bowl', 'bowl fragment|glass, gold leaf|glass-gold glass', 'md/mobile-large/sf16-174-1s1b.jpg', 1.18), + SearchData(347, 252884, 'Marble portrait head of the Emperor Constantine I', 'portrait head|marble|stone sculpture', 'gr/mobile-large/DP138715.jpg', 0.75), + SearchData(50, 245743, 'Glass amphoriskos (perfume flask)', 'amphoriskos|glass|glass', 'gr/mobile-large/DP146603.jpg', 0.75), + SearchData(74, 250086, 'Terracotta bowl', 'plate|terracotta|vases', 'gr/mobile-large/DP1421.jpg', 1.00), + SearchData(399, 256731, 'Glass bottle', 'amphoriskos|glass|glass', 'gr/mobile-large/DP124913.jpg', 1.00), + SearchData(190, 250462, 'Terracotta mortarium fragment', 'mortarium fragment|terracotta|vases', 'gr/mobile-large/DP107023.jpg', 1.00), + SearchData(100, 257633, 'Marble torso of Eros', 'torso of eros|marble|stone sculpture', 'gr/mobile-large/DP105506.jpg', 1.00), + SearchData(100, 250897, 'Marble statue of a young satyr turning to look at his tail', 'statue of a faun|marble|stone sculpture', 'gr/mobile-large/DP326488.jpg', 0.68), + SearchData(50, 249015, 'Glass pin', 'pin|glass|glass', 'gr/mobile-large/GR726.jpg', 0.58), + SearchData(25, 250783, 'Bronze statuette of a Lar', 'statuette of a lar|bronze|bronzes', 'gr/mobile-large/DP-14287-151.jpg', 0.74), + SearchData(237, 249032, 'Ivory pin', 'pin|ivory|miscellaneous-bone, ivory', 'gr/mobile-large/DP121008.jpg', 0.75), + SearchData(-5, 250945, 'Wall painting: Perseus and Andromeda in landscape, from the imperial villa at Boscotrecase', 'wall painting: perseus and andromeda in landscape|fresco|miscellaneous-paintings', 'gr/mobile-large/DP138761.jpg', 0.65), + SearchData(0, 257836, 'Mosaic glass fragment', 'fragment, mosaic base ring|glass|glass', 'gr/mobile-large/DP145723.jpg', 1.33), + SearchData(50, 255086, 'Bronze phallic ornament', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20790.jpg', 1.00), + SearchData(199, 256588, 'Glass ‘Mercury’ bottle fragment', 'bottle, mercury flask|glass|glass', 'gr/mobile-large/DP102367.jpg', 0.94), + SearchData(100, 256771, 'Marble plaque with funerary inscription', 'plaque with funerary inscription|marble|stone sculpture', 'gr/mobile-large/LC-X_248_1-20200226-06.jpg', 1.50), + SearchData(50, 246092, 'Gold, emerald, carnelian, banded onyx, and garnet necklace', 'necklace|gold, emerald, carnelian, banded onyx, with central cabochon garnet|gold and silver', 'gr/mobile-large/SF951613.jpg', 1.13), + SearchData(199, 249031, 'Ivory and gold pin', 'pin|ivory, gold|miscellaneous-bone, ivory', 'gr/mobile-large/DP121007.jpg', 0.75), + SearchData(100, 249157, 'Bronze fitting decorated with a bust of Neptune', 'boat fitting with head of poseidon|bronze|bronzes', 'gr/mobile-large/DP124614.jpg', 1.33), + SearchData(125, 250474, 'Terracotta jug fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP121120.jpg', 1.00), + SearchData(149, 250456, 'Terracotta lamp handle', 'lamp handle|terracotta|vases', 'gr/mobile-large/DP229117.jpg', 0.75), + SearchData(25, 249053, 'Terracotta scyphus (drinking cup)', 'skyphos|terracotta|vases', 'gr/mobile-large/DP1442.jpg', 1.00), + SearchData(100, 251419, 'Marble relief fragment with the head of Medea', 'relief fragment with head of medea|marble|stone sculpture', 'gr/mobile-large/263089.jpg', 0.82), + SearchData(55, 254473, 'Sardonyx cameo portrait of the Emperor Augustus', 'cameo, portrait of augustus|sardonyx|gems', 'gr/mobile-large/DP155547.jpg', 0.75), + SearchData(87, 246911, 'Bronze lamp', 'lamp|bronze|bronzes', 'gr/mobile-large/DP20383.jpg', 0.99), + SearchData(274, 250419, 'Terracotta fragment from the rim of a vase', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP229105.jpg', 1.33), + SearchData(87, 250927, 'Marble portrait of a young girl', 'portrait bust of a woman (domatilla?)|marble|stone sculpture', 'gr/mobile-large/DP331267.jpg', 0.75), + SearchData(150, 250107, 'Terracotta scyphus (drinking cup)', 'skyphos|terracotta|vases', 'gr/mobile-large/DP107682.jpg', 1.00), + SearchData(350, 465922, 'Plate Base with Peregrina between Saints Peter and Paul', 'bowl bottom|glass, gold leaf|glass-gold glass', 'md/mobile-large/DP225574.jpg', 1.07), + SearchData(100, 246902, 'Bronze aryballos', 'bottle|bronze|bronzes', 'gr/mobile-large/DP20369.jpg', 0.99), + SearchData(150, 255880, 'Gilded bronze mirror with the Three Graces', 'mirror with three graces|bronze, silver, gold, speculum|bronzes', 'gr/mobile-large/DT8597.jpg', 1.25), + SearchData(149, 250416, 'Terracotta bowl fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP107022.jpg', 1.00), + SearchData(-50, 250912, 'Four silver spoons', 'spoons, 4|silver|gold and silver', 'gr/mobile-large/h1_20.49.2-12.jpg', 1.02), + SearchData(249, 257881, 'Glass snake-thread dropper flask', 'flask, dropper|glass|glass', 'gr/mobile-large/DP281260.jpg', 0.75), + SearchData(180, 250918, 'Brass spoon', 'spoon|[bronze] brass|bronzes', 'gr/mobile-large/SF204910.jpg', 1.40), + SearchData(74, 245371, 'Glass amphoriskos with horizontal ribs', 'miniature transport amphora|glass|glass', 'gr/mobile-large/DP117534.jpg', 1.00), + SearchData(-45, 247010, 'Wall painting from Room H of the Villa of P. Fannius Synistor at Boscoreale', 'wall painting|fresco|miscellaneous-paintings', 'gr/mobile-large/DP140600.jpg', 1.08), + SearchData(0, 257839, 'Mosaic glass fragment', 'fragment, mosaic|glass|glass', 'gr/mobile-large/DP145722.jpg', 1.33), + SearchData(34, 245478, 'Glass gold-band mosaic bottle', 'bottle, gold-band mosaic|glass|glass', 'gr/mobile-large/DP105511.jpg', 1.00), + SearchData(299, 256744, 'Glass bottle', 'bottle, kuttrolf|glass|glass', 'gr/mobile-large/DP108414.jpg', 1.00), + SearchData(50, 245654, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP117540.jpg', 1.00), + SearchData(50, 249746, 'Glass pendant in the form of a leaf', 'pendant|glass|glass', 'gr/mobile-large/DP121039.jpg', 0.75), + SearchData(200, 250464, 'Terracotta medallion', 'medallion, fragment|terracotta|vases', 'gr/mobile-large/DP105842.jpg', 1.00), + SearchData(100, 245501, 'Bronze mirror handle (?) with Aphrodite', 'mirror support ? with aphrodite|bronze|bronzes', 'gr/mobile-large/DP20162.jpg', 0.99), + SearchData(149, 251492, 'Marble statuette of a woman', 'statuette of a woman|marble, pentelic|stone sculpture', 'gr/mobile-large/DP271490.jpg', 0.75), + SearchData(-50, 250459, 'Terracotta handle', 'patera handle|terracotta|vases', 'gr/mobile-large/DP107059.jpg', 1.00), + SearchData(100, 246915, 'Bronze spout in the form of a Corinthian column', 'spout in the form of a column|bronze|bronzes', 'gr/mobile-large/DP20388.jpg', 0.80), + SearchData(75, 256735, 'Glass cup with splayed foot', 'cup|glass|glass', 'gr/mobile-large/DP107067.jpg', 1.00), + SearchData(100, 253372, 'Marble statue of Aphrodite, the so-called Venus Genetrix', 'statue of aphrodite, the so-called venus genetrix type|marble|stone sculpture', 'gr/mobile-large/DP116947.jpg', 0.71), + SearchData(349, 245626, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP304006.jpg', 0.75), + SearchData(100, 250928, 'Bronze statuette of Mercury', 'statuette of hermes|bronze|bronzes', 'gr/mobile-large/DP20564.jpg', 0.95), + SearchData(215, 259248, 'Marble portrait head of a woman', 'head of a woman|marble|stone sculpture', 'gr/mobile-large/DP322106.jpg', 0.81), + SearchData(25, 249054, 'Terracotta scyphus (drinking cup)', 'skyphos|terracotta|vases', 'gr/mobile-large/DP107316.jpg', 1.00), + SearchData(100, 256650, 'Bronze statuette of Hercules', 'statuette of herakles|bronze|bronzes', 'gr/mobile-large/DP2063.jpg', 1.00), + SearchData(149, 245510, 'Terracotta statuette of Isis or a follower of her cult', 'statuette of isis|terracotta|terracottas', 'gr/mobile-large/DP105512.jpg', 1.00), + SearchData(50, 251403, 'Ivory pyxis (box with lid)', 'pyxis with erotes|ivory|miscellaneous-bone, ivory', 'gr/mobile-large/DP-14287-004.jpg', 0.94), + SearchData(100, 251476, 'Marble head and torso of Athena', 'statue of athena, upper part|marble, pentelic|stone sculpture', 'gr/mobile-large/DP357289.jpg', 0.75), + SearchData(349, 255960, 'Silver mirror', 'mirror|silver|gold and silver', 'gr/mobile-large/DP145605.jpg', 1.00), + SearchData(349, 249685, 'Glass pendant in the form of a lamp', 'pendant in the form of a lamp|glass|glass', 'gr/mobile-large/DP121032.jpg', 0.75), + SearchData(149, 250256, 'Terracotta flask', 'vase|terracotta|vases', 'gr/mobile-large/DP107688.jpg', 1.00), + SearchData(312, 241770, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-291.jpg', 1.05), + SearchData(325, 465921, 'Bowl Fragments with Menorah, Shofar, and Torah Ark', 'bowl fragments|glass, gold leaf|glass-gold glass', 'md/mobile-large/h1_18.145.1ab.jpg', 1.28), + SearchData(50, 253327, 'Fresco fragment with siren', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP229128.jpg', 0.75), + SearchData(100, 251493, 'Marble head of a Greek general', 'head of a general (strategos)|marble, pentelic ?|stone sculpture', 'gr/mobile-large/DP345013.jpg', 0.75), + SearchData(150, 250095, 'Terracotta pyxis (box) with lid', 'pyxis with lid|terracotta|vases', 'gr/mobile-large/DP1412.jpg', 1.00), + SearchData(325, 466220, 'Sarcophagus with Scenes from the Lives of Saint Peter and Christ', 'sarcophagus|marble|sculpture-stone', 'md/mobile-large/DT6738.jpg', 2.71), + SearchData(100, 251415, 'Marble statuette of a slave boy with a lantern', 'statuette of a slave with lamp|marble|stone sculpture', 'gr/mobile-large/DP292010.jpg', 0.75), + SearchData(100, 250562, 'Bronze statuette of a comic actor', 'statuette of an actor, comic|bronze|bronzes', 'gr/mobile-large/DP20985.jpg', 0.99), + SearchData(150, 257620, 'Terracotta mold fragment', 'mold fragment|terracotta|vases', 'gr/mobile-large/DP121119.jpg', 1.00), + SearchData(150, 249020, 'Gilt bronze strigil', 'strigil|bronze, gold|bronzes', 'gr/mobile-large/DP124613.jpg', 0.75), + SearchData(0, 257834, 'Glass striped mosaic fragment', 'fragment, striped mosaic|glass|glass', 'gr/mobile-large/DP145732.jpg', 1.33), + SearchData(220, 249061, 'Right corner of a marble sarcophagus with the myth of Apollo and the satyr Marsyas', 'sarcophagus fragment, apollo and marsyas|marble|stone sculpture', 'gr/mobile-large/DP-14287-039.jpg', 0.85), + SearchData(82, 251838, 'Fragments of a marble statue of the Diadoumenos (youth tying a fillet around his head)', 'statue of the diadoumenos, fragmentary|marble|stone sculpture', 'gr/mobile-large/DT11982.jpg', 0.73), + SearchData(30, 257612, 'Terracotta bowl', 'bowl|terracotta|vases', 'gr/mobile-large/DP107027.jpg', 1.00), + SearchData(149, 253386, 'Marble portrait of the emperor Antoninus Pius', 'portrait head of the emperor antoninus pius|marble|stone sculpture', 'gr/mobile-large/DP333076.jpg', 0.75), + SearchData(0, 257831, 'Glass mosaic ribbed bowl fragment', 'fragment, mosaic ribbed bowl rim|glass|glass', 'gr/mobile-large/DP145729.jpg', 1.33), + SearchData(100, 650960, 'Greywacke plate', 'plate|greywacke|miscellaneous-stone vases', 'gr/mobile-large/DP700676.jpg', 1.26), + SearchData(312, 468759, 'Table Base with Jonah Swallowed and Cast Up by the Big Fish', 'sculpture|marble, white|sculpture-stone', 'md/mobile-large/DT234784.jpg', 1.25), + SearchData(-5, 250931, 'Wall painting on black ground: Egyptianizing scene and pair of swans, from the imperial villa at Boscotrecase', 'wall painting on black ground: egyptianizing scene and pair of swans|fresco|miscellaneous-paintings', 'gr/mobile-large/DP144211.jpg', 1.00), + SearchData(349, 245717, 'Glass flask', 'bottle with engraved lines|glass|glass', 'gr/mobile-large/DP304007.jpg', 0.75), + SearchData(50, 251412, 'Bronze furniture attachment', 'furniture attachment, head of a mule|bronze|bronzes', 'gr/mobile-large/DP20617.jpg', 0.98), + SearchData(50, 250954, 'Statue of a boy', 'statuette of a youth|bekhen stone|stone sculpture', 'gr/mobile-large/DP-23278-001.jpg', 0.75), + SearchData(150, 250089, 'Terracotta bowl', 'bowl|terracotta|vases', 'gr/mobile-large/DP1400.jpg', 1.00), + SearchData(325, 258076, 'Glass bowl in the form of a shell', 'bowl|glass|glass', 'gr/mobile-large/DP153111.jpg', 1.00), + SearchData(149, 248175, 'Stucco relief fragment', 'relief of emperor antoninus pius and a barbarian suppliant|stucco|miscellaneous-stucco', 'gr/mobile-large/DP105599.jpg', 1.00), + SearchData(265, 254819, 'Marble sarcophagus with the Triumph of Dionysos and the Seasons', 'sarcophagus, triumph of dionysos and the seasons|marble|stone sculpture', 'gr/mobile-large/DP-14287-144.jpg', 0.87), + SearchData(40, 253326, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112915.jpg', 1.00), + SearchData(50, 246917, 'Bronze handle of a patera (shallow saucepan)', 'handle of a patera with wolf|bronze|bronzes', 'gr/mobile-large/DP133619.jpg', 1.33), + SearchData(10, 248973, 'Glass ribbed bowl', 'bowl, ribbed|glass|glass', 'gr/mobile-large/DP120951.jpg', 1.00), + SearchData(185, 250099, 'Terracotta indented beaker', 'beaker|terracotta indented beaker, probably made at trier|vases', 'gr/mobile-large/DP1288.jpg', 1.00), + SearchData(449, 241592, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-244.jpg', 1.05), + SearchData(-13, 248466, 'Bronze statuette of a philosopher on a lamp stand', 'statuette of philosopher on lamp stand, hermarchos ?|bronze|bronzes', 'gr/mobile-large/DP337221.jpg', 0.75), + SearchData(25, 241722, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-186.jpg', 0.91), + SearchData(100, 250955, 'Bronze statuette of Hermes seated on a rock', 'statuette of hermes holding a purse|bronze|bronzes', 'gr/mobile-large/DP20561.jpg', 0.95), + SearchData(95, 250105, 'Terracotta oil lamp', 'lamp, glazed|terracotta|terracottas', 'gr/mobile-large/DP229109.jpg', 1.33), + SearchData(100, 250142, 'Glass head of Zeus Sarapis', 'head of zeus sarapis|glass|glass', 'gr/mobile-large/DP121791.jpg', 1.00), + SearchData(199, 256823, 'Bronze steelyard weight', 'weight, bust of isis|bronze|bronzes', 'gr/mobile-large/DP2088.jpg', 1.00), + SearchData(325, 250141, 'Glass portrait head', 'head of a man|glass|glass', 'gr/mobile-large/DP141538.jpg', 1.00), + SearchData(449, 246909, 'Bronze steelyard and weight', 'steelyard|bronze|bronzes', 'gr/mobile-large/DP20386.jpg', 1.26), + SearchData(349, 258494, 'Glass dish', 'dish|glass|glass', 'gr/mobile-large/DP228638.jpg', 1.58), + SearchData(220, 257781, 'Marble strigilated sarcophagus', 'sarcophagus, strigilated|marble|stone sculpture', 'gr/mobile-large/DP130701.jpg', 1.38), + SearchData(149, 251414, 'Marble relief of a chisel and mallet', 'relief of a mallet and chisel|marble-italian ?|stone sculpture', 'gr/mobile-large/LC-23_160_81.jpg', 0.85), + SearchData(50, 250983, 'Gold and pearl earring', 'earring with pearl pendants|gold, pearl|gold and silver', 'gr/mobile-large/SF20234235.jpg', 1.31), + SearchData(87, 245683, 'Glass bottle shaped like a date', 'flask in the form of a date|glass|glass', 'gr/mobile-large/DP121411.jpg', 1.00), + SearchData(25, 249176, 'Terracotta bowl', 'bowl, footed|terracotta|vases', 'gr/mobile-large/DP105600.jpg', 1.00), + SearchData(249, 245390, 'Glass plate with head of Medusa', 'plate with head of medusa|glass|glass', 'gr/mobile-large/GR762.jpg', 1.65), + SearchData(0, 257835, 'Network mosaic glass fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP145733.jpg', 0.75), + SearchData(22, 250118, 'Terracotta scyphus (drinking cup)', 'skyphos|terracotta|vases', 'gr/mobile-large/DP107075.jpg', 1.00), + SearchData(199, 468362, 'Brooch in the Form of a Peacock', 'brooch|champlevé enamel, bronze, garnet cabochon|enamels-champlevé', 'md/mobile-large/sf51-125-1s1.jpg', 0.75), + SearchData(25, 245495, 'Glass cameo cup fragment', 'cameo cup fragment, symplegma|glass|glass', 'gr/mobile-large/DT12101.jpg', 0.79), + SearchData(249, 245815, 'Glass finger ring', 'ring|glass|glass', 'gr/mobile-large/DP121097.jpg', 0.75), + SearchData(75, 250248, 'Terracotta bowl', 'dish|terracotta|vases', 'gr/mobile-large/DP1418.jpg', 1.00), + SearchData(0, 257639, 'Bronze torso from an equestrian statue wearing a cuirass', 'torso from an equestrian statue wearing a cuirass|bronze|bronzes', 'gr/mobile-large/DP108187.jpg', 1.00), + SearchData(100, 250566, 'Bronze statuette of Jupiter Capitolinus', 'statuette of jupiter capitolinus|bronze|bronzes', 'gr/mobile-large/DP161809.jpg', 1.00), + SearchData(13, 245631, 'Glass beaker', 'beaker|glass, cast|glass', 'gr/mobile-large/SF9111238color.jpg', 0.88), + SearchData(249, 245744, 'Glass double head-shaped flask', 'flask, double head-shaped|glass|glass', 'gr/mobile-large/DP107639.jpg', 1.00), + SearchData(100, 256772, 'Marble plaque with funerary inscription', 'plaque with funerary inscription|marble|stone sculpture', 'gr/mobile-large/LC-X_248_2-20200226-06.jpg', 1.50), + SearchData(149, 250556, 'Marble torso of a youth', 'statue of a youth, torso|marble|stone sculpture', 'gr/mobile-large/DP-14287-141.jpg', 0.75), + SearchData(195, 250351, 'Fragment of terra sigillata', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP107020.jpg', 1.00), + SearchData(215, 245931, 'Gold and beryl ring', 'ring|gold, beryl|gold and silver', 'gr/mobile-large/sf9515131edited.jpg', 1.33), + SearchData(149, 257626, 'Marble cornice fragment', 'architectural fragment|marble|stone sculpture', 'gr/mobile-large/sf2002603.jpg', 1.83), + SearchData(99, 251475, 'Marble torso of Eros', 'statue of eros, fragmentary|marble|stone sculpture', 'gr/mobile-large/59444.jpg', 0.78), + SearchData(50, 255869, 'Bronze lamp', 'lamp|bronze|bronzes', 'gr/mobile-large/DP20848.jpg', 1.00), + SearchData(25, 257725, 'Bone pyxis (box with lid)', 'pyxis with lid|bone|miscellaneous-bone, ivory', 'gr/mobile-large/SF2004507abedited.jpg', 1.17), + SearchData(25, 247993, 'Marble portrait of the emperor Augustus', 'portrait head of the emperor augustus|marble|stone sculpture', 'gr/mobile-large/DP337220.jpg', 0.75), + SearchData(562, 245304, 'Glass weight', 'weight|glass|glass', 'gr/mobile-large/DP121465.jpg', 1.00), + SearchData(25, 245493, 'Glass cameo fragment of a large platter or tabletop', 'cameo, platter fragment|glass|glass', 'gr/mobile-large/DP118115.jpg', 1.68), + SearchData(150, 253037, 'Terracotta fragment of a bowl', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP105846.jpg', 1.00), + SearchData(50, 241623, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-252.jpg', 1.05), + SearchData(50, 250504, 'Fragmentary terracotta bowl', 'cup, fragment|terracotta|vases', 'gr/mobile-large/DP121123.jpg', 1.00), + SearchData(15, 250776, 'Terracotta cup', 'cup, fragment|terracotta|vases', 'gr/mobile-large/DP1415.jpg', 1.00), + SearchData(149, 250257, 'Terracotta rattle in the form of a pig', 'rattle in the form of a pig|terracotta, glass|terracottas', 'gr/mobile-large/DP2031.jpg', 1.00), + SearchData(12, 257827, 'Mosaic glass fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP145725.jpg', 1.33), + SearchData(41, 245849, 'Wall painting fragment with a swan', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112922.jpg', 1.00), + SearchData(212, 256758, 'Marble portrait head of a woman', 'portrait bust of a woman|marble|stone sculpture', 'gr/mobile-large/DP333686.jpg', 0.75), + SearchData(499, 245521, 'Terracotta statuette of a bear holding an oil lamp', 'statuette of a bear holding lamp|terracotta|terracottas', 'gr/mobile-large/DP145569.jpg', 0.74), + SearchData(100, 251460, 'Marble statuette of a seated philosopher', 'statuette of a philosopher|marble|stone sculpture', 'gr/mobile-large/DP338143.jpg', 0.75), + SearchData(-5, 250939, 'Wall painting on black ground: landscape, from the imperial villa at Boscotrecase', 'wall painting on black ground: landscape|fresco|miscellaneous-paintings', 'gr/mobile-large/DP146610.jpg', 1.00), + SearchData(100, 250787, 'Bronze statuette of Cupid', 'statuette of cupid|bronze|bronzes', 'gr/mobile-large/DP20573.jpg', 0.93), + SearchData(25, 245363, 'Glass beaker with inscription', 'beaker, inscribed|glass|glass', 'gr/mobile-large/DP120990.jpg', 1.00), + SearchData(249, 256732, 'Glass bottle shaped like a bunch of grapes', 'bottle with stylized grape cluster|glass|glass', 'gr/mobile-large/DP108406.jpg', 1.00), + SearchData(149, 250242, 'Fragmentary terracotta cup', 'cup, fragmentary|terracotta|vases', 'gr/mobile-large/DP121114.jpg', 1.00), + SearchData(195, 250350, 'Fragment of terra sigillata', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP107019.jpg', 1.00), + SearchData(74, 245836, 'Stucco relief panel', 'stucco relief of maenad|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-077.jpg', 1.13), + SearchData(50, 245734, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP107100.jpg', 1.00), + SearchData(0, 257833, 'Glass mosaic base ring fragment', 'fragment, mosaic|glass|glass', 'gr/mobile-large/DP145731.jpg', 1.33), + SearchData(100, 246992, 'Marble head of a Hellenistic ruler', 'head of a youth|marble|stone sculpture', 'gr/mobile-large/DP119205.jpg', 0.71), + SearchData(37, 250540, 'Bronze bust of Jupiter', 'bust of zeus|bronze, copper|bronzes', 'gr/mobile-large/DP-12522-001.jpg', 1.00), + SearchData(249, 257875, 'Glass snake-thread flask shaped like a mouse', 'flask, snake-thread mouse-shaped|glass, blue|glass', 'gr/mobile-large/DP286317.jpg', 1.33), + SearchData(25, 246266, 'Marble cinerary chest with lid', 'cippus of lucius gavius (funerary chest)|marble|stone sculpture', 'gr/mobile-large/47477.jpg', 0.94), + SearchData(74, 245834, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-073.jpg', 0.83), + SearchData(50, 250586, 'Miniature amber amphora (jar)', 'amphora, miniature|amber|miscellaneous-amber', 'gr/mobile-large/SF1723053edited.jpg', 1.08), + SearchData(-45, 247017, 'Cubiculum (bedroom) from the Villa of P. Fannius Synistor at Boscoreale', 'cubiculum (bedroom) from the villa of p. fannius synistor|fresco|miscellaneous-paintings', 'gr/mobile-large/DP143704.jpg', 1.04), + SearchData(212, 239584, 'Marble sarcophagus with garlands', 'sarcophagus, garland|marble, proconnesian|stone sculpture', 'gr/mobile-large/DP140135.jpg', 1.45), + SearchData(449, 245695, 'Glass bottle', 'bottle|glass|glass', 'gr/mobile-large/DP121451.jpg', 1.00), + SearchData(214, 253592, 'Marble portrait of the emperor Caracalla', 'portrait head of the emperor marcus aurelius antoninus (called caracalla)|marble|stone sculpture', 'gr/mobile-large/DP333080.jpg', 0.75), + SearchData(149, 249051, 'Marble portrait of a man', 'portrait bust of a man|marble|stone sculpture', 'gr/mobile-large/DP344994.jpg', 0.75), + SearchData(-5, 250933, 'Wall painting on black ground: supports with entrablature, from the imperial villa at Boscotrecase', 'wall painting on black ground: supports with entablature|fresco|miscellaneous-paintings', 'gr/mobile-large/DP146615.jpg', 1.00), + SearchData(50, 255089, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20802.jpg', 1.01), + SearchData(180, 250519, 'Terracotta relief from a lamp with Leda and the swan', 'lamp relief fragment|terracotta|terracottas', 'gr/mobile-large/DP229108.jpg', 0.75), + SearchData(105, 250094, 'Terracotta plate', 'plate|terracotta|vases', 'gr/mobile-large/DP107081.jpg', 1.00), + SearchData(12, 257830, 'Glass striped mosaic fragment', 'fragment, striped mosaic, quadripartite|glass|glass', 'gr/mobile-large/DP145728.jpg', 0.75), + SearchData(399, 256712, 'Glass jug with trefoil rim', 'jug with trefoil rim|glass|glass', 'gr/mobile-large/DP117557.jpg', 1.00), + SearchData(149, 468473, 'Bow Brooch', 'brooch|champlevé enamel, bronze|enamels-champlevé', 'md/mobile-large/sf55-140s4.jpg', 0.60), + SearchData(199, 245344, 'Glass spouted jug', 'jug|glass|glass', 'gr/mobile-large/DP107636.jpg', 1.00), + SearchData(87, 251452, 'Glass cinerary urn with lid', 'urn with two handles and lid|glass|glass', 'gr/mobile-large/DP107323.jpg', 1.00), + SearchData(84, 250234, 'Terracotta strainer jug', 'vase|terracotta|vases', 'gr/mobile-large/DP107686.jpg', 1.00), + SearchData(145, 241809, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-183.jpg', 0.88), + SearchData(249, 246849, 'Silver bracelet in the form of a snake', 'bracelet with snakes\' heads|silver|gold and silver', 'gr/mobile-large/DP136030.jpg', 1.33), + SearchData(350, 473395, 'Lute', 'lute|wood with traces of paint|woodwork-miscellany', 'md/mobile-large/DP302641.jpg', 1.33), + SearchData(150, 246760, 'Pair of gold earrings', 'earring|gold|gold and silver', 'gr/mobile-large/DP136021.jpg', 1.33), + SearchData(150, 250241, 'Terracotta jar with barbotine decoration', 'vase|terracotta|vases', 'gr/mobile-large/DP107687.jpg', 1.00), + SearchData(537, 241597, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP105613.jpg', 1.00), + SearchData(100, 250623, 'Bronze specillum (probe)', 'spatula|bronze|bronzes', 'gr/mobile-large/DP20539.jpg', 1.41), + SearchData(34, 249623, 'Glass and gold inlay', 'mosaic block inlay|glass|glass', 'gr/mobile-large/sf17194387color.jpg', 1.05), + SearchData(50, 255090, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20803.jpg', 1.00), + SearchData(50, 255094, 'Bronze phallic ornament', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20797.jpg', 1.00), + SearchData(0, 259216, 'Marble pillar with Neo-Attic reliefs', 'pillar|marble|stone sculpture', 'gr/mobile-large/DP325731.jpg', 0.55), + SearchData(252, 247117, 'Bronze statue of the emperor Trebonianus Gallus', 'statue of emperor trebonianus gallus|bronze|bronzes', 'gr/mobile-large/DP138716.jpg', 0.75), + SearchData(50, 253355, 'Plaster relief fragment with a male figure on a throne', 'mold of ancient casts for metal reliefs|plaster|miscellaneous-plaster', 'gr/mobile-large/sf311116.jpg', 1.33), + SearchData(50, 253354, 'Plaster relief fragment with a male figure on a throne', 'mold of ancient casts for metal reliefs|plaster|miscellaneous-plaster', 'gr/mobile-large/DP146294.jpg', 0.75), + SearchData(74, 245837, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-080.jpg', 1.07), + SearchData(349, 249676, 'Glass pendant shaped like a jug', 'pendant in the form of a vase|glass|glass', 'gr/mobile-large/DP124931.jpg', 0.75), + SearchData(20, 255973, 'Statue of Dionysos leaning on a female figure ("Hope Dionysos")', 'statue of dionysos leaning on a female figure ("hope dionysos")|marble|stone sculpture', 'gr/mobile-large/DT6494.jpg', 0.80), + SearchData(115, 250109, 'Terracotta lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP105840.jpg', 1.00), + SearchData(74, 245839, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-079.jpg', 1.37), + SearchData(399, 256746, 'Glass bottle', 'bottle|glass|glass', 'gr/mobile-large/DP117554.jpg', 1.00), + SearchData(349, 245720, 'Glass beaker', 'beaker|glass|glass', 'gr/mobile-large/DP141536.jpg', 1.00), + SearchData(50, 245381, 'Glass bottle', 'bottle|glass|glass', 'gr/mobile-large/DP107097.jpg', 1.00), + SearchData(75, 245378, 'Glass cup in the form of the head of a Black African', 'cup in the form of a head of a african|glass|glass', 'gr/mobile-large/DP118340.jpg', 1.00), + SearchData(150, 250123, 'Glass bowl fragment with later inscription', 'bowl fragment|glass|glass', 'gr/mobile-large/DP166246.jpg', 1.00), + SearchData(252, 247169, 'Bronze sestertius of Trebonianus Gallus', 'sestertius|bronze|coins', 'gr/mobile-large/DP104783.jpg', 1.00), + SearchData(100, 246708, 'Pair of gold boat-shaped earrings', 'earring, pair|gold|gold and silver', 'gr/mobile-large/DP136022.jpg', 1.33), + SearchData(349, 245689, 'Glass flask', 'flask, globular|glass|glass', 'gr/mobile-large/DP121450.jpg', 1.00), + SearchData(50, 250139, 'Ivory hairpin', 'pin|ivory|miscellaneous-bone, ivory', 'gr/mobile-large/DP121059.jpg', 0.75), + SearchData(149, 465900, 'Brooch in the Form of a Dog', 'brooch|champlevé enamel, bronze|enamels-champlevé', 'md/mobile-large/sf17-194-2390s1.jpg', 1.79), + SearchData(249, 245358, 'Glass indented jar', 'jar, indented|glass|glass', 'gr/mobile-large/DP117533.jpg', 1.00), + SearchData(50, 245370, 'Glass hexagonal jug', 'jug, hexagonal|glass|glass', 'gr/mobile-large/DP120993.jpg', 1.00), + SearchData(74, 245833, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-074.jpg', 0.89), + SearchData(100, 256660, 'Bronze statuette of a goose', 'statuette of a goose|bronze|bronzes', 'gr/mobile-large/sfx2196.jpg', 1.33), + SearchData(40, 253334, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP114268.jpg', 1.00), + SearchData(85, 250909, 'Terracotta one-handled cup', 'cup, one-handled|terracotta|vases', 'gr/mobile-large/DP1463.jpg', 1.00), + SearchData(50, 255010, 'Bronze bust of an Amazon', 'bust of amazon|bronze|bronzes', 'gr/mobile-large/DP20780.jpg', 1.00), + SearchData(50, 245374, 'Glass beaker', 'beaker|glass|glass', 'gr/mobile-large/DP107096.jpg', 1.00), + SearchData(1, 257877, 'Glass ribbed bowl', 'bowl, ribbed|glass|glass', 'gr/mobile-large/DP281252.jpg', 1.44), + SearchData(149, 256126, 'Porphyry support for a water basin', 'stand for a basin|porphyry|stone sculpture', 'gr/mobile-large/DP-14287-132.jpg', 1.71), + SearchData(150, 250093, 'Terracotta plate', 'plate|terracotta|vases', 'gr/mobile-large/DP107080.JPG', 1.00), + SearchData(40, 253335, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP114269.jpg', 1.00), + SearchData(25, 245367, 'Glass beaker with inscription', 'beaker, inscribed|glass|glass', 'gr/mobile-large/DP120992.jpg', 1.00), + SearchData(49, 241656, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-19673-097.jpg', 1.06), + SearchData(0, 257832, 'Glass striped mosaic fragment', 'fragment, striped mosaic|glass|glass', 'gr/mobile-large/DP145730.jpg', 1.33), + SearchData(-112, 246594, 'Terracotta Megarian bowl', 'bowl|terracotta|vases', 'gr/mobile-large/DP1297.jpg', 1.00), + SearchData(100, 246676, 'Bronze ladle', 'ladle|bronze|bronzes', 'gr/mobile-large/DP20324.jpg', 0.95), + SearchData(100, 245503, 'Bronze ring with bust of Serapis', 'ring with bust of serapis|bronze|bronzes', 'gr/mobile-large/sf892558.jpg', 0.93), + SearchData(50, 255088, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20801.jpg', 1.01), + SearchData(149, 256778, 'Marble head of a woman', 'portrait head of a woman|marble|stone sculpture', 'gr/mobile-large/DP333689.jpg', 0.75), + SearchData(50, 250188, 'Terracotta vase fragment with relief of Minerva', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP105601.jpg', 1.00), + SearchData(1, 258555, 'Marble statuette of young Dionysos', 'torso of dionysos|marble|stone sculpture', 'gr/mobile-large/DP265186.jpg', 0.79), + SearchData(50, 254841, 'Bronze fulcrum attachment with a bust of Eros', 'furniture attachment from a couch, eros|bronze|bronzes', 'gr/mobile-large/DP20784.jpg', 1.00), + SearchData(212, 256773, 'Marble funerary inscription', 'plaque with funerary inscription|marble|stone sculpture', 'gr/mobile-large/DP132689.jpg', 1.26), + SearchData(200, 250326, 'Terracotta medallion fragment', 'medallion, fragment|terracotta|vases', 'gr/mobile-large/DP107078.jpg', 1.00), + SearchData(199, 245840, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP145600.jpg', 1.00), + SearchData(34, 249624, 'Glass mosaic inlay fragments', 'mosaic block inlay|glass|glass', 'gr/mobile-large/sf17194388a-c_edited.jpg', 1.60), + SearchData(100, 250492, 'Terracotta vase fragment with figure of Hercules', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP105602.jpg', 1.00), + SearchData(162, 247524, 'Silver handle of a large dish', 'handle of a large dish|silver, gold|gold and silver', 'gr/mobile-large/DP107034.jpg', 1.00), + SearchData(200, 466382, 'Disk Brooch', 'brooch|champlevé enamel, bronze|enamels-champlevé', 'md/mobile-large/sf22-50-11s1.jpg', 1.09), + SearchData(199, 466218, 'S-Shaped Brooch', 'brooch|bronze|metalwork-bronze', 'md/mobile-large/MED150.jpg', 0.72), + SearchData(299, 249669, 'Glass pendant shaped like a jar', 'pendant|glass|glass', 'gr/mobile-large/DP121028.jpg', 0.75), + SearchData(75, 245685, 'Glass cinerary urn with lid', 'urn with two handles and lid|glass|glass', 'gr/mobile-large/DP109362.jpg', 1.00), + SearchData(99, 250929, 'Limestone torso of a hunter', 'torso of a hunter, fragmentary|black limestone|stone sculpture', 'gr/mobile-large/DP279059.jpg', 0.75), + SearchData(100, 251431, 'Amber disk with a nereid riding a triton', 'disk|amber|miscellaneous-amber', 'gr/mobile-large/SF2316098color.jpg', 1.08), + SearchData(50, 255087, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20800.jpg', 1.01), + SearchData(55, 245636, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP107098.jpg', 1.00), + SearchData(99, 250899, 'Marble torso of a seated man', 'torso of a youth, fragmentary|marble|stone sculpture', 'gr/mobile-large/120133.jpg', 0.67), + SearchData(149, 250784, 'Bronze statuette of Minerva', 'statuette of athena|bronze|bronzes', 'gr/mobile-large/DP-14972-001.jpg', 0.79), + SearchData(199, 250904, 'Fragmentary porphyry head of a bearded man', 'head of a man|porphyry|stone sculpture', 'gr/mobile-large/DP202796.jpg', 1.00), + SearchData(40, 253338, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP114271.jpg', 1.00), + SearchData(100, 250564, 'Bronze footed globular vessel with lid', 'pyxis with lid|bronze|bronzes', 'gr/mobile-large/DP21061.jpg', 0.99), + SearchData(299, 250487, 'Glass bowl fragment with gold leaf and painted decoration', 'bowl with gilding-gold leaf; ocean|glass, gold, paint|glass', 'gr/mobile-large/DP107355.jpg', 1.00), + SearchData(199, 250734, 'Glass jug decorated with intersecting circles', 'jug|glass|glass', 'gr/mobile-large/DP107001.jpg', 1.00), + SearchData(100, 246908, 'Bronze batillum (incense shovel)', 'shovel|bronze|bronzes', 'gr/mobile-large/DP20385.jpg', 1.35), + SearchData(275, 466645, 'Medallion with a Portrait of Gennadios', 'medallion|glass, gold leaf, polychromy|glass-gold glass', 'md/mobile-large/DP325825.jpg', 0.99), + SearchData(300, 253369, 'Fragmentary marble votive altar', 'votive relief fragment, inscription|marble|stone sculpture', 'gr/mobile-large/DP145791.jpg', 0.71), + SearchData(150, 469961, 'Wing Brooch', 'brooch|silver, gold, four carnelians|metalwork-silver', 'md/mobile-large/DT108.jpg', 0.80), + SearchData(50, 245709, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP117541.jpg', 1.00), + SearchData(100, 246904, 'Bronze aryballos', 'bottle|bronze|bronzes', 'gr/mobile-large/DP20368.jpg', 0.99), + SearchData(399, 245314, 'Glass pendant in the form of a miniature jug', 'pendant in the form of a miniature jug|glass|glass', 'gr/mobile-large/DP121094.jpg', 0.75), + SearchData(390, 250383, 'Terracotta bowl fragment', 'bowl fragment|terracotta|vases', 'gr/mobile-large/DP121116.jpg', 1.00), + SearchData(61, 250078, 'Terracotta marbled slip ware bowl', 'bowl|terracotta|vases', 'gr/mobile-large/DP107069.jpg', 1.00), + SearchData(40, 245845, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112918.jpg', 1.00), + SearchData(75, 250082, 'Terracotta amphora (jar)', 'amphora|terracotta|vases', 'gr/mobile-large/DP107070.jpg', 1.00), + SearchData(299, 466585, 'Fragment of a Sarcophagus with Putti in a Grapevine', 'relief|marble|sculpture-stone', 'md/mobile-large/sf24-97-12s1.jpg', 1.41), + SearchData(15, 250119, 'Terracotta beaker', 'cup|terracotta|vases', 'gr/mobile-large/SF17194895b.jpg', 0.80), + SearchData(40, 247173, 'Marble statue of Eirene (the personification of peace)', 'statue of eirene|marble, pentelic ?|stone sculpture', 'gr/mobile-large/DT11659.jpg', 0.73), + SearchData(297, 245923, 'Gold crossbow fibula (brooch)', 'fibula, crossbow type|gold|gold and silver', 'gr/mobile-large/DP107033.jpg', 1.00), + SearchData(149, 251474, 'Marble sarcophagus with the myth of Endymion', 'sarcophagus, endymion|marble|stone sculpture', 'gr/mobile-large/DP-14287-043.jpg', 2.00), + SearchData(100, 246910, 'Ring for the attachment of bath implements', 'ring with lions|bronze|bronzes', 'gr/mobile-large/DP20389.jpg', 1.06), + SearchData(-6, 246657, 'Terracotta antefix', 'antefix with venus and mars|terracotta|terracottas', 'gr/mobile-large/DP-14287-052.jpg', 0.83), + SearchData(0, 257837, 'Mosaic glass fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP145734.jpg', 0.75), + SearchData(149, 256403, 'Marble Statue Group of the Three Graces', 'statue group of the three graces|marble|stone sculpture', 'gr/mobile-large/DP222664.jpg', 1.21), + SearchData(-5, 250930, 'Wall painting on black ground: Aedicula with small landscape, from the imperial villa at Boscotrecase', 'wall painting on black ground: aedicula with small landscape|fresco|miscellaneous-paintings', 'gr/mobile-large/DP144209.jpg', 1.00), + SearchData(350, 245345, 'Glass beaker', 'beaker|glass|glass', 'gr/mobile-large/DP107004.jpg', 1.00), + SearchData(149, 245730, 'Glass beaker with facet-cut decoration', 'beaker|glass|glass', 'gr/mobile-large/DP117542.jpg', 1.00), + SearchData(0, 256184, 'Pair of silver scyphi (cups) with relief decoration', 'skyphoi with erotes, pair|silver with gilding|gold and silver', 'gr/mobile-large/gr1994.43.1-.2.R.jpg', 1.61), + SearchData(74, 245835, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-078.jpg', 0.87), + SearchData(199, 468713, 'Disk Brooch', 'brooch|millefiore enamel, bronze|enamels-champlevé', 'md/mobile-large/sf66-16s1.jpg', 1.02), + SearchData(50, 245737, 'Glass ribbed bottle', 'flask|glass|glass', 'gr/mobile-large/DP121000.jpg', 1.00), + SearchData(170, 250352, 'Fragment of terra sigillata', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP107021.jpg', 1.00), + SearchData(-6, 248891, 'Bronze statue of an aristocratic boy', 'statue of a boy, gaius caesar ?|bronze|bronzes', 'gr/mobile-large/DP345062.jpg', 0.75), + SearchData(-50, 250920, 'Silver cochlear (spoon)', 'spoon, cochlear|silver|gold and silver', 'gr/mobile-large/DT277656.jpg', 0.80), + SearchData(50, 253353, 'Circular plaster relief', 'mold of ancient casts for metal reliefs|plaster|miscellaneous-plaster', 'gr/mobile-large/sf311115.jpg', 1.33), + SearchData(41, 248132, 'Marble statue of an old woman', 'statue of an old market woman|marble, pentelic|stone sculpture', 'gr/mobile-large/DP277237.jpg', 0.73), + SearchData(200, 468180, 'Brooch in the Form of a Panther', 'figurine|copper alloy inlaid with silver and niello|metalwork-copper alloy', 'md/mobile-large/DP102245.JPG', 1.06), + SearchData(150, 250145, 'Glass fragments of bowl decorated with mosaic fish', 'dish or plaque fragments|glass|glass', 'gr/mobile-large/DP140700.jpg', 0.75), + SearchData(199, 462907, 'Key Handle in the Form of a Horse’s Head', 'key or knife handle|copper alloy|metalwork-copper alloy', 'md/mobile-large/sf06-176-24s1.jpg', 2.15), + SearchData(100, 250625, 'Bronze specillum (probe)', 'spoon or ligula|bronze|bronzes', 'gr/mobile-large/DP20542.jpg', 1.13), + SearchData(100, 246537, 'Bronze jug', 'jug|bronze|bronzes', 'gr/mobile-large/DP20308.jpg', 0.96), + SearchData(17, 250410, 'Terracotta vase fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP107058.jpg', 1.00), + SearchData(100, 256648, 'Bronze statuette of Mercury', 'statuette of hermes|bronze|bronzes', 'gr/mobile-large/DP133624.jpg', 0.75), + SearchData(-5, 248899, 'Ten marble fragments of the Great Eleusinian Relief', 'relief fragments from the great eleusinian relief|marble|stone sculpture', 'gr/mobile-large/DT203390.jpg', 0.69), + SearchData(150, 248992, 'Glass spoon', 'spoon|glass|glass', 'gr/mobile-large/DP107040.jpg', 1.00), + SearchData(20, 257640, 'Marble statue of a member of the imperial family', 'statue of a young man, half-draped|marble|stone sculpture', 'gr/mobile-large/DP108190.jpg', 1.00), + SearchData(50, 257625, 'Gold pin with obsidian finial', 'pin with obsidian finial|gold and obsidian|gold and silver', 'gr/mobile-large/DP-14287-026.jpg', 2.33), + SearchData(65, 245397, 'Glass gladiator cup', 'gladiator cup|glass|glass', 'gr/mobile-large/DP104755.jpg', 1.00), + SearchData(100, 250470, 'Terracotta bowl fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP229107.jpg', 1.33), + SearchData(100, 256832, 'Bronze jug handle', 'handle|bronze, silver|bronzes', 'gr/mobile-large/DP2075.jpg', 1.00), + SearchData(249, 245333, 'Glass bottle', 'bottle|glass|glass', 'gr/mobile-large/DP107634.jpg', 1.00), + SearchData(50, 242041, 'Limestone funerary monument of a woman', 'statuette of a woman and maid holding casket|limestone|stone sculpture', 'gr/mobile-large/DP274535.jpg', 0.75), + SearchData(217, 249160, 'Marble sarcophagus fragment: female figure, perhaps a nereid', 'sarcophagus fragment, nereid|marble|stone sculpture', 'gr/mobile-large/77985.jpg', 0.64), + SearchData(50, 245672, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP120997.jpg', 1.00), + SearchData(149, 245829, 'Marble bust of Herodotos', 'portrait bust of herodotos|marble, island ?|stone sculpture', 'gr/mobile-large/DP328506.jpg', 0.75), + SearchData(400, 256714, 'Glass bottle with handles', 'bottle, handled|glass|glass', 'gr/mobile-large/DP107029.jpg', 1.00), + SearchData(50, 241674, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-265.jpg', 1.05), + SearchData(150, 257882, 'Marble head of Demosthenes', 'head of demosthenes|marble|stone sculpture', 'gr/mobile-large/DP326692.jpg', 0.75), + SearchData(149, 250953, 'Marble portrait of Marciana, sister of the emperor Trajan', 'portrait head of marciana, sister of the emperor trajan|marble|stone sculpture', 'gr/mobile-large/DP328522.jpg', 0.75), + SearchData(349, 256722, 'Glass bottle with three feet', 'bottle with three feet|glass|glass', 'gr/mobile-large/DP108379.jpg', 1.00), + SearchData(350, 245402, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP117589.jpg', 1.00), + SearchData(50, 250493, 'Obsidian revetment slab fragment', 'revetment slab fragment|obsidian|glass', 'gr/mobile-large/DP105520.jpg', 1.00), + SearchData(0, 256777, 'Limestone ossuary with lid', 'ossuary with lid|limestone, paint|stone sculpture', 'gr/mobile-large/DP-15733-005.jpg', 1.38), + SearchData(349, 245392, 'Glass bowl with cut decoration', 'bowl|glass|glass', 'gr/mobile-large/DP107006.jpg', 1.00), + SearchData(50, 246806, 'Gold ear pick', 'ear pick or ligula|gold|gold and silver', 'gr/mobile-large/DP-14287-024.jpg', 2.53), + SearchData(249, 257863, 'Silver spoon and fork', 'spoon and fork|silver|gold and silver', 'gr/mobile-large/DP223161.jpg', 0.47), + SearchData(25, 250901, 'Marble fragment of a pilaster', 'pilaster fragment|marble|stone sculpture', 'gr/mobile-large/DP271386.jpg', 0.82), + SearchData(100, 245305, 'Glass finger ring', 'ring|glass|glass', 'gr/mobile-large/DP121091.jpg', 0.75), + SearchData(199, 468220, 'Funnel-Shaped Mount', 'finial mount|champlevé enamel, bronze|enamels-champlevé', 'md/mobile-large/DP102096.JPG', 1.03), + SearchData(0, 248267, 'Glass cameo plaque fragment', 'cameo plaque fragment, head|glass paste|glass', 'gr/mobile-large/DP140695.jpg', 0.75), + SearchData(-50, 250916, 'Silver spouted pitcher', 'pitcher with spout|silver|gold and silver', 'gr/mobile-large/GR873.jpg', 1.08), + SearchData(174, 253565, 'Mosaic floor panel', 'mosaic floor with the head of spring|stone, tile, and glass|miscellaneous-mosaic', 'gr/mobile-large/DP140137.jpg', 0.91), + SearchData(25, 250088, 'Terracotta bowl', 'cup with base|terracotta|vases', 'gr/mobile-large/DP107071.jpg', 1.00), + SearchData(199, 250097, 'Terracotta jar with barbotine decoration', 'urn|terracotta|vases', 'gr/mobile-large/DP121125.jpg', 1.00), + SearchData(60, 245686, 'Glass cinerary urn with lid', 'urn with two handles and lid|glass|glass', 'gr/mobile-large/DP-15454-005.jpg', 0.75), + SearchData(249, 245322, 'Glass jug with chain handle', 'jug with chain handle|glass|glass', 'gr/mobile-large/DP107032.jpg', 1.00), + SearchData(299, 249633, 'Glass mosaic fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP140698.jpg', 0.75), + SearchData(249, 250110, 'Lead-glazed stemmed cup', 'cup|terracotta|vases', 'gr/mobile-large/DP107683.jpg', 1.00), + SearchData(162, 239665, 'Glass pourer flask', 'pourer flask|glass|glass', 'gr/mobile-large/DP233498.jpg', 1.33), + SearchData(149, 248579, 'Marble head of an athlete', 'head of an athlete|marble|stone sculpture', 'gr/mobile-large/DP-14287-137retry.jpg', 0.81), + SearchData(50, 250593, 'Bronze spoon', 'spoon|bronze|bronzes', 'gr/mobile-large/DP20485.jpg', 1.43), + SearchData(-45, 247009, 'Wall painting from Room H of the Villa of P. Fannius Synistor at Boscoreale', 'wall painting|fresco|miscellaneous-stone', 'gr/mobile-large/DP105943.jpg', 0.99), + SearchData(100, 250568, 'Bronze tweezers', 'tweezers|bronze|bronzes', 'gr/mobile-large/DP20480.jpg', 1.20), + SearchData(199, 245394, 'Glass bowl with cut decoration', 'bowl|glass|glass', 'gr/mobile-large/DP117538.jpg', 1.00), + SearchData(13, 241622, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-251.jpg', 0.83), + SearchData(325, 245375, 'Glass double head-shaped flask', 'flask, double head-shaped with handle|glass|glass', 'gr/mobile-large/DP107005.jpg', 1.00), + SearchData(-5, 250942, 'Wall painting on red ground: candelabrum with frieze, from the imperial villa at Boscotrecase', 'wall painting on red ground: candelabrum with frieze|fresco|miscellaneous-paintings', 'gr/mobile-large/DP138759.jpg', 0.70), + SearchData(50, 250355, 'Terracotta lamp handle', 'lamp handle|terracotta|terracottas', 'gr/mobile-large/DP229104.jpg', 1.33), + SearchData(249, 245718, 'Glass bowl', 'bowl|glass|glass', 'gr/mobile-large/DP107008.jpg', 1.00), + SearchData(25, 257879, 'Glass two-handled bottle (amphora)', 'amphora, pointed|glass, purple|glass', 'gr/mobile-large/DP281253.jpg', 0.75), + SearchData(50, 255092, 'Bronze phallic amulet', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP-12765-001.jpg', 1.33), + SearchData(174, 257818, 'Marble strigilated vase with snake handles', 'strigilated vase with snake handles and lid|marble|stone sculpture', 'gr/mobile-large/DP146528.jpg', 1.15), + SearchData(22, 250775, 'Terracotta modiolus (drinking cup)', 'cup, "modiolus"|terracotta|vases', 'gr/mobile-large/DP1411.jpg', 1.00), + SearchData(50, 245382, 'Glass lentoid amphoriskos', 'amphoriskos, lentoid with rosette|glass|glass', 'gr/mobile-large/DP231281.jpg', 0.75), + SearchData(50, 255091, 'Bronze phallic ornament', 'amulet, grotesque ornament|bronze|bronzes', 'gr/mobile-large/DP20804.jpg', 1.00), + SearchData(100, 246903, 'Bronze aryballos', 'bottle|bronze|bronzes', 'gr/mobile-large/DP20370.jpg', 0.99), + SearchData(-1, 250325, 'Terracotta bowl fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP121115.jpg', 1.00), + SearchData(124, 245624, 'Glass cinerary urn', 'urn with two handles|glass|glass', 'gr/mobile-large/DP121449.jpg', 1.00), + SearchData(41, 245848, 'Wall painting fragment with winged figure', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112921.jpg', 1.00), + SearchData(25, 245376, 'Glass hexagonal amphoriskos', 'amphoriskos, ennion|glass|glass', 'gr/mobile-large/DP124005.jpg', 0.75), + SearchData(199, 245377, 'Glass flask decorated with intersecting circles', 'flask|glass|glass', 'gr/mobile-large/DP107638.jpg', 1.00), + SearchData(25, 245470, 'Glass mosaic perfume bottle', 'perfume bottle, mosaic|glass|glass', 'gr/mobile-large/DP141522.jpg', 1.00), + SearchData(349, 249642, 'Glass bead', 'bead|glass|glass', 'gr/mobile-large/DP121021.jpg', 0.75), + SearchData(200, 475394, 'Zoomorphic Brooch', 'brooch|copper alloy, champlevé enamel|enamels-champlevé', 'md/mobile-large/tr777-2002s1.jpg', 1.33), + SearchData(299, 465324, 'Spoon with a Panther', 'spoon|copper alloy, silvered, niello|metalwork-copper alloy', 'md/mobile-large/sf17-192-254s1.jpg', 1.33), + SearchData(312, 468268, 'Sarcophagus with a Greek Physician', 'sarcophagus|marble|sculpture-stone', 'md/mobile-large/DT11898.jpg', 3.38), + SearchData(40, 253324, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112914.jpg', 1.00), + SearchData(34, 249627, 'Glass and gold inlay', 'mosaic block inlay|glass and gold|glass', 'gr/mobile-large/sf17194391color.jpg', 1.09), + SearchData(50, 245656, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP141524.jpg', 1.00), + SearchData(25, 257610, 'Marble cinerary urn', 'cinerary urn|marble|stone sculpture', 'gr/mobile-large/DT5415.jpg', 1.25), + SearchData(125, 250463, 'Terracotta lead-glazed lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP106598.jpg', 1.00), + SearchData(40, 253333, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP114267.jpg', 1.00), + SearchData(0, 251457, 'Terracotta dish', 'plate|terracotta|vases', 'gr/mobile-large/DP107324.jpg', 1.00), + SearchData(40, 253331, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112913.jpg', 1.00), + SearchData(12, 257828, 'Glass mosaic bowl fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP145726.jpg', 1.33), + SearchData(134, 257825, 'Marble portrait head of Antinoos', 'portrait head of antinoos|marble|stone sculpture', 'gr/mobile-large/DP333683.jpg', 0.75), + SearchData(12, 257826, 'Mosaic glass bowl fragment', 'mosaic glass fragment|glass|glass', 'gr/mobile-large/DP145724.jpg', 1.33), + SearchData(149, 246912, 'Bronze handle attachment', 'handle attachments for a situla|bronze|bronzes', 'gr/mobile-large/DP20379.jpg', 0.95), + SearchData(25, 251473, 'Marble relief fragment with scenes from the Trojan War', 'relief fragment with scenes from the siege of troy, tabula iliaca|marble, palombino|stone sculpture', 'gr/mobile-large/DP202060.jpg', 1.33), + SearchData(50, 250913, 'Marble statue of Aphrodite loosening her sandal', 'statuette of aphrodite|marble, parian ?|stone sculpture', 'gr/mobile-large/DP271463.jpg', 0.73), + SearchData(41, 245838, 'Wall painting fragment with Gorgon mask', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112917.jpg', 1.00), + SearchData(100, 253373, 'Marble statue of a wounded Amazon', 'statue of a wounded amazon|marble|stone sculpture', 'gr/mobile-large/DP277772.jpg', 0.65), + SearchData(50, 250591, 'Bronze knife with ram\'s-head handle', 'knife with ram\'s head|bronze|bronzes', 'gr/mobile-large/DP20479.jpg', 0.96), + SearchData(0, 253387, 'Bronze furniture attachment', 'furniture attachment, rinceau of acanthus|bronze|bronzes', 'gr/mobile-large/DP106985.jpg', 1.00), + SearchData(299, 246102, 'Gold ornament with cloisons', 'bead ornament|gold|gold and silver', 'gr/mobile-large/DP136023.jpg', 1.33), + SearchData(159, 251929, 'Marble statue of a wounded warrior', 'statue of a wounded warrior, protesilaos ?|marble|stone sculpture', 'gr/mobile-large/DT278.jpg', 0.65), + SearchData(299, 249668, 'Glass pendant shaped like a jar', 'pendant|glass|glass', 'gr/mobile-large/DP121027.jpg', 0.75), + SearchData(50, 257613, 'Marble mask of Pan', 'mask of pan|marble|stone sculpture', 'gr/mobile-large/GR891.jpg', 0.83), + SearchData(110, 241625, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-22527-115.jpg', 0.84), + SearchData(349, 245342, 'Glass jug', 'jug|glass|glass', 'gr/mobile-large/DP107003.jpg', 1.00), + SearchData(299, 245396, 'Glass bowl', 'bowl|glass|glass', 'gr/mobile-large/DP107007.jpg', 1.00), + SearchData(50, 245810, 'Glass mosaic ornament in the form of a shell', 'ornament in the form of a shell|glass|glass', 'gr/mobile-large/DP121095.jpg', 0.75), + SearchData(100, 250645, 'Marble relief fragment', 'relief fragment with feline among acanthus leaves|marble, palombino|stone sculpture', 'gr/mobile-large/SF17230118b.jpg', 1.21), + SearchData(399, 256709, 'Glass jug with indented body', 'jug|glass|glass', 'gr/mobile-large/DP108373.jpg', 1.00), + SearchData(25, 245727, 'Glass mosaic bottle', 'perfume bottle, mosaic|glass|glass', 'gr/mobile-large/DP120999.jpg', 1.00), + SearchData(125, 250477, 'Terracotta jug fragment', 'vase fragment|terracotta|vases', 'gr/mobile-large/DP121122.jpg', 1.00), + SearchData(0, 250153, 'Faience statuette fragment of Venus', 'statuette fragment of venus|faience|miscellaneous-faience', 'gr/mobile-large/DP231268.jpg', 0.75), + SearchData(649, 241680, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP105624.jpg', 1.00), + SearchData(375, 465927, 'Bowl Base', 'bowl fragment|glass, gold leaf|glass-gold glass', 'md/mobile-large/sf18-145-6s1b.jpg', 1.00), + SearchData(-8, 250252, 'Terracotta cup', 'cup|terracotta|vases', 'gr/mobile-large/DP107077.jpg', 1.00), + SearchData(0, 245596, 'Glass mosaic inlay fragment', 'fragment, mosaic inlay|glass|glass', 'gr/mobile-large/DP21836edited.jpg', 1.00), + SearchData(34, 248892, 'Bronze portrait of a man', 'portrait bust of a man (agrippa / marcus antonius?)|bronze|bronzes', 'gr/mobile-large/DP337268.jpg', 0.75), + SearchData(250, 465919, 'Fragment of a Sarcophagus with a Seated Figure', 'relief fragment|marble|sculpture-stone', 'md/mobile-large/DP-14287-046.jpg', 0.85), + SearchData(50, 241652, 'Terracotta oil lamp', 'lamp|terracotta|terracottas', 'gr/mobile-large/DP-19673-099.jpg', 0.90), + SearchData(349, 246975, 'Bronze lamp', 'lamp|bronze|bronzes', 'gr/mobile-large/DP20380.jpg', 0.96), + SearchData(153, 258543, 'Stone head of a Julio-Claudian youth, possibly of Gaius Caesar', 'head, gaius caesar|gypsum alabaster|stone sculpture', 'gr/mobile-large/DP333705.jpg', 0.75), + SearchData(299, 246764, 'Bronze ring key', 'key|bronze|bronzes', 'gr/mobile-large/DP20344.jpg', 0.93), + SearchData(-6, 257862, 'Bronze ornament in the form of a seated male sphinx', 'ornament in the form of a seated male sphinx|bronze|bronzes', 'gr/mobile-large/DP223155.jpg', 0.75), + SearchData(25, 250176, 'Fragmentary terracotta scyphus (drinking cup)', 'skyphos fragment|terracotta|vases', 'gr/mobile-large/DP107685.jpg', 1.00), + SearchData(187, 250087, 'Terracotta dish with barbotine decoration', 'bowl|terracotta|vases', 'gr/mobile-large/DP231272.jpg', 1.33), + SearchData(61, 245831, 'Stucco relief panel', 'stucco relief|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-076.jpg', 1.00), + SearchData(74, 245384, 'Glass amphoriskos with horizontal ribs', 'miniature transport amphora|glass|glass', 'gr/mobile-large/DP146601.jpg', 0.75), + SearchData(0, 250111, 'Terracotta askos (flask with a spout and handle over the top)', 'askos|terracotta|vases', 'gr/mobile-large/DP1445_17.194.885.jpg', 1.00), + SearchData(17, 249158, 'Glass cameo cup fragment', 'cameo, cup fragment|glass|glass', 'gr/mobile-large/DP155139.jpg', 1.00), + SearchData(162, 250926, 'Marble sarcophagus fragment', 'sarcophagus fragment|marble, luni and pentelic|stone sculpture', 'gr/mobile-large/DP310512.jpg', 1.23), + SearchData(50, 245391, 'Glass ribbed bowl', 'bowl, rippenschale|glass|glass', 'gr/mobile-large/DP141521.jpg', 1.00), + SearchData(150, 251418, 'Fragment from a marble head of a man, preserving the nose and mouth', 'head, fragment, man\'s nose and mouth|marble|stone sculpture', 'gr/mobile-large/DP202799.jpg', 1.00), + SearchData(249, 250085, 'Terracotta jug in the shape of a head of a Black African', 'jug in the shape of a head of an african|terracotta|vases', 'gr/mobile-large/DP121790.jpg', 1.00), + SearchData(75, 245380, 'Glass jug in the form of a pine cone', 'flask in the form of a pine cone|glass|glass', 'gr/mobile-large/DP117536.jpg', 1.00), + SearchData(-50, 250914, 'Silver skyphos (drinking cup)', 'skyphos|silver|gold and silver', 'gr/mobile-large/GR875.jpg', 1.08), + SearchData(74, 245832, 'Stucco relief panel', 'stucco relief of maenad|stucco|miscellaneous-stucco', 'gr/mobile-large/DP-14287-075.jpg', 0.85), + SearchData(249, 246842, 'Bead ornaments, triangular, 8', 'bead ornaments, triangular, 8|gold|gold and silver', 'gr/mobile-large/DP30699.jpg', 0.93), + SearchData(149, 465206, 'Brooch in the Form of a Dog Attacking a Boar', 'brooch|champlevé enamel, bronze, gold|enamels-champlevé', 'md/mobile-large/sf17-192-148s1.jpg', 1.49), + SearchData(149, 245585, 'Marble sarcophagus with garlands and the myth of Theseus and Ariadne', 'sarcophagus, garland|marble, luni and pentelic|stone sculpture', 'gr/mobile-large/DP138720.jpg', 1.95), + SearchData(25, 257619, 'Marble fragment of a cinerary urn', 'cinerary urn, fragment|indurated limestone or fine-grained marble|stone sculpture', 'gr/mobile-large/DT1064.jpg', 1.25), + SearchData(149, 250236, 'Terracotta hanging lamp in the form of a comic actor', 'lamp in the form of an actor|terracotta|terracottas', 'gr/mobile-large/DP105841.jpg', 1.00), + SearchData(249, 250797, 'Silver spoon', 'spoon|silver|gold and silver', 'gr/mobile-large/DP107024.jpg', 1.00), + SearchData(25, 245665, 'Glass mosaic perfume bottle', 'perfume bottle, mosaic|glass|glass', 'gr/mobile-large/DP107099.jpg', 1.00), + SearchData(25, 245475, 'Glass mosaic bottle', 'bottle, mosaic|glass|glass', 'gr/mobile-large/DP120994.jpg', 1.00), + SearchData(25, 245723, 'Glass mosaic pyxis with lid', 'pyxis with lid|glass|glass', 'gr/mobile-large/DP104759-9111335.jpg', 0.87), + SearchData(149, 246913, 'Bronze handle attachment', 'handle attachment for a situla|bronze|bronzes', 'gr/mobile-large/DP20378.jpg', 1.01), + SearchData(40, 253329, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112916.jpg', 1.00), + SearchData(34, 249625, 'Glass mosaic inlay', 'mosaic block inlay|glass|glass', 'gr/mobile-large/sf17194389color_ed.jpg', 1.12), + SearchData(40, 253330, 'Wall painting fragment', 'wall painting fragment|fresco|miscellaneous-paintings', 'gr/mobile-large/DP112912.jpg', 1.00), + SearchData(175, 468226, 'Cup or Bowl', 'cup or bowl|champlevé enamel, bronze, silver liner and bottom|enamels-champlevé', 'md/mobile-large/sf47-100-8s1.jpg', 1.41), + SearchData(50, 250494, 'Obsidian revetment slab fragment', 'revetment slab fragment|obsidian|glass', 'gr/mobile-large/DP105519.jpg', 1.00), + SearchData(100, 251416, 'Marble leg of a table with a tiger\'s head', 'table leg|marble|stone sculpture', 'gr/mobile-large/DP232662.jpg', 0.51), + SearchData(0, 249414, 'Glass oinochoe (jug)', 'oinochoe|glass|glass', 'gr/mobile-large/DT203394.jpg', 0.80), + SearchData(249, 256724, 'Glass bottle in the shape of an animal', 'bottle in the form of an animal|glass|glass', 'gr/mobile-large/DP108401.jpg', 1.00), + SearchData(50, 253379, 'Marble portrait bust of a boy', 'portrait bust of a boy|marble|stone sculpture', 'gr/mobile-large/DP328528.jpg', 0.75), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/great_wall_search_data.dart b/lib/logic/data/wonders_data/search/great_wall_search_data.dart new file mode 100644 index 00000000..ae3b3f3a --- /dev/null +++ b/lib/logic/data/wonders_data/search/great_wall_search_data.dart @@ -0,0 +1,497 @@ +part of '../great_wall_data.dart'; + +// Search suggestions (92) +List _searchSuggestions = const ['longquan', 'bodhisattva', 'horn', 'figure', 'jingdezhen', 'figures', 'model', 'birds', 'copper', 'tomb', 'architectural', 'celadon', 'scroll', 'silver', 'ware', 'colored', 'sculpture', 'earthenware', 'sancai', 'incised', 'archaic', 'ink', 'color', 'ceramics', 'vase', 'cloisonn', 'rhinoceros', 'dragons', 'green', 'yellow', 'ivory', 'overglaze', 'vessel', 'glass', 'dragon', 'white', 'cobalt', 'cup', 'shape', 'limestone', 'brush', 'peach', 'cizhou', 'decoration', 'female', 'nephrite', 'lead', 'plate', 'red', 'gilt', 'lotus', 'bronze', 'ivories', 'jade', 'water', 'dish', 'porcelain', 'flowers', 'black', 'wood', 'bottle', 'pottery', 'paintings', 'bloom', 'painted', 'hardstone', 'gold', 'jar', 'landscape', 'bottles', 'washer', 'snuff', 'box', 'buddha', 'underglaze', 'traces', 'turquoise', 'pigment', 'inlaid', 'enamel', 'metalwork', 'brown', 'carved', 'covered', 'gilding', 'bowl', 'pair', 'enamels', 'blue', 'transparent', 'stoneware', 'glaze', ]; + +// The Great Wall (489) +List _searchData = const [ + SearchData(1449, 50716, 'Plate with Lotus', 'plate|porcelain with incised decoration under celadon glaze (zhejiang province, longquan ware)|ceramics', 'as/mobile-large/DP-14609-035.jpg', 1.33), + SearchData(1233, 42442, 'Dish', 'dish|stoneware with crackled blue glaze (guan ware)|ceramics', 'as/mobile-large/DP256953.jpg', 1.33), + SearchData(1350, 49855, 'Flask with Lotus Pond', 'flask|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342530.jpg', 0.75), + SearchData(658, 56154, 'Man with a hoe', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/DP-17390-001.jpg', 0.75), + SearchData(1850, 41208, 'Snuff bottle with demon queller Zhong Kui', 'snuff bottle|overlay glass with coral stopper|snuff bottles', 'as/mobile-large/DP319167.jpg', 0.75), + SearchData(599, 49547, 'Female attendant', 'figure|glazed earthenware with pigment|tomb pottery', 'as/mobile-large/53880.jpg', 0.66), + SearchData(1683, 48572, 'Vase with scenes from Romance of the West Chamber', 'vase|porcelain painted in cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/79_2_412_O1_sf.jpg', 1.24), + SearchData(1800, 39632, 'Seated luohan with a servant', 'jade mountain|malachite|hardstone', 'as/mobile-large/DP275508.jpg', 0.75), + SearchData(1833, 41649, 'Snuff bottle in the shape of a hot pepper', 'snuff bottle|porcelain painted with red enamel (jingdezhen ware) and glass stopper|snuff bottles', 'as/mobile-large/DP321916.jpg', 0.75), + SearchData(1716, 49854, 'Wine Pot in Shape of a Peach', 'wine pot|porcelain with raised decoration and colored glazes (jingdezhen ware)|ceramics', 'as/mobile-large/DP-13079-030.jpg', 0.84), + SearchData(1149, 50236, 'Vase in the shape of a flower', 'vase|porcelain with bluish white glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/17_175_5_TQ.jpg', 0.73), + SearchData(-100, 49435, 'Ferrule', 'ferrule|bronze inlaid with gold|metalwork', 'as/mobile-large/1985_214_33_238215.jpg', 0.32), + SearchData(-1100, 53952, 'Knife with Ram\'s Head', 'knife|bronze inlaid with turquoise|metalwork', 'as/mobile-large/DT11899.jpg', 0.38), + SearchData(1099, 42432, 'Jar with Peony Scroll', 'jar|stoneware with carved decoration under celadon glaze (yaozhou ware)|ceramics', 'as/mobile-large/DP-14609-072.jpg', 1.33), + SearchData(1335, 52074, 'Brush washer with lotus', 'brush washer|stoneware with incised decoration under a celadon glaze (longquan ware)|ceramics', 'as/mobile-large/DP326731.jpg', 1.33), + SearchData(1199, 42447, 'Vase with Dragonfish Handles', 'vase|porcelain with relief decoration under celadon glaze (longquan ware)|ceramics', 'as/mobile-large/DP350151.jpg', 0.75), + SearchData(1800, 76444, 'Brush holder with scholars in a garden', 'brush holder|bamboo|bamboo', 'as/mobile-large/DP248225.jpg', 0.75), + SearchData(1799, 41180, 'Snuff bottle', 'snuff bottle|glass|snuff bottles', 'as/mobile-large/DP319240.jpg', 0.75), + SearchData(1949, 42721, 'Rock in the form of a fantastic mountain', 'scholar\'s rock|taihu limestone; wood stand|sculpture', 'as/mobile-large/2011_575_1ab.jpg', 0.68), + SearchData(-2150, 51280, 'Notched Disk', 'disk|jade (nephrite)|jade', 'as/mobile-large/DT4433.jpg', 0.80), + SearchData(749, 49563, 'Ladle', 'ladle|silver|metalwork', 'as/mobile-large/22_79_5_183332.jpg', 1.17), + SearchData(0, 52104, 'Mat Weight in the Shape of a Doe', 'weight|bronze|metalwork', 'as/mobile-large/DT11856.jpg', 1.27), + SearchData(1700, 42202, 'Bottle with Dragon Chasing Pearl', 'bottle|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-080.jpg', 0.75), + SearchData(1838, 41145, 'Snuff bottle with gourd on a trellis', 'snuff bottle with stopper|chalcedony with jadeite stopper|snuff bottles', 'as/mobile-large/DP319165.jpg', 0.75), + SearchData(1199, 42482, 'Pillow in Shape of Reclining Woman', 'pillow|porcelain with incised and carved decoration under celadon glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/DT258463.jpg', 1.26), + SearchData(749, 49582, 'Decorative belt plaque', 'belt plaque|gilt bronze|metalwork', 'as/mobile-large/24_100_12_57008.jpg', 1.00), + SearchData(1649, 41880, 'Cup in the shape of an archaic vessel with feline dragons', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318344.jpg', 1.33), + SearchData(724, 44349, 'Jar', 'jar|earthenware with dappled yellow and green glaze|ceramics', 'as/mobile-large/1994_605_48_L36017.jpg', 0.80), + SearchData(716, 39805, 'Assemblage of pendants', 'plaques|jade (nephrite) with bronze and turquoise|jade', 'as/mobile-large/DT3804.jpg', 0.52), + SearchData(1892, 7985, 'Sugar Bowl', 'sugar bowl|porcelain|', 'ad/mobile-large/ADA5410.jpg', 0.92), + SearchData(1383, 42496, 'Bowl with Chrysanthemums', 'bowl|porcelain painted with copper red under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DT258470.jpg', 0.79), + SearchData(1849, 42228, 'Vase in Meiping Shape', 'vase|porcelain with crackled apple-green glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-061.jpg', 0.75), + SearchData(1683, 53962, 'Jar with Cover (one of a pair)', 'jar with cover|porcelain|ceramics', 'as/mobile-large/50_235_2ab_149358.jpg', 0.69), + SearchData(555, 42718, 'Bodhisattva, probably Avalokiteshvara (Guanyin)', 'figure|sandstone with pigment|sculpture', 'as/mobile-large/DP213356.jpg', 0.75), + SearchData(574, 44796, 'Warrior with Shield', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/62773.jpg', 0.66), + SearchData(1199, 52647, 'Water dropper', 'water dropper|stoneware with splashed blue glaze (jun ware)|ceramics', 'as/mobile-large/DP-14609-134.jpg', 1.33), + SearchData(1450, 42513, 'Altar Bowl with Winged Animals among Waves', 'bowl|porcelain painted with cobalt blue under and red enamel over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-041.jpg', 1.33), + SearchData(-351, 44398, 'Spearhead', 'spearhead|bronze|metalwork', 'as/mobile-large/1994_605_98_L35960.jpg', 0.72), + SearchData(1622, 76751, 'Plate with vase of flowers', 'plate|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14608-064.jpg', 1.33), + SearchData(1300, 51249, 'Service with Decoration of Flowers and Birds', 'service|silver with gilding|metalwork', 'as/mobile-large/DT211.jpg', 1.43), + SearchData(1700, 42208, 'Vase', 'vase|porcelain with black glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-099.jpg', 0.75), + SearchData(1694, 52828, 'Dish with Phoenixes', 'dish|porcelain painted with cobalt blue under and colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-031.jpg', 1.33), + SearchData(1902, 40018, 'Ink Tablet with Thousand-Year Fungus Motif', 'ink tablet|pine soot and binding medium|ink', 'as/mobile-large/30_76_196_sf.jpg', 1.29), + SearchData(1808, 781822, 'Brush holder with flowers and poems', 'brush holder|molded gourd, black lacquer|gourd', 'as/mobile-large/DP-16215-075.jpg', 0.75), + SearchData(1299, 42445, 'Tea Bowl with Crescent Moon, Clouds, and Blossoming Plum', 'bowl|stoneware with black and brown glaze and pigment (jizhou ware)|ceramics', 'as/mobile-large/DP342543.jpg', 1.33), + SearchData(-100, 44718, 'Weight in the Form of a Feline', 'weight|bronze inlaid with gold and silver|metalwork', 'as/mobile-large/25532.jpg', 1.23), + SearchData(1099, 42429, 'Bowl with Two Boys among Foliage', 'bowl|stoneware with mold-impressed decoration under celadon glaze (yaozhou ware)|ceramics', 'as/mobile-large/DP342503.jpg', 1.33), + SearchData(999, 61780, 'Pair of decorative medallions', 'architectural detail|sandstone|sculpture', 'as/mobile-large/1992_165_26a.JPG', 1.01), + SearchData(1888, 15980, 'Vase', 'vase|glazed and painted earthenware|', 'ad/mobile-large/DP235551.jpg', 0.82), + SearchData(-250, 54001, 'Scabbard Slide', 'scabbard slide|agate|hardstone', 'as/mobile-large/1985_214_104.JPG', 0.56), + SearchData(-100, 44790, 'Mat Weight in the form of a Bear and Tiger in Combat', 'weight|gilt bronze|metalwork', 'as/mobile-large/38391.jpg', 1.65), + SearchData(1724, 61430, 'Plate with Dragon and Waves', 'plate|porcelain painted with colored enamels over transparent glaze and gilding (jingdezhen ware)|ceramics', 'as/mobile-large/DP343065.jpg', 1.33), + SearchData(1692, 52034, 'Covered Jar', 'covered jar|porcelain painted in famille verte enamels|ceramics', 'as/mobile-large/14_40_237.jpg', 0.58), + SearchData(1805, 43303, 'Covered box with lotus spray', 'covered box|jade (nephrite)|jade', 'as/mobile-large/183841.jpg', 1.27), + SearchData(1649, 42745, 'Couch', 'couch|wood (huanghuali, or dalbergia odorifera)|furniture', 'as/mobile-large/260149.jpg', 1.60), + SearchData(1849, 653049, 'Boys with leaves and boxes', 'figure|horn|horn', 'as/mobile-large/DP321122.jpg', 1.33), + SearchData(1749, 41888, 'Cup in the shape of a magnolia blossom', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318349.jpg', 0.75), + SearchData(1700, 42248, 'Vase in Meiping Shape', 'vase|porcelain with ox-blood glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-152.jpg', 0.75), + SearchData(749, 49591, 'Mirror Back with Birds and Animals in Repoussé', 'mirror back|silver|mirrors', 'as/mobile-large/1985_214_22_sf.JPG', 0.99), + SearchData(550, 42709, 'Buddha', 'stele fragment|limestone with traces of pigment|sculpture', 'as/mobile-large/DP170196.jpg', 0.75), + SearchData(1099, 42461, 'Basin with Lotus Decoration', 'basin|porcelain with carved and incised decoration under ivory glaze (ding ware)|ceramics', 'as/mobile-large/DP342661.jpg', 1.33), + SearchData(1099, 52595, 'Bowl with “Hare’s Fur” Decoration', 'bowl|stoneware with iron-oxide glaze (jian ware); japanese lacquer repair|ceramics', 'as/mobile-large/DP275578.jpg', 1.33), + SearchData(1600, 42544, 'Plate with Geese in a Lotus Pond', 'plate|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342691.jpg', 1.33), + SearchData(1199, 52663, 'Bowl (one of a pair)', 'bowl|stoneware with splashed glaze (jun ware)|ceramics', 'as/mobile-large/DP-14609-154.jpg', 1.33), + SearchData(1749, 39527, 'Gourd-Shaped Bottle (one of a pair)', 'bottle|porcelain with celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-119.jpg', 0.75), + SearchData(1849, 49840, 'Ewer in the shape of a peach', 'ewer|porcelain with colored glazes (jingdezhen ware)|ceramics', 'as/mobile-large/DP307601.jpg', 1.33), + SearchData(1892, 2737, 'Creamer', 'cream pot|porcelain|', 'ad/mobile-large/ADA5412.jpg', 0.97), + SearchData(-1000, 49405, 'Dagger-Axe', 'dagger-axe|bronze|metalwork', 'as/mobile-large/1985_214_25_238265.jpg', 2.63), + SearchData(1805, 43960, 'Paperweight in the shape of a bitter melon', 'paperweight|jade (jadeite)|jade', 'as/mobile-large/02_18_655_O2.jpg', 1.17), + SearchData(-2500, 44724, 'Jar (Guan)', 'jar|earthenware with pigment|ceramics', 'as/mobile-large/268185.jpg', 0.84), + SearchData(1700, 42258, 'Plate with Basket of Auspicious Flowers', 'plate|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-020.jpg', 1.33), + SearchData(1799, 53120, 'Rock in the Form of a Fantastic Mountain', 'scholar\'s rock|black lingbi limestone; wood stand|sculpture', 'as/mobile-large/L37147_2011_575_3ab.jpg', 1.10), + SearchData(122, 44321, 'Wellhead with Bucket', 'architectural model|earthenware with green lead glaze|ceramics', 'as/mobile-large/1994_605_20_L35679.jpg', 0.80), + SearchData(1749, 739055, 'Brush washer in the shape of a peach', 'brush washer|overlay glass|glass', 'as/mobile-large/DP-15664-001.jpg', 1.33), + SearchData(1716, 816473, 'Plate with landscape of the West Lake', 'plate|porcelain with powder blue glaze and gold decoration (jingdezhen ware)|ceramics', 'as/mobile-large/DP-17392-001.jpg', 1.33), + SearchData(707, 61549, 'Buddha', 'figure|marble|sculpture', 'as/mobile-large/DP163984.jpg', 0.75), + SearchData(290, 42335, 'Candle Stand in the Shape of a Fantastic Animal', 'candle stand|stoneware with green glaze|ceramics', 'as/mobile-large/1990_291_1.jpg', 1.32), + SearchData(1729, 52022, 'Vase', 'vase|porcelain|ceramics', 'as/mobile-large/20878.jpg', 0.75), + SearchData(1583, 72729, 'Dish with Three Friends of Winter', 'dish|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-063.jpg', 1.33), + SearchData(1733, 73383, 'Buddhist Deity, Ushnishavijaya (Zun Sheng fo mu)', 'figure|gilt brass; lost-wax cast|sculpture', 'as/mobile-large/DP146806.jpg', 0.75), + SearchData(117, 44326, 'Square Duck Pond', 'architectural model|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/1994_605_25_side.JPG', 1.80), + SearchData(1672, 43209, 'Cup with dragon handles', 'cup|jade (nephrite)|jade', 'as/mobile-large/49179.jpg', 1.42), + SearchData(-1150, 49372, 'Ceremonial Ax', 'ceremonial ax|jade (nephrite)|jade', 'as/mobile-large/36904.jpg', 0.74), + SearchData(699, 49564, 'Stem cup', 'stem cup|silver|metalwork', 'as/mobile-large/23_226_1_54396.jpg', 1.17), + SearchData(1319, 42716, 'Buddha of Medicine Bhaishajyaguru (Yaoshi fo)', 'wall painting|water-based pigment over foundation of clay mixed with straw|paintings', 'as/mobile-large/DT227320.jpg', 1.26), + SearchData(1783, 42243, 'Vase with Cranes, Clouds, and Waves', 'vase|porcelain with incised decoration under amber glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342655.jpg', 0.75), + SearchData(1692, 52049, 'Vase', 'vase|porcelain with peach bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP307773.jpg', 0.75), + SearchData(1765, 51041, 'Bottle vase', 'bottle vase|porcelain with incised decoration (anhua) under a clair de lune glaze (jingdezhen ware)|ceramics', 'as/mobile-large/33_40_77_O1_sf.jpg', 0.65), + SearchData(1849, 60660, 'Box in the shape of a “Buddha’s hand” citron', 'box|amber|amber', 'as/mobile-large/DP324382.jpg', 0.75), + SearchData(117, 44320, 'Mill', 'architectural model|earthenware with green lead glaze|ceramics', 'as/mobile-large/1994_605_19_L35678.jpg', 1.25), + SearchData(1622, 739053, 'Octagonal cup with dragon handles', 'cup|gilt bronze|metalwork', 'as/mobile-large/DP-14002-001.jpg', 1.33), + SearchData(1135, 42720, 'Monk Sengqie', 'figure|limestone with pigment|sculpture', 'as/mobile-large/DP213354.jpg', 0.75), + SearchData(1805, 43273, 'Dish with grapes and squirrels', 'dish|jade (jadeite)|jade', 'as/mobile-large/DP321101.jpg', 1.33), + SearchData(1716, 42255, 'Vase with romantic scenes', 'vase|porcelain covered with powdered blue glaze, painted with colored enamels over transparent glaze, and painted with gold (jingdezhen ware)|ceramics', 'as/mobile-large/DP200408.jpg', 0.75), + SearchData(1649, 39645, 'Incense burner with animal-mask handles', 'incense burner|bronze with gold splashes|metalwork', 'as/mobile-large/29_100_550_tms.JPG', 1.07), + SearchData(-1100, 49404, 'Axe', 'axe|bronze|metalwork', 'as/mobile-large/DP219129.jpg', 0.75), + SearchData(1199, 52601, 'Tea Bowl with “Hare’s-Fur” Decoration', 'bowl|stoneware with iron-oxide glaze (jian ware)|ceramics', 'as/mobile-large/DP-14610-028.jpg', 1.33), + SearchData(1692, 42416, 'Water jar with clouds', 'water jar|porcelain with raised decoration under celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP323433.jpg', 0.75), + SearchData(1200, 42433, 'Pillow with a falcon attacking a swan', 'pillow|stoneware painted with brown and black pigment on white slip under transparent glaze (cizhou ware)|ceramics', 'as/mobile-large/DP274727.jpg', 1.33), + SearchData(1316, 42483, 'Incense Burner in Shape of Lion (one of a pair)', 'incense burner|porcelain with brown and raised decoration under celadon glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/34_113_2_DT5091.jpg', 0.54), + SearchData(567, 49542, 'Standing Official', 'figure|earthenware with traces of pigment|tomb pottery', 'as/mobile-large/1985_220_1_O.JPG', 0.75), + SearchData(1319, 647271, 'Wine bottle with lotuses and admonition', 'wine bottle|stoneware with cut-glaze decoration (cizhou ware)|ceramics', 'as/mobile-large/DP703074.jpg', 0.75), + SearchData(566, 49545, 'Seated Falconer', 'one of a pair of figures|earthenware with red and white pigments|tomb pottery', 'as/mobile-large/DP702295.jpg', 0.88), + SearchData(-600, 61327, 'Spouted Water Vessel (Yi)', 'vessel|bronze|metalwork', 'as/mobile-large/DP151410.jpg', 1.33), + SearchData(1099, 58453, 'Guardian Protector of the East (Dongfang chiguo tianwang)', 'figure|partially gilt arsenical bronze; lost-wax cast|sculpture', 'as/mobile-large/DP170244.jpg', 0.75), + SearchData(1771, 1980, 'Openwork fruit basket', 'basket|soft-paste porcelain with underglaze blue decoration|', 'ad/mobile-large/DP207185.jpg', 0.79), + SearchData(1499, 42510, 'Vase in Shape of Ancient Bronze Vessel', 'vase|porcelain with celadon glaze (longquan ware)|ceramics', 'as/mobile-large/DP-14609-153.jpg', 0.75), + SearchData(-2500, 44465, 'Pitcher (Hu)', 'pitcher|earthenware|ceramics', 'as/mobile-large/1987_409_4.JPG', 0.89), + SearchData(1849, 40705, 'One of a Pair of Vases with Dragon Handles', 'vase|cloisonné enamel with gilt bronze and champlevé|cloisonné', 'as/mobile-large/29_110_48_O1.jpg', 1.40), + SearchData(1765, 52975, 'Miniature vase with floral medallions', 'vase|porcelain painted with overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/24_80_296_56924.jpg', 0.53), + SearchData(1716, 42212, 'Gourd-Shaped Bottle', 'bottle|porcelain with blue glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-063.jpg', 0.75), + SearchData(1692, 50925, 'Seal-Paste Box', 'covered box|porcelain with peach-bloom glaze|ceramics', 'as/mobile-large/76134.jpg', 1.25), + SearchData(1717, 42247, 'Water Jar with Swirling Clouds', 'jar|porcelain with incised decoration under celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-170.jpg', 1.33), + SearchData(1805, 44072, 'Vessel in the shape of a bird with archaic designs', 'vessel|rock crystal|hardstone', 'as/mobile-large/DP326720.jpg', 0.75), + SearchData(1249, 39635, 'Buddha Shakyamuni with attendant bodhisattvas', 'figures|mammoth ivory|sculpture', 'as/mobile-large/DP170123.jpg', 0.75), + SearchData(1683, 42231, 'Box for Seal Paste', 'box|porcelain with peach-bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-102.jpg', 1.33), + SearchData(1833, 42240, 'Vase in Form of Archaic Bronze', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-132.jpg', 0.75), + SearchData(1600, 39626, 'Incense holder with scholars in a landscape', 'incense holder|bamboo|bamboo', 'as/mobile-large/DP323467.jpg', 0.75), + SearchData(-300, 36444, 'Wine Container (Bianhu)', 'wine vessel|bronze inlaid with copper|metalwork', 'as/mobile-large/1992_130.JPG', 0.91), + SearchData(1688, 36131, 'Landscapes with poems', 'album|fifteen leaves from an album (1980.516.2a–c and 1981.4.1a–o) of eighteen leaves|paintings', 'as/mobile-large/1981_4_1a.jpg', 1.45), + SearchData(-2450, 44723, 'Jar (Hu)', 'jar|earthenware with painted decoration|ceramics', 'as/mobile-large/DP257874.jpg', 0.75), + SearchData(-1150, 44777, 'Ceremonial dagger-ax (Ge)', 'dagger-ax|jade (nephrite)|jade', 'as/mobile-large/58227.jpg', 1.52), + SearchData(1683, 42232, 'Vase', 'vase|porcelain with peach-bloom glaze (jingdezhen ware); tiffany stand|ceramics', 'as/mobile-large/DP-14605-118.jpg', 0.75), + SearchData(1849, 43909, 'Belt buckle with dragons', 'belt buckle|jade (jadeite)|jade', 'as/mobile-large/DP321096.jpg', 1.33), + SearchData(1550, 50263, 'Dish with Children in Garden', 'dish|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-076.jpg', 1.33), + SearchData(1600, 56355, 'Wardrobe', 'wardrobe|wood with inlay of mother-of-pearl, amber, glass, ivory, and other materials|furniture', 'as/mobile-large/DP205481.jpg', 0.75), + SearchData(1799, 41756, 'Snuff bottle', 'snuff bottle|glass|snuff bottles', 'as/mobile-large/DP321914.jpg', 0.75), + SearchData(1316, 42484, 'Incense Burner in Shape of Lion (one of a pair)', 'incense burner|porcelain with brown and raised decoration under celadon glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/34_113_3_DT5091.jpg', 0.58), + SearchData(1516, 42525, 'Bowl with Dragons', 'bowl|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/62566.jpg', 1.02), + SearchData(1749, 39859, 'Brush holder with narrative scene', 'brush holder|ivory|ivories', 'as/mobile-large/DP318656.jpg', 0.75), + SearchData(1149, 39528, 'Jar with Handles', 'jar|stoneware with applied white slip ribs and black glaze (cizhou ware)|ceramics', 'as/mobile-large/DP-14610-043.jpg', 1.33), + SearchData(1771, 13370, 'Pickle Stand', 'pickle stand|soft-paste porcelain|', 'ad/mobile-large/DT5514.jpg', 1.25), + SearchData(1366, 39623, 'Disk with dragons', 'disk|ivory|ivories', 'as/mobile-large/DP310102.jpg', 0.75), + SearchData(1583, 42526, 'Dish with Gardenia', 'dish|porcelain painted with cobalt blue under and colored enamel over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-087.jpg', 1.33), + SearchData(1683, 42405, 'Plate with Phoenix and Mythical Qilin', 'plate|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14608-051.jpg', 1.33), + SearchData(1849, 41571, 'Snuff bottle with landscape', 'snuff bottle|mother-of-pearl with stained ivory and coral stopper|snuff bottles', 'as/mobile-large/DP321887.jpg', 0.75), + SearchData(1805, 43306, 'Covered box with floral design', 'covered box|jade (nephrite)|jade', 'as/mobile-large/183831.jpg', 1.35), + SearchData(1199, 78392, 'Tea Bowl with Splashed Decoration', 'tea bowl|stoneware with brown glaze and slip decoration|ceramics', 'as/mobile-large/DP344325.jpg', 1.33), + SearchData(689, 42695, 'Epitaph with Cover for Master Xu', 'epitaph|limestone|sculpture', 'as/mobile-large/DP256627.jpg', 1.33), + SearchData(1805, 43322, 'Vase on Stand', 'incense utensil|jade (nephrite)|jade', 'as/mobile-large/49360.jpg', 0.56), + SearchData(100, 44412, 'Pig in Recumbent Position', 'figure|dolomite|sculpture', 'as/mobile-large/DP-17989-051.jpg', 1.33), + SearchData(117, 49517, 'House with Courtyard', 'model|earthenware with green lead glaze|ceramics', 'as/mobile-large/20477.jpg', 1.30), + SearchData(1549, 42742, 'Wardrobe', 'wardrobe|wood (huanghuali, or dalbergia odoriferal); metal fittings|furniture', 'as/mobile-large/1976_193_7_O1.jpg', 0.57), + SearchData(524, 42162, 'Buddha Maitreya (Mile) Altarpiece', 'altarpiece|gilt bronze|sculpture', 'as/mobile-large/DP217575.jpg', 0.75), + SearchData(716, 50973, 'Miniature covered jar and “inkstone”', 'jar|earthenware with green glaze|ceramics', 'as/mobile-large/DP297292.jpg', 0.75), + SearchData(1716, 42213, 'Vase', 'vase|porcelain with light blue glaze|ceramics', 'as/mobile-large/DP-14605-084.jpg', 0.75), + SearchData(1750, 42249, 'Vase', 'vase|porcelain with ox-blood glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-004.jpg', 0.75), + SearchData(1099, 50230, 'Vase', 'vase|stoneware with white glaze (cizhou ware)|ceramics', 'as/mobile-large/DP-14610-051.jpg', 0.75), + SearchData(199, 44419, 'Jar in Shape of Archaic Bronze Vessel (Hu)', 'jar|earthenware with incised decoration under amber glaze|ceramics', 'as/mobile-large/DP-14610-038.jpg', 0.75), + SearchData(1561, 42532, 'Dish with Flowers and Birds', 'dish|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-009.jpg', 1.33), + SearchData(1849, 52825, 'Ewer in the shape of a peach', 'ewer|porcelain painted with underglaze copper red and cobalt blue glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP323436.jpg', 1.33), + SearchData(1649, 42547, 'Bodhidharma in meditation', 'figure|porcelain with ivory glaze (dehua ware)|ceramics', 'as/mobile-large/DP170200.jpg', 0.75), + SearchData(1692, 52872, 'Set of wine cups with flowers of the twelve months', 'cups|porcelain painted with underglaze cobalt blue and overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/DP325003.jpg', 1.33), + SearchData(1600, 42542, 'Box with Figures in Landscape', 'box|porcelain painted with cobalt blue under and colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-108.jpg', 1.33), + SearchData(1683, 42230, 'Brush Washer', 'brush washer|porcelain with peach-bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-124.jpg', 1.33), + SearchData(600, 76733, 'Bottle', 'bottle|stoneware with incised decoration under celadon glaze|ceramics', 'as/mobile-large/DP-14609-068.jpg', 0.75), + SearchData(-98, 49530, 'Sword Blade', 'sword blade|bronze inlaid with gold and silver|metalwork', 'as/mobile-large/65_74_1_201805.jpg', 0.21), + SearchData(1805, 56173, 'Brush washer with plum blossoms and feline dragons', 'cup|chalcedony|hardstone', 'as/mobile-large/DP324366.jpg', 1.33), + SearchData(1350, 49215, 'Bottle with Peonies', 'bottle|porcelain painted with copper red under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-013.jpg', 0.75), + SearchData(1692, 50995, 'Water coupe', 'water coupe|porcelain with peach-bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/76014.jpg', 1.18), + SearchData(566, 44797, 'Tomb Guardian (Zhenmushou)', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/DP342410.jpg', 1.33), + SearchData(100, 44330, 'Food Container (Kui)', 'container|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/1994_605_29_L35691.jpg', 1.24), + SearchData(1549, 42734, 'Side Table', 'side table|wood (huanghuali, or dalbergia odorifera)|furniture', 'as/mobile-large/1976_193_5.JPG', 1.30), + SearchData(835, 39650, 'Bowl with deer', 'bowl|silver with parcel gilding|metalwork', 'as/mobile-large/1974_267_204627.jpg', 2.39), + SearchData(1892, 8411, 'Teapot', 'teapot|porcelain|', 'ad/mobile-large/DT7709.jpg', 1.25), + SearchData(749, 49513, 'Horse', 'figure|earthenware with brown glazes|tomb pottery', 'as/mobile-large/150611.jpg', 0.77), + SearchData(1683, 42227, 'Water Jar', 'jar|porcelain with incised decoration under peachbloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-105.jpg', 1.33), + SearchData(1416, 42503, 'Bowl with Peonies, Narcissus, and Pomegranates', 'bowl|porcelain painted in cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-037.jpg', 1.33), + SearchData(1805, 44108, 'Standing Buddha', 'figurine|amethystine quartz|hardstone', 'as/mobile-large/DP324368.jpg', 0.75), + SearchData(1716, 42211, 'Vase', 'vase|porcelain with black glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-096.jpg', 0.75), + SearchData(-2400, 72376, 'Ritual object (cong)', 'ritual object|jade (nephrite)|jade', 'as/mobile-large/DP161556.jpg', 0.75), + SearchData(1300, 42477, 'Bottle with Lotus Scroll', 'bottle|porcelain with incised decoration under celadon glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/DP342585.jpg', 0.75), + SearchData(1167, 49912, 'Pillow with Peonies', 'pillow|stoneware with sgraffito decoration (cizhou ware)|ceramics', 'as/mobile-large/DP-14610-020.jpg', 1.33), + SearchData(849, 44347, 'Circular box', 'box|earthenware with painted decoration under transparent glaze (changsha ware)|ceramics', 'as/mobile-large/1994_605_46_L35610.jpg', 1.20), + SearchData(1765, 52025, 'Water pot with dragons among clouds', 'water pot|porcelain painted in underglaze copper red (jingdezhen ware)|ceramics', 'as/mobile-large/14_40_90_O1.jpg', 1.40), + SearchData(1765, 46904, 'Saucer with Butterflies', 'saucer|porcelain painted with colored enamels and gilded (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-048.jpg', 1.33), + SearchData(1099, 36085, 'Bodhisattva', 'statue|wood (foxglove) with traces of pigment and gilding; single woodblock construction|sculpture', 'as/mobile-large/DP164027.jpg', 0.75), + SearchData(708, 42378, 'Covered jar', 'covered jar|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/1991_253_6.jpg', 0.83), + SearchData(1816, 51172, 'Bowl with floral medallions', 'bowl|porcelain with underglaze cobalt blue and overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/79_2_1033_S2_sf.jpg', 1.33), + SearchData(1749, 42205, 'Vase with Basket of Auspicious Flowers', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-030.jpg', 0.75), + SearchData(710, 39640, 'Head of a Bodhisattva', 'head|sandstone with pigment|sculpture', 'as/mobile-large/DP170147.jpg', 0.75), + SearchData(-400, 49408, 'Halberd', 'halberd|bronze inlaid with copper, turquoise, and tenorite|metalwork', 'as/mobile-large/1985_214_29_238258.jpg', 1.65), + SearchData(716, 53958, 'Pillow', 'pillow|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/50_221_14.JPG', 1.14), + SearchData(1450, 75831, 'Jar with Winged Animals over Waves', 'jar|porcelain with cobalt blue under a transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342694.jpg', 0.75), + SearchData(1765, 50750, 'Bowl with bitter melons and butterflies', 'bowl|porcelain painted with overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/182560.jpg', 1.33), + SearchData(700, 64898, 'Pillow', 'pillow|earthenware with marbled veneer and brown glaze|ceramics', 'as/mobile-large/DP158762.jpg', 1.33), + SearchData(1199, 73172, 'Tea Bowl with Marbleized Veneer', 'tea bowl|earthenware with glaze (cizhou ware)|ceramics', 'as/mobile-large/DP158779.jpg', 1.33), + SearchData(1700, 75262, 'Demon queller Zhong Kui with demons', 'scuplture|bamboo|bamboo', 'as/mobile-large/DP227777.jpg', 1.33), + SearchData(1717, 42229, 'Vase', 'vase|porcelain with peach-bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP246426.jpg', 0.75), + SearchData(658, 56152, 'Woman with a hoe', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/DP-17391-001.jpg', 0.75), + SearchData(1729, 653045, 'Pair of vases with decoration of dragons among clouds', 'vase|porcelain with incised decoration (anhua) under a transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP307599.jpg', 0.75), + SearchData(653, 54078, 'Female attendant', 'one of a pair of figures|earthenware with pigment|tomb pottery', 'as/mobile-large/DP702322.jpg', 0.72), + SearchData(1549, 39639, 'Wenchang, Stellar God of Literature', 'figure|ivory|ivories', 'as/mobile-large/DP318649.jpg', 0.75), + SearchData(1849, 44001, 'Belt hook with dragons', 'belt hook|jade (jadeite)|jade', 'as/mobile-large/DP321117.jpg', 0.75), + SearchData(1692, 76453, 'Brush holder with immortal realms', 'brush holder|bamboo|bamboo', 'as/mobile-large/DP323478.jpg', 0.75), + SearchData(1650, 52798, 'Jar with Mythical Qilin', 'jar|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14608-016.jpg', 0.75), + SearchData(749, 49514, 'Groom', 'figure|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/2000_662_8_L35766.JPG', 0.67), + SearchData(749, 49566, 'Box', 'box|silver with gilding|metalwork', 'as/mobile-large/1974_268_15ab.JPG', 2.14), + SearchData(1700, 42200, 'Vase with Butterflies', 'vase|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-075.jpg', 0.75), + SearchData(1583, 42210, 'Pouring Vessel (Kendi) with Flowers and Fruits', 'kendi|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-007.jpg', 0.75), + SearchData(949, 42449, 'Jar', 'jar|porcelain with incised decoration under transparent glaze (xing ware)|ceramics', 'as/mobile-large/DP-14610-030.jpg', 1.33), + SearchData(674, 49548, 'Resting dancer', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/102458.jpg', 0.74), + SearchData(-950, 49499, 'Axe', 'axe|bronze|metalwork', 'as/mobile-large/1985_214_30_238213.jpg', 1.31), + SearchData(1749, 42209, 'Moon-Shaped Bottle', 'bottle|soft-paste porcelain with incised decoration under ivory glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-010.jpg', 0.75), + SearchData(750, 39901, 'Night-Shining White', 'handscroll|handscroll; ink on paper|paintings', 'as/mobile-large/DP153705.jpg', 1.33), + SearchData(1902, 41777, 'Ink Tablet with Black Stag Motif', 'ink tablet|black ink|ink', 'as/mobile-large/30_76_198_79233.jpg', 1.00), + SearchData(1616, 64897, 'Guardian, probably a Lokapala (Tian wang)', 'figure|gilt brass; lost-wax cast|sculpture', 'as/mobile-large/DP170251.jpg', 0.75), + SearchData(1765, 41336, 'Snuff bottle with European figures', 'snuff bottle|painted enamel on copper with gilt bronze stopper|snuff bottles', 'as/mobile-large/DP319215.jpg', 0.75), + SearchData(368, 42334, 'Handled Bowl', 'bowl|earthenware with green glaze|ceramics', 'as/mobile-large/239500.jpg', 1.34), + SearchData(1900, 69855, 'Woman\'s Robe with Peonies and Shou Medallions', 'woman\'s informal robe|tapestry-woven (kesi) silk and metallic thread|costumes-tapestries', 'as/mobile-large/137775.jpg', 0.99), + SearchData(1805, 39850, 'Boulder with three figures', 'boulder|soapstone|sculpture', 'as/mobile-large/DP324390.jpg', 0.75), + SearchData(1805, 42015, 'Scepter (ruyi) with gourds and vines', 'scepter|ivory with pigment|ivories', 'as/mobile-large/DP326740.jpg', 1.33), + SearchData(-350, 61328, 'Grain Serving Vessel (Dou)', 'grain vessel|bronze|metalwork', 'as/mobile-large/1985_214_12_238073.jpg', 1.09), + SearchData(350, 49534, 'Vessel in the Shape of a Tiger (Huzi)', 'vessel|porcelain with green glaze|ceramics', 'as/mobile-large/237219.jpg', 1.24), + SearchData(1516, 42529, 'Bowl with Lotuses', 'bowl|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-166.jpg', 1.33), + SearchData(708, 52570, 'Box', 'box|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/26_292_49ab.JPG', 1.34), + SearchData(-950, 44779, 'Tiger', 'figurine|marble|sculpture', 'as/mobile-large/DP-22992-002.jpg', 1.33), + SearchData(1700, 42254, 'Jar with Landscape Scenes', 'jar with cover|porcelain covered with powdered blue glaze, painted with colored enamels over transparent glaze, and painted with gold (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-043.jpg', 0.75), + SearchData(1133, 42463, 'Bowl', 'bowl|porcelain with black glaze (ding ware)|ceramics', 'as/mobile-large/DP-14610-036.jpg', 1.33), + SearchData(-100, 55296, 'Ferrule', 'ferrule|bronze with silver inlay|metalwork', 'as/mobile-large/268306.jpg', 0.55), + SearchData(1700, 39617, 'Vase', 'vase|copper alloy|metalwork', 'as/mobile-large/DP324380.jpg', 0.75), + SearchData(1749, 42324, 'Vase in the shape of an archaic bronze vessel (gu)', 'vase|stoneware with relief decoration (yixing ware)|ceramics', 'as/mobile-large/DP323464.jpg', 0.75), + SearchData(1865, 5777, 'Pitcher', 'pitcher|porcelain, overglaze enamel decoration, and gilding|', 'ad/mobile-large/DP235025.jpg', 0.86), + SearchData(-900, 53953, 'Yoke Bell', 'yoke bell|bronze|metalwork', 'as/mobile-large/1986_232_1_240903.jpg', 0.45), + SearchData(1805, 42126, 'Miniature vase in the shape of an ancient ritual vessel (gu)', 'vase|jade (nephrite)|jade', 'as/mobile-large/DP307597.jpg', 0.75), + SearchData(1788, 42141, 'Brush holder with immortal realms', 'brush holder|jade (nephrite)|jade', 'as/mobile-large/49305.jpg', 0.81), + SearchData(1849, 43277, 'Twin Vases', 'vases|jade (nephrite)|jade', 'as/mobile-large/183484.jpg', 0.78), + SearchData(666, 42180, 'Set of decorative belt plaques', 'belt plaques|jade (nephrite)|jade', 'as/mobile-large/DP211541.jpg', 2.76), + SearchData(117, 49521, 'Watchtower with Four Archers', 'architectural model|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/2000_662_4a-c.jpg', 0.54), + SearchData(1711, 50791, 'Brush Washer', 'brush washer|porcelain with clair de lune glazes|ceramics', 'as/mobile-large/DP-14605-167.jpg', 1.33), + SearchData(-1200, 49403, 'Axe', 'axe|bronze|metalwork', 'as/mobile-large/1976_49_207208.jpg', 0.66), + SearchData(1750, 42250, 'Vase with Flowers', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-053.jpg', 0.75), + SearchData(1749, 43264, 'Vase in the shape of a magnolia', 'vase|jade (nephrite)|jade', 'as/mobile-large/183507.jpg', 0.72), + SearchData(930, 39649, 'Bowl with Dragons among Waves', 'bowl|stoneware with carved and incised decoration under celadon glaze (yue ware)|ceramics', 'as/mobile-large/DP356175.jpg', 1.33), + SearchData(1765, 40712, 'Vase with lions', 'vase|cloisonné enamel|cloisonné', 'as/mobile-large/DP242367.jpg', 0.75), + SearchData(1649, 39857, 'Cup with two dragons in waves', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318353.jpg', 1.33), + SearchData(1099, 49202, 'Mold for a Bowl', 'mold|stoneware with carved decoration (probably yaozhou ware)|ceramics', 'as/mobile-large/DP353255.jpg', 1.33), + SearchData(1716, 42199, 'Vase with Mythical Creature Chasing Pearl', 'vase|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-066.jpg', 0.75), + SearchData(1649, 41874, 'Cup with grapes', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318307.jpg', 1.33), + SearchData(1775, 50519, 'Bowl', 'bowl|porcelain painted in underglaze blue|ceramics', 'as/mobile-large/1993_386_13_O1_sf.jpg', 1.33), + SearchData(1699, 42735, 'Long Side Table', 'table|wood (huanghuali or dalbergia odorifera)|furniture', 'as/mobile-large/1976_193_9_O2.jpg', 2.43), + SearchData(-98, 49529, 'Roof Tile End', 'roof tile end|earthenware|ceramics', 'as/mobile-large/39054.jpg', 1.04), + SearchData(1583, 42543, 'Dish with Dragon and Phoenix', 'dish|porcelain painted with cobalt blue under and colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-080.jpg', 1.33), + SearchData(1416, 42505, 'Flask with Medallion', 'flask|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342648.jpg', 0.75), + SearchData(1449, 42501, 'Plate with chrysanthemums and peonies', 'plate|porcelain painted with cobalt blue under a transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-028.jpg', 1.33), + SearchData(1749, 42216, 'Vase', 'vase|porcelain with blue glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-040.jpg', 0.75), + SearchData(-1150, 49511, 'Fitting', 'fitting|bronze inlaid with turquoise|metalwork', 'as/mobile-large/DT8149.jpg', 1.63), + SearchData(1424, 40765, 'Base for a mandala', 'base for a mandala|cloisonné enamel|cloisonné', 'as/mobile-large/DP229015.jpg', 1.33), + SearchData(1544, 42550, 'Vase', 'vase|porcelain painted in underglaze blue and overglaze yellow and red enamels|ceramics', 'as/mobile-large/DP-14609-180.jpg', 0.75), + SearchData(1749, 42226, 'Vase in Meiping Shape', 'vase|porcelain with celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-155.jpg', 0.75), + SearchData(1749, 44292, 'Old Testament figures', 'figures|cloisonné|cloisonné', 'as/mobile-large/DT4165.jpg', 1.25), + SearchData(1749, 50562, 'Cup with design of waves', 'cup|porcelain painted with red enamel and incised decoration (jingdezhen ware)|ceramics', 'as/mobile-large/LC-1971_282-O1.jpg', 1.04), + SearchData(1650, 76452, 'Vase with Scene from The Story of the Blue Robe', 'vase|porcelain painted with cobalt blue under and colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP261017.jpg', 0.75), + SearchData(1416, 39666, 'Jar with Dragon', 'jar|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DT5083.jpg', 0.80), + SearchData(1549, 42088, 'Shouxing, Stellar God of Immortality', 'figure|ivory|ivories', 'as/mobile-large/DP318638.jpg', 0.75), + SearchData(1805, 40770, 'Box in the shape of a flower', 'covered box|gilt, silver, cloisonné and painted enamels, semiprecious stones|metalwork', 'as/mobile-large/DP323432.jpg', 1.33), + SearchData(1729, 52026, 'Vase', 'vase|porcelain|ceramics', 'as/mobile-large/20871.jpg', 0.68), + SearchData(1883, 42223, 'Vase with Plum Blossoms and Birds', 'vase|porcelain painted with colored enamels on the biscuit (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-141.jpg', 0.75), + SearchData(683, 44802, 'Female musician', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/185580.jpg', 0.83), + SearchData(117, 44324, 'Square Pen with Six Rams', 'architectural model|earthenware with green lead glaze|ceramics', 'as/mobile-large/272392.jpg', 1.27), + SearchData(1835, 49865, 'Bowl with imaginary composite flowers', 'bowl|porcelain painted with overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/DP109322.jpg', 1.29), + SearchData(1550, 42552, 'Bowl', 'bowl|porcelain with yellow glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-091.jpg', 1.33), + SearchData(1749, 44208, 'Washer with magic fungus (lingzhi)', 'washer|jade (nephrite)|jade', 'as/mobile-large/49260.jpg', 1.31), + SearchData(849, 42399, 'Bowl with bird', 'bowl|stoneware with painted decoration and yellow glaze (changsha ware)|ceramics', 'as/mobile-large/DP-14609-104.jpg', 1.33), + SearchData(1800, 42139, 'Brush washer in the shape of a plum blossom', 'brush washer|jade (nephrite)|jade', 'as/mobile-large/56676.jpg', 1.21), + SearchData(1765, 52937, 'Vase in the shape of an ancient ritual cup (zhi) (one of a pair)', 'vase|porcelain with clair de lune glaze (jingdezhen ware)|ceramics', 'as/mobile-large/24_80_273_O1.jpg', 1.32), + SearchData(749, 42723, 'Monk, probably Ananda (Anantuo)', 'figure|limestone with pigment|sculpture', 'as/mobile-large/DP170269.jpg', 0.75), + SearchData(1805, 60613, 'Bowl imitating realgar', 'bowl|opaque mottled red and yellow glass|glass', 'as/mobile-large/DP307780.jpg', 1.33), + SearchData(1765, 41531, 'Snuff bottle with boy flying a bird', 'snuff bottle|coral|snuff bottles', 'as/mobile-large/DP319219.jpg', 0.75), + SearchData(1583, 52978, 'Bottle with Coiling Dragon', 'bottle|porcelain with transparent glaze (dehua ware)|ceramics', 'as/mobile-large/DP-14606-004.jpg', 0.75), + SearchData(758, 52577, 'Dog', 'figure|earthenware with brown glaze|tomb pottery', 'as/mobile-large/11_7_4.JPG', 0.93), + SearchData(1833, 41637, 'Snuff bottle in the shape of an ear of corn', 'snuff bottle|porcelain with yellow glaze (jingdezhen ware) and amber stopper|snuff bottles', 'as/mobile-large/DP321915.jpg', 0.75), + SearchData(1805, 43244, 'Incense stick holder', 'incense stick holder|jade (nephrite)|jade', 'as/mobile-large/49261.jpg', 0.60), + SearchData(-100, 54006, 'Scabbard Chape', 'scabbard slide|jade (nephrite)|jade', 'as/mobile-large/1985_214_103.jpg', 0.97), + SearchData(1199, 52031, 'Bowl with Peonies', 'bowl|porcelain with mold-impressed decoration under ivory glaze (ding ware)|ceramics', 'as/mobile-large/DP342645.jpg', 1.33), + SearchData(749, 39559, 'Vase', 'vase|earthenware with black glaze|ceramics', 'as/mobile-large/DP158745.jpg', 0.73), + SearchData(1500, 49523, 'Head of Buddha', 'head|cast iron|sculpture', 'as/mobile-large/DP170192.jpg', 0.75), + SearchData(117, 49519, 'Central Watchtower', 'architectural model|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/DP341913.jpg', 0.75), + SearchData(649, 49553, 'Amphora with dragon-shaped handles', 'amphora|stoneware with white glaze|ceramics', 'as/mobile-large/29_100_217.jpg', 0.64), + SearchData(-250, 49538, 'Scabbard Chape', 'scabbard chape|agate|hardstone', 'as/mobile-large/1985_214_105.JPG', 1.46), + SearchData(570, 42715, 'Head of a bodhisattva', 'head|limestone with pigment|sculpture', 'as/mobile-large/DP170268.jpg', 0.75), + SearchData(749, 42185, 'Cup with ring handle', 'cup|gilt bronze|metalwork', 'as/mobile-large/1974_274_1.JPG', 1.23), + SearchData(749, 75765, 'Seated court lady', 'figure|earthenware with tri-color (sancai) glaze|ceramics', 'as/mobile-large/DP227159.jpg', 0.75), + SearchData(-2500, 49371, 'Ritual Object (Bi)', 'ritual object|jade (nephrite)|jade', 'as/mobile-large/36902.jpg', 1.00), + SearchData(1698, 49156, 'The Kangxi Emperor\'s Southern Inspection Tour, Scroll Three: Ji\'nan to Mount Tai', 'handscroll|handscroll; ink and color on silk|paintings', 'as/mobile-large/DP105303_CRD.jpg', 2.19), + SearchData(1049, 44735, 'Dish with Scalloped Rim', 'dish|porcelain with ivory glaze (ding ware)|ceramics', 'as/mobile-large/DP342647.jpg', 1.33), + SearchData(1765, 42215, 'Vase in Shape of Archaic Bronze Vessel', 'vase|soft-paste porcelain (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-013.jpg', 0.75), + SearchData(1550, 42520, 'Bowl with Dragon', 'bowl|porcelain with incised decoration under and colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-171.jpg', 1.33), + SearchData(616, 42163, 'Buddha, probably Amitabha', 'figure|dry lacquer with polychrome pigment and gilding|sculpture', 'as/mobile-large/DP-15581-004.jpg', 0.90), + SearchData(1199, 50234, 'Bowl', 'bowl|porcelaneous ware with relief decoration under celadon glaze (longquan ware)|ceramics', 'as/mobile-large/34_113_8_O.jpg', 1.32), + SearchData(1505, 76550, 'Dish with mandarin ducks and lotuses', 'dish|cloisonné enamel|cloisonné', 'as/mobile-large/2011_111_O1.jpg', 1.33), + SearchData(1749, 42125, 'Miniature mountains representing the mythical realm Penglai', 'figurines|emerald|hardstone', 'as/mobile-large/DP257921.jpg', 1.33), + SearchData(-149, 42178, 'Female Dancer', 'figure|earthenware with slip and pigment|tomb pottery', 'as/mobile-large/DT206.jpg', 0.80), + SearchData(1066, 42474, 'Vase with Peony Scroll', 'vase|stoneware with white and black slip and cut decoration under transparent glaze (cizhou ware)|ceramics', 'as/mobile-large/DP-14610-001.jpg', 0.75), + SearchData(1113, 39936, 'Finches and bamboo', 'handscroll|handscroll; ink and color on silk|paintings', 'as/mobile-large/DP151504_CRD.jpg', 1.85), + SearchData(1805, 44176, 'Dish in the shape of a peach', 'dish|malachite|hardstone', 'as/mobile-large/DP240798.jpg', 1.33), + SearchData(-2150, 36441, 'Ritual Axe', 'ritual axe|jade (nephrite)|jade', 'as/mobile-large/1991_250.JPG', 1.46), + SearchData(1716, 42203, 'Vase with Flowers and Scholar’s Implements', 'vase|porcelain covered with powdered blue glaze and painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-146.jpg', 0.75), + SearchData(117, 49518, 'Stove', 'stove model|earthenware with green lead glaze|ceramics', 'as/mobile-large/186582.jpg', 0.80), + SearchData(1649, 76769, 'Buddhist monk Bodhidharma (Chinese: Damo)', 'figurine|rhinoceros horn|horn', 'as/mobile-large/DP253234.jpg', 0.75), + SearchData(1516, 50021, 'Dish with Dragons and Lotuses', 'dish|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-164.jpg', 1.33), + SearchData(1349, 42487, 'Stem Cup with Chrysanthemum Scroll', 'stem cup|porcelain with molded decoration under transparent glaze (jingdezhen shufu ware)|ceramics', 'as/mobile-large/DP222087.jpg', 0.75), + SearchData(574, 42702, 'Standing bodhisattva', 'headless figure|limestone with traces of pigment|sculpture', 'as/mobile-large/DP170182.jpg', 0.75), + SearchData(1449, 72420, 'Jug with Floral Scroll', 'jug|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP342532.jpg', 1.33), + SearchData(1849, 60682, 'Boy with leaves and box', 'figure|horn|horn', 'as/mobile-large/DP321123.jpg', 1.33), + SearchData(1833, 42238, 'Vase', 'vase|porcelain with yellow glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-087.jpg', 0.75), + SearchData(1800, 42234, 'Vase in Meiping Shape', 'vase|porcelain with coral-red glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-007.jpg', 0.75), + SearchData(1775, 50521, 'Bowl', 'bowl|porcelain painted in underglaze blue and overglaze polychrome enamels|ceramics', 'as/mobile-large/1993_386_11_O2_sf.jpg', 1.33), + SearchData(1716, 42252, 'Bowl', 'bowl|porcelain with rose-enameled glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-001.jpg', 1.33), + SearchData(1029, 42438, 'Funerary jar', 'urn|stoneware with incised and carved decoration under celadon glaze (longquan ware)|ceramics', 'as/mobile-large/DP335590.jpg', 0.81), + SearchData(1683, 42246, 'Vase', 'vase|porcelain with celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-109.jpg', 0.75), + SearchData(-800, 49437, 'Spoon (Bi)', 'spoon|bronze|metalwork', 'as/mobile-large/DP151384.jpg', 0.75), + SearchData(1765, 60920, 'One of a pair of boxes with elephants', 'box with cover|carved red lacquer|lacquer', 'as/mobile-large/LC-13_100_144ab-001.jpg', 1.20), + SearchData(658, 56155, 'Man carrying a bag', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/2000_349_4_O.jpg', 0.56), + SearchData(300, 44303, 'Set of Ten Belt Plaques', 'belt plaques|gilt bronze|metalwork', 'as/mobile-large/1994_605_2a-k_L35982.jpg', 1.56), + SearchData(1000, 42725, 'Bodhisattva Avalokiteshvara (Guanyin)', 'figure|wood (foxglove) with pigments, gilding, quartz and carnelian; single woodblock construction|sculpture', 'as/mobile-large/DP163998.jpg', 0.75), + SearchData(749, 49559, 'Covered jar', 'jar|earthenware with dappled black glaze|ceramics', 'as/mobile-large/267835.jpg', 0.75), + SearchData(1694, 52048, 'Vase', 'vase|porcelain with peach-bloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-115.jpg', 0.75), + SearchData(1683, 42245, 'Vase', 'vase|porcelain with moonlight glaze (jingdezhen ware); tiffany stand|ceramics', 'as/mobile-large/DP-14605-112.jpg', 0.75), + SearchData(1413, 57612, 'Bodhisattva Manjushri as Tikshna-Manjushri (Minjie Wenshu)', 'figure|gilt brass; lost-wax casting|sculpture', 'as/mobile-large/DP164061.jpg', 0.75), + SearchData(1733, 52056, 'Vase with Coiling Dragon', 'vase|porcelain with peach-bloom glaze (jingdezhen ware); western mount|ceramics', 'as/mobile-large/DP-14606-001.jpg', 0.75), + SearchData(1805, 43289, 'Tray with magic fungus (lingzhi)', 'tray|jade (nephrite)|jade', 'as/mobile-large/184066.jpg', 1.26), + SearchData(1749, 42207, 'Jar with Basket of Auspicious Flowers', 'jar|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-025.jpg', 0.75), + SearchData(117, 49522, 'Circular Ram Pen with Rider', 'architectural model|earthenware with green lead glaze|ceramics', 'as/mobile-large/2000_662_5_L35685.jpg', 1.06), + SearchData(1692, 48616, 'Bottle with Lion', 'bottle|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-163.jpg', 0.75), + SearchData(1765, 60919, 'One of a pair of boxes with elephants', 'box with cover|carved red lacquer|lacquer', 'as/mobile-large/LC-13_100_143ab-003.jpg', 1.38), + SearchData(1199, 42470, 'Pillow with Character Reading Zhen (Pillow)', 'pillow|stoneware with splashed glaze (jun ware)|ceramics', 'as/mobile-large/DP-14609-140.jpg', 1.33), + SearchData(1808, 51176, 'Bowl with decorative medallions', 'bowl|porcelain painted with overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/DP307774.jpg', 1.33), + SearchData(749, 42184, 'Scissors', 'scissors|silver with parcel gilding|metalwork', 'as/mobile-large/1974_268_13.JPG', 3.30), + SearchData(475, 42711, 'Bodhisattva (Maitreya) with crossed ankles', 'figure|sandstone with traces of pigment|sculpture', 'as/mobile-large/DP170134.jpg', 0.75), + SearchData(1750, 48649, 'Jar with Flowers', 'jar|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-057.jpg', 0.75), + SearchData(1849, 41514, 'Snuff bottle with design of coins', 'snuff bottle|glass|snuff bottles', 'as/mobile-large/DP319222.jpg', 0.75), + SearchData(1765, 43967, 'Vessel in the shape of a chrysanthemum', 'vessel|jade (nephrite)|jade', 'as/mobile-large/DP324367.jpg', 1.33), + SearchData(-1016, 42174, 'Ritual Wine Cup (Zhi)', 'wine cup|bronze|metalwork', 'as/mobile-large/DP219870.jpg', 0.75), + SearchData(-98, 44333, 'Tripod Cauldron (Ding)', 'cauldron|earthenware with pigment|ceramics', 'as/mobile-large/1994_605_32ab_268197.jpg', 1.02), + SearchData(-800, 49436, 'Spoon (Bi)', 'spoon|bronze|metalwork', 'as/mobile-large/DP151383.jpg', 0.75), + SearchData(1765, 42206, 'Vase with Basket of Auspicious Flowers', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-035.jpg', 0.75), + SearchData(749, 49565, 'Box', 'box|silver with parcel gilding|metalwork', 'as/mobile-large/1974_268_14.JPG', 2.17), + SearchData(724, 49556, 'Tripod cup with ring handle (bei)', 'cup|earthenware with marblized body and brown glaze|ceramics', 'as/mobile-large/50_221_15.JPG', 1.47), + SearchData(1692, 52035, 'Vase', 'vase|porcelain painted in famille verte enamels|ceramics', 'as/mobile-large/23478.jpg', 1.09), + SearchData(1749, 50898, 'Gourd-Shaped Bottle (one of a pair)', 'bottle|porcelain with celadon glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-121.jpg', 0.75), + SearchData(699, 42380, 'Rhyton in the shape of a bird', 'rhyton|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/202348.jpg', 1.14), + SearchData(762, 73218, 'Seated Musician', 'figure|white marble|sculpture', 'as/mobile-large/DP140157.jpg', 0.70), + SearchData(700, 42183, 'Dish in the shape of a leaf', 'dish|silver with parcel gilding|metalwork', 'as/mobile-large/DP100673.jpg', 1.32), + SearchData(1416, 49220, 'Dish', 'dish|porcelain with copper oxide glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-132.jpg', 1.33), + SearchData(1805, 39851, 'Ornament in the form of a mountain with figures', 'relief|soapstone|sculpture', 'as/mobile-large/DP324395.jpg', 0.75), + SearchData(112, 44313, 'Mirror with animals of the four directions and other mythical creatures', 'mirror|bronze|mirrors', 'as/mobile-large/1994.605.12.jpg', 1.33), + SearchData(-1150, 49504, 'Finial', 'finial|bronze|metalwork', 'as/mobile-large/1985_214_38_238462.jpg', 0.52), + SearchData(1583, 65599, 'Elephant-Shaped Kendi Drinking Vessel', 'vessel|porcelain painted in underglaze blue|ceramics', 'as/mobile-large/DP274936.jpg', 0.75), + SearchData(399, 64920, 'Ornamental Plaque', 'plaque|gilt bronze, gold, lapis lazuli, turquoise, and white coral|metalwork', 'as/mobile-large/DP290762.jpg', 1.33), + SearchData(1692, 52033, 'Covered Jar', 'covered jar|porcelain painted in famille verte enamels|ceramics', 'as/mobile-large/20713.jpg', 0.74), + SearchData(1700, 49353, 'European Sitting on a Lion', 'figure|porcelain with colored glazes and vermilion pigment on the biscuit (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-033.jpg', 1.33), + SearchData(850, 42395, 'Ewer with dancing figures', 'ewer|stoneware with white slip, pigment, and applied decoration under straw glaze (changsha ware)|ceramics', 'as/mobile-large/DP-14609-047.jpg', 1.33), + SearchData(658, 56153, 'Man with a shovel', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/DP-17413-001.jpg', 0.75), + SearchData(1282, 61658, 'Bodhisattva Avalokiteshvara (Guanyin)', 'figure|wood (wiillow) with traces of pigment; single woodblock construction|sculpture', 'as/mobile-large/DP223478.jpg', 0.75), + SearchData(1749, 41918, 'Boy with water buffalo', 'figurine|jade (nephrite)|jade', 'as/mobile-large/DT257389.jpg', 1.26), + SearchData(1124, 42465, 'Bowl', 'bowl|stoneware with blue glaze (jun ware)|ceramics', 'as/mobile-large/DP342538.jpg', 1.33), + SearchData(1692, 48665, 'Vase', 'vase|porcelain with ox-blood glaze|ceramics', 'as/mobile-large/20946.jpg', 0.52), + SearchData(-900, 44308, 'Dagger-Axe', 'dagger-axe|bronze|metalwork', 'as/mobile-large/1994_605_7_L35710.jpg', 1.57), + SearchData(574, 44795, 'Camel', 'figure|earthenware with traces of pigment|tomb pottery', 'as/mobile-large/DT4914.jpg', 0.80), + SearchData(1600, 41482, 'Folding Fan with Fishing Net Decoration', 'folding fan|folding fan; ink on gold-flecked paper and gold-flecked lacquered bamboo fan bones and end pieces|paintings', 'as/mobile-large/1988_212.JPG', 1.22), + SearchData(949, 42434, 'Ewer with Parrots', 'ewer|stoneware with incised decoration under celadon glaze (yue ware)|ceramics', 'as/mobile-large/DP-14609-032.jpg', 0.75), + SearchData(1649, 78434, 'Buddhist disciple, or luohan, holding a peach', 'figure|soapstone|sculpture', 'as/mobile-large/DP326722.jpg', 0.75), + SearchData(1833, 41879, 'Cup with Eight Daoist Immortals', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318310.jpg', 1.33), + SearchData(-50, 49539, 'Covered Jar (Hu)', 'covered jar|earthenware with painted decoration|tomb pottery', 'as/mobile-large/DP341151.jpg', 0.75), + SearchData(1835, 41321, 'Snuff bottle with boys at play', 'snuff bottle|porcelain painted with underglaze cobalt blue and overglaze enamels (jingdezhen ware) with coral and metal stopper|snuff bottles', 'as/mobile-large/DP319203.jpg', 0.75), + SearchData(7, 58942, 'Pole Base in the Form of a Tiger', 'pole base|steatite|sculpture', 'as/mobile-large/DP158753.jpg', 1.33), + SearchData(249, 44390, 'Ladle with handle in the shape of a dragon\'s head', 'ladle|gilt bronze|metalwork', 'as/mobile-large/DP219351.jpg', 1.33), + SearchData(1765, 52968, 'Bowl with floral sprays', 'bowl|porcelain painted with overglaze enamels (jingdezhen ware)|ceramics', 'as/mobile-large/49_37_145289.jpg', 1.16), + SearchData(1649, 42242, 'Vase in Meiping Shape', 'vase|porcelain with incised decoration under yellow glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-089.jpg', 0.75), + SearchData(7, 61524, 'Tally in the shape of a tiger (Hu fu)', 'tally|gilt bronze|sculpture', 'as/mobile-large/18_43_7_O1.jpg', 1.83), + SearchData(758, 52576, 'Dog', 'figure|earthenware with brown glaze|tomb pottery', 'as/mobile-large/11_7_3.JPG', 0.89), + SearchData(122, 49532, 'Model of Wellhead with Bucket', 'wellhead model|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/2000_662_12ab_L35951.JPG', 0.74), + SearchData(1600, 40747, 'Panel with Dragons', 'panel|cloisonné enamel|cloisonné', 'as/mobile-large/29_110_94_74185.jpg', 1.37), + SearchData(1849, 44277, 'Bowl with archaic design', 'bowl|jade (nephrite)|jade', 'as/mobile-large/49217.jpg', 1.18), + SearchData(1765, 46905, 'Cup with Butterflies', 'cup|porcelain painted with colored enamels and gilded (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14606-044.jpg', 1.33), + SearchData(117, 44322, 'Animal Pen with Figures', 'architectural model|earthenware with green lead glaze|ceramics', 'as/mobile-large/DT3800.jpg', 1.21), + SearchData(-49, 49900, 'Jar', 'jar|earthenware with green glaze|ceramics', 'as/mobile-large/240389.jpg', 1.04), + SearchData(1749, 39844, 'Boy with water buffalo', 'figure|jade (nephrite)|jade', 'as/mobile-large/DP318968.jpg', 1.33), + SearchData(1765, 41333, 'Snuff bottle in imitation of painted enamel metalwork', 'snuff bottle|porcelain painted with overglaze enamels (jingdezhen ware)|snuff bottles', 'as/mobile-large/DP319212.jpg', 0.75), + SearchData(649, 73219, 'Covered jar', 'covered jar|earthenware with blue glaze|ceramics', 'as/mobile-large/DP145863.jpg', 1.00), + SearchData(1350, 40211, 'Dish with long-tailed birds and hibiscuses', 'dish|carved red lacquer|lacquer', 'as/mobile-large/DT2677.jpg', 1.26), + SearchData(1650, 39868, 'Dish with pine trees', 'dish|bamboo|bamboo', 'as/mobile-large/DP323470.jpg', 1.33), + SearchData(649, 44346, 'Bowl', 'bowl|porcelain with white glaze|ceramics', 'as/mobile-large/DP342524.jpg', 1.33), + SearchData(1749, 42121, 'Cup with figures in a landscape', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318308.jpg', 1.33), + SearchData(1466, 42509, 'Dish with Blossoming Plum and Crescent Moon', 'dish|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-102.jpg', 1.33), + SearchData(1765, 52938, 'Vase in the shape of an ancient ritual cup (zhi) (one of a pair)', 'vase|porcelain with clair de lune glaze (jingdezhen ware)|ceramics', 'as/mobile-large/24_80_274_O1.jpg', 1.32), + SearchData(1419, 42012, 'Seal with mantra in Vartu script', 'seal|ivory, turquoise, and iron damascened with gold|ivories', 'as/mobile-large/DP318643.jpg', 0.75), + SearchData(1683, 46094, 'Vase with landscape scenes', 'vase|porcelain painted under the glaze with cobalt blue (jiangxi province; jingdezhen ware)|ceramics', 'as/mobile-large/23525.jpg', 0.75), + SearchData(1416, 52713, 'Altar Bowl with Fish', 'bowl|porcelain painted in copper red under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-082.jpg', 0.75), + SearchData(1805, 44118, 'Figure of a cicada', 'figure|smoky quartz|hardstone', 'as/mobile-large/DP323427.jpg', 1.33), + SearchData(749, 49368, 'Camel', 'figure|earthenware with three-color (sancai) glaze|tomb pottery', 'as/mobile-large/67_43_1.JPG', 0.70), + SearchData(749, 42182, 'Octagonal cup with ring handle', 'cup|silver with parcel gilding|metalwork', 'as/mobile-large/1985_214_17_238110.jpg', 1.24), + SearchData(1350, 39638, 'Plate with Carp', 'plate|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-052.jpg', 1.33), + SearchData(1550, 42549, 'Jar with carp in lotus pond', 'jar|porcelain painted in underglaze cobalt blue and overglaze polychrome enamels (jingdezhen ware)|ceramics', 'as/mobile-large/DP342697.jpg', 0.75), + SearchData(1849, 41201, 'Snuff bottle', 'snuff bottle|jade (jadeite)|snuff bottles', 'as/mobile-large/DP319176.jpg', 0.75), + SearchData(1334, 40256, 'Dish with Flowering Plum and Birds', 'dish|black lacquer with mother-of-pearl inlay|lacquer', 'as/mobile-large/DP222095.jpg', 0.75), + SearchData(7, 44334, 'Covered Bowl (He)', 'bowl|earthenware with pigment|ceramics', 'as/mobile-large/268198.jpg', 0.93), + SearchData(1199, 48121, 'Tea bowl with decoration of six-petaled flowers', 'bowl|stoneware with black and brown glazes and paper-cut designs (jizhou ware)|ceramics', 'as/mobile-large/DP342686.jpg', 1.33), + SearchData(290, 44733, 'Funerary Urn (Hunping)', 'funerary urn|stoneware with olive green glaze (yue ware)|ceramics', 'as/mobile-large/DT5080.jpg', 0.78), + SearchData(1749, 62039, 'Lion and cubs', 'figurine|jade (nephrite)|jade', 'as/mobile-large/183844.jpg', 1.17), + SearchData(1600, 56356, 'Wardrobe', 'wardrobe|wood with inlay of mother-of-pearl, amber, glass, ivory, and other materials|furniture', 'as/mobile-large/DP205480.jpg', 0.75), + SearchData(-1000, 49506, 'Fitting in the Shape of a Bird', 'fitting|bronze|metalwork', 'as/mobile-large/DP167769.jpg', 1.33), + SearchData(1513, 42522, 'Jar with Dragon', 'jar|porcelain with incised decoration under colored glazes|ceramics', 'as/mobile-large/DP-14609-095.jpg', 1.33), + SearchData(1499, 42511, 'Jar with Scrolling Vine and Gourds', 'jar|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP225475.jpg', 1.33), + SearchData(-550, 61041, 'Grain Serving Vessel (Dui)', 'grain vessel|bronze|metalwork', 'as/mobile-large/DP155209.jpg', 1.33), + SearchData(1099, 51076, 'Tea Bowl with Hare’s-Fur Decoration', 'bowl|stoneware with copper-oxide glaze (jian ware)|ceramics', 'as/mobile-large/DP295375.jpg', 1.33), + SearchData(572, 64921, 'Jar with floral decorations and musicians', 'jar|stoneware with applied decoration under celadon glaze|ceramics', 'as/mobile-large/DP-14609-056.jpg', 0.75), + SearchData(1600, 40505, 'Medallion with return from a spring outing', 'medallion|ivory|ivories', 'as/mobile-large/DT7032.jpg', 1.25), + SearchData(1099, 42468, 'Jar', 'jar|stoneware with splashed glaze (jun ware)|ceramics', 'as/mobile-large/DP-14609-160.jpg', 1.33), + SearchData(1749, 41996, 'Arhat', 'figure|ivory|ivories', 'as/mobile-large/DP-15968-045.jpg', 0.75), + SearchData(1299, 51075, 'Tea Bowl with “Tortoiseshell” Design', 'bowl|stoneware with iron glaze splashed with wood-ash solution (jizhou ware)|ceramics', 'as/mobile-large/DP-14610-058.jpg', 1.33), + SearchData(1350, 49920, 'Vase with Peony Scrolls', 'vase|porcelain with molded decoration under celadon glaze (longquan ware)|ceramics', 'as/mobile-large/DP-14609-141.jpg', 0.75), + SearchData(1700, 42204, 'Vase with Flowering Plants and Birds', 'vase|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-070.jpg', 0.75), + SearchData(566, 49544, 'Seated Falconer', 'one of a pair of figures|earthenware with red and white pigments|tomb pottery', 'as/mobile-large/DP702293.jpg', 0.90), + SearchData(749, 52565, 'Horse and boy', 'figure|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/50_221_2.JPG', 0.99), + SearchData(1849, 42237, 'Vase', 'vase|porcelain with coral red glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-017.jpg', 0.75), + SearchData(683, 39524, 'Amphora', 'ewer|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/DP222014.jpg', 0.75), + SearchData(1692, 40754, 'Herdboy with Water Buffalo', 'group|cloisonné, gilded bronze|cloisonné', 'as/mobile-large/LC-30_128_2ab_001.jpg', 1.50), + SearchData(1800, 646726, 'Bowl in the shape of a lotus leaf', 'bowl|yellow glass with carved and incised decoration|glass', 'as/mobile-large/DP702964.jpg', 1.33), + SearchData(1149, 50235, 'Vase', 'vase|stoneware with crackled glaze (longquan ware)|ceramics', 'as/mobile-large/DP335583.jpg', 0.92), + SearchData(1550, 64484, 'Bowl with children in a garden', 'bowl|porcelain painted in underglaze cobalt blue (jingdezhen ware)|ceramics', 'as/mobile-large/DP154006.jpg', 1.33), + SearchData(1749, 39847, 'Cup with two feline dragons', 'cup|jade (nephrite)|jade', 'as/mobile-large/DP318971.jpg', 1.33), + SearchData(1849, 41352, 'Snuff bottle with boys at play', 'snuff bottle|porcelain painted with underglaze cobalt blue (jingdezhen ware)|snuff bottles', 'as/mobile-large/DP293330.jpg', 0.75), + SearchData(1080, 39668, 'Old Trees, Level Distance', 'handscroll|handscroll; ink and color on silk|paintings', 'as/mobile-large/DP167812_CRD.jpg', 1.88), + SearchData(1699, 42736, 'Altar coffer', 'altar coffer|wood (huanghuli or dalbergia odorifera)|furniture', 'as/mobile-large/1976_344.JPG', 1.44), + SearchData(1099, 49204, 'Ewer with Phoenixes', 'ewer|stoneware with incised and carved decoration under celadon glaze (yaozhou ware)|ceramics', 'as/mobile-large/DT11750.jpg', 0.80), + SearchData(683, 44803, 'Female musician', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/23_180_6.jpg', 0.64), + SearchData(1903, 19897, 'Vase', 'vase|earthenware|', 'ad/mobile-large/L.2009.22.168.jpg', 1.32), + SearchData(-100, 49901, 'Cocoon-Shaped Vessel', 'vessel|earthenware with incised decoration and burnishing|ceramics', 'as/mobile-large/DT8729.jpg', 1.25), + SearchData(749, 49584, 'Hair Ornament', 'hair ornament|gold inlaid with turquoise|jewelry', 'as/mobile-large/31_54_1.JPG', 0.41), + SearchData(1733, 42201, 'Vase with Scholars in Landscape', 'vase|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-091.jpg', 0.75), + SearchData(-1150, 53954, 'Implement with Curved Blade', 'implement|bronze|metalwork', 'as/mobile-large/DP219140.jpg', 0.75), + SearchData(1000, 42722, 'Arhat (Luohan)', 'figure|stoneware with three-color glaze|sculpture', 'as/mobile-large/DP163962.jpg', 0.75), + SearchData(1805, 42137, 'Fantastic animal carrying books', 'figurine|jade (nephrite)|jade', 'as/mobile-large/24_80_139_O1_sf.jpg', 1.50), + SearchData(486, 42733, 'Buddha Maitreya (Mile)', 'figure|gilt bronze with traces of pigment; piece-mold cast|sculpture', 'as/mobile-large/DP170102.jpg', 0.75), + SearchData(1805, 43214, 'Cup and saucer', 'cup and saucer|jade (nephrite)|jade', 'as/mobile-large/183836.jpg', 1.24), + SearchData(725, 53957, 'Box', 'box|earthenware with three-color (sancai) glaze|ceramics', 'as/mobile-large/50_221_13ab.JPG', 1.31), + SearchData(1550, 54245, 'Bodhisattva Avalokiteshvara of the Lion\'s Roar, or Simhanada Avalokiteshvara (Shi Hou Guanyin)', 'figure|wood (poplar) with pigment; single-woodblock construction|sculpture', 'as/mobile-large/DP170241.jpg', 0.75), + SearchData(949, 44351, 'Comb top', 'comb top|jade (nephrite)|jade', 'as/mobile-large/L35682.jpg', 1.38), + SearchData(683, 44804, 'Female musician with harp', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/185582.jpg', 0.80), + SearchData(1750, 42251, 'Vase with Flowers', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-049.jpg', 0.75), + SearchData(649, 65100, 'Standing attendant', 'figure|earthenware with pigment|sculpture', 'as/mobile-large/2002_501.JPG', 0.64), + SearchData(1150, 40055, 'Emperor Xuanzong\'s Flight to Shu', 'hanging scroll|hanging scroll; ink, color, and gold on silk|paintings', 'as/mobile-large/DP247676.jpg', 1.33), + SearchData(566, 49543, 'Camel and Rider', 'figures|earthenware with pigment|tomb pottery', 'as/mobile-large/DP702303.jpg', 0.98), + SearchData(-2200, 49139, 'Vase (Hu)', 'vase|burnished earthenware|ceramics', 'as/mobile-large/202507.jpg', 0.83), + SearchData(653, 54077, 'Female attendant', 'one of a pair of figures|earthenware with pigment|tomb pottery', 'as/mobile-large/DP702324.jpg', 0.72), + SearchData(1849, 41897, 'Boy with leaves and box', 'figure|horn|horn', 'as/mobile-large/DP321099.jpg', 1.33), + SearchData(-98, 44399, 'Roof-Tile End with Auspicious Inscription', 'tile|earthenware|ceramics', 'as/mobile-large/1994_605_99.JPG', 1.02), + SearchData(1099, 49914, 'Vase', 'vase|porcelain with celadon glaze (jingdezhen qingbai ware)|ceramics', 'as/mobile-large/DP-15944-039.jpg', 0.75), + SearchData(1416, 39880, 'Finial for a Buddhist staff (khatvanga)', 'finial|ivory|ivories', 'as/mobile-large/DP318675.jpg', 0.75), + SearchData(-2549, 49379, 'Tripod Vessel (Gui)', 'vessel|earthenware|ceramics', 'as/mobile-large/DP-12552-001.jpg', 0.67), + SearchData(1591, 48948, 'The Sixteen Luohans', 'handscroll|handscroll; ink and color on paper|paintings', 'as/mobile-large/DP153754.jpg', 1.44), + SearchData(849, 42400, 'Flask', 'flask|stoneware with splashed glaze (jun ware)|ceramics', 'as/mobile-large/DP-14609-022.jpg', 0.75), + SearchData(1616, 41877, 'Cup in the shape of a magnolia blossom', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318319.jpg', 0.75), + SearchData(1295, 40081, 'Wang Xizhi watching geese', 'handscroll|handscroll; ink, color, and gold on paper|paintings', 'as/mobile-large/DP273822.jpg', 0.75), + SearchData(100, 44396, 'Pig in Recumbent Position', 'figure|jade (nephrite)|jade', 'as/mobile-large/DP-17989-049.jpg', 1.33), + SearchData(117, 52010, 'Granary Tower', 'model of farm building|earthenware with green lead glaze|ceramics', 'as/mobile-large/18736.jpg', 0.79), + SearchData(1600, 42190, 'Vase in Meiping Shape with Phoenix', 'vase|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP271348.jpg', 0.75), + SearchData(1099, 49207, 'Vase with abstract scroll decoration', 'vase|stoneware with sgraffito decoration (cizhou ware)|ceramics', 'as/mobile-large/DP-14610-053.jpg', 0.75), + SearchData(7, 44335, 'Goblet (one of a pair)', 'goblet|earthenware with pigment|ceramics', 'as/mobile-large/268196.jpg', 1.38), + SearchData(1849, 51142, 'Vase in Meiping Shape', 'vase|porcelain with iron-red glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-175.jpg', 0.75), + SearchData(-500, 49407, 'Halberd', 'halberd|bronze inlaid with silver|metalwork', 'as/mobile-large/1985_214_28_238257.jpg', 1.55), + SearchData(1350, 49216, 'Bottle with Peony Scroll', 'bottle|porcelain painted with cobalt blue under a transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP274702.jpg', 0.75), + SearchData(1700, 42403, 'Vase in Shape of Archaic Bronze Vessel with Flowers and Birds', 'vase|porcelain painted with colored enamels over transparent glaze and gilded (jingdezhen ware)|ceramics', 'as/mobile-large/DT218.jpg', 0.80), + SearchData(683, 44801, 'Female musician with lute', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/DP337805.jpg', 0.75), + SearchData(1550, 50022, 'Dish with Rock, Peonies, Chrysanthemums, and Fungus', 'dish|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14609-001.jpg', 1.33), + SearchData(749, 49587, 'Mirror with Grapes and Fantastic Sea Animals', 'mirror|bronze|mirrors', 'as/mobile-large/25_82_8_59801.JPG', 0.85), + SearchData(7, 52527, 'Well Head', 'wellhead|pottery|ceramics', 'as/mobile-large/67822.jpg', 0.78), + SearchData(122, 72544, 'Comb', 'comb|jade (nephrite) and gold|jade', 'as/mobile-large/DP116267.jpg', 1.21), + SearchData(1506, 61826, 'Seated Lion (one of a pair)', 'statue|marble|sculpture', 'as/mobile-large/1979_457_2_F.JPG', 0.80), + SearchData(749, 50664, 'Rhyton with handle in the shape of a monkey', 'rhyton|earthenware with brown glaze|ceramics', 'as/mobile-large/1985_208_5.JPG', 1.63), + SearchData(1516, 42528, 'Brush Rest with Persian Inscription', 'brush rest|porcelain painted with cobalt blue under transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14156-003.jpg', 1.33), + SearchData(1692, 49349, 'Brush Washer', 'brush washer|porcelain with peachbloom glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP326725.jpg', 1.33), + SearchData(1765, 42317, 'Vase with Nine Peaches', 'vase|porcelain painted with colored enamels over transparent glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP249036.jpg', 0.75), + SearchData(658, 49552, 'Foreign dancer', 'figure|earthenware with pigment|tomb pottery', 'as/mobile-large/226505.jpg', 0.71), + SearchData(1729, 52028, 'Vase with decoration of dragons among clouds (one of a pair)', 'vase|porcelain with incised decoration (anhua) under glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP307599.jpg', 0.75), + SearchData(1749, 41882, 'Cup depicting Three Laughers of Tiger Ravine', 'cup|rhinoceros horn|horn', 'as/mobile-large/DP318336.jpg', 0.75), + SearchData(1099, 52652, 'Plate', 'plate|pottery (jun ware, possibly guan ware)|ceramics', 'as/mobile-large/DP-14609-137.jpg', 1.33), + SearchData(1893, 20735, 'Vase', 'vase|porcelain|', 'ad/mobile-large/DP253316.jpg', 0.71), + SearchData(117, 44323, 'Goat Pen', 'architectural model|earthenware with green lead glaze|tomb pottery', 'as/mobile-large/272393.jpg', 1.26), + SearchData(749, 52970, 'Bowl', 'bowl|earthenware with molded decoration and three color (sancai) glaze*|ceramics', 'as/mobile-large/DP342650.jpg', 1.33), + SearchData(1716, 42241, 'Vase in Shape of Double Gourd', 'vase|porcelain with yellow glaze (jingdezhen ware)|ceramics', 'as/mobile-large/DP-14605-022.jpg', 0.75), + SearchData(1199, 42466, 'Bowl with “Oil-Spot” Design', 'bowl|stoneware with iron-oxide slip and glaze (cizhou-type ware)|ceramics', 'as/mobile-large/DP-14610-077.jpg', 1.33), + SearchData(1800, 43225, 'Ornament in the shape of a boat', 'ornament|jade (nephrite)|jade', 'as/mobile-large/31627.jpg', 1.53), + SearchData(1424, 42014, 'Seal with knob in the shape of a wheel', 'seal|ivory|ivories', 'as/mobile-large/DP318655.jpg', 0.75), + SearchData(-1049, 49406, 'Animal-Head Knife', 'knife|bronze inlaid with turquoise|metalwork', 'as/mobile-large/1985_214_26_238255.jpg', 0.27), + SearchData(800, 44616, 'Comb top', 'comb top|mother-of-pearl|jewelry', 'as/mobile-large/DP138489.jpg', 1.00), + SearchData(-49, 49533, 'Jar (Labakou Hu)', 'jar|earthenware with brown-green glaze and raised decoration|ceramics', 'as/mobile-large/232140.jpg', 0.81), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/machu_picchu_search_data.dart b/lib/logic/data/wonders_data/search/machu_picchu_search_data.dart new file mode 100644 index 00000000..b2ad4233 --- /dev/null +++ b/lib/logic/data/wonders_data/search/machu_picchu_search_data.dart @@ -0,0 +1,374 @@ +part of '../machu_picchu_data.dart'; + +// Search suggestions (78) +List _searchSuggestions = const ['hat', 'figure', 'earflare', 'prosopis', 'fiber', 'ornament', 'paccha', 'copper', 'feline', 'bronze', 'canopa', 'figurine', 'costumes', 'dish', 'tomb', 'lime', 'miniature', 'top', 'bottle', 'wood', 'gilded', 'inlay', 'tumi', 'alloy', 'silver', 'pin', 'slip', 'resin', 'warrior', 'woven', 'tupu', 'gold', 'implements', 'stirrup', 'tunic', 'votive', 'jar', 'camelid', 'stone', 'sculpture', 'spoon', 'cornered', 'knife', 'sheet', 'ceramics', 'turquoise', 'ornaments', 'pigment', 'metal', 'profile', 'post', 'head', 'spout', 'pigmented', 'sling', 'cast', 'ceramic', 'cotton', 'shell', 'hammered', 'containers', 'kero', 'bird', 'double', 'escallonia', 'textiles', 'ceremonial', 'hair', 'bag', 'staff', 'tapestry', 'vessel', 'funerary', 'feathers', 'paint', 'disk', 'fragment', 'container', ]; + +// Machu Picchu (366) +List _searchData = const [ + SearchData(1400, 310616, 'Double Vessel, Monkey', 'vessel|silver|metal-containers', 'ao/mobile-large/DT10189.jpg', 0.78), + SearchData(600, 318644, 'Profile Warrior Ornament', 'ornament|gilded copper, shell, turquoise|metal-ornaments', 'ao/mobile-large/1987.394.72.JPG', 0.82), + SearchData(1500, 313275, 'Vessel', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/vs1979_206_1082.jpg', 0.84), + SearchData(1500, 315620, 'Copper Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_560.jpg', 0.39), + SearchData(750, 316963, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_138.jpg', 1.11), + SearchData(1500, 315624, 'Ornamented Knife', 'knife|copper|metal-implements', 'ao/mobile-large/hz1987_394_564.jpg', 1.15), + SearchData(1500, 313287, 'Storage Jar (aryballos)', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/vs1979_206_1094.jpg', 0.88), + SearchData(1650, 316847, 'Kero', 'kero|wood, metal inlay, red pigment|wood-containers', 'ao/mobile-large/1994.35.19_a.JPG', 0.78), + SearchData(1385, 312796, 'Hanging', 'hanging|cotton|textiles-woven', 'ao/mobile-large/DT9943.jpg', 0.76), + SearchData(1500, 315228, 'Bronze Mace Head in Feline Form', 'mace head|bronze (cast)|metal-implements', 'ao/mobile-large/1987.394.166_a.jpg', 1.47), + SearchData(1500, 315283, 'Pin', 'pin|copper, gilt|metal-ornaments', 'ao/mobile-large/vs1987_394_223A.JPG', 0.36), + SearchData(1500, 313222, 'Ceremonial Implement', 'implement|wood, resin paint, metal|wood-implements', 'ao/mobile-large/DP-23805-001.jpg', 0.39), + SearchData(1400, 315609, 'Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/DP216738.jpg', 0.64), + SearchData(1500, 315490, 'Copper Tumi with Figure', 'knife|copper (cast)|metal-implements', 'ao/mobile-large/vs1987_394_415.jpg', 0.78), + SearchData(1400, 315608, 'Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/DP216737.jpg', 0.65), + SearchData(1475, 313341, 'Double bowl', 'bowl|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-24356-001.jpg', 1.33), + SearchData(1500, 313152, 'Tunic', 'tunic|camelid hair|textiles-woven', 'ao/mobile-large/DP244273.jpg', 1.33), + SearchData(1500, 313205, 'Dish with Bird Head', 'dish|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1979.206.1008.jpg', 1.55), + SearchData(1500, 315285, 'Copper Pin', 'pin|copper, gilt|metal-ornaments', 'ao/mobile-large/vs1987_394_225.JPG', 0.42), + SearchData(750, 316970, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_145.jpg', 1.08), + SearchData(1500, 314528, 'Tunic with Diamond Band', 'tunic|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP107698.jpg', 0.90), + SearchData(500, 314681, 'Prisoner jar', 'jar|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-24362-001.jpg', 0.75), + SearchData(1140, 309105, 'Tupu (pin)', 'pin|copper|metal-ornaments', 'ao/mobile-large/hz64_228_607.jpg', 0.36), + SearchData(1466, 317753, 'Female figurine', 'figure|silver-gold alloy|sculpture-sheet metal', 'ao/mobile-large/DP-13440-036.jpg', 0.67), + SearchData(550, 309427, 'Ear Ornament, Winged Runner', 'earflare|gold, turquoise, sodalite, shell|metal-ornaments', 'ao/mobile-large/66.196.40.jpg', 0.77), + SearchData(825, 307975, 'Tunic with Confronting Catfish', 'tunic|camelid hair, tapestry-weave|textiles-woven', 'ao/mobile-large/DT829.jpg', 1.98), + SearchData(1350, 307467, 'Tweezers', 'tweezers|silver|metal-implements', 'ao/mobile-large/82.1.23.JPG', 0.93), + SearchData(400, 309411, 'Headdress Ornament', 'headdress ornament|gold|metal-ornaments', 'ao/mobile-large/DP-14786-039.jpg', 1.34), + SearchData(1500, 313225, 'Funerary Staff', 'tomb post|wood, silver, nails|wood-sculpture', 'ao/mobile-large/DP-23803-001.jpg', 0.42), + SearchData(1500, 315618, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_558.JPG', 0.77), + SearchData(1500, 313226, 'Funerary Staff', 'staff|wood, paint, metal sheathing|wood-sculpture', 'ao/mobile-large/DP-23808-001.jpg', 0.33), + SearchData(750, 308121, 'Four-Cornered Hat', 'hat|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP264889.jpg', 0.75), + SearchData(750, 316962, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/hz1994_35_137.jpg', 1.17), + SearchData(1300, 310476, 'Feathered Tabard', 'tunic|cotton, feathers|feathers-costumes', 'ao/mobile-large/DP218937.jpg', 1.33), + SearchData(750, 316339, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_149.jpg', 1.03), + SearchData(1500, 307941, 'Bag Tassel', 'bag fragment|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP18687_28.171.4,1.jpg', 0.60), + SearchData(1650, 316841, 'Kero', 'kero|wood (escallonia), pigmented resin inlay|wood-containers', 'ao/mobile-large/DP104798.jpg', 1.00), + SearchData(1466, 309227, 'Tupu (pin)', 'pin|copper alloy|metal-ornaments', 'ao/mobile-large/vs64_228_701.jpg', 0.48), + SearchData(750, 316980, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_160.jpg', 1.09), + SearchData(1500, 313269, 'Deer Stick', 'staff|wood, paint|wood-sculpture', 'ao/mobile-large/1979.206.1076.jpg', 1.50), + SearchData(750, 312911, 'Four-Cornered Hat', 'hat|camelid hair, cotton|textiles-costumes', 'ao/mobile-large/1979.206.724 a.jpg', 0.99), + SearchData(1650, 316846, 'Kero', 'kero|wood, tin inlay|wood-containers', 'ao/mobile-large/DP-24246-001.jpg', 0.86), + SearchData(1500, 313271, 'Headband', 'head ornament|gold (hammered)|metal-ornaments', 'ao/mobile-large/vs1979_206_1078.jpg', 0.16), + SearchData(1500, 313206, 'Dish with Bird Head', 'dish|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1979.206.1009.jpg', 1.50), + SearchData(1466, 313251, 'Female figurine', 'figure|gold|sculpture-sheet metal', 'ao/mobile-large/DP-13440-033.jpg', 0.70), + SearchData(1500, 315286, 'Ceremonial Knife (Tumi)', 'knife|copper (cast)|metal-implements', 'ao/mobile-large/vs1987_394_226.jpg', 1.00), + SearchData(1500, 315227, 'Ceremonial Knife (Tumi)', 'knife|bronze (cast)|metal-implements', 'ao/mobile-large/1987.394.165_a.jpg', 1.36), + SearchData(1700, 316855, 'Kero', 'kero|wood, pigmented resin inlay|wood-containers', 'ao/mobile-large/DP104805.jpg', 0.95), + SearchData(550, 308526, 'Stirrup Spout Bottle with Bird and Snake', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/64.228.20.JPG', 0.75), + SearchData(550, 308508, 'Owl Warrior Bottle', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/64.228.2.JPG', 0.67), + SearchData(750, 316973, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_148.jpg', 1.07), + SearchData(-250, 307617, 'Bowl', 'bowl|ceramic, post-fired paint|ceramics-containers', 'ao/mobile-large/63.232.73_b.jpg', 1.36), + SearchData(400, 308535, 'Figure Jar', 'jar|ceramic, slip|ceramics-containers', 'ao/mobile-large/64.228.29_a.JPG', 0.67), + SearchData(1475, 309389, 'Storage Jar (Aryballus)', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/66.30.6_a.jpg', 0.80), + SearchData(1500, 317591, 'Votive Container (Canopa)', 'vessel|stone|stone-containers', 'ao/mobile-large/1994.35.758.jpg', 1.50), + SearchData(1600, 316843, 'Kero', 'kero|wood, pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_15.jpg', 0.71), + SearchData(-1000, 310662, 'Feline-shaped stirrup-spout bottle', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/DP-24349-001.jpg', 0.75), + SearchData(325, 309145, 'Chisel or tupu (pin)', 'pin|copper|metal-implements', 'ao/mobile-large/vs64_228_619.jpg', 0.31), + SearchData(1600, 316845, 'Kero', 'kero|wood (prosopis?), pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_17.jpg', 0.80), + SearchData(1250, 316723, 'Serpent (tunjo)', 'figure|gold|metal-ornaments', 'ao/mobile-large/vs1992_92_1.jpg', 0.30), + SearchData(1500, 312635, 'Bronze Tumi with Figures', 'knife|bronze|metal-implements', 'ao/mobile-large/1979.206.415.JPG', 0.67), + SearchData(750, 316965, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_140.jpg', 1.12), + SearchData(1466, 315606, 'Tupu (pin)', 'pin|copper alloy|metal-ornaments', 'ao/mobile-large/vs1987_394_546.JPG', 0.64), + SearchData(1450, 314615, 'Woven Sling Shot', 'sling shot|camelid hair|textiles-woven', 'ao/mobile-large/250581.jpg', 2.65), + SearchData(600, 315161, 'Disk Ornament', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/1987.394.99.JPG', 1.24), + SearchData(1500, 315290, 'Knife (Tumi)', 'knife|copper (cast)|metal-implements', 'ao/mobile-large/1987.394.230_a.jpg', 0.92), + SearchData(1475, 315486, 'Ornamented Knife (tumi)', 'knife|tin bronze|metal-implements', 'ao/mobile-large/DP108299.jpg', 1.03), + SearchData(-250, 308498, 'Bowl', 'bowl|ceramic, post-fired paint|ceramics-containers', 'ao/mobile-large/63.232.95a.jpg', 1.61), + SearchData(800, 309104, 'Tupu (pin)', 'pin|copper and gold|metal-ornaments', 'ao/mobile-large/hz64_228_606.jpg', 0.37), + SearchData(1499, 308287, 'Feathered Tabard', 'tabard|cotton, feathers|feathers-costumes', 'ao/mobile-large/DP216335.jpg', 0.83), + SearchData(1500, 315249, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_188.JPG', 0.50), + SearchData(-50, 310550, 'Bottle, Trophy-Head', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/vs1978_412_98.jpg', 0.77), + SearchData(600, 315147, 'Profile Warrior Ornament', 'ornament|gilded copper, shell|metal-ornaments', 'ao/mobile-large/1987.394.85.JPG', 0.84), + SearchData(600, 315119, 'Disk with Owl', 'ornament|gilded copper, silvered copper, shell, beads, fibers, organic pseudomorphs|metal-ornaments', 'ao/mobile-large/AOA49.jpg', 0.95), + SearchData(1700, 316850, 'Kero', 'kero|wood, tin studs, and pigmented resin inlays|wood-containers', 'ao/mobile-large/hz1994_35_22.jpg', 0.72), + SearchData(1500, 315333, 'Copper Lime Spoon with Feline Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_288.JPG', 0.45), + SearchData(1500, 315265, 'Copper Lime Spoon', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/vs1987_394_205a.jpg', 0.78), + SearchData(650, 308408, 'Bottle with fox head', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-23624-001.jpg', 0.98), + SearchData(1250, 309540, 'Figure Pendant', 'pendant|gold|metal-ornaments', 'ao/mobile-large/DP-17780-001.jpg', 1.33), + SearchData(1420, 316939, 'Miniature Tunic', 'miniature tunic|cotton, camelid hair|textiles-woven', 'ao/mobile-large/DP-13440-003.jpg', 1.27), + SearchData(1475, 309396, 'Miniature Jar with Two Handles', 'jar|ceramic|ceramics-containers', 'ao/mobile-large/66.30.13.jpg', 1.05), + SearchData(1500, 315622, 'Silver Pin', 'pin|silver (hammered), gold|metal-ornaments', 'ao/mobile-large/VS1987_394_562.JPG', 0.27), + SearchData(1400, 310205, 'Bottle, Anthropomorphic Crab', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/1976.287.14.JPG', 0.67), + SearchData(750, 316964, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_139.jpg', 1.24), + SearchData(1300, 698331, 'Pair of Earflares with Multifigure Scenes', 'earflare|gold|metal-ornaments', 'ao/mobile-large/DP370838.jpg', 1.82), + SearchData(500, 308415, 'Architectural Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-23896-001.jpg', 0.65), + SearchData(1500, 315303, 'Lime Spoon, Bird', 'lime spoon|silver (hammered)|metal-implements', 'ao/mobile-large/VS1987_394_243.JPG', 0.45), + SearchData(1700, 316849, 'Kero', 'kero|wood (prosopis?)|wood-containers', 'ao/mobile-large/hz1994_35_21.jpg', 0.81), + SearchData(1700, 698427, 'Pair of Keros with Carved Feline Handles', 'kero|wood, pigmented resin inlays|wood-containers', 'ao/mobile-large/DP104803.jpg', 1.16), + SearchData(1600, 318145, 'Tunic', 'tunic|camelid hair|textiles-woven', 'ao/mobile-large/DP137345.jpg', 1.99), + SearchData(1450, 309753, 'Stirrup Spout Bottle with Fish', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/1970.245.38.jpg', 0.67), + SearchData(700, 316978, 'Four-Cornered Hat', 'hat|camelid fiber|textiles-costumes', 'ao/mobile-large/DP212590.jpg', 0.76), + SearchData(1500, 315253, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_192.JPG', 0.61), + SearchData(1500, 317593, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/1994.35.760.jpg', 1.51), + SearchData(1500, 313223, 'Funerary Staff', 'tomb post|wood, metal, paint, silver, gold (?)|wood-sculpture', 'ao/mobile-large/DP-23804-001.jpg', 0.39), + SearchData(800, 309229, 'Tupu (pin)', 'pin|silver|metal-ornaments', 'ao/mobile-large/DP-13440-018.jpg', 1.69), + SearchData(1500, 313227, 'Funerary Staffs', 'tomb staff|wood|wood-sculpture', 'ao/mobile-large/DP-23811-001.jpg', 0.42), + SearchData(1475, 309398, 'Miniature Jar with Two Handles', 'jar|ceramic|ceramics-containers', 'ao/mobile-large/66.30.15.jpg', 1.12), + SearchData(1475, 316822, 'Feathered Tunic', 'tunic fragment|cotton, feathers|textiles-featherwork', 'ao/mobile-large/lb1993_474.jpg', 0.71), + SearchData(950, 309414, 'Beaker, Figure with Shell', 'beaker|gold|metal-containers', 'ao/mobile-large/66.196.27_a.JPG', 0.67), + SearchData(750, 316727, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/vs1994.35164a.jpg', 0.98), + SearchData(750, 316972, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_147.jpg', 1.09), + SearchData(1400, 318888, 'Headband', 'headband|camelid hair|textiles-woven', 'ao/mobile-large/DP-13440-001.jpg', 0.95), + SearchData(1550, 316930, 'Sling', 'sling|camelid hair|textiles-woven', 'ao/mobile-large/DP101341.jpg', 0.51), + SearchData(1550, 316920, 'Bag', 'bag|camelid hair|textiles-woven', 'ao/mobile-large/DP18714.jpg', 1.19), + SearchData(650, 313010, 'Figure with Ceremonial Objects', 'male figure|stone|stone-sculpture', 'ao/mobile-large/DP-23813-001.jpg', 0.51), + SearchData(1550, 316923, 'Bag', 'bag|cotton, camelid hair|textiles-woven', 'ao/mobile-large/DP101296.jpg', 0.94), + SearchData(1500, 317752, 'Lime Spoon, Bird', 'lime spoon|copper alloy (cast)|metal-implements', 'ao/mobile-large/DP-18884-002.jpg', 0.75), + SearchData(1250, 312669, 'Panel', 'panel|cotton, paint|textiles-woven', 'ao/mobile-large/1979_206_459_EX_01.jpg', 1.77), + SearchData(750, 316987, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_167.jpg', 1.06), + SearchData(750, 316343, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_153.jpg', 1.12), + SearchData(600, 315155, 'Stirrup Spout Bottle with Warrior', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/1987.394.93.JPG', 0.67), + SearchData(1450, 308770, 'Ceremonial Knife (Tumi)', 'knife|copper|metal-implements', 'ao/mobile-large/64.228.242.JPG', 1.26), + SearchData(1475, 316838, 'Kero', 'kero|wood|wood-containers', 'ao/mobile-large/vs1994_35_10.jpg', 0.75), + SearchData(1491, 316938, 'Serpent ornament', 'ornament|cotton, camelid hair|textiles-woven', 'ao/mobile-large/1994.35.113_d.JPG', 1.50), + SearchData(1500, 312726, 'Ornamental Knife', 'knife|copper|metal-implements', 'ao/mobile-large/1979.206.516.JPG', 0.67), + SearchData(1475, 310233, 'Blackware Paccha with Feline', 'paccha|ceramic|ceramics-containers', 'ao/mobile-large/1976.287.42.jpg', 1.23), + SearchData(1450, 318338, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/1999.367.1.jpg', 1.50), + SearchData(1700, 316851, 'Kero', 'kero|wood, metal stud and pigmented resin inlays|wood-containers', 'ao/mobile-large/hz1994_35_23.jpg', 0.68), + SearchData(1600, 320804, 'Hanging (?) Fragment', 'tapestry fragment|camelid hair, cotton|textiles-woven', 'ao/mobile-large/TR.489.2010_a.jpg', 0.52), + SearchData(1500, 310667, 'Tomb Post', 'tomb post|wood, silver sheathing, feathers|metal-sculpture', 'ao/mobile-large/vs1978.412.222.jpg', 0.28), + SearchData(750, 314621, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1983_497_4.jpg', 1.03), + SearchData(450, 314679, 'Fox Warrior Bottle', 'bottle|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/DP227399.jpg', 0.75), + SearchData(1500, 315254, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_193.JPG', 0.48), + SearchData(550, 309428, 'Ear Ornament, Winged Runner', 'earflare|gold, turquoise, sodalite, shell|metal-ornaments', 'ao/mobile-large/66.196.41.jpg', 0.90), + SearchData(750, 314620, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/hz1983_497_3.jpg', 1.15), + SearchData(800, 315686, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/250467.jpg', 0.90), + SearchData(1500, 315426, 'Copper Lime Spoon in Snake Form', 'lime spoon|copper (cast)|metal-ornaments', 'ao/mobile-large/VS1987_394_577.JPG', 0.27), + SearchData(1500, 315307, 'Ball', 'ball|brass (cast), copper|metal-implements', 'ao/mobile-large/vs1987_394_246_7.JPG', 0.72), + SearchData(1450, 318339, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/1999.367.2.jpg', 1.27), + SearchData(1500, 313056, 'Funerary Staffs', 'tomb staff|wood, paint|wood-sculpture', 'ao/mobile-large/DP-23810-001.jpg', 0.43), + SearchData(1500, 308123, 'Beaker with scroll ornamentation', 'beaker|silver, gold|metal-containers', 'ao/mobile-large/vs33_149_103.jpg', 0.55), + SearchData(1450, 314614, 'Cap Woven with Human Hair', 'hat|camelid hair, human hair|textiles-woven', 'ao/mobile-large/250580.jpg', 0.58), + SearchData(1600, 314616, 'Woven Sling Shot', 'sling shot|camelid hair|textiles-woven', 'ao/mobile-large/250582.jpg', 2.14), + SearchData(1500, 315487, 'Ornamented Knife', 'knife|copper|metal-implements', 'ao/mobile-large/hz1987_394_412.jpg', 1.18), + SearchData(1500, 313274, 'Male Effigy Vessel', 'vessel|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1979.206.1081_a.jpg', 0.71), + SearchData(1465, 319574, 'Tunic', 'tunic|camelid fiber|textiles-woven', 'ao/mobile-large/DP120795.jpg', 0.95), + SearchData(1400, 315610, 'Group of pins', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/DP219006.jpg', 0.67), + SearchData(1500, 315284, 'Copper Pin', 'pin|copper, gilt|metal-ornaments', 'ao/mobile-large/vs1987_394_224.JPG', 0.33), + SearchData(500, 309438, 'Bird Warrior Bottle', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/67.167.2.jpg', 0.67), + SearchData(1466, 309943, 'Male figurine', 'figure|gold-silver alloy|sculpture-sheet metal', 'ao/mobile-large/DP-13440-014.jpg', 0.76), + SearchData(1530, 313273, 'Ear Spool', 'ear spool|gold, silver|metal-ornaments', 'ao/mobile-large/vs1979_206_1079_80.jpg', 1.84), + SearchData(1650, 316848, 'Kero', 'kero|wood (prosopis?), copper/silver alloy inlay|wood-containers', 'ao/mobile-large/DT239579.jpg', 0.80), + SearchData(1500, 308083, 'Tapestry Fragment', 'textile fragment|camelid hair|textiles-woven', 'ao/mobile-large/DP101353.jpg', 1.31), + SearchData(1491, 316929, 'Sling', 'sling|camelid hair|textiles-woven', 'ao/mobile-large/DP101340.jpg', 0.72), + SearchData(600, 315133, 'Profile Warrior Ornament', 'ornament|gilded copper, shell turquoise|metal-ornaments', 'ao/mobile-large/1987.394.70.JPG', 0.76), + SearchData(1500, 315635, 'Figure Lime Spoon', 'lime spoon|copper|metal-implements', 'ao/mobile-large/1987.394.586.jpg', 0.67), + SearchData(1500, 310702, 'Storage Jar (aryballos)', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/vs1978_412_258.jpg', 0.79), + SearchData(1500, 313053, 'Funerary Staffs', 'tomb staff|wood, paint, metal|wood-sculpture', 'ao/mobile-large/DP-23809-001.jpg', 0.28), + SearchData(1500, 315425, 'Lime Spoon', 'lime spoon|silver (cast)|metal-ornaments', 'ao/mobile-large/VS1987_394_576.JPG', 0.38), + SearchData(1049, 309959, 'Funerary Mask', 'mask|gold, silver-copper overlays, cinnabar|metal-sculpture', 'ao/mobile-large/DT1274.jpg', 1.25), + SearchData(200, 314874, 'Cutout Disk, Crab and Fish', 'disk|gilded copper|metal-ornaments', 'ao/mobile-large/DT5791.jpg', 0.80), + SearchData(1500, 315260, 'Pin', 'pin|silver (cast)|metal-ornaments', 'ao/mobile-large/vs1987_394_200.JPG', 0.40), + SearchData(1650, 320054, 'Miniature Tabard', 'tunic|cotton, camelid hair, silk, metal|textiles-woven', 'ao/mobile-large/DP107700.jpg', 0.79), + SearchData(600, 315191, 'Disc', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/1987.394.129.JPG', 1.30), + SearchData(1250, 319053, 'Collar', 'collar|spondylus shell and black stone beads, cotton|beads-costumes', 'ao/mobile-large/DP216742.jpg', 1.37), + SearchData(1500, 315354, 'Ceremonial Knife (Tumi)', 'knife|copper (cast), silver (?)|metal-implements', 'ao/mobile-large/vs1987_394_309.jpg', 0.93), + SearchData(1500, 315251, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_190.JPG', 0.38), + SearchData(750, 316985, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_165.jpg', 1.02), + SearchData(400, 310601, 'Ritual Scene Vessel', 'vessel|ceramic|ceramics-containers', 'ao/mobile-large/DP-24357-001.jpg', 1.33), + SearchData(1475, 316837, 'Kero', 'kero|wood (prosopis ?)|wood-containers', 'ao/mobile-large/DP104801.jpg', 0.83), + SearchData(500, 309304, 'Stirrup Spout Bottle with Cat', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/65.266.6.JPG', 0.67), + SearchData(1500, 315248, 'Copper Pin', 'pin|copper (cast)|metal-ornaments', 'ao/mobile-large/vs1987_394_187.JPG', 0.34), + SearchData(1500, 315690, 'Bag', 'bag|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP18706_1987.394.642.jpg', 0.82), + SearchData(1466, 312558, 'Female Figurine', 'figure|silver|sculpture-sheet metal', 'ao/mobile-large/DP-13440-027.jpg', 0.67), + SearchData(500, 308527, 'Portrait Head Bottle', 'bottle|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/DP-23630-001.jpg', 0.75), + SearchData(-500, 308426, 'Bottle, Feline face', 'bottle|ceramic, post-fired paint|ceramics-containers', 'ao/mobile-large/63.232.9.jpg', 0.75), + SearchData(1500, 315708, 'Band Fragment', 'textile fragment|cotton, camelid hair|textiles-woven', 'ao/mobile-large/DP18703.jpg', 0.35), + SearchData(1500, 314954, 'Paccha (ritual vessel)', 'vessel|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-23632-001.jpg', 1.06), + SearchData(1500, 312636, 'Ornamental Pin (Tupu)', 'pin|bronze|metal-ornaments', 'ao/mobile-large/vs1979_206_416aa.jpg', 0.38), + SearchData(1850, 29313, 'Pair of Stirrups', 'stirrups|wood, iron|equestrian equipment-stirrups', 'aa/mobile-large/LC-42_50_440_441-007.jpg', 1.50), + SearchData(750, 316968, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_143.jpg', 1.13), + SearchData(750, 312672, 'Mantle', 'mantle|cotton, camelid hair|textiles-woven', 'ao/mobile-large/DP264884.jpg', 1.02), + SearchData(1500, 315242, 'Ceremonial Knife (Tumi)', 'knife|copper (cast)|metal-implements', 'ao/mobile-large/1987.394.181.jpg', 0.67), + SearchData(600, 313398, 'Nose Ornament, Turbaned Head', 'ornament|gold (partially silvered), silver|metal-ornaments', 'ao/mobile-large/DP-16100-001.jpg', 1.33), + SearchData(1200, 307471, 'Border Fragment', 'border fragment|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP101363.jpg', 1.12), + SearchData(1500, 313054, 'Funerary Staffs', 'tomb staff|wood, paint|wood-sculpture', 'ao/mobile-large/DP-22195-004.jpg', 0.65), + SearchData(550, 316983, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_163.jpg', 0.90), + SearchData(1515, 313324, 'Sleeveless Tunic', 'tunic|cotton, camelid hair|textiles-woven', 'ao/mobile-large/1979.206.1131_a.jpg', 0.77), + SearchData(550, 319459, 'Pair of Ear Ornaments with Winged Runners', 'earflare|gold, turquoise, sodalite, shell|metal-ornaments', 'ao/mobile-large/DP-10734-01.jpg', 1.35), + SearchData(1500, 315338, 'Knife (?)', 'knife|copper|metal-implements', 'ao/mobile-large/vs1987_394_293.jpg', 1.29), + SearchData(1410, 316437, 'Earflare with Multifigure Scene', 'earflare|gold|metal-ornaments', 'ao/mobile-large/DP370837.jpg', 0.99), + SearchData(1500, 315613, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_553.JPG', 0.39), + SearchData(1475, 316836, 'Kero', 'kero|wood (prosopis ?)|wood-containers', 'ao/mobile-large/hz1994_35_8.jpg', 0.77), + SearchData(-550, 307622, 'Double Spout and Bridge Bottle', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/64.228.97_a.jpg', 1.09), + SearchData(1500, 313267, 'Kero', 'kero|wood|wood-containers', 'ao/mobile-large/1979.206.1074.jpg', 1.00), + SearchData(1500, 308120, 'Eight-Pointed Star Tunic', 'tunic|camelid hair, cotton|textiles-woven', 'ao/mobile-large/ra33.149.100.R.jpg', 0.81), + SearchData(1462, 319524, 'Fragmentary Woman\'s Dress', 'dress fragment|camelid fiber|textiles-woven', 'ao/mobile-large/2004.406_detail.jpg', 0.67), + SearchData(600, 313411, 'Nose Ornament with Shrimp', 'nose ornament|gold, silver, stone|metal-ornaments', 'ao/mobile-large/DT9425.jpg', 1.25), + SearchData(450, 318746, 'Belt', 'belt|camelid fiber|textiles-woven', 'ao/mobile-large/2001.172_b.jpg', 0.38), + SearchData(500, 309441, 'Raptorial Bird Bottle', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/67.167.6.JPG', 0.67), + SearchData(850, 316976, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_156.jpg', 1.24), + SearchData(650, 316979, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/DT3833.jpg', 0.80), + SearchData(1500, 315653, 'Copper Tumi with Figure', 'knife|copper|metal-implements', 'ao/mobile-large/vs1987_394_605.jpg', 1.02), + SearchData(550, 308383, 'Spectacled Bear Bottle', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/63.112.5.JPG', 0.67), + SearchData(1475, 317726, 'Tumi with Figure', 'knife|copper|metal-implements', 'ao/mobile-large/VS1995_63_1.JPG', 1.06), + SearchData(1399, 310619, 'Bottle, Audiencias with Figures', 'vessel|silver|metal-containers', 'ao/mobile-large/DP338094.jpg', 1.33), + SearchData(800, 316975, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_155.jpg', 1.10), + SearchData(1700, 316892, 'Woman\'s Mantle (lliclla)', 'mantle|camelid hair|textiles-woven', 'ao/mobile-large/DT3832rev.jpg', 1.25), + SearchData(1475, 316834, 'Paccha', 'paccha|wood|wood-containers', 'ao/mobile-large/vs1994_35_6.jpg', 1.52), + SearchData(0, 308455, 'Bottle, Monkey', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP234684.jpg', 1.13), + SearchData(600, 314537, 'Disk', 'ornament|gilded copper, silvered copper, shell inlay|metal-ornaments', 'ao/mobile-large/1982.392.8.jpg', 1.50), + SearchData(1500, 315636, 'Lime Spoon, Figure', 'lime spoon|copper|metal-implements', 'ao/mobile-large/1987.394.587.jpg', 0.67), + SearchData(300, 316270, 'Corn stalk-shaped vessel', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-23629-001.jpg', 0.75), + SearchData(650, 844007, 'Warrior Figure', 'figure|turquoise|stone-sculpture', 'ao/mobile-large/DP-20565-001.jpg', 0.85), + SearchData(1450, 316928, 'Sling', 'sling|camelid hair|textiles-woven', 'ao/mobile-large/vs1994.35.103a.jpg', 1.03), + SearchData(1500, 315268, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper|metal-implements', 'ao/mobile-large/VS1987_394_208.jpg', 0.36), + SearchData(700, 310308, 'Prisoner Lime Container', 'lime container|wood, bone inlay, paint, fiber|wood-containers', 'ao/mobile-large/DP270994.jpg', 0.90), + SearchData(800, 309106, 'Tupu (pin)', 'pin|copper and gold|metal-ornaments', 'ao/mobile-large/hz64_228_608.jpg', 0.40), + SearchData(850, 314624, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1983_497_7.jpg', 1.13), + SearchData(350, 308507, 'Bottle with Runners', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/64.228.1.JPG', 0.67), + SearchData(100, 314776, 'Ornamental Plume', 'ornament|gold|metal-ornaments', 'ao/mobile-large/DT7665.jpg', 0.57), + SearchData(1450, 315363, 'Face Beaker', 'beaker|silver|metal-containers', 'ao/mobile-large/DT5795.jpg', 0.61), + SearchData(1750, 316852, 'Kero', 'kero|wood (escallonia?), pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_24.jpg', 1.02), + SearchData(800, 316982, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_162.jpg', 1.01), + SearchData(1500, 315804, 'Copper Tumi with Figure', 'knife|copper|metal-implements', 'ao/mobile-large/vs1987_394_723.jpg', 0.89), + SearchData(1849, 126673, 'Mantle pin (ttipqui)', 'pin|silver, glass|metal-ornaments', 'ao/mobile-large/1982.420.13.jpg', 0.25), + SearchData(750, 316977, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_157.jpg', 1.16), + SearchData(1500, 315266, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_206.JPG', 0.87), + SearchData(1400, 315611, 'Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_551.JPG', 0.70), + SearchData(1500, 315306, 'Ball', 'ball|brass (cast)|metal-implements', 'ao/mobile-large/vs1987_394_246_7.JPG', 0.72), + SearchData(1500, 319536, 'Seated Figure Ornament with Dangles', 'ornament|silver and thread|metal-ornaments', 'ao/mobile-large/vs565.jpg', 0.71), + SearchData(1420, 317727, 'Feline Bowl', 'bowl|stone|stone-containers', 'ao/mobile-large/1995.63.2_a.jpg', 1.47), + SearchData(1500, 315633, 'Figure Lime Spoon', 'lime spoon|copper|metal-implements', 'ao/mobile-large/1987.394.584.jpg', 0.67), + SearchData(1500, 317592, 'Votive Container (Canopa)', 'vessel|stone|stone-containers', 'ao/mobile-large/1994.35.759.jpg', 1.50), + SearchData(750, 316340, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_150.jpg', 1.04), + SearchData(1500, 309528, 'Band', 'band|camelid hair|textiles-woven', 'ao/mobile-large/189514.jpg', 2.68), + SearchData(1475, 309393, 'Miniature Vessel', 'miniature dish|ceramic|ceramics-containers', 'ao/mobile-large/DP-25093-001.jpg', 2.15), + SearchData(1530, 313272, 'Ear Spool', 'ear spool|gold, silver|metal-ornaments', 'ao/mobile-large/vs1979_206_1079_80.jpg', 1.84), + SearchData(1505, 502538, 'Whistling Jar', 'whistling jar|clay|aerophone-blow hole-vessel flute', 'mi/mobile-large/188965.jpg', 0.91), + SearchData(1500, 317594, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/1994.35.761.jpg', 1.42), + SearchData(1600, 214310, 'Woman\'s wedding mantle (lliclla) with interlace and tocapu design', 'mantle|tapestry weave, cotton warp and camelid weft|', 'ad/mobile-large/DP265667.jpg', 1.12), + SearchData(-650, 310655, 'Bottle: Leaf-Nosed Bat Head', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/DP-24265-003.jpg', 0.78), + SearchData(1500, 313224, 'Funerary Staffs', 'tomb post|wood, paint, metal|wood-sculpture', 'ao/mobile-large/DP-23802-001.jpg', 0.36), + SearchData(1500, 315252, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_191.JPG', 0.42), + SearchData(600, 315148, 'Profile Warrior Ornament', 'ornament|gilded copper, shell, turquoise|metal-ornaments', 'ao/mobile-large/1987.394.86.JPG', 0.88), + SearchData(1500, 315667, 'Copper Atlatl Spear Thrower', 'spear thrower|copper (cast)|metal-implements', 'ao/mobile-large/vs1987_394_619.JPG', 1.33), + SearchData(700, 314623, 'Four-Cornered Hat', 'hat|camelid fiber|textiles-costumes', 'ao/mobile-large/DP264888.jpg', 0.83), + SearchData(1500, 310668, 'Tomb Post', 'tomb post|wood, silver sheathing, feathers|metal-sculpture', 'ao/mobile-large/vs1978.412.223.jpg', 0.27), + SearchData(1466, 309960, 'Camelid figurine', 'figure|alloys of silver, gold and copper|metalwork-sculpture', 'ao/mobile-large/DP-13440-031.jpg', 0.76), + SearchData(1250, 313148, 'Weaving Basket', 'basket with weaving implements|cane, shell, bone, fiber, camelid hair, wood, pigment, metal, stone|textiles-implements', 'ao/mobile-large/1979.206.950.1_a.JPG', 1.50), + SearchData(1550, 316840, 'Kero', 'kero|wood, pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_12.jpg', 0.75), + SearchData(1500, 319319, 'Kero', 'kero|ceramic|ceramics-containers', 'ao/mobile-large/hzTR355_1_2003.jpg', 0.86), + SearchData(600, 314878, 'Cutout Disk', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/1987.394.50.JPG', 1.22), + SearchData(1500, 313204, 'Dish with Bird Head', 'dish|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1979.206.1007.jpg', 1.58), + SearchData(1500, 315771, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/vs1987.394.691.jpg', 1.17), + SearchData(350, 308510, 'Bottle, Warriors', 'bottle|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/64.228.4.JPG', 0.67), + SearchData(750, 316989, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_169.jpg', 1.12), + SearchData(1150, 320329, 'Monkey Pendant', 'pendant|gold|metal-ornaments', 'ao/mobile-large/TR.394.18.2008 Back.jpg', 2.21), + SearchData(1500, 315639, 'Silver Lime Spoon with Fish Top', 'lime spoon|silver (cast)|metal-implements', 'ao/mobile-large/vs1987_394_590.jpg', 0.48), + SearchData(550, 316984, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/1994.35.164_b.jpg', 0.74), + SearchData(400, 308540, 'Stirrup Spout Bottle with Seated Figure', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/LC-64_228_34_TMS3.jpg', 0.66), + SearchData(1200, 312826, 'Miniature Dress', 'miniature dress|cotton, feathers|textiles-featherwork', 'ao/mobile-large/DP224272.jpg', 1.33), + SearchData(1650, 316853, 'Kero', 'kero|wood (escallonia), pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_25.jpg', 1.36), + SearchData(50, 313364, 'Nose Ornament with Spiders', 'nose ornament|gold|metal-ornaments', 'ao/mobile-large/DP313281.jpg', 1.33), + SearchData(650, 308410, 'Stirrup spout bottle with warrior', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-23622-001.jpg', 0.75), + SearchData(1400, 308555, 'Double Chambered Bottle', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/64.228.54_b.jpg', 1.05), + SearchData(1466, 315668, 'Tupu (pin)', 'pin|copper or alloy of copper|metal-ornaments', 'ao/mobile-large/DP-13440-020.jpg', 1.69), + SearchData(1500, 315294, 'Mace Head', 'mace head|silver (cast)|metal-implements', 'ao/mobile-large/DP-20709-001.jpg', 1.09), + SearchData(1238, 646249, 'Man\'s Tunic', 'tunic|camelid and cotton fibers|textiles-costumes', 'ao/mobile-large/TR.113.2014_image_001.jpg', 0.89), + SearchData(1549, 751901, 'Checkerboard Tunic', 'tunic|camelid fiber|textiles-costumes', 'ao/mobile-large/DP-14281-001.jpg', 0.88), + SearchData(1475, 317791, 'Kero', 'kero|wood (prosopis?)|wood-containers', 'ao/mobile-large/DP104877.jpg', 1.01), + SearchData(1500, 313289, 'Vessel, Hand with Kero', 'kero|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1979.206.1096_b.jpg', 1.40), + SearchData(1475, 309399, 'Miniature Jar', 'jar|ceramic|ceramics-containers', 'ao/mobile-large/66.30.16.jpg', 1.09), + SearchData(750, 316969, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_144.jpg', 1.07), + SearchData(1462, 319487, 'Kero', 'kero|wood|wood-containers', 'ao/mobile-large/DP-13440-026.jpg', 0.86), + SearchData(1500, 315476, 'Axe', 'axe|bronze|metal-implements', 'ao/mobile-large/vs1987_394_401.JPG', 0.45), + SearchData(1550, 316922, 'Bag', 'bag|cotton, camelid hair|textiles-woven', 'ao/mobile-large/DP101295.jpg', 0.93), + SearchData(1420, 313295, 'Face Beaker', 'beaker|silver|metal-containers', 'ao/mobile-large/DT9410.jpg', 0.80), + SearchData(1600, 314617, 'Woven Sling Shot', 'sling shot|camelid hair|textiles-woven', 'ao/mobile-large/250583.jpg', 2.00), + SearchData(750, 833953, 'Tapestry Tunic', 'tunic|camelid wool|textiles-woven', 'ao/mobile-large/DP-18736-001.jpg', 0.95), + SearchData(850, 316974, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_154.jpg', 1.08), + SearchData(750, 316967, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_142.jpg', 1.04), + SearchData(1500, 315773, 'Sling Shot', 'sling shot|camelid hair|textiles-woven', 'ao/mobile-large/250471.jpg', 2.17), + SearchData(650, 307472, 'Bottle with portrait head', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-24066-002.jpg', 0.75), + SearchData(1500, 315637, 'Figure Lime Spoon', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/1987.394.588.jpg', 0.67), + SearchData(1500, 315619, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_559.JPG', 0.64), + SearchData(1500, 314955, 'Paccha (ritual vessel)', 'vessel|ceramic, slip|ceramics-containers', 'ao/mobile-large/vs1986_383_2.jpg', 1.22), + SearchData(-400, 308487, 'Double Spouted Vessel with Snake', 'bottle|ceramic, post-fired paint|ceramics-containers', 'ao/mobile-large/63.232.84.jpg', 1.40), + SearchData(1500, 315770, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/1987.394.690.jpg', 1.31), + SearchData(500, 309447, 'Spotted Feline Bottle', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/67.167.12.JPG', 0.67), + SearchData(-350, 308590, 'Double Spout and Bridge Bottle with Snake', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/64.228.89_a.jpg', 1.17), + SearchData(1500, 309760, 'Painted Aryballus Jar', 'bottle|ceramic, pigment, slip|ceramics-containers', 'ao/mobile-large/1970.246.7.jpg', 0.84), + SearchData(-250, 308491, 'Miniature Effigy Vessel', 'bottle|ceramic, post-fired paint|ceramics-containers', 'ao/mobile-large/63.232.88.jpg', 1.02), + SearchData(1500, 315255, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_194.JPG', 0.42), + SearchData(250, 308537, 'Stirrup Spout Bottle with Warrior Figure', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/64.228.31.jpg', 0.67), + SearchData(1410, 316436, 'Earflare with Multifigure Scene', 'earflare|gold|metal-ornaments', 'ao/mobile-large/DP370836.jpg', 0.99), + SearchData(750, 316981, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_161.jpg', 0.99), + SearchData(1466, 315492, 'Male Figurine', 'figure|silver|sculpture-sheet metal', 'ao/mobile-large/DP-13440-021.jpg', 0.68), + SearchData(1500, 307846, 'Panel with Birds', 'panel|camelid hair|textiles-woven', 'ao/mobile-large/88392.jpg', 1.07), + SearchData(75, 319223, 'Neck Emblem or Sash', 'emblem or sash|camelid and human hair|textiles-woven', 'ao/mobile-large/DT5611.jpg', 0.28), + SearchData(800, 309228, 'Tupu', 'pin|silver|metal-ornaments', 'ao/mobile-large/DP-13440-016.jpg', 1.69), + SearchData(1500, 315638, 'Figure Lime Spoon', 'lime spoon|copper|metal-implements', 'ao/mobile-large/1987.394.589.jpg', 0.67), + SearchData(1500, 315701, 'Vessel, Leg', 'vessel|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1987.394.653_a.jpg', 0.67), + SearchData(1600, 316844, 'Kero', 'kero|wood, pigmented resin inlay|wood-containers', 'ao/mobile-large/hz1994_35_16.jpg', 0.70), + SearchData(1500, 308079, 'Tapestry Panel with Stars', 'panel|camelid hair, cotton|textiles-woven', 'ao/mobile-large/DP18702_33.149.44.jpg', 0.71), + SearchData(1300, 312568, 'Standing Female Figure', 'figure|wood, pigment|wood-sculpture', 'ao/mobile-large/DP-23635-002.jpg', 0.52), + SearchData(1500, 315614, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_554.JPG', 0.64), + SearchData(1600, 698417, 'Pair of Keros', 'kero|wood, pigmented resin inlay|wood-containers', 'ao/mobile-large/DP104802.jpg', 1.06), + SearchData(1500, 315250, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_189.JPG', 0.49), + SearchData(1650, 318164, 'Poncho', 'poncho|wool|textiles-woven', 'ao/mobile-large/DP137349.jpg', 1.76), + SearchData(400, 308732, 'Figure of an Owl', 'figure|bone, cinnabar|bone/ivory-sculpture', 'ao/mobile-large/DP224140.jpg', 0.75), + SearchData(1500, 313055, 'Funerary Staff', 'tomb staff|wood, paint, metal, copper|wood-sculpture', 'ao/mobile-large/DP-23806-001.jpg', 0.32), + SearchData(1475, 309391, 'Miniature Vessel', 'miniature dish|ceramic|ceramics-containers', 'ao/mobile-large/DP-24276-001.jpg', 1.11), + SearchData(-200, 308298, 'Bottle, Falcon', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/62.266.8.jpg', 0.93), + SearchData(1500, 307475, 'Knife (Tumi)', 'knife|copper|metal-implements', 'ao/mobile-large/82.1.31_a.JPG', 1.50), + SearchData(1500, 315479, 'Knife', 'knife|copper|metal-implements', 'ao/mobile-large/vs1987_394_404.jpg', 0.37), + SearchData(1500, 315616, 'Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_556.JPG', 0.62), + SearchData(650, 312940, 'Bird Pin or Spatula', 'lime spatula or pin|gold|metal-implements', 'ao/mobile-large/271370.jpg', 1.30), + SearchData(500, 309439, 'Warrior Bottle', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/DP229470.jpg', 0.75), + SearchData(750, 316971, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_146.jpg', 1.12), + SearchData(1575, 316842, 'Kero', 'kero|wood (escallonia ?), pigmented resin inlay|wood-containers', 'ao/mobile-large/DP104799.jpg', 0.97), + SearchData(1475, 316839, 'Kero', 'kero|wood (escallonia ?)|wood-containers', 'ao/mobile-large/DP104864.jpg', 0.94), + SearchData(1500, 315615, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_555.JPG', 0.82), + SearchData(600, 315135, 'Profile Warrior Ornament', 'ornament|copper, shell|metal-ornaments', 'ao/mobile-large/1987.394.73.JPG', 0.74), + SearchData(1466, 315651, 'Tupu (pin)', 'pin|copper|metal-ornaments', 'ao/mobile-large/VS1987_394_603.JPG', 0.58), + SearchData(1475, 310520, 'Small urpu (Jar)', 'bottle|ceramic, slip|ceramics-containers', 'ao/mobile-large/DP-24355-001.jpg', 0.75), + SearchData(1500, 315612, 'Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_552.JPG', 0.44), + SearchData(650, 315136, 'Profile Warrior Ornament', 'ornament|silvered copper, shell|metal-ornaments', 'ao/mobile-large/1987.394.74.JPG', 0.71), + SearchData(750, 316966, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_141.jpg', 1.06), + SearchData(1500, 315623, 'Silver Pin', 'pin|silver|metal-ornaments', 'ao/mobile-large/VS1987_394_563.jpg', 0.39), + SearchData(1475, 316926, 'Feathered Bag', 'bag|cotton, feathers|textiles-featherwork', 'ao/mobile-large/DP158704.jpg', 0.75), + SearchData(600, 314705, 'Cutout Disk', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/1987.394.35.JPG', 1.50), + SearchData(1650, 316854, 'Kero', 'kero|wood (escallonia), pigmented resin inlay|wood-containers', 'ao/mobile-large/1994.35.26_a.JPG', 0.67), + SearchData(1350, 313268, 'Lime Spoon with Seated Figure', 'lime spoon|silver (cast)|metal-implements', 'ao/mobile-large/vs1979_206_1075.jpg', 0.29), + SearchData(1000, 309123, 'Crown', 'crown|gold|metal-ornaments', 'ao/mobile-large/DP-12423-001.jpg', 0.88), + SearchData(1475, 309392, 'Miniature Dish with Handle', 'dish|ceramic|ceramics-containers', 'ao/mobile-large/DP-13440-005.jpg', 1.82), + SearchData(750, 316342, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_152.jpg', 1.17), + SearchData(1475, 309395, 'Miniature Vessel', 'miniature bowl|ceramic|ceramics-containers', 'ao/mobile-large/DP-13440-010.jpg', 0.99), + SearchData(600, 313414, 'Nose Ornament', 'nose ornament|gold|metal-ornaments', 'ao/mobile-large/vs1979_206_1239.jpg', 1.64), + SearchData(1466, 309944, 'Female Figurine', 'figure|gold-rich silver alloy|sculpture-sheet metal', 'ao/mobile-large/DP-13440-023.jpg', 0.69), + SearchData(1500, 310656, 'Hunchback Paccha', 'paccha|wood, beads|wood-containers', 'ao/mobile-large/vs1978_412_211.jpg', 0.76), + SearchData(750, 314625, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1983_497_8.jpg', 1.17), + SearchData(600, 314872, 'Cutout Disk', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/1987.394.34.jpg', 1.50), + SearchData(1475, 309401, 'Single Spout Bottle with Strap Handles', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/66.30.18_a.jpg', 1.29), + SearchData(550, 316913, 'Coca Bag', 'bag|camelid hair, cotton|textiles-woven', 'ao/mobile-large/LC-1994_35_88_TMS.jpg', 0.66), + SearchData(600, 315174, 'Disk Ornament', 'ornament|gilded copper|metal-ornaments', 'ao/mobile-large/DP-16099-001.jpg', 1.33), + SearchData(750, 316988, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_168.jpg', 1.18), + SearchData(750, 316341, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_151.jpg', 1.13), + SearchData(1050, 316986, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1994_35_166.jpg', 1.19), + SearchData(1475, 314823, 'Votive Container (Canopa)', 'container|stone|stone-containers', 'ao/mobile-large/vs1984_524_1.jpg', 1.40), + SearchData(1350, 315748, 'Sling shot', 'sling shot|silver (hammered), feathers|metal-implements', 'ao/mobile-large/SlingShot_1987_394_668_1.jpg', 1.11), + SearchData(500, 309436, 'Stirrup Spout Bottle with Felines', 'bottle|ceramic, pigment|ceramics-containers', 'ao/mobile-large/67.92.jpg', 0.67), + SearchData(750, 312244, 'Feathered Panel', 'panel|feathers, cotton, camelid hair|textiles-featherwork', 'ao/mobile-large/DP-15008-019.jpg', 2.24), + SearchData(1500, 307449, 'Bag', 'bag|cotton|textiles-woven', 'ao/mobile-large/50407.jpg', 0.72), + SearchData(1500, 315332, 'Lime Spoon, Bird', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_287.JPG', 0.57), + SearchData(1543, 751900, 'Votive Checkerboard Tunic', 'tunic|camelid fiber|textiles-costumes', 'ao/mobile-large/DP-14285-001.jpg', 0.84), + SearchData(1475, 309394, 'Storage Jar (Aryballus)', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/DP-13440-012.jpg', 1.00), + SearchData(250, 308539, 'Stirrup Spout Bottle with Sleeping Warrior', 'bottle|ceramic, slip, pigment|ceramics-containers', 'ao/mobile-large/64.228.33.JPG', 0.67), + SearchData(1650, 307469, 'Knife, Figures', 'knife|bronze|metal-implements', 'ao/mobile-large/82.1.25_a.JPG', 0.67), + SearchData(1500, 317751, 'Paccha', 'paccha|ceramic, pigment|ceramics-containers', 'ao/mobile-large/1995.481.3_b.jpg', 1.99), + SearchData(750, 314622, 'Four-Cornered Hat', 'hat|camelid hair|textiles-costumes', 'ao/mobile-large/HZ1983_497_5.jpg', 1.19), + SearchData(1500, 315267, 'Copper Lime Spoon with Bird Top', 'lime spoon|copper (cast)|metal-implements', 'ao/mobile-large/VS1987_394_207.jpg', 0.44), + SearchData(1000, 316431, 'Beaker with Figure Displaying a Shell', 'beaker|gold|metal-containers', 'ao/mobile-large/DP215719.jpg', 0.80), + SearchData(1385, 312952, 'Standing Male Figure', 'male figure|wood|wood-sculpture', 'ao/mobile-large/DP-24345-001.jpg', 0.52), + SearchData(600, 315134, 'Profile Warrior Ornament', 'ornament|gilded copper, shell|metal-ornaments', 'ao/mobile-large/1987.394.71.JPG', 0.67), + SearchData(1400, 310566, 'Copper Knife with Figure Handle', 'knife|copper|metal-implements', 'ao/mobile-large/1978.412.114_a.JPG', 1.50), + SearchData(1500, 315634, 'Figure Lime Spoon', 'lime spoon|copper|metal-implements', 'ao/mobile-large/1987.394.585.jpg', 0.67), + SearchData(1475, 316835, 'Paccha', 'paccha|wood|wood-containers', 'ao/mobile-large/1994.35.7.jpg', 0.64), + SearchData(1500, 315617, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/vs1987_394_557.JPG', 0.50), + SearchData(1475, 309390, 'Miniature Vessel', 'miniature dish|ceramic|ceramics-containers', 'ao/mobile-large/DP-24277-001.jpg', 1.09), + SearchData(1350, 312801, 'Plume', 'plume|gold|metal-ornaments', 'ao/mobile-large/rlf1979.206.605b.jpg', 0.75), + SearchData(1425, 317736, 'Woman\'s Dress', 'dress|camelid hair|textiles-woven', 'ao/mobile-large/DT1275.jpg', 1.25), + SearchData(1500, 313310, 'Single Spout Bottle', 'bottle|ceramic|ceramics-containers', 'ao/mobile-large/1979.206.1117.jpg', 0.80), + SearchData(1475, 309397, 'Miniature Jar with Two Handles', 'jar|ceramic|ceramics-containers', 'ao/mobile-large/66.30.14.jpg', 1.09), + SearchData(1500, 315621, 'Silver Pin', 'pin|silver (hammered)|metal-ornaments', 'ao/mobile-large/VS1987_394_561.JPG', 0.37), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/petra_search_data.dart b/lib/logic/data/wonders_data/search/petra_search_data.dart new file mode 100644 index 00000000..ba9a35ae --- /dev/null +++ b/lib/logic/data/wonders_data/search/petra_search_data.dart @@ -0,0 +1,344 @@ +part of '../petra_data.dart'; + +// Search suggestions (67) +List _searchSuggestions = const ['stamp', 'canaanite', 'figure', 'membranophone', 'seal', 'copper', 'molded', 'brass', 'dagger', 'alloy', 'silver', 'slip', 'animal', 'stone', 'sculpture', 'opaque', 'earthenware', 'incised', 'ink', 'jambiya', 'paper', 'ceramics', 'jasper', 'metal', 'unguentarium', 'stonepaste', 'ceramic', 'leather', 'green', 'folio', 'glazed', 'sherd', 'vessel', 'white', 'chordophone', 'fragment', 'book', 'lute', 'decoration', 'headed', 'female', 'codices', 'illustrated', 'scaraboid', 'drum', 'shahnama', 'jewelry', 'bronze', 'steatite', 'porcelain', 'plucked', 'wood', 'cultic', 'kings', 'painted', 'gold', 'watercolor', 'quartz', 'scarab', 'steel', 'manuscript', 'daggers', 'splash', 'head', 'sheath', 'bowl', 'scene', ]; + +// Petra (336) +List _searchData = const [ + SearchData(-1630, 324559, 'Scarab seal', 'stamp seal|stone, white|', 'an/mobile-large/ss56_152_5.jpg', 1.39), + SearchData(899, 451503, 'Fragment of a Bowl', 'bowl fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-6b.jpg', 1.22), + SearchData(50, 325916, 'Plate', 'plate|ceramic|', 'an/mobile-large/ME67_246_35.jpg', 1.54), + SearchData(-1450, 328935, 'Canaanite jar', 'jar|ceramic|', 'an/mobile-large/DP-16796-001.jpg', 0.77), + SearchData(1850, 31539, 'Dagger (Jambiya) with Sheath', 'dagger (jambiya) with sheath|steel, wood, brass, silver, gold, copper, brass wire|daggers', 'aa/mobile-large/36.25.947ab_002july2014.jpg', 0.67), + SearchData(849, 451547, 'Fragment', 'fragment|earthenware; slip painted, glazed|ceramics', 'is/mobile-large/sf60-23-52a.jpg', 1.63), + SearchData(1750, 31745, 'Dagger (Jambiya)', 'dagger (jambiya)|steel, wood, silver, copper, brass|daggers', 'aa/mobile-large/36.25.765_001june2014.jpg', 0.56), + SearchData(849, 451535, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-39.jpg', 0.76), + SearchData(-1500, 325125, 'Adze head', 'adze|bronze|', 'an/mobile-large/ME61_73_2.jpg', 1.63), + SearchData(1049, 451602, 'Fragment', 'fragment|earthenware; incised and glazed|ceramics', 'is/mobile-large/sf60-23-109a.jpg', 0.81), + SearchData(899, 451533, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-37a.jpg', 1.60), + SearchData(1199, 451513, 'Fragment', 'fragment|stonepaste; underglaze painted|ceramics', 'is/mobile-large/sf60-23-16a.jpg', 1.62), + SearchData(937, 451509, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-12.jpg', 1.26), + SearchData(1399, 451554, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-59a.jpg', 1.74), + SearchData(849, 455152, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-25a.jpg', 1.13), + SearchData(1849, 443096, 'Headband', 'headband|silk, metal thread; wrapped and braided|textiles-costumes', 'is/mobile-large/DP18737.jpg', 0.40), + SearchData(1649, 448587, 'Panel from a Tent Lining (Qanat)', 'panel|cotton; plain weave, mordant dyed and painted, resist-dyed|textiles-painted and/or printed', 'is/mobile-large/DP212659.jpg', 0.49), + SearchData(-650, 321648, 'Stamp seal (conoid) with cultic scene', 'stamp seal|brown chalcedony (quartz), possibly etched to produce yellow mottling|', 'an/mobile-large/ss86_11_28gp.jpg', 1.01), + SearchData(-650, 326247, 'Inlay', 'inlay|bone|', 'an/mobile-large/ME1977_234_16.jpg', 1.10), + SearchData(899, 451594, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-101.jpg', 1.07), + SearchData(-650, 327043, 'Palette with a sculpted female head and incised decoration', 'palette|calcite|', 'an/mobile-large/vs1984_453_9.jpg', 0.79), + SearchData(1525, 446603, '"Laila and Majnun in School", Folio 129 from a Khamsa (Quintet) of Nizami of Ganja', 'folio from an illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DT232547.jpg', 0.62), + SearchData(1875, 500993, 'Stringed Instrument', 'stringed instrument|turtle shell and wood, 4 strings.|chordophone-lute-plucked-fretted', 'mi/mobile-large/midp89.4.371.jpg', 1.60), + SearchData(-750, 323748, 'Stamp seal (scarab) with anthropomorphic figure', 'stamp seal|mottled green glass|', 'an/mobile-large/ss41_160_166.jpg', 1.65), + SearchData(50, 325899, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_18.jpg', 0.71), + SearchData(-2500, 325836, 'Spouted vessel', 'vessel|ceramic|', 'an/mobile-large/ME66_183.jpg', 1.35), + SearchData(-50, 325075, 'Fragment of a grave stele', 'relief|alabaster (gypsum)|', 'an/mobile-large/frieze.jpg', 3.23), + SearchData(50, 325914, 'Vessel', 'vessel|ceramic|', 'an/mobile-large/hb67_246_33.jpg', 1.94), + SearchData(50, 325897, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_16.jpg', 0.71), + SearchData(1299, 451584, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf-60-23-91b.jpg', 1.66), + SearchData(849, 451544, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-49a.jpg', 1.35), + SearchData(-450, 323936, 'Stamp seal', 'stamp seal|agate|', 'an/mobile-large/ss41_160_661.jpg', 1.42), + SearchData(50, 325892, 'Cup', 'cup|ceramic|', 'an/mobile-large/ME67_246_11.jpg', 1.37), + SearchData(-1630, 324560, 'Scarab seal', 'stamp seal|steatite, white pink|', 'an/mobile-large/ss56_152_6.jpg', 0.72), + SearchData(1341, 451414, '"Siyavush Displays his Skill at Polo before Afrasiyab," Folio from a Shahnama (Book of Kings)', 'folio from an illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/sf57-51-35r.jpg', 0.83), + SearchData(50, 325883, 'Small bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_2.jpg', 1.47), + SearchData(-1630, 326991, 'Scarab seal', 'stamp seal|faience, white (?)|', 'an/mobile-large/ss1984_383_30.jpg', 1.38), + SearchData(1200, 451379, 'Bowl with Courtly and Astrological Motifs', 'bowl|stonepaste; polychrome inglaze and overglaze painted and gilded on opaque monochrome glaze (mina\'i)|ceramics', 'is/mobile-large/DP170379.jpg', 0.75), + SearchData(-750, 323750, 'Stamp seal (oval bezel) with cultic scene', 'stamp seal|carnelian (quartz)|', 'an/mobile-large/ss41_160_168.jpg', 1.67), + SearchData(899, 451590, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-97a.jpg', 1.55), + SearchData(50, 325891, 'Male figurine', 'sculpture|ceramic|', 'an/mobile-large/ME67_246_10.jpg', 0.78), + SearchData(1649, 646829, 'Filigree Casket with Sliding Top', 'box|silver filigree; parcel-gilt|metal', 'is/mobile-large/DP340529.jpg', 0.78), + SearchData(1049, 451599, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-106a.jpg', 1.08), + SearchData(1675, 24298, 'Dagger with Sheath', 'dagger with sheath|steel, nephrite, gold, rubies, emeralds, silver-gilt, leather|daggers', 'aa/mobile-large/DP157696.jpg', 0.73), + SearchData(-550, 323933, 'Stamp seal', 'stamp seal|carnelian|', 'an/mobile-large/ss41_160_560.jpg', 1.88), + SearchData(899, 451565, 'Fragment', 'fragment|stoneware|ceramics', 'is/mobile-large/sf60-23-72b.jpg', 2.02), + SearchData(1687, 453279, 'Calligraphic Plaque', 'plaque|steel; forged and pierced|metal', 'is/mobile-large/DP170397.jpg', 1.71), + SearchData(-1597, 544660, 'Scarab with a Crocodile Headed Figure Holding a Flower', 'scarab, crocodile figure, flower|glazed steatite|', 'eg/mobile-large/M_398_bottom.jpg', 0.78), + SearchData(1649, 446546, 'Shahnama (Book of Kings) of Firdausi', 'illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP215296.jpg', 0.70), + SearchData(-1673, 324558, 'Scarab seal', 'stamp seal|stone, white|', 'an/mobile-large/ss56_152_4.jpg', 0.75), + SearchData(-750, 324012, 'Stamp seal (scarab) with animal', 'stamp seal|hematite|', 'an/mobile-large/ss45_4_18.jpg', 0.75), + SearchData(-600, 321633, 'Stamp seal (scaraboid) with animal', 'stamp seal|neutral chalcedony (quartz)|', 'an/mobile-large/ss86_11_9gp.jpg', 1.20), + SearchData(1399, 451553, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-58a.jpg', 1.60), + SearchData(899, 451595, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-102a.jpg', 1.44), + SearchData(50, 324247, 'Sherd', 'sherd|ceramic|', 'an/mobile-large/ME52_129_4.jpg', 0.94), + SearchData(50, 325902, 'Cooking pot', 'pot|ceramic|', 'an/mobile-large/ME67_246_21.jpg', 1.13), + SearchData(50, 324252, 'Sherd', 'sherd|ceramic|', 'an/mobile-large/ME52_129_9.jpg', 1.64), + SearchData(1540, 452413, 'Velvet Panel with Hunting Scene', 'tent panel|silk, flat metal thread; cut and voided velvet|textiles', 'is/mobile-large/DP267484.jpg', 0.97), + SearchData(1399, 451556, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-61a.jpg', 1.55), + SearchData(1187, 446860, 'Luster Bowl with Winged Horse', 'bowl|stonepaste; luster-painted on opaque monochrome glaze|ceramics', 'is/mobile-large/DP212285.jpg', 0.75), + SearchData(-600, 323924, 'Stamp seal (loaf-shaped hemispheroid) with animal', 'stamp seal|banded carnelian (quartz)|', 'an/mobile-large/seal1.jpg', 1.42), + SearchData(0, 326243, 'Open bowl', 'bowl|ceramic, paint|', 'an/mobile-large/DT904.jpg', 1.71), + SearchData(1049, 451606, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-113a.jpg', 0.87), + SearchData(-1630, 324567, 'Scarab seal', 'stamp seal|jasper, green (?)|', 'an/mobile-large/ss56_152_13.jpg', 0.66), + SearchData(1199, 451564, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-71b.jpg', 0.75), + SearchData(-850, 326010, 'Cylinder seal with chariot hunting scene', 'cylinder seal|egyptian blue|', 'an/mobile-large/SS1970_183_2.jpg', 2.08), + SearchData(-1300, 322598, 'Enthroned deity', 'sculpture|bronze, gold foil|', 'an/mobile-large/hb32_18_3.jpg', 0.53), + SearchData(50, 324250, 'Sherd', 'sherd|ceramic|', 'an/mobile-large/ME52_129_7.jpg', 0.65), + SearchData(1800, 30999, 'Sword (Saif) with Scabbard', 'sword (saif) with scabbard|steel, silver, leather|swords', 'aa/mobile-large/36.25.1547ab_001_Aug2015.jpg', 1.50), + SearchData(787, 327822, 'Stamp seal', 'stamp seal|sandstone or siltstone ?|', 'an/mobile-large/vsz1999_325_226.jpg', 1.11), + SearchData(1399, 451555, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-60a.jpg', 1.10), + SearchData(-1750, 322913, 'Nude female figure', 'sculpture|ceramic|', 'an/mobile-large/hb33_47_1.jpg', 0.37), + SearchData(1199, 451511, 'Fragment', 'fragment|stonepaste; underglaze painted|ceramics', 'is/mobile-large/sf60-23-14a.jpg', 2.14), + SearchData(1527, 452142, '"Kai Khusrau Rides Bihzad for the First Time", Folio 212r from the Shahnama (Book of Kings) of Shah Tahmasp', 'folio from an illustrated manuscript|opaque watercolor, ink, silver, and gold on paper|codices', 'is/mobile-large/DP107149.jpg', 0.68), + SearchData(1199, 451558, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-63a.jpg', 1.30), + SearchData(-750, 326250, 'Fragments of animal figurines', 'sculpture|ceramic, paint|', 'an/mobile-large/ME1977_234_19.jpg', 1.55), + SearchData(849, 451526, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-30a.jpg', 1.36), + SearchData(899, 451581, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-88b.jpg', 0.88), + SearchData(50, 324251, 'Fragment of painted ware', 'sherd|ceramic|', 'an/mobile-large/ME52_129_8.jpg', 0.88), + SearchData(50, 325915, 'Cup', 'cup|ceramic|', 'an/mobile-large/ME67_246_34.jpg', 1.42), + SearchData(1875, 500988, 'Naqqāra', 'naqqāra|metal|membranophone-single-headed / kettle drum', 'mi/mobile-large/midp89.4.366-368.jpg', 1.65), + SearchData(50, 325918, 'Vessel', 'vessel|ceramic|', 'an/mobile-large/hb67_246_37.jpg', 0.73), + SearchData(-1630, 324566, 'Scaraboid seal', 'stamp seal|rock crystal (?)|', 'an/mobile-large/ss56_152_12.jpg', 1.29), + SearchData(849, 451520, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-23a.jpg', 1.65), + SearchData(1892, 32370, 'Dagger (Jambiya) with Scabbard and Fitted Storage Case', 'dagger (jambiya) with scabbard and fitted storage case|steel, silver, wood, textile, gold|daggers', 'aa/mobile-large/DP157410.jpg', 0.82), + SearchData(899, 451500, 'Fragment', 'fragment|earthenware; unglazed|ceramics', 'is/mobile-large/sf60-23-3a.jpg', 0.80), + SearchData(-1500, 323559, 'Cylinder seal', 'cylinder seal|stone|', 'an/mobile-large/ss36_106_1.jpg', 1.74), + SearchData(-750, 322309, 'Stamp seal (scaraboid) with cultic scene', 'stamp seal|black limestone|', 'an/mobile-large/ss99_22_40gp.jpg', 0.83), + SearchData(899, 451592, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-99a.jpg', 1.63), + SearchData(-700, 324013, 'Stamp seal (scarab) with monster', 'stamp seal|hematite|', 'an/mobile-large/ss45_4_19.jpg', 1.31), + SearchData(-1750, 322916, 'Nude female figure', 'sculpture|ceramic|', 'an/mobile-large/hb33_47_4.jpg', 0.43), + SearchData(-750, 326011, 'Stamp seal (scarab) with monster', 'stamp seal|lapis lazuli|', 'an/mobile-large/ss1970_183_3.jpg', 1.14), + SearchData(-1630, 327099, 'Scarab seal', 'stamp seal|faience, white|', 'an/mobile-large/ss1985_192_30.jpg', 1.39), + SearchData(1449, 451552, 'Fragment', 'fragment|stonepaste; slip and underglaze painted|ceramics', 'is/mobile-large/sf60-23-57a.jpg', 1.57), + SearchData(899, 451576, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-83b.jpg', 1.23), + SearchData(-1630, 322528, 'Elliptical ring bezel seal', 'stamp seal|rock crystal|', 'an/mobile-large/me23_10_9.jpg', 1.40), + SearchData(1532, 452170, '"Bahrum Gur Before His Father, Yazdigird I", Folio 551v from the Shahnama (Book of Kings) of Shah Tahmasp', 'folio from an illustrated manuscript|opaque watercolor, ink, silver, and gold on paper|codices', 'is/mobile-large/DP107205.jpg', 0.67), + SearchData(1887, 500569, 'Gumuri', 'gumuri|wood, parchment, hide|chordophone-lute-plucked-unfretted', 'mi/mobile-large/MUS2027.jpg', 0.23), + SearchData(1850, 31773, 'Dagger (Jambiya) with Sheath and Belt', 'dagger (jambiya) with sheath and belt|steel, wood, gold, silver, textile, leather, brass|daggers', 'aa/mobile-large/36.25.793ab_001July2014.jpg', 1.50), + SearchData(1799, 444633, 'Powder Horn', 'powder horn|brass and silver|arms and armor', 'is/mobile-large/sf91-1-1082a.jpg', 0.60), + SearchData(1800, 30761, 'Spear', 'spear|steel, brass|shafted weapons', 'aa/mobile-large/36.25.1949_001jan2015.jpg', 0.56), + SearchData(899, 451574, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-81b.jpg', 1.24), + SearchData(50, 325913, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_32.jpg', 0.66), + SearchData(-1630, 324561, 'Scarab seal', 'stamp seal|steatite, bone colored|', 'an/mobile-large/ss56_152_7.jpg', 0.78), + SearchData(-1750, 322915, 'Nude female figure', 'sculpture|ceramic|', 'an/mobile-large/me33_47_3.jpg', 0.64), + SearchData(-750, 321638, 'Stamp seal (scaraboid) with animals', 'stamp seal|variegated brown jasper (quartz)|', 'an/mobile-large/ss86_11_14gp.jpg', 0.87), + SearchData(1850, 31703, 'Dagger (Jambiya) with Sheath and Belt', 'dagger (jambiya) with sheath and belt|steel, silver, wood, leather, iron|daggers', 'aa/mobile-large/36.25.723ab_002june2014.jpg', 0.56), + SearchData(-750, 327771, 'Stamp seal (ovoid) with deity (?)', 'stamp seal|steatite|', 'an/mobile-large/vsz1999_325_175.jpg', 0.85), + SearchData(1882, 73632, 'Mirror of National Costumes of All Nations (Bankoku ishō kagami)', 'print|triptych of woodblock prints (nishiki-e); ink and color on paper|prints', 'as/mobile-large/DP148176.jpg', 1.89), + SearchData(-125, 327483, 'Votive plaque inscribed with Sabaean dedication', 'relief|copper alloy|', 'an/mobile-large/DP368662.jpg', 0.75), + SearchData(899, 451588, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-95a.jpg', 1.60), + SearchData(-500, 324027, 'Standing bull', 'sculpture|bronze|', 'an/mobile-large/DT895.jpg', 0.85), + SearchData(150, 324317, 'Head-shaped flask', 'flask|glass|', 'an/mobile-large/ME54_101_2.jpg', 0.74), + SearchData(-550, 323747, 'Scarab seal', 'stamp seal|jasper, green|', 'an/mobile-large/ss41_160_165.jpg', 0.99), + SearchData(1299, 451585, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf60-23-92a.jpg', 0.73), + SearchData(999, 451567, 'Fragment', 'fragment|celadon|ceramics', 'is/mobile-large/sf60-23-74a.jpg', 1.25), + SearchData(50, 325903, 'Lamp', 'lamp|ceramic|', 'an/mobile-large/ME67_246_22.jpg', 1.46), + SearchData(-1630, 324555, 'Scarab seal', 'stamp seal|stone, white|', 'an/mobile-large/ss56_152_1.jpg', 0.74), + SearchData(-700, 323163, 'Nude female figure', 'sculpture|ceramic|', 'an/mobile-large/an34.126.53.jpg', 0.66), + SearchData(1875, 500981, 'Junuk', 'junuk|wood|chordophone-harp', 'mi/mobile-large/Mus196A.jpg', 0.65), + SearchData(937, 451505, 'Fragment of a Bowl', 'fragment of a bowl|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-8.jpg', 1.37), + SearchData(-700, 326238, 'Weight', 'weight|hematite|', 'an/mobile-large/ME1977_234_6.jpg', 1.08), + SearchData(1149, 451068, 'Earring, One of a Pair', 'earring|gold wire with filigree|jewelry', 'is/mobile-large/LC-52_4_3.jpg', 1.00), + SearchData(-500, 326993, 'Stamp seal (scaraboid) with cartouche', 'stamp seal|limestone|', 'an/mobile-large/ss1984_383_32.jpg', 1.18), + SearchData(1556, 444864, 'Hawk Coin of the Emperor Akbar', 'coin|gold|coins', 'is/mobile-large/LC-99_35_7400.jpg', 1.50), + SearchData(1199, 449159, 'Roundel with a Mounted Falconer and Hare', 'roundel|gypsum plaster; modeled, painted, and gilded|sculpture', 'is/mobile-large/DP373033.jpg', 1.05), + SearchData(-1650, 325690, 'Seven-cup offering bowl', 'bowl|ceramic|', 'an/mobile-large/180468.jpg', 1.25), + SearchData(50, 325894, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_13.jpg', 0.78), + SearchData(1199, 451550, 'Fragment', 'fragment|stonepaste; slip painted, glazed|ceramics', 'is/mobile-large/sf60-23-55a.jpg', 1.05), + SearchData(-750, 326997, 'Stamp seal (scaraboid) with animal', 'stamp seal|steatite, gray (?)|', 'an/mobile-large/ss1984_383_36.jpg', 0.94), + SearchData(-1800, 330882, 'Standing figure', 'sculpture|copper alloy|', 'an/mobile-large/2006_511.jpg', 0.75), + SearchData(-1630, 324562, 'Scarab seal', 'stamp seal|steatite, white|', 'an/mobile-large/ss56_152_8.jpg', 1.37), + SearchData(50, 324248, 'Fragment of painted ware', 'sherd|ceramic|', 'an/mobile-large/ME52_129_5.jpg', 1.26), + SearchData(937, 451504, 'Fragment of a Bowl', 'fragment of a bowl|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-7.jpg', 1.24), + SearchData(849, 451527, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-31a.jpg', 1.06), + SearchData(-750, 325519, 'Head of bull', 'sculpture|ceramic|', 'an/mobile-large/ME62_116_2.jpg', 0.91), + SearchData(50, 325889, 'Bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_7.jpg', 1.42), + SearchData(50, 325909, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_28.jpg', 0.65), + SearchData(1875, 500962, 'Darabukka', 'darabukka|clay, polychrome|membranophone-single-headed / goblet drum', 'mi/mobile-large/C2589 89.4.335.jpg', 0.67), + SearchData(899, 451596, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-103.jpg', 1.09), + SearchData(-700, 326255, 'Arrowhead', 'arrowhead|bronze|', 'an/mobile-large/ME1977_234_24.jpg', 0.80), + SearchData(-750, 326995, 'Stamp seal (scaraboid) with divine symbol', 'stamp seal|limestone (?)|', 'an/mobile-large/ss1984_383_34.jpg', 1.07), + SearchData(1299, 451582, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf60-23-89a.jpg', 0.94), + SearchData(849, 451518, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-21a.jpg', 1.11), + SearchData(899, 451498, 'Fragment of a Cup', 'fragment of a cup|earthenware; unglazed|ceramics', 'is/mobile-large/sf60-23-1a.jpg', 1.35), + SearchData(-700, 323823, 'Stamp seal (duck-shaped) with cultic scene', 'stamp seal|variegated pink and white chalcedony (quartz)|', 'an/mobile-large/ss41_160_232.jpg', 1.55), + SearchData(1887, 500570, 'Guenbri', 'gunibri|tortoise shell and wood. floral design in white, yellow, and pink.|chordophone-lute-plucked-unfretted', 'mi/mobile-large/MUS2015.jpg', 0.27), + SearchData(899, 451589, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-96a.jpg', 0.89), + SearchData(937, 451508, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-11a.jpg', 1.81), + SearchData(50, 325890, 'Bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_9.jpg', 1.40), + SearchData(899, 451578, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-85a.jpg', 1.47), + SearchData(-662, 323177, 'Jar handle with a seal impression', 'jar handle|ceramic|', 'an/mobile-large/DP-16934-003.jpg', 1.43), + SearchData(849, 451529, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-33a.jpg', 1.24), + SearchData(-750, 322302, 'Stamp seal (scaraboid) with geometric design', 'stamp seal|steatite, black|', 'an/mobile-large/ss99_22_33gp.jpg', 0.72), + SearchData(-750, 322307, 'Stamp seal (scaraboid) with cultic banquet scene', 'stamp seal|mottled orange jasper (quartz)|', 'an/mobile-large/ss99_22_38gp.jpg', 1.19), + SearchData(-1630, 324565, 'Scarab seal ring with Hyksos-period an-ra inscription', 'stamp seal ring|steatite, cream colored, bronze (?)|', 'an/mobile-large/ss56_152_11.jpg', 0.95), + SearchData(-500, 323745, 'Scarab seal with Bes dominating two lions below a winged sun disc', 'stamp seal|jasper, green|', 'an/mobile-large/ss41_160_163.jpg', 1.74), + SearchData(-2000, 327544, 'Standing female figure wearing a strap and a necklace', 'sculpture|sandstone, quartzite|', 'an/mobile-large/DT868.jpg', 0.76), + SearchData(937, 451506, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-9.jpg', 1.52), + SearchData(-1875, 322521, 'Fenestrated axe blade', 'axe|bronze|', 'an/mobile-large/hb23_10_2.jpg', 0.89), + SearchData(899, 451501, 'Fragment', 'fragment|earthenware; unglazed|ceramics', 'is/mobile-large/sf60-23-4a.jpg', 0.92), + SearchData(899, 451531, 'Fragment', 'fragment|earthenware; incised decoration, glazed|ceramics', 'is/mobile-large/sf60-23-35a.jpg', 0.95), + SearchData(799, 451571, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-78b.jpg', 1.48), + SearchData(1299, 451583, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf60-23-90a.jpg', 1.30), + SearchData(-2323, 544034, 'Head of a monkey from an ointment vessel', 'ointment jar fragment, monkey head|serpentinite|', 'eg/mobile-large/1970.184.3_EGDP010831.jpg', 1.02), + SearchData(899, 451591, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-98a.jpg', 1.00), + SearchData(50, 325919, 'Lamp', 'lamp|ceramic|', 'an/mobile-large/ME67_246_38.jpg', 1.42), + SearchData(1489, 30812, 'Axe (Berdiche)', 'axe (berdiche)|steel, wood, silver|shafted weapons', 'aa/mobile-large/DP165529.jpg', 0.68), + SearchData(849, 451563, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-70a.jpg', 1.08), + SearchData(1199, 451510, 'Fragment', 'fragment|stonepaste; underglaze painted|ceramics', 'is/mobile-large/sf60-23-13a.jpg', 1.23), + SearchData(11, 442859, 'Basket Earring', 'earring|gold, silver (?); decorated with filigree and granulation|jewelry', 'is/mobile-large/74.51.3607.jpg', 1.08), + SearchData(899, 451537, 'Fragment', 'fragment|earthenware; splash glazed decoration|ceramics', 'is/mobile-large/60-23-41.jpg', 1.67), + SearchData(50, 325907, 'Bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_26.jpg', 1.34), + SearchData(849, 451561, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-66a.jpg', 1.01), + SearchData(1099, 451566, 'Fragment', 'fragment|celadon|ceramics', 'is/mobile-large/sf60-23-73a.jpg', 0.88), + SearchData(1124, 446283, 'Ewer with Molded Inscriptions and Figures on Horseback', 'ewer|stonepaste; molded, monochrome glazed|ceramics', 'is/mobile-large/LC-13_93_3.jpg', 0.67), + SearchData(1149, 451069, 'Earring, One of a Pair', 'earring|gold wire with filigree|jewelry', 'is/mobile-large/52.4.3-4.jpg', 0.70), + SearchData(-700, 326252, 'Fragment of animal figurine', 'sculpture|ceramic|', 'an/mobile-large/ME1977_234_21.jpg', 0.87), + SearchData(-2500, 327596, 'Rectangular mortar', 'mortar|veined marble|', 'an/mobile-large/vs1999_324.jpg', 1.08), + SearchData(-1600, 556377, 'Canaanite Cowroid Seal Amulet with Falcon Headed Deity', 'cowroid seal amulet, falcon headed deity|steatite|', 'eg/mobile-large/05.3.32_bottom.jpg', 1.07), + SearchData(50, 325884, 'Bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_3.jpg', 1.27), + SearchData(-1500, 325126, 'Staff (?)', 'staff|bronze|', 'an/mobile-large/ME61_73_3.jpg', 0.56), + SearchData(849, 451543, 'Fragment', 'fragment|earthenware; splash glazed|ceramics', 'is/mobile-large/sf60-23-48a.jpg', 1.44), + SearchData(50, 324249, 'Sherd', 'sherd|ceramic|', 'an/mobile-large/ME52_129_6.jpg', 0.94), + SearchData(899, 451499, 'Fragment', 'fragment|earthenware; unglazed|ceramics', 'is/mobile-large/sf60-23-2a.jpg', 1.07), + SearchData(899, 451539, 'Fragment', 'fragment|earthenware; painted splash glazed decoration|ceramics', 'is/mobile-large/sf60-23-43.jpg', 1.19), + SearchData(-250, 328941, 'Standing bull', 'sculpture|copper alloy, shell|', 'an/mobile-large/2002.34.jpg', 0.79), + SearchData(50, 325917, 'Bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_36.jpg', 1.39), + SearchData(999, 455153, 'Fragment', 'fragment|earthenware; luster-painted|ceramics', 'is/mobile-large/sf60-23-46a.jpg', 1.24), + SearchData(849, 451560, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-65a.jpg', 1.35), + SearchData(849, 455173, 'Fragment', 'fragment|earthenware; painted and glazed|ceramics', 'is/mobile-large/sf60-23-68a.jpg', 1.64), + SearchData(1849, 444669, 'Necklace', 'necklace|silver and cloth|jewelry', 'is/mobile-large/LC-91_1_1132.jpg', 1.50), + SearchData(849, 451530, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-34a.jpg', 1.07), + SearchData(1875, 500989, 'Naqqareh', 'naqqareh|wood|membranophone-single-headed / kettle drum', 'mi/mobile-large/C2589 89.4.367.jpg', 1.50), + SearchData(50, 325910, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_29.jpg', 0.65), + SearchData(937, 451507, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-10a.jpg', 0.85), + SearchData(849, 451568, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-75a.jpg', 1.00), + SearchData(-1892, 322525, 'Fish amulet', 'amulet|stone|', 'an/mobile-large/me23_10_6.jpg', 1.38), + SearchData(899, 451532, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-36a.jpg', 1.44), + SearchData(-125, 326695, 'Standing male figure', 'sculpture|alabaster (gypsum)|', 'an/mobile-large/an1982.317.3.jpg', 0.72), + SearchData(50, 325901, 'Lamp', 'lamp|ceramic|', 'an/mobile-large/ME67_246_20.jpg', 1.57), + SearchData(849, 451528, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-32a.jpg', 1.53), + SearchData(1849, 817059, 'Tapis, ceremonial skirt with squid pattern (cumi-cumi) iconography', 'ceremonial skirt (tapis)|cotton, silk, mica, dyes|textiles-costumes', 'ao/mobile-large/Squid_textile_Confino.jpg', 1.28), + SearchData(899, 451577, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-84a.jpg', 0.98), + SearchData(1049, 452939, 'Pair of Earrings', 'earrings|gold; filigree and granulation|jewelry', 'is/mobile-large/DP233260.jpg', 1.34), + SearchData(1875, 500990, 'Naqqāra', 'naqqāra|metal|membranophone-single-headed / kettle drum', 'mi/mobile-large/MUS687A1.jpg', 1.03), + SearchData(-1875, 325093, 'Fenestrated axe blade', 'axe|bronze|', 'an/mobile-large/HB61_29.jpg', 1.81), + SearchData(50, 325886, 'Juglet', 'juglet|ceramic|', 'an/mobile-large/ME67_246_5.jpg', 0.78), + SearchData(50, 325908, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_27.jpg', 0.63), + SearchData(849, 451522, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-26a.jpg', 1.19), + SearchData(1875, 500998, 'Kamānja agūz (old fiddle)', 'kamānja agūz (old fiddle)|wood, coconut shell, skin|chordophone-lute-bowed-unfretted', 'mi/mobile-large/MUS195Aright.jpg', 0.23), + SearchData(1602, 453336, 'A Stallion', 'illustrated album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP234078.jpg', 1.48), + SearchData(-1750, 322914, 'Nude female figure', 'sculpture|ceramic|', 'an/mobile-large/me33_47_2.jpg', 0.54), + SearchData(899, 451540, 'Fragment', 'fragment|earthenware|ceramics', 'is/mobile-large/sf60-23-44ab.jpg', 1.81), + SearchData(1315, 451412, '"Yazdegird I Kicked to Death by the Water Horse", Folio from a Shahnama (Book of Kings)', 'folio from an illustrated manuscript|ink, opaque watercolor, silver, and gold on paper|codices', 'is/mobile-large/DP159355.jpg', 0.79), + SearchData(1399, 451557, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-62a.jpg', 1.28), + SearchData(1800, 31772, 'Dagger (Jambiya) with Sheath', 'dagger (jambiya) with sheath|steel, brass, wood, textile|daggers', 'aa/mobile-large/36.25.792ab_001Mar2015.jpg', 0.71), + SearchData(849, 451521, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-24a.jpg', 1.36), + SearchData(-1892, 322526, 'Pendant', 'pendant|stone|', 'an/mobile-large/me23_10_7.jpg', 0.91), + SearchData(-500, 324075, 'Incense burner', 'incense burner|bronze|', 'an/mobile-large/DT893.jpg', 0.88), + SearchData(899, 451538, 'Fragment', 'fragment|earthenware; splash glazed decoration|ceramics', 'is/mobile-large/sf60-23-42.jpg', 1.08), + SearchData(50, 324246, 'Sherd', 'sherd|ceramic|', 'an/mobile-large/ME52_129_3.jpg', 1.64), + SearchData(-750, 326998, 'Stamp seal (scarab) with animal', 'stamp seal|mottled greenstone|', 'an/mobile-large/ss1984_383_37.jpg', 1.13), + SearchData(1149, 450930, 'Jar', 'jar|earthenware; glazed|ceramics', 'is/mobile-large/LC-48_113_2.jpg', 0.80), + SearchData(849, 451515, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-18a.jpg', 1.24), + SearchData(50, 325906, 'Juglet', 'juglet|ceramic|', 'an/mobile-large/ME67_246_25.jpg', 0.79), + SearchData(50, 325896, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_15.jpg', 0.73), + SearchData(-500, 323925, 'Scarab seal with human head', 'stamp seal|stone, mottled red and white|', 'an/mobile-large/ss41_160_459.jpg', 1.51), + SearchData(1049, 451603, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-110a.jpg', 1.19), + SearchData(1049, 451605, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-112a.jpg', 0.87), + SearchData(50, 325905, 'Vessel', 'vessel|ceramic|', 'an/mobile-large/ME67_246_24.jpg', 0.80), + SearchData(849, 451542, 'Fragment', 'fragment|earthenware; splash glazed|ceramics', 'is/mobile-large/sf60-23-47a.jpg', 1.18), + SearchData(1875, 501012, 'Daff', 'daff|wood and parchment.|membranophone-double-headed / frame drum', 'mi/mobile-large/midp89.4.392.jpg', 1.78), + SearchData(50, 325911, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_30.jpg', 0.72), + SearchData(50, 325882, 'Small bowl', 'bowl|ceramic|', 'an/mobile-large/ME67_246_1.jpg', 1.47), + SearchData(-750, 322308, 'Stamp seal (scaraboid) with cultic scene', 'stamp seal|green-black serpentine|', 'an/mobile-large/ss99_22_39gp.jpg', 0.85), + SearchData(899, 451579, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-86b.jpg', 0.94), + SearchData(849, 451562, 'Fragment', 'fragment|earthenware; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-67a.jpg', 1.60), + SearchData(-1892, 322522, 'Pin', 'pin|bronze|', 'an/mobile-large/me23_10_3.jpg', 1.78), + SearchData(50, 324245, 'Fragment of painted ware', 'sherd|ceramic|', 'an/mobile-large/ME52_129_2.jpg', 1.29), + SearchData(1199, 451570, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-77a.jpg', 1.13), + SearchData(1849, 503640, 'Daraboukkeh', 'daraboukkeh|pottery|membranophone-single-headed / goblet drum', 'mi/mobile-large/C2589 89.4.364.jpg', 0.67), + SearchData(-1300, 327319, 'Smiting weather god or warrior with horned headgear', 'sculpture|bronze|', 'an/mobile-large/242307.jpg', 0.80), + SearchData(-300, 329093, 'Figure of a lion', 'sculpture|copper alloy|', 'an/mobile-large/DT2856.jpg', 1.26), + SearchData(-750, 326251, 'Fragment of animal figurine', 'sculpture|ceramic|', 'an/mobile-large/ME1977_234_20.jpg', 0.83), + SearchData(1875, 500984, 'Stringed Instrument (Qanbus)', 'stringed instrument (qanbus)|wood, hide|chordophone-lute-plucked-fretted', 'mi/mobile-large/Mus214A middle.jpg', 0.73), + SearchData(-1600, 544670, 'Canaanite Scarab with a Lion over a Crocodile', 'scarab, lion, crocodile|steatite|', 'eg/mobile-large/D_767_bottom.jpg', 1.18), + SearchData(50, 325885, 'Ribbed juglet', 'juglet|ceramic|', 'an/mobile-large/hb67_246_4.jpg', 0.83), + SearchData(-1892, 322523, 'Bracelet', 'bracelet|bronze|', 'an/mobile-large/me23_10_4.jpg', 1.22), + SearchData(50, 325895, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_14.jpg', 0.70), + SearchData(1049, 451601, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-108a.jpg', 1.34), + SearchData(-650, 326240, 'Cosmetic palette', 'cosmetic palette|stone|', 'an/mobile-large/ME1977_234_9.jpg', 1.64), + SearchData(-1888, 322527, 'Hemispheroid seal with addorsed Egyptianizing human heads', 'stamp seal|steatite, white|', 'an/mobile-large/ss23_10_8a.jpg', 1.49), + SearchData(-550, 323941, 'Scarab seal', 'stamp seal|rock crystal|', 'an/mobile-large/ss42_8.jpg', 0.81), + SearchData(-500, 327499, 'Vessel with a lid', 'vessel and lid|calcite alabaster|', 'an/mobile-large/jarxx.jpg', 0.76), + SearchData(1887, 500568, 'Guenbri', 'guenbri|gourd, wood, parchment decorated with floral designs|chordophone-lute-plucked-unfretted', 'mi/mobile-large/MUS2014.jpg', 0.29), + SearchData(849, 451516, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-19a.jpg', 1.22), + SearchData(50, 325887, 'Small lamp', 'lamp|ceramic|', 'an/mobile-large/ME67_246_6.jpg', 1.54), + SearchData(-1648, 544664, 'Canaanite Scarab with Two Men and a Lion', 'scarab, men, lion|steatite (glazed)|', 'eg/mobile-large/D_764_bottom.jpg', 1.46), + SearchData(1049, 451607, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-114a.jpg', 1.20), + SearchData(150, 324319, 'Pitcher', 'pitcher|glass|', 'an/mobile-large/ME54_101_4.jpg', 0.67), + SearchData(1600, 451725, '"The Concourse of the Birds", Folio 11r from a Mantiq al-Tayr (Language of the Birds)', 'folio from an illustrated manuscript|ink, opaque watercolor, gold, and silver on paper|codices', 'is/mobile-large/DP234083.jpg', 0.67), + SearchData(-900, 326249, 'Bead', 'bead|stone|', 'an/mobile-large/ME1977_234_18.jpg', 1.03), + SearchData(849, 451514, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-17a.jpg', 1.17), + SearchData(50, 324288, 'Vessel', 'vessel|ceramic|', 'an/mobile-large/hb53_208.jpg', 0.40), + SearchData(899, 451580, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-87a.jpg', 1.22), + SearchData(-1630, 324556, 'Scarab seal', 'stamp seal|stone, white|', 'an/mobile-large/ss56_152_2.jpg', 1.43), + SearchData(150, 324318, 'Vessel', 'vessel|glass|', 'an/mobile-large/ME54_101_3.jpg', 0.66), + SearchData(-1500, 325124, 'Axe head', 'axe|bronze|', 'an/mobile-large/ME61_73_1.jpg', 1.45), + SearchData(849, 451523, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-27b.jpg', 1.30), + SearchData(50, 325893, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_12.jpg', 0.65), + SearchData(849, 451519, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-22a.jpg', 1.12), + SearchData(1330, 448938, '"The Funeral of Isfandiyar," Folio from a Shahnama (Book of Kings)', 'folio from an illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP238058.jpg', 0.73), + SearchData(-1981, 552927, 'Middle Kingdom statuette, reinscribed for Harsiese High Priest of Memphis in the Third Intermediate Period', 'statuette, man, reinscribed for harsiese|greywacke|', 'eg/mobile-large/LC-68_101_EGDP026382.jpg', 0.67), + SearchData(1882, 500567, 'Rebab', 'rebab|wood, leather, shells|chordophone-lute-bowed-unfretted', 'mi/mobile-large/MUS2019.jpg', 0.55), + SearchData(-1630, 324564, 'Scarab seal', 'stamp seal|steatite, white|', 'an/mobile-large/ss56_152_10.jpg', 0.77), + SearchData(1049, 451604, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-111a.jpg', 0.85), + SearchData(1049, 451598, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-105a.jpg', 1.65), + SearchData(-1600, 544669, 'Canaanite Scarab with a Roaring Lion', 'scarab, lion|steatite|', 'eg/mobile-large/D_769_bottom.jpg', 1.39), + SearchData(-1500, 327792, 'Cylinder seal', 'cylinder seal|copper alloy (leaded bronze)|', 'an/mobile-large/1999,325,196.jpg', 5.56), + SearchData(1850, 31572, 'Dagger (Jambiya) with Sheath and Belt', 'dagger (jambiya) with sheath and belt|steel, wood, silver, silver wire, textile, silk, leather|daggers', 'aa/mobile-large/36.25.982ab_003july2014.jpg', 0.56), + SearchData(-1900, 325342, 'Oil lamp', 'lamp|ceramic|', 'an/mobile-large/ME61_150_1.jpg', 1.42), + SearchData(150, 324316, 'Head-shaped flask', 'flask|glass|', 'an/mobile-large/ME54_101_1.jpg', 0.68), + SearchData(1299, 451587, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf60-23-94b.jpg', 1.02), + SearchData(1299, 451586, 'Fragment', 'fragment|porcelain|ceramics', 'is/mobile-large/sf60-23-93a.jpg', 1.13), + SearchData(-1300, 322889, 'Enthroned deity', 'sculpture|bronze, gold foil|', 'an/mobile-large/DP137934.jpg', 0.70), + SearchData(-1966, 545105, 'Head of a sphinx, possibly of Amenemhat I', 'head, sphinx; 12th-dyn-king|dolomitic marble|', 'eg/mobile-large/66.99.4_EGDP017910.jpg', 0.80), + SearchData(50, 325912, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_31.jpg', 0.62), + SearchData(1149, 453615, 'Basket Earring, One of a Pair', 'earring|gold|jewelry', 'is/mobile-large/35.29.5_6.jpg', 1.03), + SearchData(849, 451524, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-28a.jpg', 1.47), + SearchData(849, 455174, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-69a.jpg', 1.55), + SearchData(1049, 451597, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-104a.jpg', 0.81), + SearchData(849, 451545, 'Fragment', 'fragment|earthenware; slip painted, glazed|ceramics', 'is/mobile-large/sf60-23-50a.jpg', 1.59), + SearchData(1249, 451551, 'Fragment', 'fragment|stonepaste; slip painted, incised, and splash glazed|ceramics', 'is/mobile-large/sf60-23-56a.jpg', 1.36), + SearchData(899, 451534, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-38a.jpg', 1.23), + SearchData(-700, 326244, 'Tridacna shell', 'shell|shell (tridacna squamosa)|', 'an/mobile-large/ME1977_234_13.jpg', 1.46), + SearchData(-1500, 322524, 'Pendant', 'pendant|silver|', 'an/mobile-large/me23_10_5.jpg', 1.09), + SearchData(899, 451575, 'Fragment', 'fragment|earthenware; incised|ceramics', 'is/mobile-large/sf60-23-82a.jpg', 1.49), + SearchData(1199, 451512, 'Fragment', 'fragment|stonepaste; underglaze painted|ceramics', 'is/mobile-large/sf60-23-15a.jpg', 0.92), + SearchData(899, 451536, 'Fragment', 'fragment|earthenware; painted decoration|ceramics', 'is/mobile-large/sf60-23-40.jpg', 1.20), + SearchData(1550, 451092, 'Safavid Courtiers Leading Georgian Captives', 'panel|silk, metal wrapped thread; lampas|textiles-woven', 'is/mobile-large/DP230052.jpg', 0.56), + SearchData(1850, 31573, 'Dagger (Jambiya) with Sheath', 'dagger (jambiya) with sheath|steel, wood, silver, gold, copper foil, pigment, paper, glue|daggers', 'aa/mobile-large/36.25.983ab_003july2014.jpg', 0.67), + SearchData(4, 323749, 'Stamp seal (scaraboid) with animals', 'stamp seal|variegated red and neutral carnelian (quartz)|', 'an/mobile-large/ss41_160_167.jpg', 1.74), + SearchData(1738, 446556, 'Shahnama (Book of Kings) of Firdausi', 'illustrated manuscript|ink, opaque watercolor, silver, and gold on paper|codices', 'is/mobile-large/sf13-228-22-64r.jpg', 0.59), + SearchData(899, 451502, 'Fragment', 'fragment|earthenware; glazed|ceramics', 'is/mobile-large/sf60-23-5a.jpg', 0.86), + SearchData(1249, 450578, 'Dish with Horse and Rider', 'dish|stonepaste; incised decoration through a black slip ground under a turquoise glaze (silhouette ware)|ceramics', 'is/mobile-large/sf45-153-2b.jpg', 1.00), + SearchData(-500, 326422, 'Stamp seal (duck-shaped) with geometric design', 'stamp seal|stone|', 'an/mobile-large/ss1986_311_5a.jpg', 1.19), + SearchData(849, 451525, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-29a.jpg', 0.97), + SearchData(-1400, 327231, 'Smiting god, wearing an Egyptian atef crown', 'sculpture|bronze|', 'an/mobile-large/DP370818.jpg', 0.70), + SearchData(-900, 326245, 'Object', 'object|stone|', 'an/mobile-large/ME1977_234_14.jpg', 1.03), + SearchData(50, 325888, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_8.jpg', 0.63), + SearchData(-450, 324255, 'Figure of ibex', 'sculpture|bronze|', 'an/mobile-large/IBEX.JPG', 0.66), + SearchData(300, 326693, 'Head of a man', 'sculpture|alabaster (gypsum)|', 'an/mobile-large/DT6598.jpg', 0.80), + SearchData(-550, 323746, 'Scarab seal', 'stamp seal|jasper, green|', 'an/mobile-large/ss41_160_164.jpg', 0.87), + SearchData(1049, 451600, 'Fragment', 'fragment|earthenware; incised and glazed|ceramics', 'is/mobile-large/sf60-23-107a.jpg', 1.50), + SearchData(50, 325898, 'Vessel', 'vessel|ceramic|', 'an/mobile-large/ME67_246_17.jpg', 1.12), + SearchData(-1594, 556437, 'Scarab with a Crocodile Headed Deity', 'scarab, sebek|glazed steatite|', 'eg/mobile-large/c_456_bottom.jpg', 1.29), + SearchData(-550, 323931, 'Scaraboid seal', 'stamp seal|serpentine, green|', 'an/mobile-large/ss41_160_548.jpg', 1.63), + SearchData(-1, 322592, 'Camel and riders', 'sculpture|silver|', 'an/mobile-large/DP-14352-001.jpg', 0.77), + SearchData(-1630, 324563, 'Scarab seal', 'stamp seal|stone, cream colored|', 'an/mobile-large/ss56_152_9.jpg', 0.87), + SearchData(50, 325904, 'Juglet', 'juglet|ceramic|', 'an/mobile-large/ME67_246_23.jpg', 0.80), + SearchData(899, 451541, 'Fragment', 'fragment|earthenware; slip painted, incised, and splash glazed|ceramics', 'is/mobile-large/sf60-23-45a.jpg', 1.28), + SearchData(1199, 451569, 'Fragment', 'fragment|stonepaste; slip painted and glazed|ceramics', 'is/mobile-large/sf60-23-76a.jpg', 1.41), + SearchData(1875, 500985, 'Stringed Instrument', 'stringed instrument|wood|chordophone-lute-plucked-fretted', 'mi/mobile-large/MUS251A.jpg', 0.31), + SearchData(-1630, 324557, 'Scarab seal ring', 'stamp seal ring|stone, white, copper alloy mount|', 'an/mobile-large/ss56_152_3.jpg', 0.56), + SearchData(-750, 327024, 'Stamp seal (scaraboid) with animal', 'stamp seal|serpentine, black (?)|', 'an/mobile-large/ss1984_383_63.jpg', 1.16), + SearchData(-2500, 327496, 'Head of a bull', 'sculpture|ivory (hippopotamus)|', 'an/mobile-large/sd1994_81.jpg', 0.84), + SearchData(-600, 321452, 'Stamp seal (grooved oval base with ribbed handle) with cultic scene', 'stamp seal|neutral chalcedony (quartz)|', 'an/mobile-large/ss74_51_4364.jpg', 0.67), + SearchData(50, 324244, 'Fragment of painted ware', 'sherd|ceramic, pigment|', 'an/mobile-large/ME52_129_1.jpg', 1.58), + SearchData(849, 451517, 'Fragment', 'fragment|earthenware; molded decoration, glazed|ceramics', 'is/mobile-large/sf60-23-20a.jpg', 1.40), + SearchData(849, 451546, 'Fragment', 'fragment|earthenware; slip painted, glazed|ceramics', 'is/mobile-large/sf60-23-51a.jpg', 1.53), + SearchData(-1648, 556529, 'Canaanite Scarab of the "Anra" Type', 'scarab, "anra"|steatite|', 'eg/mobile-large/30.8.896_bottom.jpg', 0.78), + SearchData(1355, 449537, 'Mihrab (Prayer Niche)', 'mihrab|mosaic of polychrome-glazed cut tiles on stonepaste body; set into mortar|ceramics', 'is/mobile-large/DP235035.jpg', 0.67), + SearchData(899, 451593, 'Fragment', 'fragment|earthenware; splash glazed|ceramics', 'is/mobile-large/sf60-23-100.jpg', 1.52), + SearchData(-125, 326694, 'Votive or funerary stele', 'stele|alabaster (gypsum)|', 'an/mobile-large/DT959.jpg', 0.78), + SearchData(50, 325900, 'Unguentarium', 'vessel|ceramic|', 'an/mobile-large/ME67_246_19.jpg', 0.73), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/pyramids_giza_search_data.dart b/lib/logic/data/wonders_data/search/pyramids_giza_search_data.dart new file mode 100644 index 00000000..03694c58 --- /dev/null +++ b/lib/logic/data/wonders_data/search/pyramids_giza_search_data.dart @@ -0,0 +1,381 @@ +part of '../pyramids_giza_data.dart'; + +// Search suggestions (85) +List _searchSuggestions = const ['maatkare', 'cylinder', 'seal', 'iii', 'alloy', 'travertine', 'faience', 'jasper', 'impressed', 'stela', 'khasekhemwy', 'clay', 'royal', 'glass', 'fragment', 'bronzes', 'relief', 'red', 'cosmetic', 'bronze', 'steatite', 'egyptian', 'horus', 'black', 'lower', 'wood', 'inlay', 'isis', 'upper', 'scarab', 'jar', 'pendant', 'hatshepsut', 'statue', 'ptah', 'bowl', 'nesut', 'god', 'plaque', 'figure', 'man', 'amun', 'greywacke', 'model', 'leaf', 'copper', 'goddess', 'miscellaneous', 'lion', 'silver', 'king', 'linen', 'seker', 'standing', 'ivory', 'group', 'glazed', 'quartzite', 'funerary', 'dwarf', 'paint', 'limestone', 'sealing', 'statuette', 'amulet', 'woman', 'image', 'depicting', 'unfired', 'bity', 'alabaster', 'child', 'pottery', 'document', 'mud', 'egypt', 'gold', 'queen', 'name', 'carnelian', 'head', 'pataikos', 'block', 'pair', 'inscribed', ]; + +// Pyramids of Giza (373) +List _searchData = const [ + SearchData(-2650, 547434, 'Copper pin?', 'cosmetic pin (?)|copper alloy|', 'eg/mobile-large/01.4.41_EGDP011664.jpg', 1.78), + SearchData(-347, 243717, 'Red jasper amulet of the sun on the horizon', 'amulet, sun on horizon|jasper, red|gold and silver', 'gr/mobile-large/DP121805.jpg', 0.75), + SearchData(-2649, 543870, 'Figure of a male beer-maker', 'statuette, male beer-maker|limestone, paint|', 'eg/mobile-large/20.2.1.JPG', 0.73), + SearchData(-2649, 561720, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1618_EGDP032392.jpg', 0.67), + SearchData(-2551, 543895, 'Archers', 'relief, archers|limestone, paint|', 'eg/mobile-large/DT259178.jpg', 1.25), + SearchData(650, 326230, 'Textile fragment: walking ram with a neckband and fluttering ribbons', 'textile|wool, cotton|', 'an/mobile-large/ra1977.232.R.jpg', 0.52), + SearchData(-347, 243760, 'Faience amulet in the form of a lion-headed deity', 'amulet, bast|clay, glazed|gold and silver', 'gr/mobile-large/DP121824.jpg', 0.75), + SearchData(-2475, 545826, 'Headless statue of Babaef as older man', 'statue, babaef as older man|limestone|', 'eg/mobile-large/64.66.2_EGDP014893.jpg', 0.50), + SearchData(-2520, 548547, 'Relief fragment showing fishing scene', 'relief, fishing scene|limestone|', 'eg/mobile-large/58.161.jpg', 1.95), + SearchData(-347, 243809, 'Faience button seal', 'button seal|clay, glazed|gold and silver', 'gr/mobile-large/DP121847.jpg', 1.33), + SearchData(-1249, 249678, 'Glass pendant ornament in the form of a pair of birds', 'ornament in the form of two ducks|glass|glass', 'gr/mobile-large/DP121789.jpg', 1.33), + SearchData(-664, 553106, 'Bottle', 'bottle|glass|', 'eg/mobile-large/DP-24757-001.jpg', 0.77), + SearchData(-2650, 570768, 'Clay jar sealing impressed faintly with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.102_EGDP012135.jpg', 1.40), + SearchData(-347, 243731, 'Faience amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121811.jpg', 0.75), + SearchData(-347, 244463, 'Bronze statuette of Osiris', 'statuette of osiris|bronze|bronzes', 'gr/mobile-large/DP20092.jpg', 0.92), + SearchData(-1465, 573652, 'Model Ax from the Foundation Deposit for Hatshepsut\'s Tomb', 'model ax|bronze or copper alloy, wood|', 'eg/mobile-large/30.8.3.jpg', 1.33), + SearchData(-2649, 543922, 'Ewer', 'vessel, ewer|copper|', 'eg/mobile-large/26-2-15.jpg', 1.63), + SearchData(-2465, 543929, 'Miniature ointment jar', 'ointment jar, miniature|gneiss|', 'eg/mobile-large/LC-07_228_91_EGDP033551.jpg', 0.67), + SearchData(-1250, 547899, 'Kneeling official', 'statuette, kneeling official|bronze|', 'eg/mobile-large/DP139142.jpg', 1.00), + SearchData(-2557, 545776, 'Ball', 'ball|clay or mud|', 'eg/mobile-large/20-2-49-50.jpg', 1.95), + SearchData(125, 547951, 'Portrait of the Boy Eutyches', 'panel painting, eutyches|encaustic on wood, paint|', 'eg/mobile-large/DT564.jpg', 0.47), + SearchData(-2650, 547428, 'Model harpoon', 'model harpoon|copper alloy|', 'eg/mobile-large/01.4.35_EGDP011631.jpg', 1.50), + SearchData(-1800, 544326, 'Coffin of Khnumnakht', 'coffin, khnumnakht|wood, paint|', 'eg/mobile-large/DP354909.jpg', 1.36), + SearchData(-1465, 549727, 'Scarab Inscribed for the King of Upper and Lower Egypt Maatkare (Hatshepsut)', 'scarab; maatkare, nesut bity, ankh; notched back|steatite (glazed)|', 'eg/mobile-large/27.3.247_bot.jpg', 1.01), + SearchData(-1390, 767349, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_206.jpg', 1.27), + SearchData(-995, 243763, 'Faience Ushabti', 'amulet, ushabti|clay, glazed|gold and silver', 'gr/mobile-large/DP121826.jpg', 0.75), + SearchData(349, 464049, 'Gold Necklace with Amphora (Vase) Pendant', 'necklace|gold|metalwork-gold', 'md/mobile-large/LC_17_190_1643s01.jpg', 0.76), + SearchData(-2650, 547425, 'Model ax-head', 'model ax head|copper alloy|', 'eg/mobile-large/01.4.32_EGDP011629.jpg', 1.00), + SearchData(-2650, 547426, 'Model adze blade', 'model adze blade|copper alloy|', 'eg/mobile-large/01.4.33_EGDP011637.jpg', 1.50), + SearchData(-1390, 767353, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_210.jpg', 1.12), + SearchData(-2650, 570767, 'Clay jar sealing impressed faintly with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.101_EGDP012122.jpg', 1.40), + SearchData(-2500, 543888, 'Large high-shouldered jar', 'jar|travertine (egyptian alabaster)|', 'eg/mobile-large/DT227156.jpg', 0.80), + SearchData(-2465, 543928, 'Miniature "nw" pot', 'pot, "nw" pot, miniature|gneiss|', 'eg/mobile-large/LC-07_228_92_EGDP033556.jpg', 0.67), + SearchData(-2650, 547430, 'Model chisel', 'model chisel|copper alloy|', 'eg/mobile-large/01.4.37_EGDP011641.jpg', 1.78), + SearchData(-100, 577944, 'King\'s Head with Egyptian Headdress but Greek Hair and Features', 'head, king, greek hair|gabbro|', 'eg/mobile-large/DP246577.jpg', 1.33), + SearchData(-1282, 547704, 'Head of a goddess', 'head, goddess|quartzite|', 'eg/mobile-large/DT4632.jpg', 0.80), + SearchData(-2520, 543896, 'Fragmentary Face of King Khafre', 'head fragment, king khafre|travertine (egyptian alabaster)|', 'eg/mobile-large/DT11751.jpg', 0.77), + SearchData(-2465, 543902, 'Statue of Demedji and Hennutsen', 'statue, seated pair, demedji, hennutsen|limestone, paint|', 'eg/mobile-large/DT521.jpg', 0.80), + SearchData(-2551, 543909, 'Reserve head', 'head, reserve|limestone|', 'eg/mobile-large/48.156(1).JPG', 0.66), + SearchData(-215, 549187, 'Hedgehog on box pendant', 'pendant, hedgehog on box|gold|', 'eg/mobile-large/DP356247.jpg', 0.87), + SearchData(-181, 548310, 'The Goddess Isis and her Son Horus', 'statuette of isis with the horus child|faience|', 'eg/mobile-large/DP241036.jpg', 0.75), + SearchData(-2649, 561722, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1612_EGDP032319.jpg', 0.67), + SearchData(1250, 831188, 'Icon of the Virgin and Child, Hodegetria variant', '|tempera on wood|paintings-icons', 'md/mobile-large/DP-22753-001.jpg', 0.74), + SearchData(6, 330095, 'Pyx Fragments with the Multiplication of the Loaves and Fishes', 'pyx fragments|elephant ivory|ivories-elephant', 'md/mobile-large/kn314a.jpg', 1.20), + SearchData(-155, 243774, 'Faience amulet in the form of a tree frog', 'amulet, frog|faience|gold and silver', 'gr/mobile-large/DP121833.jpg', 0.75), + SearchData(-1344, 547692, 'Head of a princess from a group statue', 'head, amarna princess|quartzite|', 'eg/mobile-large/DP137930.jpg', 1.00), + SearchData(-664, 243713, 'Lapis lazuli heart amulet', 'amulet pendant, heart|lapis lazuli|gold and silver', 'gr/mobile-large/DP121803.jpg', 0.75), + SearchData(-664, 550945, 'Amulet: Crown of Upper Egypt', 'amulet, crown, upper egypt|faience|', 'eg/mobile-large/LC-24_4_4_EGDP028726.jpg', 0.67), + SearchData(-2650, 547440, 'Child\'s bracelet', 'bracelet, child\'s|gold|', 'eg/mobile-large/01.4.2_01-20-01.jpg', 1.69), + SearchData(-300, 587759, 'God\'s Wife Tagerem, daughter of the priest Imhotep', 'statue, lower half of a woman|limestone|', 'eg/mobile-large/DP224656.jpg', 0.75), + SearchData(-2650, 551834, 'Ivory label incised with an early hieroglyph that may be the image of a bundle of arrows', 'label, bundle of arrows|wood, ink|', 'eg/mobile-large/01.4.162_EGDP011678.jpg', 0.67), + SearchData(-347, 243770, 'Faience amulet in the form of a cat', 'amulet, cat|clay, glazed|gold and silver', 'gr/mobile-large/DP121831.jpg', 0.75), + SearchData(-2420, 543901, 'Nikare with his Wife and Daughter', 'statue group, nikare, wife, daughter|limestone, paint|', 'eg/mobile-large/DT242488.jpg', 0.80), + SearchData(-347, 243781, 'Faience djed-pillar amulet', 'amulet pendant, djed sign|clay, glazed|gold and silver', 'gr/mobile-large/DP121841.jpg', 0.75), + SearchData(-995, 243764, 'Faience Overseer Ushabti', 'amulet, ushabti|clay, glazed|gold and silver', 'gr/mobile-large/DP121827.jpg', 0.75), + SearchData(-182, 250532, 'Faience statuette of Aphrodite', 'amulet, aphrodite|faience|miscellaneous-faience', 'gr/mobile-large/DP121795.jpg', 0.75), + SearchData(-1425, 329799, 'Head with tripartite wig, probably from a shabti', 'head of a shabti|steatite or serpentinite|', 'eg/mobile-large/LC-2021_41_34_EGDP032381.jpg', 0.80), + SearchData(-2575, 561157, 'Relief Depicting Personified Estates from the Tomb of Akhtihotep', 'relief, akhtihotep|limestone, paint|', 'eg/mobile-large/58.44.2b.jpg', 0.75), + SearchData(-1981, 556559, 'Figure of a Female Dwarf', 'statuette, dwarf, female|faience|', 'eg/mobile-large/1972.48_front.jpg', 0.78), + SearchData(-2465, 552614, 'Statue, young boy', 'statue, young boy|limestone|', 'eg/mobile-large/66.195.jpg', 0.55), + SearchData(-2465, 552238, 'Oarsmen and an Official', 'relief, oarsmen|limestone, paint traces|', 'eg/mobile-large/DP251956.jpg', 0.57), + SearchData(-1295, 590955, 'Two Bottles in the Form of Pomegranates', 'bottle, pomegranate|glass, opaque|', 'eg/mobile-large/DP112594.jpg', 1.00), + SearchData(-995, 243734, 'Faience two-sided amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121814.jpg', 0.75), + SearchData(-349, 548360, 'Torso of a High General', 'statue, torso, striding general|meta-greywacke|', 'eg/mobile-large/DT3997.jpg', 0.80), + SearchData(-1452, 547772, 'Ritual Statuette of Thutmose III', 'statuette, kneeling king, thutmose iii|black bronze, gold inlay|', 'eg/mobile-large/DT537.jpg', 0.80), + SearchData(-1422, 560965, 'Goddess of Lower Egypt', 'statuette, standing goddess, lower egypt|ivory|', 'eg/mobile-large/LC-22_9_5_EGDP028548.jpg', 0.67), + SearchData(-1750, 544249, 'Model of a House', 'model, house, soul house|pottery|', 'eg/mobile-large/07.231.10.jpg', 1.02), + SearchData(-300, 551285, 'Plaque Depicting a Goddess or Queen, and on Opposite Side a King', 'sculptor\'s modeland/or votive, goddess or queen, king on other side|limestone, paint|', 'eg/mobile-large/DP243478.jpg', 0.74), + SearchData(-1371, 547777, 'Male god', 'statue, god|granodiorite|', 'eg/mobile-large/DP-24214-001.jpg', 0.61), + SearchData(-2649, 572178, 'Linen Cloth', 'linen cloth|linen|', 'eg/mobile-large/12.187.50_EGDP020601.jpg', 1.50), + SearchData(-1465, 559860, 'Scarab Inscribed King of Upper and Lower Egypt, Sobek Crocodile', 'scarab; two gods upper and lower egypt, crocodile; notched back|steatite (glazed)|', 'eg/mobile-large/27.3.291_bot.jpg', 1.04), + SearchData(-1961, 544185, 'Seated Statue of King Senwosret I', 'statue, senwosret i, seated|greywacke|', 'eg/mobile-large/25.6 front.jpg', 0.40), + SearchData(-2960, 547442, 'Bull\'s leg', 'furniture leg, bull\'s leg|ivory|', 'eg/mobile-large/LC-06_1162_2_EGDP033541.jpg', 0.72), + SearchData(-1360, 554751, 'Sealing from a Jar with the Name of a king Amenhotep', 'jar sealing, amenhotep iii|mud, pottery, paint|', 'eg/mobile-large/36.2.4_EGDP011892_1.jpg', 0.67), + SearchData(-1810, 560903, 'Tile inlay fragment', 'tile inlay fragment|faience|', 'eg/mobile-large/DP241474.jpg', 0.75), + SearchData(-2465, 543883, 'Relief with running troops', 'relief, running troops|limestone, paint|', 'eg/mobile-large/15.3.1163_EGDP015380.jpg', 1.49), + SearchData(-2575, 543903, 'Striding Figure', 'statue, striding man|quartzite, paint|', 'eg/mobile-large/DT206639.jpg', 0.68), + SearchData(-1371, 547775, 'Head of a Hippopotamus', 'head, hippopotamus|travertine (egyptian alabaster) with traces of gesso and red pigment|', 'eg/mobile-large/DT4210.jpg', 0.91), + SearchData(-1390, 767345, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_202.jpg', 0.81), + SearchData(1285, 447000, 'Mosque Lamp for the Mausoleum of Amir Aydakin al-\'Ala\'i al-Bunduqdar', 'mosque lamp|glass; blown, folded foot, applied handles, enameled, and gilded|glass', 'is/mobile-large/DP170366.jpg', 0.75), + SearchData(-1344, 544680, 'Pair of Clappers', 'music, clapper, arm, hand|hippopotamus ivory|', 'eg/mobile-large/DT11781.jpg', 0.79), + SearchData(-266, 551786, 'Book of the Dead of the Priest of Horus, Imhotep (Imuthes)', 'papyrus, funerary, book of the dead, imouthes, imhotep, imuthes|papyrus, ink|', 'eg/mobile-large/2-35.9.20a–w_EGDP014589-4594.jpg', 4.12), + SearchData(-1991, 544024, 'Cosmetic jar', 'cosmetic jar|travertine (egyptian alabaster)|', 'eg/mobile-large/26.7.1435.jpg', 1.39), + SearchData(-6, 547804, 'Head of Augustus', 'head, augustus|faience|', 'eg/mobile-large/DP247261.jpg', 0.75), + SearchData(-1000, 551038, 'Amulet Plaque with Figure of Thoth', 'amulet, djedmutesankh, thoth|faience|', 'eg/mobile-large/62361.jpg', 1.21), + SearchData(-2649, 561726, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1615_EGDP032340.jpg', 0.67), + SearchData(-2575, 543912, 'Corner of niche from the tomb of Akhtihotep', 'false door niche block, akhtihotep, corner|limestone, paint|', 'eg/mobile-large/58.123_01.jpg', 0.59), + SearchData(-1919, 590947, 'Coffins and Mummy of the Lady Nephthys', 'coffin, mummy, nephthys|painted wood, cartonnage, linen, human remains, mummification material|', 'eg/mobile-large/11.150.15a-b_0028.jpg', 1.50), + SearchData(-2649, 552016, 'Jar', 'jar|pottery|', 'eg/mobile-large/28.2.11_EGDP010293.jpg', 0.70), + SearchData(-2650, 547432, 'Model chisel', 'model chisel|copper alloy|', 'eg/mobile-large/01.4.39_EGDP011643.jpg', 1.78), + SearchData(-2458, 543882, 'King Sahure Accompanied by a Divine Figure', 'statue group, king sahure, nome god|gneiss|', 'eg/mobile-large/DP-1691-03.jpg', 0.73), + SearchData(-166, 559969, 'Funerary amulet depicting one of the Four Sons of Horus, Imsety', 'amulet, four sons of horus, imsety|glass|', 'eg/mobile-large/LC-30_8_283_EGDP027476.jpg', 0.67), + SearchData(-595, 546748, 'Statue of Harbes, called Psamtiknefer, son of Ptahhotep', 'statue, harbes|meta-greywacke|', 'eg/mobile-large/DP-14816-003.jpg', 0.75), + SearchData(-1371, 544853, 'Whip Handle in the Shape of a Horse', 'whip handle, horse|ivory, garnet, paint|', 'eg/mobile-large/DP-17703-001.jpg', 1.33), + SearchData(-2000, 545879, 'Head of a King, Possibly Seankhkare Mentuhotep III', 'head, royal, nemes, mentuhotep iii (?)|limestone|', 'eg/mobile-large/DP323866.jpg', 0.83), + SearchData(-2100, 555983, 'Blade inscribed for the Overseer of Upper Egypt Idi', 'blade|unalloyed copper|', 'eg/mobile-large/29.2.8_EGDP014604.jpg', 0.67), + SearchData(-1197, 544752, 'Head of King Seti II Wearing the Blue Crown', 'head, amenmesse, seti ii, blue crown|quartzite, paint|', 'eg/mobile-large/34.2.2_EGDP011841.jpg', 0.67), + SearchData(-1390, 767343, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_200.jpg', 1.02), + SearchData(-1465, 559857, 'Scarab Inscribed King of Upper and Lower Egypt', 'scarab; nesut bity; simple back|steatite (glazed)|', 'eg/mobile-large/27.3.288_bot.jpg', 1.16), + SearchData(-995, 243801, 'Faience amulet plaque of Isis nourishing a pharaoh', 'amulet plaque, isis nourishing a king|clay, glazed|gold and silver', 'gr/mobile-large/DP121844.jpg', 0.75), + SearchData(-950, 243740, 'Faience amulet of Bes image', 'amulet, bes|clay, glazed|gold and silver', 'gr/mobile-large/DP121817.jpg', 0.75), + SearchData(-2575, 547737, 'Lintel block from the false door of Mery\'s chapel', 'false door, lintel block, mery\'s mastaba|limestone|', 'eg/mobile-large/67.50_EGDP015829.jpg', 2.09), + SearchData(-1537, 547950, 'Head of Ahmose I', 'head, king ahmose|limestone|', 'eg/mobile-large/DP140854.jpg', 0.75), + SearchData(-1891, 544319, 'Writing board', 'writing board|wood, gesso, paint|', 'eg/mobile-large/DP234742.jpg', 1.34), + SearchData(-2520, 552235, 'Sarcophagus of Mindjedef', 'sarcophagus, mindjedef|granite|', 'eg/mobile-large/54.80a-b_EGDP015827.jpg', 1.50), + SearchData(-2649, 568268, 'Inscribed seal impression', 'sealing|clay or mud|', 'eg/mobile-large/LC-10_130_978_EGDP033231.jpg', 1.25), + SearchData(-2650, 570770, 'Jar sealing', 'sealing|clay (unfired)|', 'eg/mobile-large/01.4.163_EGDP012148.jpg', 1.00), + SearchData(-182, 250531, 'Faience statuette of Aphrodite', 'amulet, aphrodite|faience|miscellaneous-faience', 'gr/mobile-large/DP132034.jpg', 0.75), + SearchData(-1182, 545919, 'Figure of an Asiatic captive', 'asiatic captive, furniture decoration|ivory, red and pink pigment, white ground|', 'eg/mobile-large/66.99.50_EGDP020841.jpg', 0.67), + SearchData(-1390, 767344, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_201.jpg', 0.85), + SearchData(-2465, 548225, 'Foot Amulet', 'amulet, foot|carnelian|', 'eg/mobile-large/DP109378.jpg', 1.17), + SearchData(600, 473068, 'Necklace and Pendant Cross', 'necklace|rock crystal, silver mount|lapidary work-crystal', 'md/mobile-large/DP-14824-001.jpg', 0.72), + SearchData(-2575, 545820, 'Seated man', 'statue, seated man|limestone|', 'eg/mobile-large/62.201.1_01.jpg', 0.67), + SearchData(-1465, 547562, 'Carpenter\'s Adze from a Foundation Deposit for Hatshepsut\'s Temple', 'adze, carpenter, hatshepsut, maatkare|wood, bronze or copper alloy, leather|', 'eg/mobile-large/96.4.7.rp.jpg', 1.32), + SearchData(-1390, 767341, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_198.jpg', 1.24), + SearchData(-181, 544118, 'Cat Statuette intended to contain a mummified cat', 'statuette, cat|leaded bronze|', 'eg/mobile-large/DP245141.jpg', 0.75), + SearchData(-1859, 544186, 'Senwosret III as a Sphinx', 'sphinx, senwosret iii; 12th-dyn-king|gneiss|', 'eg/mobile-large/DP247658.jpg', 1.33), + SearchData(-2512, 549541, 'Recumbent Lion', 'statue, lion recumbent|granite|', 'eg/mobile-large/DT2860.jpg', 1.66), + SearchData(-50, 547905, 'A child god, probably Harpokrates', 'statuette, standing harpokrates|leaded bronze, formerly gilded|', 'eg/mobile-large/DP139143.jpg', 1.00), + SearchData(-664, 545368, 'Amulet: Crown of Lower Egypt', 'amulet, crown of lower egypt|faience|', 'eg/mobile-large/LC-10_130_1822_EGDP028703.jpg', 0.67), + SearchData(-1550, 329943, 'Head of Ptah', '|egyptian blue, gold leaf|', 'eg/mobile-large/DP-21434-002.jpg', 0.74), + SearchData(-2650, 547399, 'Clay jar sealing Clay jar sealing impressed with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.99_EGDP012141.jpg', 1.00), + SearchData(165, 547698, 'Plaster Portrait Mask of a Youth', 'mask, youth portrait|plaster, linen, paint, lapis lazuli, glass|', 'eg/mobile-large/DT278928.jpg', 1.26), + SearchData(-1465, 559830, 'Scarab Inscribed for the King of Upper and Lower Egypt Maatkare (Hatshepsut)', 'scarab; maatkare, nesut bity; notched back|steatite (glazed)|', 'eg/mobile-large/27.3.245_bot.jpg', 0.97), + SearchData(-199, 246747, 'Terracotta head of a woman', 'head of a woman|terracotta|terracottas', 'gr/mobile-large/DP121878.jpg', 1.00), + SearchData(-2649, 570883, 'Linen Cloth', 'linen cloth|linen|', 'eg/mobile-large/12.187.47_EGDP020600.jpg', 1.50), + SearchData(-1371, 544068, 'Gazelle', 'figurine, gazelle|ivory (elephant), wood, blue-pigment inlay|', 'eg/mobile-large/DP-17702-001.jpg', 1.07), + SearchData(-1344, 545756, 'Goblet Inscribed with the Names of King Amenhotep IV and Queen Nefertiti', 'cup, amenhotep iv, nefertiti. goblet|travertine (egyptian alabaster)|', 'eg/mobile-large/DT11826.jpg', 0.80), + SearchData(-1353, 544683, 'Statue of two men and a boy that served as a domestic icon', 'statue group, two men, boy|limestone, paint|', 'eg/mobile-large/DP206147.jpg', 0.75), + SearchData(-1390, 767352, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_209.jpg', 1.42), + SearchData(-347, 243726, 'Jasper amulet of headrest', 'amulet, pillow charm|jasper|gold and silver', 'gr/mobile-large/DP121808.jpg', 1.33), + SearchData(-828, 544874, 'Statuette of Amun', 'statuette, amun, standing, god|gold|', 'eg/mobile-large/DT553.jpg', 0.80), + SearchData(-2649, 561718, 'Cylinder seal', 'cylinder seal|gray stone|', 'eg/mobile-large/26-7-12.jpg', 0.68), + SearchData(-167, 243747, 'Faience amulet of Mut with double crown', 'amulet, mut with double crown|clay, glazed|gold and silver', 'gr/mobile-large/DP121820.jpg', 0.75), + SearchData(-2649, 561721, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1617_EGDP032354.jpg', 0.67), + SearchData(-1368, 249682, 'Glass pendant in the form of crescent horns', 'pendant in the form of a crescent|glass|glass', 'gr/mobile-large/DP121031.jpg', 0.75), + SearchData(-1412, 554920, 'Funerary Cone of the Fourth Prophet of Amun Kaemamun', 'cone, circular impression, kaemamun, seal-bearer, lower egypt, prophet, amun|pottery|', 'eg/mobile-large/30.6.50-acc.jpg', 1.06), + SearchData(-1981, 546288, 'Flail of Hapiankhtifi', 'scepter, hapiankhtifi|wood, gold leaf, carnelian, faience|', 'eg/mobile-large/DP354266.jpg', 0.74), + SearchData(-1295, 330125, 'Bald-headed Man Wearing Gold Collars', '|steatite or schist|', 'eg/mobile-large/DP-20855-002.jpg', 0.75), + SearchData(549, 446236, 'Pendant Cross', 'pendant|bone|ivories and bone', 'is/mobile-large/sf12-182-109s1.jpg', 0.80), + SearchData(-2630, 543904, 'Wall tiles from the funerary apartments of king Djoser', 'tile, apartments of king djoser|faience|', 'eg/mobile-large/DT258569.jpg', 0.67), + SearchData(300, 466583, 'Sarcophagus Lid with Last Judgement', 'sarcophagus lid|marble|sculpture-stone', 'md/mobile-large/DT271481.jpg', 5.41), + SearchData(-1452, 544860, 'Drinking Cup', 'cup, nuzi, mitanni, button-base|glassy faience, gold|', 'eg/mobile-large/1228_3B_01BBr2.jpg', 0.75), + SearchData(-2649, 574279, 'Shell Amulet', 'amulet, shell|carnelian|', 'eg/mobile-large/10.130.2436_EGDP021795.jpg', 1.00), + SearchData(-1990, 544039, 'Cosmetic Vessel in the Shape of a Cat', 'vessel, cat|travertine (egyptian alabaster), copper, quartz crystal, paint|', 'eg/mobile-large/DT532.jpg', 0.80), + SearchData(449, 466266, 'Dionysos', 'plaque|bone|bone', 'md/mobile-large/sf1993-516-1s1.jpg', 0.46), + SearchData(-594, 590745, 'Barque Sphinx', 'barque sphinx|leaded bronze|', 'eg/mobile-large/DP247004.jpg', 1.33), + SearchData(-181, 544864, 'Statuette of the Goddess Taweret', 'statuette of taweret|glassy faience|', 'eg/mobile-large/DP243443.jpg', 0.75), + SearchData(-1745, 556561, 'Magic Wand', 'magic wand|ivory|', 'eg/mobile-large/86.1.91_EGDP011957.jpg', 1.78), + SearchData(-202, 329895, 'Statuette of a flutist', 'bronze, non-royal, private, flutist|copper alloy|', 'eg/mobile-large/LC-2021_41_117_EGDP032491.jpg', 0.67), + SearchData(-2557, 545777, 'Ball', 'ball|clay or mud|', 'eg/mobile-large/20-2-49-50.jpg', 1.95), + SearchData(-1465, 559831, 'Scarab Inscribed King of Upper and Lower Egypt Maatkare, Having Dominion', 'scarab; maatkare, nesut bity, wasti; detailed back|steatite (glazed)|', 'eg/mobile-large/27.3.249_bot.jpg', 0.85), + SearchData(-1390, 544794, 'Stela of Senu', 'stela, senu, imsety, hapy|limestone|', 'eg/mobile-large/LC-12_182_39_EGDP029823.jpg', 0.67), + SearchData(-1367, 244340, 'Bronze handle of a jug', 'handle of a jug|bronze|bronzes', 'gr/mobile-large/DP2297.jpg', 1.00), + SearchData(-2417, 577367, 'Tomb chapel of Raemkai: South wall of the entrance corridor', 'relief, tomb chapel, raemkai, offering bearers, statute, slaughtering cattle|limestone, paint|', 'eg/mobile-large/EG599.jpg', 0.99), + SearchData(-1465, 549730, 'Scarab Inscribed King of Upper and Lower Egypt Maatkare, Having Dominion', 'scarab; maatkare, nesut bity, wasti; detailed back|steatite (glazed)|', 'eg/mobile-large/27.3.248_bot.jpg', 0.85), + SearchData(-867, 561047, 'Wedjat Eye Amulet', 'amulet, wedjat eye|faience, aragonite|', 'eg/mobile-large/26.7.1032_EGDP011735.jpg', 1.50), + SearchData(-1339, 544689, 'Canopic Jar (07.226.1) with a Lid Depicting a Queen (30.8.54)', 'canopic jar lid to the jar 07.226.1 of kiya, secondary queen of akhenaten|travertine (egyptian alabaster), blue glass, obsidian, unidentified stone|', 'eg/mobile-large/DT227703.jpg', 0.68), + SearchData(-2465, 543894, 'Relief Fragment with a Ship Under Sail', 'relief, ship under sail|limestone, paint traces|', 'eg/mobile-large/DT256967.jpg', 1.25), + SearchData(-1390, 767348, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_205.jpg', 1.27), + SearchData(-1981, 546286, 'Model Broad Collar of Hapiankhtifi', 'broad collar, hapiankhtifi|faience, blue green and black|', 'eg/mobile-large/12.183.12_0013.jpg', 1.55), + SearchData(-2649, 570882, 'Linen Cloth', 'linen cloth|linen|', 'eg/mobile-large/12.187.46_EGDP020594.jpg', 1.50), + SearchData(-347, 243780, 'Faience djed-pillar amulet', 'amulet pendant, djed sign|clay, glazed|gold and silver', 'gr/mobile-large/DP121839.jpg', 0.75), + SearchData(-995, 243730, 'Faience amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121810.jpg', 0.75), + SearchData(1750, 31890, 'Pair of Stirrups', 'stirrups|iron, gold|equestrian equipment-stirrups', 'aa/mobile-large/LC-36_25_534a_b-002.jpg', 1.78), + SearchData(-2649, 547738, 'Relief Fragment depicting offering bearers from the mastaba of Idut, daughter of King Unas (?)', 'relief, offering bearers|limestone|', 'eg/mobile-large/66-99-80.jpg', 1.98), + SearchData(-2539, 551091, 'False door niche block of Merykhufu', 'false door niche block, merykhufu|limestone|', 'eg/mobile-large/68.13a-b_EGDP015343.jpg', 1.41), + SearchData(-700, 587591, 'Head of a goddess, probably Mut, for attachment to a processional barque (?)', 'head, goddess mut, protome|cupreous alloy, gold leaf, formerly inlaid|', 'eg/mobile-large/DP206149.jpg', 0.75), + SearchData(-2649, 545798, 'Jar sealing', 'sealing, jar|mud or clay|', 'eg/mobile-large/LC-60_95_EGDP033245.jpg', 1.00), + SearchData(-347, 243761, 'Faience amulet in the form of a lion-headed deity', 'amulet, bast|clay, glazed|gold and silver', 'gr/mobile-large/DP231279.jpg', 0.75), + SearchData(-1353, 545907, 'Funerary Figure of Isis, Singer of the Aten', 'funerary figure, shabti, isis, singer, aten|limestone|', 'eg/mobile-large/66.99.38_EGDP013435.jpg', 0.67), + SearchData(-351, 544888, 'Nectanebo II Offers to Osiris Hemag', 'relief, nectanebo ii|granodiorite|', 'eg/mobile-large/12.182.4c_EGDP011900.jpg', 1.50), + SearchData(-1350, 239937, 'Glass krateriskos (unguent jar)', 'krateriskos|glass|glass', 'gr/mobile-large/DP153938.jpg', 1.00), + SearchData(-2649, 561727, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1614_EGDP032333.jpg', 0.67), + SearchData(-167, 243729, 'Faience amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121809.jpg', 0.75), + SearchData(-289, 547900, 'Miniature broad collar', 'necklace, broad collar, miniature|gold, carnelian, turquoise, lapis lazuli|', 'eg/mobile-large/DP139141.jpg', 1.00), + SearchData(-3000, 543866, 'Libation Dish Depicting Ka-Arms Presenting an Ankh-Sign', 'dish, libation|greywacke|', 'eg/mobile-large/DP249937.jpg', 0.75), + SearchData(167, 245500, 'Bronze statuette of a horseman', 'statuette of a horseman|bronze|bronzes', 'gr/mobile-large/DP20160.jpg', 1.00), + SearchData(-249, 246749, 'Terracotta head of a woman', 'head of a woman|terracotta|terracottas', 'gr/mobile-large/DP121879.jpg', 1.00), + SearchData(-2649, 544001, 'Offering slab for seven oils of Ankhwadjes', 'offering slab, seven oils|travertine (egyptian alabaster)|', 'eg/mobile-large/11-150-1a.jpg', 2.42), + SearchData(-2551, 569692, 'Specimen of mortar from the Great Pyramid', 'mortar fragment, giza pyramid|mortar (shell, quartz, bricks)|', 'eg/mobile-large/23.187_EGDP017918.jpg', 1.33), + SearchData(-995, 243733, 'Faience amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121813.jpg', 0.75), + SearchData(-2394, 561064, 'Ewer, from basin and ewer set', 'ewer|arsenical copper, surface enriched in arsenic|', 'eg/mobile-large/LC-26_9_13_EGDP029546.jpg', 1.50), + SearchData(-181, 590939, 'Statuette of Anubis', 'anubis, jackal-headed|plastered and painted wood|', 'eg/mobile-large/38.5_EGDP022863.jpg', 0.67), + SearchData(-347, 243798, 'Faience Wedjat-eye amulet', 'amulet, eye, udjati|clay, glazed|gold and silver', 'gr/mobile-large/DP121843.jpg', 1.33), + SearchData(-349, 547556, 'Inlay Depicting "Horus of Gold"', 'inlay, falcon, golden horus|faience|', 'eg/mobile-large/DP240847.jpg', 0.74), + SearchData(-2650, 547431, 'Model adze blade', 'model adze blade|copper alloy|', 'eg/mobile-large/01.4.38_EGDP011640.jpg', 1.78), + SearchData(-484, 243719, 'Red jasper amulet of tyet', 'amulet, girdle tie|jasper, red|gold and silver', 'gr/mobile-large/DP121807.jpg', 0.75), + SearchData(-167, 253575, 'Wood statuette of Hekate', 'statuette of hekate|wood, juniper|miscellaneous-wood', 'gr/mobile-large/DP145604.jpg', 1.00), + SearchData(-3000, 544077, 'Lion Cub', 'statuette, animal, lion|quartzite|', 'eg/mobile-large/DP244929.jpg', 0.74), + SearchData(-350, 548439, 'Inlay Depicting a Falcon with Spread Wings', 'inlay, falcon|faience|', 'eg/mobile-large/DP243445.jpg', 1.33), + SearchData(-274, 547699, 'Head Attributed to Arsinoe II', 'head of a statue of arsinoe ii|limestone (indurated)|', 'eg/mobile-large/DT10849.jpg', 0.80), + SearchData(-265, 548230, 'Face attributed to Ptolemy II Philadelphos or a contemporary', 'head fragment, ptolemy ii|greywacke|', 'eg/mobile-large/DP-24213-001.jpg', 0.75), + SearchData(-347, 244461, 'Bronze statuette of Isis with infant Horus', 'statuette of isis and horus|bronze|bronzes', 'gr/mobile-large/DP20096.jpg', 0.92), + SearchData(350, 473395, 'Lute', 'lute|wood with traces of paint|woodwork-miscellany', 'md/mobile-large/DP302641.jpg', 1.33), + SearchData(-2465, 688030, 'Hand and Foot Amulets', 'amulet, foot|carnelian|', 'eg/mobile-large/DP109378.jpg', 1.17), + SearchData(-2649, 552017, 'Jar with flat base', 'jar, flat base|pottery|', 'eg/mobile-large/28.2.6_EGDP010303.jpg', 0.91), + SearchData(-347, 243768, 'Faience amulet in the form of a lion', 'amulet, lion|clay, glazed|gold and silver', 'gr/mobile-large/DP121830.jpg', 1.33), + SearchData(-2462, 545827, 'Head of male statue, perhaps Babaef', 'head, male statue, perhaps babaef|granite|', 'eg/mobile-large/64.66.3_01.jpg', 0.72), + SearchData(-2551, 543891, 'Relief with the head of a female personification of an estate', 'relief, female personification of an estate|limestone|', 'eg/mobile-large/DT259179.jpg', 0.80), + SearchData(-1109, 329785, 'Two-Sided Plaque with Gazelles', '|faience|', 'eg/mobile-large/LC-2021_41_20_EGDP032448.jpg', 1.50), + SearchData(-1272, 554769, 'Stelophorous Statue of Bay', 'bay, stela, stelephorous|limestone|', 'eg/mobile-large/DP231293.jpg', 0.75), + SearchData(-2551, 543892, 'Relief with a billy goat', 'relief, billy goat|limestone|', 'eg/mobile-large/DT212583.jpg', 1.25), + SearchData(-484, 243718, 'Red jasper amulet of tyet', 'amulet, girdle tie|jasper, red|gold and silver', 'gr/mobile-large/DP121806.jpg', 0.75), + SearchData(-2650, 547395, 'Tweezers', 'tweezers|copper alloy|', 'eg/mobile-large/01.4.31_EGDP011693.jpg', 1.78), + SearchData(-2649, 570919, 'Amulet, leg', 'amulet, leg|carnelian|', 'eg/mobile-large/15-43-336.jpg', 0.61), + SearchData(-2575, 543899, 'The King\'s Acquaintances Memi and Sabu', 'statue, standing pair, memi, sabu|limestone, paint|', 'eg/mobile-large/DT8836.jpg', 0.80), + SearchData(-167, 244503, 'Bronze statuette of Horus', 'statuette of horus as an infant|bronze|bronzes', 'gr/mobile-large/DP20109.jpg', 0.96), + SearchData(-347, 243744, 'Faience amulet of Isis and Horus', 'amulet, isis|clay, glazed|gold and silver', 'gr/mobile-large/DP121819.jpg', 0.75), + SearchData(-347, 243766, 'Faience amulet of Ra Horakhty', 'amulet, hawk|clay, glazed|gold and silver', 'gr/mobile-large/DP121829.jpg', 0.75), + SearchData(-262, 249622, 'Glass mosaic relief fragment', 'mosaic inlay|glass|glass', 'gr/mobile-large/DP121782.jpg', 0.75), + SearchData(-2550, 558193, 'Speciman of Mortar from the Great Pyramid', 'mortar, giza pyramid|mortar|', 'eg/mobile-large/17.6.143_EGDP017920.jpg', 1.33), + SearchData(-1514, 587532, 'Relief with the Head of Amenhotep I', 'relief, head, king, amenhotep i|limestone|', 'eg/mobile-large/45.2.7_EGDP011785.jpg', 1.00), + SearchData(1000, 451717, 'Bowl with Eagle', 'bowl|earthenware; luster-painted on opaque white glaze|ceramics', 'is/mobile-large/DP221332.jpg', 0.75), + SearchData(-1882, 544232, 'Pectoral and Necklace of Sithathoryunet with the Name of Senwosret II', 'necklace, pectoral, sithathoryunet-view-jewelry, senwosret ii|gold, carnelian, lapis lazuli, turquoise, garnet (pectoral) gold, carnelian, lapis lazuli, turquoise, green feldspar (necklace)|', 'eg/mobile-large/DT531.jpg', 1.25), + SearchData(-712, 587760, 'Kushite priest wearing garment with leopard\'s head and tassels, subsequently adapted for a king', 'statue, priest|leaded bronze, gold leaf|', 'eg/mobile-large/DP236108.jpg', 0.75), + SearchData(-995, 243778, 'Faience amulet', 'amulet pendant|clay, glazed|gold and silver', 'gr/mobile-large/DP121838.jpg', 0.75), + SearchData(-2649, 561725, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/26-7-8.jpg', 0.85), + SearchData(1287, 450409, 'Enameled and Gilded Bottle', 'bottle|glass, greenish; blown, folded foot; enameled and gilded|glass', 'is/mobile-large/DP170374.jpg', 0.75), + SearchData(-1492, 548731, 'Cosmetic Jar Sealed with Linen', 'jar|pottery, linen|', 'eg/mobile-large/LC-36_3_68_EGDP026072.jpg', 1.25), + SearchData(-2520, 543910, 'Stand for an Offering Basin with the Name of King Khafre', 'offering stand, king khafre|gneiss|', 'eg/mobile-large/2833.jpg', 0.55), + SearchData(-2575, 545821, 'Group statue', 'statue group, lower section|limestone|', 'eg/mobile-large/62.201.2_01.jpg', 1.20), + SearchData(-167, 243802, 'Faience amulet plaque with a group of deities', 'amulet plaque, group of deities|clay, glazed|gold and silver', 'gr/mobile-large/DP121845.jpg', 1.33), + SearchData(-1878, 544184, 'Face of Senwosret III', 'head, senwosret iii; 12th-dyn-king|red quartzite|', 'eg/mobile-large/DP323868.jpg', 0.80), + SearchData(-1391, 544478, 'Standing figure of Amenhotep III', 'statuette, standing king, amenhotep iii|chlorite schist|', 'eg/mobile-large/DP-17706-001.jpg', 0.71), + SearchData(-2650, 547429, 'Model harpoon', 'model harpoon|copper alloy|', 'eg/mobile-large/01.4.36_EGDP011633.jpg', 1.78), + SearchData(-1331, 544690, 'Head of Tutankhamun', 'head, tutankhamun, god\'s hand|indurated limestone|', 'eg/mobile-large/DT546.jpg', 0.80), + SearchData(-249, 547904, 'Bes-image of the god Hor-Asha-Khet', 'statuette, bes-image, hor-asha-khet|bronze; gold, electrum, auriferous-silver, copper and copper-alloy inlays|', 'eg/mobile-large/DP139129.jpg', 1.00), + SearchData(-1465, 559832, 'Scarab Inscribed King of Upper and Lower Egypt Maatkare, Having Dominion', 'scarab; maatkare, nesut bity, wasti; detailed back|steatite (glazed)|', 'eg/mobile-large/27.3.250_bot.jpg', 0.84), + SearchData(-361, 546751, 'Bust of an Official', 'bust, official|greywacke|', 'eg/mobile-large/25.2.1_EGDP017888.jpg', 0.67), + SearchData(-1353, 544060, 'Attendants in a Procession', 'talatat, attendants, foreigners|limestone, paint|', 'eg/mobile-large/DT8198.jpg', 1.58), + SearchData(-924, 553738, 'Statue of the God Reshef', 'statue, reshef, reshep, foreign, mace, shield, war|limestone|', 'eg/mobile-large/89.2.215_EGDP011775.jpg', 0.67), + SearchData(-2649, 561719, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1613_EGDP032327.jpg', 0.67), + SearchData(149, 544919, 'Figure of Isis-Aphrodite', 'statuette, standing goddess, isis-aphrodite|terracotta painted brown, black, red, and pink on white engobe|', 'eg/mobile-large/DT6643.jpg', 0.60), + SearchData(-2465, 548367, 'Hand Amulet', 'amulet, hand|carnelian|', 'eg/mobile-large/chrDP109378.jpg', 1.17), + SearchData(-589, 547705, 'Relief from the Palace of Apries in Memphis', 'relief, wall, apries|limestone|', 'eg/mobile-large/09.183.1a_EGDP011859.jpg', 0.92), + SearchData(-2649, 570914, 'Shell Amulet', 'amulet, shell|carnelian|', 'eg/mobile-large/10.130.2435_EGDP021796.jpg', 1.00), + SearchData(-1311, 544776, 'Votive stela of Userhat', 'stela, userhat|limestone, paint|', 'eg/mobile-large/DP226728.jpg', 1.13), + SearchData(-867, 546227, 'Bastet on a throne decorated with the decans', 'statuette, bastet|faience|', 'eg/mobile-large/LC-44_4_19_EGDP030618.jpg', 0.67), + SearchData(-2650, 547427, 'Model chisel', 'model chisel|copper alloy|', 'eg/mobile-large/01.4.34_EGDP011635.jpg', 1.78), + SearchData(1482, 702966, 'Shirt of Mail and Plate of Al-Ashraf Sayf ad-Din Qaitbay (ca. 1416/18–1496), 18th Burji Mamluk Sultan of Egypt', 'shirt of mail and plate|steel, iron, copper alloy, gold|mail', 'aa/mobile-large/DP-891-001.jpg', 0.75), + SearchData(-2650, 547400, 'Clay jar sealing Clay jar sealing impressed with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.100_EGDP012137.jpg', 1.25), + SearchData(-2465, 551279, 'Bowl', 'bowl|red polished pottery|', 'eg/mobile-large/MMA28.2.7.jpg', 1.55), + SearchData(-2557, 551049, 'Box Coffin and Rope', 'coffin, rope|wood (tamarisk), pigment, fiber|', 'eg/mobile-large/12.187.54_EGDP015378.jpg', 1.18), + SearchData(-1371, 544519, 'Mechanical Dog', 'figurine, dog|ivory (elephant)|', 'eg/mobile-large/0227r2_SEC501K.jpg', 1.27), + SearchData(-1344, 544695, 'Amarna letter: Royal Letter from Ashur-uballit, the king of Assyria, to the king of Egypt', 'tablet, amarna letter|clay|', 'eg/mobile-large/24.2.11_EGDP021806.jpg', 0.75), + SearchData(-1700, 548325, 'Double "Tell el-Yahudiya" Vase with Incised Lotus Flowers, probably manufactured in Egypt', 'vase, double, yahudiya ware|pottery, smoke blackening, white gypsum|', 'eg/mobile-large/23.3.39.jpg', 1.09), + SearchData(-690, 549533, 'Block Statue of Ankhwennefer', 'block statue, ankhwennefer|limestone|', 'eg/mobile-large/DT7022.jpg', 0.80), + SearchData(-175, 259138, 'Plaster cast of a metal emblema of Isis-Tyche', 'cast|plaster|miscellaneous-plaster', 'gr/mobile-large/sftr236ab2012.jpg', 0.93), + SearchData(-234, 547773, 'Head of Ptolemy II or III', 'head, ptolemy ii or iii|black bronze|', 'eg/mobile-large/DP-24219-001.jpg', 0.81), + SearchData(-945, 549169, 'Cult Image of the God Ptah', 'statuette, ptah|lapis lazuli|', 'eg/mobile-large/DP142956.jpg', 0.75), + SearchData(-1390, 767346, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_203.jpg', 1.35), + SearchData(-2524, 543998, 'Statue of Kaipunesut', 'statue, kaipunesut|wood, paint|', 'eg/mobile-large/268829.jpg', 0.48), + SearchData(-995, 243741, 'Faience amulet of Bes image', 'amulet, bes|clay, glazed|gold and silver', 'gr/mobile-large/DP121815.jpg', 0.75), + SearchData(-347, 243775, 'Faience snake amulet', 'amulet, snake|clay, glazed|gold and silver', 'gr/mobile-large/DP121835.jpg', 0.75), + SearchData(-1344, 544693, 'Face from a Composite Statue, probably Queen Tiye', 'head, queen tiye|quartzite|', 'eg/mobile-large/DT11514.jpg', 0.77), + SearchData(774, 449211, 'Panel', 'panel|wood (fig); mosaic with bone and four different types of wood|wood', 'is/mobile-large/DP160115.jpg', 3.32), + SearchData(-2490, 543935, 'Seated Statue of King Menkaure', 'statue, menkaure seated|indurated limestone|', 'eg/mobile-large/DP109397.jpg', 0.65), + SearchData(-199, 246746, 'Terracotta head of a woman', 'head of a woman|terracotta|terracottas', 'gr/mobile-large/DP121877.jpg', 1.00), + SearchData(-1344, 544861, 'Spindle Bottle with Handle', 'bottle, spindle, handle|glass|', 'eg/mobile-large/DT2547.jpg', 0.78), + SearchData(-2650, 547439, 'Squat jar', 'jar|magnesite|', 'eg/mobile-large/01.4.85_EGDP011686.jpg', 1.33), + SearchData(-347, 243753, 'Faience amulet of Taweret', 'amulet, thueris|clay, glazed|gold and silver', 'gr/mobile-large/DP121822.jpg', 0.75), + SearchData(-1994, 545393, 'Stela of the Overseer of the Fortress Intef', 'stela, intef, iti|limestone|', 'eg/mobile-large/DP322110.jpg', 1.72), + SearchData(-347, 243765, 'Faience amulet of Ra Horakhty', 'amulet, hawk|clay, glazed|gold and silver', 'gr/mobile-large/DP121828.jpg', 0.75), + SearchData(-2575, 544531, 'Facsimile Painting of Geese, Tomb of Nefermaat and Itet', 'facsimile, nefermaat and itet, meidum geese|tempera on paper|', 'eg/mobile-large/DT226227.jpg', 5.70), + SearchData(-2040, 544207, 'Sarcophagus of the Hathor Priestess Henhenet', 'sarcophagus, henhenet|limestone, sandstone, paint|', 'eg/mobile-large/07.230.1a-b_EGDP011903.jpg', 1.30), + SearchData(-347, 243749, 'Faience amulet of Thoth', 'amulet, khnum|clay, glazed|gold and silver', 'gr/mobile-large/DP121821.jpg', 0.75), + SearchData(-1422, 554490, 'Goddess of Upper Egypt', 'statuette, standing goddess, upper egypt|ivory|', 'eg/mobile-large/LC-22_9_4_EGDP028554.jpg', 0.67), + SearchData(-347, 243776, 'Faience snake amulet', 'amulet, snake|clay, glazed|gold and silver', 'gr/mobile-large/DP121836.jpg', 0.75), + SearchData(-361, 551898, 'Composite Papyrus Capital', 'column capital, papyrus|sandstone, paint|', 'eg/mobile-large/10.177.2_EGDP018080.jpg', 1.28), + SearchData(-2649, 552018, 'Jar with pointed base and intact seal', 'jar, sealed|pottery|', 'eg/mobile-large/28.2.20_EGDP010302.jpg', 0.67), + SearchData(-2650, 570769, 'Clay jar sealing impressed faintly with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.103_EGDP012117.jpg', 1.00), + SearchData(-2649, 569690, 'Stake?', 'stake (?)|wood|', 'eg/mobile-large/97-4-1.jpg', 5.41), + SearchData(-1353, 566533, 'Head of Akhenaten or Nefertiti', 'head, king, queen|gypsum plaster|', 'eg/mobile-large/DP261897.jpg', 0.84), + SearchData(-1304, 545870, 'Statuette of Kary', 'statue, kary|wood|', 'eg/mobile-large/LC-65_114_EGDP030140.jpg', 0.67), + SearchData(-1390, 767356, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_213.jpg', 0.91), + SearchData(-995, 243794, 'Faience Wedjat-eye amulet', 'amulet, eye, udjati|clay, glazed|gold and silver', 'gr/mobile-large/DP121842.jpg', 1.33), + SearchData(-1468, 547684, 'Artist\'s Gridded Sketch of Senenmut', 'ostracon, figured, grid, senenmut|limestone, ink|', 'eg/mobile-large/DT1534.jpg', 0.81), + SearchData(-1891, 557549, 'Coffin of Ameny', 'coffin, ameny, rectangular|wood, paint|', 'eg/mobile-large/11.150.39a-b-gc-detail.jpg', 0.78), + SearchData(-2649, 551016, 'Bowl', 'bowl|pottery|', 'eg/mobile-large/O.C.2997_EGDP011332.jpg', 1.78), + SearchData(170, 547334, 'Shroud of a Woman Wearing a Fringed Tunic', 'shroud, woman, fringed tunic|linen, paint (tempera)|', 'eg/mobile-large/EG198.jpg', 0.49), + SearchData(-1550, 555062, 'Funerary Cone of Heby', 'cone, circular impression, heby|pottery|', 'eg/mobile-large/bw13.180.72.jpg', 1.05), + SearchData(-594, 545213, 'Figure of a Baboon', 'amulet, baboon|faience|', 'eg/mobile-large/DT8831.jpg', 0.80), + SearchData(-181, 544092, 'Striding Thoth', 'statuette, thoth|faience|', 'eg/mobile-large/DP240843.jpg', 0.75), + SearchData(-1200, 329885, 'Pectoral Fragment', '|faience|', 'eg/mobile-large/LC-2021_41_107_EGDP032464.jpg', 1.50), + SearchData(-2040, 659735, 'Fragment of a torus molding from the shrine of a royal woman within the temple of Mentuhotep II', 'relief, nebhepetre mentuhotep\'s wives, torus molding|limestone, paint|', 'eg/mobile-large/06.1231.77.jpg', 2.90), + SearchData(-1465, 560252, 'Name Stone of Senenmut', 'name stone, tally, senenmut|limestone|', 'eg/mobile-large/36-3-241_ac.jpg', 0.67), + SearchData(-2649, 561723, 'Cylinder seal', 'cylinder seal|steatite|', 'eg/mobile-large/LC-10_130_1619_EGDP032398.jpg', 0.80), + SearchData(-347, 243716, 'Jasper amulet in the form of a frog', 'amulet pendant, frog|jasper|gold and silver', 'gr/mobile-large/DP121804.jpg', 0.75), + SearchData(-347, 244462, 'Bronze statuette of Osiris', 'statuette of osiris|bronze|bronzes', 'gr/mobile-large/DP20095.jpg', 0.92), + SearchData(-589, 544884, 'Fragment of a Royal Head, Probably Apries', 'head fragment, apries|black diorite|', 'eg/mobile-large/DT3837.jpg', 0.80), + SearchData(-347, 244464, 'Bronze portrait head of a pharaoh', 'portrait head of a pharaoh|bronze|bronzes', 'gr/mobile-large/DP20099.jpg', 0.92), + SearchData(-1595, 548350, 'Bucranium', 'bucranium, horns, antlers, pan grave, medjayu|horn, bone, paint|', 'eg/mobile-large/DP100986.jpg', 1.13), + SearchData(-1473, 547553, 'Head from an Osiride Statue of Hatshepsut', 'head, hatshepsut, niche, double crown; hatshepsut-sculpture|limestone, paint|', 'eg/mobile-large/21M_CAT074R4.jpg', 0.79), + SearchData(-1919, 546275, 'Canopic Chest of Senbi', 'canopic chest, senbi|wood (ziziphus sp.), paint, string|', 'eg/mobile-large/11.150.17a_1-3_0009.jpg', 0.82), + SearchData(-1390, 767347, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_204.jpg', 0.96), + SearchData(-1919, 544227, 'Hippopotamus ("William")', 'figurine, hippopotamus ("william")|faience|', 'eg/mobile-large/DP248993.jpg', 0.75), + SearchData(-2475, 545825, 'Headless statue of Babaef as younger man', 'statue, babaef as younger man|limestone|', 'eg/mobile-large/64.66.1_EGDP014892.jpg', 0.50), + SearchData(-1390, 767351, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_208.jpg', 0.89), + SearchData(-1966, 544206, 'Lintel of Amenemhat I and Deities', 'relief, lintel, amenemhat i|limestone, paint|', 'eg/mobile-large/DP322051.jpg', 3.82), + SearchData(-2639, 543939, 'Squat jar with two lugs', 'jar, squat, two lugs|probably pegmatitic quartz diorite|', 'eg/mobile-large/24.7.5.jpg', 1.71), + SearchData(-550, 553045, 'Statue of a goddess, probably Nehemetaui or Nebethetepet', 'statuette, nehemetaui or nebethetepet|cupreous metal|', 'eg/mobile-large/DP237849.jpg', 0.75), + SearchData(-2551, 574407, 'Block of Relief', 'relief|limestone, paint|', 'eg/mobile-large/09.180.6.jpg', 2.09), + SearchData(-2650, 551833, 'Clay jar sealing', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/LC-01_4_164_EGDP024739.jpg', 1.50), + SearchData(-1353, 544696, 'Amarna letter: Royal Letter from Abi-milku of Tyre to the king of Egypt', 'tablet, amarna letter|clay (unfired)|', 'eg/mobile-large/24.2.12_EGDP021809.jpg', 0.75), + SearchData(-1465, 559829, 'Scarab Inscribed for the King of Upper and Lower Egypt Maatkare (Hatshepsut)', 'scarab; maatkare, nesut bity; notched back|steatite (glazed)|', 'eg/mobile-large/27.3.244_bot.jpg', 0.97), + SearchData(-610, 546746, 'Statuette of a Royal (?) Woman with the Cartouches of Necho II on her Arms', 'female statuette with cartouches of necho ii on her arms|silver|', 'eg/mobile-large/DP139132.jpg', 1.00), + SearchData(-1810, 545718, 'Kamares jar', 'cup, aegean|pottery, paint|', 'eg/mobile-large/DT327376.jpg', 0.80), + SearchData(1327, 444812, 'Pair of Minbar Doors', 'doors|wood (rosewood and mulberry); carved and inlaid with carved ivory, ebony, and other woods|wood', 'is/mobile-large/DP235241.jpg', 0.64), + SearchData(-347, 243732, 'Faience amulet in the form of the dwarf god Pataikos', 'amulet, ptah-seker|clay, glazed|gold and silver', 'gr/mobile-large/DP121812.jpg', 0.75), + SearchData(-351, 544887, 'God Horus Protecting King Nectanebo II', 'statue, horus, king nectanebo ii|meta-greywacke|', 'eg/mobile-large/DP152085.jpg', 0.75), + SearchData(1505, 24426, 'Standard Head', 'standard head|steel, iron|shafted weapons', 'aa/mobile-large/DP158310.jpg', 0.61), + SearchData(-2551, 543890, 'Relief fragment with king Khufu\'s cattle', 'relief, king khufu\'s cattle|limestone|', 'eg/mobile-large/DT244362.jpg', 1.67), + SearchData(-347, 243769, 'Faience amulet in the form of a ram', 'amulet, ram|clay, glazed|gold and silver', 'gr/mobile-large/DP131208.jpg', 1.33), + SearchData(-181, 788904, 'Three Arrowheads', 'three arrowheads|bronze|archery equipment-arrowheads', 'aa/mobile-large/LC-08_99_18-001.jpg', 1.25), + SearchData(-2051, 544008, 'Statue of Nebhepetre Mentuhotep II in the Jubilee Garment', 'statue, standing king, mentuhotep, nebhepetre|sandstone, paint|', 'eg/mobile-large/DP302395.jpg', 0.45), + SearchData(-1070, 587598, 'Statue of the God Ptah', 'statue, ptah|bronze, gold leaf, glass|', 'eg/mobile-large/DP216330.jpg', 0.69), + SearchData(-1722, 557108, 'Scarab Incised with Hieroglyphs and Scroll', 'scarab, scroll, wedjat, hieroglyphs, sa, vase|steatite, traces of green glaze|', 'eg/mobile-large/20.1.138_EGDP020342.jpg', 0.80), + SearchData(-690, 547694, 'Block Statue of a Prophet of Montu and Scribe Djedkhonsuefankh, son of Khonsumes and Taat', 'block statue, djedkhonsuefankh|gabbro|', 'eg/mobile-large/DT318224.jpg', 0.80), + SearchData(-1740, 544225, 'Group of two women and a child', 'statuette group, two women, child|limestone, paint|', 'eg/mobile-large/DT327377.jpg', 0.70), + SearchData(-2649, 565438, 'Contracted body which has been naturally mummified', 'body, mummified|human remains, linen|', 'eg/mobile-large/Images-Restricted.jpg', 1.00), + SearchData(-698, 544881, 'Donation Stela of Shebitqo', 'stela, shebitqo, donation, patjenef, horus, hathor, hurbeit|limestone|', 'eg/mobile-large/EG65.45.JPG', 1.10), + SearchData(-1329, 544692, 'Haremhab as a Scribe of the King', 'statue, scribe, haremhab|granodiorite|', 'eg/mobile-large/DP238391.jpg', 0.67), + SearchData(-347, 243745, 'Faience amulet of Isis and Horus', 'amulet, isis|clay, glazed|gold and silver', 'gr/mobile-large/DP121840.jpg', 0.75), + SearchData(-1000, 548253, 'Wedjat Eye Incision Plaque', 'amulet plaque, wedjat eye, gautsoshen|bronze or copper alloy|', 'eg/mobile-large/62415.jpg', 1.22), + SearchData(275, 466645, 'Medallion with a Portrait of Gennadios', 'medallion|glass, gold leaf, polychromy|glass-gold glass', 'md/mobile-large/DP325825.jpg', 0.99), + SearchData(-2550, 543871, 'Head of a statue of an older man', 'head, male statue|limestone, paint|', 'eg/mobile-large/DT259188.jpg', 0.75), + SearchData(-347, 243754, 'Faience amulet of Taweret', 'amulet, thueris|clay, glazed|gold and silver', 'gr/mobile-large/DP121823.jpg', 0.75), + SearchData(-2650, 547398, 'Clay jar sealing impressed with name of Khasekhemwy', 'sealing, jar|clay (unfired)|', 'eg/mobile-large/01.4.98_EGDP012127.jpg', 1.78), + SearchData(-1371, 544498, 'Sphinx of Amenhotep III, possibly from a Model of a Temple', 'sphinx, amenhotep iii|faience, remains of a travertine (egyptian alabaster) tenon|', 'eg/mobile-large/DT539.jpg', 1.25), + SearchData(-1400, 243816, 'Mirror handle', 'handle of a mirror with lotus petals|clay, glazed|terracottas', 'gr/mobile-large/DP121848.jpg', 1.33), + SearchData(-2551, 551276, 'Temple relief', 'temple relief, names of khufu|limestone|', 'eg/mobile-large/LC-22_1_19_EGDP030917.jpg', 1.50), + SearchData(-2649, 551010, 'Cup', 'cup|pottery|', 'eg/mobile-large/28.2.30_EGDP011316.jpg', 0.80), + SearchData(-2500, 543887, 'Shallow bowl with a recurved rim', 'bowl|travertine (egyptian alabaster)|', 'eg/mobile-large/DT227150.jpg', 1.25), + SearchData(0, 547376, 'Bracelet with Agathodaimon, Isis-Tyche, Aphrodite, and Thermouthis', 'bracelet, agathodaimon, isis-tyche, aphrodite, thermouthis|gold|', 'eg/mobile-large/DP-14787-002.jpg', 0.89), + SearchData(1274, 444540, 'Brazier of Rasulid Sultan al-Malik al-Muzaffar Shams al-Din Yusuf ibn \'Umar', 'brazier|brass; cast, chased, and inlaid with silver and black compound|metal', 'is/mobile-large/DP170388.jpg', 1.03), + SearchData(-493, 243949, 'Faience figurine of a lion', 'pendant ? of a lion|faience|miscellaneous-faience', 'gr/mobile-large/DP121850.jpg', 1.33), + SearchData(-2005, 548212, 'Relief of Nebhepetre Mentuhotep II and the Goddess Hathor', 'relief, temple wall, mentuhotep ii|limestone, paint|', 'eg/mobile-large/DP322047.jpg', 2.53), + SearchData(-2267, 590943, 'Two Vases in the Shape of a Mother Monkey with her Young', 'vase, monkey, baby|travertine (egyptian alabaster), paint, resin and pigment|', 'eg/mobile-large/eg30.8.134_1992.338.jpg', 0.80), + SearchData(-688, 243777, 'Faience ram\'s head amulet', 'amulet, ram\'s head|clay, glazed|gold and silver', 'gr/mobile-large/DP121837.jpg', 0.75), + SearchData(-1363, 544514, 'Fragment of a Queen\'s Face', 'head, queen, fragment, lips|yellow jasper|', 'eg/mobile-large/DP355835.jpg', 0.88), + SearchData(-347, 243773, 'Faience amulet in the form of a hare', 'amulet, hare|clay, glazed|gold and silver', 'gr/mobile-large/DP121832.jpg', 1.33), + SearchData(325, 465921, 'Bowl Fragments with Menorah, Shofar, and Torah Ark', 'bowl fragments|glass, gold leaf|glass-gold glass', 'md/mobile-large/h1_18.145.1ab.jpg', 1.28), + SearchData(65, 547257, 'Mummy Mask', 'mask, cartonnage, woman|cartonnage, plaster, paint, plant fibers|', 'eg/mobile-large/DT10852.jpg', 0.80), + SearchData(-1045, 243739, 'Faience amulet of Bes image', 'amulet, bes|clay, glazed|gold and silver', 'gr/mobile-large/DP131207.jpg', 0.75), + SearchData(-1186, 544076, 'Artist\'s Sketch of Pharaoh Spearing a Lion', 'ostracon, figured, pharaoh, lion, hieratic|limestone, ink|', 'eg/mobile-large/1191R2_Sec501M.jpg', 1.08), + SearchData(312, 463989, 'Gold Glass Medallion with a Mother and Child', 'medallion|glass, gold leaf|glass-gold glass', 'md/mobile-large/sf17-190-109as1.jpg', 0.99), + SearchData(-1390, 548584, 'Cosmetic Spoon in the Shape of Swimming Woman Holding a Dish', 'cosmetic spoon, swimming woman, gazelle|travertine (egyptian alabaster), steatite|', 'eg/mobile-large/DT223706.jpg', 1.25), + SearchData(-2557, 551045, 'Drinking Cup', 'cup, spouted|limestone|', 'eg/mobile-large/10.176.143_EGDP015841.jpg', 1.00), + SearchData(1049, 448401, 'Crescent-Shaped Pendant with Confronted Birds', 'pendant|gold, cloisonné enamel, turquoise; filigree|jewelry', 'is/mobile-large/DP170371.jpg', 0.80), + SearchData(-1944, 544320, 'Stela of the Steward Mentuwoser', 'stela, mentuwoser|limestone, paint|', 'eg/mobile-large/DP322064.jpg', 0.52), + SearchData(-1003, 552609, 'Winged Goddess', 'winged goddess|faience|', 'eg/mobile-large/26.7.982a-c_EGDP011734.jpg', 1.50), + SearchData(-1342, 546194, 'Arched Harp (shoulder harp)', 'music, harp, arched, naviform, boat shaped|wood|', 'eg/mobile-large/DP302724.jpg', 0.75), + SearchData(-313, 547689, 'Ritual Figure', 'statuette, ritual figure|wood, formerly clad with lead sheet|', 'eg/mobile-large/DT1016.jpg', 0.80), + SearchData(-1465, 548957, 'Name Stone of Senenmut', 'name stone, tally, senenmut|limestone, paint|', 'eg/mobile-large/36-3-245_ac.jpg', 0.83), + SearchData(-1700, 545108, 'Head from a Large Statue of a Priest or Dignitary', 'head, priest or dignitary|quartzite|', 'eg/mobile-large/262112.jpg', 0.82), + SearchData(-2880, 545799, 'Stela of Raneb', 'stela, raneb|granite|', 'eg/mobile-large/DP259528.jpg', 0.55), + SearchData(-995, 243808, 'Openwork faience ring', 'ring, openwork faience|faience|gold and silver', 'gr/mobile-large/DP121846.jpg', 0.75), + SearchData(-351, 546037, 'Magical Stela (Cippus of Horus)', 'cippus, cippus of horus|meta-greywacke|', 'eg/mobile-large/DP319007.jpg', 0.79), + SearchData(-2649, 543881, 'Weight equal to five deben', 'weight, 5 deben|yellow jasper or opal|', 'eg/mobile-large/DT259187.jpg', 0.80), + SearchData(-2649, 552020, 'Large jar', 'jar|pottery|', 'eg/mobile-large/28.2.9_EGDP010299.jpg', 0.67), + SearchData(-867, 544078, 'Amulet in the Form of a Lion-Headed Goddess', 'amulet, lion headed goddess|faience|', 'eg/mobile-large/DT226125.jpg', 0.80), + SearchData(1049, 446191, 'Panel with Horse Heads', 'panel|wood (teak); carved|wood', 'is/mobile-large/DP170363.jpg', 0.75), + SearchData(-1390, 767342, 'Document Sealing', 'sealing, amun-re, upper egypt, lower egypt|mud|', 'eg/mobile-large/SC-PA_199.jpg', 1.23), + SearchData(149, 590953, 'Theatrical Masks and Ram Vessel for Offering', 'mask, theatrical, ram vessel|faience|', 'eg/mobile-large/26.7.1019-group_EGDP011909.jpg', 0.92), + SearchData(-2650, 547433, 'Copper pin?', 'cosmetic pin (?)|copper alloy|', 'eg/mobile-large/01.4.40_EGDP011663.jpg', 1.78), + SearchData(-525, 544070, 'Antelope Head', 'head, antelope|greywacke, travertine (egyptian alabaster), agate|', 'eg/mobile-large/DT6857.jpg', 1.25), + SearchData(-2575, 543994, 'Scenes from a King\'s Thirty-Year Jubilee', 'relief, snefru (?), heb-sed|limestone, paint|', 'eg/mobile-large/DT259173.jpg', 1.33), + SearchData(-2649, 550160, 'Bowl', 'bowl|granodiorite|', 'eg/mobile-large/2001.726.2.jpg', 1.57), + SearchData(-2458, 543936, 'Palm Column of Sahure', 'column, sahure, palm capital|granite|', 'eg/mobile-large/10.175.137_EGDP011910.jpg', 0.67), + SearchData(-182, 250525, 'Fragmentary faience statuette of Aphrodite', 'statuette of aphrodite, fragment|faience|miscellaneous-faience', 'gr/mobile-large/DP121794.jpg', 1.33), + SearchData(-5, 551807, 'Man Holding a Shrine Containing an Image of Osiris', 'statue, priest, osiris shrine|greywacke|', 'eg/mobile-large/DP152079.jpg', 0.75), + SearchData(-1390, 767350, 'Document Sealing', 'sealing, upper egypt, lower egypt, amun-re|mud|', 'eg/mobile-large/SC-PA_207.jpg', 0.91), +]; \ No newline at end of file diff --git a/lib/logic/data/wonders_data/search/search_data.dart b/lib/logic/data/wonders_data/search/search_data.dart new file mode 100644 index 00000000..7932fa33 --- /dev/null +++ b/lib/logic/data/wonders_data/search/search_data.dart @@ -0,0 +1,17 @@ +class SearchData { + static const String baseImagePath = 'https://images.metmuseum.org/CRDImages/'; + + const SearchData(this.year, this.id, this.title, this.keywords, this.imagePath, [this.aspectRatio = 0]); + final int year; + final int id; + final String imagePath; + final String keywords; + final String title; + final double aspectRatio; + + String get imageUrl => baseImagePath + imagePath; + + // used by the search helper tool: + String write() => + "SearchData($year, $id, '$title', '$keywords', '$imagePath'${aspectRatio == 0 ? '' : ', ${aspectRatio.toStringAsFixed(2)}'})"; +} diff --git a/lib/logic/data/wonders_data/search/taj_mahal_search_data.dart b/lib/logic/data/wonders_data/search/taj_mahal_search_data.dart new file mode 100644 index 00000000..e4474564 --- /dev/null +++ b/lib/logic/data/wonders_data/search/taj_mahal_search_data.dart @@ -0,0 +1,1223 @@ +part of '../taj_mahal_data.dart'; + +// Search suggestions (85) +List _searchSuggestions = const [ + 'bodhisattva', + 'shrine', + 'dagger', + 'alloy', + 'stone', + 'sculpture', + 'ink', + 'panel', + 'paper', + 'album', + 'schist', + 'leather', + 'katar', + 'folio', + 'textiles', + 'sandstone', + 'fragment', + 'coins', + 'male', + 'female', + 'illustrated', + 'relief', + 'red', + 'silk', + 'bronze', + 'jade', + 'black', + 'wood', + 'inlay', + 'single', + 'paintings', + 'painted', + 'steel', + 'manuscript', + 'buddha', + 'deity', + 'cotton', + 'crystal', + 'metalwork', + 'carved', + 'bowl', + 'portrait', + 'inlays', + 'plaque', + 'figure', + 'vishnu', + 'rubies', + 'leaf', + 'goddess', + 'copper', + 'shah', + 'brass', + 'coin', + 'silver', + 'terracotta', + 'king', + 'opaque', + 'metal', + 'iron', + 'blade', + 'standing', + 'ivory', + 'rock', + 'khanjar', + 'ruby', + 'shiva', + 'codices', + 'nephrite', + 'stele', + 'ivories', + 'work', + 'gold', + 'dyed', + 'watercolor', + 'seated', + 'box', + 'daggers', + 'inlaid', + 'head', + 'emerald', + 'armor', + 'textile', + 'sheath', + 'velvet', + 'jahan', +]; + +// Taj Mahal (444) +List _searchData = const [ + SearchData( + 1049, 38148, 'Celestial Beauty (Surasundari)', 'figure|marble|sculpture', 'as/mobile-large/69_247.jpg', 0.44), + SearchData(1024, 38574, 'Jain Svetambara Tirthankara in Meditation', 'figure|marble|sculpture', + 'as/mobile-large/DT5161.jpg', 0.80), + SearchData(1574, 453344, 'Pierced Window Screen', 'screen|red sandstone; pierced, carved|stone', + 'is/mobile-large/DP326717.jpg', 0.74), + SearchData(150, 38752, 'Relief of a Female Deity', 'relief fragment|terracotta|sculpture', + 'as/mobile-large/33_50_16.JPG', 0.64), + SearchData(649, 38254, 'Section of a Diptych in Linga Form, Interior Depicting Shiva and Parvati', + 'portable linga section|chlorite schist|sculpture', 'as/mobile-large/DT8677.jpg', 0.80), + SearchData( + 1794, + 22871, + 'Saber with Scabbard', + 'saber with scabbard|steel, gold, silver, jade (nephrite), diamonds, emeralds, pearls|swords', + 'aa/mobile-large/DP215265.jpg', + 0.75), + SearchData(1149, 38934, 'Tara', 'figurine|bronze with silver inlay|sculpture', 'as/mobile-large/DP310497.jpg', 0.75), + SearchData(-33, 38925, 'Female Worshipper in Front of a Column Support', 'figure and column|bronze|sculpture', + 'as/mobile-large/1987_142_316.jpg', 0.42), + SearchData(0, 38074, 'Toy Model of a Cart', 'cart|terracotta|sculpture', 'as/mobile-large/1991_455_2_F.jpg', 0.82), + SearchData(1850, 31849, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, jade or agate, gold|daggers', + 'aa/mobile-large/36.25.679_001june2014.jpg', 0.56), + SearchData(1850, 31840, 'Dagger', 'dagger|steel, gold, jade, ruby|daggers', + 'aa/mobile-large/36.25.669_001june2014.jpg', 0.56), + SearchData(449, 38511, 'Krishna Killing the Horse Demon Keshi', 'relief|terracotta|sculpture', + 'as/mobile-large/DT5237.jpg', 0.80), + SearchData(-100, 65011, 'The Sun God Surya(?) in His Chariot with Wives and Attendants', + 'bowl fragment|ivory|ivories', 'as/mobile-large/DP158770.jpg', 1.33), + SearchData(1621, 444865, 'Coin', 'coin|gold|coins', 'is/mobile-large/DP29.jpg', 1.00), + SearchData(1800, 24292, 'Shield (Dhàl)', 'shield (dhàl)|leather, lacquer, gold leaf, silver|shields', + 'aa/mobile-large/DP153433.jpg', 1.25), + SearchData(1183, 75201, 'Foliate Pedestal for a Buddhist Image', + 'pedestal|partially gilded brass, copper base|metalwork', 'as/mobile-large/DP208972.jpg', 0.75), + SearchData(1049, 38420, 'The God Danda and the Goddess Niksubha (Attendants of Surya, the Sun God)', + 'stele fragment|sandstone|sculpture', 'as/mobile-large/1983_553.jpg', 0.60), + SearchData(100, 38103, 'Standing Female Figure with an Offering', 'figure|gold|sculpture', + 'as/mobile-large/1987_142_309_DT8680.jpg', 0.59), + SearchData(1570, 451276, '"Portrait of Zamana Beg, Mahabat Khan", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP246561.jpg', 0.68), + SearchData( + 1649, 453242, 'Basin with Stylized Lotus Blossoms', 'basin|marble|stone', 'is/mobile-large/DT7746.jpg', 1.25), + SearchData( + -50, 38575, 'Plaque with a Royal Family', 'plaque|terracotta|sculpture', 'as/mobile-large/DT6881.jpg', 0.80), + SearchData(-1250, 50576, 'Ax Blade', 'ax|copper|metalwork', 'as/mobile-large/2001_433_46.JPG', 0.55), + SearchData(549, 38395, 'Head of a Male Figure', 'head|terracotta|sculpture', 'as/mobile-large/264800.jpg', 1.04), + SearchData(-50, 39320, 'One from a Pair of Ear Ornaments (Prakaravapra Kundala)', + 'earring|gold, sheet, wire and granulation|jewelry', 'as/mobile-large/DP-14791-007.jpg', 1.37), + SearchData(1640, 453253, 'Dagger with Hilt in the Form of a Blue Bull (Nilgai)', + 'dagger|hilt: nephrite blade: watered steel|stone', 'is/mobile-large/DP158315.jpg', 0.75), + SearchData(-50, 38089, 'Auspicious Emblem with Four Dancing Figures', 'rattle|terracotta|sculpture', + 'as/mobile-large/1987_142_379_O.jpg', 0.99), + SearchData(1683, 38639, 'Three Arches', 'arches|wood|sculpture', 'as/mobile-large/DP263684.jpg', 1.68), + SearchData(-50, 38549, 'Rattle in the Form of a Crouching Yaksha (Male Nature Spirit)', 'rattle|terracotta|sculpture', + 'as/mobile-large/DP-18264-001.jpg', 0.78), + SearchData( + 1749, + 448936, + 'Tent Lining Fragment', + 'fragment|cotton, silk; plain weave, embroidered, quilted|textiles-embroidered', + 'is/mobile-large/wb-33.65.9.JPG', + 0.80), + SearchData(1587, 74425, 'Ten Elements for East Window of an Architectural Ensemble from a Jain Meeting Hall', + 'architectural element|teak with traces of color|sculpture', 'as/mobile-large/hb_16.133.jpg', 0.87), + SearchData(249, 38240, 'The Conversion and Ordination of Nanda', 'relief|limestone|sculpture', + 'as/mobile-large/DP-15581-020.jpg', 7.00), + SearchData(874, 719420, 'Goddess Durga Slaying the Demon Mahisha', 'stele|schist|sculpture', + 'as/mobile-large/DP-12769-001.jpg', 0.75), + SearchData(749, 38964, 'Stupa', 'stupa|bronze|metalwork', 'as/mobile-large/1987_142_144_276_O.jpg', 0.67), + SearchData(483, 38198, 'Standing Buddha Offering Protection', 'figure|red sandstone|sculpture', + 'as/mobile-large/DT237.jpg', 0.80), + SearchData(1100, 39328, 'Shiva as Lord of Dance (Nataraja)', 'figure|copper alloy|sculpture', + 'as/mobile-large/DT240.jpg', 0.75), + SearchData(1640, 453367, 'Portrait of the Elephant \'Alam Guman', + 'illustrated album leaf|opaque watercolor and gold on paper|codices', 'is/mobile-large/DP234016.jpg', 1.42), + SearchData(1749, 666471, 'Bowl in shape of a shell', 'wine cup|jade (nephrite)|jade', + 'as/mobile-large/DP-22439-001.jpg', 1.33), + SearchData(1799, 56233, 'Box with tray', 'box with tray|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP108028.jpg', 1.06), + SearchData(610, 39142, 'Seated Buddha', 'figure|bronze with silver and copper inlay|sculpture', + 'as/mobile-large/DT5712.jpg', 0.80), + SearchData(1800, 31863, 'Dagger (Katar)', 'dagger (katar)|steel, gold|daggers', + 'aa/mobile-large/36.25.693_002june2014.jpg', 0.56), + SearchData(885, 38928, 'Enthroned Preaching Buddha', 'shrine|bronze with silver inlay|sculpture', + 'as/mobile-large/1987_142_337_F_sf.jpg', 0.75), + SearchData( + 1699, 38724, 'Fragment from a Processional Car', 'figure|wood|sculpture', 'as/mobile-large/20_52_2.JPG', 0.66), + SearchData( + 1749, 666489, 'Bowl with floral design', 'bowl|jade (nephrite)|jade', 'as/mobile-large/DP-22442-001.jpg', 1.33), + SearchData(1149, 38143, 'Vishnu with His Mount, Garuda, His Consort, Lakshmi, and Attendants', + 'stele|black stone|sculpture', 'as/mobile-large/57_51_2.jpg', 0.50), + SearchData(1799, 56221, 'Bowl', 'bowl|jade (nephrite) with painted decoration|jade', + 'as/mobile-large/DP-14153-041.jpg', 1.33), + SearchData(199, 38739, 'Male Head', 'head|terracotta|sculpture', 'as/mobile-large/28_159_9.JPG', 0.75), + SearchData( + 1587, + 452310, + 'Fragment of an Animal Carpet', + 'carpet fragment|cotton (warp and weft), wool (pile); asymmetrically knotted pile|textiles-rugs', + 'is/mobile-large/DT5457.jpg', + 1.39), + SearchData( + 1690, + 453673, + 'Nan va Halva (Breads and Sweets)', + 'illustrated manuscript|ink, opaque watercolor, and gold on paper binding: leather|codices', + 'is/mobile-large/DP235872.jpg', + 0.69), + SearchData(1615, 37855, 'Krishna\'s Parents Search for Him', 'painting|ink and opaque watercolor on paper|paintings', + 'as/mobile-large/DP156112.jpg', 1.19), + SearchData( + 633, 713007, 'Vajrapani', 'figure|gray chorite|sculpture', 'as/mobile-large/LC-2016_241_sr1-001.jpg', 0.57), + SearchData(1150, 39202, 'Standing Tara', 'figure|bronze|sculpture', 'as/mobile-large/LC-1993_524_1-001.jpg', 0.67), + SearchData( + 1712, + 448476, + '"Prince With his Courtiers and Musicians", Folio from the Davis Album', + 'illustrated album leaf|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/sf30-95-174-17a.jpg', + 0.65), + SearchData( + 1800, + 31401, + 'Crutch Dagger (Zafar Takieh, "Cushion of Victory") with Sheath', + 'crutch dagger (zafar takieh) with sheath|steel, silver, gold, jade, ruby, turquoise, emerald|daggers', + 'aa/mobile-large/36.25.1001ab_004july2014.jpg', + 0.56), + SearchData(149, 38729, 'Ornamental Band', 'relief|red sandstone|sculpture', 'as/mobile-large/28_147_1.JPG', 1.29), + SearchData( + 1625, + 445261, + 'Pashmina Carpet Fragment', + 'carpet fragment|silk (warp and weft), pashmina wool (pile); asymmetrically knotted pile|textiles-rugs', + 'is/mobile-large/DP229987.jpg', + 0.47), + SearchData(1400, 39227, 'Durga Mahishasura', 'figure|bronze|sculpture', 'as/mobile-large/1987_142_336.JPG', 0.66), + SearchData( + 1800, + 31847, + 'Dagger (Khanjar) with Sheath', + 'dagger (khanjar) with sheath|steel, jade, gold, turquoise, crystal, garnet, ruby, gemstone silk, silver, copper|daggers', + 'aa/mobile-large/36.25.677ab_002june2014.jpg', + 0.56), + SearchData(449, 38389, 'Plaque with Figure Holding a Sword', 'plaque|terracotta|sculpture', + 'as/mobile-large/DT6021.jpg', 0.80), + SearchData(1149, 38771, 'Relief of an Episode of Kiratarjuniya from the Mahabharata', 'relief|schist|sculpture', + 'as/mobile-large/1970_13.JPG', 2.67), + SearchData(1649, 56206, 'Dish', 'dish|nephrite, white|jade', 'as/mobile-large/02_18_758.JPG', 1.38), + SearchData(1799, 56256, 'Vase with stopper', 'vase|jade (nephrite) with gold and silver inlays|jade', + 'as/mobile-large/DP-14153-019.jpg', 0.75), + SearchData(100, 38379, 'Tree Spirit Deity (Yakshi)', 'bracket|red sandstone|sculpture', + 'as/mobile-large/DP251641.jpg', 0.75), + SearchData( + 1800, + 24297, + 'Dagger with Sheath', + 'dagger with sheath|steel, rock crystal, gold, silver, rubies, diamonds, emeralds, textile, wood|daggers', + 'aa/mobile-large/23.232.8ab_003july2014.jpg', + 0.56), + SearchData(1749, 56203, 'Wine Cup', 'wine cup|nephrite|jade', 'as/mobile-large/32833.jpg', 1.36), + SearchData(599, 38769, 'Doorframe with Three Ganas (Nature Deities) Dancing and Playing Instruments', + 'doorframe fragment|sandstone|sculpture', 'as/mobile-large/64_10.JPG', 0.57), + SearchData(599, 72605, 'Standing Goddess', 'figure|bronze|sculpture', 'as/mobile-large/DP238955.jpg', 0.75), + SearchData(1650, 453281, 'Three Ornaments from a Bridle', 'ornaments|silver; stamped, punched, gilded|metal', + 'is/mobile-large/1987.254.1-3-D.JPG', 0.54), + SearchData( + 1650, + 453642, + 'Writing Box with Lattice and Flower Design', + 'writing box|wood; overlaid with dyed wool, stamped silver and gilt-copper plaques|metal', + 'is/mobile-large/DP240984.jpg', + 1.17), + SearchData(799, 38149, 'Bust of a Female Deity', 'bust|stone|sculpture', 'as/mobile-large/1987_418_1.jpg', 0.50), + SearchData(250, 38970, 'Seated Ascetic, Deified King, Agni (The God of Fire)', 'figure|bronze|sculpture', + 'as/mobile-large/DP155519.jpg', 0.77), + SearchData(1624, 73309, 'Plate', 'plate|mother-of-pearl with copper alloy base (underside) and rim frame|shell', + 'as/mobile-large/DP138506.jpg', 1.00), + SearchData(1199, 38140, 'Tree Dryad (Shalabhanjika)', 'figure|ferruginous stone|sculpture', + 'as/mobile-large/65_108.jpg', 0.33), + SearchData(1149, 37397, 'Ganesha', 'figure|copper alloy|sculpture', 'as/mobile-large/DP148767.jpg', 0.75), + SearchData(1016, 38931, 'Crowned and Jeweled Buddha', 'shrine|bronze with silver and copper inlay|sculpture', + 'as/mobile-large/1987_142_319_O.jpg', 0.37), + SearchData(699, 38414, 'Head of a Bodhisattva', 'head|stucco on a clay core|sculpture', + 'as/mobile-large/29_11_F_sf.jpg', 1.22), + SearchData(849, 39129, 'Seated Maitreya', 'figurine|bronze|sculpture', 'as/mobile-large/1987_142_347_F.JPG', 0.80), + SearchData(949, 38259, 'Manjushri, the Bodhisattva of Transcendent Wisdom', 'figure|brass|sculpture', + 'as/mobile-large/1983_555_5.jpg', 0.59), + SearchData( + 549, 38601, 'Gaja Lakshmi, Goddess of Fortune', 'relief|stone|sculpture', 'as/mobile-large/DP253395.jpg', 0.75), + SearchData(1610, 58449, 'Krishna Dancing: Page from the Dispersed "Boston" Rasikapriya (Lover\'s Breviary)', + 'folio|ink, opaque watercolor, and gold on paper|paintings', 'as/mobile-large/DP153225.jpg', 0.63), + SearchData( + 1644, + 451288, + 'Rosette Bearing the Name and Title of Emperor Aurangzeb (Recto), from the Shah Jahan Album', + 'non-illustrated album leaf|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/DP247731.jpg', + 0.69), + SearchData(499, 38244, 'Tile showing a woman carrying a pot', 'tile|terracotta|sculpture', + 'as/mobile-large/1994_77.jpg', 1.23), + SearchData( + 1650, + 31861, + 'Dagger (Katar) with Sheath', + 'dagger (katar) with sheath|iron, gold, steel, diamond, velvet, wood|daggers', + 'aa/mobile-large/36.25.691ab_002june2014.jpg', + 0.56), + SearchData(1799, 56247, 'Box with cover', 'box|jade (nephrite) with gold, silver, and stone inlays|jade', + 'as/mobile-large/DP-14153-035.jpg', 1.39), + SearchData( + 1650, + 448351, + 'Joined Fragments: Velvet Panel with Rows of Flowers', + 'joined fragments|silk, cut and voided velvet, with continuous floats of metal thread|textiles-woven', + 'is/mobile-large/DP230001.jpg', + 0.46), + SearchData(1116, 39331, 'Yashoda with the Infant Krishna', 'figure|copper alloy|sculpture', + 'as/mobile-large/DT7387.jpg', 0.80), + SearchData(-100, 38737, 'Soldier', 'statuette|terracotta|sculpture', 'as/mobile-large/28_159_14.jpg', 0.46), + SearchData(1249, 38768, 'Lion', 'figure|stone|sculpture', 'as/mobile-large/60_123.JPG', 1.16), + SearchData(1799, 58448, 'Box with cover', 'box|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/32916.jpg', 1.27), + SearchData(274, 38238, 'Drum panel depicting a stupa with the Buddha Descent from Trayastrimsa Heaven', + 'drum slab|limestone|sculpture', 'as/mobile-large/DP-18912-001.jpg', 0.79), + SearchData(-50, 38578, 'Rattle in the Form of a Crouching Grotesque Yaksha (Male Nature Spirit)', + 'rattle|terracotta|sculpture', 'as/mobile-large/DP-14671-001.jpg', 0.71), + SearchData( + 1616, + 24907, + 'Dagger with Scabbard', + 'dagger with scabbard|steel, iron, gold, rubies, emeralds, glass, wood, textile|daggers', + 'aa/mobile-large/DP157706.jpg', + 0.72), + SearchData( + 1650, + 453330, + 'Velvet Panel with Rows of Flowers', + 'joined fragments|silk, cut and voided velvet, with continuous floats of flat metal thread|textiles-woven', + 'is/mobile-large/DP230001.jpg', + 0.46), + SearchData( + 1750, + 31570, + 'Dagger (Jambiya) with Sheath and Carrier', + 'dagger (jambiya) with sheath and carrier|steel, silver, jade, leather, wood, lacquer, velvet|daggers', + 'aa/mobile-large/36.25.980abc_007july2014.jpg', + 0.67), + SearchData(199, 38730, 'Bodhisattva and Yaksha', 'relief fragment|red sandstone|sculpture', + 'as/mobile-large/28_147_2.JPG', 1.19), + SearchData(-50, 40098, 'Plaque with Standing Yakshi', 'plaque|wood|sculpture', 'as/mobile-large/DP158749.jpg', 0.75), + SearchData(791, 38134, 'Garuda (Vishnu\'s Mount) Seated in Royal Ease', 'figure|granite|sculpture', + 'as/mobile-large/DT7595.jpg', 0.80), + SearchData( + 1700, 31838, 'Dagger', 'dagger|steel, nephrite, silver, gold|daggers', 'aa/mobile-large/DP157414.jpg', 0.75), + SearchData(1049, 38132, 'Carved Conch', 'carved conch|shell|sculpture', 'as/mobile-large/DP-17091-001.jpg', 0.75), + SearchData(1622, 444861, 'Coin with Sign of Cancer', 'coin|silver|coins', 'is/mobile-large/LC-99_35_2397.jpg', 1.00), + SearchData(1800, 200032, 'Chessmen (32)', 'chessmen|ivory with polychrome lacquer and gilding|chess sets', + 'es/mobile-large/DP-18258-011.jpg', 1.64), + SearchData( + 1597, + 446562, + '"Bahram Gur Sees a Herd of Deer Mesmerized by Dilaram\' s Music", Folio from a Khamsa (Quintet) of Amir Khusrau Dihlavi', + 'folio from an illustrated manuscript|main support: ink, opaque watercolor, and gold on paper margins: gold on dyed paper|codices', + 'is/mobile-large/DP120803.jpg', + 0.69), + SearchData(261, 38634, 'Crowned Bodhisattva', 'bust|sandstone|sculpture', 'as/mobile-large/DP701395.jpg', 0.85), + SearchData( + 1083, 75960, 'Child Saint Sambandar', 'figure|copper alloy|sculpture', 'as/mobile-large/DP234672.jpg', 0.75), + SearchData(-1250, 50588, 'Eccentric Anthropomorph', 'anthropomorph|copper|metalwork', + 'as/mobile-large/2001_433_7_O.JPG', 1.11), + SearchData(1635, 65584, 'Krishna Revels with the Gopis: Page from a Dispersed Gita Govinda (Song of the Cowherds)', + 'folio|opaque watercolor and silver on paper|paintings', 'as/mobile-large/DP152290.jpg', 0.58), + SearchData( + 1787, + 506151, + 'Vina (वीणा)', + 'vina|jackwood, gold leaf, papier-mâché, bone, steel, brass|chordophone-lute-plucked-fretted', + 'mi/mobile-large/DP-15104-001.jpg', + 1.65), + SearchData( + 899, 38136, 'Jyeshtha Flanked by Her Children', 'figures|granite|sculpture', 'as/mobile-large/DT8567.jpg', 0.80), + SearchData(1649, 646829, 'Filigree Casket with Sliding Top', 'box|silver filigree; parcel-gilt|metal', + 'is/mobile-large/DP340529.jpg', 0.78), + SearchData(1699, 447086, 'Finial in the Form of a Parrot', 'finial|brass|metal', 'is/mobile-large/DT4880.jpg', 1.25), + SearchData(1749, 56211, 'Bowl in the shape of a chrysanthemum flower', 'bowl|jade (nephrite)|jade', + 'as/mobile-large/DP108021.jpg', 1.50), + SearchData(1800, 31687, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, jade|daggers', + 'aa/mobile-large/36.25.707_001june2014.jpg', 0.56), + SearchData(1800, 31467, 'Dagger (Katar)', 'dagger (katar)|steel, gold|daggers', + 'aa/mobile-large/36.25.1071_001july2014.jpg', 0.56), + SearchData(1949, 44670, 'Head of a Male Deity (Study Collection)', 'head|stone|sculpture', + 'as/mobile-large/1991_453_3.JPG', 0.60), + SearchData( + -1250, 50610, 'Antennae Sword', 'antennae sword|copper|metalwork', 'as/mobile-large/2001_433_50_O.JPG', 0.27), + SearchData(1750, 24423, 'Ceremonial Mace', 'ceremonial mace|rock crystal, gold, copper alloy, ruby|shafted weapons', + 'aa/mobile-large/DP163751.jpg', 0.80), + SearchData(649, 38250, 'Linga with Face of Shiva (Ekamukhalinga)', 'linga|stone|sculpture', + 'as/mobile-large/DT6118.jpg', 0.80), + SearchData(1700, 453339, 'Elephant Goad', 'elephant goad|steel or iron, inlaid with gold and silver|metal', + 'is/mobile-large/DP231148.jpg', 0.75), + SearchData(787, 38515, 'Vaikuntha Vishnu', 'figure|stone|sculpture', 'as/mobile-large/DT5253.jpg', 0.80), + SearchData( + 1779, + 456949, + 'Great Indian Fruit Bat', + 'illustrated single work|pencil, ink, and opaque watercolor on paper|paintings', + 'is/mobile-large/DP167067.jpg', + 1.36), + SearchData(849, 38128, 'Sealing Depicting the Buddha at the Mahabodhi Temple, Bodhgaya', + 'sealing|terracotta|sculpture', 'as/mobile-large/33_50_10_F_sf.jpg', 0.98), + SearchData(1577, 451280, '"Portrait of Ibrahim \'Adil Shah II of Bijapur", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP159395.jpg', 0.73), + SearchData(-50, 40101, 'Plaque with Erotic Scene', 'plaque|bone|sculpture', 'as/mobile-large/1999_226_1.jpg', 1.08), + SearchData( + 1750, + 24208, + 'Arm Guard (Dastana)', + 'arm guard (dastana)|steel, gold, textile (velvet), textile (silk), textile (linen), copper alloy|armor parts', + 'aa/mobile-large/DP-651-001.jpg', + 1.08), + SearchData(1799, 74696, 'Design of Intertwining Animals and Plants', + 'painting|ink, opaque and translucent watercolor on paper|paintings', 'as/mobile-large/DP166103.jpg', 1.58), + SearchData(-50, 38473, 'Rattle in the Form of a Crouching Yaksha (Male Nature Spirit)', 'rattle|terracotta|sculpture', + 'as/mobile-large/DP-18263-001.jpg', 0.91), + SearchData( + -94, 38612, 'Dish with a Bee Pollinating a Lotus', 'dish|ceramic|ceramics', 'as/mobile-large/1993_371.jpg', 1.00), + SearchData(1837, 781965, 'Album of Hindu deities', 'album|watercolor, ink, and gold on paper|manuscripts', + 'as/mobile-large/LC-TR_100ai_2018_012_crd.jpg', 0.73), + SearchData(674, 38125, 'Vishnu', 'figure|stone|sculpture', 'as/mobile-large/DT7427.jpg', 0.80), + SearchData(1578, 451281, '"Portrait of Mulla Muhammad Khan Vali of Bijapur", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP247726.jpg', 0.67), + SearchData(649, 37533, 'Varuna Holding a Noose and Riding a Swan', 'figure|sandstone|sculpture', + 'as/mobile-large/19_90.JPG', 0.89), + SearchData(1580, 451268, '"Akbar With Lion and Calf", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DT200627.jpg', 0.66), + SearchData(449, 38512, 'Rondel with a Racing Male Deity Cradling His Consort (Probably Shiva and Parvati)', + 'rondel|terracotta|sculpture', 'as/mobile-large/1991_263.jpg', 0.98), + SearchData(-1875, 39126, 'Woman Riding Two Brahman Bulls', 'sculpture|bronze|sculpture', + 'as/mobile-large/DP702288.jpg', 0.75), + SearchData(1649, 56232, 'Box with Cover', 'box with cover|nephrite, gray with faint greenish tint|jade', + 'as/mobile-large/32914.jpg', 1.32), + SearchData(449, 38392, 'Head Fragment from a Plaque', 'head fragment|terracotta|sculpture', + 'as/mobile-large/1987_424_24.jpg', 0.86), + SearchData(1850, 24006, 'Shirt and Leg Defenses of Mail', 'shirt and leg defenses of mail|iron, brass|mail', + 'aa/mobile-large/DP147307.jpg', 1.00), + SearchData( + 1597, + 446567, + '"The Story of the Princess of the Blue Pavillion: The Youth of Rum Is Entertained in a Garden by a Fairy and her Maidens", Folio from a Khamsa (Quintet) of Amir Khusrau Dihlavi', + 'folio from an illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/DP120808.jpg', + 0.68), + SearchData(-1250, 50637, 'Ax Blade', 'ax|copper|metalwork', 'as/mobile-large/2001_433_35.jpg', 0.50), + SearchData(1799, 56231, 'Jar with cover', 'jar with cover|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP108027.jpg', 1.00), + SearchData( + 1837, + 24483, + 'Flintlock Gun', + 'flintlock gun|steel, ebony, gold, enamel, rubies, emeralds, textile|firearms-guns-flintlock', + 'aa/mobile-large/DP166296.jpg', + 2.68), + SearchData( + 1850, + 31851, + 'Dagger (Khanjar) with Sheath', + 'dagger (khanjar) with sheath|steel, jade, gold, velvet, pearl, turquoise, wood|daggers', + 'aa/mobile-large/36.25.681ab_002june2014.jpg', + 0.56), + SearchData(1799, 56254, 'Buckle', 'buckle|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP108038.jpg', 1.27), + SearchData(1650, 24306, 'Dagger (Katar) and Sheath', 'dagger (katar) and sheath|steel, leather, gold|daggers', + 'aa/mobile-large/DP158187.jpg', 1.62), + SearchData(1949, 38773, 'Head of a Buddha (Study Collection)', 'figure|stone|sculpture', + 'as/mobile-large/1991_381_12.JPG', 0.70), + SearchData(250, 38241, 'Naga Attendant Holding a Fly Whisk', 'relief|limestone|tablets and plaques', + 'as/mobile-large/DP-18916-001.jpg', 0.30), + SearchData(-1250, 50630, 'Ax Blade (Celt)', 'ax blade|copper|metalwork', 'as/mobile-large/2001_433_20_O.JPG', 0.75), + SearchData( + 1049, 38142, 'Celestial Musician (Gandharva)', 'figure|slate|sculpture', 'as/mobile-large/DP206242.jpg', 0.75), + SearchData(-50, 38468, 'Yaksha', 'yaksha|sandstone|sculpture', 'as/mobile-large/DP-18910-001.jpg', 0.70), + SearchData(435, 39405, 'Gold Coin Showing King Kumaragupta as an Archer', 'coin|gold|metalwork', + 'as/mobile-large/1990_319_1.jpg', 1.03), + SearchData(774, 38598, 'Kamadeva, the God of Love', 'bust|stone|sculpture', 'as/mobile-large/DP130033.jpg', 0.75), + SearchData(1799, 56225, 'Bracelet', 'bracelet|jade (nephrite) with gold, enamel, and stone inlays|jade', + 'as/mobile-large/DP108025.jpg', 1.22), + SearchData( + 1850, + 31831, + 'Dagger (Khanjar)', + 'dagger (khanjar)|steel, jade, gold, ruby, emerald, beryl, rock crystal, turquoise|daggers', + 'aa/mobile-large/36.25.658_001june2014.jpg', + 0.56), + SearchData(438, 39892, 'Nagini (Serpent Queen or Consort of Nagaraja)', 'figure|stone|sculpture', + 'as/mobile-large/DT5825.jpg', 0.80), + SearchData(1798, 1986, 'Chess set', 'chess set|lacquered wood, ivory|', 'ad/mobile-large/138425.jpg', 1.66), + SearchData(199, 38736, 'Bust of a Woman', 'bust|terracotta|sculpture', 'as/mobile-large/28_159_13.JPG', 0.71), + SearchData(499, 38197, 'Tile with Impressed Figures of Emaciated Ascetics and Couples behind Balconies and Ganders', + 'tile|terracotta|sculpture', 'as/mobile-large/1995_570_6.JPG', 0.64), + SearchData(1800, 31441, 'Knife with Sheath', 'knife with sheath|steel, jade, silver, emerald, ruby, gold|knives', + 'aa/mobile-large/36.25.1045ab_002july2014.jpg', 0.56), + SearchData(1949, 44672, 'Woman with a Parrot (Study Collection)', 'relief fragment|stone|sculpture', + 'as/mobile-large/1991_453_5.JPG', 0.88), + SearchData(1750, 31714, 'Crutch Dagger (Zafar Takieh, "Cushion of Victory")', 'crutch dagger|jade, steel|daggers', + 'aa/mobile-large/36.25.734_005june2014.jpg', 0.56), + SearchData(1049, 38123, 'Buddha Preaching the First Sermon at Sarnath', 'figure|black stone|sculpture', + 'as/mobile-large/4 DP314867r4_61A.jpg', 0.75), + SearchData(1850, 31828, 'Dagger', 'dagger|steel, rock crystal, gold, ruby, emerald|daggers', + 'aa/mobile-large/36.25.655_001june2014.jpg', 0.56), + SearchData(1624, 444867, 'Coin with Sign of Leo', 'coin|gold|coins', 'is/mobile-large/DP33.jpg', 1.00), + SearchData(1725, 27218, 'Shield', 'shield|wood, leather, lacquer, gold, pigment, gesso, copper alloy, iron|shields', + 'aa/mobile-large/sfsb14.25.728_001.jpg', 1.00), + SearchData(50, 73258, 'Jewelry Mold with Figures in a Temple', 'jewelry mold|argillite|sculpture', + 'as/mobile-large/DP143664.jpg', 1.33), + SearchData(449, 38533, 'Head of a Buddha', 'figure|stone|sculpture', 'as/mobile-large/DP145451.jpg', 1.00), + SearchData( + 1649, + 448241, + 'Panel from a Tent Lining', + 'fragment of a tent lining|cotton; plain weave, mordant painted and dyed, resist dyed|textiles-painted and/or printed', + 'is/mobile-large/ad-29.68.2.JPG', + 0.75), + SearchData(1570, 451266, '"A Youth Fallen From a Tree", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP240815.jpg', 0.67), + SearchData(1800, 31836, 'Dagger', 'dagger|steel, jade, gold, diamond, emerald, ruby, agate|daggers', + 'aa/mobile-large/36.25.664_001june2014.jpg', 0.56), + SearchData( + 1762, + 688478, + 'Casket', + 'casket|ebony and engraved ivory, tortoiseshell; mirror glass; silver hardware; brass feet; iron lock|natural substances-ivory', + 'es/mobile-large/DP-12252-017.jpg', + 1.29), + SearchData(1799, 56234, 'Sword handle in the shape of a horse’s head', + 'sword handle|jade (nephrite) with gold and stone inlays|jade', 'as/mobile-large/DP108029.jpg', 1.00), + SearchData(1618, 444866, 'Coin', 'coin|gold|coins', 'is/mobile-large/DP31.jpg', 1.00), + SearchData(1700, 31865, 'Dagger (Katar) with Sheath', 'dagger (katar) with sheath|steel, gold, velvet, wood|daggers', + 'aa/mobile-large/36.25.697ab_002june2014.jpg', 0.56), + SearchData(799, 38247, 'Vishnu Rescuing Gajendra, the Lord of the Elephants', 'stele|stone|sculpture', + 'as/mobile-large/1986_306.jpg', 0.82), + SearchData(199, 38599, 'Architectural Frieze with Merman Playing Musical Instruments', + 'architectural frieze|red sandstone|sculpture', 'as/mobile-large/1993_192ab_O.jpg', 7.54), + SearchData( + 1680, + 453183, + 'The House of Bijapur', + 'illustrated album leaf|ink, opaque watercolor, gold, and silver on paper|codices', + 'is/mobile-large/DP231353.jpg', + 0.76), + SearchData(1749, 856419, 'Processional image of the goddess Gauri', + 'head|brass with glass inlay and silver attachments|sculpture', 'as/mobile-large/DP-23603-002.jpg', 0.75), + SearchData(1674, 457020, 'Pair of Flower Style Doors', 'pair of doors|wood; carved with residues of paint|wood', + 'is/mobile-large/DP230666.jpg', 0.53), + SearchData( + 1600, + 453366, + 'Bidri Box for Holding Pan', + 'box|zinc alloy; cast, engraved, inlaid with silver and brass (bidri ware)|metal', + 'is/mobile-large/DP221344.jpg', + 0.75), + SearchData(1449, 39263, 'Seated Jain Tirthankara', 'figure|bronze|sculpture', 'as/mobile-large/DP124075.jpg', 1.00), + SearchData(-1250, 50595, 'Harpoon', 'harpoon|copper|metalwork', 'as/mobile-large/2001_433_72_O.JPG', 0.39), + SearchData(1799, 56226, 'Bracelet (one of a pair)', + 'bracelet|jade (nephrite) with gold, enamel, and stone inlays|jade', 'as/mobile-large/DP108025.jpg', 1.22), + SearchData(-50, 38092, 'Plaque with a Dancer and a Vina Player', 'plaque|terracotta|sculpture', + 'as/mobile-large/DT8684.jpg', 0.80), + SearchData( + 1550, 679021, 'Elephant Sword', 'elephant sword|iron or steel|swords', 'aa/mobile-large/DP350518.jpg', 1.71), + SearchData(1687, 453214, 'Painted and Inlaid Game Board', + 'game board|wood; painted, varnished and gilded; with metal hinges|wood', 'is/mobile-large/DP303328.jpg', 1.15), + SearchData(1875, 501899, 'Karnā (Trumpet)', 'karnā (trumpet)|brass|aerophone-lip vibrated-trumpet / trombone', + 'mi/mobile-large/DP-12679-025.jpg', 1.84), + SearchData(549, 38202, 'Standing Male Deity (possibly Shiva)', 'figure|stone|sculpture', + 'as/mobile-large/1993_477_4.jpg', 0.44), + SearchData(549, 38453, 'Standing Jain Tirthankara Parshvanatha', 'figure|sandstone|sculpture', + 'as/mobile-large/CT_35465.jpg', 0.51), + SearchData(249, 37640, 'Yakshi', 'statuette|brown sandstone|sculpture', 'as/mobile-large/33_50_1.JPG', 0.62), + SearchData(749, 39342, 'The Bodhisattva Vajrasattva', 'figure|brass with silver inlay|sculpture', + 'as/mobile-large/DP130035.jpg', 0.75), + SearchData(499, 38393, 'Lion Standing on a Pillar Capital', 'capital|terracotta|sculpture', + 'as/mobile-large/28_159_7.jpg', 0.51), + SearchData(699, 38412, 'Head of a Bodhisattva', 'head|stucco on a clay core|sculpture', + 'as/mobile-large/33_50_3_F_sf.jpg', 1.23), + SearchData(-100, 65010, 'The Moon God Chandra(?) in His Chariot with Wife and Attendant', + 'bowl fragment|ivory|ivories', 'as/mobile-large/DP110808.jpg', 1.20), + SearchData(749, 38514, 'The Brahmanical Triad: Brahma, Shiva, Vishnu', 'group|stone|sculpture', + 'as/mobile-large/1985_85_O.jpg', 1.06), + SearchData(1150, 38563, 'Twelve-Armed Chakrasamvara and His Consort Vajravarahi', 'figure|phyllite|sculpture', + 'as/mobile-large/39 DP310521R1_61D.jpg', 0.75), + SearchData( + 849, 38387, 'Seated Kubera (Study Collection)', 'figure|stone|sculpture', 'as/mobile-large/1984_318.JPG', 0.70), + SearchData(1599, 447796, 'Royal Horse and Runner', + 'illustrated album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DT4799.jpg', 1.51), + SearchData( + 1649, + 453276, + '"The Battle of Shahbarghan", Folio from a Padshahnama (Chronicle of the Emperor)', + 'folio from an illustrated manuscript|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/DT8442.jpg', + 0.68), + SearchData(0, 40097, 'Plaque with a Winged Goddess and Two Attendants', 'plaque|ivory|ivories', + 'as/mobile-large/2004_553_2.JPG', 0.77), + SearchData( + 0, 38518, 'Goddess and Attendants', 'plaque|terracotta|sculpture', 'as/mobile-large/h1_1990.281.jpg', 0.81), + SearchData(1649, 56209, 'Shallow Bowl', 'bowl|nephrite, very light opalescent yellowish-gray|jade', + 'as/mobile-large/02_18_761.JPG', 1.54), + SearchData( + 1850, + 31444, + 'Dagger with Sheath', + 'dagger with sheath|steel, jade, silver, gold, leather, wood, coral, turquoise|daggers', + 'aa/mobile-large/36.25.1048ab_002july2014.jpg', + 0.56), + SearchData(1099, 38139, 'Preening Celestial Deity', 'figure|ferruginous stone|sculpture', + 'as/mobile-large/264792.jpg', 0.34), + SearchData(1632, 35633, 'Shirt of Mail and Plate of Emperor Shah Jahan (reigned 1624-58)', + 'shirt of mail and plate|steel, iron, gold, leather|mail', 'aa/mobile-large/DP219616.jpg', 0.75), + SearchData(699, 37413, 'Panel from a Portable Shrine: The Descent of the Buddha from Trayastrimsha Heaven', + 'portable shrine panel|ivory with traces of color|ivories', 'as/mobile-large/DP267829.jpg', 0.75), + SearchData(1800, 64852, 'Royal Lovers in a Landscape Setting', + 'album leaf|leaf from an album; ink, colors and gold on paper|paintings', 'as/mobile-large/64_167_15.jpg', 0.68), + SearchData(949, 38743, 'Head of a Female', 'head|red sandstone|sculpture', 'as/mobile-large/33_50_4.JPG', 0.83), + SearchData(438, 38196, 'Nagaraja (Serpent King)', 'figure|stone|sculpture', 'as/mobile-large/1987_415_1.jpg', 0.57), + SearchData(1800, 31428, 'Dagger with Sheath', 'dagger with sheath|steel, jade, leather, wood|daggers', + 'aa/mobile-large/36.25.1031ab_003july2014.jpg', 0.56), + SearchData( + 1849, + 500709, + 'Taūs (mayuri)', + 'taūs (mayuri)|wood, parchment, metal, feathers|chordophone-lute-bowed-fretted', + 'mi/mobile-large/MI89.4.163.jpg', + 1.51), + SearchData( + 1850, + 31691, + 'Dagger (Jambiya) with Sheath', + 'dagger (jambiya) with sheath|steel, marble, gold, silver, silk, wood|daggers', + 'aa/mobile-large/36.25.711ab_002june2014.jpg', + 0.56), + SearchData( + 1675, + 23725, + 'Priming Flask', + 'priming flask|ivory, steel, pigment, resin|firearms accessories-flasks & primers', + 'aa/mobile-large/DP158306.jpg', + 1.58), + SearchData(1650, 31845, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, ivory (elephant), gemstone, gold|daggers', + 'aa/mobile-large/36.25.675_001june2014.jpg', 0.56), + SearchData(1778, 22848, 'Cuirass', 'cuirass|steel, iron, textile (velvet)|armor parts-cuirasses', + 'aa/mobile-large/DP219403.jpg', 0.79), + SearchData(899, 38435, 'Diadem with Kinnaris (Half-Bird, Half-Female Creatures)', + 'diadem|gold inset with garnet|jewelry', 'as/mobile-large/DP-14791-023.jpg', 1.80), + SearchData( + 1687, + 453243, + 'Base for a Water Pipe (Huqqa) with Irises', + 'water pipe base|zinc alloy; cast, engraved, inlaid with brass (bidri ware)|metal', + 'is/mobile-large/DP214317.jpg', + 0.75), + SearchData(1016, 39327, 'Shiva, Uma, and Their Son Skanda (Somaskandamurti)', 'figure|copper alloy|sculpture', + 'as/mobile-large/DT7390.jpg', 1.25), + SearchData(849, 38256, 'Shiva and Parvati with their Sons Karttikeya and Ganesha and the Calf Bull', + 'figure|stone|sculpture', 'as/mobile-large/DT6248.jpg', 0.80), + SearchData(1799, 56223, 'Oval box with cover', 'box|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP108024.jpg', 1.19), + SearchData(395, 39406, 'Gold Coin Showing King Chandragupta II as an Archer', 'coin|gold|jewelry', + 'as/mobile-large/1990_319_2.jpg', 1.07), + SearchData( + 649, 78910, 'Lakshmi, Goddess of Prosperity', 'figure|brass|sculpture', 'as/mobile-large/DP326749.jpg', 0.75), + SearchData(150, 38758, 'Lamp', 'lamp|stone|sculpture', 'as/mobile-large/33_50_22.JPG', 1.47), + SearchData(199, 38738, 'Head with Hat', 'head|terracotta|sculpture', 'as/mobile-large/28_162_2.JPG', 0.57), + SearchData(749, 39341, 'Karttikeya, the God of War', 'figure|brass with silver inlay|sculpture', + 'as/mobile-large/DP702274.jpg', 0.69), + SearchData(1875, 500736, 'Ghanti (bell)', 'ghanti (bell)|brass|idiophone-struck-bell-clapper', + 'mi/mobile-large/mi89.4.154.R.jpg', 0.66), + SearchData( + 1749, 56237, 'Dish', 'dish|jade (nephrite) with silver inlays|jade', 'as/mobile-large/02_18_780.JPG', 1.68), + SearchData( + 1725, + 454013, + 'Footed Bowl and Plate', + 'bowl and dish|glass, opalescent white; blown, bowl with applied stem and blown applied foot, fired silver and gold decoration|glass', + 'is/mobile-large/DT4697.jpg', + 1.25), + SearchData(250, 38754, 'Relief of a Female Deity', 'relief fragment|terracotta|sculpture', + 'as/mobile-large/33_50_18.JPG', 0.69), + SearchData(950, 38929, 'Seated Tara', 'figure|bronze|sculpture', 'as/mobile-large/1987_142_343.JPG', 0.68), + SearchData(499, 53472, 'Bust of Vishnu', 'bust|terracotta|sculpture', 'as/mobile-large/2000_82.jpg', 0.65), + SearchData(300, 49817, 'Box Lid with Incised Figural Decoration', 'box lid|ivory|ivories', + 'as/mobile-large/DT4347.jpg', 1.25), + SearchData(999, 38260, 'Shrine Relief Fragment Depicting Ashtamahabhaya Tara, the Buddhist Savioress', + 'fragment|wood|sculpture', 'as/mobile-large/30 DP130034R1_61C.jpg', 0.56), + SearchData( + 1612, 453261, 'Pierced Window Screen (Jali)', 'screen|marble|stone', 'is/mobile-large/DP-19479-001.jpg', 0.63), + SearchData(1049, 38146, 'Vishnu', 'stele|sandstone|sculpture', 'as/mobile-large/DT5052.jpg', 0.79), + SearchData(1149, 39251, 'Yoga Narasimha, Vishnu\'s Man-Lion Incarnation', 'figure|copper alloy|sculpture', + 'as/mobile-large/DP237096.jpg', 0.75), + SearchData(1800, 24793, 'Cuirass (Char-aina)', 'cuirass (char-aina)|steel, gold, textile|armor parts-cuirasses', + 'aa/mobile-large/DP151798.jpg', 1.00), + SearchData(1049, 38945, 'Hanuman Conversing', 'figure|copper alloy|sculpture', 'as/mobile-large/DT5250.jpg', 0.80), + SearchData(1250, 38947, 'Standing Shiva', 'figure|bronze|sculpture', 'as/mobile-large/1979_508.jpg', 0.64), + SearchData( + 1050, 38153, 'Celestial dancer (Devata)', 'figure|sandstone|sculpture', 'as/mobile-large/DP-1062-001.jpg', 0.83), + SearchData( + 1849, + 503672, + 'Śankh', + 'śankh|shell (turbinella pyrum), brass, wax|aerophone-lip vibrated-trumpet / trombone', + 'mi/mobile-large/DP353270.jpg', + 1.33), + SearchData( + 1750, 31726, 'Dagger (Katar)', 'dagger (katar)|steel|daggers', 'aa/mobile-large/36.25.746_003june2014.jpg', 0.56), + SearchData(1750, 24301, 'Dagger (Katar)', 'dagger (katar)|steel, gold|daggers', + 'aa/mobile-large/36.25.696_002july2014.jpg', 0.56), + SearchData(1649, 56205, 'Bowl', 'bowl|jade|jade', 'as/mobile-large/193272.jpg', 1.35), + SearchData(1587, 39317, 'Architectural Ensemble from a Jain Meeting Hall', + 'architectural ensemble|teak with traces of color|sculpture', 'as/mobile-large/DT5057.jpg', 1.25), + SearchData( + 1597, + 446563, + '"A Muslim Pilgrim Learns a Lesson in Piety from a Brahman", Folio from a Khamsa (Quintet) of Amir Khusrau Dihlavi', + 'folio from an illustrated manuscript|image: ink, opaque watercolor, and gold on paper margins: gold on dyed paper|codices', + 'is/mobile-large/DP159383.jpg', + 0.84), + SearchData(0, 38364, 'Two Lotuses, from the Bharhut Stupa', 'architectural relief|red sandstone|sculpture', + 'as/mobile-large/DP-15581-042.jpg', 1.45), + SearchData(0, 49910, 'Plaque with the Goddess Durga Standing on a Lotus', 'plaque|wood|sculpture', + 'as/mobile-large/2004_553_1.JPG', 0.32), + SearchData(1099, 37531, 'Stupa', 'stupa|bronze|metalwork', 'as/mobile-large/264772_3.jpg', 0.59), + SearchData(699, 38413, 'Head of a Bodhisattva', 'head|stucco on a clay core|sculpture', + 'as/mobile-large/33_50_5_F_sf.jpg', 1.15), + SearchData( + -249, 38363, 'Shard with Three Goddesses', 'shard|terracotta|sculpture', 'as/mobile-large/264805.jpg', 0.89), + SearchData(1462, 452780, 'Fragment of a Cornice with a Frieze of Masks', 'fragment of a cornice|sandstone|stone', + 'is/mobile-large/sf1975-197a.jpg', 2.69), + SearchData(1800, 24008, 'Armor of Mail and Plate', + 'armor of mail and plate|steel, iron, copper alloy, textile|armor for man', 'aa/mobile-large/DP151779.jpg', 0.92), + SearchData( + 1800, + 31823, + 'Dagger (Pesh-kabz) with Sheath', + 'dagger (pesh-kabz) with sheath|steel, rock crystal, silver, enamel, velvet, wood|daggers', + 'aa/mobile-large/36.25.650ab_006june2014.jpg', + 0.56), + SearchData(-1250, 50598, 'Harpoon', 'harpoon|copper|metalwork', 'as/mobile-large/2001_433_61_O.JPG', 0.46), + SearchData( + 1612, + 457019, + 'Engraved Bowl', + 'bowl|copper; cast, engraved, tinned, and inlaid with a black compound|metal', + 'is/mobile-large/DP246498.jpg', + 0.85), + SearchData( + 1649, + 446991, + 'Pen Box with Flowers, Birds and Animals', + 'pen box|ivory; carved, incised, and inlaid with black lacquer and gold|ivories and bone', + 'is/mobile-large/sf17-190-819a.jpg', + 4.05), + SearchData(1749, 39450, 'Guardian (Ainar)', 'portable idol|brass|sculpture', 'as/mobile-large/19_135_27.JPG', 0.69), + SearchData(149, 38740, 'Head of Buddha', 'head|stone|sculpture', 'as/mobile-large/29_68_1.JPG', 0.64), + SearchData(1949, 38543, 'Standing Nagaraja (Study Collection)', 'figure|stone|sculpture', + 'as/mobile-large/1991_453_1.JPG', 0.58), + SearchData( + 1750, + 31827, + 'Dagger with Sheath', + 'dagger with sheath|steel, gold, jade, beryl, ruby, emerald, topaz, silver, pearl, wood, velvet|daggers', + 'aa/mobile-large/36.25.654ab_002june2014.jpg', + 0.56), + SearchData(-49, 38091, 'Figures Riding an Elephant', 'figure|terracotta|sculpture', + 'as/mobile-large/1987_142_378_F.jpg', 0.80), + SearchData(1750, 31824, 'Dagger', 'dagger|steel, nephrite, gold, ruby|daggers', + 'aa/mobile-large/36.25.651_001june2014.jpg', 0.56), + SearchData(1827, 31466, 'Dagger (Katar) with Sheath', 'dagger (katar) with sheath|steel, gold, skin, silver|daggers', + 'aa/mobile-large/36.25.1070ab_002july2014.jpg', 0.56), + SearchData( + 1675, + 30360, + 'Priming Flask', + 'priming flask|ivory, iron, copper alloy|firearms accessories-flasks & primers', + 'aa/mobile-large/36.25.2419_001AA2016.jpg', + 1.78), + SearchData( + 1249, 38141, 'Loving Couple (Mithuna)', 'relief|ferruginous stone|sculpture', 'as/mobile-large/DT241.jpg', 0.80), + SearchData(1800, 24319, 'Saber', 'saber|steel, jade (nephrite), gold|swords', 'aa/mobile-large/DP163330.jpg', 0.75), + SearchData( + 1725, + 31505, + 'Punch Dagger (Katar) with Sheath', + 'punch dagger (katar) with sheath|steel, iron, silver, gold, rubies|daggers', + 'aa/mobile-large/DP157417.jpg', + 1.72), + SearchData(450, 38200, 'Standing Balarama or Nagaraja (Serpent King)', 'figure|red sandstone|sculpture', + 'as/mobile-large/1991_83_2_O.jpg', 0.50), + SearchData(1799, 56224, 'Sword handle', 'sword handle|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/99832.jpg', 0.72), + SearchData(483, 54489, 'Mask of Vaikuntha Vishnu', 'mask|bronze|sculpture', 'as/mobile-large/DP141923.jpg', 0.82), + SearchData( + 749, 38253, 'Gaja Lakshmi, Goddess of Fortune', 'relief|stone|sculpture', 'as/mobile-large/250880.jpg', 0.80), + SearchData(1099, 38131, 'Carved Conch with Lakshmi-Narayana', 'conch|shell with silver additions|shell', + 'as/mobile-large/DP-17092-001.jpg', 0.75), + SearchData(912, 39325, 'Standing Parvati', 'figure|copper alloy|metalwork', 'as/mobile-large/DT239.jpg', 0.75), + SearchData(-1250, 50627, 'Ax with Punch Marks', 'ax|copper|metalwork', 'as/mobile-large/2001_433_23.jpg', 0.66), + SearchData(600, 38965, 'Buddha Offering Protection', 'figure|copper alloy|sculpture', + 'as/mobile-large/DP-15581-036.jpg', 0.68), + SearchData(1750, 24932, 'Helmet', 'helmet|steel, brass, gold, textile, metallic thread|helmets', + 'aa/mobile-large/1988.147_007mar2015.jpg', 0.67), + SearchData( + 1750, + 31837, + 'Dagger (Jambiya)', + 'dagger (jambiya)|steel, ivory (walrus), silver, ruby, rose quartz|daggers', + 'aa/mobile-large/LC-36_25_665-004.jpg', + 0.56), + SearchData(1620, 444868, 'Coin', 'coin|silver|coins', 'is/mobile-large/LC-99_35_7405.jpg', 1.00), + SearchData(1600, 454003, 'Inlaid Box for the Portuguese Market', + 'box|wood (teak); veneered with ebony, inlaid ivory, and lac|wood', 'is/mobile-large/DP217229.jpg', 1.34), + SearchData( + 1816, 24013, 'Mail Shirt', 'mail shirt|iron, copper alloys, leather|mail', 'aa/mobile-large/DP147309.jpg', 1.00), + SearchData(1799, 56208, 'Box with cover', 'box|jade (nephrite)|jade', 'as/mobile-large/02_18_760_O2.jpg', 1.26), + SearchData(1637, 451286, '"Rosette Bearing the Names and Titles of Shah Jahan", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP240657.jpg', 0.70), + SearchData(1617, 444860, 'Coin with Gemini Zodiac Sign', 'coin|silver|coins', 'is/mobile-large/DP221340.jpg', 0.75), + SearchData(1800, 31846, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, rock crystal, gold|daggers', + 'aa/mobile-large/36.25.676_001june2014.jpg', 0.56), + SearchData(999, 38152, 'Chamunda, the Horrific Destroyer of Evil', 'figure|sandstone|sculpture', + 'as/mobile-large/DT5234.jpg', 0.80), + SearchData(1699, 457711, 'Floral Canopy or Screen', 'hanging|cotton, gold leaf; plain weave, painted|textiles', + 'is/mobile-large/DP330218.jpg', 1.01), + SearchData(450, 38394, 'Male Figure', 'figure|terracotta|sculpture', 'as/mobile-large/1982_471_1.JPG', 0.61), + SearchData(250, 38751, 'Fragment of a Horse\'s Head', 'statuette fragment|terracotta|sculpture', + 'as/mobile-large/33_50_15.JPG', 1.17), + SearchData(1099, 705435, 'Eight Great Events Stele', 'stele|stone|sculpture', 'as/mobile-large/DP-592-001.jpg', 0.71), + SearchData(1618, 448257, 'Inkpot of the Emperor Jahangir', 'inkwell|nephrite, gold|stone', + 'is/mobile-large/DP216054.jpg', 0.89), + SearchData(949, 38930, 'Shiva Seated with Uma (Umamaheshvara)', 'statuette|bronze|sculpture', + 'as/mobile-large/1978_253_F_sf.jpg', 0.71), + SearchData(1683, 72598, 'Upper Section of an Arch', 'archway section|wood|woodwork', + 'as/mobile-large/2004_466_01_Strm1.jpg', 2.52), + SearchData(791, 38133, 'Enthroned Vishnu', 'statue|granulite|sculpture', 'as/mobile-large/DT5240.jpg', 0.80), + SearchData( + 1800, + 31479, + 'Knife with Sheath', + 'knife with sheath|steel, jade, gold, ruby, gemstone, leather, silver|knives', + 'aa/mobile-large/36.25.1086ab_002july2014.jpg', + 0.56), + SearchData( + 1650, + 22100, + 'Priming Flask', + 'priming flask|ivory, wood, pigment, resin|firearms accessories-flasks & primers', + 'aa/mobile-large/DP158300.jpg', + 1.52), + SearchData(1885, 500718, 'Māhatī Vīṇa', 'māhatī vīṇa|gourd, various materials|chordophone-lute-plucked-fretted', + 'mi/mobile-large/156798.jpg', 1.95), + SearchData( + 149, 38732, 'Relief of a Mother Goddess(?)', 'relief|terracotta|sculpture', 'as/mobile-large/28_159_8.JPG', 0.62), + SearchData(1712, 38048, 'Shah Jahan Hunting Blackbuck with Trained Cheetahs', + '|ink, gold, and opaque watercolor on paper|paintings', 'as/mobile-large/DP156161.jpg', 1.51), + SearchData( + 550, 38203, 'Mother Goddess (Matrika)', 'figure|gray schist|sculpture', 'as/mobile-large/DT3648.jpg', 0.58), + SearchData(1849, 56248, 'Dagger', 'dagger|nephrite, very dark green, almost black|jade', + 'as/mobile-large/02_18_787.JPG', 3.51), + SearchData( + 1750, + 454049, + 'Prince and Ladies in a Garden', + 'illustrated single work|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/DP246522.jpg', + 0.70), + SearchData(100, 38430, 'Loving Couple (Mithuna)', 'disk|double-molded terracotta|sculpture', + 'as/mobile-large/DT8565.jpg', 0.80), + SearchData(1149, 38937, 'Shadakshari Lokeshvara', 'figure|bronze with silver and copper inlay|sculpture', + 'as/mobile-large/1982_457.jpg', 0.62), + SearchData(966, 75593, 'Chakra-Purusha, the Personified Discus Weapon of Vishnu', 'figure|copper alloy|sculpture', + 'as/mobile-large/DP-20870-001.jpg', 0.75), + SearchData( + 1687, + 453184, + 'Fragment of a Floorspread', + 'fragment|cotton; plain weave, mordant-painted and dyed, resist-dyed|textiles-rugs', + 'is/mobile-large/DP344703.jpg', + 2.02), + SearchData(849, 38384, 'Four-Armed Durga Seated on Her Lion Vehicle', 'stele|stone|sculpture', + 'as/mobile-large/1990_15.jpg', 0.71), + SearchData(1649, 451313, 'Portrait of Islam Khan Mashhadi', + 'illustrated single work|opaque watercolor and gold on paper|codices', 'is/mobile-large/DT4816.jpg', 0.66), + SearchData(1749, 447774, 'Mirror', 'mirror|jade|stone', 'is/mobile-large/24.80.562.jpg', 0.61), + SearchData(963, 39326, 'Standing Vishnu', 'figure|copper alloy|metalwork', 'as/mobile-large/DT329883.jpg', 0.80), + SearchData(1574, 453343, 'Pierced Window Screen', 'screen|red sandstone; pierced, carved|stone', + 'is/mobile-large/sf1993-67-1a.jpg', 0.68), + SearchData(1799, 56227, 'Bowl', 'bowl|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP-14153-043.jpg', 1.33), + SearchData(999, 39189, 'Crowned Buddha', 'shrine|bronze inlaid with silver, lapis lazuli, and rock crystal|sculpture', + 'as/mobile-large/16 NEW DP314094r4_61E.jpg', 0.75), + SearchData(1790, 204015, 'Four-light candelabrum', 'candelabrum|ivory|natural substances-ivory', + 'es/mobile-large/DP-13853-069.jpg', 0.63), + SearchData(1800, 31686, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, jade|daggers', + 'aa/mobile-large/36.25.706_001june2014.jpg', 0.56), + SearchData( + -1000, 50573, 'Antennae Sword', 'antennae sword|copper|metalwork', 'as/mobile-large/2001_433_53_O.JPG', 0.43), + SearchData(1650, 32137, 'Helmet', 'helmet|steel, iron, copper alloy|helmets', 'aa/mobile-large/DP151784.jpg', 1.00), + SearchData( + 1750, + 22872, + 'Dagger with Sheath', + 'dagger with sheath|steel, nephrite, gold, emerald, ruby, diamond, sapphire, jade|daggers', + 'aa/mobile-large/DP158185.jpg', + 0.69), + SearchData(-50, 39676, 'One from a Pair of Ear Ornaments (Prakaravapra Kundala)', + 'earring|gold, sheet, wire and granulation|jewelry', 'as/mobile-large/DP-14791-006.jpg', 1.37), + SearchData(1099, 39248, 'Standing Surya', 'figure|copper alloy|sculpture', 'as/mobile-large/DT4673.jpg', 0.63), + SearchData(1618, 444859, 'Coin', 'coin|gold|coins', 'is/mobile-large/DP25.jpg', 1.00), + SearchData( + 1750, + 31442, + 'Knife (Kard) with Sheath', + 'knife (kard) with sheath|steel, nephrite, silver, gold, ruby, velvet, wood|daggers', + 'aa/mobile-large/36.25.1046ab_003july2014.jpg', + 0.56), + SearchData(-1000, 50599, 'Serrated Harpoon', 'harpoon|copper|metalwork', 'as/mobile-large/2001_433_69_O.JPG', 0.42), + SearchData(1799, 56236, 'Bottle in the shape of a gourd', 'bottle|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/02_18_779.JPG', 0.58), + SearchData(1640, 453188, 'Dagger with Hilt of Leafy Plants', + 'dagger|hilt: nephrite blade: watered steel|arms and armor', 'is/mobile-large/DP158638.jpg', 0.70), + SearchData(1772, 456967, 'A Gathering of Holy Men of Different Faiths', + 'illustrated single work|opaque watercolor and gold on paper|codices', 'is/mobile-large/DP213133.jpg', 0.76), + SearchData(1149, 38137, 'Shiva Emerging from the Linga (Lingodbhavamurti)', 'carved pillar|gray stone|sculpture', + 'as/mobile-large/DP-16372-001.jpg', 0.75), + SearchData(799, 39344, 'Linga with Face of Shiva (Ekamukhalinga)', + 'linga|brass with copper and silver inlay|sculpture', 'as/mobile-large/DP-23782-001.jpg', 0.75), + SearchData(1850, 31850, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, jade, stone|daggers', + 'aa/mobile-large/36.25.680_001june2014.jpg', 0.56), + SearchData(272, 38237, 'Buddha', 'torso|limestone|sculpture', 'as/mobile-large/DP701399.jpg', 0.72), + SearchData(1800, 31826, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, jade, gold, silver|daggers', + 'aa/mobile-large/36.25.653_001june2014.jpg', 0.56), + SearchData(1625, 444863, 'Coin with Sign of Libra', 'coin|gold|coins', 'is/mobile-large/DP27.jpg', 1.00), + SearchData( + 1625, + 457730, + 'Dagger with Zoomorphic Hilt', + 'dagger|hilt: copper; cast, chased, gilded, and inlaid with rubies. blade: steel; forged|arms and armor', + 'is/mobile-large/DP253146.jpg', + 0.75), + SearchData(-1250, 50592, 'Anthropomorph', 'anthropomorph|copper|metalwork', 'as/mobile-large/2001_433_5.jpg', 1.49), + SearchData( + 1597, + 822698, + 'Portrait of Kuchal Oghlan: Folio from Salim\'s Album', + 'album leaf, illustrated|opaque watercolor and gold on paper|codices', + 'is/mobile-large/TR.82.2019 3-28-19.jpg', + 0.65), + SearchData(1799, 56219, 'Mirror frame', 'mirror frame|jade (nephrite)|jade', 'as/mobile-large/ISL143.jpg', 0.69), + SearchData(1800, 31685, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, nephrite, gold, ruby|daggers', + 'aa/mobile-large/36.25.705_002june2014.jpg', 0.56), + SearchData(199, 39124, 'Seated Goddess', 'figurine|bronze|sculpture', 'as/mobile-large/1987_142_338_F.JPG', 0.74), + SearchData(-1250, 50639, 'Eccentric Anthropomorph', 'anthropomorph|copper|metalwork', + 'as/mobile-large/2001_433_10_O.JPG', 0.93), + SearchData(-50, 64486, 'Yakshi Holding a Crowned Child with a Visiting Parrot', 'plaque|terracotta|sculpture', + 'as/mobile-large/DP158763.jpg', 0.75), + SearchData(1635, 453054, 'Floral Tent Panel', 'tent lining|silk, gold; cut velvet, painted|textiles', + 'is/mobile-large/DP210624.jpg', 2.05), + SearchData( + 1597, + 446560, + '"A King Offers to Make Amends to a Bereaved Mother", Folio from a Khamsa (Quintet) of Amir Khusrau Dihlavi', + 'folio from an illustrated manuscript|main support: ink, opaque watercolor, gold on paper margins: gold on dyed paper|codices', + 'is/mobile-large/DP120801.jpg', + 0.68), + SearchData(1650, 453341, 'Mango-Shaped Flask', + 'flask|rock crystal; set with gold, enamel, rubies, and emeralds|stone', 'is/mobile-large/DP240307.jpg', 0.75), + SearchData(1700, 24029, 'Helmet', 'helmet|steel, iron, gold|helmets', 'aa/mobile-large/DP152944.jpg', 0.75), + SearchData(1617, 453435, 'Coin', 'coin|silver|coins', 'is/mobile-large/99.35.6553.JPG', 1.07), + SearchData(1112, 38517, 'Standing Vishnu as Keshava', 'figure|stone|sculpture', 'as/mobile-large/DT5252.jpg', 0.54), + SearchData(1600, 453260, 'Calligraphic Roundel, inscribed "Ya \'Aziz" (Oh Mighty)', + 'roundel|sandstone; carved, traces of pigment|stone', 'is/mobile-large/DT8177.jpg', 0.80), + SearchData(1799, 56255, 'Buckle', 'buckle|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP108026.jpg', 1.25), + SearchData( + 1675, + 30363, + 'Priming Flask', + 'priming flask|ivory, iron, copper alloy|firearms accessories-flasks & primers', + 'aa/mobile-large/36.25.2423_004AA2016.jpg', + 1.78), + SearchData(666, 38559, 'Vajrapani, the Thunderbolt-bearing Bodhisattva', 'stele|stone|sculpture', + 'as/mobile-large/DP-15581-058.jpg', 0.74), + SearchData( + 1750, + 35949, + 'Cuirass (Char-aina) with Mail Shirt', + 'cuirass (char-aina) with mail shirt|steel, iron, gold, leather, textile|armor parts', + 'aa/mobile-large/DP147307.jpg', + 1.00), + SearchData(1780, 457783, 'Two Late Mughal Letters', 'non-illustrated single work|ink and gold on paper|codices', + 'is/mobile-large/DP273298.jpg', 0.59), + SearchData(150, 38509, 'Standing Indra', 'standing indra|sandstone|sculpture', 'as/mobile-large/DT5718.jpg', 0.80), + SearchData(1612, 453241, 'Pierced Window Screen (Jali)', 'screen|marble|stone', 'is/mobile-large/DT7720.jpg', 0.62), + SearchData(-50, 38734, 'Bust of a Female Deity (Yakshi?)', 'bust|gray clay|sculpture', + 'as/mobile-large/28_159_11.JPG', 0.80), + SearchData(599, 38424, 'Mirror Handle with a Woman Playing the Lute', 'mirror handle|chlorite schist|sculpture', + 'as/mobile-large/DT8673.jpg', 0.80), + SearchData(224, 38239, 'Drum panel with Great Departure and Temptation of the Buddha scenes', + 'relief|limestone|sculpture', 'as/mobile-large/DP-18911-001.jpg', 0.83), + SearchData(199, 38733, 'Head of a Female', 'head|terracotta|sculpture', 'as/mobile-large/28_159_10.JPG', 0.80), + SearchData(1749, 452777, 'Bottle with Gilded Flowers', 'bottle|glass, purple; mold blown, gilded|glass', + 'is/mobile-large/sf1975-194-2a.jpg', 0.67), + SearchData( + 1597, + 446561, + '"Alexander is Lowered into the Sea", Folio from a Khamsa (Quintet) of Amir Khusrau Dihlavi', + 'folio from an illustrated manuscript|main support: ink, watercolor, gold on paper margins: gold on dyed paper|codices', + 'is/mobile-large/DP120802.jpg', + 0.67), + SearchData(1687, 450750, 'Sash (Patka)', 'sash|cotton, silk; plain weave, embroidered|textiles-costumes', + 'is/mobile-large/DP272849.jpg', 5.09), + SearchData(1749, 56218, 'Dish in the shape of a chrysanthemum flower', 'dish|jade (nephrite)|jade', + 'as/mobile-large/DP108023.jpg', 1.41), + SearchData(1099, 38120, 'Vishnu with His Consorts, Lakshmi and Sarasvati', 'relief|black stone|sculpture', + 'as/mobile-large/57_51_7.jpg', 0.83), + SearchData(1749, 77164, 'Vessel in the form of a Mango', 'vessel|silver and fabric|metalwork', + 'as/mobile-large/DP-23606-001.jpg', 1.33), + SearchData(-50, 38487, 'Disk Stone with a Four-Part Tracery Design of Palmettes', 'disk stone|stone|sculpture', + 'as/mobile-large/DP-18252-016.jpg', 1.33), + SearchData(550, 38492, 'Lotus-Headed Fertility Goddess Lajja Gauri', 'relief|sandstone|sculpture', + 'as/mobile-large/DP253528.jpg', 1.33), + SearchData(-1250, 56621, 'Harpoon', 'harpoon|copper|metalwork', 'as/mobile-large/2001_433_77_O.JPG', 0.42), + SearchData(1649, 70582, 'Dye-Patterned Silk', 'lining|silk (clamp resist, dyed)|textiles-printed', + 'as/mobile-large/DP278411.jpg', 2.35), + SearchData( + 1783, + 65576, + 'Pichhwai for the Festival of Cows', + 'ceremonial cloth|painted and printed gold and silver leaf and opaque watercolor on indigo-dyed cotton|textiles-painted and printed', + 'as/mobile-large/DP156677.jpg', + 1.04), + SearchData(1749, 444832, 'Rock Crystal Box', 'box|rock crystal; inlaid with gold, inset with rubies|stone', + 'is/mobile-large/LC-95_14_12.jpg', 1.40), + SearchData( + 1675, + 24298, + 'Dagger with Sheath', + 'dagger with sheath|steel, nephrite, gold, rubies, emeralds, silver-gilt, leather|daggers', + 'aa/mobile-large/DP157696.jpg', + 0.73), + SearchData(549, 38251, 'Vishvarupa Vishnu', 'sculpture|stone|sculpture', 'as/mobile-large/DP-14892-001.jpg', 1.33), + SearchData(949, 713008, 'Miniature Brahmanical Shrine', 'shrine|copper alloy|metalwork', + 'as/mobile-large/LC-2016_242_sr1-002.jpg', 0.65), + SearchData(816, 39194, 'Vishnu Flanked by His Personified Attributes', 'group|bronze|sculpture', + 'as/mobile-large/DP702345.jpg', 0.63), + SearchData(1617, 444857, 'Coin', 'coin|silver|coins', 'is/mobile-large/LC-99.35.2385.jpg', 1.50), + SearchData(1649, 56228, 'Jewelled Dagger Handle', 'dagger-handle|nephrite, white with faint bluish tint|jade', + 'as/mobile-large/99834.jpg', 0.74), + SearchData(633, 78430, 'Mask of Bhairava', 'mask|copper alloy, possibly brass|sculpture', + 'as/mobile-large/DP702309.jpg', 0.70), + SearchData(1010, 768115, 'Shiva as Vanquisher of the Three Cities (Shiva Tripuravijaya)', + 'figure|copper alloy|sculpture', 'as/mobile-large/DP-16371-002.jpg', 0.75), + SearchData(1580, 451262, '"Great Hornbill", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP246533.jpg', 1.46), + SearchData(850, 39340, 'Crowned and Jeweled Buddha', 'sculpture|copper with silver inlay|metalwork', + 'as/mobile-large/1983_555_4_O.jpg', 0.58), + SearchData(-200, 38431, 'Standing Female Deity', 'figure|terracotta, black oxidation patina|sculpture', + 'as/mobile-large/DP251647.jpg', 0.75), + SearchData(949, 38615, 'Stele with Eight Great Events from the Life of the Buddha', + 'stele|black schist with traces of gilding|sculpture', 'as/mobile-large/11r1_61B.jpg', 0.75), + SearchData(1612, 452680, 'Reception of a Persian Ambassador by a Mughal Prince', + 'illustrated album leaf|opaque watercolor and gold on paper|codices', 'is/mobile-large/DP291186.jpg', 1.01), + SearchData(-50, 64487, 'Pot', 'pot|ceramic|ceramics', 'as/mobile-large/2001_588_Strm1.JPG', 1.51), + SearchData( + 1749, 447773, 'Bowl with Acanthus Leaf Handles', 'bowl|nephrite|stone', 'is/mobile-large/sf24-80-141b.jpg', 1.20), + SearchData(799, 38154, 'Torso of a Hindu Deity', 'figure|stone|sculpture', 'as/mobile-large/33_65_1_F.JPG', 0.59), + SearchData(-1250, 50626, 'Ax with Punch Marks', 'ax|copper|metalwork', 'as/mobile-large/2001_433_22.jpg', 0.67), + SearchData(-1000, 39432, 'Anthropomorph', 'anthropomorph|copper|metalwork', 'as/mobile-large/2000_284_37.jpg', 0.81), + SearchData( + -99, 38079, 'Ring Stone', 'ringstone fragment|stone|sculpture', 'as/mobile-large/1987_142_374_O.jpg', 1.84), + SearchData(150, 38264, 'Head of a Demonic Male Deity', 'head|mottled red sandstone|sculpture', + 'as/mobile-large/DT8557.jpg', 0.80), + SearchData( + 1775, + 22938, + 'Smallsword with Scabbard', + 'smallsword with scabbard|steel, gold, wood, leather, textile|swords', + 'aa/mobile-large/LC-26_145_338a_b-002.jpg', + 0.71), + SearchData( + 1605, + 37936, + 'Six Gopis Seated Beneath Trees: Page from a Dispersed Bhagavata Purana (Ancient Stories of Lord Vishnu)', + 'folio|ink and opaque watercolor on paper|paintings', + 'as/mobile-large/DP152322.jpg', + 2.79), + SearchData(1149, 39193, 'Jain Digambara Tirthanhara Standing in Kayotsarga Meditation Posture', + 'figure|copper alloy|sculpture', 'as/mobile-large/DP-593-001.jpg', 0.61), + SearchData(250, 38755, 'Relief of a Female Deity', 'relief fragment|terracotta|sculpture', + 'as/mobile-large/33_50_19.JPG', 0.73), + SearchData(-1250, 50619, 'Ax Blade (Celt)', 'ax blade|copper|metalwork', 'as/mobile-large/2001_433_19_O.JPG', 0.87), + SearchData(600, 39128, 'Tara', 'figure|copper alloy|sculpture', 'as/mobile-large/DP253393.jpg', 0.75), + SearchData( + 1800, + 31825, + 'Dagger (Khanjar) with Sheath', + 'dagger (khanjar) with sheath|steel, jade, gold, ruby, emerald, diamond, silver, pearl, wood, velvet|daggers', + 'aa/mobile-large/36.25.652ab_003june2014.jpg', + 0.56), + SearchData(-100, 38087, 'Plaque with Goddess and Attendant', 'plaque|terracotta|sculpture', + 'as/mobile-large/1987_417_3.jpg', 0.65), + SearchData( + 1800, + 31862, + 'Dagger (Katar) with Sheath', + 'dagger (katar) with sheath|steel, gold, ruby, emerald, diamond, wood, velvet|daggers', + 'aa/mobile-large/36.25.692ab_010june2014.jpg', + 0.56), + SearchData(1710, 454619, 'The Emperor Aurangzeb Carried on a Palanquin', + 'illustrated single work|opaque watercolor and gold on paper|codices', 'is/mobile-large/DP116022.jpg', 0.68), + SearchData(1700, 32259, 'Dagger', 'dagger|steel, jade, ruby, gold|daggers', + 'aa/mobile-large/32.75.264_001june2014.jpg', 0.56), + SearchData(1850, 21922, 'Shield (Dhál)', 'shield (dhàl)|steel, gold, silk-velvet|shields', + 'aa/mobile-large/sfma02.5.4_165461.jpg', 0.96), + SearchData( + 1649, + 452813, + 'Flower-Style Box with Drawers', + 'box|wood (poplar); overlaid with ebony inlaid with wood and incised, stained ivory|wood', + 'is/mobile-large/wb-1976.176.1b.JPG', + 1.50), + SearchData(-149, 38924, 'Goddess with Weapons in Her Hair', 'figure|copper alloy|sculpture', + 'as/mobile-large/DP252968.jpg', 0.69), + SearchData( + 1700, 33536, 'Hilt of a Dagger', 'hilt of a dagger|nephrite|daggers', 'aa/mobile-large/DP158302.jpg', 0.84), + SearchData(1875, 500764, 'Turhā or Karnā', 'turhā or karnā|brass|aerophone-lip vibrated-trumpet / trombone', + 'mi/mobile-large/DP-12679-003.jpg', 1.97), + SearchData(1850, 31689, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, horn, ruby|daggers', + 'aa/mobile-large/36.25.709_002june2014.jpg', 0.56), + SearchData(983, 39608, 'Celestial Attendant', 'figure|bronze|sculpture', 'as/mobile-large/1996_475_O1.jpg', 1.33), + SearchData(-283, 38084, 'Ring stone with goddesses and aquatic plants', 'ring stone|steatite (?)|sculpture', + 'as/mobile-large/DP-18252-025.jpg', 1.33), + SearchData(424, 38242, 'Linga with Face of Shiva (Ekamukhalinga)', 'linga|sandstone|sculpture', + 'as/mobile-large/DP297454.jpg', 0.75), + SearchData(1700, 457771, 'A Bejewelled Maiden with a Parakeet', + 'illustrated single work|opaque watercolor and gold on paper|codices', 'is/mobile-large/DP269558.jpg', 0.70), + SearchData(1600, 456312, 'Spittoon or Incense Burner', 'spittoon|brass; cast in sections, joined, engraved|metal', + 'is/mobile-large/DP231251.jpg', 0.75), + SearchData(499, 40076, 'Tile with Impressed Figure of Emaciated Ascetics and Couples Behind Balconies', + 'tile|terracotta|ceramics', 'as/mobile-large/1998_122.jpg', 0.76), + SearchData(1799, 56230, 'Jewelled plate', 'plate|jade (nephrite) with gold and stone inlays|jade', + 'as/mobile-large/DP-14153-029.jpg', 1.14), + SearchData(1649, 56204, 'Bowl', 'bowl|nephrite|jade', 'as/mobile-large/32883.jpg', 1.41), + SearchData(1750, 24425, 'Lance', 'lance|steel, gold, silver|shafted weapons', 'aa/mobile-large/DP210386.jpg', 0.42), + SearchData(-1, 38367, 'Toy', 'toy|terracotta|sculpture', 'as/mobile-large/1984_491_16.jpg', 1.00), + SearchData(850, 37400, 'Stupa', 'stupa|bronze|sculpture', 'as/mobile-large/264819.jpg', 0.75), + SearchData(50, 38746, 'Figure of a Ram', 'statuette|terracotta|sculpture', 'as/mobile-large/33_50_8.JPG', 1.45), + SearchData(885, 38252, 'Four-Armed Goddess, possibly Sarada', 'figure|chlorite schist|sculpture', + 'as/mobile-large/DT5248.jpg', 0.80), + SearchData(1949, 72384, 'Standing Buddha', 'figure|bronze|sculpture', 'as/mobile-large/271785.jpg', 0.61), + SearchData( + 1700, + 454738, + 'Goa Stone and Gold Case', + 'goa stone and container|container: gold; pierced, repoussé, with cast legs and finials goa stone: compound of organic and inorganic materials|metal', + 'is/mobile-large/DP116018.jpg', + 1.00), + SearchData(450, 38219, 'Ear Plug with Kinnari (Half-Bird, Half-Female Creature)', 'ear plug|terracotta|sculpture', + 'as/mobile-large/1987_142_390.jpg', 1.15), + SearchData( + 1150, + 73214, + 'Model of the Mahabodhi Temple', + 'temple model|quartz-muscovite-chlorite-talc phyllite|sculpture', + 'as/mobile-large/8 NEW DP257785r1_61E.jpg', + 0.75), + SearchData( + 1627, + 451270, + '"Shah Jahan on a Terrace, Holding a Pendant Set With His Portrait", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', + 'is/mobile-large/DP246547.jpg', + 0.67), + SearchData(1585, 451265, '"Portrait of Maharaja Bhim Kanwar", Folio from the Shah Jahan Album', + 'album leaf|ink, opaque watercolor, and gold on paper|codices', 'is/mobile-large/DP246545.jpg', 0.67), + SearchData(449, 38731, 'Head of a Buddha', 'head|mottled red and white sandstone|sculpture', + 'as/mobile-large/28_159_5.JPG', 0.64), + SearchData(1850, 31443, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, rock crystal|daggers', + 'aa/mobile-large/36.25.1047_001july2014.jpg', 0.56), + SearchData( + 449, 38205, 'Standing Four-Armed Vishnu', 'figure|terracotta|sculpture', 'as/mobile-large/DP323567.jpg', 0.75), + SearchData(50, 38728, 'Head of Buddha', 'head|red sandstone|sculpture', 'as/mobile-large/28_97_2.JPG', 0.74), + SearchData(453, 38199, 'Standing Nagaraja (Serpent King)', 'figure|red sandstone|sculpture', + 'as/mobile-large/1991_83_1_O.jpg', 0.55), + SearchData( + 949, 74100, 'Dancing Ganesha', 'figure|mottled red sandstone|sculpture', 'as/mobile-large/DP158921.jpg', 0.75), + SearchData(1149, 74502, 'The Goddess Durga Slaying the Demon Buffalo Mahisha', 'figure|brass|sculpture', + 'as/mobile-large/DP231297.jpg', 0.75), + SearchData( + 1949, 44669, 'Standing Nagini (Study Collection)', 'figure||sculpture', 'as/mobile-large/1991_453_2.JPG', 0.48), + SearchData(9, 39345, 'Crowned Buddha', 'figure|brass|sculpture', 'as/mobile-large/DT229537.jpg', 0.80), + SearchData(1749, 452734, 'Gilded Green Bottle', 'bottle|glass, green; mold blown, gilded, and silvered|glass', + 'is/mobile-large/sf1975-64a.jpg', 0.57), + SearchData(1793, 24490, 'Flintlock Blunderbuss', 'flintlock blunderbuss|steel, wood, gold, silver|firearms', + 'aa/mobile-large/DP165542.jpg', 2.07), + SearchData(749, 37412, 'Panel of a Portable Buddhist Shrine with Dancer and Musician Celebrants', + 'panel|ivory with traces of color|ivories', 'as/mobile-large/1988_148_1.jpg', 1.34), + SearchData(-249, 38086, 'Sphere with Scenes of Rites at the Shrine of a Yaksha (Male Nature Spirit)', + 'sphere|stone|sculpture', 'as/mobile-large/DP-18252-030.jpg', 1.33), + SearchData(1674, 452815, 'Shield with Hunting and Landscape Vignettes', + 'shield|steel; with gold overlay|arms and armor', 'is/mobile-large/DP153429.jpg', 1.25), + SearchData( + 1786, + 24328, + 'Saber (Talwar) with Scabbard', + 'saber (talwar) with scabbard|steel, silver, diamonds enamel, leather|swords', + 'aa/mobile-large/DP163746.jpg', + 0.77), + SearchData(1850, 31844, 'Dagger (Khanjar)', 'dagger (khanjar)|steel, nephrite, ruby, gold|daggers', + 'aa/mobile-large/36.25.674_002june2014.jpg', 0.56), + SearchData( + 1800, + 31681, + 'Dagger (Pesh-kabz) with Sheath', + 'dagger (pesh-kabz) with sheath|jade, steel, silver, wood, velvet, ruby, gold|daggers', + 'aa/mobile-large/36.25.700ab_002june2014.jpg', + 0.56), + SearchData(116, 38381, 'Princely Couple', 'relief|terracotta|sculpture', 'as/mobile-large/31_82_6.JPG', 0.70), + SearchData(1792, 203778, 'Chandelier', 'chandelier|ivory|natural substances-ivory', + 'es/mobile-large/DP-13853-084.jpg', 1.00), + SearchData(1000, 39489, 'Meditating Buddha with Alms Bowl Enthroned in a Foliated Niche', + 'architectural element|stone|sculpture', 'as/mobile-large/DP158746.jpg', 1.33), +]; diff --git a/lib/logic/data/wonders_data/taj_mahal_data.dart b/lib/logic/data/wonders_data/taj_mahal_data.dart new file mode 100644 index 00000000..320faf2f --- /dev/null +++ b/lib/logic/data/wonders_data/taj_mahal_data.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; + +part 'search/taj_mahal_search_data.dart'; + +class TajMahalData extends WonderData { + TajMahalData() + : super( + searchData: _searchData, // included as a part from ./search/ + searchSuggestions: _searchSuggestions, // included as a part from ./search/ + type: WonderType.tajMahal, + title: $strings.tajMahalTitle, + subTitle: $strings.tajMahalSubTitle, + regionTitle: $strings.tajMahalRegionTitle, + videoId: 'EWkDzLrhpXI', + startYr: 1632, + endYr: 1653, + artifactStartYr: 1600, + artifactEndYr: 1700, + artifactCulture: $strings.tajMahalArtifactCulture, + artifactGeolocation: $strings.tajMahalArtifactGeolocation, + lat: 27.17405039840427, + lng: 78.04211890065208, + unsplashCollectionId: '684IRta86_c', + pullQuote1Top: $strings.tajMahalPullQuote1Top, + pullQuote1Bottom: $strings.tajMahalPullQuote1Bottom, + pullQuote1Author: $strings.tajMahalPullQuote1Author, + pullQuote2: $strings.tajMahalPullQuote2, + pullQuote2Author: $strings.tajMahalPullQuote2Author, + callout1: $strings.tajMahalCallout1, + callout2: $strings.tajMahalCallout2, + videoCaption: $strings.tajMahalVideoCaption, + mapCaption: $strings.tajMahalMapCaption, + historyInfo1: $strings.tajMahalHistoryInfo1, + historyInfo2: $strings.tajMahalHistoryInfo2, + constructionInfo1: $strings.tajMahalConstructionInfo1, + constructionInfo2: $strings.tajMahalConstructionInfo2, + locationInfo1: $strings.tajMahalLocationInfo1, + locationInfo2: $strings.tajMahalLocationInfo2, + highlightArtifacts: const [ + '453341', + '453243', + '73309', + '24932', + '56230', + '35633', + ], + hiddenArtifacts: const [ + '24907', + '453183', + '453983', + ], + events: { + 1631: $strings.tajMahal1631ce, + 1647: $strings.tajMahal1647ce, + 1658: $strings.tajMahal1658ce, + 1901: $strings.tajMahal1901ce, + 1984: $strings.tajMahal1984ce, + 1998: $strings.tajMahal1998ce, + }, + ); +} diff --git a/lib/logic/locale_logic.dart b/lib/logic/locale_logic.dart new file mode 100644 index 00000000..c07ac9e3 --- /dev/null +++ b/lib/logic/locale_logic.dart @@ -0,0 +1,22 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:intl/intl_standalone.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class LocaleLogic { + AppLocalizations? _strings; + AppLocalizations get strings => _strings!; + + bool get isLoaded => _strings != null; + + Future load() async { + final localeCode = await findSystemLocale(); + Locale locale = Locale(localeCode.split('_')[0]); + if (kDebugMode) { + // Uncomment for testing in chinese + // locale = Locale('zh'); + } + _strings = await AppLocalizations.delegate.load(locale); + } +} diff --git a/lib/logic/met_api_logic.dart b/lib/logic/met_api_logic.dart new file mode 100644 index 00000000..cf37a1ba --- /dev/null +++ b/lib/logic/met_api_logic.dart @@ -0,0 +1,23 @@ +import 'dart:collection'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/artifact_data.dart'; +import 'package:wonders/logic/met_api_service.dart'; + +import 'package:wonders/logic/common/http_client.dart'; + +class MetAPILogic { + final HashMap _artifactCache = HashMap(); + + MetAPIService get service => GetIt.I.get(); + + /// Returns artifact data by ID. Returns null if artifact cannot be found. */ + Future getArtifactByID(String id) async { + if (_artifactCache.containsKey(id)) return _artifactCache[id]; + ServiceResult result = (await service.getObjectByID(id)); + if (!result.success) throw StringUtils.supplant($strings.artifactDetailsErrorNotFound, {'{artifactId}': id}); + ArtifactData? artifact = result.content; + return _artifactCache[id] = artifact; + } +} diff --git a/lib/logic/met_api_service.dart b/lib/logic/met_api_service.dart new file mode 100644 index 00000000..c8cb9a3e --- /dev/null +++ b/lib/logic/met_api_service.dart @@ -0,0 +1,30 @@ +import 'package:wonders/logic/common/http_client.dart'; +import 'package:wonders/logic/data/artifact_data.dart'; + +class MetAPIService { + final String _baseMETUrl = 'https://collectionapi.metmuseum.org/public/collection/v1'; + + Future> getObjectByID(String id) async { + HttpResponse? response = await HttpClient.send('$_baseMETUrl/objects/$id'); + return ServiceResult(response, _parseArtifactData); + } + + ArtifactData? _parseArtifactData(Map content) { + // Source: https://metmuseum.github.io/ + return ArtifactData( + objectId: content['objectID'].toString(), + title: content['title'] ?? '', + image: content['primaryImage'] ?? '', + date: content['objectDate'] ?? '', + objectType: content['objectName'] ?? '', + period: content['period'] ?? '', + country: content['country'] ?? '', + medium: content['medium'] ?? '', + dimension: content['dimension'] ?? '', + classification: content['classification'] ?? '', + culture: content['culture'] ?? '', + objectBeginYear: content['objectBeginDate'], + objectEndYear: content['objectEndDate'], + ); + } +} diff --git a/lib/logic/settings_logic.dart b/lib/logic/settings_logic.dart new file mode 100644 index 00000000..451a1111 --- /dev/null +++ b/lib/logic/settings_logic.dart @@ -0,0 +1,26 @@ +import 'package:flutter/foundation.dart'; +import 'package:wonders/logic/common/save_load_mixin.dart'; + +class SettingsLogic with ThrottledSaveLoadMixin { + @override + String get fileName => 'settings.dat'; + + late final hasCompletedOnboarding = ValueNotifier(false)..addListener(scheduleSave); + late final hasDismissedSearchMessage = ValueNotifier(false)..addListener(scheduleSave); + + final bool useBlurs = defaultTargetPlatform != TargetPlatform.android; + + @override + void copyFromJson(Map value) { + hasCompletedOnboarding.value = value['hasCompletedOnboarding'] ?? false; + hasDismissedSearchMessage.value = value['hasDismissedSearchMessage'] ?? false; + } + + @override + Map toJson() { + return { + 'hasCompletedOnboarding': hasCompletedOnboarding.value, + 'hasDismissedSearchMessage': hasDismissedSearchMessage.value, + }; + } +} diff --git a/lib/logic/timeline_logic.dart b/lib/logic/timeline_logic.dart new file mode 100644 index 00000000..32dc2995 --- /dev/null +++ b/lib/logic/timeline_logic.dart @@ -0,0 +1,20 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/timeline_data.dart'; + +class TimelineLogic { + final List events = []; + + Future init() async { + events.addAll(GlobalEventsData().globalEvents); + + for (var w in wondersLogic.all) { + events.add( + TimelineEvent( + w.startYr, + StringUtils.supplant($strings.timelineLabelConstruction, {'{title}': w.title}), + ), + ); + } + } +} diff --git a/lib/logic/unsplash_logic.dart b/lib/logic/unsplash_logic.dart new file mode 100644 index 00000000..a85a505f --- /dev/null +++ b/lib/logic/unsplash_logic.dart @@ -0,0 +1,12 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/unsplash_photo_data.dart'; +import 'package:wonders/logic/unsplash_service.dart'; + +class UnsplashLogic { + final Map> _idsByCollection = UnsplashPhotoData.photosByCollectionId; + + UnsplashService get service => GetIt.I.get(); + + List? getCollectionPhotos(String collectionId) => _idsByCollection[collectionId]; + +} diff --git a/lib/logic/unsplash_service.dart b/lib/logic/unsplash_service.dart new file mode 100644 index 00000000..db9e2bfe --- /dev/null +++ b/lib/logic/unsplash_service.dart @@ -0,0 +1,33 @@ +import 'package:unsplash_client/unsplash_client.dart'; +import 'package:wonders/logic/data/unsplash_photo_data.dart'; + +/// Note: This service is no-longer used in the production app, but exist to enable development tools like [UnsplashDownloadService] +String unsplashAccessKey = 'dxqHsX7IOURA5hfh0fuhL-cuX6q2-5DqghC77mnmrAU'; +String unsplashSecretKey = 'yTDPsxt6soBmcym7shd24t4vlYYDcOnzWyJ07O3UyEY'; + +class UnsplashService { + final client = UnsplashClient( + settings: ClientSettings( + credentials: AppCredentials( + accessKey: unsplashAccessKey, + secretKey: unsplashSecretKey, + ), + ), + ); + + Future?> loadCollectionPhotos(String id) async { + final photo = await client.collections.photos(id, page: 1, perPage: 25).go(); + final data = photo.data; + if (data == null) return null; + return data.map((e) => e.id).toList(); + } + + Future loadInfo(String id) async { + final photo = await client.photos.get(id).go(); + final data = photo.data; + if (data == null) { + throw ('Photo did not load. statusCode=${photo.statusCode}'); + } + return UnsplashPhotoData(id: id, url: '${data.urls.raw}'); + } +} diff --git a/lib/logic/wallpaper_logic.dart b/lib/logic/wallpaper_logic.dart new file mode 100644 index 00000000..fc9adfbc --- /dev/null +++ b/lib/logic/wallpaper_logic.dart @@ -0,0 +1,61 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:flutter/rendering.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/platform_info.dart'; +import 'package:wonders/ui/common/modals/app_modals.dart'; + +class WallPaperLogic { + /// Walks user through flow to save a Wonder Poster to their gallery + Future save(State state, RenderRepaintBoundary boundary, {required String name}) async { + // Time to create an image! + Uint8List? pngBytes = await _getPngFromBoundary(boundary); + final context = state.context, mounted = state.mounted; + if (pngBytes != null && mounted) { + bool? result = await showModal(context, + child: OkCancelModal( + msg: $strings.wallpaperModalSave, + )); + if (result == true && mounted) { + showModal(context, child: LoadingModal(msg: $strings.wallpaperModalSaving)); + if (PlatformInfo.isMobile) { + await ImageGallerySaver.saveImage(pngBytes, quality: 95, name: name); + } else { + await Future.delayed(500.ms); + } + if (state.mounted) { + Navigator.pop(context); + showModal(context, child: OkModal(msg: $strings.wallpaperModalSaveComplete)); + } + } + } + } + + Future share(BuildContext context, RenderRepaintBoundary boundary, + {required String name, String wonderName = 'Wonderous'}) async { + Uint8List? pngBytes = await _getPngFromBoundary(boundary); + if (pngBytes != null) { + final directory = (await getApplicationDocumentsDirectory()).path; + File imgFile = File('$directory/$name.png'); + await imgFile.writeAsBytes(pngBytes); + Share.shareFiles([imgFile.path], + mimeTypes: ['image/png'], + subject: '$wonderName Wallpaper', + text: 'Check out this $wonderName wallpaper from the Wonderous app!'); + } + } +} + +Future _getPngFromBoundary(RenderRepaintBoundary boundary) async { + ui.Image uiImage = await boundary.toImage(); + ByteData? byteData = await uiImage.toByteData(format: ui.ImageByteFormat.png); + if (byteData != null) { + return byteData.buffer.asUint8List(); + } + return null; +} diff --git a/lib/logic/wonders_logic.dart b/lib/logic/wonders_logic.dart new file mode 100644 index 00000000..34362286 --- /dev/null +++ b/lib/logic/wonders_logic.dart @@ -0,0 +1,32 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/chichen_itza_data.dart'; +import 'package:wonders/logic/data/wonders_data/christ_redeemer_data.dart'; +import 'package:wonders/logic/data/wonders_data/colosseum_data.dart'; +import 'package:wonders/logic/data/wonders_data/great_wall_data.dart'; +import 'package:wonders/logic/data/wonders_data/machu_picchu_data.dart'; +import 'package:wonders/logic/data/wonders_data/petra_data.dart'; +import 'package:wonders/logic/data/wonders_data/pyramids_giza_data.dart'; +import 'package:wonders/logic/data/wonders_data/taj_mahal_data.dart'; + +class WondersLogic { + late List all = [ + GreatWallData(), + PetraData(), + ColosseumData(), + ChichenItzaData(), + MachuPicchuData(), + TajMahalData(), + ChristRedeemerData(), + PyramidsGizaData(), + ]; + + final int timelineStartYear = -3000; + final int timelineEndYear = 2200; + + WonderData getData(WonderType value) { + WonderData? result = all.firstWhereOrNull((w) => w.type == value); + if (result == null) throw ('Could not find data for wonder type $value'); + return result; + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 00000000..fed31757 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,82 @@ +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_native_splash/flutter_native_splash.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/collectibles_logic.dart'; +import 'package:wonders/logic/locale_logic.dart'; +import 'package:wonders/logic/met_api_logic.dart'; +import 'package:wonders/logic/met_api_service.dart'; +import 'package:wonders/logic/timeline_logic.dart'; +import 'package:wonders/logic/unsplash_logic.dart'; +import 'package:wonders/logic/wallpaper_logic.dart'; +import 'package:wonders/logic/wonders_logic.dart'; + +void main() async { + WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + // Keep native splash screen up until app is finished bootstrapping + FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + + // Start app + registerSingletons(); + runApp(WondersApp()); + await appLogic.bootstrap(); + + // Remove splash screen when bootstrap is complete + FlutterNativeSplash.remove(); +} + +/// Creates an app using the [MaterialApp.router] constructor and the `appRouter`, an instance of [GoRouter]. +class WondersApp extends StatelessWidget { + const WondersApp({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + routerDelegate: appRouter.routerDelegate, + routeInformationProvider: appRouter.routeInformationProvider, + routeInformationParser: appRouter.routeInformationParser, + theme: ThemeData(fontFamily: $styles.text.body.fontFamily), + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: AppLocalizations.supportedLocales, + ); + } +} + +/// Create singletons (controllers and services) that can be shared across the app. +void registerSingletons() { + // Top level app controller + GetIt.I.registerLazySingleton(() => AppLogic()); + // Wonders + GetIt.I.registerLazySingleton(() => WondersLogic()); + // Timeline / Events + GetIt.I.registerLazySingleton(() => TimelineLogic()); + // Search + GetIt.I.registerLazySingleton(() => MetAPILogic()); + GetIt.I.registerLazySingleton(() => MetAPIService()); + // Settings + GetIt.I.registerLazySingleton(() => SettingsLogic()); + // Unsplash + GetIt.I.registerLazySingleton(() => UnsplashLogic()); + // Collectibles + GetIt.I.registerLazySingleton(() => CollectiblesLogic()); + // Localizations + GetIt.I.registerLazySingleton(() => LocaleLogic()); +} + +/// Add syntax sugar for quickly accessing the main logical controllers in the app +/// We deliberately do not create shortcuts for services, to discourage their use directly in the view/widget layer. +AppLogic get appLogic => GetIt.I.get(); +WondersLogic get wondersLogic => GetIt.I.get(); +TimelineLogic get timelineLogic => GetIt.I.get(); +SettingsLogic get settingsLogic => GetIt.I.get(); +UnsplashLogic get unsplashLogic => GetIt.I.get(); +MetAPILogic get metAPILogic => GetIt.I.get(); +CollectiblesLogic get collectiblesLogic => GetIt.I.get(); +WallPaperLogic get wallpaperLogic => GetIt.I.get(); +LocaleLogic get localeLogic => GetIt.I.get(); +AppLocalizations get $strings => localeLogic.strings; diff --git a/lib/router.dart b/lib/router.dart new file mode 100644 index 00000000..c8635614 --- /dev/null +++ b/lib/router.dart @@ -0,0 +1,109 @@ +import 'package:flutter/cupertino.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/app_scaffold.dart'; +import 'package:wonders/ui/common/modals//fullscreen_video_viewer.dart'; +import 'package:wonders/ui/common/modals/fullscreen_maps_viewer.dart'; +import 'package:wonders/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart'; +import 'package:wonders/ui/screens/artifact/artifact_details/artifact_details_screen.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/artifact_search_screen.dart'; +import 'package:wonders/ui/screens/collection/collection_screen.dart'; +import 'package:wonders/ui/screens/home/wonders_home_screen.dart'; +import 'package:wonders/ui/screens/intro/intro_screen.dart'; +import 'package:wonders/ui/screens/timeline/timeline_screen.dart'; +import 'package:wonders/ui/screens/wallpaper_photo/wallpaper_photo_screen.dart'; +import 'package:wonders/ui/screens/wonder_details/wonders_details_screen.dart'; + +/// Shared paths / urls used across the app +class ScreenPaths { + static String splash = '/'; + static String intro = '/welcome'; + static String home = '/home'; + static String settings = '/settings'; + static String wonderDetails(WonderType type) => '/wonder/${type.name}'; + static String video(String id) => '/video/$id'; + static String highlights(WonderType type) => '/highlights/${type.name}'; + static String search(WonderType type) => '/search/${type.name}'; + static String artifact(String id) => '/artifact/$id'; + static String collection(String id) => '/collection?id=$id'; + static String maps(WonderType type) => '/maps/${type.name}'; + static String timeline(WonderType? type) => '/timeline?type=${type?.name ?? ''}'; + static String wallpaperPhoto(WonderType type) => '/wallpaperPhoto/${type.name}'; +} + +/// Routing table, matches string paths to UI Screens +final appRouter = GoRouter( + redirect: _handleRedirect, + navigatorBuilder: (_, __, child) => WondersAppScaffold(child: child), + routes: [ + AppRoute(ScreenPaths.splash, (_) => Container(color: $styles.colors.greyStrong)), // This will be hidden + AppRoute(ScreenPaths.home, (_) => HomeScreen()), + AppRoute(ScreenPaths.intro, (_) => IntroScreen()), + AppRoute('/wonder/:type', (s) { + return WonderDetailsScreen(type: _parseWonderType(s.params['type']!)); + }, useFade: true), + AppRoute('/timeline', (s) { + return TimelineScreen(type: _tryParseWonderType(s.queryParams['type']!)); + }), + AppRoute('/video/:id', (s) { + return FullscreenVideoPage(id: s.params['id']!); + }), + AppRoute('/highlights/:type', (s) { + return ArtifactCarouselScreen(type: _parseWonderType(s.params['type']!)); + }), + AppRoute('/search/:type', (s) { + return ArtifactSearchScreen(type: _parseWonderType(s.params['type']!)); + }), + AppRoute('/artifact/:id', (s) { + return ArtifactDetailsScreen(artifactId: s.params['id']!); + }), + AppRoute('/collection', (s) { + return CollectionScreen(fromId: s.queryParams['id'] ?? ''); + }), + AppRoute('/maps/:type', (s) { + return FullscreenMapsViewer(type: _parseWonderType(s.params['type']!)); + }), + AppRoute('/wallpaperPhoto/:type', (s) { + return WallpaperPhotoScreen(type: _parseWonderType(s.params['type']!)); + }), + ], +); + +/// Custom GoRoute sub-class to make the router declaration easier to read +class AppRoute extends GoRoute { + AppRoute(String path, Widget Function(GoRouterState s) builder, + {List routes = const [], this.useFade = false}) + : super( + path: path, + routes: routes, + pageBuilder: (context, state) { + final pageContent = Scaffold( + body: builder(state), + resizeToAvoidBottomInset: false, + ); + if (useFade) { + return CustomTransitionPage( + key: state.pageKey, + child: pageContent, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return FadeTransition(opacity: animation, child: child); + }, + ); + } + return CupertinoPage(child: pageContent); + }, + ); + final bool useFade; +} + +String? _handleRedirect(GoRouterState state) { + // Prevent anyone from navigating away from `/` if app is starting up. + if (!appLogic.isBootstrapComplete && state.location != ScreenPaths.splash) { + return ScreenPaths.splash; + } + debugPrint('Navigate to: ${state.location}'); + return null; // do nothing +} + +WonderType _parseWonderType(String value) => _tryParseWonderType(value) ?? WonderType.chichenItza; + +WonderType? _tryParseWonderType(String value) => WonderType.values.asNameMap()[value]; diff --git a/lib/styles/colors.dart b/lib/styles/colors.dart new file mode 100644 index 00000000..989a1b5f --- /dev/null +++ b/lib/styles/colors.dart @@ -0,0 +1,52 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/color_utils.dart'; + +export 'wonders_color_extensions.dart'; + +class AppColors { + /// Common + final Color accent1 = Color(0xFFE4935D); + final Color accent2 = Color(0xFFBEABA1); + final Color offWhite = Color(0xFFF8ECE5); + final Color caption = const Color(0xFF7D7873); + final Color body = const Color(0xFF514F4D); + final Color greyStrong = const Color(0xFF272625); + final Color greyMedium = const Color(0xFF9D9995); + final Color white = Colors.white; + final Color black = const Color(0xFF1E1B18); + + final bool isDark = false; + + Color shift(Color c, double d) => ColorUtils.shiftHsl(c, d * (isDark ? -1 : 1)); + + ThemeData toThemeData() { + /// Create a TextTheme and ColorScheme, that we can use to generate ThemeData + TextTheme txtTheme = (isDark ? ThemeData.dark() : ThemeData.light()).textTheme; + Color txtColor = white; + ColorScheme colorScheme = ColorScheme( + // Decide how you want to apply your own custom them, to the MaterialApp + brightness: isDark ? Brightness.dark : Brightness.light, + primary: accent1, + primaryContainer: accent1, + secondary: accent1, + secondaryContainer: accent1, + background: offWhite, + surface: offWhite, + onBackground: txtColor, + onSurface: txtColor, + onError: Colors.white, + onPrimary: Colors.white, + onSecondary: Colors.white, + error: Colors.red.shade400); + + /// Now that we have ColorScheme and TextTheme, we can create the ThemeData + /// Also add on some extra properties that ColorScheme seems to miss + var t = ThemeData.from(textTheme: txtTheme, colorScheme: colorScheme).copyWith( + textSelectionTheme: TextSelectionThemeData(cursorColor: accent1), + highlightColor: accent1, + ); + + /// Return the themeData which MaterialApp can now use + return t; + } +} diff --git a/lib/styles/styles.dart b/lib/styles/styles.dart new file mode 100644 index 00000000..caf64d58 --- /dev/null +++ b/lib/styles/styles.dart @@ -0,0 +1,143 @@ +// ignore_for_file: library_private_types_in_public_api + +import 'dart:ui'; + +import 'package:wonders/common_libs.dart'; + +export 'colors.dart'; + +final $styles = AppStyle(); + +@immutable +class AppStyle { + /// The current theme colors for the app + final AppColors colors = AppColors(); + + /// Rounded edge corner radii + late final _Corners corners = _Corners(); + + late final _Shadows shadows = _Shadows(); + + /// Padding and margin values + late final _Insets insets = _Insets(); + + /// Text styles + late final _Text text = _Text(); + + /// Animation Durations + final _Times times = _Times(); +} + +@immutable +class _Text { + final Map _titleFonts = { + 'en': TextStyle(fontFamily: 'Tenor'), + }; + + final Map _monoTitleFonts = { + 'en': TextStyle(fontFamily: 'B612Mono'), + }; + + final Map _quoteFonts = { + 'en': TextStyle(fontFamily: 'Cinzel'), + 'zh': TextStyle(fontFamily: 'MaShanZheng'), + }; + + final Map _wonderTitleFonts = { + 'en': TextStyle(fontFamily: 'Yeseva'), + }; + + final Map _contentFonts = { + 'en': TextStyle(fontFamily: 'Raleway', fontFeatures: const [ + FontFeature.enable('dlig'), + FontFeature.enable('kern'), + ]), + }; + + TextStyle _getFontForLocale(Map fonts) { + if (localeLogic.isLoaded) { + return fonts.entries.firstWhere((x) => x.key == $strings.localeName, orElse: () => fonts.entries.first).value; + } else { + return fonts.entries.first.value; + } + } + + TextStyle get titleFont => _getFontForLocale(_titleFonts); + TextStyle get quoteFont => _getFontForLocale(_quoteFonts); + TextStyle get wonderTitleFont => _getFontForLocale(_wonderTitleFonts); + TextStyle get contentFont => _getFontForLocale(_contentFonts); + TextStyle get monoTitleFont => _getFontForLocale(_monoTitleFonts); + + late final TextStyle dropCase = copy(quoteFont, sizePx: 56, heightPx: 20); + + late final TextStyle wonderTitle = copy(wonderTitleFont, sizePx: 64, heightPx: 56); + + late final TextStyle h1 = copy(titleFont, sizePx: 64, heightPx: 62); + late final TextStyle h2 = copy(titleFont, sizePx: 32, heightPx: 46); + late final TextStyle h3 = copy(titleFont, sizePx: 24, heightPx: 36, weight: FontWeight.w600); + late final TextStyle h4 = copy(contentFont, sizePx: 14, heightPx: 23, spacingPc: 5, weight: FontWeight.w600); + + late final TextStyle title1 = copy(titleFont, sizePx: 16, heightPx: 26, spacingPc: 5); + late final TextStyle title2 = copy(titleFont, sizePx: 14, heightPx: 16.38); + + late final TextStyle body = copy(contentFont, sizePx: 16, heightPx: 27); + late final TextStyle bodyBold = copy(contentFont, sizePx: 16, heightPx: 26, weight: FontWeight.w600); + late final TextStyle bodySmall = copy(contentFont, sizePx: 14, heightPx: 23); + late final TextStyle bodySmallBold = copy(contentFont, sizePx: 14, heightPx: 23, weight: FontWeight.w600); + + late final TextStyle quote1 = copy(quoteFont, sizePx: 36, heightPx: 40, weight: FontWeight.w600, spacingPc: -3); + late final TextStyle quote2 = copy(quoteFont, sizePx: 21, heightPx: 32, weight: FontWeight.w400); + late final TextStyle quote2Sub = copy(body, sizePx: 16, heightPx: 40, weight: FontWeight.w400); + + late final TextStyle caption = + copy(contentFont, sizePx: 12, heightPx: 18, weight: FontWeight.w500).copyWith(fontStyle: FontStyle.italic); + + late final TextStyle callout = + copy(contentFont, sizePx: 16, heightPx: 26, weight: FontWeight.w600).copyWith(fontStyle: FontStyle.italic); + late final TextStyle btn = copy(titleFont, sizePx: 12, weight: FontWeight.w600, heightPx: 13.2); + + TextStyle copy(TextStyle style, {required double sizePx, double? heightPx, double? spacingPc, FontWeight? weight}) { + return style.copyWith( + fontSize: sizePx, + height: heightPx != null ? (heightPx / sizePx) : style.height, + letterSpacing: spacingPc != null ? sizePx * spacingPc * 0.01 : style.letterSpacing, + fontWeight: weight); + } +} + +@immutable +class _Times { + final Duration fast = Duration(milliseconds: 300); + final Duration med = Duration(milliseconds: 600); + final Duration slow = Duration(milliseconds: 900); + final Duration pageTransition = Duration(milliseconds: 200); +} + +@immutable +class _Corners { + late final double sm = 4; + late final double md = 8; + late final double lg = 32; +} + +@immutable +class _Insets { + late final double xxs = 4; + late final double xs = 8; + late final double sm = 16; + late final double md = 24; + late final double lg = 32; + late final double xl = 48; + late final double xxl = 56; + late final double offset = 80; +} + +@immutable +class _Shadows { + final text = [ + Shadow(color: Colors.black.withOpacity(.6), offset: Offset(0, 2), blurRadius: 2), + ]; + final textStrong = [ + Shadow(color: Colors.black.withOpacity(.6), offset: Offset(0, 4), blurRadius: 6), + ]; +} diff --git a/lib/styles/wonders_color_extensions.dart b/lib/styles/wonders_color_extensions.dart new file mode 100644 index 00000000..8db3a84e --- /dev/null +++ b/lib/styles/wonders_color_extensions.dart @@ -0,0 +1,45 @@ +import 'package:wonders/common_libs.dart'; + +extension WonderColorExtensions on WonderType { + Color get bgColor { + switch (this) { + case WonderType.pyramidsGiza: + return const Color(0xFF16184D); + case WonderType.greatWall: + return const Color(0xFF642828); + case WonderType.petra: + return const Color(0xFF444B9B); + case WonderType.colosseum: + return const Color(0xFF1E736D); + case WonderType.chichenItza: + return const Color(0xFF164F2A); + case WonderType.machuPicchu: + return const Color(0xFF0E4064); + case WonderType.tajMahal: + return const Color(0xFFC96454); + case WonderType.christRedeemer: + return const Color(0xFF1C4D46); + } + } + + Color get fgColor { + switch (this) { + case WonderType.pyramidsGiza: + return const Color(0xFF444B9B); + case WonderType.greatWall: + return const Color(0xFF688750); + case WonderType.petra: + return const Color(0xFF1B1A65); + case WonderType.colosseum: + return const Color(0xFF4AA39D); + case WonderType.chichenItza: + return const Color(0xFFE2CFBB); + case WonderType.machuPicchu: + return const Color(0xFFC1D9D1); + case WonderType.tajMahal: + return const Color(0xFF642828); + case WonderType.christRedeemer: + return const Color(0xFFED7967); + } + } +} diff --git a/lib/ui/app_scaffold.dart b/lib/ui/app_scaffold.dart new file mode 100644 index 00000000..9824c777 --- /dev/null +++ b/lib/ui/app_scaffold.dart @@ -0,0 +1,28 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/app_scroll_behavior.dart'; + +class WondersAppScaffold extends StatelessWidget with GetItMixin { + WondersAppScaffold({Key? key, required this.child}) : super(key: key); + final Widget child; + + @override + Widget build(BuildContext context) { + Animate.defaultDuration = $styles.times.fast; + return Stack( + children: [ + Theme( + data: $styles.colors.toThemeData(), + // Provide a default texts style to allow Hero's to render text properly + child: DefaultTextStyle( + style: $styles.text.body, + // Use a custom scroll behavior across entire app + child: ScrollConfiguration( + behavior: AppScrollBehavior(), + child: child, + ), + ), + ), + ], + ); + } +} diff --git a/lib/ui/common/app_backdrop.dart b/lib/ui/common/app_backdrop.dart new file mode 100644 index 00000000..9993fc56 --- /dev/null +++ b/lib/ui/common/app_backdrop.dart @@ -0,0 +1,35 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:wonders/common_libs.dart'; + +class AppBackdrop extends StatelessWidget { + const AppBackdrop({ + Key? key, + this.strength = 1, + this.child, + }) : super(key: key); + + final double strength; + final Widget? child; + + @override + Widget build(BuildContext context) { + final double normalStrength = clampDouble(strength, 0, 1); + if (settingsLogic.useBlurs) { + return BackdropFilter( + filter: ImageFilter.blur(sigmaX: normalStrength * 5.0, sigmaY: normalStrength * 5.0), + child: child ?? SizedBox.expand(), + ); + } + final fill = Container(color: $styles.colors.black.withOpacity(.8 * strength)); + return child == null + ? fill + : Stack( + children: [ + child!, + Positioned.fill(child: fill), + ], + ); + } +} diff --git a/lib/ui/common/app_icons.dart b/lib/ui/common/app_icons.dart new file mode 100644 index 00000000..666e9aa3 --- /dev/null +++ b/lib/ui/common/app_icons.dart @@ -0,0 +1,49 @@ +// ignore_for_file: constant_identifier_names + +import 'package:flutter/foundation.dart'; +import 'package:wonders/common_libs.dart'; + +class AppIcon extends StatelessWidget { + const AppIcon(this.icon, {Key? key, this.size = 22, this.color}) : super(key: key); + final AppIcons icon; + final double size; + final Color? color; + + @override + Widget build(BuildContext context) { + String i = describeEnum(icon).toLowerCase().replaceAll('_', '-'); + String path = 'assets/images/_common/icons/icon-$i.png'; + //print(path); + return SizedBox( + width: size, + height: size, + child: Center( + child: Image.asset(path, + width: size, height: size, color: color ?? $styles.colors.offWhite, filterQuality: FilterQuality.high), + ), + ); + } +} + +enum AppIcons { + close, + close_large, + collection, + download, + expand, + fullscreen, + fullscreen_exit, + info, + menu, + next_large, + north, + prev, + reset_location, + search, + share_android, + share_ios, + timeline, + wallpaper, + zoom_in, + zoom_out +} diff --git a/lib/ui/common/app_scroll_behavior.dart b/lib/ui/common/app_scroll_behavior.dart new file mode 100644 index 00000000..3e13dc40 --- /dev/null +++ b/lib/ui/common/app_scroll_behavior.dart @@ -0,0 +1,18 @@ +import 'package:flutter/gestures.dart'; +import 'package:wonders/common_libs.dart'; + +/// Add mouse drag on desktop for easier responsive testing +class AppScrollBehavior extends ScrollBehavior { + @override + Set get dragDevices { + return Set.from(super.dragDevices)..add(PointerDeviceKind.mouse); + } + + @override + ScrollPhysics getScrollPhysics(BuildContext context) => const BouncingScrollPhysics(); + + @override + Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) { + return RawScrollbar(controller: details.controller, child: child); + } +} diff --git a/lib/ui/common/blend_mask.dart b/lib/ui/common/blend_mask.dart new file mode 100644 index 00000000..bbfd5194 --- /dev/null +++ b/lib/ui/common/blend_mask.dart @@ -0,0 +1,42 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +class BlendMask extends SingleChildRenderObjectWidget { + final List blendModes; + final double opacity; + + const BlendMask({required this.blendModes, this.opacity = 1.0, Key? key, required Widget child}) + : super(key: key, child: child); + + @override + RenderObject createRenderObject(context) => RenderBlendMask(blendModes, opacity); + + @override + void updateRenderObject(BuildContext context, RenderBlendMask renderObject) { + renderObject.blendModes = blendModes; + renderObject.opacity = opacity; + } +} + +class RenderBlendMask extends RenderProxyBox { + List blendModes; + double opacity; + + RenderBlendMask(this.blendModes, this.opacity); + + @override + void paint(context, offset) { + // Complex blend modes can be raster cached incorrectly on the Skia backend. + context.setWillChangeHint(); + for (var blend in blendModes) { + context.canvas.saveLayer( + offset & size, + Paint() + ..blendMode = blend + ..color = Color.fromARGB((opacity * 255).round(), 255, 255, 255), + ); + } + super.paint(context, offset); + context.canvas.restore(); + } +} diff --git a/lib/ui/common/cards/opening_card.dart b/lib/ui/common/cards/opening_card.dart new file mode 100644 index 00000000..a2ac62b4 --- /dev/null +++ b/lib/ui/common/cards/opening_card.dart @@ -0,0 +1,65 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/measurable_widget.dart'; + +class OpeningCard extends StatefulWidget { + const OpeningCard( + {Key? key, + required this.closedBuilder, + required this.openBuilder, + required this.isOpen, + this.background, + this.padding}) + : super(key: key); + + final Widget Function(BuildContext) closedBuilder; + final Widget Function(BuildContext) openBuilder; + final Widget? background; + final bool isOpen; + final EdgeInsets? padding; + + @override + State createState() => _OpeningCardState(); +} + +class _OpeningCardState extends State { + Size _size = Size.zero; + @override + Widget build(BuildContext context) { + return TweenAnimationBuilder( + duration: $styles.times.fast, + curve: Curves.easeOut, + builder: (_, value, child) => Stack( + children: [ + if (widget.background != null) Positioned.fill(child: widget.background!), + Padding( + padding: widget.padding ?? EdgeInsets.zero, + child: SizedBox( + width: value.width, + height: value.height, + child: child, + ), + ), + ], + ), + tween: Tween(begin: _size, end: _size), + child: AnimatedSwitcher( + duration: $styles.times.fast, + child: ClipRect( + key: ValueKey(widget.isOpen), + child: OverflowBox( + minWidth: 0.0, + maxWidth: double.infinity, + minHeight: 0.0, + maxHeight: double.infinity, + child: MeasurableWidget( + onChange: (size) { + setState(() => _size = size); + }, + child: widget.isOpen ? widget.openBuilder(context) : widget.closedBuilder(context), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/common/collectible_item.dart b/lib/ui/common/collectible_item.dart new file mode 100644 index 00000000..25f45bbf --- /dev/null +++ b/lib/ui/common/collectible_item.dart @@ -0,0 +1,66 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/collectibles_logic.dart'; +import 'package:wonders/logic/data/collectible_data.dart'; +import 'package:wonders/ui/common/cards/opening_card.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; +import 'package:wonders/ui/screens/collectible_found/collectible_found_screen.dart'; + +class CollectibleItem extends StatelessWidget with GetItMixin { + CollectibleItem(this.collectible, {this.size = 64.0, Key? key}) : super(key: key) { + // pre-fetch the image, so it's ready if we show the collectible found screen. + _imageProvider = NetworkImage(collectible.imageUrlSmall); + _imageProvider.resolve(ImageConfiguration()).addListener(ImageStreamListener((_, __) {})); + } + + final CollectibleData collectible; + final double size; + late final ImageProvider _imageProvider; + + void _handleTap(BuildContext context) async { + final screen = CollectibleFoundScreen(collectible: collectible, imageProvider: _imageProvider); + appLogic.showFullscreenDialogRoute(context, screen); + AppHaptics.mediumImpact(); + + // wait to update the state, to ensure the hero works properly: + await Future.delayed($styles.times.pageTransition); + collectiblesLogic.updateState(collectible.id, CollectibleState.discovered); + } + + @override + Widget build(BuildContext context) { + final states = watchX((CollectiblesLogic c) => c.statesById); + bool isLost = states[collectible.id] == CollectibleState.lost; + // Use an OpeningCard to let the collectible smoothly collapse its size once it has been found + return SizedBox( + height: isLost ? size : null, + child: RepaintBoundary( + child: OpeningCard( + isOpen: isLost, + // Note: In order for the collapse animation to run properly, we must return a non-zero height or width. + closedBuilder: (_) => SizedBox(width: 1, height: 0), + openBuilder: (_) => AppBtn.basic( + semanticLabel: $strings.collectibleItemSemanticCollectible, + onPressed: () => _handleTap(context), + enableFeedback: false, + child: Hero( + tag: 'collectible_icon_${collectible.id}', + child: Image( + image: collectible.icon, + width: size, + height: size, + fit: BoxFit.contain, + ), + ) + .animate(onPlay: (controller) => controller.repeat()) + // TODO SB (Aug 17, 2022): Temporarily removed on Jonahs request, due to a bug in Impeller which should be fixed soon. Re-enable when fixed. + //.shimmer(delay: 4000.ms, duration: $styles.times.med * 3) + .shake(delay: 4000.ms, duration: $styles.times.med * 3, curve: Curves.easeInOutCubic, hz: 4) + .scale(begin: 1.0, end: 1.1, duration: $styles.times.med) + .then(delay: $styles.times.med) + .scale(begin: 1.0, end: 1 / 1.1), + ), + ), + ), + ); + } +} diff --git a/lib/ui/common/compass_divider.dart b/lib/ui/common/compass_divider.dart new file mode 100644 index 00000000..ed2442b7 --- /dev/null +++ b/lib/ui/common/compass_divider.dart @@ -0,0 +1,56 @@ +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:wonders/common_libs.dart'; + +class CompassDivider extends StatelessWidget { + const CompassDivider({Key? key, required this.isExpanded, this.duration, this.linesColor, this.compassColor}) + : super(key: key); + final bool isExpanded; + final Duration? duration; + final Color? linesColor; + final Color? compassColor; + + @override + Widget build(BuildContext context) { + Duration duration = this.duration ?? 1500.ms; + Widget buildHzAnimatedDivider({bool alignLeft = false}) { + return TweenAnimationBuilder( + duration: duration, + tween: Tween(begin: 0, end: isExpanded ? 1 : 0), + curve: Curves.easeOut, + child: Divider(height: 1, thickness: .5, color: linesColor ?? $styles.colors.accent2), + builder: (_, value, child) { + return Transform.scale( + scaleX: value, + alignment: alignLeft ? Alignment.centerLeft : Alignment.centerRight, + child: child!, + ); + }, + ); + } + + return Row( + children: [ + Expanded(child: buildHzAnimatedDivider()), + Gap($styles.insets.sm), + TweenAnimationBuilder( + duration: duration, + tween: Tween(begin: 0, end: isExpanded ? .5 : 0), + curve: Curves.easeOutBack, + builder: (_, value, child) => Transform.rotate( + angle: value * pi * 2, + child: child, + ), + child: SizedBox( + height: 32, + width: 32, + child: SvgPicture.asset( + SvgPaths.compassFull, + color: compassColor ?? $styles.colors.accent2, + )), + ), + Gap($styles.insets.sm), + Expanded(child: buildHzAnimatedDivider(alignLeft: true)), + ], + ); + } +} diff --git a/lib/ui/common/controls/app_image.dart b/lib/ui/common/controls/app_image.dart new file mode 100644 index 00000000..300b7dd9 --- /dev/null +++ b/lib/ui/common/controls/app_image.dart @@ -0,0 +1,94 @@ +import 'package:image_fade/image_fade.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/retry_image.dart'; +import 'package:wonders/ui/common/controls/app_loading_indicator.dart'; + +class AppImage extends StatefulWidget { + const AppImage({ + Key? key, + required this.image, + this.fit = BoxFit.scaleDown, + this.alignment = Alignment.center, + this.duration, + this.syncDuration, + this.distractor = false, + this.progress = false, + this.color, + this.scale, + }) : super(key: key); + + final ImageProvider? image; + final BoxFit fit; + final Alignment alignment; + final Duration? duration; + final Duration? syncDuration; + final bool distractor; + final bool progress; + final Color? color; + final double? scale; + + @override + State createState() => _AppImageState(); +} + +class _AppImageState extends State { + ImageProvider? _displayImage; + ImageProvider? _sourceImage; + + @override + void didChangeDependencies() { + _updateImage(); + super.didChangeDependencies(); + } + + @override + void didUpdateWidget(AppImage oldWidget) { + _updateImage(); + super.didUpdateWidget(oldWidget); + } + + void _updateImage() { + if (widget.image == _sourceImage) return; + _sourceImage = widget.image; + _displayImage = _capImageSize(_addRetry(_sourceImage)); + } + + @override + Widget build(BuildContext context) { + return ImageFade( + image: _displayImage, + fit: widget.fit, + alignment: widget.alignment, + duration: widget.duration ?? $styles.times.fast, + syncDuration: widget.syncDuration ?? 0.ms, + loadingBuilder: (_, value, ___) { + if (!widget.distractor && !widget.progress) return SizedBox(); + return Center(child: AppLoadingIndicator(value: widget.progress ? value : null, color: widget.color)); + }, + errorBuilder: (_, __) => Container( + padding: EdgeInsets.all($styles.insets.xs), + alignment: Alignment.center, + child: LayoutBuilder(builder: (_, constraints) { + double size = min(constraints.biggest.width, constraints.biggest.height); + if (size < 16) return SizedBox(); + return Icon( + Icons.image_not_supported_outlined, + color: $styles.colors.white.withOpacity(0.1), + size: min(size, $styles.insets.lg), + ); + }), + ), + ); + } + + ImageProvider? _addRetry(ImageProvider? image) { + return image == null ? image : RetryImage(image); + } + + ImageProvider? _capImageSize(ImageProvider? image) { + if (image == null || widget.scale == null) return image; + final MediaQueryData mq = MediaQuery.of(context); + final Size screenSize = mq.size * mq.devicePixelRatio * widget.scale!; + return ResizeImage(image, width: screenSize.width.round()); + } +} diff --git a/lib/ui/common/controls/app_loading_indicator.dart b/lib/ui/common/controls/app_loading_indicator.dart new file mode 100644 index 00000000..eb00cca7 --- /dev/null +++ b/lib/ui/common/controls/app_loading_indicator.dart @@ -0,0 +1,20 @@ +import 'package:wonders/common_libs.dart'; + +class AppLoadingIndicator extends StatelessWidget { + const AppLoadingIndicator({Key? key, this.value, this.color}) : super(key: key); + final Color? color; + final double? value; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator( + color: color ?? $styles.colors.accent1, + value: value, + strokeWidth: 1.0, + ), + ); + } +} diff --git a/lib/ui/common/controls/app_page_indicator.dart b/lib/ui/common/controls/app_page_indicator.dart new file mode 100644 index 00000000..ba5a19f2 --- /dev/null +++ b/lib/ui/common/controls/app_page_indicator.dart @@ -0,0 +1,82 @@ +import 'package:smooth_page_indicator/smooth_page_indicator.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; + +class AppPageIndicator extends StatefulWidget { + AppPageIndicator( + {Key? key, + required this.count, + required this.controller, + this.onDotPressed, + this.color, + this.dotSize, + String? semanticPageTitle}) + : semanticPageTitle = semanticPageTitle ?? $strings.appPageDefaultTitlePage, + super(key: key); + final int count; + final PageController controller; + final void Function(int index)? onDotPressed; + final Color? color; + final double? dotSize; + final String semanticPageTitle; + + @override + State createState() => _AppPageIndicatorState(); +} + +class _AppPageIndicatorState extends State { + final _currentPage = ValueNotifier(0); + + @override + void initState() { + super.initState(); + widget.controller.addListener(_handlePageChanged); + } + + int get _controllerPage => widget.controller.page?.round() ?? 0; + + void _handlePageChanged() => _currentPage.value = _controllerPage; + + @override + Widget build(BuildContext context) { + return Stack(children: [ + Container( + color: Colors.transparent, + height: 30, + alignment: Alignment.center, + child: ValueListenableBuilder( + valueListenable: _currentPage, + builder: (_, value, child) { + return Semantics( + container: true, + liveRegion: true, + label: StringUtils.supplant($strings.appPageSemanticSwipe, { + '{pageTitle}': widget.semanticPageTitle, + '{count}': (value % (widget.count) + 1).toString(), + '{total}': widget.count.toString(), + }), + child: Container()); + }), + ), + Positioned.fill( + child: Center( + child: ExcludeSemantics( + child: SmoothPageIndicator( + controller: widget.controller, + count: widget.count, + onDotClicked: widget.onDotPressed, + effect: ExpandingDotsEffect( + dotWidth: widget.dotSize ?? 6, + dotHeight: widget.dotSize ?? 6, + paintStyle: PaintingStyle.fill, + strokeWidth: (widget.dotSize ?? 6) / 2, + dotColor: widget.color ?? $styles.colors.accent1, + activeDotColor: widget.color ?? $styles.colors.accent1, + expansionFactor: 2), + ), + ), + ), + ), + ]); + } +} diff --git a/lib/ui/common/controls/buttons.dart b/lib/ui/common/controls/buttons.dart new file mode 100644 index 00000000..2cfd61eb --- /dev/null +++ b/lib/ui/common/controls/buttons.dart @@ -0,0 +1,220 @@ +import 'package:wonders/common_libs.dart'; + +/// Shared methods across button types +Widget _buildIcon(BuildContext context, IconData icon, {required bool isSecondary, required double? size}) => + Icon(icon, color: isSecondary ? $styles.colors.black : $styles.colors.offWhite, size: size ?? 18); + +/// The core button that drives all other buttons. +class AppBtn extends StatelessWidget { + // ignore: prefer_const_constructors_in_immutables + AppBtn({ + Key? key, + required this.onPressed, + required this.semanticLabel, + this.enableFeedback = true, + this.pressEffect = true, + this.child, + this.padding, + this.expand = false, + this.isSecondary = false, + this.circular = false, + this.minimumSize, + this.bgColor, + this.border, + }) : _builder = null, + super(key: key); + + AppBtn.from({ + Key? key, + required this.onPressed, + this.enableFeedback = true, + this.pressEffect = true, + this.padding, + this.expand = false, + this.isSecondary = false, + this.minimumSize, + this.bgColor, + this.border, + String? semanticLabel, + String? text, + IconData? icon, + double? iconSize, + }) : child = null, + circular = false, + super(key: key) { + if (semanticLabel == null && text == null) throw ('AppBtn.from must include either text or semanticLabel'); + this.semanticLabel = semanticLabel ?? text ?? ''; + _builder = (context) { + if (text == null && icon == null) return SizedBox.shrink(); + Text? txt = text == null + ? null + : Text(text.toUpperCase(), + style: $styles.text.btn, textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false)); + Widget? icn = icon == null ? null : _buildIcon(context, icon, isSecondary: isSecondary, size: iconSize); + if (txt != null && icn != null) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [txt, Gap($styles.insets.xs), icn], + ); + } else { + return (txt ?? icn)!; + } + }; + } + + // ignore: prefer_const_constructors_in_immutables + AppBtn.basic({ + Key? key, + required this.onPressed, + required this.semanticLabel, + this.enableFeedback = true, + this.pressEffect = true, + this.child, + this.padding = EdgeInsets.zero, + this.isSecondary = false, + this.circular = false, + this.minimumSize, + }) : expand = false, + bgColor = Colors.transparent, + border = null, + _builder = null, + super(key: key); + + // interaction: + final VoidCallback onPressed; + late final String semanticLabel; + final bool enableFeedback; + + // content: + late final Widget? child; + late final WidgetBuilder? _builder; + + // layout: + final EdgeInsets? padding; + final bool expand; + final bool circular; + final Size? minimumSize; + + // style: + final bool isSecondary; + final BorderSide? border; + final Color? bgColor; + final bool pressEffect; + + @override + Widget build(BuildContext context) { + Color defaultColor = isSecondary ? $styles.colors.white : $styles.colors.greyStrong; + Color textColor = isSecondary ? $styles.colors.black : $styles.colors.white; + BorderSide side = border ?? BorderSide.none; + + Widget content = _builder?.call(context) ?? child ?? SizedBox.shrink(); + if (expand) content = Center(child: content); + + OutlinedBorder shape = circular + ? CircleBorder(side: side) + : RoundedRectangleBorder(side: side, borderRadius: BorderRadius.circular($styles.corners.md)); + + ButtonStyle style = ButtonStyle( + minimumSize: ButtonStyleButton.allOrNull(minimumSize ?? Size.zero), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + splashFactory: NoSplash.splashFactory, + backgroundColor: ButtonStyleButton.allOrNull(bgColor ?? defaultColor), + overlayColor: ButtonStyleButton.allOrNull(Colors.transparent), // disable default press effect + shape: ButtonStyleButton.allOrNull(shape), + padding: ButtonStyleButton.allOrNull(padding ?? EdgeInsets.all($styles.insets.md)), + enableFeedback: enableFeedback, + ); + + Widget button = TextButton( + onPressed: () => onPressed(), + style: style, + child: DefaultTextStyle( + style: DefaultTextStyle.of(context).style.copyWith(color: textColor), + child: content, + ), + ); + + // add press effect: + if (pressEffect) button = _ButtonPressEffect(button); + + // add semantics: + if (semanticLabel.isEmpty) { + button = ExcludeSemantics(child: button); + } else { + button = Semantics( + label: semanticLabel, + button: true, + container: true, + child: button, + ); + } + + return button; + } +} + +/// ////////////////////////////////////////////////// +/// _ButtonDecorator +/// Add a transparency-based press effect to buttons. +/// ////////////////////////////////////////////////// +class _ButtonPressEffect extends StatefulWidget { + const _ButtonPressEffect(this.child, {Key? key}) : super(key: key); + final Widget child; + + @override + State<_ButtonPressEffect> createState() => _ButtonPressEffectState(); +} + +class _ButtonPressEffectState extends State<_ButtonPressEffect> { + bool _isDown = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + excludeFromSemantics: true, + onTapDown: (_) => setState(() => _isDown = true), + onTapUp: (_) => setState(() => _isDown = false), // not called, TextButton swallows this. + onTapCancel: () => setState(() => _isDown = false), + behavior: HitTestBehavior.translucent, + child: Opacity( + opacity: _isDown ? 0.7 : 1, + child: ExcludeSemantics(child: widget.child), + ), + ); + } +} + +// TODO: this is currently unused, and can probably be removed: +// This is a very simple button for elements that don't require button UI (states, focus, etc) +// For example panel backgrounds. +class BasicBtn extends StatelessWidget { + const BasicBtn({ + required this.onPressed, + required this.semanticLabel, + this.behavior = HitTestBehavior.opaque, + this.enableFeedback = true, + this.child, + Key? key, + }) : super(key: key); + + final VoidCallback onPressed; + final String? semanticLabel; + final HitTestBehavior behavior; + final bool enableFeedback; + final Widget? child; + + @override + Widget build(BuildContext context) { + Widget button = GestureDetector( + excludeFromSemantics: true, + onTap: enableFeedback ? Feedback.wrapForTap(onPressed, context) : onPressed, + behavior: behavior, + child: child, + ); + + if (semanticLabel != null) button = Semantics(label: semanticLabel, button: true, container: true, child: button); + + return button; + } +} diff --git a/lib/ui/common/controls/checkbox.dart b/lib/ui/common/controls/checkbox.dart new file mode 100644 index 00000000..455cd9b2 --- /dev/null +++ b/lib/ui/common/controls/checkbox.dart @@ -0,0 +1,38 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; + +class SimpleCheckbox extends StatelessWidget { + const SimpleCheckbox({Key? key, required this.active, required this.onToggled, required this.label}) + : super(key: key); + final bool active; + final String label; + final Function(bool? onToggle) onToggled; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular($styles.corners.sm)), + ), + child: Checkbox( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular($styles.corners.sm))), + value: active, + visualDensity: VisualDensity(horizontal: 0.5, vertical: 0.5), + checkColor: $styles.colors.black.withOpacity(0.75), + activeColor: $styles.colors.white.withOpacity(0.75), + onChanged: (bool? active) { + AppHaptics.mediumImpact(); + onToggled.call(active); + }), + ), + Gap($styles.insets.xs), + Text(label, style: $styles.text.body.copyWith(color: $styles.colors.offWhite)), + ], + ); + } +} diff --git a/lib/ui/common/controls/circle_buttons.dart b/lib/ui/common/controls/circle_buttons.dart new file mode 100644 index 00000000..3bddf06d --- /dev/null +++ b/lib/ui/common/controls/circle_buttons.dart @@ -0,0 +1,135 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/app_icons.dart'; + +class CircleBtn extends StatelessWidget { + const CircleBtn({ + Key? key, + required this.child, + required this.onPressed, + this.border, + this.bgColor, + this.size, + required this.semanticLabel, + }) : super(key: key); + + static double defaultSize = 48; + + final VoidCallback onPressed; + final Color? bgColor; + final BorderSide? border; + final Widget child; + final double? size; + final String semanticLabel; + + @override + Widget build(BuildContext context) { + double sz = size ?? defaultSize; + return AppBtn( + onPressed: onPressed, + semanticLabel: semanticLabel, + minimumSize: Size(sz, sz), + padding: EdgeInsets.zero, + circular: true, + bgColor: bgColor, + border: border, + child: child, + ); + } +} + +class CircleIconBtn extends StatelessWidget { + const CircleIconBtn({ + Key? key, + required this.icon, + required this.onPressed, + this.border, + this.bgColor, + this.color, + this.size, + this.iconSize, + required this.semanticLabel, + }) : super(key: key); + + //TODO: Reduce size if design re-exports icon-images without padding + static double defaultSize = 28; + + final AppIcons icon; + final VoidCallback onPressed; + final BorderSide? border; + final Color? bgColor; + final Color? color; + final String semanticLabel; + final double? size; + final double? iconSize; + + @override + Widget build(BuildContext context) { + Color defaultColor = $styles.colors.greyStrong; + Color iconColor = color ?? $styles.colors.offWhite; + return CircleBtn( + onPressed: onPressed, + border: border, + size: size, + bgColor: bgColor ?? defaultColor, + semanticLabel: semanticLabel, + child: AppIcon(icon, size: iconSize ?? defaultSize, color: iconColor), + ); + } + + Widget safe() => _SafeAreaWithPadding(child: this); +} + +class BackBtn extends StatelessWidget { + const BackBtn({ + Key? key, + this.icon = AppIcons.prev, + this.onPressed, + this.semanticLabel, + this.bgColor, + this.iconColor, + }) : super(key: key); + + final Color? bgColor; + final Color? iconColor; + final AppIcons icon; + final VoidCallback? onPressed; + final String? semanticLabel; + + BackBtn.close({Key? key, VoidCallback? onPressed, Color? bgColor, Color? iconColor}) + : this( + key: key, + icon: AppIcons.close, + onPressed: onPressed, + semanticLabel: $strings.circleButtonsSemanticClose, + bgColor: bgColor, + iconColor: iconColor); + @override + Widget build(BuildContext context) { + return CircleIconBtn( + icon: icon, + bgColor: bgColor, + color: iconColor, + onPressed: onPressed ?? () => Navigator.pop(context), + semanticLabel: semanticLabel ?? $strings.circleButtonsSemanticBack, + ); + } + + Widget safe() => _SafeAreaWithPadding(child: this); +} + +class _SafeAreaWithPadding extends StatelessWidget { + const _SafeAreaWithPadding({Key? key, required this.child}) : super(key: key); + + final Widget child; + + @override + Widget build(BuildContext context) { + return SafeArea( + bottom: false, + child: Padding( + padding: EdgeInsets.all($styles.insets.sm), + child: child, + ), + ); + } +} diff --git a/lib/ui/common/controls/diagonal_text_page_indicator.dart b/lib/ui/common/controls/diagonal_text_page_indicator.dart new file mode 100644 index 00000000..f7fa0ee3 --- /dev/null +++ b/lib/ui/common/controls/diagonal_text_page_indicator.dart @@ -0,0 +1,83 @@ +import 'package:wonders/common_libs.dart'; + +class DiagonalTextPageIndicator extends StatelessWidget { + const DiagonalTextPageIndicator({Key? key, required this.current, required this.total}) : super(key: key); + final int current; + final int total; + static const double _fontSize = 32; + + @override + Widget build(BuildContext context) { + //final textShadows = [Shadow(color: Colors.black.withOpacity(.5), offset: Offset(0, 4), blurRadius: 6)]; + final textStyle = $styles.text.titleFont.copyWith(fontSize: _fontSize, height: 1); + const size = _fontSize * 1.5; + return Padding( + padding: EdgeInsets.symmetric(horizontal: textStyle.fontSize! * .4).copyWith(top: textStyle.fontSize! * .2), + child: Stack(children: [ + ClipPath( + clipper: _DiagonalClipper(leftSide: true), + child: Transform.translate( + offset: Offset(-_fontSize * .7, 0), + child: SizedBox( + width: size, + height: size, + child: Text('0$current', + style: textStyle.copyWith(shadows: $styles.shadows.text), textAlign: TextAlign.right)), + ), + ), + ClipPath( + clipper: _DiagonalClipper(leftSide: false), + child: Transform.translate( + offset: Offset(_fontSize * .45, _fontSize * .6), + child: SizedBox( + width: size, + height: size, + child: Opacity( + opacity: .5, + child: Text( + '0$total', + style: textStyle.copyWith(shadows: $styles.shadows.textStrong), + ), + ), + ), + ), + ), + Positioned.fill( + child: Center( + child: Transform.rotate( + angle: pi * -.25, + child: Container(height: 2, color: Colors.white), + ), + ), + ), + ]), + ); + } +} + +class _DiagonalClipper extends CustomClipper { + _DiagonalClipper({required this.leftSide}); + final bool leftSide; + + @override + Path getClip(Size size) { + const double lineGap = 2; + if (leftSide) { + return Path() + ..lineTo(size.width - lineGap, 0) + ..lineTo(-lineGap, size.height) + ..lineTo(-lineGap, 0) + ..addRect(Rect.fromLTRB(-size.width * .5, 0, 0, size.height)) + ..addRect(Rect.fromLTRB(-size.width * .5, -size.height * .5, size.width, 0)); + } + return Path() + ..moveTo(size.width + lineGap, 0) + ..lineTo(size.width, size.height + lineGap) + ..lineTo(lineGap, size.height + lineGap) + ..lineTo(size.width + lineGap, 0) + ..addRect(Rect.fromLTRB(size.width, 0, size.width * 1.5, size.height * 1.5)); + } + + @override + bool shouldReclip(covariant Object oldClipper) => true; +} diff --git a/lib/ui/common/controls/eight_way_swipe_detector.dart b/lib/ui/common/controls/eight_way_swipe_detector.dart new file mode 100644 index 00000000..e831cdd2 --- /dev/null +++ b/lib/ui/common/controls/eight_way_swipe_detector.dart @@ -0,0 +1,69 @@ +import 'package:wonders/common_libs.dart'; + +class EightWaySwipeDetector extends StatefulWidget { + const EightWaySwipeDetector({Key? key, required this.child, this.threshold = 50, required this.onSwipe}) + : super(key: key); + final Widget child; + final double threshold; + final void Function(Offset dir)? onSwipe; + + @override + State createState() => _EightWaySwipeDetectorState(); +} + +class _EightWaySwipeDetectorState extends State { + Offset _startPos = Offset.zero; + Offset _endPos = Offset.zero; + bool _isSwiping = false; + + void _resetSwipe() { + _startPos = _endPos = Offset.zero; + _isSwiping = false; + } + + void _maybeTriggerSwipe() { + // Exit early if we're not currently swiping + if (_isSwiping == false) return; + // Get the distance of the swipe + Offset moveDelta = _endPos - _startPos; + final distance = moveDelta.distance; + // Trigger swipe if threshold has been exceeded, if threshold is < 1, use 1 as a minimum value. + if (distance >= max(widget.threshold, 1)) { + // Normalize the dx/dy values between -1 and 1 + moveDelta /= distance; + // Round the dx/dy values to snap them to -1, 0 or 1, creating an 8-way directional vector. + Offset dir = Offset( + moveDelta.dx.roundToDouble(), + moveDelta.dy.roundToDouble(), + ); + widget.onSwipe?.call(dir); + _resetSwipe(); + } + } + + void _handleSwipeStart(d) { + _isSwiping = true; + _startPos = _endPos = d.localPosition; + } + + void _handleSwipeUpdate(d) { + _endPos = d.localPosition; + _maybeTriggerSwipe(); + } + + void _handleSwipeEnd(d) { + _maybeTriggerSwipe(); + _resetSwipe(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onPanStart: _handleSwipeStart, + onPanUpdate: _handleSwipeUpdate, + onPanCancel: _resetSwipe, + onPanEnd: _handleSwipeEnd, + child: widget.child); + } +} diff --git a/lib/ui/common/controls/scroll_decorator.dart b/lib/ui/common/controls/scroll_decorator.dart new file mode 100644 index 00000000..3f97f116 --- /dev/null +++ b/lib/ui/common/controls/scroll_decorator.dart @@ -0,0 +1,133 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +/// Easily add visual decorations to a scrolling widget based on the state of its controller. +class ScrollDecorator extends StatefulWidget { + + /// Creates a widget that builds foreground and/or background decorations for a scrolling widget based on the state of + /// its ScrollController. + // ignore: prefer_const_constructors_in_immutables + ScrollDecorator({Key? key, required this.builder, this.foregroundBuilder, this.backgroundBuilder, this.controller}) + : super(key: key); + + /// Creates a ScrollDecorator that fades a widget in at the begin or end of the scrolling widget based on the scroll + /// position. For example on a vertical list, it would fade in the `begin` widget when the list is not scrolled to the + /// top. + ScrollDecorator.fade({ + Key? key, + required this.builder, + this.controller, + Widget? begin, + Widget? end, + bool background = false, + Axis direction = Axis.vertical, + Duration duration = const Duration(milliseconds: 150), + }) : super(key: key) { + Flex flexBuilder(controller) { + return Flex( + direction: direction, + children: [ + if (begin != null) + AnimatedOpacity( + duration: duration, + opacity: controller.hasClients && controller.position.extentBefore > 3 ? 1 : 0, + child: begin, + ), + Spacer(), + if (end != null) + AnimatedOpacity( + duration: duration, + opacity: controller.hasClients && controller.position.extentAfter > 3 ? 1 : 0, + child: end, + ), + ], + ); + } + + backgroundBuilder = background ? flexBuilder : null; + foregroundBuilder = !background ? flexBuilder : null; + } + + /// Creates an ScrollDecorator that adds a shadow to the top of a vertical list when it is scrolled down. + ScrollDecorator.shadow({ + Key? key, + required this.builder, + this.controller, + Color color = Colors.black54, + }) : super(key: key) { + backgroundBuilder = null; + foregroundBuilder = (controller) { + final double ratio = controller.hasClients ? min(1, controller.position.extentBefore / 60) : 0; + return IgnorePointer( + child: Container( + height: 24, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [color.withOpacity(ratio * color.opacity), Colors.transparent], + stops: [0, ratio], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ); + }; + } + + /// The ScrollController to use when building the scrolling widget. If null, a ScrollController will be created + /// automatically. + final ScrollController? controller; + + /// Builder to create the scrolling widget. It should use the ScrollController that is provided to the builder. + final ScrollBuilder builder; + + /// Builder to create the decoration widget that will be layered in front of the scrolling widget. It should use the + /// provided ScrollController to adjust its output as appropriate. + late final ScrollBuilder? foregroundBuilder; + + /// Builder to create the decoration widget that will be layered behind the scrolling widget. It should use the + /// provided ScrollController to adjust its output as appropriate. + late final ScrollBuilder? backgroundBuilder; + + @override + State createState() => _ScrollDecoratorState(); +} + +class _ScrollDecoratorState extends State { + ScrollController? _controller; + late Widget content; + + @override + void initState() { + if (widget.controller == null) _controller = ScrollController(); + super.initState(); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + ScrollController controller = (widget.controller ?? _controller)!; + + content = widget.builder(controller); + return AnimatedBuilder( + animation: controller, + builder: (_, __) { + return Stack( + children: [ + if (widget.backgroundBuilder != null) widget.backgroundBuilder!(controller), + content, + if (widget.foregroundBuilder != null) widget.foregroundBuilder!(controller), + ], + ); + }); + } +} + +/// Builder function type for use with ScrollDecorator. +typedef ScrollBuilder = Widget Function(ScrollController controller); diff --git a/lib/ui/common/controls/simple_header.dart b/lib/ui/common/controls/simple_header.dart new file mode 100644 index 00000000..50b83fb7 --- /dev/null +++ b/lib/ui/common/controls/simple_header.dart @@ -0,0 +1,52 @@ +import 'package:wonders/common_libs.dart'; + +class SimpleHeader extends StatelessWidget { + const SimpleHeader(this.title, + {Key? key, this.subtitle, this.child, this.showBackBtn = true, this.isTransparent = false, this.onBack}) + : super(key: key); + final String title; + final String? subtitle; + final Widget? child; + final bool showBackBtn; + final bool isTransparent; + final VoidCallback? onBack; + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: isTransparent ? Colors.transparent : $styles.colors.black, + child: SafeArea( + bottom: false, + child: Row(children: [ + if (showBackBtn) BackBtn(onPressed: onBack).safe(), + Flexible( + fit: FlexFit.tight, + child: MergeSemantics( + child: Semantics( + header: true, + child: Column( + children: [ + if (!showBackBtn) Gap($styles.insets.xs), + Text( + title.toUpperCase(), + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: $styles.text.h4.copyWith(color: $styles.colors.offWhite), + ), + if (subtitle != null) + Text( + subtitle!.toUpperCase(), + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: $styles.text.title1.copyWith(color: $styles.colors.accent1), + ), + if (!showBackBtn) Gap($styles.insets.md), + ], + ), + ), + ), + ), + if (showBackBtn) Container(width: $styles.insets.lg * 2, alignment: Alignment.centerLeft, child: child), + ]), + ), + ); + } +} diff --git a/lib/ui/common/curved_clippers.dart b/lib/ui/common/curved_clippers.dart new file mode 100644 index 00000000..cd2d2921 --- /dev/null +++ b/lib/ui/common/curved_clippers.dart @@ -0,0 +1,123 @@ +import 'package:wonders/common_libs.dart'; + +enum ArchType { spade, pyramid, arch, wideArch, flatPyramid } + +class ArchClipper extends CustomClipper { + ArchClipper(this.type); + final ArchType type; + + @override + Path getClip(Size size) { + final pts = _getArchPts(size, type); + // Points start at bottom left corner, and go clock-wise + var clip = Path()..moveTo(pts[0].pos.dx, pts[0].pos.dy); + for (var i = 1; i < pts.length; i++) { + final p = pts[i]; + clip.quadraticBezierTo(p.control.dx, p.control.dy, p.pos.dx, p.pos.dy); + } + clip.lineTo(pts[0].pos.dx, pts[0].pos.dy); + return clip; + } + + @override + bool shouldReclip(covariant ArchClipper oldClipper) => oldClipper.type != type; +} + +class ArchPoint { + ArchPoint(this.pos, [Offset? control]) { + this.control = control ?? pos; + } + final Offset pos; + late final Offset control; + + static ArchPoint lerp(ArchPoint ptA, ArchPoint ptB, double t) { + return ArchPoint( + Offset.lerp(ptA.pos, ptB.pos, t) ?? Offset.zero, + Offset.lerp(ptA.control, ptB.control, t) ?? Offset.zero, + ); + } +} + +List _getArchPts(Size size, ArchType type) { + double distanceFromTop = 100; + switch (type) { + case ArchType.pyramid: + return [ + ArchPoint(Offset(0, size.height)), + ArchPoint(Offset(0, distanceFromTop)), + ArchPoint(Offset(size.width / 2, 0)), + ArchPoint(Offset(size.width, distanceFromTop)), + ArchPoint(Offset(size.width, size.height)), + ]; + case ArchType.spade: + return [ + ArchPoint(Offset(0, size.height)), + ArchPoint(Offset(0, distanceFromTop)), + ArchPoint(Offset(size.width / 2, 0), Offset(0, distanceFromTop * .66)), + ArchPoint(Offset(size.width, distanceFromTop), Offset(size.width, distanceFromTop * .66)), + ArchPoint(Offset(size.width, size.height)), + ]; + case ArchType.arch: + return [ + ArchPoint(Offset(0, size.height)), + ArchPoint(Offset(0, distanceFromTop * 2)), + ArchPoint(Offset(size.width / 2, 0), Offset(0, 0)), + ArchPoint(Offset(size.width, distanceFromTop * 2), Offset(size.width, 0)), + ArchPoint(Offset(size.width, size.height)), + ]; + case ArchType.wideArch: + return [ + ArchPoint(Offset(0, size.height)), + ArchPoint(Offset(0, distanceFromTop * 2)), + ArchPoint(Offset(0, distanceFromTop)), + ArchPoint(Offset(size.width / 2, 0), Offset(0, 0)), + ArchPoint(Offset(size.width, distanceFromTop), Offset(size.width, 0)), + ArchPoint(Offset(size.width, distanceFromTop * 2)), + ArchPoint(Offset(size.width, size.height)), + ]; + case ArchType.flatPyramid: + return [ + ArchPoint(Offset(0, size.height)), + ArchPoint(Offset(0, distanceFromTop)), + ArchPoint(Offset(size.width * 0.8 / 2, 0)), + ArchPoint(Offset(size.width * 1.2 / 2, 0)), + ArchPoint(Offset(size.width, distanceFromTop)), + ArchPoint(Offset(size.width, size.height)), + ]; + } +} + +class CurvedTopClipper extends CustomClipper { + CurvedTopClipper({this.flip = false}); + final bool flip; + + @override + Path getClip(Size size) { + double radius = size.width / 2; + var path = Path(); + if (flip) { + // path.addOval(Rect.fromCircle(center: Offset.zero, radius: 40)); + path.lineTo(0, size.height - radius); + path.arcToPoint( + Offset(size.width, size.height - radius), + radius: Radius.circular(size.width / 2), + clockwise: false, + ); + path.lineTo(size.width, 0); + path.lineTo(0, 0); + } else { + path.lineTo(0, 0); + path.lineTo(0, radius); + path.arcToPoint( + Offset(size.width, radius), + radius: Radius.circular(radius / 2), + ); + path.lineTo(size.width, size.height); + path.lineTo(0, size.height); + } + return path; + } + + @override + bool shouldReclip(CurvedTopClipper oldClipper) => oldClipper.flip != flip; +} diff --git a/lib/ui/common/dashed_line.dart b/lib/ui/common/dashed_line.dart new file mode 100644 index 00000000..52b0da90 --- /dev/null +++ b/lib/ui/common/dashed_line.dart @@ -0,0 +1,40 @@ +import 'package:wonders/common_libs.dart'; + +class DashedLine extends StatelessWidget { + const DashedLine({Key? key, this.vertical = false}) : super(key: key); + final bool vertical; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: vertical ? 2 : double.infinity, + height: vertical ? double.infinity : 2, + child: CustomPaint(painter: _DashedLinePainter(vertical)), + ); + } +} + +class _DashedLinePainter extends CustomPainter { + _DashedLinePainter(this.vertical); + final bool vertical; + + @override + void paint(Canvas canvas, Size size) { + double dashPx = 3, gapPx = 3, pos = 0; + final paint = Paint()..color = Colors.white; + if (vertical) { + while (pos < size.height) { + canvas.drawLine(Offset(0, pos), Offset(0, pos + dashPx), paint); + pos += dashPx + gapPx; + } + } else { + while (pos < size.width) { + canvas.drawLine(Offset(pos, 0), Offset(pos + dashPx, 0), paint); + pos += dashPx + gapPx; + } + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} diff --git a/lib/ui/common/fade_color_transition.dart b/lib/ui/common/fade_color_transition.dart new file mode 100644 index 00000000..fd394a4b --- /dev/null +++ b/lib/ui/common/fade_color_transition.dart @@ -0,0 +1,15 @@ +import 'package:wonders/common_libs.dart'; + +/// Colored box that can fade in and out, should yield better performance than +/// fading with an additional Opacity layer. +class FadeColorTransition extends StatelessWidget { + const FadeColorTransition({Key? key, required this.animation, required this.color}) : super(key: key); + final Animation animation; + final Color color; + + @override + Widget build(BuildContext context) => AnimatedBuilder( + animation: animation, + builder: (_, __) => Container(color: color.withOpacity(animation.value)), + ); +} diff --git a/lib/ui/common/google_maps_marker.dart b/lib/ui/common/google_maps_marker.dart new file mode 100644 index 00000000..c621826f --- /dev/null +++ b/lib/ui/common/google_maps_marker.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:wonders/assets.dart'; + +Marker getMapsMarker(LatLng position) => Marker( + markerId: MarkerId('0'), + position: position, + icon: AppBitmaps.mapMarker, + anchor: Offset(.5, .7), + ); diff --git a/lib/ui/common/gradient_container.dart b/lib/ui/common/gradient_container.dart new file mode 100644 index 00000000..5010904b --- /dev/null +++ b/lib/ui/common/gradient_container.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +class GradientContainer extends StatelessWidget { + const GradientContainer(this.colors, this.stops, + {Key? key, + this.child, + this.width, + this.height, + this.alignment, + this.begin, + this.end, + this.blendMode, + this.borderRadius}) + : super(key: key); + final List colors; + final List stops; + final double? width; + final double? height; + final Widget? child; + final Alignment? begin; + final Alignment? end; + final Alignment? alignment; + final BlendMode? blendMode; + final BorderRadius? borderRadius; + + @override + Widget build(BuildContext context) => IgnorePointer( + child: Container( + width: width, + height: height, + alignment: alignment, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: begin ?? Alignment.centerLeft, + end: end ?? Alignment.centerRight, + colors: colors, + stops: stops, + ), + backgroundBlendMode: blendMode, + borderRadius: borderRadius, + ), + child: child, + ), + ); +} + +class HzGradient extends GradientContainer { + const HzGradient(List colors, List stops, + {Key? key, + Widget? child, + double? width, + double? height, + Alignment? alignment, + BlendMode? blendMode, + BorderRadius? borderRadius}) + : super(colors, stops, + key: key, + child: child, + width: width, + height: height, + alignment: alignment, + blendMode: blendMode, + borderRadius: borderRadius); +} + +class VtGradient extends GradientContainer { + const VtGradient(List colors, List stops, + {Key? key, + Widget? child, + double? width, + double? height, + Alignment? alignment, + BlendMode? blendMode, + BorderRadius? borderRadius}) + : super(colors, stops, + key: key, + child: child, + width: width, + height: height, + alignment: alignment, + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + blendMode: blendMode, + borderRadius: borderRadius); +} diff --git a/lib/ui/common/hidden_collectible.dart b/lib/ui/common/hidden_collectible.dart new file mode 100644 index 00000000..ecab5e2e --- /dev/null +++ b/lib/ui/common/hidden_collectible.dart @@ -0,0 +1,24 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/collectible_item.dart'; + +/// Shows a [CollectibleItem], for a specific set of wonders. +/// The item is looked up via index, and expects that 3 items always exist for each wonder. +/// If `wonders` is empty, then the collectible is always shown. +class HiddenCollectible extends StatelessWidget with GetItMixin { + HiddenCollectible(this.currentWonder, {Key? key, required this.index, this.matches = const [], this.size = 64}) + : assert(index <= 2, 'index should not exceed 2'), + super(key: key); + final int index; + final double size; + final List matches; + final WonderType currentWonder; + @override + Widget build(BuildContext context) { + final data = collectiblesLogic.forWonder(currentWonder); + assert(data.length == 3, 'Each wonder should have exactly 3 collectibles'); + if (matches.isNotEmpty && matches.contains(currentWonder) == false) { + return SizedBox.shrink(); + } + return CollectibleItem(data[index], size: size); + } +} diff --git a/lib/ui/common/lazy_indexed_stack.dart b/lib/ui/common/lazy_indexed_stack.dart new file mode 100644 index 00000000..acc82e8e --- /dev/null +++ b/lib/ui/common/lazy_indexed_stack.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +/// A lazy-loading [IndexedStack] that loads [children] accordingly. +class LazyIndexedStack extends StatefulWidget { + const LazyIndexedStack({ + Key? key, + this.alignment = AlignmentDirectional.topStart, + this.textDirection, + this.sizing = StackFit.loose, + this.index = 0, + this.children = const [], + }) : super(key: key); + + final AlignmentGeometry alignment; + final TextDirection? textDirection; + final StackFit sizing; + final int index; + final List children; + + @override + LazyIndexedStackState createState() => LazyIndexedStackState(); +} + +class LazyIndexedStackState extends State { + late List _activated = _initializeActivatedList(); + + List _initializeActivatedList() => List.generate(widget.children.length, (i) => i == widget.index); + + @override + void didUpdateWidget(covariant LazyIndexedStack oldWidget) { + if (oldWidget.children.length != widget.children.length) { + _activated = _initializeActivatedList(); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + // Mark current index as active + _activated[widget.index] = true; + final children = List.generate(_activated.length, (i) { + return _activated[i] ? widget.children[i] : const SizedBox.shrink(); + }); + return IndexedStack( + alignment: widget.alignment, + sizing: widget.sizing, + textDirection: widget.textDirection, + index: widget.index, + children: children, + ); + } +} diff --git a/lib/ui/common/list_gradient.dart b/lib/ui/common/list_gradient.dart new file mode 100644 index 00000000..3c35371b --- /dev/null +++ b/lib/ui/common/list_gradient.dart @@ -0,0 +1,17 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/gradient_container.dart'; + +/// Used for situations where the list content should blend into a background color. +/// Can be placed at the top or bottom of a list, using the `flip' option when on the bottom +class ListOverscollGradient extends StatelessWidget { + const ListOverscollGradient({Key? key, this.size = 100, this.color, this.bottomUp = false}) : super(key: key); + final bool bottomUp; + final double size; + final Color? color; + + @override + Widget build(BuildContext context) { + final c = color ?? $styles.colors.black; + return VtGradient([c.withOpacity(bottomUp ? 0 : 1), c.withOpacity(bottomUp ? 1 : 0)], const [0, 1], height: size); + } +} diff --git a/lib/ui/common/measurable_widget.dart b/lib/ui/common/measurable_widget.dart new file mode 100644 index 00000000..dc3bd472 --- /dev/null +++ b/lib/ui/common/measurable_widget.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:flutter/rendering.dart'; +import 'package:wonders/common_libs.dart'; + +class MeasurableWidget extends SingleChildRenderObjectWidget { + const MeasurableWidget({Key? key, required this.onChange, required Widget child}) : super(key: key, child: child); + final void Function(Size size) onChange; + @override + RenderObject createRenderObject(BuildContext context) => MeasureSizeRenderObject(onChange); +} + +class MeasureSizeRenderObject extends RenderProxyBox { + MeasureSizeRenderObject(this.onChange); + void Function(Size size) onChange; + + Size _prevSize = Size.zero; + @override + void performLayout() { + super.performLayout(); + Size newSize = child?.size ?? Size.zero; + if (_prevSize == newSize) return; + _prevSize = newSize; + scheduleMicrotask(() => onChange(newSize)); + } +} diff --git a/lib/ui/common/modals/app_modals.dart b/lib/ui/common/modals/app_modals.dart new file mode 100644 index 00000000..e2e62b93 --- /dev/null +++ b/lib/ui/common/modals/app_modals.dart @@ -0,0 +1,100 @@ +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/themed_text.dart'; + +Future showModal(BuildContext context, {required Widget child}) async { + return await showMaterialModalBottomSheet( + expand: false, + context: context, + backgroundColor: $styles.colors.greyStrong, + builder: (_) => child, + ) ?? + false; +} + +class LoadingModal extends StatelessWidget { + const LoadingModal({Key? key, this.title, this.msg, this.child}) : super(key: key); + final String? title; + final String? msg; + final Widget? child; + + @override + Widget build(BuildContext context) { + return _BaseContentModal( + title: title, + msg: msg, + buttons: const [], + child: child, + ); + } +} + +class OkModal extends StatelessWidget { + const OkModal({Key? key, this.title, this.msg, this.child}) : super(key: key); + final String? title; + final String? msg; + final Widget? child; + + @override + Widget build(BuildContext context) { + return _BaseContentModal( + title: title, + msg: msg, + buttons: [ + AppBtn.from(text: $strings.appModalsButtonOk, expand: true, isSecondary: true, onPressed: () => Navigator.of(context).pop(true)), + ], + child: child, + ); + } +} + +class OkCancelModal extends StatelessWidget { + const OkCancelModal({Key? key, this.title, this.msg, this.child}) : super(key: key); + final String? title; + final String? msg; + final Widget? child; + + @override + Widget build(BuildContext context) { + return _BaseContentModal( + title: title, + msg: msg, + buttons: [ + AppBtn.from(text: $strings.appModalsButtonOk, expand: true, isSecondary: true, onPressed: () => Navigator.of(context).pop(true)), + Gap($styles.insets.xs), + AppBtn.from(text: $strings.appModalsButtonCancel, expand: true, onPressed: () => Navigator.of(context).pop(false)), + ], + child: child, + ); + } +} + +/// Allows for a title, msg and body widget +class _BaseContentModal extends StatelessWidget { + final String? title; + final String? msg; + final Widget? child; + final List buttons; + + const _BaseContentModal({Key? key, this.title, this.msg, required this.buttons, this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all($styles.insets.lg), + child: LightText( + child: SeparatedColumn( + mainAxisSize: MainAxisSize.min, + separatorBuilder: () => Gap($styles.insets.md), + children: [ + if (title != null) Text(title!, style: $styles.text.h2), + if (child != null) child!, + if (msg != null) Text(msg!, style: $styles.text.body), + Gap($styles.insets.md), + Column(children: buttons.map((e) => e).toList()) + ], + ), + ), + ); + } +} diff --git a/lib/ui/common/modals/fullscreen_maps_viewer.dart b/lib/ui/common/modals/fullscreen_maps_viewer.dart new file mode 100644 index 00000000..a076277c --- /dev/null +++ b/lib/ui/common/modals/fullscreen_maps_viewer.dart @@ -0,0 +1,27 @@ +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/google_maps_marker.dart'; + +class FullscreenMapsViewer extends StatelessWidget { + FullscreenMapsViewer({Key? key, required this.type}) : super(key: key); + final WonderType type; + + WonderData get data => wondersLogic.getData(type); + late final startPos = CameraPosition(target: LatLng(data.lat, data.lng), zoom: 17); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + GoogleMap( + mapType: MapType.hybrid, + markers: {getMapsMarker(startPos.target)}, + initialCameraPosition: startPos, + myLocationButtonEnabled: false, + ), + BackBtn().safe(), + ], + ); + } +} diff --git a/lib/ui/common/modals/fullscreen_url_img_viewer.dart b/lib/ui/common/modals/fullscreen_url_img_viewer.dart new file mode 100644 index 00000000..5868fc7a --- /dev/null +++ b/lib/ui/common/modals/fullscreen_url_img_viewer.dart @@ -0,0 +1,102 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; + +class FullscreenUrlImgViewer extends StatefulWidget { + const FullscreenUrlImgViewer({Key? key, required this.urls, this.index = 0}) : super(key: key); + final List urls; + final int index; + + @override + State createState() => _FullscreenUrlImgViewerState(); +} + +class _FullscreenUrlImgViewerState extends State { + final _isZoomed = ValueNotifier(false); + late final _controller = PageController(initialPage: widget.index); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _handleBackPressed() => Navigator.pop(context, _controller.page!.round()); + + @override + Widget build(BuildContext context) { + Widget content = AnimatedBuilder( + animation: _isZoomed, + builder: (_, __) { + final bool enableSwipe = !_isZoomed.value && widget.urls.length > 1; + return PageView.builder( + physics: enableSwipe ? PageScrollPhysics() : NeverScrollableScrollPhysics(), + controller: _controller, + itemCount: widget.urls.length, + itemBuilder: (_, index) => _Viewer(widget.urls[index], _isZoomed), + onPageChanged: (_) => AppHaptics.lightImpact(), + ); + }, + ); + + content = Semantics( + label: $strings.fullscreenImageViewerSemanticFull, + container: true, + image: true, + child: ExcludeSemantics(child: content), + ); + + return Container( + color: $styles.colors.black, + child: Stack( + children: [ + Positioned.fill(child: content), + BackBtn.close(onPressed: _handleBackPressed).safe(), + ], + ), + ); + } +} + +class _Viewer extends StatefulWidget { + const _Viewer(this.url, this.isZoomed, {Key? key}) : super(key: key); + + final String url; + final ValueNotifier isZoomed; + + @override + State<_Viewer> createState() => _ViewerState(); +} + +class _ViewerState extends State<_Viewer> with SingleTickerProviderStateMixin { + final _controller = TransformationController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + /// Reset zoom level to 1 on double-tap + void _handleDoubleTap() => _controller.value = Matrix4.identity(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onDoubleTap: _handleDoubleTap, + child: InteractiveViewer( + transformationController: _controller, + onInteractionEnd: (_) => widget.isZoomed.value = _controller.value.getMaxScaleOnAxis() > 1, + minScale: 1, + maxScale: 5, + child: Hero( + tag: widget.url, + child: AppImage( + image: NetworkImage(widget.url), + fit: BoxFit.contain, + scale: 2.5, + ), + ), + ), + ); + } +} diff --git a/lib/ui/common/modals/fullscreen_video_viewer.dart b/lib/ui/common/modals/fullscreen_video_viewer.dart new file mode 100644 index 00000000..ce4d02c5 --- /dev/null +++ b/lib/ui/common/modals/fullscreen_video_viewer.dart @@ -0,0 +1,59 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/controls/app_loading_indicator.dart'; +import 'package:youtube_player_iframe/youtube_player_iframe.dart'; + +class FullscreenVideoPage extends StatefulWidget { + const FullscreenVideoPage({Key? key, required this.id}) : super(key: key); + final String id; + + @override + State createState() => _FullscreenVideoPageState(); +} + +class _FullscreenVideoPageState extends State { + late final _controller = YoutubePlayerController( + initialVideoId: widget.id, + params: const YoutubePlayerParams(autoPlay: true, startAt: Duration(seconds: 1)), + )..play(); + + @override + void initState() { + super.initState(); + appLogic.setDeviceOrientation(null); + } + + @override + void dispose() { + appLogic.setDeviceOrientation(Axis.vertical); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + double aspect = context.isLandscape ? MediaQuery.of(context).size.aspectRatio : 9 / 9; + return Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + Center( + child: AspectRatio( + aspectRatio: aspect, + child: Stack( + children: [ + const Center(child: AppLoadingIndicator()), + YoutubePlayerIFrame(controller: _controller, aspectRatio: aspect), + ], + ), + ), + ), + SafeArea( + child: Padding( + padding: EdgeInsets.all($styles.insets.md), + child: const BackBtn(), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/common/modals/fullscreen_web_view.dart b/lib/ui/common/modals/fullscreen_web_view.dart new file mode 100644 index 00000000..431bd51d --- /dev/null +++ b/lib/ui/common/modals/fullscreen_web_view.dart @@ -0,0 +1,19 @@ +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:wonders/common_libs.dart'; + +class FullscreenWebView extends StatelessWidget { + const FullscreenWebView(this.url, {Key? key}) : super(key: key); + final String url; + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar(), + body: InAppWebView( + initialUrlRequest: URLRequest(url: Uri.parse(url)), + ), + ), + ); + } +} diff --git a/lib/ui/common/scaling_list_item.dart b/lib/ui/common/scaling_list_item.dart new file mode 100644 index 00000000..5aaa7b40 --- /dev/null +++ b/lib/ui/common/scaling_list_item.dart @@ -0,0 +1,54 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/utils/context_utils.dart'; + +class AnimatedListItem extends StatelessWidget { + const AnimatedListItem({Key? key, required this.scrollPos, required this.builder}) : super(key: key); + final ValueNotifier scrollPos; + final Widget Function(BuildContext context, double pctVisible) builder; + + @override + Widget build(BuildContext context) { + // Use Animate.toggle to build the child twice, this will allow it to properly measure its size and position. + return Animate().toggle( + builder: (_, value, __) => ValueListenableBuilder( + valueListenable: scrollPos, + builder: (_, value, __) { + return LayoutBuilder( + builder: (_, constraints) { + Offset? pos = ContextUtils.getGlobalPos(context); + final yPos = pos?.dy; + final widgetHeight = constraints.maxHeight; + double pctVisible = 0; + if (yPos != null) { + final amtVisible = context.heightPx - yPos; + pctVisible = (amtVisible / widgetHeight * .5).clamp(0, 1); + } + return builder(context, pctVisible); + }, + ); + }, + ), + ); + } +} + +/// Takes a scroll position notifier and a child. +/// Scales its child as it scrolls onto screen for a nice effect. +class ScalingListItem extends StatelessWidget { + const ScalingListItem({Key? key, required this.scrollPos, required this.child}) : super(key: key); + final ValueNotifier scrollPos; + final Widget child; + + @override + Widget build(BuildContext context) { + return AnimatedListItem( + scrollPos: scrollPos, + builder: (_, pctVisible) { + final scale = 1.35 - pctVisible * .35; + return ClipRect( + child: Transform.scale(scale: scale, child: child), + ); + }, + ); + } +} diff --git a/lib/ui/common/stacked_page_view_builder.dart b/lib/ui/common/stacked_page_view_builder.dart new file mode 100644 index 00000000..850278e6 --- /dev/null +++ b/lib/ui/common/stacked_page_view_builder.dart @@ -0,0 +1,65 @@ +import 'package:wonders/common_libs.dart'; + +/// Decouples the scrollable area from the size of the PageView itself. +/// Helps simplify the layout of the underlying view and can create a better UX with screen-readers. +/// +/// Internally it creates a stack with an invisible PageView on the bottom to handle scrolling, +/// then drives a second PageController (follower) which is passed to the `builder` so a nested PageView can +/// be created to hold the content itself. +/// +/// Scrolling of the nested PageView will then be driven by the outer one. +/// +class StackedPageViewBuilder extends StatefulWidget { + const StackedPageViewBuilder({ + Key? key, + this.initialIndex = 0, + required this.pageCount, + required this.builder, + this.onInit, + }) : super(key: key); + final int initialIndex; + final int pageCount; + final Widget Function(BuildContext builder, PageController controller, PageController follower) builder; + final void Function(PageController controller, PageController follower)? onInit; + @override + State createState() => _StackedPageViewBuilderState(); +} + +class _StackedPageViewBuilderState extends State { + late final _controller = PageController(initialPage: widget.initialIndex); + late final _follower = PageController(initialPage: widget.initialIndex); + + @override + void initState() { + super.initState(); + _controller.addListener(_handleControllerChanged); + widget.onInit?.call(_controller, _follower); + } + + @override + void dispose() { + _controller.dispose(); + _follower.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned.fill( + child: ExcludeSemantics( + child: PageView.builder( + itemCount: widget.pageCount, + controller: _controller, + itemBuilder: (_, __) => Container(color: Colors.transparent), + ), + ), + ), + widget.builder(context, _controller, _follower), + ], + ); + } + + void _handleControllerChanged() => _follower.jumpTo(_controller.position.pixels); +} diff --git a/lib/ui/common/themed_text.dart b/lib/ui/common/themed_text.dart new file mode 100644 index 00000000..db4d9e39 --- /dev/null +++ b/lib/ui/common/themed_text.dart @@ -0,0 +1,37 @@ +import 'package:wonders/common_libs.dart'; + +class DefaultTextColor extends StatelessWidget { + const DefaultTextColor({Key? key, required this.color, required this.child}) : super(key: key); + final Color color; + final Widget child; + + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: DefaultTextStyle.of(context).style.copyWith(color: color), + child: child, + ); + } +} + +class LightText extends StatelessWidget { + const LightText({Key? key, required this.child}) : super(key: key); + final Widget child; + + @override + Widget build(BuildContext context) => DefaultTextColor( + color: Colors.white, + child: child, + ); +} + +class DarkText extends StatelessWidget { + const DarkText({Key? key, required this.child}) : super(key: key); + final Widget child; + + @override + Widget build(BuildContext context) => DefaultTextColor( + color: $styles.colors.black, + child: child, + ); +} diff --git a/lib/ui/common/timeline_event_card.dart b/lib/ui/common/timeline_event_card.dart new file mode 100644 index 00000000..46c43dd7 --- /dev/null +++ b/lib/ui/common/timeline_event_card.dart @@ -0,0 +1,40 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; + +class TimelineEventCard extends StatelessWidget { + const TimelineEventCard({Key? key, required this.year, required this.text}) : super(key: key); + final int year; + final String text; + + @override + Widget build(BuildContext context) { + return MergeSemantics( + child: Padding( + padding: EdgeInsets.only(bottom: $styles.insets.sm), + child: Container( + color: $styles.colors.offWhite, + padding: EdgeInsets.all($styles.insets.sm), + child: Row( + children: [ + SizedBox( + width: 75, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('${year.abs()}', style: $styles.text.h3.copyWith(fontWeight: FontWeight.w400, height: 1)), + Text(StringUtils.getYrSuffix(year), style: $styles.text.bodySmall), + ], + ), + ), + Center(child: Container(width: 1, height: 50, color: $styles.colors.black)), + Gap($styles.insets.sm), + Expanded( + child: Text(text, style: $styles.text.bodySmall), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/common/unsplash_photo.dart b/lib/ui/common/unsplash_photo.dart new file mode 100644 index 00000000..6b744b57 --- /dev/null +++ b/lib/ui/common/unsplash_photo.dart @@ -0,0 +1,26 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/unsplash_photo_data.dart'; + +class UnsplashPhoto extends StatelessWidget { + const UnsplashPhoto(this.id, {Key? key, this.fit = BoxFit.cover, required this.size, this.showCredits = false}) + : super(key: key); + final String id; + final BoxFit fit; + final UnsplashPhotoSize size; + final bool showCredits; + + @override + Widget build(BuildContext context) { + return Stack( + fit: StackFit.expand, + children: [ + AppImage( + image: NetworkImage(UnsplashPhotoData.getSelfHostedUrl(id, size)), + fit: fit, + progress: true, + scale: 1, + ), + ], + ); + } +} diff --git a/lib/ui/common/utils/app_haptics.dart b/lib/ui/common/utils/app_haptics.dart new file mode 100644 index 00000000..f09fc667 --- /dev/null +++ b/lib/ui/common/utils/app_haptics.dart @@ -0,0 +1,50 @@ +// Simple class wrapping HapticFeedback to make testing a bit simpler. + +import 'package:flutter/foundation.dart'; +import 'package:wonders/common_libs.dart'; + +class AppHaptics { + // note: system sounds are pretty buggy on Android: https://github.com/flutter/flutter/issues/57531 + static bool debugSound = kDebugMode; + static bool debugLog = kDebugMode; + + static void buttonPress() { + // Android/Fuchsia expect haptics on all button presses, iOS does not. + if (defaultTargetPlatform != TargetPlatform.android || defaultTargetPlatform != TargetPlatform.fuchsia) { + lightImpact(); + } + } + + static Future lightImpact() { + _debug('lightImpact'); + return HapticFeedback.lightImpact(); + } + + static Future mediumImpact() { + _debug('mediumImpact'); + return HapticFeedback.mediumImpact(); + } + + static Future heavyImpact() { + _debug('heavyImpact'); + return HapticFeedback.heavyImpact(); + } + + static Future selectionClick() { + _debug('selectionClick'); + return HapticFeedback.selectionClick(); + } + + static Future vibrate() { + _debug('vibrate'); + return HapticFeedback.vibrate(); + } + + static void _debug(String label) { + if (debugLog) debugPrint('Haptic.$label'); + if (debugSound) { + SystemSound.play(SystemSoundType.alert); // only plays on desktop + SystemSound.play(SystemSoundType.click); // only plays on mobile + } + } +} diff --git a/lib/ui/common/utils/context_utils.dart b/lib/ui/common/utils/context_utils.dart new file mode 100644 index 00000000..3c6ae7d0 --- /dev/null +++ b/lib/ui/common/utils/context_utils.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class ContextUtils { + static Offset? getGlobalPos(BuildContext context, [Offset offset = Offset.zero]) { + final rb = context.findRenderObject() as RenderBox?; + if (rb?.hasSize == true) { + return rb?.localToGlobal(offset); + } + return null; + } + + static Size? getSize(BuildContext context) { + final rb = context.findRenderObject() as RenderBox?; + if (rb?.hasSize == true) { + return rb?.size; + } + return null; + } +} diff --git a/lib/ui/common/utils/page_routes.dart b/lib/ui/common/utils/page_routes.dart new file mode 100644 index 00000000..ecb3b5f7 --- /dev/null +++ b/lib/ui/common/utils/page_routes.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class PageRoutes { + static const Duration kDefaultDuration = Duration(milliseconds: 300); + + static Route dialog(Widget child, [Duration duration = kDefaultDuration, bool opaque = false]) { + return PageRouteBuilder( + transitionDuration: duration, + reverseTransitionDuration: duration, + pageBuilder: (context, animation, secondaryAnimation) => child, + opaque: opaque, + fullscreenDialog: true, + transitionsBuilder: (context, animation, secondaryAnimation, child) => + FadeTransition(opacity: animation, child: child), + ); + } +} diff --git a/lib/ui/common/wonders_timeline_builder.dart b/lib/ui/common/wonders_timeline_builder.dart new file mode 100644 index 00000000..f3e23d9b --- /dev/null +++ b/lib/ui/common/wonders_timeline_builder.dart @@ -0,0 +1,112 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; + +/// Visualizes all of the wonders over time. +/// Distributes the wonders over multiple "tracks" so that they do not overlap. +/// Provides a builder, so the visual representation of each track entry can be customized +class WondersTimelineBuilder extends StatelessWidget { + const WondersTimelineBuilder({ + Key? key, + this.selectedWonders = const [], + this.timelineBuilder, + this.axis = Axis.horizontal, + this.crossAxisGap, + this.minSize = 10, + }) : super(key: key); + final List selectedWonders; + final Widget Function(BuildContext, WonderData type, bool isSelected)? timelineBuilder; + final Axis axis; + final double? crossAxisGap; + final double minSize; + bool get isHz => axis == Axis.horizontal; + + @override + Widget build(BuildContext context) { + final gap = crossAxisGap ?? $styles.insets.xs; + // Depending on axis, we put all the wonders in a hz row, or vt column + Widget wrapFlex(List c) { + c = c.map((w) => Expanded(child: w)).toList(); + return isHz + ? SeparatedColumn(verticalDirection: VerticalDirection.up, separatorBuilder: () => Gap(gap), children: c) + : SeparatedRow(separatorBuilder: () => Gap(gap), children: c); + } + + return LayoutBuilder(builder: (_, constraints) { + /// Builds one timeline track, may contain multiple wonders, but they should not overlap + Widget buildSingleTimelineTrack(BuildContext context, List types) { + return Stack( + clipBehavior: Clip.none, + children: types.map( + (t) { + final data = wondersLogic.getData(t); + // To keep the math simple, first figure out a multiplier we can use to convert yrs to pixels. + int totalYrs = wondersLogic.timelineEndYear - wondersLogic.timelineStartYear; + double pxToYrRatio = totalYrs / ((isHz ? constraints.maxWidth : constraints.maxHeight)); + // Now we just need to calculate year spans, and then convert them to pixels for the start/end position in the Stack + int wonderYrs = data.endYr - data.startYr; + int yrsFromStart = data.startYr - wondersLogic.timelineStartYear; + double startPx = yrsFromStart / pxToYrRatio; + double sizePx = wonderYrs / pxToYrRatio; + if (sizePx < minSize) { + double yearDelta = ((minSize - sizePx) / 2); + sizePx = minSize; + startPx -= yearDelta; + } + final isSelected = selectedWonders.contains(data.type); + final child = + timelineBuilder?.call(context, data, isSelected) ?? _DefaultTrackEntry(isSelected: isSelected); + return isHz + ? Positioned(left: startPx, width: sizePx, top: 0, bottom: 0, child: child) + : Positioned(top: startPx, height: sizePx, left: 0, right: 0, child: child); + }, + ).toList(), + ); + } + + return wrapFlex([ + // Track 1 + buildSingleTimelineTrack( + context, + [ + WonderType.greatWall, + WonderType.pyramidsGiza, + WonderType.christRedeemer, + ], + ), + // Track 2 + buildSingleTimelineTrack( + context, + [ + WonderType.petra, + WonderType.machuPicchu, + ], + ), + // Track 3 + buildSingleTimelineTrack( + context, + [ + WonderType.chichenItza, + WonderType.tajMahal, + WonderType.colosseum, + ], + ), + ]); + }); + } +} + +class _DefaultTrackEntry extends StatelessWidget { + const _DefaultTrackEntry({Key? key, required this.isSelected}) : super(key: key); + final bool isSelected; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: isSelected ? $styles.colors.accent2 : Colors.transparent, + borderRadius: BorderRadius.circular(99), + border: Border.all(color: $styles.colors.accent2), + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart b/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart new file mode 100644 index 00000000..4b81ed94 --- /dev/null +++ b/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart @@ -0,0 +1,215 @@ +import 'dart:math' as math; +import 'dart:ui'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/highlight_data.dart'; +import 'package:wonders/ui/common/controls/app_page_indicator.dart'; +import 'package:wonders/ui/common/controls/simple_header.dart'; +part 'widgets/_blurred_image_bg.dart'; +part 'widgets/_carousel_item.dart'; + +class ArtifactCarouselScreen extends StatefulWidget { + final WonderType type; + const ArtifactCarouselScreen({Key? key, required this.type}) : super(key: key); + + @override + State createState() => _ArtifactScreenState(); +} + +class _ArtifactScreenState extends State { + // Used to cap white background dimensions. + static const double _maxElementWidth = 440; + static const double _partialElementWidth = 0.9; + static const double _maxElementHeight = 640; + + // Locally store loaded artifacts. + late List _artifacts; + late PageController _controller; + + double get _currentOffset { + bool inited = _controller.hasClients && _controller.position.haveDimensions; + return inited ? _controller.page! : _controller.initialPage * 1.0; + } + + int get _currentIndex => _currentOffset.round() % _artifacts.length; + + @override + void initState() { + super.initState(); + _artifacts = HighlightData.forWonder(widget.type); + + _controller = PageController( + // start at a high offset so we can scroll backwards: + initialPage: _artifacts.length * 9999, + viewportFraction: 0.5, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _handleArtifactTap(int index) { + int delta = index - _currentOffset.round(); + if (delta == 0) { + HighlightData data = _artifacts[index % _artifacts.length]; + context.push(ScreenPaths.artifact(data.artifactId)); + } else { + _controller.animateToPage( + _currentOffset.round() + delta, + duration: $styles.times.fast, + curve: Curves.easeOut, + ); + } + } + + void _handleSearchTap() { + context.push(ScreenPaths.search(widget.type)); + } + + @override + Widget build(BuildContext context) { + return Container( + color: $styles.colors.greyStrong, + child: AnimatedBuilder(animation: _controller, builder: _buildScreen), + ); + } + + Widget _buildScreen(BuildContext context, _) { + final double w = context.widthPx; + final double backdropWidth = w <= _maxElementWidth ? w : min(w * _partialElementWidth, _maxElementWidth); + final double backdropHeight = math.min(context.heightPx * 0.65, _maxElementHeight); + final bool small = backdropHeight / _maxElementHeight < 0.7; + final HighlightData artifact = _artifacts[_currentIndex]; + + return Stack( + children: [ + Positioned.fill(child: _BlurredImageBg(url: artifact.imageUrl)), + Column( + children: [ + SimpleHeader($strings.artifactsTitleArtifacts, showBackBtn: false, isTransparent: true), + Gap($styles.insets.xs), + Expanded( + child: Stack(children: [ + // White arch, covering bottom half: + BottomCenter( + child: Container( + width: backdropWidth, + height: backdropHeight, + decoration: BoxDecoration( + color: $styles.colors.offWhite.withOpacity(0.8), + borderRadius: BorderRadius.vertical(top: Radius.circular(999)), + ), + ), + ), + + // Carousel: + Center( + child: SizedBox( + width: backdropWidth, + child: PageView.builder( + controller: _controller, + clipBehavior: Clip.none, + itemBuilder: (context, index) { + bool isCurrentIndex = index % _artifacts.length == _currentIndex; + return ExcludeSemantics( + excluding: isCurrentIndex == false, + child: MergeSemantics( + child: Semantics( + onIncrease: () => _handleArtifactTap(_currentIndex + 1), + onDecrease: () => _handleArtifactTap(_currentIndex - 1), + child: _CarouselItem( + index: index, + currentPage: _currentOffset, + artifact: _artifacts[index % _artifacts.length], + bottomPadding: backdropHeight, + maxWidth: backdropWidth, + maxHeight: backdropHeight, + onPressed: () => _handleArtifactTap(index), + ), + ), + ), + ); + }, + ), + ), + ), + + // Text content + BottomCenter( + child: Container( + width: backdropWidth, + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Gap($styles.insets.md), + _buildContent(context, artifact, backdropWidth, small), + Gap(small ? $styles.insets.sm : $styles.insets.md), + AppBtn.from( + text: $strings.artifactsButtonBrowse, + icon: Icons.search, + expand: true, + onPressed: _handleSearchTap, + ), + Gap(small ? $styles.insets.md : $styles.insets.lg), + ], + ), + ), + ), + ]), + ), + ], + ), + ], + ); + } + + Widget _buildContent(BuildContext context, HighlightData artifact, double width, bool small) { + return Container( + width: width, + padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IgnorePointer( + child: ExcludeSemantics( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: small ? 90 : 110, + alignment: Alignment.center, + child: Text( + artifact.title, + overflow: TextOverflow.ellipsis, + style: $styles.text.h2.copyWith(color: $styles.colors.black, height: 1.2), + textAlign: TextAlign.center, + maxLines: 2, + ), + ), + if (!small) Gap($styles.insets.xxs), + Text( + artifact.date.isEmpty ? '--' : artifact.date, + style: $styles.text.body, + textAlign: TextAlign.center, + ), + ], + ).animate(key: ValueKey(artifact.artifactId)).fadeIn(), + ), + ), + Gap(small ? $styles.insets.xs : $styles.insets.sm), + AppPageIndicator( + count: _artifacts.length, + controller: _controller, + semanticPageTitle: $strings.artifactsSemanticArtifact, + ), + ], + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_carousel/widgets/_blurred_image_bg.dart b/lib/ui/screens/artifact/artifact_carousel/widgets/_blurred_image_bg.dart new file mode 100644 index 00000000..fa5d32bf --- /dev/null +++ b/lib/ui/screens/artifact/artifact_carousel/widgets/_blurred_image_bg.dart @@ -0,0 +1,33 @@ +part of '../artifact_carousel_screen.dart'; + +/// Blurry image background for the Artifact Highlights view. Contains horizontal and vertical gradients that stack overtop the blended image. +class _BlurredImageBg extends StatelessWidget { + const _BlurredImageBg({Key? key, this.url}) : super(key: key); + final String? url; + + @override + Widget build(BuildContext context) { + final img = AppImage( + image: url == null ? null : NetworkImage(url!), + syncDuration: $styles.times.fast, + fit: BoxFit.cover, + scale: 0.5, + ); + final fgOpacity = settingsLogic.useBlurs ? 0.6 : 0.8; + return Transform.scale( + scale: 1.25, + alignment: Alignment(0, 0.8), + child: Container( + foregroundDecoration: BoxDecoration( + color: $styles.colors.black.withOpacity(fgOpacity), + ), + child: settingsLogic.useBlurs + ? ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 6, sigmaY: 6), + child: img, + ) + : img, + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart b/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart new file mode 100644 index 00000000..35e5e999 --- /dev/null +++ b/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart @@ -0,0 +1,136 @@ +part of '../artifact_carousel_screen.dart'; + +class _CarouselItem extends StatelessWidget { + const _CarouselItem({ + Key? key, + required this.index, + required this.currentPage, + required this.artifact, + required this.bottomPadding, + required this.maxWidth, + required this.maxHeight, + required this.onPressed, + }) : super(key: key); + final HighlightData artifact; + final int index; + final double currentPage; + final double bottomPadding; + final double maxWidth; + final double maxHeight; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return AppBtn.basic( + semanticLabel: '${artifact.title} ${artifact.date}', + onPressed: onPressed, + pressEffect: false, + child: _ImagePreview( + image: NetworkImage(artifact.imageUrlSmall), + bottomPadding: bottomPadding, + maxWidth: maxWidth, + maxHeight: maxHeight, + offsetAmt: currentPage - index.toDouble(), + ), + ); + } +} + +class _ImagePreview extends StatelessWidget { + const _ImagePreview( + {Key? key, + required this.image, + required this.offsetAmt, + required this.bottomPadding, + required this.maxWidth, + required this.maxHeight}) + : super(key: key); + final ImageProvider image; + final double offsetAmt; + final double bottomPadding; + final double maxWidth; + final double maxHeight; + + @override + Widget build(BuildContext context) { + // Additional scale of the main page, making it larger than the others. + const double mainPageScaleFactor = 0.40; + + // Additional Y scale of the main page, making it elongated. + const double mainPageScaleFactorY = -0.13; + + // Border variables + const double borderPadding = 4.0; + const double borderWidth = 1.0; + + // Base scale units that we can treat like pixels. + double baseWidthScale = 1 / maxWidth; + double baseHeightScale = 1 / context.heightPx; + + // Size of the pages themselves. + double pageWidth = baseWidthScale * maxWidth * 0.65; + double pageHeight = baseHeightScale * maxWidth * 0.45; + + // Get the current page offset value compared to other pages. -1 is left, 0 is middle, 1 is right, etc. + // Note: This value can be in between whole numbers, like 0.25 and -0.75. + double pageOffset = math.max(-2, math.min(2, offsetAmt)); + + // Add a scale-up to the main page. + double midPageScaleUp = (1 - math.min(1, pageOffset.abs())) * mainPageScaleFactor; + double midPageScaleUpY = (1 - math.min(1, pageOffset.abs())) * mainPageScaleFactorY; + + // Use absolute value of offset so images always move down. + double yOffsetFactor = pageOffset.abs(); + + if (pageOffset >= -1 && pageOffset <= 1) { + // Create an offset factor using sin/cos to ease. + yOffsetFactor = 1 - math.cos(pageOffset * math.pi / 2.0).abs(); + } + + // Multiply the offset factors with the width/height scale to convert them to fractionals. + double yOffset = yOffsetFactor * ((baseHeightScale / 2) * (maxWidth / 2)); + + // Apply a vertical offset based on the bottom padding provided. This includes half the element width. + double bottomPadding = (baseHeightScale / 2) * (maxHeight * 2 - (maxWidth / 2)); + + double widthFactor = pageWidth + midPageScaleUp; + double heightFactor = pageHeight + midPageScaleUp + midPageScaleUpY; + + double opacity = max(0, 1 - max(0, pageOffset.abs() - 1) * 2); + + // Scale box for sizing. Uses both the element scale and the element Y scale. + return FractionalTranslation( + // Move the pages around before scaling, as scaling will directly affect their translation. + translation: Offset(0, yOffset - bottomPadding), + child: FractionallySizedBox( + // Scale the elements according to whether they are on the sides or middle. + alignment: Alignment.bottomCenter, + widthFactor: widthFactor, + heightFactor: heightFactor, + // Translation box for positioning. + child: Opacity( + opacity: opacity, + child: Container( + // Add an outer border with the rounded ends. + decoration: BoxDecoration( + shape: BoxShape.rectangle, + border: Border.all(color: $styles.colors.offWhite, width: borderWidth), + borderRadius: BorderRadius.all(Radius.circular(999)), + ), + + child: Padding( + padding: EdgeInsets.all(borderPadding), + child: ClipRRect( + borderRadius: BorderRadius.circular(999), + child: ColoredBox( + color: $styles.colors.greyMedium, + child: AppImage(image: image, fit: BoxFit.cover, scale: 0.5), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart b/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart new file mode 100644 index 00000000..7de55b15 --- /dev/null +++ b/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart @@ -0,0 +1,73 @@ +import 'package:flutter/cupertino.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/artifact_data.dart'; +import 'package:wonders/ui/common/compass_divider.dart'; +import 'package:wonders/ui/common/controls/app_loading_indicator.dart'; +import 'package:wonders/ui/common/gradient_container.dart'; +import 'package:wonders/ui/common/modals/fullscreen_url_img_viewer.dart'; + +part 'widgets/_header.dart'; +part 'widgets/_content.dart'; + +class ArtifactDetailsScreen extends StatefulWidget { + const ArtifactDetailsScreen({Key? key, required this.artifactId}) : super(key: key); + final String artifactId; + + @override + State createState() => _ArtifactDetailsScreenState(); +} + +class _ArtifactDetailsScreenState extends State { + late final _future = metAPILogic.getArtifactByID(widget.artifactId); + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: $styles.colors.greyStrong, + child: FutureBuilder( + future: _future, + builder: (_, snapshot) { + final data = snapshot.data; + late Widget content; + if (snapshot.hasError || (snapshot.hasData && data == null)) { + content = Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center(child: Icon(Icons.warning_amber_outlined, color: $styles.colors.accent1, size: $styles.insets.lg,)), + Gap($styles.insets.xs), + SizedBox(width: $styles.insets.xxl * 3, child: Text( + StringUtils.supplant($strings.artifactDetailsErrorNotFound, {'{artifactId}': widget.artifactId}), + style: $styles.text.body.copyWith(color: $styles.colors.offWhite), + textAlign: TextAlign.center, + ),), + ], + ).animate().fadeIn(); + } else if (!snapshot.hasData) { + content = Center(child: AppLoadingIndicator()); + } else { + content = CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + elevation: 0, + leading: SizedBox.shrink(), + expandedHeight: context.heightPx * .5, + collapsedHeight: context.heightPx * .35, + flexibleSpace: _Header(data: data!), + ), + SliverToBoxAdapter(child: _Content(data: data)), + ], + ); + } + + return Stack(children: [ + content, + BackBtn().safe(), + ]); + }, + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_details/widgets/_content.dart b/lib/ui/screens/artifact/artifact_details/widgets/_content.dart new file mode 100644 index 00000000..9e81da2c --- /dev/null +++ b/lib/ui/screens/artifact/artifact_details/widgets/_content.dart @@ -0,0 +1,97 @@ +part of '../artifact_details_screen.dart'; + +class _Content extends StatelessWidget { + const _Content({Key? key, required this.data}) : super(key: key); + final ArtifactData data; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.lg), + child: Column( + children: [ + Gap($styles.insets.xl), + if (data.culture.isNotEmpty) ...[ + Text( + data.culture.toUpperCase(), + style: $styles.text.titleFont.copyWith(color: $styles.colors.accent1), + ).animate().fade(delay: 150.ms, duration: 600.ms), + Gap($styles.insets.xs), + ], + Semantics( + header: true, + child: Text( + data.title, + textAlign: TextAlign.center, + style: $styles.text.h2.copyWith(color: $styles.colors.offWhite, height: 1.2), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ).animate().fade(delay: 250.ms, duration: 600.ms), + ), + Gap($styles.insets.lg), + Animate().toggle( + delay: 500.ms, + builder: (_, value, __) { + return CompassDivider(isExpanded: !value, duration: $styles.times.med); + }), + Gap($styles.insets.lg), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...[ + _InfoRow($strings.artifactDetailsLabelDate, data.date), + _InfoRow($strings.artifactDetailsLabelPeriod, data.period), + _InfoRow($strings.artifactDetailsLabelGeography, data.country), + _InfoRow($strings.artifactDetailsLabelMedium, data.medium), + _InfoRow($strings.artifactDetailsLabelDimension, data.dimension), + _InfoRow($strings.artifactDetailsLabelClassification, data.classification), + ] + .animate(interval: 100.ms) + .fade(delay: 600.ms, duration: $styles.times.med) + .slide(begin: Offset(0.2, 0), curve: Curves.easeOut), + ], + ), + Gap($styles.insets.md), + Text( + $strings.homeMenuAboutMet, + style: $styles.text.caption.copyWith(color: $styles.colors.accent2), + ), + Gap($styles.insets.offset), + ], + ), + ); + } +} + +class _InfoRow extends StatelessWidget { + const _InfoRow(this.label, this.value, {Key? key}) : super(key: key); + + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return ExcludeSemantics( + excluding: value.isEmpty, + child: MergeSemantics( + child: Padding( + padding: EdgeInsets.only(bottom: $styles.insets.sm), + child: Row(children: [ + Expanded( + child: Text( + label.toUpperCase(), + style: $styles.text.titleFont.copyWith(color: $styles.colors.accent2), + ), + ), + Expanded( + child: Text( + value.isEmpty ? '--' : value, + style: $styles.text.body.copyWith(color: $styles.colors.offWhite), + ), + ), + ]), + ), + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_details/widgets/_header.dart b/lib/ui/screens/artifact/artifact_details/widgets/_header.dart new file mode 100644 index 00000000..ffd12a4a --- /dev/null +++ b/lib/ui/screens/artifact/artifact_details/widgets/_header.dart @@ -0,0 +1,47 @@ +part of '../artifact_details_screen.dart'; + +class _Header extends StatelessWidget { + const _Header({Key? key, required this.data}) : super(key: key); + final ArtifactData data; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + BottomCenter( + child: Transform.translate( + offset: Offset(0, $styles.insets.xl - 1), + child: VtGradient( + [$styles.colors.greyStrong, $styles.colors.greyStrong.withOpacity(0)], + const [0, 1], + height: $styles.insets.xl, + ), + ), + ), + Container( + color: $styles.colors.black, + alignment: Alignment.center, + child: AppBtn.basic( + semanticLabel: $strings.artifactDetailsSemanticThumbnail, + onPressed: () => _handleImagePressed(context), + child: SafeArea( + bottom: false, + minimum: EdgeInsets.symmetric(vertical: $styles.insets.sm), + child: AppImage( + image: NetworkImage(data.image), + fit: BoxFit.cover, + alignment: Alignment.topCenter, + distractor: true, + scale: 1.0, + ), + ), + ), + ), + ], + ); + } + + void _handleImagePressed(BuildContext context) { + Navigator.push(context, CupertinoPageRoute(builder: (_) => FullscreenUrlImgViewer(urls: [data.image]))); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/artifact_search_screen.dart b/lib/ui/screens/artifact/artifact_search/artifact_search_screen.dart new file mode 100644 index 00000000..65716b8d --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/artifact_search_screen.dart @@ -0,0 +1,217 @@ +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/common/controls/simple_header.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/time_range_selector/expanding_time_range_selector.dart'; + +part 'widgets/_result_tile.dart'; +part 'widgets/_results_grid.dart'; +part 'widgets/_search_input.dart'; + +/// User can use this screen to search the MET server for an artifact by name or timeline. Artifacts results will +/// appear as images, which the user can click on to being up the details view for more information. +class ArtifactSearchScreen extends StatefulWidget with GetItStatefulWidgetMixin { + ArtifactSearchScreen({Key? key, required this.type}) : super(key: key); + final WonderType type; + + @override + State createState() => _ArtifactSearchScreenState(); +} + +class _ArtifactSearchScreenState extends State with GetItStateMixin { + List _searchResults = []; + List _filteredResults = []; + String _query = ''; + + late final WonderData wonder = wondersLogic.getData(widget.type); + late final PanelController panelController = PanelController(true); + late final SearchVizController vizController = SearchVizController( + _searchResults, + minYear: wondersLogic.timelineStartYear, + maxYear: wondersLogic.timelineEndYear, + ); + late double _startYear = wonder.artifactStartYr * 1.0, _endYear = wonder.artifactEndYr * 1.0; + + @override + void initState() { + _search(); + panelController.addListener(() { + AppHaptics.lightImpact(); + }); + super.initState(); + } + + void _handleSearchSubmitted(String query) { + _query = query; + _search(); + } + + void _handleTimelineChanged(double start, double end) { + _startYear = start; + _endYear = end; + _filter(); + } + + void _search() { + if (_query.isEmpty) { + _searchResults = wonder.searchData; + } else { + // whole word search on title and keywords. + // this is a somewhat naive search, but is sufficient for demoing the UI. + final RegExp q = RegExp('\\b${_query}s?\\b', caseSensitive: false); + _searchResults = wonder.searchData.where((o) => o.title.contains(q) || o.keywords.contains(q)).toList(); + } + vizController.value = _searchResults; + _filter(); + } + + void _filter() { + _filteredResults = _searchResults.where((o) => o.year >= _startYear && o.year <= _endYear).toList(); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + // tone down the orange just a bit: + vizController.color = Color.lerp($styles.colors.accent1, $styles.colors.black, 0.2)!; + Widget content = GestureDetector( + onTap: () => WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SimpleHeader($strings.artifactsSearchTitleBrowse, subtitle: wonder.title), + Gap($styles.insets.xs), + Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), + child: _SearchInput(onSubmit: _handleSearchSubmitted, wonder: wonder), + ), + Gap($styles.insets.sm), + Container( + color: $styles.colors.black, + padding: EdgeInsets.all($styles.insets.xs * 1.5), + child: _buildStatusText(context), + ), + Expanded( + child: RepaintBoundary( + child: _filteredResults.isEmpty + ? _buildEmptyIndicator(context) + : _ResultsGrid( + searchResults: _filteredResults, + onPressed: (o) => context.push(ScreenPaths.artifact(o.id.toString())), + ), + ), + ), + ], + ), + ); + + return Stack(children: [ + Positioned.fill(child: ColoredBox(color: $styles.colors.greyStrong, child: content)), + Positioned.fill( + child: RepaintBoundary( + child: ExpandingTimeRangeSelector( + wonder: wonder, + startYear: _startYear, + endYear: _endYear, + panelController: panelController, + vizController: vizController, + onChanged: _handleTimelineChanged, + ), + ), + ), + ]); + } + + Widget _buildStatusText(BuildContext context) { + final TextStyle statusStyle = $styles.text.body.copyWith(color: $styles.colors.accent1); + if (_searchResults.isEmpty) { + return Text( + $strings.artifactsSearchLabelNotFound, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: statusStyle, + textAlign: TextAlign.center, + ); + } + return MergeSemantics( + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Gap($styles.insets.sm), + Text( + StringUtils.supplant( + $strings.artifactsSearchLabelFound, + { + '{numFound}': _searchResults.length.toString(), + '{numResults}': _filteredResults.length.toString(), + }, + ), + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: statusStyle, + ), + AppBtn.basic( + semanticLabel: $strings.artifactsSearchButtonToggle, + onPressed: () => panelController.toggle(), + enableFeedback: false, // handled when panelController changes. + child: Text( + $strings.artifactsSearchSemanticTimeframe, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: statusStyle.copyWith(decoration: TextDecoration.underline), + ), + ), + Gap($styles.insets.sm), + ]), + ); + } + + Widget _buildEmptyIndicator(BuildContext context) { + final strings = $strings; + String text = + '${strings.artifactsSearchLabelAdjust} ${_searchResults.isEmpty ? strings.artifactsSearchLabelSearch : strings.artifactsSearchLabelTimeframe}'; + IconData icon = _searchResults.isEmpty ? Icons.search_outlined : Icons.edit_calendar_outlined; + Color color = $styles.colors.greyMedium; + Widget widget = Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Spacer(), + Icon(icon, size: $styles.insets.xl, color: color.withOpacity(0.5)), + Gap($styles.insets.xs), + Text(text, style: $styles.text.body.copyWith(color: color)), + Spacer( + flex: 3, + ), + ], + ); + if (_searchResults.isNotEmpty) { + widget = GestureDetector(child: widget, onTap: () => panelController.toggle()); + } + return widget; + } +} + +class PanelController extends ValueNotifier { + PanelController(bool value) : super(value); + void toggle() => value = !value; +} + +// this is basically a ValueNotifier, but it always notifies when the value is assigned, w/o checking equality. +class SearchVizController extends ChangeNotifier { + SearchVizController( + List value, { + required this.minYear, + required this.maxYear, + this.color = Colors.black, + }) : _value = value; + + Color color; + final int minYear; + final int maxYear; + + List _value; + List get value => _value; + set value(List value) { + _value = value; + notifyListeners(); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/time_range_selector/expanding_time_range_selector.dart b/lib/ui/screens/artifact/artifact_search/time_range_selector/expanding_time_range_selector.dart new file mode 100644 index 00000000..fe1f058e --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/time_range_selector/expanding_time_range_selector.dart @@ -0,0 +1,268 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/cards/opening_card.dart'; +import 'package:wonders/ui/common/wonders_timeline_builder.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/artifact_search_screen.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/time_range_selector/range_selector.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/time_range_selector/time_range_painter.dart'; + +// Expandable timerange selector component that further refines Artifact Search based on date range. +class ExpandingTimeRangeSelector extends StatefulWidget { + const ExpandingTimeRangeSelector({ + Key? key, + required this.wonder, + required this.startYear, + required this.endYear, + required this.onChanged, + required this.panelController, + required this.vizController, + }) : super(key: key); + final WonderData wonder; + final double startYear; + final double endYear; + final void Function(double start, double end) onChanged; + final PanelController panelController; + final SearchVizController vizController; + + @override + State createState() => _ExpandingTimeRangeSelectorState(); +} + +class _ExpandingTimeRangeSelectorState extends State { + late final TimeRangePainter _painter; + + @override + void initState() { + widget.panelController.addListener(() => setState(() {})); + _painter = TimeRangePainter(controller: widget.vizController); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final double pad = $styles.insets.sm; + final bool isOpen = widget.panelController.value; + double safeBottom = max($styles.insets.md, MediaQuery.of(context).padding.bottom); + + return LayoutBuilder(builder: (_, constraints) { + return BottomCenter( + child: AnimatedPadding( + duration: $styles.times.fast, + padding: isOpen ? EdgeInsets.zero : EdgeInsets.only(bottom: safeBottom + $styles.insets.xxs), + child: AppBtn.basic( + onPressed: () => widget.panelController.toggle(), + semanticLabel: '', + pressEffect: false, + child: OpeningCard( + isOpen: isOpen, + padding: EdgeInsets.symmetric(horizontal: pad, vertical: $styles.insets.xs), + background: Container( + decoration: BoxDecoration( + color: $styles.colors.black.withOpacity(0.85), + borderRadius: BorderRadius.circular($styles.corners.md), + boxShadow: [ + BoxShadow( + color: $styles.colors.black.withOpacity(0.66), + offset: Offset(0, 4), + blurRadius: 4, + ) + ], + ), + ), + closedBuilder: (_) => _ClosedTimeRange(startYear: widget.startYear, endYear: widget.endYear), + openBuilder: (_) => SizedBox( + width: constraints.maxWidth - pad * 2, + child: _OpenedTimeRange( + startYear: widget.startYear, + endYear: widget.endYear, + onChange: widget.onChanged, + wonder: widget.wonder, + painter: _painter, + onClose: widget.panelController.toggle, + ), + ), + ), + ), + ), + ); + }); + } +} + +class _ClosedTimeRange extends StatelessWidget { + const _ClosedTimeRange({ + Key? key, + required this.startYear, + required this.endYear, + }) : super(key: key); + final double startYear, endYear; + + @override + Widget build(BuildContext context) { + final String text = '${StringUtils.formatYr(startYear.round())} - ${StringUtils.formatYr(endYear.round())}'; + + return Padding( + padding: EdgeInsets.symmetric(vertical: $styles.insets.xs), + child: Row( + children: [ + Text(text, style: $styles.text.titleFont.copyWith(color: $styles.colors.offWhite)), + Gap($styles.insets.xs), + Icon(Icons.edit_calendar_outlined, color: $styles.colors.accent1, size: $styles.insets.md), + ], + ), + ); + } +} + +class _OpenedTimeRange extends StatelessWidget { + const _OpenedTimeRange({ + Key? key, + required this.onChange, + required this.startYear, + required this.endYear, + required this.wonder, + required this.painter, + required this.onClose, + }) : super(key: key); + final double startYear; + final double endYear; + final void Function(double start, double end) onChange; + final WonderData wonder; + final TimeRangePainter painter; + final void Function() onClose; + + List _buildChineseDateLayout(TextStyle headingTextStyle, TextStyle captionTextStyle, int startYr, int endYr) { + return [ + Text(StringUtils.getYrSuffix(startYr), style: captionTextStyle), + Gap($styles.insets.xxs), + Text(startYr.abs().toString(), style: headingTextStyle), + Text($strings.year, style: captionTextStyle), + Gap($styles.insets.xs), + Text('~', style: captionTextStyle), + Gap($styles.insets.xs), + Text(StringUtils.getYrSuffix(endYr.round()), style: captionTextStyle), + Gap($styles.insets.xxs), + Text(endYr.abs().toString(), style: headingTextStyle), + Text($strings.year, style: captionTextStyle), + ]; + } + + List _buildDefaultDateLayout(TextStyle headingTextStyle, TextStyle captionTextStyle, int startYr, int endYr) { + return [ + Text(startYr.abs().toString(), style: headingTextStyle), + Gap($styles.insets.xxs), + Text(StringUtils.getYrSuffix(startYr), style: captionTextStyle), + Gap($styles.insets.xs), + Text('—', style: captionTextStyle), + Gap($styles.insets.xs), + Text(endYr.abs().toString(), style: headingTextStyle), + Gap($styles.insets.xxs), + Text(StringUtils.getYrSuffix(endYr.round()), style: captionTextStyle), + ]; + } + + @override + Widget build(BuildContext context) { + double safeBottom = max($styles.insets.sm, MediaQuery.of(context).padding.bottom); + List timelineGrid = List.generate(5, (_) => Container(width: 1, color: $styles.colors.black)); + + final headingTextStyle = $styles.text.title1.copyWith(color: $styles.colors.offWhite, fontSize: 18); + final captionTextStyle = $styles.text.bodySmall.copyWith(color: $styles.colors.greyMedium); + + final startYr = startYear.round(), endYr = endYear.round(); + + return Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Gap($styles.insets.xl), + Spacer(), + if (localeLogic.strings.localeName == 'zh') ...{ + ..._buildChineseDateLayout(headingTextStyle, captionTextStyle, startYr, endYr), + } else ...{ + ..._buildDefaultDateLayout(headingTextStyle, captionTextStyle, startYr, endYr), + }, + Spacer(), + SizedBox( + width: $styles.insets.xl, + child: AppBtn.from( + onPressed: onClose, + semanticLabel: $strings.expandingTimeSelectorSemanticSelector, + enableFeedback: false, // handled when panelController changes. + icon: Icons.close, + iconSize: 20, + padding: EdgeInsets.symmetric(vertical: $styles.insets.xxs), + bgColor: Colors.transparent, + ), + ), + ], + ), + + Gap($styles.insets.xs * 1.5), + + // Timeframe slider + SizedBox( + height: $styles.insets.lg * 2, + child: Stack(children: [ + // grid lines: + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular($styles.corners.md), + color: Color.lerp($styles.colors.black, Colors.black, 0.2), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: timelineGrid, + ), + ), + + // results visualization: + Positioned.fill( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: RangeSelector.handleWidth), + child: RepaintBoundary( + child: CustomPaint(painter: painter), + ), + ), + ), + + // wonder minimap: + Positioned.fill( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: RangeSelector.handleWidth, vertical: 12), + child: WondersTimelineBuilder( + crossAxisGap: 6, + minSize: 16, + selectedWonders: [wonder.type], + timelineBuilder: (_, __, sel) => Container( + decoration: BoxDecoration( + color: $styles.colors.offWhite.withOpacity(sel ? 0.75 : 0.25), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + ), + ), + + // Time slider itself + Positioned.fill( + child: RangeSelector( + key: ValueKey('RangeSelectorIsWonderTime'), + min: wondersLogic.timelineStartYear * 1.0, + max: wondersLogic.timelineEndYear * 1.0, + minDelta: 500, + start: startYear, + end: endYear, + onUpdated: onChange, + ), + ), + ]), + ), + + Gap(safeBottom), + ], + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/time_range_selector/range_selector.dart b/lib/ui/screens/artifact/artifact_search/time_range_selector/range_selector.dart new file mode 100644 index 00000000..b8555f1f --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/time_range_selector/range_selector.dart @@ -0,0 +1,143 @@ +import 'package:flutter/gestures.dart'; +import 'package:wonders/common_libs.dart'; + +// Expandable timerange selector component that further refines Artifact Search based on date range. +class RangeSelector extends StatefulWidget { + static const double handleWidth = 20; + + const RangeSelector({ + Key? key, + required this.start, + required this.end, + required this.min, + required this.max, + this.minDelta = 0, + this.isLocked = false, + this.onUpdated, + this.onChanged, + }) : super(key: key); + final double start; + final double end; + final double min; + final double max; + final double minDelta; + final bool isLocked; + final void Function(double start, double end)? onUpdated; + final void Function(double start, double end)? onChanged; + + @override + State createState() => _RangeSelectorState(); +} + +class _RangeSelectorState extends State { + // drag values: + double _initStart = 0, _initEnd = 0, _initX = 0; + + // shortcuts to make calculations less fussy: + double get _start => widget.start; + double get _end => widget.end; + double get _min => widget.min; + double get _max => widget.max; + double get _delta => widget.max - widget.min; + + void _handleStartDrag(DragDownDetails d) { + if (widget.isLocked) return; + _initStart = _start; + _initEnd = _end; + _initX = d.localPosition.dx; + } + + void _handleLeftDrag(DragUpdateDetails d, double width) { + if (widget.isLocked) return; + double value = _initStart + (d.localPosition.dx - _initX) / width * _delta; + value = max(_min, min(_end - widget.minDelta, value)); + widget.onUpdated?.call(value, _end); + } + + void _handleMidDrag(DragUpdateDetails d, double width) { + if (widget.isLocked) return; + double delta = (d.localPosition.dx - _initX) / width * _delta; + delta = max(_min - _initStart, min(_max - _initEnd, delta)); + widget.onUpdated?.call(_initStart + delta, _initEnd + delta); + } + + void _handleRightDrag(DragUpdateDetails d, double width) { + if (widget.isLocked) return; + double value = _initEnd + (d.localPosition.dx - _initX) / width * _delta; + value = max(_start + widget.minDelta, min(_max, value)); + widget.onUpdated?.call(_start, value); + } + + void _handleEndDrag(DragEndDetails d, double width) { + if (widget.isLocked) return; + widget.onChanged?.call(_start, _end); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, constraints) { + double dragWidth = constraints.maxWidth - RangeSelector.handleWidth * 2; + + return Row( + children: [ + Gap(dragWidth * (_start - _min) / _delta), + _getHandle(dragWidth: dragWidth), + Expanded( + child: _getDragDetector( + onUpdate: _handleMidDrag, + dragWidth: dragWidth, + child: Container( + decoration: BoxDecoration( + color: $styles.colors.offWhite.withOpacity(0), + border: Border.symmetric( + horizontal: BorderSide(color: $styles.colors.white.withOpacity(0.75), width: 2), + ), + ), + ), + ), + ), + _getHandle(dragWidth: dragWidth, isRight: true), + Gap(dragWidth * (1 - (_end - _min) / _delta)), + ], + ); + }); + } + + Widget _getHandle({required double dragWidth, bool isRight = false}) { + return _getDragDetector( + onUpdate: isRight ? _handleRightDrag : _handleLeftDrag, + dragWidth: dragWidth, + child: Transform.scale( + scaleX: isRight ? 1 : -1, + child: Container( + alignment: Alignment.center, + width: RangeSelector.handleWidth, + decoration: BoxDecoration( + color: $styles.colors.white.withOpacity(0.75), + borderRadius: BorderRadius.only( + topRight: Radius.circular($styles.corners.md), + bottomRight: Radius.circular($styles.corners.md), + ), + ), + child: Icon(Icons.chevron_right, color: $styles.colors.greyStrong, size: RangeSelector.handleWidth), + ), + ), + ); + } + + GestureDetector _getDragDetector({ + required Function(DragUpdateDetails, double) onUpdate, + required double dragWidth, + required Widget child, + }) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onHorizontalDragDown: _handleStartDrag, + onHorizontalDragUpdate: (d) => onUpdate(d, dragWidth), + onHorizontalDragEnd: (d) => _handleEndDrag(d, dragWidth), + dragStartBehavior: DragStartBehavior.down, + onTap: () {}, // block parent + child: child, + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/time_range_selector/time_range_painter.dart b/lib/ui/screens/artifact/artifact_search/time_range_selector/time_range_painter.dart new file mode 100644 index 00000000..b3227c2f --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/time_range_selector/time_range_painter.dart @@ -0,0 +1,50 @@ +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonders_data/search/search_data.dart'; +import 'package:wonders/ui/screens/artifact/artifact_search/artifact_search_screen.dart'; + +class TimeRangePainter extends CustomPainter { + final SearchVizController controller; + + TimeRangePainter({ + required this.controller, + }) : super(repaint: controller); + + @override + void paint(Canvas canvas, Size size) { + List results = controller.value; + int l = results.length; + if (l == 0) return; + + Paint fill = Paint()..color = controller.color.withOpacity(0.25); + var positions = Float32List(12 * l); + + double height = size.height, width = size.width; + int minYr = controller.minYear, delta = controller.maxYear - minYr; + for (int i = 0; i < l; i++) { + SearchData o = results[i]; + double x = width * (o.year - minYr) / delta; + var j = i * 12; + positions[j] = x - 1; + positions[j + 1] = 0; + positions[j + 2] = x + 1; + positions[j + 3] = 0; + positions[j + 4] = x - 1; + positions[j + 5] = height; + + positions[j + 6] = x + 1; + positions[j + 7] = 0; + positions[j + 8] = x - 1; + positions[j + 9] = height; + positions[j + 10] = x + 1; + positions[j + 11] = height; + } + var vertices = Vertices.raw(VertexMode.triangles, positions); + canvas.drawVertices(vertices, BlendMode.src, fill); + } + + @override + bool shouldRepaint(TimeRangePainter oldDelegate) => true; +} diff --git a/lib/ui/screens/artifact/artifact_search/widgets/_result_tile.dart b/lib/ui/screens/artifact/artifact_search/widgets/_result_tile.dart new file mode 100644 index 00000000..29c35ce7 --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/widgets/_result_tile.dart @@ -0,0 +1,34 @@ +part of '../artifact_search_screen.dart'; + +class _ResultTile extends StatelessWidget { + const _ResultTile({Key? key, required this.onPressed, required this.data}) : super(key: key); + + final void Function(SearchData data) onPressed; + final SearchData data; + + @override + Widget build(BuildContext context) { + final Widget content = Container( + color: $styles.colors.black, + width: double.infinity, + child: AppImage( + key: ValueKey(data.id), + image: NetworkImage(data.imageUrl), + fit: BoxFit.cover, + scale: 0.5, + ), + ); + + return AspectRatio( + aspectRatio: (data.aspectRatio == 0) ? (data.id % 10) / 15 + 0.6 : max(0.5, data.aspectRatio), + child: ClipRRect( + borderRadius: BorderRadius.circular($styles.insets.xs), + child: AppBtn.basic( + semanticLabel: data.title, + onPressed: () => onPressed(data), + child: content, + ), + ), + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/widgets/_results_grid.dart b/lib/ui/screens/artifact/artifact_search/widgets/_results_grid.dart new file mode 100644 index 00000000..d2c89da2 --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/widgets/_results_grid.dart @@ -0,0 +1,61 @@ +part of '../artifact_search_screen.dart'; + +/// Staggered Masonry styled grid for displaying two columns of different aspect-ratio images. +class _ResultsGrid extends StatelessWidget { + const _ResultsGrid({Key? key, required this.searchResults, required this.onPressed}) : super(key: key); + final void Function(SearchData) onPressed; + final List searchResults; + + @override + Widget build(BuildContext context) { + return ScrollDecorator.shadow( + builder: (controller) => CustomScrollView( + controller: controller, + scrollBehavior: const ScrollBehavior().copyWith(scrollbars: false), + clipBehavior: Clip.hardEdge, + slivers: [ + SliverToBoxAdapter(child: _buildLanguageMessage(context)), + SliverPadding( + padding: EdgeInsets.all($styles.insets.sm).copyWith(bottom: $styles.insets.offset * 1.5), + sliver: SliverMasonryGrid.count( + crossAxisCount: 2, + mainAxisSpacing: $styles.insets.sm, + crossAxisSpacing: $styles.insets.sm, + childCount: searchResults.length, + itemBuilder: (context, index) => _ResultTile(onPressed: onPressed, data: searchResults[index]), + ), + ), + ], + ), + ); + } + + Widget _buildLanguageMessage(BuildContext context) { + bool isEnglish = localeLogic.strings.localeName == 'en'; + return ValueListenableBuilder( + valueListenable: settingsLogic.hasDismissedSearchMessage, + builder: (_, value, __) { + if (isEnglish || value) return SizedBox(); + return AppBtn.basic( + onPressed: () => settingsLogic.hasDismissedSearchMessage.value = true, + semanticLabel: $strings.resultsSemanticDismiss, + child: Container( + color: $styles.colors.offWhite.withOpacity(0.1), + padding: EdgeInsets.all($styles.insets.sm), + child: Row( + children: [ + Flexible( + child: Text($strings.resultsPopupEnglishContent), + ), + Icon( + Icons.close, + size: $styles.insets.md, + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/screens/artifact/artifact_search/widgets/_search_input.dart b/lib/ui/screens/artifact/artifact_search/widgets/_search_input.dart new file mode 100644 index 00000000..754a8d0a --- /dev/null +++ b/lib/ui/screens/artifact/artifact_search/widgets/_search_input.dart @@ -0,0 +1,157 @@ +part of '../artifact_search_screen.dart'; + +/// Autopopulating textfield used for searching for Artifacts by name. +class _SearchInput extends StatelessWidget { + const _SearchInput({Key? key, required this.onSubmit, required this.wonder}) : super(key: key); + final void Function(String) onSubmit; + final WonderData wonder; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (ctx, constraints) => Autocomplete( + displayStringForOption: (data) => data, + onSelected: onSubmit, + optionsBuilder: _getSuggestions, + optionsViewBuilder: (context, onSelected, results) => + _buildSuggestionsView(context, onSelected, results, constraints), + fieldViewBuilder: _buildInput, + ), + ); + } + + Iterable _getSuggestions(TextEditingValue textEditingValue) { + if (textEditingValue.text == '') return wonder.searchSuggestions.getRange(0, 10); + + return wonder.searchSuggestions.where((str) { + return str.startsWith(textEditingValue.text.toLowerCase()); + }).toList() + ..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); + } + + Widget _buildSuggestionsView(BuildContext context, onSelected, Iterable results, BoxConstraints constraints) { + List items = results.map((str) => _buildSuggestion(context, str, () => onSelected(str))).toList(); + items.insert(0, _buildSuggestionTitle(context)); + + return TopLeft( + child: Container( + margin: EdgeInsets.only(top: $styles.insets.xxs), + width: constraints.maxWidth, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: $styles.colors.black.withOpacity(0.25), + blurRadius: 4, + offset: Offset(0, 4), + ), + ], + ), + child: Container( + padding: EdgeInsets.all($styles.insets.xs), + decoration: BoxDecoration( + color: $styles.colors.offWhite.withOpacity(0.92), + borderRadius: BorderRadius.circular($styles.insets.xs), + ), + child: ConstrainedBox( + constraints: BoxConstraints(maxHeight: 200), + child: ListView( + padding: EdgeInsets.all($styles.insets.xs), + shrinkWrap: true, + children: items, + ), + ), + ), + ), + ); + } + + Widget _buildSuggestionTitle(BuildContext context) { + return Container( + padding: EdgeInsets.all($styles.insets.xs).copyWith(top: 0), + margin: EdgeInsets.only(bottom: $styles.insets.xxs), + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: $styles.colors.greyStrong.withOpacity(0.1)))), + child: CenterLeft( + child: Text( + $strings.searchInputTitleSuggestions.toUpperCase(), + overflow: TextOverflow.ellipsis, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: $styles.text.title2.copyWith(color: $styles.colors.black), + ), + ), + ); + } + + Widget _buildSuggestion(BuildContext context, String suggestion, VoidCallback onPressed) { + return AppBtn.basic( + semanticLabel: suggestion, + onPressed: onPressed, + child: Padding( + padding: EdgeInsets.all($styles.insets.xs), + child: CenterLeft( + child: Text( + suggestion, + overflow: TextOverflow.ellipsis, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: $styles.text.bodySmall.copyWith(color: $styles.colors.greyStrong), + ), + ), + ), + ); + } + + Widget _buildInput(BuildContext context, TextEditingController textController, FocusNode focusNode, _) { + Color captionColor = $styles.colors.caption; + return Container( + height: $styles.insets.xl, + decoration: BoxDecoration( + color: $styles.colors.offWhite, + borderRadius: BorderRadius.circular($styles.insets.xs), + ), + child: Row(children: [ + Gap($styles.insets.xs * 1.5), + Icon(Icons.search, color: $styles.colors.caption), + Expanded( + child: TextField( + onSubmitted: onSubmit, + controller: textController, + focusNode: focusNode, + style: TextStyle(color: captionColor), + textAlignVertical: TextAlignVertical.top, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.all($styles.insets.xs), + labelStyle: TextStyle(color: captionColor), + hintStyle: TextStyle(color: captionColor.withOpacity(0.5)), + prefixStyle: TextStyle(color: captionColor), + focusedBorder: OutlineInputBorder(borderSide: BorderSide.none), + enabledBorder: UnderlineInputBorder(borderSide: BorderSide.none), + hintText: $strings.searchInputHintSearch, + ), + ), + ), + Gap($styles.insets.xs), + ValueListenableBuilder( + valueListenable: textController, + builder: (_, value, __) => Visibility( + visible: textController.value.text.isNotEmpty, + child: Padding( + padding: EdgeInsets.only(right: $styles.insets.xs), + child: CircleIconBtn( + bgColor: $styles.colors.caption, + color: $styles.colors.white, + icon: AppIcons.close, + semanticLabel: $strings.searchInputSemanticClear, + size: $styles.insets.lg, + iconSize: $styles.insets.sm, + onPressed: () { + textController.clear(); + onSubmit(''); + }, + ), + ), + ), + ) + ]), + ); + } +} diff --git a/lib/ui/screens/collectible_found/collectible_found_screen.dart b/lib/ui/screens/collectible_found/collectible_found_screen.dart new file mode 100644 index 00000000..387951fa --- /dev/null +++ b/lib/ui/screens/collectible_found/collectible_found_screen.dart @@ -0,0 +1,167 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/collectible_data.dart'; +import 'package:particle_field/particle_field.dart'; + +part 'widgets/_animated_ribbon.dart'; +part 'widgets/_celebration_particles.dart'; + +class CollectibleFoundScreen extends StatelessWidget { + // CollectibleItem passes in a (theoretically) pre-loaded imageProvider. + // we could check for load completion, and hold after introT, but that shouldn't be necessary in a real-world scenario. + const CollectibleFoundScreen({required this.collectible, required this.imageProvider, Key? key}) : super(key: key); + + final CollectibleData collectible; + final ImageProvider imageProvider; + + @override + Widget build(BuildContext context) { + return RepaintBoundary( + child: _buildIntro(context).animate().swap( + delay: $styles.times.fast * 3.5, + builder: (_, __) => _buildDetail(context), + ), + ); + } + + Widget _buildIntro(BuildContext context) { + Duration t = $styles.times.fast; + return Stack(children: [ + Animate().custom(duration: t * 5, builder: (context, ratio, _) => _buildGradient(context, ratio, 0)), + + // icon is handled by Hero initially, then scales slowly: + Center( + child: FractionallySizedBox( + widthFactor: 0.33, + heightFactor: 0.33, + child: Hero( + tag: 'collectible_icon_${collectible.id}', + child: Image( + image: collectible.icon, + fit: BoxFit.contain, + ), + ), + ).animate().scale(begin: 1.5, end: 3, curve: Curves.easeInExpo, delay: t, duration: t * 3).fadeOut(), + ) + ]); + } + + Widget _buildDetail(BuildContext context) { + Duration t = $styles.times.fast; + return Stack(key: ValueKey('detail'), children: [ + Animate().custom(duration: t, builder: (context, ratio, _) => _buildGradient(context, 1, ratio)), + _CelebrationParticles(fadeMs: (t * 6).inMilliseconds), + SafeArea( + child: Column(children: [ + Spacer(flex: 5), + Flexible( + flex: 18, + child: Center(child: Hero(tag: 'collectible_image_${collectible.id}', child: _buildImage(context))), + ), + Spacer(flex: 2), + _buildRibbon(context), + Spacer(flex: 2), + _buildTitle(context, collectible.title, $styles.text.h2, $styles.colors.offWhite, t * 1.5), + Gap($styles.insets.xs), + _buildTitle(context, collectible.subtitle.toUpperCase(), $styles.text.title2, $styles.colors.accent1, t * 2), + Spacer(flex: 2), + _buildCollectionButton(context), + ]), + ), + BackBtn.close().safe().animate().fade(delay: t * 4, duration: t * 2), + ]); + } + + Widget _buildGradient(BuildContext context, double ratioIn, double ratioOut) { + // used by both intro and detail animations to ensure they share a mid-point. + ratioIn = Curves.easeOutExpo.transform(ratioIn); + final double opacity = 1.0 * (ratioIn * 0.8 + ratioOut * 0.2); + final Color light = $styles.colors.offWhite; + final Color dark = $styles.colors.black; + + // final state is a solid fill, so optimize that case: + if (ratioOut == 1) return Container(color: dark.withOpacity(opacity)); + + return Container( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [Color.lerp(light, dark, ratioOut)!.withOpacity(opacity), dark.withOpacity(opacity)], + stops: [0.2, min(1, 0.25 + ratioIn * 0.5 + ratioOut * 0.5)], + ), + ), + ); + } + + Widget _buildImage(BuildContext context) { + Duration t = $styles.times.fast; + // build an image with animated shadows and scaling + return AppImage(image: imageProvider, scale: 1.0) + .animate() + .custom( + duration: t * 6, + builder: (_, ratio, child) => Container( + padding: EdgeInsets.all($styles.insets.xxs), + margin: EdgeInsets.symmetric(horizontal: $styles.insets.xl), + decoration: BoxDecoration(color: $styles.colors.offWhite, boxShadow: [ + BoxShadow( + color: $styles.colors.accent1.withOpacity(ratio * 0.75), + blurRadius: $styles.insets.xl * 2, + ), + BoxShadow( + color: $styles.colors.black.withOpacity(ratio * 0.75), + offset: Offset(0, $styles.insets.xxs), + blurRadius: $styles.insets.sm, + ), + ]), + child: child, + ), + ) + .scale(begin: 0.3, duration: t * 2, curve: Curves.easeOutExpo, alignment: Alignment(0, 0.7)); + } + + Widget _buildRibbon(BuildContext context) { + Duration t = $styles.times.fast; + return _AnimatedRibbon($strings.collectibleFoundTitleArtifactDiscovered.toUpperCase()) + .animate() + .scale(begin: 0.3, duration: t * 2, curve: Curves.easeOutExpo, alignment: Alignment(0, -1)); + } + + Widget _buildTitle(BuildContext context, String text, TextStyle style, Color color, Duration delay) { + Duration t = $styles.times.fast; + // because this is a performance-sensitive screen, we are fading in the text by adjusting color: + return Container( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.lg), + child: Animate().custom( + delay: delay, + duration: t * 2, + builder: (_, m, __) => Text( + text, + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: style.copyWith(color: color.withOpacity(m)), + ), + ), + ); + } + + Widget _buildCollectionButton(BuildContext context) { + Duration t = $styles.times.fast; + return Container( + padding: EdgeInsets.all($styles.insets.lg), + child: AppBtn.from( + text: $strings.collectibleFoundButtonViewCollection, + isSecondary: true, + expand: true, + onPressed: () => _handleViewCollectionPressed(context), + ), + ) + .animate() + .show(delay: t * 4) + .move(begin: Offset(0, $styles.insets.md), duration: t * 3, curve: Curves.easeOutCubic); + } + + void _handleViewCollectionPressed(BuildContext context) { + Navigator.pop(context); + context.push(ScreenPaths.collection(collectible.id)); + } +} diff --git a/lib/ui/screens/collectible_found/widgets/_animated_ribbon.dart b/lib/ui/screens/collectible_found/widgets/_animated_ribbon.dart new file mode 100644 index 00000000..4feed8ab --- /dev/null +++ b/lib/ui/screens/collectible_found/widgets/_animated_ribbon.dart @@ -0,0 +1,44 @@ +part of '../collectible_found_screen.dart'; + +class _AnimatedRibbon extends StatelessWidget { + const _AnimatedRibbon(this.text, {Key? key}) : super(key: key); + + final String text; + static const double height = 48; + + @override + Widget build(BuildContext context) { + return Stack(children: [ + Positioned.fill( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildEnd(context, false), + Spacer(), + _buildEnd(context, true), + ], + ), + ), + Container( + height: height, + color: $styles.colors.accent1, + padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), + margin: EdgeInsets.only(bottom: 10), + // this aligns the text vertically, without expanding the container: + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [Text(text, textAlign: TextAlign.center, style: $styles.text.title1)], + ), + ), + ]); + } + + Widget _buildEnd(BuildContext context, bool flip) { + Widget end = Image.asset(ImagePaths.ribbonEnd, height: height); + if (flip) end = Transform.scale(scaleX: -1, child: end); + double m = flip ? 1 : -1; + return end + .animate() + .move(begin: Offset(m * 8, 2), end: Offset(m * 32, 10), duration: 400.ms, curve: Curves.easeOut); + } +} diff --git a/lib/ui/screens/collectible_found/widgets/_celebration_particles.dart b/lib/ui/screens/collectible_found/widgets/_celebration_particles.dart new file mode 100644 index 00000000..909675a0 --- /dev/null +++ b/lib/ui/screens/collectible_found/widgets/_celebration_particles.dart @@ -0,0 +1,59 @@ +part of '../collectible_found_screen.dart'; + +class _CelebrationParticles extends StatelessWidget { + const _CelebrationParticles({Key? key, this.fadeMs = 1000}) : super(key: key); + + final int fadeMs; + + @override + Widget build(BuildContext context) { + final Color color = $styles.colors.accent1; + int particleCount = 1200; + + return Positioned.fill( + child: RepaintBoundary( + child: ParticleField( + blendMode: BlendMode.dstIn, + spriteSheet: SpriteSheet( + image: AssetImage(ImagePaths.particle), + frameWidth: 21, + scale: 0.75, + ), + onTick: (controller, elapsed, size) { + List particles = controller.particles; + + // calculate base distance from center & velocity based on width/height: + final double d = min(size.width, size.height) * 0.3; + final double v = d * 0.08; + + // calculate an opacity multiplier based on time elapsed (ie. fade out): + controller.opacity = Curves.easeOutExpo.transform(max(0, 1 - elapsed.inMilliseconds / fadeMs)); + if (controller.opacity == 0) return; + + // add new particles, reducing the number added each tick: + int addCount = particleCount ~/ 30; + particleCount -= addCount; + while (--addCount > 0) { + final double angle = rnd.getRad(); + particles.add(Particle( + // adding random variation makes it more visually interesting: + x: cos(angle) * d * rnd(0.8, 1), + y: sin(angle) * d * rnd(0.8, 1), + vx: cos(angle) * v * rnd(0.5, 1.5), + vy: sin(angle) * v * rnd(0.5, 1.5), + color: color.withOpacity(rnd(0.5, 1)), + )); + } + + // update existing particles & remove old ones: + for (int i = particles.length - 1; i >= 0; i--) { + final Particle o = particles[i]; + o.update(frame: o.age ~/ 3); + if (o.age > 40) particles.removeAt(i); + } + }, + ), + ), + ); + } +} diff --git a/lib/ui/screens/collection/collection_screen.dart b/lib/ui/screens/collection/collection_screen.dart new file mode 100644 index 00000000..05faf49a --- /dev/null +++ b/lib/ui/screens/collection/collection_screen.dart @@ -0,0 +1,101 @@ +import 'dart:async'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/collectibles_logic.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/collectible_data.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/controls/simple_header.dart'; +import 'package:wonders/ui/common/gradient_container.dart'; +import 'package:wonders/ui/common/modals/app_modals.dart'; + +part 'widgets/_collection_tile.dart'; +part 'widgets/_newly_discovered_row.dart'; +part 'widgets/_collection_list.dart'; +part 'widgets/_collection_footer.dart'; + +class CollectionScreen extends StatefulWidget with GetItStatefulWidgetMixin { + CollectionScreen({required this.fromId, Key? key}) : super(key: key); + + final String fromId; + + @override + State createState() => _CollectionScreenState(); +} + +class _CollectionScreenState extends State with GetItStateMixin { + Map _states = collectiblesLogic.statesById.value; + GlobalKey? _scrollKey; + + WonderType? get scrollTargetWonder { + String? id = widget.fromId; + if (_states[id] != CollectibleState.discovered) { + id = _states.keys.firstWhereOrNull((id) => _states[id] == CollectibleState.discovered); + } + return collectiblesLogic.fromId(id)?.wonder; + } + + @override + void initState() { + super.initState(); + if (widget.fromId.isNotEmpty && _states[widget.fromId] == CollectibleState.discovered) { + scheduleMicrotask(() => _scrollToTarget(false)); + } + } + + void _scrollToTarget([bool animate = true]) { + if (_scrollKey != null) { + Scrollable.ensureVisible(_scrollKey!.currentContext!, alignment: 0.15, duration: animate ? 300.ms : 0.ms); + } + } + + void _showDetails(BuildContext context, CollectibleData collectible) { + context.push(ScreenPaths.artifact(collectible.artifactId)); + Future.delayed(300.ms).then((_) => collectiblesLogic.updateState(collectible.id, CollectibleState.explored)); + } + + void _handleReset() async { + String msg = $strings.collectionPopupResetConfirm; + final result = await showModal(context, child: OkCancelModal(msg: msg)); + if (result == true) { + collectiblesLogic.reset(); + } + } + + @override + Widget build(BuildContext context) { + _states = watchX((CollectiblesLogic o) => o.statesById); + int discovered = 0, explored = 0, total = collectiblesLogic.all.length; + _states.forEach((_, state) { + if (state == CollectibleState.discovered) discovered++; + if (state == CollectibleState.explored) explored++; + }); + + WonderType? scrollWonder = scrollTargetWonder; + if (scrollWonder != null) _scrollKey = GlobalKey(); + + return ColoredBox( + color: $styles.colors.greyStrong, + child: Stack(children: [ + Positioned.fill( + child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + SimpleHeader($strings.collectionTitleCollection), + _NewlyDiscoveredRow(count: discovered, onPressed: _scrollToTarget), + _CollectionList( + states: _states, + fromId: widget.fromId, + scrollKey: _scrollKey, + scrollWonder: scrollWonder, + onPressed: (o) => _showDetails(context, o), + onReset: discovered + explored > 0 ? _handleReset : null, + ), + ]), + ), + Positioned.fill( + top: null, + child: _CollectionFooter(count: discovered + explored, total: total), + ), + ]), + ); + } +} diff --git a/lib/ui/screens/collection/widgets/_collection_footer.dart b/lib/ui/screens/collection/widgets/_collection_footer.dart new file mode 100644 index 00000000..43e90d19 --- /dev/null +++ b/lib/ui/screens/collection/widgets/_collection_footer.dart @@ -0,0 +1,83 @@ +part of '../collection_screen.dart'; + +@immutable +class _CollectionFooter extends StatelessWidget { + const _CollectionFooter({Key? key, required this.count, required this.total}) : super(key: key); + + final int count; + final int total; + + @override + Widget build(BuildContext context) { + return Stack(children: [ + Transform.translate( + offset: Offset(0, -$styles.insets.xl), + child: VtGradient( + [$styles.colors.greyStrong.withOpacity(0), $styles.colors.greyStrong], + const [0, 1], + height: $styles.insets.xl, + ), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md, vertical: $styles.insets.sm), + color: $styles.colors.greyStrong, + child: SafeArea( + top: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildProgressRow(context), + Gap($styles.insets.sm), + _buildProgressBar(context), + Gap($styles.insets.sm), + ], + ), + ), + ) + ]); + } + + Widget _buildProgressRow(BuildContext context) { + return Row(children: [ + Text( + StringUtils.supplant( + $strings.collectionLabelDiscovered, + {'{percentage}': (count / total * 100).round().toString()}, + ), + style: $styles.text.body.copyWith(color: $styles.colors.accent1), + ), + Spacer(), + Text( + StringUtils.supplant( + $strings.collectionLabelCount, + {'{count}': count.toString(), '{total}': total.toString()}, + ), + style: $styles.text.body.copyWith(color: $styles.colors.offWhite), + ) + ]); + } + + Widget _buildProgressBar(BuildContext context) { + return RepaintBoundary( + child: Container( + height: $styles.insets.xs, + decoration: BoxDecoration( + color: $styles.colors.white.withOpacity(0.25), + borderRadius: BorderRadius.circular(1000), + ), + child: Container( + decoration: BoxDecoration( + color: $styles.colors.accent1, + borderRadius: BorderRadius.circular(1000), + ), + ).animate().fade(duration: 1500.ms, curve: Curves.easeOutExpo).custom( + builder: (_, m, child) => FractionallySizedBox( + alignment: Alignment.centerLeft, + widthFactor: m * count / total, + child: child, + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/collection/widgets/_collection_list.dart b/lib/ui/screens/collection/widgets/_collection_list.dart new file mode 100644 index 00000000..f3937035 --- /dev/null +++ b/lib/ui/screens/collection/widgets/_collection_list.dart @@ -0,0 +1,91 @@ +part of '../collection_screen.dart'; + +@immutable +class _CollectionList extends StatelessWidget { + const _CollectionList({ + Key? key, + required this.states, + required this.onPressed, + this.onReset, + this.fromId, + this.scrollWonder, + this.scrollKey, + }) : super(key: key); + + final Map states; + final ValueSetter onPressed; + final VoidCallback? onReset; + final Key? scrollKey; + final WonderType? scrollWonder; + final String? fromId; + + @override + Widget build(BuildContext context) { + List wonders = wondersLogic.all; + List children = []; + for (int i = 0; i < wonders.length; i++) { + WonderData data = wonders[i]; + children.add(_buildCategoryTitle(context, data, data.type == scrollWonder ? scrollKey : null)); + children.add(Gap($styles.insets.md)); + children.add(_buildCollectibleRow(context, data.type, states)); + children.add(Gap($styles.insets.xl)); + } + + children.add(_buildResetBtn(context)); + + return Flexible( + child: RepaintBoundary( + child: ScrollDecorator.shadow( + builder: (controller) => SingleChildScrollView( + controller: controller, + padding: EdgeInsets.all($styles.insets.md).copyWith(bottom: $styles.insets.offset * 2.5), + child: Column( + children: children, + ), + ), + ), + ), + ); + } + + Widget _buildCategoryTitle(BuildContext context, WonderData data, Key? key) { + return Text( + data.title.toUpperCase(), + textAlign: TextAlign.left, + key: key, + style: $styles.text.title1.copyWith(color: $styles.colors.offWhite), + ); + } + + Widget _buildCollectibleRow(BuildContext context, WonderType wonder, Map states) { + final double height = $styles.insets.lg * 6; + List list = collectiblesLogic.forWonder(wonder); + if (list.isEmpty) return Container(height: height, color: $styles.colors.black); + + List children = []; + for (int i = 0; i < list.length; i++) { + if (i > 0) children.add(Gap($styles.insets.md)); + CollectibleData collectible = list[i]; + int state = states[collectible.id] ?? CollectibleState.lost; + children.add(Flexible( + child: _CollectionTile( + collectible: collectible, + state: state, + onPressed: onPressed, + heroTag: collectible.id == fromId ? 'collectible_image_$fromId' : null, + ), + )); + } + return SizedBox(height: height, child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: children)); + } + + Widget _buildResetBtn(BuildContext context) { + Widget btn = AppBtn.from( + onPressed: onReset ?? () {}, + text: $strings.collectionButtonReset, + isSecondary: true, + expand: true, + ); + return AnimatedOpacity(opacity: onReset == null ? 0.25 : 1, duration: $styles.times.fast, child: btn); + } +} diff --git a/lib/ui/screens/collection/widgets/_collection_tile.dart b/lib/ui/screens/collection/widgets/_collection_tile.dart new file mode 100644 index 00000000..e55afa19 --- /dev/null +++ b/lib/ui/screens/collection/widgets/_collection_tile.dart @@ -0,0 +1,63 @@ +part of '../collection_screen.dart'; + +class _CollectionTile extends StatelessWidget { + const _CollectionTile({ + Key? key, + required this.collectible, + required this.state, + required this.onPressed, + this.heroTag, + }) : super(key: key); + + final CollectibleData collectible; + final ValueSetter onPressed; + final int state; + final String? heroTag; + + @override + Widget build(BuildContext context) { + return RepaintBoundary( + child: state == CollectibleState.lost + ? _buildHidden(context, collectible) + : _buildFound(context, collectible, state), + ); + } + + Widget _buildHidden(BuildContext context, CollectibleData collectible) { + return Container( + color: $styles.colors.black, + child: Center( + child: FractionallySizedBox( + widthFactor: 0.6, + heightFactor: 0.6, + child: Image(image: collectible.icon, color: $styles.colors.greyStrong), + ), + ), + ); + } + + Widget _buildFound(BuildContext context, CollectibleData collectible, int state) { + final bool isNew = state == CollectibleState.discovered; + Widget content = Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + color: $styles.colors.black, + border: isNew ? Border.all(color: $styles.colors.accent1, width: 3) : null, + boxShadow: + !isNew ? null : [BoxShadow(color: $styles.colors.accent1.withOpacity(0.6), blurRadius: $styles.insets.sm)], + ), + child: AppImage( + image: NetworkImage(collectible.imageUrlSmall), + fit: BoxFit.cover, + scale: 0.5, + ), + ); + + return AppBtn.basic( + semanticLabel: collectible.title, + onPressed: () => onPressed(collectible), + child: content, + ); + } +} diff --git a/lib/ui/screens/collection/widgets/_newly_discovered_row.dart b/lib/ui/screens/collection/widgets/_newly_discovered_row.dart new file mode 100644 index 00000000..4fe30170 --- /dev/null +++ b/lib/ui/screens/collection/widgets/_newly_discovered_row.dart @@ -0,0 +1,37 @@ +part of '../collection_screen.dart'; + +@immutable +class _NewlyDiscoveredRow extends StatelessWidget { + const _NewlyDiscoveredRow({Key? key, this.count = 0, required this.onPressed}) : super(key: key); + + final int count; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + if (count == 0) return SizedBox.shrink(); + + return AppBtn.basic( + semanticLabel: StringUtils.supplant( + $strings.newlyDiscoveredSemanticNew, + {'{count}': count.toString(), '{plural}': count == 1 ? '' : 's'}, + ), + onPressed: onPressed, + child: Container( + alignment: Alignment.center, + height: 40, + color: $styles.colors.black, + padding: EdgeInsets.symmetric(vertical: $styles.insets.xs), + child: Text( + StringUtils.supplant( + $strings.newlyDiscoveredLabelNew, + {'{count}': count.toString(), '{plural}': count == 1 ? '' : 's'}, + ), + textAlign: TextAlign.center, + textHeightBehavior: TextHeightBehavior(applyHeightToFirstAscent: false), + style: $styles.text.bodySmallBold.copyWith(color: $styles.colors.accent1), + ), + ), + ); + } +} diff --git a/lib/ui/screens/editorial/editorial_screen.dart b/lib/ui/screens/editorial/editorial_screen.dart new file mode 100644 index 00000000..26d86240 --- /dev/null +++ b/lib/ui/screens/editorial/editorial_screen.dart @@ -0,0 +1,186 @@ +import 'dart:async'; + +import 'package:drop_cap_text/drop_cap_text.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_circular_text/circular_text.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/common/blend_mask.dart'; +import 'package:wonders/ui/common/compass_divider.dart'; +import 'package:wonders/ui/common/curved_clippers.dart'; +import 'package:wonders/ui/common/google_maps_marker.dart'; +import 'package:wonders/ui/common/gradient_container.dart'; +import 'package:wonders/ui/common/hidden_collectible.dart'; +import 'package:wonders/ui/common/scaling_list_item.dart'; +import 'package:wonders/ui/common/themed_text.dart'; +import 'package:wonders/ui/common/utils/context_utils.dart'; +import 'package:wonders/ui/wonder_illustrations/common/animated_clouds.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_title_text.dart'; + +part 'widgets/_app_bar.dart'; +part 'widgets/_callout.dart'; +part 'widgets/_circular_title_bar.dart'; +part 'widgets/_collapsing_pull_quote_image.dart'; +part 'widgets/_large_simple_quote.dart'; +part 'widgets/_scrolling_content.dart'; +part 'widgets/_section_divider.dart'; +part 'widgets/_sliding_image_stack.dart'; +part 'widgets/_title_text.dart'; +part 'widgets/_top_illustration.dart'; + +class WonderEditorialScreen extends StatefulWidget { + const WonderEditorialScreen(this.data, {Key? key, required this.onScroll}) : super(key: key); + final WonderData data; + final void Function(double scrollPos) onScroll; + + @override + State createState() => _WonderEditorialScreenState(); +} + +class _WonderEditorialScreenState extends State { + late final ScrollController _scroller = ScrollController()..addListener(_handleScrollChanged); + final _scrollPos = ValueNotifier(0.0); + final _sectionIndex = ValueNotifier(0); + final _scrollToPopThreshold = 50; + bool _isPointerDown = false; + + @override + void dispose() { + _scroller.dispose(); + super.dispose(); + } + + /// Various [ValueListenableBuilders] are mapped to the _scrollPos and will rebuild when it changes + void _handleScrollChanged() { + _scrollPos.value = _scroller.position.pixels; + widget.onScroll.call(_scrollPos.value); + // If user pulls far down on the elastic list, pop back to + if (_scrollPos.value < -_scrollToPopThreshold) { + if (_isPointerDown) { + context.pop(); + _scroller.removeListener(_handleScrollChanged); + } + } + } + + bool _checkPointerIsDown(d) => _isPointerDown = d.dragDetails != null; + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, constraints) { + bool shortMode = constraints.biggest.height < 700; + double illustrationHeight = shortMode ? 250 : 280; + double minAppBarHeight = shortMode ? 80 : 120; + double maxAppBarHeight = shortMode ? 400 : 500; + + return NotificationListener( + onNotification: _checkPointerIsDown, + child: ColoredBox( + color: $styles.colors.offWhite, + child: Stack( + children: [ + /// Background + Positioned.fill( + child: ValueListenableBuilder( + valueListenable: _scrollPos, + builder: (_, value, __) { + return Container( + color: widget.data.type.bgColor.withOpacity(_scrollPos.value > 1000 ? 0 : 1), + ); + }, + ), + ), + + /// Top Illustration - Sits underneath the scrolling content, fades out as it scrolls + SizedBox( + height: illustrationHeight, + child: ValueListenableBuilder( + valueListenable: _scrollPos, + builder: (_, value, child) { + // get some value between 0 and 1, based on the amt scrolled + double opacity = (1 - value / 700).clamp(0, 1); + return Opacity(opacity: opacity, child: child); + }, + // This is due to a bug: https://github.com/flutter/flutter/issues/101872 + child: RepaintBoundary(child: _TopIllustration(widget.data.type)), + ), + ), + + /// Scrolling content - Includes an invisible gap at the top, and then scrolls over the illustration + CustomScrollView( + primary: false, + controller: _scroller, + cacheExtent: 500, + slivers: [ + /// Invisible padding at the top of the list, so the illustration shows through the btm + SliverToBoxAdapter( + child: SizedBox(height: illustrationHeight), + ), + + /// Text content, animates itself to hide behind the app bar as it scrolls up + SliverToBoxAdapter( + child: ValueListenableBuilder( + valueListenable: _scrollPos, + builder: (_, value, child) { + double offsetAmt = max(0, value * .3); + double opacity = (1 - offsetAmt / 150).clamp(0, 1); + return Transform.translate( + offset: Offset(0, offsetAmt), + child: Opacity(opacity: opacity, child: child), + ); + }, + child: _TitleText(widget.data, scroller: _scroller), + ), + ), + + /// Collapsing App bar, pins to the top of the list + SliverAppBar( + pinned: true, + collapsedHeight: minAppBarHeight, + toolbarHeight: minAppBarHeight, + expandedHeight: maxAppBarHeight, + backgroundColor: Colors.transparent, + elevation: 0, + leading: SizedBox.shrink(), + flexibleSpace: SizedBox.expand( + child: _AppBar( + widget.data.type, + scrollPos: _scrollPos, + sectionIndex: _sectionIndex, + ).animate().fade(duration: $styles.times.med, delay: $styles.times.pageTransition), + ), + ), + + /// Editorial content (text and images) + _ScrollingContent(widget.data, scrollPos: _scrollPos, sectionNotifier: _sectionIndex), + + /// Bottom padding + SliverToBoxAdapter( + child: Container(height: 150, color: $styles.colors.offWhite), + ), + ], + ), + + /// Home Btn + AnimatedBuilder( + animation: _scroller, + builder: (_, child) { + return AnimatedOpacity( + opacity: _scrollPos.value > 0 ? 0 : 1, + duration: $styles.times.med, + child: child, + ); + }, + child: BackBtn(icon: AppIcons.north).safe()), + ], + ), + ), + ); + }); + } +} diff --git a/lib/ui/screens/editorial/widgets/_app_bar.dart b/lib/ui/screens/editorial/widgets/_app_bar.dart new file mode 100644 index 00000000..b84e102f --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_app_bar.dart @@ -0,0 +1,104 @@ +part of '../editorial_screen.dart'; + +class _AppBar extends StatelessWidget { + _AppBar(this.wonderType, {Key? key, required this.sectionIndex, required this.scrollPos}) : super(key: key); + final WonderType wonderType; + final ValueNotifier sectionIndex; + final ValueNotifier scrollPos; + final _titleValues = [ + $strings.appBarTitleFactsHistory, + $strings.appBarTitleConstruction, + $strings.appBarTitleLocation, + ]; + + final _iconValues = const [ + 'history.png', + 'construction.png', + 'geography.png', + ]; + + ArchType _getArchType() { + switch (wonderType) { + case WonderType.chichenItza: + return ArchType.flatPyramid; + case WonderType.christRedeemer: + return ArchType.wideArch; + case WonderType.colosseum: + return ArchType.arch; + case WonderType.greatWall: + return ArchType.arch; + case WonderType.machuPicchu: + return ArchType.pyramid; + case WonderType.petra: + return ArchType.wideArch; + case WonderType.pyramidsGiza: + return ArchType.pyramid; + case WonderType.tajMahal: + return ArchType.spade; + } + } + + @override + Widget build(BuildContext context) { + final arch = _getArchType(); + return LayoutBuilder(builder: (_, constraints) { + bool showOverlay = constraints.biggest.height < 300; + return Stack( + fit: StackFit.expand, + children: [ + AnimatedSwitcher( + duration: $styles.times.fast * .5, + child: Stack( + key: ValueKey(showOverlay), + fit: StackFit.expand, + children: [ + /// Masked image + ClipPath( + // Switch arch type to Rect if we are showing the title bar + clipper: showOverlay ? null : ArchClipper(arch), + child: ValueListenableBuilder( + valueListenable: scrollPos, + builder: (_, value, child) { + double opacity = (.4 + (value / 1500)).clamp(0, 1); + return ScalingListItem( + scrollPos: scrollPos, + child: Image.asset( + wonderType.photo1, + fit: BoxFit.cover, + opacity: AlwaysStoppedAnimation(opacity), + ), + ); + }, + ), + ), + + /// Colored overlay + if (showOverlay) ...[ + ClipRect( + child: ColoredBox( + color: wonderType.bgColor.withOpacity(.8), + ).animate().fade(duration: $styles.times.fast), + ), + ], + ], + ), + ), + + /// Circular Titlebar + BottomCenter( + child: ValueListenableBuilder( + valueListenable: sectionIndex, + builder: (_, value, __) { + return _CircularTitleBar( + index: value, + titles: _titleValues, + icons: _iconValues, + ); + }, + ), + ), + ], + ); + }); + } +} diff --git a/lib/ui/screens/editorial/widgets/_callout.dart b/lib/ui/screens/editorial/widgets/_callout.dart new file mode 100644 index 00000000..f7fb0d1f --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_callout.dart @@ -0,0 +1,25 @@ +part of '../editorial_screen.dart'; + +class _Callout extends StatelessWidget { + final String text; + + const _Callout({Key? key, required this.text}) : super(key: key); + @override + Widget build(BuildContext context) { + return IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container(color: $styles.colors.accent1, width: 1), + Gap($styles.insets.sm), + Expanded( + child: Text( + text, + style: $styles.text.callout, + ), + ) + ], + ), + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_circular_title_bar.dart b/lib/ui/screens/editorial/widgets/_circular_title_bar.dart new file mode 100644 index 00000000..9314a938 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_circular_title_bar.dart @@ -0,0 +1,151 @@ +part of '../editorial_screen.dart'; + +class _CircularTitleBar extends StatelessWidget { + const _CircularTitleBar({Key? key, required this.titles, required this.icons, required this.index}) + : assert(titles.length == icons.length, 'The number of titles and icons do not match.'), + super(key: key); + final List titles; + final List icons; + final int index; + + @override + Widget build(BuildContext context) { + double barSize = 100; // the actual size of this widget + double barTopPadding = 40; // negative space at the top of the bar + double circleSize = 190; // circle is bigger than bar, and overhangs it + assert(index >= 0 && index < titles.length, 'Can not find a title for index $index'); + // note: this offset eliminates a subpixel line Flutter draws below the header + return Transform.translate( + offset: Offset(0, 1), + child: SizedBox( + height: barSize, + child: Stack( + children: [ + // Bg + BottomCenter(child: Container(height: barSize - barTopPadding, color: $styles.colors.offWhite)), + + ClipRect( + child: OverflowBox( + alignment: Alignment.topCenter, + maxHeight: circleSize, + child: _AnimatedCircleWithText(titles: titles, index: index), + ), + ), + + BottomCenter( + child: Padding( + padding: EdgeInsets.only(bottom: 20), + child: Image.asset('${ImagePaths.common}/${icons[index]}') + .animate(key: ValueKey(index)) + .fade() + .scale(begin: .5, end: 1, curve: Curves.easeOutBack, duration: $styles.times.med), + ), + ), + ], + ), + ), + ); + } +} + +class _AnimatedCircleWithText extends StatefulWidget { + const _AnimatedCircleWithText({ + Key? key, + required this.titles, + required this.index, + }) : super(key: key); + + final List titles; + final int index; + + @override + State<_AnimatedCircleWithText> createState() => _AnimatedCircleWithTextState(); +} + +class _AnimatedCircleWithTextState extends State<_AnimatedCircleWithText> with SingleTickerProviderStateMixin { + int _prevIndex = -1; + String get oldTitle => _prevIndex == -1 ? '' : widget.titles[_prevIndex]; + String get newTitle => widget.titles[widget.index]; + late final _anim = AnimationController( + vsync: this, + duration: $styles.times.med, + )..forward(); + + bool get isAnimStopped => _anim.value == 0 || _anim.value == _anim.upperBound; + + @override + void dispose() { + _anim.dispose(); + super.dispose(); + } + + @override + void didUpdateWidget(covariant _AnimatedCircleWithText oldWidget) { + // Spin 180 degrees each time index changes + if (oldWidget.index != widget.index) { + _prevIndex = oldWidget.index; + // If the animation is already in motion, we don't need to interrupt it, just let the text change + if (isAnimStopped) { + _anim.forward(from: 0); + } + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(_) { + return AnimatedBuilder( + animation: _anim, + builder: (_, __) { + var rot = _prevIndex > widget.index ? -pi : pi; + return Transform.rotate( + angle: Curves.easeInOut.transform(_anim.value) * rot, + child: Container( + decoration: BoxDecoration(shape: BoxShape.circle, color: $styles.colors.offWhite), + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.all(16), + // 2 circles that are counter rotated / opposite (one on top, one on bottom) + // Each time index is changed, the stack is rotated 180 degrees. + // When the animation completes, the rotation snaps back to 0 and the titles also swap position + // This creates the effect of a new title always rolling in, with the old rolling out + child: Semantics( + label: newTitle, + child: Stack( + children: [ + Transform.rotate( + angle: _anim.isCompleted ? rot : 0, + child: _buildCircularText(_anim.isCompleted ? newTitle : oldTitle), + ), + if (!_anim.isCompleted) ...[ + Transform.rotate( + angle: _anim.isCompleted ? 0 : rot, + child: _buildCircularText(_anim.isCompleted ? oldTitle : newTitle), + ), + ] + ], + ), + ), + ), + ), + ); + }, + ); + } + + Widget _buildCircularText(String title) { + final textStyle = $styles.text.monoTitleFont.copyWith(fontSize: 22, color: $styles.colors.accent1); + return CircularText( + position: CircularTextPosition.inside, + children: [ + TextItem( + text: Text(title.toUpperCase(), style: textStyle), + space: 9, + startAngle: -90, + startAngleAlignment: StartAngleAlignment.center, + direction: CircularTextDirection.clockwise, + ), + ], + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_collapsing_pull_quote_image.dart b/lib/ui/screens/editorial/widgets/_collapsing_pull_quote_image.dart new file mode 100644 index 00000000..2eec13a4 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_collapsing_pull_quote_image.dart @@ -0,0 +1,144 @@ +part of '../editorial_screen.dart'; + +class _CollapsingPullQuoteImage extends StatelessWidget { + const _CollapsingPullQuoteImage({Key? key, required this.scrollPos, required this.data}) : super(key: key); + final ValueNotifier scrollPos; + final WonderData data; + + @override + Widget build(BuildContext context) { + final textScale = MediaQuery.of(context).textScaleFactor; + // Start transitioning when we are halfway up the screen + final collapseStartPx = context.heightPx * 1; + final collapseEndPx = context.heightPx * .35; + const double imgHeight = 430; + const double outerPadding = 100; + + /// A single piece of quote text, this widget has one on top, and one on bottom + Widget buildText(String value, double collapseAmt, {required bool top, bool isAuthor = false}) { + var quoteStyle = $styles.text.quote1; + var quoteSize = quoteStyle.fontSize; + quoteStyle = quoteStyle.copyWith( + color: $styles.colors.caption, + fontSize: (quoteSize ??= 36) / textScale, //dynamic font size for more consistent quote layout + ); + if (isAuthor) { + quoteStyle = quoteStyle.copyWith(fontSize: 20, fontWeight: FontWeight.w600); + } + double offsetY = (imgHeight / 2 + outerPadding * .25) * (1 - collapseAmt); + if (top) offsetY *= -1; // flip? + return Transform.translate( + offset: Offset(0, offsetY), + child: BlendMask( + blendModes: const [BlendMode.colorBurn], + child: Text(value, style: quoteStyle, textAlign: TextAlign.center), + )); + } + + return ValueListenableBuilder( + valueListenable: scrollPos, + builder: (context, value, __) { + double collapseAmt = 1.0; + final yPos = ContextUtils.getGlobalPos(context)?.dy; + if (yPos != null && yPos < collapseStartPx) { + // Get a normalized value, 0 - 1, representing the current amount of collapse. + collapseAmt = (collapseStartPx - max(collapseEndPx, yPos)) / (collapseStartPx - collapseEndPx); + } + + // The sized boxes in the column collapse to a zero height, allowing the quotes to naturally sit over top of the image + return MergeSemantics( + child: Padding( + padding: EdgeInsets.symmetric(vertical: outerPadding), + child: Stack( + children: [ + Container( + width: context.widthPx, + height: imgHeight, + decoration: BoxDecoration( + border: Border.all(color: $styles.colors.accent2), + borderRadius: BorderRadius.only( + topRight: Radius.circular(context.widthPx / 2), + topLeft: Radius.circular(context.widthPx / 2), + ), + ), + ), + + /// Main image + Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: imgHeight, + + // Clip the image with an curved top + child: Stack( + children: [ + Container( + alignment: Alignment.topRight, + margin: const EdgeInsets.all(12), + child: ClipPath( + clipper: CurvedTopClipper(), + child: _buildImage(collapseAmt), + ), + ), + ], + ), + ), + ], + ), + + /// Collapsing text + Positioned.fill( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: 32), // push down vertical centre + buildText(data.pullQuote1Top, collapseAmt, top: true), + buildText(data.pullQuote1Bottom, collapseAmt, top: false), + if (data.pullQuote1Author.isNotEmpty) ...[ + Container( + margin: const EdgeInsets.only(top: 16), + child: buildText('- ${data.pullQuote1Author}', collapseAmt, top: false, isAuthor: true), + ) + ], + ], + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + Stack _buildImage(double collapseAmt) { + return Stack( + fit: StackFit.expand, + children: [ + ScalingListItem( + scrollPos: scrollPos, + child: Image.asset( + data.type.photo2, + fit: BoxFit.cover, + opacity: AlwaysStoppedAnimation(1 - collapseAmt * .7), + ), + ), + BlendMask( + blendModes: const [BlendMode.colorBurn], + opacity: .9, + child: VtGradient( + [ + Color(0xFFBEABA1).withOpacity(1), + Color(0xFFA6958C).withOpacity(1), + ], + const [0, 1], + ), + ), + ], + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_large_simple_quote.dart b/lib/ui/screens/editorial/widgets/_large_simple_quote.dart new file mode 100644 index 00000000..651a0b06 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_large_simple_quote.dart @@ -0,0 +1,39 @@ +part of '../editorial_screen.dart'; + +class _LargeSimpleQuote extends StatelessWidget { + const _LargeSimpleQuote({Key? key, required this.text, required this.author}) : super(key: key); + final String text; + final String author; + + @override + Widget build(BuildContext context) { + return MergeSemantics( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.lg, vertical: $styles.insets.xl), + child: Column(children: [ + FractionalTranslation( + translation: Offset(0, .5), + child: Text( + '“', + style: $styles.text.quote1.copyWith( + color: $styles.colors.accent1, + fontSize: 90, + height: .7, + ), + ), + ), + Text( + text, + style: $styles.text.quote2, + textAlign: TextAlign.center, + ), + Gap($styles.insets.md), + Text( + '- $author', + style: $styles.text.quote2Sub.copyWith(color: $styles.colors.accent1), + ), + ]), + ), + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_scrolling_content.dart b/lib/ui/screens/editorial/widgets/_scrolling_content.dart new file mode 100644 index 00000000..5c81d6d2 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_scrolling_content.dart @@ -0,0 +1,315 @@ +part of '../editorial_screen.dart'; + +class _ScrollingContent extends StatelessWidget { + const _ScrollingContent(this.data, {Key? key, required this.scrollPos, required this.sectionNotifier}) + : super(key: key); + final WonderData data; + final ValueNotifier scrollPos; + final ValueNotifier sectionNotifier; + + String _fixNewlines(String text) { + const nl = '\n'; + final chunks = text.split(nl); + while (chunks.last == nl) { + chunks.removeLast(); + } + chunks.removeWhere((element) => element.trim().isEmpty); + final result = chunks.join('$nl$nl'); + return result; + } + + @override + Widget build(BuildContext context) { + Text buildText(String value) => Text(_fixNewlines(value), style: $styles.text.body); + + Widget buildDropCapText(String value) { + final TextStyle dropStyle = $styles.text.dropCase; + final TextStyle bodyStyle = $styles.text.body; + final String dropChar = value.substring(0, 1); + final double dropCapWidth = StringUtils.measure(dropChar, dropStyle).width; + final bool isEnglish = localeLogic.strings.localeName == 'en'; //TODO EC: Helper method for localLogic.isEnglish? + final bool skipCaps = !isEnglish || MediaQuery.of(context).accessibleNavigation; + return Semantics( + label: value, + child: !skipCaps + ? DropCapText( + _fixNewlines(value).substring(1), + dropCap: DropCap( + width: dropCapWidth, + height: $styles.text.body.fontSize! * $styles.text.body.height! * 2, + child: Transform.translate( + offset: Offset(0, bodyStyle.fontSize! * (bodyStyle.height! - 1) - 2), + child: Text( + dropChar, + overflow: TextOverflow.visible, + style: $styles.text.dropCase.copyWith( + color: $styles.colors.accent1, + height: 1, + ), + ), + ), + ), + style: $styles.text.body, + dropCapPadding: EdgeInsets.only(right: 6), + dropCapStyle: $styles.text.dropCase.copyWith( + color: $styles.colors.accent1, + height: 1, + ), + ) + : Text(value, style: bodyStyle), + ); + } + + Widget buildHiddenCollectible({required int slot}) { + List getTypesForSlot(slot) { + switch (slot) { + case 0: + return [WonderType.chichenItza, WonderType.colosseum]; + case 1: + return [WonderType.pyramidsGiza, WonderType.petra]; + case 2: + return [WonderType.machuPicchu, WonderType.christRedeemer]; + default: + return [WonderType.tajMahal, WonderType.greatWall]; + } + } + + return HiddenCollectible( + data.type, + index: 0, + matches: getTypesForSlot(slot), + size: 128, + ); + } + + return SliverBackgroundColor( + color: $styles.colors.offWhite, + sliver: SliverPadding( + padding: EdgeInsets.symmetric(vertical: $styles.insets.md), + sliver: SliverList( + delegate: SliverChildListDelegate([ + ..._contentSection([ + Center(child: buildHiddenCollectible(slot: 0)), + + /// History 1 + buildDropCapText(data.historyInfo1), + + /// Quote1 + _CollapsingPullQuoteImage(data: data, scrollPos: scrollPos), + Center(child: buildHiddenCollectible(slot: 1)), + + /// Callout1 + _Callout(text: data.callout1), + + /// History 2 + buildText(data.historyInfo2), + _SectionDivider(scrollPos, sectionNotifier, index: 1), + + /// Construction 1 + buildDropCapText(data.constructionInfo1), + Center(child: buildHiddenCollectible(slot: 2)), + ]), + Gap($styles.insets.md), + _YouTubeThumbnail(id: data.videoId, caption: data.videoCaption), + Gap($styles.insets.md), + ..._contentSection([ + /// Callout2 + Gap($styles.insets.xs), + _Callout(text: data.callout2), + + /// Construction 2 + buildText(data.constructionInfo2), + _SlidingImageStack(scrollPos: scrollPos, type: data.type), + _SectionDivider(scrollPos, sectionNotifier, index: 2), + + /// Location + buildDropCapText(data.locationInfo1), + _LargeSimpleQuote(text: data.pullQuote2, author: data.pullQuote2Author), + buildText(data.locationInfo2), + ]), + Gap($styles.insets.md), + _MapsThumbnail(data, height: 200), + Gap($styles.insets.md), + ..._contentSection([Center(child: buildHiddenCollectible(slot: 3))]), + ]), + ), + ), + ); + } + + /// Helper widget to provide hz padding to multiple widgets. Keeps the layout of the scrolling content cleaner. + List _contentSection(List children) { + return [ + for (int i = 0; i < children.length - 1; i++) ...[ + Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: children[i], + ), + Gap($styles.insets.md) + ], + Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: children.last, + ), + ]; + } +} + +class _YouTubeThumbnail extends StatelessWidget { + const _YouTubeThumbnail({Key? key, required this.id, required this.caption}) : super(key: key); + final String id; + final String caption; + + String get imageUrl => 'http://img.youtube.com/vi/$id/hqdefault.jpg'; + + @override + Widget build(BuildContext context) { + void handlePressed() => context.push(ScreenPaths.video(id)); + return MergeSemantics( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 400), + child: Column( + children: [ + AppBtn.basic( + semanticLabel: $strings.scrollingContentSemanticYoutube, + onPressed: handlePressed, + child: Stack(children: [ + AppImage(image: NetworkImage(imageUrl), fit: BoxFit.cover, scale: 1.0), + Positioned.fill( + child: Center( + child: Container( + padding: EdgeInsets.all($styles.insets.xs), + decoration: BoxDecoration( + color: $styles.colors.black.withOpacity(0.66), + borderRadius: BorderRadius.circular(999), + ), + child: Icon( + Icons.play_arrow, + color: $styles.colors.white, + size: $styles.insets.xl, + ), + ), + ), + ), + ]), + ), + Gap($styles.insets.xs), + Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: Text(caption, style: $styles.text.caption)), + ], + ), + ), + ); + } +} + +class _MapsThumbnail extends StatefulWidget { + const _MapsThumbnail(this.data, {Key? key, required this.height}) : super(key: key); + final WonderData data; + final double height; + + @override + State<_MapsThumbnail> createState() => _MapsThumbnailState(); +} + +class _MapsThumbnailState extends State<_MapsThumbnail> { + CameraPosition get startPos => CameraPosition(target: LatLng(widget.data.lat, widget.data.lng), zoom: 3); + + @override + Widget build(BuildContext context) { + void handlePressed() => context.push(ScreenPaths.maps(widget.data.type)); + return MergeSemantics( + child: Column( + children: [ + SizedBox( + height: widget.height, + child: ClipRRect( + borderRadius: BorderRadius.circular($styles.corners.md), + child: AppBtn.basic( + semanticLabel: $strings.scrollingContentSemanticOpen, + onPressed: handlePressed, + + /// To prevent the map widget from absorbing the onPressed action, use a Stack + IgnorePointer + a transparent Container + child: Stack( + children: [ + Positioned.fill(child: ColoredBox(color: Colors.transparent)), + IgnorePointer( + child: GoogleMap( + markers: {getMapsMarker(startPos.target)}, + zoomControlsEnabled: false, + mapType: MapType.normal, + mapToolbarEnabled: false, + initialCameraPosition: startPos, + myLocationButtonEnabled: false, + ), + ), + ], + ), + ), + ), + ), + Gap($styles.insets.xs), + Semantics( + sortKey: OrdinalSortKey(0), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: Text(widget.data.mapCaption, style: $styles.text.caption), + ), + ), + ], + ), + ); + } +} + +class SliverBackgroundColor extends SingleChildRenderObjectWidget { + const SliverBackgroundColor({ + Key? key, + required this.color, + Widget? sliver, + }) : super(key: key, child: sliver); + + final Color color; + + @override + RenderSliverBackgroundColor createRenderObject(BuildContext context) { + return RenderSliverBackgroundColor( + color, + ); + } + + @override + void updateRenderObject(BuildContext context, RenderSliverBackgroundColor renderObject) { + renderObject.color = color; + } +} + +class RenderSliverBackgroundColor extends RenderProxySliver { + RenderSliverBackgroundColor(this._color); + + Color get color => _color; + Color _color; + set color(Color value) { + if (value == color) { + return; + } + _color = color; + markNeedsPaint(); + } + + @override + void paint(PaintingContext context, Offset offset) { + if (child != null && child!.geometry!.visible) { + final SliverPhysicalParentData childParentData = child!.parentData! as SliverPhysicalParentData; + final Rect childRect = + offset + childParentData.paintOffset & Size(constraints.crossAxisExtent, child!.geometry!.paintExtent); + context.canvas.drawRect( + childRect, + Paint() + ..style = PaintingStyle.fill + ..color = color); + context.paintChild(child!, offset + childParentData.paintOffset); + } + } +} diff --git a/lib/ui/screens/editorial/widgets/_section_divider.dart b/lib/ui/screens/editorial/widgets/_section_divider.dart new file mode 100644 index 00000000..84097a9a --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_section_divider.dart @@ -0,0 +1,50 @@ +part of '../editorial_screen.dart'; + +class _SectionDivider extends StatefulWidget { + const _SectionDivider(this.scrollNotifier, this.sectionNotifier, {Key? key, required this.index}) : super(key: key); + final int index; + final ValueNotifier scrollNotifier; + final ValueNotifier sectionNotifier; + + @override + State<_SectionDivider> createState() => _SectionDividerState(); +} + +class _SectionDividerState extends State<_SectionDivider> with SingleTickerProviderStateMixin { + final _isActivated = ValueNotifier(false); + + double _getSwitchPt(BuildContext c) => c.heightPx * .5; + + void _checkPosition(BuildContext context) { + final yPos = ContextUtils.getGlobalPos(context)?.dy; + if (yPos == null || yPos < 0) return; + // Only allow headers to switch if it's above the switch pt + bool activated = yPos < _getSwitchPt(context); + if (activated != _isActivated.value) { + scheduleMicrotask(() { + // When activated, set our index as active. When de-activated, set it to the index before ours (index - 1). + int newIndex = activated ? widget.index : widget.index - 1; + widget.sectionNotifier.value = newIndex; + }); + _isActivated.value = activated; + } + } + + @override + Widget build(BuildContext _) { + // When scroll position changes, the divider needs to check whether it should mark itself as the active index + return ValueListenableBuilder( + valueListenable: widget.scrollNotifier, + builder: (context, value, _) { + _checkPosition(context); + return ValueListenableBuilder( + valueListenable: _isActivated, + builder: (_, value, __) => Padding( + padding: EdgeInsets.symmetric(vertical: $styles.insets.xl * 2), + child: CompassDivider(isExpanded: value), + ), + ); + }, + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_sliding_image_stack.dart b/lib/ui/screens/editorial/widgets/_sliding_image_stack.dart new file mode 100644 index 00000000..a40f127a --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_sliding_image_stack.dart @@ -0,0 +1,93 @@ +part of '../editorial_screen.dart'; + +class _SlidingImageStack extends StatelessWidget { + const _SlidingImageStack({Key? key, required this.scrollPos, required this.type}) : super(key: key); + + final ValueNotifier scrollPos; + final WonderType type; + + @override + Widget build(BuildContext context) { + final totalSize = Size(280, 400); + Container buildPhoto(double scale, String url, Alignment align, {bool top = true}) { + return Container( + width: totalSize.width * scale, + height: totalSize.height * scale, + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical( + top: Radius.circular(top ? totalSize.width / 2 : 0), + bottom: Radius.circular(top ? 0 : totalSize.width / 2), + ), + image: DecorationImage(image: AssetImage(url), fit: BoxFit.fitWidth, alignment: align), + ), + ); + } + + return ExcludeSemantics( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + width: totalSize.width, + height: totalSize.height, + child: ValueListenableBuilder( + valueListenable: scrollPos, + builder: (context, value, child) { + double pctVisible = 0; + final yPos = ContextUtils.getGlobalPos(context)?.dy; + final height = ContextUtils.getSize(context)?.height; + if (yPos != null && height != null) { + final amtVisible = context.heightPx - yPos; + pctVisible = (amtVisible / height).clamp(0, 3); + } + return Stack( + children: [ + Center( + child: FractionalTranslation( + translation: Offset(0, 0.05 * pctVisible), + child: Transform( + alignment: Alignment.center, //origin: Offset(100, 100) + transform: Matrix4.rotationZ(0.9), + child: Container( + width: context.widthPx / 1.75, + height: context.widthPx, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.elliptical(200, 300)), + border: Border.all( + color: $styles.colors.accent2, + width: 1, + ), + ), + ), + ), + ), + ), + TopRight( + child: FractionalTranslation( + translation: Offset(0, -.1 + .2 * pctVisible), + child: buildPhoto( + .73, + type.photo3, + Alignment(0, -.3 + .6 * pctVisible), + ), + ), + ), + BottomLeft( + child: FractionalTranslation( + translation: Offset(0, -.14 * pctVisible), + child: buildPhoto( + .45, + type.photo4, + Alignment(0, .3 - .6 * pctVisible), + top: false, + ), + ), + ), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/editorial/widgets/_title_text.dart b/lib/ui/screens/editorial/widgets/_title_text.dart new file mode 100644 index 00000000..42515d05 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_title_text.dart @@ -0,0 +1,79 @@ +part of '../editorial_screen.dart'; + +class _TitleText extends StatelessWidget { + const _TitleText(this.data, {Key? key, required this.scroller}) : super(key: key); + final WonderData data; + final ScrollController scroller; + + @override + Widget build(BuildContext context) => MergeSemantics( + child: DefaultTextColor( + color: $styles.colors.offWhite, + child: Column( + children: [ + Gap($styles.insets.md), + Gap(30), + SeparatedRow( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), + separatorBuilder: () => Gap($styles.insets.sm), + children: [ + Expanded( + child: Divider( + color: data.type.fgColor, + ).animate().scale(curve: Curves.easeOut, delay: 500.ms), + ), + Semantics( + header: true, + sortKey: OrdinalSortKey(1), + child: Text( + data.subTitle.toUpperCase(), + style: $styles.text.title2, + ).animate().fade(delay: 100.ms), + ), + Expanded( + child: Divider( + color: data.type.fgColor, + ).animate().scale(curve: Curves.easeOut, delay: 500.ms), + ), + ], + ), + Gap($styles.insets.md), + Semantics(sortKey: OrdinalSortKey(0), child: WonderTitleText(data)), + Gap($styles.insets.xs), + Text( + data.regionTitle.toUpperCase(), + style: $styles.text.title1, + textAlign: TextAlign.center, + ), + Gap($styles.insets.md), + ExcludeSemantics( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: AnimatedBuilder( + animation: scroller, + builder: (_, __) => CompassDivider( + isExpanded: scroller.position.pixels <= 0, + linesColor: data.type.fgColor, + compassColor: $styles.colors.offWhite, + ), + ), + ), + ), + Gap($styles.insets.sm), + Text( + StringUtils.supplant( + $strings.titleLabelDate, + { + '{fromDate}': StringUtils.formatYr(data.startYr), + '{endDate}': StringUtils.formatYr(data.endYr), + }, + ), + style: $styles.text.h4, + textAlign: TextAlign.center, + ), + Gap($styles.insets.sm), + ], + ), + ), + ); +} diff --git a/lib/ui/screens/editorial/widgets/_top_illustration.dart b/lib/ui/screens/editorial/widgets/_top_illustration.dart new file mode 100644 index 00000000..5e716fc5 --- /dev/null +++ b/lib/ui/screens/editorial/widgets/_top_illustration.dart @@ -0,0 +1,27 @@ +part of '../editorial_screen.dart'; + +class _TopIllustration extends StatelessWidget { + const _TopIllustration(this.type, {Key? key}) : super(key: key); + final WonderType type; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + WonderIllustration(type, config: WonderIllustrationConfig.bg(enableAnims: false, shortMode: true)), + Positioned.fill( + bottom: 50, + child: AnimatedClouds(wonderType: type, enableAnimations: false, opacity: .5), + ), + Transform.translate( + // Small bump down to make sure we cover the edge between the editorial page and the sky. + offset: Offset(0, 10), + child: WonderIllustration( + type, + config: WonderIllustrationConfig.mg(enableAnims: false, shortMode: true), + ), + ), + ], + ); + } +} diff --git a/lib/ui/screens/home/_vertical_swipe_controller.dart b/lib/ui/screens/home/_vertical_swipe_controller.dart new file mode 100644 index 00000000..d513e8ac --- /dev/null +++ b/lib/ui/screens/home/_vertical_swipe_controller.dart @@ -0,0 +1,70 @@ +part of 'wonders_home_screen.dart'; + +class _VerticalSwipeController { + _VerticalSwipeController(this.ticker, this.onSwipeComplete); + final TickerProvider ticker; + final swipeAmt = ValueNotifier(0); + final isPointerDown = ValueNotifier(false); + late final swipeReleaseAnim = AnimationController(vsync: ticker)..addListener(handleSwipeReleaseAnimTick); + final double _pullToViewDetailsThreshold = 150; + final VoidCallback onSwipeComplete; + + /// When the _swipeReleaseAnim plays, sync its value to _swipeUpAmt + void handleSwipeReleaseAnimTick() => swipeAmt.value = swipeReleaseAnim.value; + void handleTapDown() => isPointerDown.value = true; + void handleTapCancelled() => isPointerDown.value = false; + + void handleVerticalSwipeCancelled() { + swipeReleaseAnim.duration = swipeAmt.value.seconds * .5; + swipeReleaseAnim.reverse(from: swipeAmt.value); + isPointerDown.value = false; + } + + void handleVerticalSwipeUpdate(DragUpdateDetails details) { + if (swipeReleaseAnim.isAnimating) swipeReleaseAnim.stop(); + if (details.delta.dy > 0) { + swipeAmt.value = 0; + } else { + isPointerDown.value = true; + double value = (swipeAmt.value - details.delta.dy / _pullToViewDetailsThreshold).clamp(0, 1); + if (value != swipeAmt.value) { + swipeAmt.value = value; + if (swipeAmt.value == 1) { + onSwipeComplete(); + } + } + } + //print(_swipeUpAmt.value); + } + + /// Utility method to wrap a couple of ValueListenableBuilders and pass the values into a builder methods. + /// Saves the UI some boilerplate when subscribing to changes. + Widget buildListener( + {required Widget Function(double swipeUpAmt, bool isPointerDown, Widget? child) builder, Widget? child}) { + return ValueListenableBuilder( + valueListenable: swipeAmt, + builder: (_, swipeAmt, __) => ValueListenableBuilder( + valueListenable: isPointerDown, + builder: (_, isPointerDown, __) { + return builder(swipeAmt, isPointerDown, child); + }, + ), + ); + } + + /// Utility method to wrap a gesture detector and wire up the required handlers. + Widget wrapGestureDetector(Widget child, {Key? key}) => Semantics( + button: false, + child: GestureDetector( + key: key, + onTapDown: (_) { + handleTapDown(); + }, + onTapUp: (_) => handleTapCancelled(), + onVerticalDragUpdate: handleVerticalSwipeUpdate, + onVerticalDragEnd: (_) => handleVerticalSwipeCancelled(), + onVerticalDragCancel: handleVerticalSwipeCancelled, + behavior: HitTestBehavior.translucent, + child: child), + ); +} diff --git a/lib/ui/screens/home/widgets/_animated_arrow_button.dart b/lib/ui/screens/home/widgets/_animated_arrow_button.dart new file mode 100644 index 00000000..54de2f42 --- /dev/null +++ b/lib/ui/screens/home/widgets/_animated_arrow_button.dart @@ -0,0 +1,55 @@ +part of '../wonders_home_screen.dart'; + +/// An arrow that fades out, then fades back in and slides down, ending in it's original position with full opacity. +class _AnimatedArrowButton extends StatelessWidget { + _AnimatedArrowButton({Key? key, required this.onTap, required this.semanticTitle}) : super(key: key); + + final String semanticTitle; + final VoidCallback onTap; + + final _fadeOutIn = TweenSequence([ + TweenSequenceItem(tween: Tween(begin: 1, end: 0), weight: .5), + TweenSequenceItem(tween: Tween(begin: 0, end: 1), weight: .5), + ]); + + final _slideDown = TweenSequence([ + TweenSequenceItem(tween: Tween(begin: 1, end: 1), weight: .5), + TweenSequenceItem(tween: Tween(begin: -1, end: 1), weight: .5) + ]); + + @override + Widget build(BuildContext context) { + final Duration duration = $styles.times.med; + return AppBtn.basic( + semanticLabel: StringUtils.supplant( + $strings.animatedArrowSemanticSwipe, + {'{title}': semanticTitle}, + ), + onPressed: onTap, + child: SizedBox( + height: 80, + width: 50, + child: Animate( + effects: [ + CustomEffect(builder: _buildOpacityTween, duration: duration, curve: Curves.easeOut), + CustomEffect(builder: _buildSlideTween, duration: duration, curve: Curves.easeOut), + ], + child: Transform.rotate( + angle: pi * .5, + child: Icon(Icons.chevron_right, size: 42, color: $styles.colors.white), + ), + ), + ), + ); + } + + Widget _buildOpacityTween(BuildContext _, double value, Widget child) { + final opacity = _fadeOutIn.evaluate(AlwaysStoppedAnimation(value)); + return Opacity(opacity: opacity, child: child); + } + + Widget _buildSlideTween(BuildContext _, double value, Widget child) { + double yOffset = _slideDown.evaluate(AlwaysStoppedAnimation(value)); + return Align(alignment: Alignment(0, -1 + yOffset * 2), child: child); + } +} diff --git a/lib/ui/screens/home/wonders_home_screen.dart b/lib/ui/screens/home/wonders_home_screen.dart new file mode 100644 index 00000000..06861261 --- /dev/null +++ b/lib/ui/screens/home/wonders_home_screen.dart @@ -0,0 +1,327 @@ +import 'package:flutter/rendering.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/common/controls/app_page_indicator.dart'; +import 'package:wonders/ui/common/controls/diagonal_text_page_indicator.dart'; +import 'package:wonders/ui/common/gradient_container.dart'; +import 'package:wonders/ui/common/themed_text.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; +import 'package:wonders/ui/screens/home_menu/home_menu.dart'; +import 'package:wonders/ui/wonder_illustrations/common/animated_clouds.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +part '_vertical_swipe_controller.dart'; +part 'widgets/_animated_arrow_button.dart'; + +class HomeScreen extends StatefulWidget with GetItStatefulWidgetMixin { + HomeScreen({Key? key}) : super(key: key); + + @override + State createState() => _HomeScreenState(); +} + +/// Shows a horizontally scrollable list PageView sandwiched between Foreground and Background layers +/// arranged in a parallax style. +class _HomeScreenState extends State with SingleTickerProviderStateMixin { + late final _pageController = PageController( + viewportFraction: 1, + initialPage: _numWonders * 9999, // allow 'infinite' scrolling by starting at a very high page + ); + final _wonders = wondersLogic.all; + bool _isMenuOpen = false; + + /// Set initial wonderIndex + late int _wonderIndex = 0; + int get _numWonders => _wonders.length; + + /// Used to polish the transition when leaving this page for the details view. + /// Used to capture the _swipeAmt at the time of transition, and freeze the wonder foreground in place as we transition away. + double? _swipeOverride; + + /// Used to let the foreground fade in when this view is returned to (from details) + bool _fadeInOnNextBuild = false; + + /// All of the items that should fade in when returning from details view. + /// Using individual tweens is more efficient than tween the entire parent + final _fadeAnims = []; + + WonderData get currentWonder => _wonders[_wonderIndex]; + + late final _VerticalSwipeController _swipeController = _VerticalSwipeController(this, _showDetailsPage); + + bool _isSelected(WonderType t) => t == currentWonder.type; + + void _handlePageViewChanged(v) { + setState(() => _wonderIndex = v % _numWonders); + AppHaptics.lightImpact(); + } + + void _handleOpenMenuPressed() async { + setState(() => _isMenuOpen = true); + WonderType? pickedWonder = await appLogic.showFullscreenDialogRoute( + context, + HomeMenu(data: currentWonder), + ); + setState(() => _isMenuOpen = false); + if (pickedWonder != null) { + _setPageIndex(_wonders.indexWhere((w) => w.type == pickedWonder)); + } + } + + void _handleFadeAnimInit(AnimationController controller) { + _fadeAnims.add(controller); + controller.value = 1; + } + + void _handlePageIndicatorDotPressed(int index) => _setPageIndex(index); + + void _setPageIndex(int index) { + if (index == _wonderIndex) return; + // To support infinite scrolling, we can't jump directly to the pressed index. Instead, make it relative to our current position. + final pos = ((_pageController.page ?? 0) / _numWonders).floor() * _numWonders; + _pageController.jumpToPage(pos + index); + } + + void _showDetailsPage() async { + _swipeOverride = _swipeController.swipeAmt.value; + context.push(ScreenPaths.wonderDetails(currentWonder.type)); + await Future.delayed(100.ms); + _swipeOverride = null; + _fadeInOnNextBuild = true; + } + + void _startDelayedFgFade() async { + for (var a in _fadeAnims) { + a.value = 0; + } + await Future.delayed(300.ms); + for (var a in _fadeAnims) { + a.forward(); + } + } + + @override + Widget build(BuildContext context) { + if (_fadeInOnNextBuild == true) { + _startDelayedFgFade(); + _fadeInOnNextBuild = false; + } + return _swipeController.wrapGestureDetector( + Container( + color: $styles.colors.black, + child: Stack( + children: [ + /// Background + ..._buildBgChildren(), + + /// Clouds + FractionallySizedBox( + widthFactor: 1, + heightFactor: .5, + child: AnimatedClouds(wonderType: currentWonder.type, opacity: 1), + ), + + /// Wonders Illustrations + MergeSemantics( + child: Semantics( + header: true, + image: true, + liveRegion: true, + onIncrease: () => _setPageIndex(_wonderIndex + 1), + onDecrease: () => _setPageIndex(_wonderIndex - 1), + child: PageView.builder( + controller: _pageController, + onPageChanged: _handlePageViewChanged, + itemBuilder: _buildMgChild, + ), + ), + ), + + Stack(children: [ + /// Foreground gradient-1, gets darker when swiping up + BottomCenter( + child: _buildSwipeableBgGradient(currentWonder.type.bgColor.withOpacity(.5)), + ), + + /// Foreground decorators + ..._buildFgChildren(), + + /// Foreground gradient-2, gets darker when swiping up + BottomCenter( + child: _buildSwipeableBgGradient(currentWonder.type.bgColor.withOpacity(.5)), + ), + + /// Floating controls / UI + AnimatedSwitcher( + duration: $styles.times.fast, + child: RepaintBoundary( + child: OverflowBox( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: double.infinity), + const Spacer(), + + /// Title Content + LightText( + child: MergeSemantics( + child: Column( + children: [ + /// Page indicator + IgnorePointer( + child: DiagonalTextPageIndicator(current: _wonderIndex + 1, total: _numWonders), + ), + Gap($styles.insets.sm), + + AppPageIndicator( + count: _numWonders, + controller: _pageController, + color: $styles.colors.white, + dotSize: 8, + onDotPressed: _handlePageIndicatorDotPressed, + semanticPageTitle: $strings.homeSemanticWonder, + ), + ], + ), + ), + ), + Gap($styles.insets.xs), + + /// Animated arrow and background + /// Wrap in a container that is full-width to make it easier to find for screen readers + MergeSemantics( + child: Container( + width: double.infinity, + alignment: Alignment.center, + + /// Lose state of child objects when index changes, this will re-run all the animated switcher and the arrow anim + key: ValueKey(_wonderIndex), + child: Stack( + children: [ + /// Expanding rounded rect that grows in height as user swipes up + Positioned.fill( + child: _buildSwipeableArrowBg(), + ), + + /// Arrow Btn that fades in and out + _AnimatedArrowButton(onTap: _showDetailsPage, semanticTitle: currentWonder.title), + ], + ), + ), + ), + Gap($styles.insets.md), + ], + ), + ), + ), + ), + + /// Menu Btn + TopLeft( + child: AnimatedOpacity( + duration: $styles.times.fast, + opacity: _isMenuOpen ? 0 : 1, + child: MergeSemantics( + child: Semantics( + sortKey: OrdinalSortKey(0), + child: CircleIconBtn( + icon: AppIcons.menu, + onPressed: _handleOpenMenuPressed, + semanticLabel: $strings.homeSemanticOpenMain, + ).safe(), + ), + ), + ), + ), + ]), + ], + ).animate().fadeIn(), + ), + ); + } + + Widget _buildMgChild(_, index) { + final wonder = _wonders[index % _wonders.length]; + final wonderType = wonder.type; + bool isShowing = _isSelected(wonderType); + return _swipeController.buildListener(builder: (swipeAmt, _, child) { + final config = WonderIllustrationConfig.mg( + isShowing: isShowing, + zoom: .05 * swipeAmt, + ); + return ExcludeSemantics( + excluding: !isShowing, + child: Semantics( + label: wonder.title, + child: WonderIllustration(wonderType, config: config), + ), + ); + }); + } + + List _buildBgChildren() { + return _wonders.map((e) { + final config = WonderIllustrationConfig.bg(isShowing: _isSelected(e.type)); + return WonderIllustration(e.type, config: config); + }).toList(); + } + + List _buildFgChildren() { + return _wonders.map((e) { + return _swipeController.buildListener(builder: (swipeAmt, _, child) { + final config = WonderIllustrationConfig.fg( + isShowing: _isSelected(e.type), + zoom: .4 * (_swipeOverride ?? swipeAmt), + ); + return Animate( + effects: const [FadeEffect()], + onPlay: _handleFadeAnimInit, + child: IgnorePointer(child: WonderIllustration(e.type, config: config))); + }); + }).toList(); + } + + Widget _buildSwipeableArrowBg() { + return _swipeController.buildListener( + builder: (swipeAmt, _, child) { + double heightFactor = .5 + .5 * (1 + swipeAmt * 4); + return FractionallySizedBox( + alignment: Alignment.bottomCenter, + heightFactor: heightFactor, + child: Opacity(opacity: swipeAmt * .5, child: child), + ); + }, + child: VtGradient( + [$styles.colors.white.withOpacity(0), $styles.colors.white.withOpacity(1)], + const [.3, 1], + borderRadius: BorderRadius.circular(99), + ), + ); + } + + Widget _buildSwipeableBgGradient(Color fgColor) { + return _swipeController.buildListener(builder: (swipeAmt, isPointerDown, _) { + return IgnorePointer( + child: FractionallySizedBox( + heightFactor: .5, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + fgColor.withOpacity(0), + fgColor.withOpacity(fgColor.opacity * .75 + (isPointerDown ? .05 : 0) + swipeAmt * .20), + ], + stops: const [0, 1], + ), + ), + ), + ), + ); + }); + } +} diff --git a/lib/ui/screens/home_menu/about_dialog_content.dart b/lib/ui/screens/home_menu/about_dialog_content.dart new file mode 100644 index 00000000..1303cb62 --- /dev/null +++ b/lib/ui/screens/home_menu/about_dialog_content.dart @@ -0,0 +1,78 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/gestures.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/modals/fullscreen_web_view.dart'; + +class AboutDialogContent extends StatelessWidget { + const AboutDialogContent({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + void handleTap(String url) => Navigator.push(context, CupertinoPageRoute(builder: (_) => FullscreenWebView(url))); + + List buildSpan(String text, {Map>? linkSupplants}) { + if (linkSupplants?.isNotEmpty ?? false) { + final r = RegExp(r'\{\w+\}'); + final matches = r.allMatches(text); + final a = text.split(r); + + final supplantKeys = matches.map((x) => x.group(0)); + final sortedEntries = supplantKeys.map((x) => linkSupplants?.entries.firstWhere((e) => e.key == x)); + + final spans = []; + for (var i = 0; i < a.length; i++) { + spans.add(TextSpan(text: a[i])); + if (i < sortedEntries.length) { + final label = sortedEntries.elementAt(i)!.value[0]; + final link = sortedEntries.elementAt(i)!.value[1]; + spans.add(TextSpan( + text: label, + recognizer: TapGestureRecognizer()..onTap = () => handleTap(link), + style: TextStyle(fontWeight: FontWeight.bold, color: $styles.colors.accent1), + )); + } + } + return spans; + } else { + return [TextSpan(text: text)]; + } + } + + return SingleChildScrollView( + child: Column(children: [ + Gap($styles.insets.sm), + RichText( + text: TextSpan( + style: $styles.text.bodySmall.copyWith(color: Colors.black), + children: [ + ...buildSpan($strings.homeMenuAboutWonderous), + ...buildSpan($strings.homeMenuAboutBuilt, linkSupplants: { + '{flutterUrl}': [$strings.homeMenuAboutFlutter, 'https://flutter.dev'], + '{gskinnerUrl}': [$strings.homeMenuAboutGskinner, 'https://gskinner.com/flutter'], + }), + ...buildSpan('\n\n'), + ...buildSpan($strings.homeMenuAboutLearn, linkSupplants: { + '{wonderousUrl}': [$strings.homeMenuAboutApp, 'https://wonderous.app'], + }), + ...buildSpan('\n\n'), + ...buildSpan($strings.homeMenuAboutSource, linkSupplants: { + '{githubUrl}': [$strings.homeMenuAboutRepo, 'https://github.com/gskinnerTeam/flutter-wonders-app'], + }), + ...buildSpan('\n\n'), + ...buildSpan($strings.homeMenuAboutPublic, linkSupplants: { + '{metUrl}': [ + $strings.homeMenuAboutMet, + 'https://www.metmuseum.org/about-the-met/policies-and-documents/open-access' + ], + }), + ...buildSpan('\n\n'), + ...buildSpan($strings.homeMenuAboutPhotography, linkSupplants: { + '{unsplashUrl}': [$strings.homeMenuAboutUnsplash, 'https://unsplash.com/@gskinner/collections'], + }), + ], + ), + ), + ]), + ); + } +} diff --git a/lib/ui/screens/home_menu/home_menu.dart b/lib/ui/screens/home_menu/home_menu.dart new file mode 100644 index 00000000..6b023cdf --- /dev/null +++ b/lib/ui/screens/home_menu/home_menu.dart @@ -0,0 +1,194 @@ +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/app_backdrop.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/screens/home_menu/about_dialog_content.dart'; + +class HomeMenu extends StatelessWidget { + const HomeMenu({Key? key, required this.data}) : super(key: key); + final WonderData data; + + void _handleAboutPressed(BuildContext context) async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + showAboutDialog( + context: context, + applicationName: $strings.appName, + applicationVersion: packageInfo.version, + applicationLegalese: '© 2022 gskinner', + children: [AboutDialogContent()], + applicationIcon: Container( + color: $styles.colors.black, + padding: EdgeInsets.all($styles.insets.xs), + child: Image.asset( + ImagePaths.appLogoPlain, + fit: BoxFit.cover, + width: 52, + filterQuality: FilterQuality.high, + ), + ), + ); + } + + void _handleCollectionPressed(BuildContext context) => context.push(ScreenPaths.collection('')); + + void _handleTimelinePressed(BuildContext context) => context.push(ScreenPaths.timeline(data.type)); + + void _handleWonderPressed(BuildContext context, WonderData data) => Navigator.pop(context, data.type); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + /// Backdrop / Underlay + AppBackdrop( + strength: .5, + child: Container( + color: $styles.colors.greyStrong.withOpacity(.7), + ), + ), + + /// Back btn + BackBtn.close( + bgColor: Colors.transparent, + iconColor: $styles.colors.offWhite, + ).safe(), + + /// Content + Positioned.fill( + child: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: $styles.insets.lg), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Spacer(flex: 3), + _buildIconGrid(context) + .animate() + .fade(duration: $styles.times.fast) + .scale(begin: .8, curve: Curves.easeOut), + Spacer(flex: 2), + _buildBottomBtns(context), + Gap($styles.insets.xl), + ], + ), + ), + ), + ) + ], + ); + } + + Widget _buildIconGrid(BuildContext context) { + return GridView.count( + physics: NeverScrollableScrollPhysics(), + crossAxisCount: 3, + clipBehavior: Clip.none, + shrinkWrap: true, + crossAxisSpacing: $styles.insets.sm, + mainAxisSpacing: $styles.insets.sm, + children: [ + _buildGridBtn(context, wondersLogic.all[0]), + _buildGridBtn(context, wondersLogic.all[1]), + _buildGridBtn(context, wondersLogic.all[2]), + _buildGridBtn(context, wondersLogic.all[3]), + Padding( + padding: EdgeInsets.all($styles.insets.sm), + child: SvgPicture.asset(SvgPaths.compassFull, color: $styles.colors.offWhite), + ), + _buildGridBtn(context, wondersLogic.all[4]), + _buildGridBtn(context, wondersLogic.all[5]), + _buildGridBtn(context, wondersLogic.all[6]), + _buildGridBtn(context, wondersLogic.all[7]), + ], + ); + } + + Widget _buildBottomBtns(BuildContext context) { + return SeparatedColumn( + separatorBuilder: () => Divider(thickness: 1.5, height: 1).animate().scale( + duration: $styles.times.slow, + delay: $styles.times.pageTransition + 200.ms, + curve: Curves.easeOutBack, + ), + children: [ + _MenuTextBtn( + label: $strings.homeMenuButtonExplore, + icon: AppIcons.timeline, + onPressed: () => _handleTimelinePressed(context)), + _MenuTextBtn( + label: $strings.homeMenuButtonView, + icon: AppIcons.collection, + onPressed: () => _handleCollectionPressed(context)), + _MenuTextBtn( + label: $strings.homeMenuButtonAbout, + icon: AppIcons.info, + onPressed: () => _handleAboutPressed(context), + ), + ] + .animate(interval: 50.ms) + .fade(delay: $styles.times.pageTransition + 50.ms) + .slide(begin: Offset(0, .1), curve: Curves.easeOut), + ); + } + + Widget _buildGridBtn(BuildContext context, WonderData btnData) { + bool isSelected = btnData == data; + return AspectRatio( + aspectRatio: 1, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular($styles.corners.md), + boxShadow: !isSelected + ? null + : [ + BoxShadow( + color: Colors.black.withOpacity(.3), + blurRadius: 3, + spreadRadius: 3, + offset: Offset(0, 2), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular($styles.corners.md), + child: AppBtn( + border: !isSelected ? null : BorderSide(color: $styles.colors.offWhite, width: 5), + bgColor: btnData.type.fgColor, + onPressed: () => _handleWonderPressed(context, btnData), + padding: EdgeInsets.zero, + semanticLabel: btnData.title, + child: SizedBox.expand(child: Image.asset(btnData.type.homeBtn, fit: BoxFit.cover)), + ), + ), + ), + ); + } +} + +class _MenuTextBtn extends StatelessWidget { + const _MenuTextBtn({Key? key, required this.label, required this.onPressed, required this.icon}) : super(key: key); + final String label; + final VoidCallback onPressed; + final AppIcons icon; + + @override + Widget build(BuildContext context) { + return AppBtn( + expand: true, + padding: EdgeInsets.symmetric(vertical: $styles.insets.sm), + onPressed: onPressed, + bgColor: Colors.transparent, + semanticLabel: label, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AppIcon(icon, color: $styles.colors.offWhite), + Gap($styles.insets.xs), + Text(label, style: $styles.text.bodyBold.copyWith(height: 1)), + ], + ), + ); + } +} diff --git a/lib/ui/screens/intro/intro_screen.dart b/lib/ui/screens/intro/intro_screen.dart new file mode 100644 index 00000000..6f20a4b9 --- /dev/null +++ b/lib/ui/screens/intro/intro_screen.dart @@ -0,0 +1,270 @@ +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/common/controls/app_page_indicator.dart'; +import 'package:wonders/ui/common/themed_text.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; + +class IntroScreen extends StatefulWidget { + const IntroScreen({Key? key}) : super(key: key); + + @override + State createState() => _IntroScreenState(); +} + +class _IntroScreenState extends State { + static const double _imageSize = 264; + static const double _logoHeight = 126; + static const double _textHeight = 155; + static const double _pageIndicatorHeight = 55; + + static List<_PageData> pageData = [ + _PageData($strings.introTitleJourney, $strings.introDescriptionNavigate, 'camel', '1'), + _PageData($strings.introTitleExplore, $strings.introDescriptionUncover, 'petra', '2'), + _PageData($strings.introTitleDiscover, $strings.introDescriptionLearn, 'statue', '3'), + ]; + + late final PageController _pageController = PageController()..addListener(_handlePageChanged); + final ValueNotifier _currentPage = ValueNotifier(0); + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + void _handleIntroCompletePressed() { + context.go(ScreenPaths.home); + settingsLogic.hasCompletedOnboarding.value = true; + } + + void _handlePageChanged() { + int newPage = _pageController.page?.round() ?? 0; + _currentPage.value = newPage; + } + + void _handleSemanticSwipe(int dir) { + _pageController.animateToPage((_pageController.page ?? 0).round() + dir, + duration: $styles.times.fast, curve: Curves.easeOut); + } + + @override + Widget build(BuildContext context) { + // This view uses a full screen PageView to enable swipe navigation. + // However, we only want the title / description to actually swipe, + // so we stack a PageView with that content over top of all the other + // content, and line up their layouts. + + final List pages = pageData.map((e) => _Page(data: e)).toList(); + + final Widget content = Stack(children: [ + // page view with title & description: + MergeSemantics( + child: Semantics( + onIncrease: () => _handleSemanticSwipe(1), + onDecrease: () => _handleSemanticSwipe(-1), + child: PageView( + controller: _pageController, + children: pages, + onPageChanged: (_) => AppHaptics.lightImpact(), + ), + ), + ), + + IgnorePointer( + ignoringSemantics: false, + child: Column(children: [ + Spacer(), + + // logo: + Semantics( + header: true, + child: Container( + height: _logoHeight, + alignment: Alignment.center, + child: _WonderousLogo(), + ), + ), + + // masked image: + SizedBox( + height: _imageSize, + width: _imageSize, + child: ValueListenableBuilder( + valueListenable: _currentPage, + builder: (_, value, __) { + return AnimatedSwitcher( + duration: $styles.times.slow, + child: KeyedSubtree( + key: ValueKey(value), // so AnimatedSwitcher sees it as a different child. + child: _PageImage(data: pageData[value]), + ), + ); + }, + ), + ), + + // placeholder gap for text: + Gap(_IntroScreenState._textHeight), + + // page indicator: + Container( + height: _pageIndicatorHeight, + alignment: Alignment(0.0, -0.75), + child: + AppPageIndicator(count: pageData.length, controller: _pageController, color: $styles.colors.offWhite), + ), + + Spacer(flex: 2), + ]), + ), + + // finish button: + Positioned( + right: $styles.insets.lg, + bottom: $styles.insets.lg, + child: _buildFinishBtn(context), + ), + + // nav help text: + BottomCenter( + child: Padding( + padding: EdgeInsets.only(bottom: $styles.insets.lg), + child: _buildNavText(context), + ), + ), + ]); + + return DefaultTextColor( + color: $styles.colors.offWhite, + child: Container( + color: $styles.colors.black, + child: SafeArea(child: content.animate().fadeIn(delay: 500.ms)), + ), + ); + } + + Widget _buildFinishBtn(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _currentPage, + builder: (_, pageIndex, __) { + return AnimatedOpacity( + opacity: pageIndex == pageData.length - 1 ? 1 : 0, + duration: $styles.times.fast, + child: CircleIconBtn( + icon: AppIcons.next_large, + bgColor: $styles.colors.accent1, + onPressed: _handleIntroCompletePressed, + semanticLabel: $strings.introSemanticEnterApp, + ), + ); + }, + ); + } + + Widget _buildNavText(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _currentPage, + builder: (_, pageIndex, __) { + return AnimatedOpacity( + opacity: pageIndex == pageData.length - 1 ? 0 : 1, + duration: $styles.times.fast, + child: Semantics( + onTapHint: $strings.introSemanticNavigate, + onTap: () { + final int current = _pageController.page!.round(); + _pageController.animateToPage(current + 1, duration: 250.ms, curve: Curves.easeIn); + }, + child: Text($strings.introSemanticSwipeLeft, style: $styles.text.bodySmall), + ), + ); + }, + ); + } +} + +@immutable +class _PageData { + const _PageData(this.title, this.desc, this.img, this.mask); + + final String title; + final String desc; + final String img; + final String mask; +} + +class _Page extends StatelessWidget { + const _Page({Key? key, required this.data}) : super(key: key); + + final _PageData data; + + @override + Widget build(BuildContext context) { + return Semantics( + liveRegion: true, + child: Column(children: [ + Spacer(), + Gap(_IntroScreenState._imageSize + _IntroScreenState._logoHeight), + SizedBox( + height: _IntroScreenState._textHeight, + width: _IntroScreenState._imageSize + $styles.insets.md, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(data.title, style: $styles.text.wonderTitle.copyWith(fontSize: 24)), + Gap($styles.insets.sm), + Text(data.desc, style: $styles.text.body, textAlign: TextAlign.center), + ], + ), + ), + Gap(_IntroScreenState._pageIndicatorHeight), + Spacer(flex: 2), + ]), + ); + } +} + +class _WonderousLogo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ExcludeSemantics( + child: SvgPicture.asset(SvgPaths.compassSimple, color: $styles.colors.offWhite, height: 48), + ), + Gap($styles.insets.xs), + Text( + $strings.introSemanticWonderous, + style: $styles.text.wonderTitle.copyWith(fontSize: 32, color: $styles.colors.offWhite), + ) + ], + ); + } +} + +class _PageImage extends StatelessWidget { + const _PageImage({Key? key, required this.data}) : super(key: key); + + final _PageData data; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + SizedBox.expand( + child: Image.asset( + '${ImagePaths.common}/intro-${data.img}.jpg', + fit: BoxFit.cover, + alignment: Alignment.centerRight, + ), + ), + Positioned.fill( + child: Image.asset( + '${ImagePaths.common}/intro-mask-${data.mask}.png', + fit: BoxFit.fill, + )), + ], + ); + } +} diff --git a/lib/ui/screens/photo_gallery/photo_gallery.dart b/lib/ui/screens/photo_gallery/photo_gallery.dart new file mode 100644 index 00000000..f3475fff --- /dev/null +++ b/lib/ui/screens/photo_gallery/photo_gallery.dart @@ -0,0 +1,253 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/unsplash_photo_data.dart'; +import 'package:wonders/ui/common/controls/app_loading_indicator.dart'; +import 'package:wonders/ui/common/controls/eight_way_swipe_detector.dart'; +import 'package:wonders/ui/common/hidden_collectible.dart'; +import 'package:wonders/ui/common/modals/fullscreen_url_img_viewer.dart'; +import 'package:wonders/ui/common/unsplash_photo.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; + +part 'widgets/_animated_cutout_overlay.dart'; + +class PhotoGallery extends StatefulWidget { + const PhotoGallery({Key? key, this.imageSize, required this.collectionId, required this.wonderType}) + : super(key: key); + final Size? imageSize; + final String collectionId; + final WonderType wonderType; + + @override + State createState() => _PhotoGalleryState(); +} + +class _PhotoGalleryState extends State { + static const int _gridSize = 5; + // Index starts in the middle of the grid (eg, 25 items, index will start at 13) + int _index = ((_gridSize * _gridSize) / 2).round(); + Offset _lastSwipeDir = Offset.zero; + final double _scale = 1; + bool _skipNextOffsetTween = false; + late Duration swipeDuration = $styles.times.med * .4; + final _photoIds = ValueNotifier>([]); + int get _imgCount => pow(_gridSize, 2).round(); + + @override + void initState() { + super.initState(); + _initPhotoIds(); + } + + Future _initPhotoIds() async { + var ids = unsplashLogic.getCollectionPhotos(widget.collectionId); + if (ids != null && ids.isNotEmpty) { + // Ensure we have enough images to fill the grid, repeat if necessary + while (ids.length < _imgCount) { + ids.addAll(List.from(ids)); + if (ids.length > _imgCount) ids.length = _imgCount; + } + } + setState(() => _photoIds.value = ids ?? []); + } + + void _setIndex(int value, {bool skipAnimation = false}) { + if (value < 0 || value >= _imgCount) return; + _skipNextOffsetTween = skipAnimation; + setState(() => _index = value); + } + + /// Determine the required offset to show the current selected index. + /// index=0 is top-left, and the index=max is bottom-right. + Offset _calculateCurrentOffset(double padding, Size size) { + double halfCount = (_gridSize / 2).floorToDouble(); + Size paddedImageSize = Size(size.width + padding, size.height + padding); + // Get the starting offset that would show the top-left image (index 0) + final originOffset = Offset(halfCount * paddedImageSize.width, halfCount * paddedImageSize.height); + // Add the offset for the row/col + int col = _index % _gridSize; + int row = (_index / _gridSize).floor(); + final indexedOffset = Offset(-paddedImageSize.width * col, -paddedImageSize.height * row); + return originOffset + indexedOffset; + } + + /// Used for hiding collectibles around the photo grid. + int _getCollectibleIndex() { + switch (widget.wonderType) { + case WonderType.chichenItza: + case WonderType.petra: + return 0; + case WonderType.colosseum: + case WonderType.pyramidsGiza: + return _gridSize - 1; + case WonderType.christRedeemer: + case WonderType.machuPicchu: + return _imgCount - 1; + case WonderType.greatWall: + case WonderType.tajMahal: + return _imgCount - _gridSize; + } + } + + /// Converts a swipe direction into a new index + void _handleSwipe(Offset dir) { + // Calculate new index, y swipes move by an entire row, x swipes move one index at a time + int newIndex = _index; + if (dir.dy != 0) newIndex += _gridSize * (dir.dy > 0 ? -1 : 1); + if (dir.dx != 0) newIndex += (dir.dx > 0 ? -1 : 1); + // After calculating new index, exit early if we don't like it... + if (newIndex < 0 || newIndex > _imgCount - 1) return; // keep the index in range + if (dir.dx < 0 && newIndex % _gridSize == 0) return; // prevent right-swipe when at right side + if (dir.dx > 0 && newIndex % _gridSize == _gridSize - 1) return; // prevent left-swipe when at left side + _lastSwipeDir = dir; + AppHaptics.lightImpact(); + _setIndex(newIndex); + } + + Future _handleImageTapped(int index) async { + if (_index == index) { + int? newIndex = await Navigator.push( + context, + CupertinoPageRoute(builder: (_) { + final urls = _photoIds.value.map((e) { + return UnsplashPhotoData.getSelfHostedUrl(e, UnsplashPhotoSize.med); + }).toList(); + return FullscreenUrlImgViewer(urls: urls, index: _index); + }), + ); + if (newIndex != null) { + _setIndex(newIndex, skipAnimation: true); + } + } else { + _setIndex(index); + } + } + + bool _checkCollectibleIndex(int index) { + return index == _getCollectibleIndex() && collectiblesLogic.isLost(widget.wonderType, 1); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: _photoIds, + builder: (_, value, __) { + if (value.isEmpty) { + return Center(child: AppLoadingIndicator()); + } + + Size imgSize = (widget.imageSize ?? Size(context.widthPx * .66, context.heightPx * .5)) * _scale; + // Get transform offset for the current _index + final padding = $styles.insets.md; + + var gridOffset = _calculateCurrentOffset(padding, imgSize); + gridOffset += Offset(0, -context.mq.padding.top / 2); + final offsetTweenDuration = _skipNextOffsetTween ? Duration.zero : swipeDuration; + final cutoutTweenDuration = _skipNextOffsetTween ? Duration.zero : swipeDuration * .5; + return Stack( + children: [ + // A overlay with a transparent middle sits on top of everything, animating itself each time index changes + _AnimatedCutoutOverlay( + animationKey: ValueKey(_index), + cutoutSize: imgSize, + swipeDir: _lastSwipeDir, + duration: cutoutTweenDuration, + opacity: _scale == 1 ? .7 : .5, + child: SafeArea( + bottom: false, + // Place content in overflow box, to allow it to flow outside the parent + child: OverflowBox( + maxWidth: _gridSize * imgSize.width + padding * (_gridSize - 1), + maxHeight: _gridSize * imgSize.height + padding * (_gridSize - 1), + alignment: Alignment.center, + // Detect swipes in order to change index + child: EightWaySwipeDetector( + onSwipe: _handleSwipe, + threshold: 30, + // A tween animation builder moves from image to image based on current offset + child: TweenAnimationBuilder( + tween: Tween(begin: gridOffset, end: gridOffset), + duration: offsetTweenDuration, + curve: Curves.easeOut, + builder: (_, value, child) => Transform.translate(offset: value, child: child), + child: GridView.count( + physics: NeverScrollableScrollPhysics(), + crossAxisCount: _gridSize, + childAspectRatio: imgSize.aspectRatio, + mainAxisSpacing: padding, + crossAxisSpacing: padding, + children: List.generate(_imgCount, (i) => _buildImage(i, swipeDuration, imgSize)), + ), + ), + ), + ), + ), + ), + ], + ); + }); + } + + Widget _buildImage(int index, Duration swipeDuration, Size imgSize) { + /// Bind to collectibles.statesById because we might need to rebuild if a collectible is found. + return ValueListenableBuilder( + valueListenable: collectiblesLogic.statesById, + builder: (_, __, ___) { + bool selected = index == _index; + final imgUrl = _photoIds.value[index]; + + late String semanticLbl; + if (_checkCollectibleIndex(index)) { + semanticLbl = $strings.collectibleItemSemanticCollectible; + } else { + semanticLbl = !selected + ? StringUtils.supplant($strings.photoGallerySemanticFocus, { + '{photoIndex}': (index + 1).toString(), + '{photoTotal}': _imgCount.toString(), + }) + : StringUtils.supplant($strings.photoGallerySemanticFullscreen, { + '{photoIndex}': (index + 1).toString(), + '{photoTotal}': _imgCount.toString(), + }); + } + return MergeSemantics( + child: Semantics( + focused: selected, + image: !_checkCollectibleIndex(index), + liveRegion: selected, + onIncrease: () => _handleImageTapped(_index + 1), + onDecrease: () => _handleImageTapped(_index - 1), + child: AppBtn.basic( + semanticLabel: semanticLbl, + onPressed: () { + if (_checkCollectibleIndex(index) && selected) return; + _handleImageTapped(index); + }, + child: _checkCollectibleIndex(index) + ? HiddenCollectible(widget.wonderType, index: 1, size: 100) + : ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + width: imgSize.width, + height: imgSize.height, + child: TweenAnimationBuilder( + duration: $styles.times.med, + curve: Curves.easeOut, + tween: Tween(begin: 1, end: selected ? 1.15 : 1), + builder: (_, value, child) => Transform.scale(scale: value, child: child), + child: UnsplashPhoto( + imgUrl, + fit: BoxFit.cover, + size: UnsplashPhotoSize.med, + ).animate().fade(), + ), + ), + ), + ), + ), + ); + }); + } +} diff --git a/lib/ui/screens/photo_gallery/widgets/_animated_cutout_overlay.dart b/lib/ui/screens/photo_gallery/widgets/_animated_cutout_overlay.dart new file mode 100644 index 00000000..5c3559d6 --- /dev/null +++ b/lib/ui/screens/photo_gallery/widgets/_animated_cutout_overlay.dart @@ -0,0 +1,78 @@ +part of '../photo_gallery.dart'; + +/// An overlay with a animated cutout in the middle. +/// When animationKey changes, the box animates its size, shrinking then returning to its original size. +/// Uses[_CutoutClipper] to create the cutout. +class _AnimatedCutoutOverlay extends StatelessWidget { + const _AnimatedCutoutOverlay( + {Key? key, + required this.child, + required this.cutoutSize, + required this.animationKey, + this.duration, + required this.swipeDir, + required this.opacity}) + : super(key: key); + final Widget child; + final Size cutoutSize; + final Key animationKey; + final Offset swipeDir; + final Duration? duration; + final double opacity; + @override + Widget build(BuildContext context) { + return Stack( + children: [ + child, + Animate( + effects: [CustomEffect(builder: _buildAnimatedCutout, curve: Curves.easeOut, duration: duration)], + key: animationKey, + onComplete: (c) => c.reverse(), + child: IgnorePointer(child: Container(color: Colors.black.withOpacity(opacity))), + ), + ], + ); + } + + /// Scales from 1 --> (1 - scaleAmt) --> 1 + Widget _buildAnimatedCutout(BuildContext context, double anim, Widget child) { + // controls how much the center cutout will shrink when changing images + const scaleAmt = .25; + final size = Size( + cutoutSize.width * (1 - scaleAmt * anim * swipeDir.dx.abs()), + cutoutSize.height * (1 - scaleAmt * anim * swipeDir.dy.abs()), + ); + return ClipPath(clipper: _CutoutClipper(size), child: child); + } +} + +/// Creates an overlay with a hole in the middle of a certain size. +class _CutoutClipper extends CustomClipper { + _CutoutClipper(this.cutoutSize); + final Size cutoutSize; + + @override + Path getClip(Size size) { + double padX = (size.width - cutoutSize.width) / 2; + double padY = (size.height - cutoutSize.height) / 2; + + return Path.combine( + PathOperation.difference, + Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height)), + Path() + ..addRRect( + RRect.fromLTRBR( + padX, + padY, + size.width - padX, + size.height - padY, + Radius.circular(6), + ), + ) + ..close(), + ); + } + + @override + bool shouldReclip(_CutoutClipper oldClipper) => oldClipper.cutoutSize != cutoutSize; +} diff --git a/lib/ui/screens/timeline/timeline_screen.dart b/lib/ui/screens/timeline/timeline_screen.dart new file mode 100644 index 00000000..94cfee85 --- /dev/null +++ b/lib/ui/screens/timeline/timeline_screen.dart @@ -0,0 +1,84 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/debouncer.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/timeline_data.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/blend_mask.dart'; +import 'package:wonders/ui/common/controls/simple_header.dart'; +import 'package:wonders/ui/common/dashed_line.dart'; +import 'package:wonders/ui/common/list_gradient.dart'; +import 'package:wonders/ui/common/timeline_event_card.dart'; +import 'package:wonders/ui/common/utils/app_haptics.dart'; +import 'package:wonders/ui/common/wonders_timeline_builder.dart'; + +part 'widgets/_bottom_scrubber.dart'; +part 'widgets/_dashed_divider_with_year.dart'; +part 'widgets/_event_markers.dart'; +part 'widgets/_event_popups.dart'; +part 'widgets/_scrolling_viewport.dart'; +part 'widgets/_scrolling_viewport_controller.dart'; +part 'widgets/_timeline_section.dart'; +part 'widgets/_year_markers.dart'; + +class TimelineScreen extends StatefulWidget { + final WonderType? type; + + const TimelineScreen({Key? key, required this.type}) : super(key: key); + + @override + State createState() => _TimelineScreenState(); +} + +class _TimelineScreenState extends State { + /// Create a scroll controller that the top and bottom timelines can share + final ScrollController _scroller = ScrollController(); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, constraints) { + // Determine min and max size of the timeline based on the size available to this widget + const double scrubberSize = 70; + const double minSize = 1200; + const double maxSize = 5500; + return Container( + color: $styles.colors.black, + child: Padding( + padding: EdgeInsets.only(bottom: 0), + child: Column( + children: [ + SimpleHeader($strings.timelineTitleGlobalTimeline), + + /// Vertically scrolling timeline, manages a ScrollController. + Expanded( + child: Stack( + children: [ + /// The timeline content itself + _ScrollingViewport( + scroller: _scroller, + minSize: minSize, + maxSize: maxSize, + selectedWonder: widget.type, + ), + ], + ), + ), + + /// Mini Horizontal timeline, reacts to the state of the larger scrolling timeline, + /// and changes the timelines scroll position on Hz drag + _BottomScrubber( + _scroller, + size: scrubberSize, + timelineMinSize: minSize, + selectedWonder: widget.type, + ), + Gap($styles.insets.lg), + ], + ), + ), + ); + }); + } +} diff --git a/lib/ui/screens/timeline/widgets/_bottom_scrubber.dart b/lib/ui/screens/timeline/widgets/_bottom_scrubber.dart new file mode 100644 index 00000000..bc8a3040 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_bottom_scrubber.dart @@ -0,0 +1,102 @@ +part of '../timeline_screen.dart'; + +class _BottomScrubber extends StatelessWidget { + const _BottomScrubber(this.scroller, + {Key? key, required this.timelineMinSize, required this.size, required this.selectedWonder}) + : super(key: key); + final ScrollController? scroller; + final double timelineMinSize; + final double size; + final WonderType? selectedWonder; + + /// Calculate what fraction the scroller has travelled + double _calculateScrollFraction(ScrollPosition? pos) { + if (pos == null || pos.maxScrollExtent == 0) return 0; + return pos.pixels / pos.maxScrollExtent; + } + + /// Calculates what fraction of the scroller is current visible + double _calculateViewPortFraction(ScrollPosition? pos) { + if (pos == null) return 1; + final viewportSize = pos.viewportDimension; + final result = viewportSize / (pos.maxScrollExtent + viewportSize); + return result.clamp(0, 1); + } + + @override + Widget build(BuildContext context) { + final scroller = this.scroller; + + /// It might take a frame until we receive a valid scroller + if (scroller == null) return SizedBox.shrink(); + void handleScrubberPan(DragUpdateDetails details) { + if (!scroller.hasClients) return; + double dragMultiplier = (scroller.position.maxScrollExtent + timelineMinSize) / context.widthPx; + double newPos = scroller.position.pixels + details.delta.dx * dragMultiplier; + scroller.position.jumpTo(newPos.clamp(0, scroller.position.maxScrollExtent)); + } + + /// Create a list for the timeline builder to indicate the selected wonder + final wonder = selectedWonder; + return SizedBox( + height: size, + child: Stack( + children: [ + /// Timeline background + Padding( + padding: EdgeInsets.all($styles.insets.sm), + child: WondersTimelineBuilder( + crossAxisGap: 4, + selectedWonders: [ + if (wonder != null) ...[wonder] + ], + ), + ), + + /// Visible area, follows the position of scroller + AnimatedBuilder( + animation: scroller, + builder: (_, __) { + ScrollPosition? pos; + if (scroller.hasClients) pos = scroller.position; + // Get current scroll offset and move the viewport to match + double scrollFraction = _calculateScrollFraction(pos); + double viewPortFraction = _calculateViewPortFraction(pos); + final scrubberAlign = Alignment(-1 + scrollFraction * 2, 0); + + return Positioned.fill( + child: Semantics( + container: true, + slider: true, + label: $strings.bottomScrubberSemanticTimeline, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onPanUpdate: handleScrubberPan, + + /// Scrub area + child: Align( + alignment: scrubberAlign, + child: FractionallySizedBox( + widthFactor: viewPortFraction, + heightFactor: 1, + child: _buildOutlineBox(context, scrubberAlign), + ), + ), + ), + ), + ); + }, + ) + ], + ), + ); + } + + Container _buildOutlineBox(BuildContext context, Alignment alignment) { + final borderColor = $styles.colors.white; + return Container( + decoration: BoxDecoration(border: Border.all(color: borderColor)), + child: Align(alignment: alignment, child: DashedLine(vertical: true)), + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_dashed_divider_with_year.dart b/lib/ui/screens/timeline/widgets/_dashed_divider_with_year.dart new file mode 100644 index 00000000..4aec9fa8 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_dashed_divider_with_year.dart @@ -0,0 +1,41 @@ +part of '../timeline_screen.dart'; + +class _DashedDividerWithYear extends StatelessWidget { + const _DashedDividerWithYear(this.year, {Key? key}) : super(key: key); + final int year; + + @override + Widget build(BuildContext context) { + int yrGap = 10; + final roundedYr = (year / yrGap).round() * yrGap; + return Stack( + children: [ + Center(child: DashedLine()), + CenterRight( + child: FractionalTranslation( + translation: Offset(0, -.5), + child: MergeSemantics( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${roundedYr.abs()}', + style: $styles.text.h2.copyWith(color: $styles.colors.white, shadows: $styles.shadows.text), + ), + Gap($styles.insets.xs), + Text( + StringUtils.getYrSuffix(roundedYr), + style: $styles.text.body.copyWith( + color: Colors.white, + shadows: $styles.shadows.textStrong, + ), + ), + ], + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_event_markers.dart b/lib/ui/screens/timeline/widgets/_event_markers.dart new file mode 100644 index 00000000..e7675730 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_event_markers.dart @@ -0,0 +1,125 @@ +part of '../timeline_screen.dart'; + +/// A vertically aligned stack of dots that represent global events +/// The event closest to the [selectedYr] param will be visible selected +class _EventMarkers extends StatefulWidget { + const _EventMarkers(this.selectedYr, {Key? key, required this.onEventChanged}) : super(key: key); + + final void Function(TimelineEvent? event) onEventChanged; + final int selectedYr; + + @override + State<_EventMarkers> createState() => _EventMarkersState(); +} + +class _EventMarkersState extends State<_EventMarkers> { + int get startYr => wondersLogic.timelineStartYear; + + int get endYr => wondersLogic.timelineEndYear; + + late final int _totalYrs = endYr - startYr; + + TimelineEvent? selectedEvent; + + /// Normalizes a given year to a value from 0 - 1, based on start and end yr. + double _calculateOffsetY(int yr) => (yr - startYr) / _totalYrs; + + /// Loops through the global events, and does a px-based check to see whether + /// one of them should be selected (as oppose to year-based proximity). + /// This ensures consistent UX at different zoom levels. + void _updateSelectedEvent(double maxPxHeight) { + const double minDistance = 10; + TimelineEvent? closestEvent; + double closestDistance = double.infinity; + // Convert current yr to a px position + double currentYearPx = _calculateOffsetY(widget.selectedYr) * maxPxHeight; + for (var e in timelineLogic.events) { + // Convert both the event.yr to px, and compare with currentYearPx + double eventPx = _calculateOffsetY(e.year) * maxPxHeight; + double d = (eventPx - currentYearPx).abs(); + // Keep the closest event that is within minDistance + if (d <= minDistance && d < closestDistance) { + closestEvent = e; + closestDistance = d; + } + } + // Dispatch if event has actually changed since last time + if (closestEvent != selectedEvent) { + scheduleMicrotask(() => widget.onEventChanged(closestEvent)); + } + selectedEvent = closestEvent; + } + + @override + Widget build(BuildContext context) { + return IgnorePointer( + ignoringSemantics: false, + child: LayoutBuilder(builder: (_, constraints) { + /// Figure out which event is "selected" + _updateSelectedEvent(constraints.maxHeight); + + /// Create a marker for each event + List markers = timelineLogic.events.map((event) { + final offsetY = _calculateOffsetY(event.year); + return _EventMarker(offsetY, + isSelected: event == selectedEvent, semanticLabel: '${event.year}: ${event.description}'); + }).toList(); + + /// Stack of fractionally positioned markers + return FocusTraversalGroup( + policy: WidgetOrderTraversalPolicy(), + child: Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.only(left: 75), + child: SizedBox( + width: 20, + child: Stack(children: markers), + ), + ), + ); + }), + ); + } +} + +/// A dot that represents a single global event. +/// Animated to a selected state which is is larger in size. +class _EventMarker extends StatelessWidget { + const _EventMarker( + this.offset, { + Key? key, + required this.isSelected, + required this.semanticLabel, + }) : super(key: key); + final double offset; + final bool isSelected; + final String semanticLabel; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment(0, -1 + offset * 2), + child: Semantics( + label: semanticLabel, + child: Container( + alignment: Alignment.center, + height: 30, + child: AnimatedContainer( + width: isSelected ? 6 : 2, + height: isSelected ? 6 : 2, + curve: Curves.easeOutBack, + duration: $styles.times.med, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(99), + color: $styles.colors.accent1, + boxShadow: [ + BoxShadow( + color: $styles.colors.accent1.withOpacity(isSelected ? .5 : 0), spreadRadius: 3, blurRadius: 3), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_event_popups.dart b/lib/ui/screens/timeline/widgets/_event_popups.dart new file mode 100644 index 00000000..70410351 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_event_popups.dart @@ -0,0 +1,67 @@ +part of '../timeline_screen.dart'; + +class _EventPopups extends StatefulWidget { + const _EventPopups({Key? key, required this.currentEvent}) : super(key: key); + final TimelineEvent? currentEvent; + + @override + State<_EventPopups> createState() => _EventPopupsState(); +} + +class _EventPopupsState extends State<_EventPopups> { + final _debouncer = Debouncer(500.ms); + TimelineEvent? _eventToShow; + + @override + void dispose() { + _debouncer.reset(); + super.dispose(); + } + + @override + void didUpdateWidget(covariant _EventPopups oldWidget) { + super.didUpdateWidget(oldWidget); + _debouncer.call(showCardForCurrentYr); + } + + void showCardForCurrentYr() { + setState(() { + _eventToShow = widget.currentEvent; + }); + } + + @override + Widget build(BuildContext context) { + final evt = _eventToShow; + return TopCenter( + child: ClipRect( + child: IgnorePointer( + ignoringSemantics: false, + child: AnimatedSwitcher( + duration: $styles.times.fast, + child: evt == null + ? SizedBox.shrink() + : Semantics( + liveRegion: true, + child: Animate( + effects: const [ + SlideEffect(begin: Offset(0, -.1)), + ], + key: ValueKey(_eventToShow?.year), + child: IntrinsicHeight( + child: Padding( + padding: EdgeInsets.all($styles.insets.md), + child: TimelineEventCard( + text: evt.description, + year: evt.year, + ), + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart b/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart new file mode 100644 index 00000000..13627b16 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart @@ -0,0 +1,182 @@ +part of '../timeline_screen.dart'; + +class _ScrollingViewport extends StatefulWidget { + const _ScrollingViewport({ + Key? key, + // ignore: unused_element + this.onInit, + required this.scroller, + required this.minSize, + required this.maxSize, + required this.selectedWonder, + }) : super(key: key); + final double minSize; + final double maxSize; + final ScrollController scroller; + final WonderType? selectedWonder; + final void Function(_ScrollingViewportController controller)? onInit; + + @override + State<_ScrollingViewport> createState() => _ScalingViewportState(); +} + +class _ScalingViewportState extends State<_ScrollingViewport> { + late final _ScrollingViewportController controller = _ScrollingViewportController(this); + static const double _minTimelineSize = 100; + final _currentEventMarker = ValueNotifier(null); + @override + void initState() { + super.initState(); + controller.init(); + widget.onInit?.call(controller); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + void _handleEventMarkerChanged(TimelineEvent? event) { + _currentEventMarker.value = event; + AppHaptics.selectionClick(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, constraints) { + // cache constraints, so they can be used to maintain the selected year while zooming + controller._constraints = constraints; + double vtPadding = constraints.maxHeight / 2; + double size = controller.calculateContentHeight(); + return GestureDetector( + // Handle pinch to zoom + onScaleUpdate: controller._handleScaleUpdate, + onScaleStart: controller._handleScaleStart, + behavior: HitTestBehavior.translucent, + // Fade in entire view when first shown + child: Animate( + effects: const [FadeEffect()], + child: Stack( + children: [ + Column( + children: [ + /// Main scrolling area, holds the year markers, and the [WondersTimelineBuilder] + Expanded( + child: _buildScrollingArea(vtPadding, size, context, constraints), + ), + Gap($styles.insets.xs), + + /// Era Text (classical, modern etc) + _buildAnimatedEraText(context), + Gap($styles.insets.xs), + ], + ), + + /// Dashed line with a year that changes as we scroll + IgnorePointer( + ignoringSemantics: false, + child: AnimatedBuilder( + animation: controller.scroller, + builder: (_, __) { + return _DashedDividerWithYear(controller.calculateYearFromScrollPos()); + }, + ), + ), + ], + ), + ), + ); + }); + } + + AnimatedBuilder _buildAnimatedEraText(BuildContext context) { + return AnimatedBuilder( + animation: controller.scroller, + builder: (_, __) { + String era = StringUtils.getEra(controller.calculateYearFromScrollPos()); + final style = $styles.text.body.copyWith(color: $styles.colors.offWhite); + return AnimatedSwitcher( + duration: $styles.times.fast, + child: Semantics( + liveRegion: true, + child: Text(era, key: ValueKey(era), style: style) + .animate(key: ValueKey(era)) + .slide(begin: Offset(0, .2))), + ); + }); + } + + Widget _buildScrollingArea(double vtPadding, double size, BuildContext context, BoxConstraints constraints) { + // Builds a TimelineSection, and passes it the currently selected yr based on scroll position. + // Rebuilds when timeline is scrolled. + Widget buildTimelineSection(WonderData data) { + return ClipRRect( + borderRadius: BorderRadius.circular(99), + child: AnimatedBuilder( + animation: controller.scroller, + builder: (_, __) => TimelineSection( + data, + controller.calculateYearFromScrollPos(), + selectedWonder: widget.selectedWonder, + ), + ), + ); + } + + return Stack( + children: [ + SingleChildScrollView( + controller: controller.scroller, + padding: EdgeInsets.symmetric(vertical: vtPadding), + // A stack inside a SizedBox which sets its overall height + child: SizedBox( + height: size, + width: double.infinity, + child: Stack( + children: [ + /// Year Markers + _YearMarkers(), + + /// individual timeline sections + Positioned.fill( + left: 100, + right: $styles.insets.sm, + child: FocusTraversalGroup( + child: WondersTimelineBuilder( + axis: Axis.vertical, + crossAxisGap: max(6, (constraints.maxWidth - (120 * 3)) / 2), + minSize: _minTimelineSize, + timelineBuilder: (_, data, __) => buildTimelineSection(data)), + ), + ), + + /// Event Markers, rebuilds on scroll + AnimatedBuilder( + animation: controller.scroller, + builder: (_, __) => _EventMarkers( + controller.calculateYearFromScrollPos(), + onEventChanged: _handleEventMarkerChanged, + ), + ), + ], + ), + ), + ), + + /// Top and bottom gradients for visual style + ListOverscollGradient(), + BottomCenter( + child: ListOverscollGradient(bottomUp: true), + ), + + /// Event Popups, rebuilds when [_currentEventMarker] changes + ValueListenableBuilder( + valueListenable: _currentEventMarker, + builder: (_, data, __) { + return _EventPopups(currentEvent: data); + }) + ], + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_scrolling_viewport_controller.dart b/lib/ui/screens/timeline/widgets/_scrolling_viewport_controller.dart new file mode 100644 index 00000000..1b21a1ba --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_scrolling_viewport_controller.dart @@ -0,0 +1,86 @@ +part of '../timeline_screen.dart'; + +class _ScrollingViewportController extends ChangeNotifier { + _ScrollingViewportController(this.state); + final _ScalingViewportState state; + + int get startYr => wondersLogic.timelineStartYear; + int get endYr => wondersLogic.timelineEndYear; + + double _zoom = .5; + double _zoomOnScaleStart = 0; + late BoxConstraints _constraints; + _ScrollingViewport get widget => state.widget; + ScrollController get scroller => widget.scroller; + + void init() { + scheduleMicrotask(() { + setZoom(.5); + final w = widget.selectedWonder; + if (w != null) { + final data = wondersLogic.getData(w); + final pos = calculateScrollPosFromYear(data.startYr); + scroller.jumpTo(pos - 200); + scroller.animateTo(pos, duration: 1.35.seconds, curve: Curves.easeOutCubic); + } + }); + } + + /// Allows ancestors to set zoom directly + void setZoom(double d) { + // ignore: invalid_use_of_protected_member + state.setState(() { + // Determine current yr, based on scroll position + int currentYr = calculateYearFromScrollPos(); + + // Change zoom, which will scale our content, and change our scroll position + _zoom = d; + _zoom = _zoom.clamp(0, 1.0); + // Jump to whatever yr we were on before changing the zoom + jumpToYear(currentYr); + }); + } + + /// Jump to the scroll position for a given yr. Does not animated. + void jumpToYear(int yr, {bool animate = false}) { + double yrRatio = (yr - startYr) / (endYr - startYr); + double newMaxScroll = calculateContentHeight(); + final newPos = newMaxScroll * yrRatio; + if (animate) { + scroller.animateTo(newPos, duration: $styles.times.med, curve: Curves.easeOut); + } else { + scroller.jumpTo(newPos); + } + } + + /// Calculates current content height, taking zoom into account. + double calculateContentHeight() { + //double minSize = 300; + double vtPadding = _constraints.maxHeight / 2; + return lerpDouble(widget.minSize - vtPadding, widget.maxSize, _zoom) ?? widget.maxSize; + } + + /// Derive current yr based on the scroll position and the current content height. + int calculateYearFromScrollPos() { + if (scroller.hasClients == false) return startYr; + int totalYrs = endYr - startYr; + double currentPx = scroller.position.pixels; + double scrollAmt = currentPx / calculateContentHeight(); + int result = (startYr + scrollAmt * totalYrs).round(); + return result.clamp(startYr, endYr); + } + + double calculateScrollPosFromYear(int yr) { + int totalYrs = endYr - startYr; + double yrFraction = totalYrs / (yr - startYr); + return calculateContentHeight() / yrFraction; + } + + /// Since the onScale gesture always starts from 1, we need to hold onto the zoom + /// value that we had when the scale gesture started and multiply it with the gesture data, to get the real new scale. + void _handleScaleStart(ScaleStartDetails _) => _zoomOnScaleStart = _zoom; + + void _handleScaleUpdate(ScaleUpdateDetails details) { + setZoom(details.scale * _zoomOnScaleStart); + } +} diff --git a/lib/ui/screens/timeline/widgets/_timeline_section.dart b/lib/ui/screens/timeline/widgets/_timeline_section.dart new file mode 100644 index 00000000..799ec291 --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_timeline_section.dart @@ -0,0 +1,55 @@ +part of '../timeline_screen.dart'; + +class TimelineSection extends StatelessWidget { + const TimelineSection(this.data, this.selectedYr, {Key? key, required this.selectedWonder}) : super(key: key); + final WonderData data; + final int selectedYr; + final WonderType? selectedWonder; + + @override + Widget build(BuildContext context) { + bool isSelected = selectedWonder == data.type; + // get a fraction from 0 - 1 based on selected yr and start/end yr of the wonder + // 500, 250, 750 + int startYr = data.startYr, endYr = data.endYr; + double fraction = (selectedYr - startYr) / (endYr - startYr); + fraction = fraction.clamp(0, 1); + + return Semantics( + label: '${data.title}, ${StringUtils.supplant($strings.timelineSemanticDate, { + '{fromDate}': StringUtils.formatYr(data.startYr), + '{endDate}': StringUtils.formatYr(data.endYr) + })}', + child: IgnorePointer( + ignoringSemantics: false, + child: Container( + alignment: Alignment(0, -1 + fraction * 2), + padding: EdgeInsets.all($styles.insets.xs), + decoration: BoxDecoration(color: data.type.fgColor), + child: ClipRRect( + borderRadius: BorderRadius.circular(99), + child: BlendMask( + blendModes: isSelected ? [] : const [BlendMode.luminosity], + opacity: .6, + child: _buildWonderImage(), + ), + ), + ), + ), + ); + } + + Container _buildWonderImage() { + return Container( + height: 160, + decoration: BoxDecoration( + color: data.type.bgColor, + image: DecorationImage( + fit: BoxFit.cover, + alignment: Alignment(0, -.5), + image: AssetImage(data.type.flattened), + ), + ), + ); + } +} diff --git a/lib/ui/screens/timeline/widgets/_year_markers.dart b/lib/ui/screens/timeline/widgets/_year_markers.dart new file mode 100644 index 00000000..e0c96c5d --- /dev/null +++ b/lib/ui/screens/timeline/widgets/_year_markers.dart @@ -0,0 +1,67 @@ +part of '../timeline_screen.dart'; + +class _YearMarkers extends StatelessWidget { + _YearMarkers({Key? key}) : super(key: key); + + int get startYr => wondersLogic.timelineStartYear; + int get endYr => wondersLogic.timelineEndYear; + + late final int _totalYrs = endYr - startYr; + + /// Normalizes a given year to a value from 0 - 1, based on start and end yr. + double _calculateOffsetY(int yr) => (yr - startYr) / _totalYrs; + + @override + Widget build(BuildContext context) { + return IgnorePointer( + ignoringSemantics: false, + child: LayoutBuilder(builder: (_, constraints) { + int interval = 100; + if (constraints.maxHeight < 800) { + interval = 500; + } else if (constraints.maxHeight < 1500) { + interval = 250; + } + + // If interval is 100 and time is 0 - 1000 yrs, make a list of 11 items: + // [0, 100, 200, ..., 1000 ] + int numMarkers = (_totalYrs / interval).round() + 1; + late final markers = List.generate(numMarkers, (i) { + return startYr + i * interval; + }); + + return SizedBox( + width: 100, + child: AnimatedSwitcher( + duration: $styles.times.med, + child: Stack( + key: ValueKey(interval), + children: markers.map((yr) { + return _YearMarker(yr, _calculateOffsetY(yr)); + }).toList(), + ), + ), + ); + }), + ); + } +} + +class _YearMarker extends StatelessWidget { + const _YearMarker(this.yr, this.offset, {Key? key}) : super(key: key); + final int yr; + final double offset; + + @override + Widget build(BuildContext context) { + return ExcludeSemantics( + child: Align( + alignment: Alignment(0, -1 + offset * 2), + child: FractionalTranslation( + translation: Offset(0, 0), + child: Text('${yr.abs()}', style: $styles.text.body.copyWith(color: Colors.white, height: 1)), + ), + ), + ); + } +} diff --git a/lib/ui/screens/wallpaper_photo/wallpaper_photo_screen.dart b/lib/ui/screens/wallpaper_photo/wallpaper_photo_screen.dart new file mode 100644 index 00000000..ce44d3cc --- /dev/null +++ b/lib/ui/screens/wallpaper_photo/wallpaper_photo_screen.dart @@ -0,0 +1,176 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/app_icons.dart'; +import 'package:wonders/ui/common/controls/checkbox.dart'; +import 'package:wonders/ui/wonder_illustrations/common/animated_clouds.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_title_text.dart'; + +class WallpaperPhotoScreen extends StatefulWidget { + const WallpaperPhotoScreen({Key? key, required this.type}) : super(key: key); + final WonderType type; + + @override + State createState() => _WallpaperPhotoScreenState(); +} + +class _WallpaperPhotoScreenState extends State { + final GlobalKey _containerKey = GlobalKey(); + Widget? _illustration; + + bool _showTitleText = true; + Timer? _photoRetryTimer; + + @override + void dispose() { + _photoRetryTimer?.cancel(); + super.dispose(); + } + + void _handleTakePhoto(BuildContext context, String wonderName) async { + final boundary = _containerKey.currentContext?.findRenderObject() as RenderRepaintBoundary?; + if (boundary != null) { + wallpaperLogic.save(this, boundary, name: '${wonderName}_wallpaper'); + } + } + + void _handleSharePhoto(BuildContext context, String wonderName) async { + final boundary = _containerKey.currentContext!.findRenderObject() as RenderRepaintBoundary; + wallpaperLogic.share(context, boundary, name: '${wonderName}_wallpaper', wonderName: wonderName); + } + + void _handleTextToggle(bool? isActive) { + setState(() => _showTitleText = isActive ?? !_showTitleText); + } + + @override + Widget build(BuildContext context) { + WonderData wonderData = wondersLogic.getData(widget.type); + WonderIllustrationConfig bgConfig = WonderIllustrationConfig.bg( + enableAnims: false, + enableHero: false, + ); + WonderIllustrationConfig fgConfig = WonderIllustrationConfig( + enableAnims: false, + enableHero: false, + enableBg: false, + ); + Color fgColor = wonderData.type.bgColor; //.withOpacity(.5); + + _illustration = RepaintBoundary( + key: _containerKey, + child: ClipRect( + child: Stack( + children: [ + // Background - apply additional filter to make moon brighter + WonderIllustration( + widget.type, + config: bgConfig, + ), + + // Clouds + FractionallySizedBox( + widthFactor: 1, + heightFactor: .5, + child: AnimatedClouds( + wonderType: wonderData.type, + opacity: 1, + enableAnimations: false, + ), + ), + + // Wonder illustration + WonderIllustration( + widget.type, + config: fgConfig, + ), + + // Foreground gradient + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + fgColor.withOpacity(0), + fgColor.withOpacity(fgColor.opacity * .75), + ], + stops: const [0, 1], + ), + ), + ), + + // Title text + if (_showTitleText) + BottomCenter( + child: Transform.translate( + offset: Offset(0.0, -$styles.insets.xl * 2), + child: WonderTitleText(wonderData, enableShadows: true), + ), + ), + ], + ), + ), + ); + + return Stack(children: [ + Container( + decoration: BoxDecoration(backgroundBlendMode: BlendMode.color, color: Colors.blue), + child: _illustration ?? Container(), + ), + TopCenter( + child: SafeArea( + child: Padding( + padding: EdgeInsets.all($styles.insets.md), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10.0), + child: BackBtn.close( + bgColor: $styles.colors.offWhite, + iconColor: $styles.colors.black, + ), + ), + Expanded(child: Container()), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0, right: 16.0), + child: CircleIconBtn( + icon: defaultTargetPlatform == TargetPlatform.iOS ? AppIcons.share_ios : AppIcons.share_android, + bgColor: $styles.colors.offWhite, + color: $styles.colors.black, + onPressed: () => _handleSharePhoto(context, wonderData.title), + semanticLabel: $strings.wallpaperSemanticSharePhoto, + size: 44, + ), + ), + CircleIconBtn( + icon: AppIcons.download, + onPressed: () => _handleTakePhoto(context, wonderData.title), + semanticLabel: $strings.wallpaperSemanticTakePhoto, + size: 64, + ), + ], + ), + ), + ), + ), + BottomCenter( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SimpleCheckbox( + active: _showTitleText, label: $strings.wallpaperCheckboxShowTitle, onToggled: _handleTextToggle), + Gap($styles.insets.xl), + ], + ), + ), + ]); + } +} diff --git a/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart b/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart new file mode 100644 index 00000000..112480e0 --- /dev/null +++ b/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart @@ -0,0 +1,187 @@ +import 'package:wonders/common_libs.dart'; + +class WonderDetailsTabMenu extends StatelessWidget { + static double bottomPadding = 0; + static double buttonInset = 12; + + const WonderDetailsTabMenu( + {Key? key, required this.tabController, this.showBg = false, required this.wonderType, required this.showHomeBtn}) + : super(key: key); + + final TabController tabController; + final bool showBg; + final WonderType wonderType; + final bool showHomeBtn; + + @override + Widget build(BuildContext context) { + Color iconColor = showBg ? $styles.colors.black : $styles.colors.white; + const double homeBtnSize = 74; + // Use SafeArea padding if its more than the default padding. + bottomPadding = max(context.mq.padding.bottom, $styles.insets.xs * 1.5); + return Stack( + children: [ + //Background + Positioned.fill( + child: AnimatedOpacity( + duration: $styles.times.fast, + opacity: showBg ? 1 : 0, + child: Padding( + padding: EdgeInsets.only(top: buttonInset), + child: ColoredBox(color: $styles.colors.offWhite), + ), + ), + ), + // Buttons + Padding( + padding: EdgeInsets.only(left: $styles.insets.sm, right: $styles.insets.xxs, bottom: bottomPadding), + // TabButtons are a Stack with a row of icon buttons, and an illustrated home button sitting on top. + // The home buttons shows / hides itself based on `showHomeBtn` + // The row contains an animated placeholder gap which makes room for the icon as it transitions in. + child: Stack( + children: [ + // Main tab btns + animated gap + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // Holds a gap for the Home button which pushed the other icons to the side + AnimatedContainer( + curve: Curves.easeOut, + duration: $styles.times.fast, + width: showHomeBtn ? homeBtnSize : 0, + height: 0, + ), + _TabBtn(0, tabController, + iconImg: 'editorial', label: $strings.wonderDetailsTabLabelInformation, color: iconColor), + _TabBtn(1, tabController, + iconImg: 'photos', label: $strings.wonderDetailsTabLabelImages, color: iconColor), + _TabBtn(2, tabController, + iconImg: 'artifacts', label: $strings.wonderDetailsTabLabelArtifacts, color: iconColor), + _TabBtn(3, tabController, + iconImg: 'timeline', label: $strings.wonderDetailsTabLabelEvents, color: iconColor), + ], + ), + + // Home btn + TweenAnimationBuilder( + duration: $styles.times.fast, + tween: Tween(begin: 0, end: showHomeBtn ? 1 : 0), + child: _WonderHomeBtn( + size: homeBtnSize, + wonderType: wonderType, + borderSize: showBg ? 6 : 2, + ), + builder: (_, value, child) { + final curvedValue = Curves.easeOut.transform(value); + return Transform.scale( + scale: .5 + .5 * curvedValue, + child: Transform.translate( + offset: Offset(0, 100 * (1 - curvedValue)), + child: AnimatedOpacity( + opacity: showHomeBtn ? 1 : 0, + duration: $styles.times.fast, + child: child!, + ), + ), + ); + }, + // Wonder Button + ), + ], + ), + ), + ], + ); + } +} + +class _WonderHomeBtn extends StatelessWidget { + const _WonderHomeBtn({Key? key, required this.size, required this.wonderType, required this.borderSize}) + : super(key: key); + + final double size; + final WonderType wonderType; + final double borderSize; + + @override + Widget build(BuildContext context) { + return CircleBtn( + onPressed: () => Navigator.of(context).pop(), + bgColor: $styles.colors.offWhite, + semanticLabel: $strings.wonderDetailsTabSemanticBack, + child: AnimatedContainer( + curve: Curves.easeOut, + duration: $styles.times.fast, + width: size - borderSize * 2, + height: size - borderSize * 2, + margin: EdgeInsets.all(borderSize), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(99), + color: wonderType.fgColor, + image: DecorationImage(image: AssetImage(wonderType.homeBtn), fit: BoxFit.fill), + ), + ), + ); + } +} + +class _TabBtn extends StatelessWidget { + const _TabBtn( + this.index, + this.tabController, { + Key? key, + required this.iconImg, + required this.color, + required this.label, + }) : super(key: key); + + final int index; + final TabController tabController; + final String iconImg; + final Color color; + final String label; + + @override + Widget build(BuildContext context) { + bool selected = tabController.index == index; + + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + final iconImgPath = '${ImagePaths.common}/tab-$iconImg${selected ? '-active' : ''}.png'; + String tabLabel = localizations.tabLabel(tabIndex: index + 1, tabCount: tabController.length); + tabLabel = '$label: $tabLabel'; + return Expanded( + child: MergeSemantics( + child: Semantics( + selected: selected, + label: tabLabel, + child: ExcludeSemantics( + child: AppBtn.basic( + padding: EdgeInsets.only(top: $styles.insets.md + $styles.insets.xs, bottom: $styles.insets.sm), + onPressed: () => tabController.index = index, + semanticLabel: label, + child: Stack( + children: [ + /// Image icon + Image.asset(iconImgPath, height: 32, width: 32, color: selected ? null : color), + if (selected) + Positioned.fill( + child: BottomCenter( + child: Transform.translate( + offset: Offset(0, $styles.insets.xxs), + child: Animate().custom( + curve: Curves.easeOutCubic, + end: 24, + builder: (_, v, __) => Container(height: 3, width: v, color: $styles.colors.accent1), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/screens/wonder_details/wonders_details_screen.dart b/lib/ui/screens/wonder_details/wonders_details_screen.dart new file mode 100644 index 00000000..cb017e9d --- /dev/null +++ b/lib/ui/screens/wonder_details/wonders_details_screen.dart @@ -0,0 +1,84 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/lazy_indexed_stack.dart'; +import 'package:wonders/ui/common/measurable_widget.dart'; +import 'package:wonders/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart'; +import 'package:wonders/ui/screens/editorial/editorial_screen.dart'; +import 'package:wonders/ui/screens/photo_gallery/photo_gallery.dart'; +import 'package:wonders/ui/screens/wonder_details/wonder_details_tab_menu.dart'; +import 'package:wonders/ui/screens/wonder_events/wonder_events.dart'; + +class WonderDetailsScreen extends StatefulWidget with GetItStatefulWidgetMixin { + WonderDetailsScreen({Key? key, required this.type}) : super(key: key); + final WonderType type; + + @override + State createState() => _WonderDetailsScreenState(); +} + +class _WonderDetailsScreenState extends State + with GetItStateMixin, SingleTickerProviderStateMixin { + late final _tabController = TabController(length: 4, vsync: this)..addListener(_handleTabChanged); + AnimationController? _fade; + + final _detailsHasScrolled = ValueNotifier(false); + double? _tabBarHeight; + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + void _handleTabChanged() { + _fade?.forward(from: 0); + setState(() {}); + } + + void _handleDetailsScrolled(double scrollPos) => _detailsHasScrolled.value = scrollPos > 0; + + void _handleTabMenuSized(Size size) { + setState(() => _tabBarHeight = size.height - WonderDetailsTabMenu.buttonInset); + } + + @override + Widget build(BuildContext context) { + final wonder = wondersLogic.getData(widget.type); + int tabIndex = _tabController.index; + bool showTabBarBg = tabIndex != 1; + final tabBarHeight = _tabBarHeight ?? 0; + //final double tabBarHeight = WonderDetailsTabMenu.bottomPadding + 60; + return ColoredBox( + color: Colors.black, + child: Stack( + children: [ + /// Fullscreen tab views + LazyIndexedStack( + index: _tabController.index, + children: [ + WonderEditorialScreen(wonder, onScroll: _handleDetailsScrolled), + PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type), + Padding(padding: EdgeInsets.only(bottom: tabBarHeight), child: ArtifactCarouselScreen(type: wonder.type)), + Padding(padding: EdgeInsets.only(bottom: tabBarHeight), child: WonderEvents(type: widget.type)), + ], + ), + + /// Tab menu + BottomCenter( + child: ValueListenableBuilder( + valueListenable: _detailsHasScrolled, + builder: (_, value, ___) => MeasurableWidget( + onChange: _handleTabMenuSized, + child: WonderDetailsTabMenu( + tabController: _tabController, + wonderType: wonder.type, + showBg: showTabBarBg, + showHomeBtn: true, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/screens/wonder_events/widgets/_events_list.dart b/lib/ui/screens/wonder_events/widgets/_events_list.dart new file mode 100644 index 00000000..d2edeb20 --- /dev/null +++ b/lib/ui/screens/wonder_events/widgets/_events_list.dart @@ -0,0 +1,134 @@ +part of '../wonder_events.dart'; + +class _EventsList extends StatefulWidget { + const _EventsList({Key? key, required this.data}) : super(key: key); + final WonderData data; + + @override + State<_EventsList> createState() => _EventsListState(); +} + +class _EventsListState extends State<_EventsList> { + late final ScrollController _scroller = ScrollController()..addListener(_handleScrollChanged); + bool _hasPopped = false; + bool _isPointerDown = false; + @override + void dispose() { + _scroller.dispose(); + super.dispose(); + } + + void _handleScrollChanged() { + if (!_isPointerDown) return; + if (_scroller.position.pixels < -100 && !_hasPopped) { + _hasPopped = true; + context.pop(); + } + } + + bool _checkPointerIsDown(d) => _isPointerDown = d.dragDetails != null; + + void _handleGlobalTimelinePressed() => context.push(ScreenPaths.timeline(widget.data.type)); + + @override + Widget build(BuildContext context) { + return NotificationListener( + onNotification: _checkPointerIsDown, + child: LayoutBuilder(builder: (_, constraints) { + return Stack( + children: [ + AnimatedBuilder( + animation: _scroller, + builder: (_, __) { + bool showBackdrop = true; + double backdropAmt = 0; + if (_scroller.hasClients) { + double blurStart = 50; + double maxScroll = 150; + double scrollPx = _scroller.position.pixels - blurStart; + // Normalize scroll position to a value between 0 and 1 + backdropAmt = (_scroller.position.pixels - blurStart).clamp(0, maxScroll) / maxScroll; + // Disable backdrop once it is offscreen for an easy perf win + showBackdrop = (scrollPx <= 500); + } + // Container provides a underlay which gets darker as the background blurs + return Stack( + children: [ + if (showBackdrop) ...[ + AppBackdrop( + strength: backdropAmt, + child: IgnorePointer( + child: Container( + color: $styles.colors.greyStrong.withOpacity(backdropAmt * .6), + ), + )), + ], + _buildScrollingList() + ], + ); + }, + ), + ], + ); + }), + ); + } + + Widget _buildScrollingList() { + Container buildHandle() { + return Container( + width: 35, + height: 5, + decoration: BoxDecoration(color: $styles.colors.greyMedium, borderRadius: BorderRadius.circular(99)), + ); + } + + final events = widget.data.events; + + final listItems = []; + for (var e in events.entries) { + final delay = 100.ms + (100 * listItems.length).ms; + listItems.add( + TimelineEventCard(year: e.key, text: e.value) + .animate() + .fade(delay: delay, duration: $styles.times.med * 1.5) + .slide(begin: Offset(0, 1), curve: Curves.easeOutBack), + ); + } + return SingleChildScrollView( + controller: _scroller, + child: Column( + children: [ + IgnorePointer(child: Gap(WonderEvents._topHeight)), + Container( + decoration: BoxDecoration( + color: $styles.colors.white, + borderRadius: BorderRadius.circular($styles.corners.md), + ), + padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), + child: Column( + children: [ + Gap($styles.insets.xs), + buildHandle(), + Gap($styles.insets.sm), + ...listItems, + Gap($styles.insets.lg), + AppBtn.from( + text: $strings.eventsListButtonOpenGlobal, + expand: true, + onPressed: _handleGlobalTimelinePressed, + semanticLabel: $strings.eventsListButtonOpenGlobal, + ), + Gap($styles.insets.xl), + CompassDivider(isExpanded: true), + Gap($styles.insets.md), + HiddenCollectible(widget.data.type, index: 2, size: 150), + Gap(150), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/screens/wonder_events/widgets/_top_content.dart b/lib/ui/screens/wonder_events/widgets/_top_content.dart new file mode 100644 index 00000000..ac3efec0 --- /dev/null +++ b/lib/ui/screens/wonder_events/widgets/_top_content.dart @@ -0,0 +1,120 @@ +part of '../wonder_events.dart'; + +class _TopContent extends StatelessWidget { + const _TopContent({Key? key, required this.data}) : super(key: key); + final WonderData data; + + Color _fixLuminence(Color color, [double luminence = 0.35]) { + double d = luminence - color.computeLuminance(); + if (d <= 0) return color; + int r = color.red, g = color.green, b = color.blue; + return Color.fromARGB(255, (r + (255 - r) * d).toInt(), (g + (255 - g) * d).toInt(), (b + (255 - b) * d).toInt()); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: WonderEvents._topHeight, + child: MergeSemantics( + child: LightText( + child: SeparatedColumn( + separatorBuilder: () => Gap($styles.insets.xs * 1.5), + padding: EdgeInsets.only(top: $styles.insets.md, bottom: $styles.insets.sm), + children: [ + /// Text and image in a stack + Expanded( + child: Stack(children: [ + /// Image with fade on btm + Center( + child: _buildImageWithFade(context), + ), + + /// Title text + BottomCenter( + child: WonderTitleText(data), + ) + ]), + ), + + /// Bottom timeline + ExcludeSemantics( + child: SizedBox( + height: 50, + child: WondersTimelineBuilder( + selectedWonders: [data.type], + timelineBuilder: (_, data, isSelected) { + return Container( + decoration: BoxDecoration( + color: isSelected ? _fixLuminence(data.type.fgColor) : Colors.transparent, + border: Border.all(color: $styles.colors.greyMedium), + borderRadius: BorderRadius.circular($styles.corners.md), + ), + ); + }), + ), + ), + _buildEraTextRow(context) + ], + ), + ), + ), + ); + } + + Widget _buildImageWithFade(BuildContext context) { + return ExcludeSemantics( + child: Stack( + children: [ + /// Image + ClipPath( + clipper: CurvedTopClipper(), + child: Image.asset( + data.type.flattened, + width: 200, + fit: BoxFit.cover, + alignment: Alignment(0, -.5), + ), + ), + + /// Vertical gradient on btm + Positioned.fill( + child: BottomCenter( + child: ListOverscollGradient(bottomUp: true, size: 200), + ), + ) + ], + ), + ); + } + + Widget _buildEraTextRow(BuildContext context) { + final textStyle = $styles.text.body.copyWith(color: $styles.colors.accent2, height: 1); + return SeparatedRow( + separatorBuilder: () => Gap($styles.insets.sm), + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + StringUtils.supplant( + $strings.titleLabelDate, + { + '{fromDate}': StringUtils.formatYr(data.startYr), + '{endDate}': StringUtils.formatYr(data.endYr), + }, + ), + style: textStyle, + ), + _buildDot(context), + Text(StringUtils.getEra(data.startYr), style: textStyle), + ], + ).animate().fade(delay: $styles.times.pageTransition); + } + + Widget _buildDot(BuildContext context) { + return Container( + width: 4, + height: 4, + decoration: BoxDecoration(color: $styles.colors.accent2, borderRadius: BorderRadius.circular(99)), + ); + } +} diff --git a/lib/ui/screens/wonder_events/wonder_events.dart b/lib/ui/screens/wonder_events/wonder_events.dart new file mode 100644 index 00000000..192d0343 --- /dev/null +++ b/lib/ui/screens/wonder_events/wonder_events.dart @@ -0,0 +1,43 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; +import 'package:wonders/ui/common/app_backdrop.dart'; +import 'package:wonders/ui/common/compass_divider.dart'; +import 'package:wonders/ui/common/curved_clippers.dart'; +import 'package:wonders/ui/common/hidden_collectible.dart'; +import 'package:wonders/ui/common/list_gradient.dart'; +import 'package:wonders/ui/common/themed_text.dart'; +import 'package:wonders/ui/common/timeline_event_card.dart'; +import 'package:wonders/ui/common/wonders_timeline_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_title_text.dart'; + +part 'widgets/_events_list.dart'; +part 'widgets/_top_content.dart'; + +class WonderEvents extends StatelessWidget { + static const double _topHeight = 450; + WonderEvents({Key? key, required this.type}) : super(key: key); + final WonderType type; + late final _data = wondersLogic.getData(type); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_, constraints) { + return Container( + color: $styles.colors.black, + child: SafeArea( + bottom: false, + child: Stack( + children: [ + /// Top content, sits underneath scrolling list + _TopContent(data: _data), + + /// Scrolling Events list, takes up the full view + _EventsList(data: _data), + ], + ), + ), + ); + }); + } +} diff --git a/lib/ui/wonder_illustrations/chichen_itza_illustration.dart b/lib/ui/wonder_illustrations/chichen_itza_illustration.dart new file mode 100644 index 00000000..1688091c --- /dev/null +++ b/lib/ui/wonder_illustrations/chichen_itza_illustration.dart @@ -0,0 +1,133 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class ChichenItzaIllustration extends StatelessWidget { + ChichenItzaIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final assetPath = WonderType.chichenItza.assetPath; + final fgColor = WonderType.chichenItza.fgColor; + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder(config: config, bgBuilder: _buildBg, mgBuilder: _buildMg, fgBuilder: _buildFg); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: fgColor), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller2, + color: Colors.white, + opacity: anim.drive(Tween(begin: 0, end: .5)), + flipY: true, + ), + ), + Align( + alignment: Alignment(config.shortMode ? .25 : .7, config.shortMode ? 1 : -.15), + child: WonderHero( + config, + 'chichen-sun', + child: FractionalTranslation( + translation: Offset(0, -.2 * anim.value), + child: Image.asset( + '$assetPath/sun.png', + width: config.shortMode ? 100 : 200, + cacheWidth: context.widthPx.round() * 2, + opacity: anim, + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) { + return [ + Align( + alignment: Alignment(0, config.shortMode ? 1.2 : -.15), + child: FractionallySizedBox( + widthFactor: config.shortMode ? 1.5 : 2.6, + child: WonderHero( + config, + 'chichen-mg', + child: Image.asset('$assetPath/chichen.png', opacity: anim, fit: BoxFit.contain), + ), + ), + ), + ]; + } + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Stack( + children: [ + Transform.scale( + scale: 1 + config.zoom * .2, + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: BottomLeft( + child: FractionallySizedBox( + heightFactor: .5, + child: FractionalTranslation( + translation: Offset(-.4, 0), + child: Image.asset('$assetPath/foreground-left.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + Transform.scale( + scale: 1 + config.zoom * .1, + child: FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: BottomRight( + child: FractionallySizedBox( + heightFactor: .33, + child: FractionalTranslation( + translation: Offset(.5, -.32), + child: Image.asset('$assetPath/foreground-right.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + Transform.scale( + scale: 1 + config.zoom * .15, + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: TopLeft( + child: FractionallySizedBox( + heightFactor: .55, + child: FractionalTranslation( + translation: Offset(-.3, -.45), + child: Image.asset('$assetPath/top-left.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + Transform.scale( + scale: 1 + config.zoom * .3, + child: FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: TopRight( + child: FractionallySizedBox( + heightFactor: .65, + child: FractionalTranslation( + translation: Offset(.45, -.35), + child: Image.asset('$assetPath/top-right.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + ], + ), + ]; + } +} diff --git a/lib/ui/wonder_illustrations/christ_redeemer_illustration.dart b/lib/ui/wonder_illustrations/christ_redeemer_illustration.dart new file mode 100644 index 00000000..fefd334f --- /dev/null +++ b/lib/ui/wonder_illustrations/christ_redeemer_illustration.dart @@ -0,0 +1,122 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class ChristRedeemerIllustration extends StatelessWidget { + ChristRedeemerIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.christRedeemer.assetPath; + final fgColor = WonderType.christRedeemer.fgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: fgColor), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller1, + color: Colors.white, + flipX: false, + opacity: anim.drive(Tween(begin: 0, end: .4)), + ), + ), + Align( + alignment: config.shortMode ? Alignment(.5, -1.5) : Alignment(.5, -.75), + child: FractionalTranslation( + translation: Offset(0, .5 * anim.value), + child: WonderHero( + config, + 'christ-sun', + child: Transform.scale( + scale: config.shortMode ? 1.4 : 1.6, + child: Image.asset( + '$assetPath/sun.png', + cacheWidth: context.widthPx.round() * 2, + opacity: anim, + ), + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) { + return [ + ClipRect( + child: Transform.scale( + scale: 1 + config.zoom * .2, + child: FractionalTranslation( + translation: Offset(0, config.shortMode ? .5 : .2), + child: BottomCenter( + child: FractionallySizedBox( + heightFactor: config.shortMode ? 1.5 : 1.2, + child: WonderHero( + config, + 'christ-mg', + child: Image.asset( + '$assetPath/redeemer.png', + opacity: anim, + fit: BoxFit.cover, + ), + ), + ), + ), + ), + ), + ) + ]; + } + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Stack( + children: [ + Transform.scale( + scale: 1 + config.zoom * .15, + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: BottomLeft( + child: FractionallySizedBox( + widthFactor: 1.5, + child: FractionalTranslation( + translation: Offset(-.25, .03), + child: Image.asset('$assetPath/foreground-left.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + Transform.scale( + scale: 1 + config.zoom * .3, + child: FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: BottomRight( + child: FractionallySizedBox( + widthFactor: 1.5, + child: FractionalTranslation( + translation: Offset(.3, .2), + child: Image.asset('$assetPath/foreground-right.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + ], + ), + ]; + } +} diff --git a/lib/ui/wonder_illustrations/colosseum_illustration.dart b/lib/ui/wonder_illustrations/colosseum_illustration.dart new file mode 100644 index 00000000..703d7972 --- /dev/null +++ b/lib/ui/wonder_illustrations/colosseum_illustration.dart @@ -0,0 +1,120 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class ColosseumIllustration extends StatelessWidget { + ColosseumIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.colosseum.assetPath; + final bgColor = WonderType.colosseum.bgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: $styles.colors.shift(bgColor, .15)), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller1, + color: Colors.white, + opacity: anim.drive(Tween(begin: 0, end: .5)), + ), + ), + Align( + alignment: config.shortMode ? Alignment(-.3, 1) : Alignment(-.5, -.4), + child: FractionalTranslation( + translation: Offset(0, -.5 * anim.value), + child: WonderHero( + config, + 'colosseum-sun', + child: Transform.scale( + scale: config.shortMode ? .75 : 1, + child: Image.asset( + '$assetPath/sun.png', + cacheWidth: context.widthPx.round() * 2, + opacity: anim, + ), + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) { + return [ + Stack( + children: [ + if (config.shortMode) ...[ + FractionalTranslation( + translation: Offset(0, .9), + child: Container(color: bgColor), + ) + ], + Center( + child: FractionalTranslation( + translation: Offset(0, config.shortMode ? .1 : -.15), + child: Transform.scale( + scale: config.shortMode ? .85 : 1.55 + config.zoom * .2, + child: WonderHero( + config, + 'colosseum-mg', + child: Image.asset('$assetPath/colosseum.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ], + ) + ]; + } + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Stack(children: [ + BottomLeft( + child: FractionallySizedBox( + heightFactor: .56, + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1 + config.zoom * .3, + child: FractionalTranslation( + translation: Offset(-.1, .1), + child: Image.asset('$assetPath/foreground-left.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + BottomRight( + child: FractionallySizedBox( + heightFactor: .56, + child: FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1 + config.zoom * .3, + child: FractionalTranslation( + translation: Offset(.3, .2), + child: Image.asset('$assetPath/foreground-right.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + ]) + ]; + } +} diff --git a/lib/ui/wonder_illustrations/common/animated_clouds.dart b/lib/ui/wonder_illustrations/common/animated_clouds.dart new file mode 100644 index 00000000..a39eeb78 --- /dev/null +++ b/lib/ui/wonder_illustrations/common/animated_clouds.dart @@ -0,0 +1,148 @@ +import 'dart:async'; +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/utils/context_utils.dart'; + +// Shows a set of clouds that animated onto stage. +// When value-key is changed, a new set of clouds will animate into place and the old ones will animate out. +// Uses a random seed system, to make sure we get the same set of clouds for each wonder, without actually having to hand-position them. +class AnimatedClouds extends StatefulWidget with GetItStatefulWidgetMixin { + AnimatedClouds({Key? key, this.enableAnimations = true, required this.wonderType, required this.opacity}) + : super(key: key); + final WonderType wonderType; + final bool enableAnimations; + final double opacity; + @override + State createState() => _AnimatedCloudsState(); +} + +class _AnimatedCloudsState extends State with SingleTickerProviderStateMixin, GetItStateMixin { + late List<_Cloud> _clouds = []; + List<_Cloud> _oldClouds = []; + late final AnimationController _anim = AnimationController(vsync: this, duration: 1500.ms); + + @override + void initState() { + super.initState(); + scheduleMicrotask(() { + setState(() => _clouds = _getClouds()); + }); + _showClouds(); + } + + @override + void dispose() { + _anim.dispose(); + super.dispose(); + } + + @override + void didUpdateWidget(covariant AnimatedClouds oldWidget) { + if (oldWidget.wonderType != widget.wonderType) { + _oldClouds = _clouds; + _clouds = _getClouds(); + _showClouds(); + } + super.didUpdateWidget(oldWidget); + } + + int _getCloudSeed(WonderType type) { + switch (type) { + case WonderType.chichenItza: + return 2; + case WonderType.christRedeemer: + return 78; + case WonderType.colosseum: + return 1; + case WonderType.greatWall: + return 500; + case WonderType.machuPicchu: + return 37; + case WonderType.petra: + return 111; + case WonderType.pyramidsGiza: + return 15; + case WonderType.tajMahal: + return 2; + } + } + + /// Starts playing the clouds animation, or jumps right to the end, based on [AnimatedClouds.enableAnimations] + void _showClouds() { + widget.enableAnimations ? _anim.forward(from: 0) : _anim.value = 1; + } + + @override + Widget build(BuildContext context) { + // Old clouds animate from 0 to startOffset, new clouds do the opposite. + Widget buildCloud(c, {required bool isOld, required int startOffset}) { + // Use a positive, or negative start offset, based on index + final stOffset = _clouds.indexOf(c) % 2 == 0 ? -startOffset : startOffset; + // If old, we will end at the stOffset and start at 0, if new, start at stOffset, and end at 0 + double curvedValue = Curves.easeOut.transform(_anim.value); + return Positioned( + top: c.pos.dy, + left: isOld ? c.pos.dx - stOffset * curvedValue : c.pos.dx + stOffset * (1 - curvedValue), + child: c, + ); + } + + return RepaintBoundary( + child: ClipRect( + child: OverflowBox( + child: AnimatedBuilder( + animation: _anim, + builder: (_, __) { + // A stack with 2 sets of clouds, one set is moving out of view while the other moves in. + return Stack( + clipBehavior: Clip.hardEdge, + key: ValueKey(widget.wonderType), + children: [ + if (_anim.value != 1) ...[ + ..._oldClouds.map((c) => buildCloud(c, isOld: true, startOffset: 1000)), + ], + ..._clouds.map((c) => buildCloud(c, isOld: false, startOffset: 1000)), + ], + ); + }, + ), + ), + ), + ); + } + + List<_Cloud> _getClouds() { + Size size = ContextUtils.getSize(context) ?? Size(context.widthPx, 400); + rndSeed = _getCloudSeed(widget.wonderType); + return List<_Cloud>.generate(3, (index) { + return _Cloud( + Offset(rnd.getDouble(-200, size.width - 100), rnd.getDouble(50, size.height - 50)), + scale: rnd.getDouble(.7, 1), + flipX: rnd.getBool(), + flipY: rnd.getBool(), + opacity: widget.opacity, + ); + }).toList(); + } +} + +class _Cloud extends StatelessWidget { + final Offset pos; + final double scale; + final bool flipX; + final bool flipY; + final double opacity; + + const _Cloud(this.pos, {this.scale = 1, this.flipX = false, this.flipY = false, required this.opacity}); + + @override + Widget build(BuildContext context) => Transform.scale( + scaleX: scale * (flipX ? -1 : 1), + scaleY: scale * (flipY ? -1 : 1), + child: Image.asset( + ImagePaths.cloud, + opacity: AlwaysStoppedAnimation(.4 * opacity), + width: context.widthPx * .65 * scale, + fit: BoxFit.fitWidth, + ), + ); +} diff --git a/lib/ui/wonder_illustrations/common/paint_textures.dart b/lib/ui/wonder_illustrations/common/paint_textures.dart new file mode 100644 index 00000000..196f027a --- /dev/null +++ b/lib/ui/wonder_illustrations/common/paint_textures.dart @@ -0,0 +1,24 @@ +import 'package:wonders/common_libs.dart'; + +class IllustrationTexture extends StatelessWidget { + const IllustrationTexture(this.path, + {Key? key, this.scale = 1, this.color, this.flipX = false, this.flipY = false, this.opacity}) + : super(key: key); + final Color? color; + final double scale; + final bool flipX; + final bool flipY; + final String path; + final Animation? opacity; + + @override + Widget build(BuildContext context) => AnimatedBuilder( + animation: opacity ?? AlwaysStoppedAnimation(1), + builder: (context, child) => ClipRect( + child: Transform.scale( + scaleX: scale * (flipX ? -1 : 1), + scaleY: scale * (flipY ? -1 : 1), + child: Image.asset(path, fit: BoxFit.cover, color: color, opacity: opacity, cacheWidth: 1024)), + ), + ); +} diff --git a/lib/ui/wonder_illustrations/common/wonder_hero.dart b/lib/ui/wonder_illustrations/common/wonder_hero.dart new file mode 100644 index 00000000..cf106e9c --- /dev/null +++ b/lib/ui/wonder_illustrations/common/wonder_hero.dart @@ -0,0 +1,19 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +/// Utility class that wraps a normal [Hero] widget, but respects WonderIllustrationConfig.enableHero setting +class WonderHero extends StatelessWidget { + const WonderHero(this.config, this.tag, {Key? key, required this.child}) : super(key: key); + final WonderIllustrationConfig config; + final Widget child; + final String tag; + + @override + Widget build(BuildContext context) => config.enableHero + ? Hero( + createRectTween: (begin, end) => RectTween(begin: begin!, end: end!), + tag: tag, + child: child, + ) + : child; +} diff --git a/lib/ui/wonder_illustrations/common/wonder_illustration.dart b/lib/ui/wonder_illustrations/common/wonder_illustration.dart new file mode 100644 index 00000000..ff49cd49 --- /dev/null +++ b/lib/ui/wonder_illustrations/common/wonder_illustration.dart @@ -0,0 +1,39 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/wonder_illustrations/chichen_itza_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/christ_redeemer_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/colosseum_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; +import 'package:wonders/ui/wonder_illustrations/great_wall_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/machu_picchu_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/petra_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/pyramids_giza_illustration.dart'; +import 'package:wonders/ui/wonder_illustrations/taj_mahal_illustration.dart'; + +/// Convenience class for showing an illustration when all you have is the type. +class WonderIllustration extends StatelessWidget { + const WonderIllustration(this.type, {Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final WonderType type; + + @override + Widget build(BuildContext context) { + switch (type) { + case WonderType.chichenItza: + return ChichenItzaIllustration(config: config); + case WonderType.christRedeemer: + return ChristRedeemerIllustration(config: config); + case WonderType.colosseum: + return ColosseumIllustration(config: config); + case WonderType.greatWall: + return GreatWallIllustration(config: config); + case WonderType.machuPicchu: + return MachuPicchuIllustration(config: config); + case WonderType.petra: + return PetraIllustration(config: config); + case WonderType.pyramidsGiza: + return PyramidsGizaIllustration(config: config); + case WonderType.tajMahal: + return TajMahalIllustration(config: config); + } + } +} diff --git a/lib/ui/wonder_illustrations/common/wonder_illustration_builder.dart b/lib/ui/wonder_illustrations/common/wonder_illustration_builder.dart new file mode 100644 index 00000000..58041910 --- /dev/null +++ b/lib/ui/wonder_illustrations/common/wonder_illustration_builder.dart @@ -0,0 +1,63 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +/// Takes a builder for each of the 3 illustration layers. +/// Each builder returns a list of Widgets which will be added directly to a Stack. +/// Checks the config, and only calls builders that are currently enabled. +/// +/// Also manages an AnimationController that is passed to each layer if it would like to animate itself on or off screen. +class WonderIllustrationBuilder extends StatefulWidget { + const WonderIllustrationBuilder({ + Key? key, + required this.config, + required this.fgBuilder, + required this.mgBuilder, + required this.bgBuilder, + }) : super(key: key); + final List Function(BuildContext context, Animation animation) fgBuilder; + final List Function(BuildContext context, Animation animation) mgBuilder; + final List Function(BuildContext context, Animation animation) bgBuilder; + final WonderIllustrationConfig config; + + @override + State createState() => _WonderIllustrationBuilderState(); +} + +class _WonderIllustrationBuilderState extends State with SingleTickerProviderStateMixin { + late final _anim = AnimationController(vsync: this, duration: $styles.times.med * .75) + ..addListener(() => setState(() {})); + + bool get isShowing => widget.config.isShowing; + @override + void initState() { + super.initState(); + if (isShowing) _anim.forward(from: 0); + } + + @override + void dispose() { + _anim.dispose(); + super.dispose(); + } + + @override + void didUpdateWidget(covariant WonderIllustrationBuilder oldWidget) { + if (isShowing != oldWidget.config.isShowing) { + isShowing ? _anim.forward(from: 0) : _anim.reverse(from: 1); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + // Optimization: no need to return all of these children if the widget is fully invisible. + if (_anim.value == 0 && widget.config.enableAnims) return SizedBox.expand(); + Animation anim = widget.config.enableAnims ? _anim : AlwaysStoppedAnimation(1); + + return Stack(key: ValueKey(anim.value == 0), children: [ + if (widget.config.enableBg) ...widget.bgBuilder(context, _anim), + if (widget.config.enableMg) ...widget.mgBuilder(context, _anim), + if (widget.config.enableFg) ...widget.fgBuilder(context, _anim), + ]); + } +} diff --git a/lib/ui/wonder_illustrations/common/wonder_illustration_config.dart b/lib/ui/wonder_illustrations/common/wonder_illustration_config.dart new file mode 100644 index 00000000..c732bc78 --- /dev/null +++ b/lib/ui/wonder_illustrations/common/wonder_illustration_config.dart @@ -0,0 +1,73 @@ +/// Indicates the current setup for a WonderIllustration, allowing the single widget to be used in a variety of contexts. +class WonderIllustrationConfig { + static const double _defaultZoom = 1; + + const WonderIllustrationConfig({ + this.zoom = _defaultZoom, + this.isShowing = true, + this.enableFg = true, + this.enableBg = true, + this.enableMg = true, + this.enableHero = true, + this.enableAnims = true, + this.shortMode = false, + }); + final double zoom; + final bool isShowing; + final bool enableFg; + final bool enableBg; + final bool enableMg; + final bool enableHero; + final bool enableAnims; + final bool shortMode; + + /// Shortcut constructors to reduce boilerplate in the views when only 1 layer is required. + factory WonderIllustrationConfig.fg({ + double zoom = _defaultZoom, + bool isShowing = true, + bool enableHero = true, + bool enableAnims = true, + bool shortMode = false, + }) => + WonderIllustrationConfig( + zoom: zoom, + isShowing: isShowing, + enableHero: enableHero, + enableAnims: enableAnims, + enableBg: false, + enableMg: false, + shortMode: shortMode, + ); + factory WonderIllustrationConfig.bg({ + double zoom = _defaultZoom, + bool isShowing = true, + bool enableHero = true, + bool enableAnims = true, + bool shortMode = false, + }) => + WonderIllustrationConfig( + zoom: zoom, + isShowing: isShowing, + enableHero: enableHero, + enableAnims: enableAnims, + enableFg: false, + enableMg: false, + shortMode: shortMode, + ); + factory WonderIllustrationConfig.mg({ + double zoom = _defaultZoom, + bool isShowing = true, + bool enableHero = true, + bool enableAnims = true, + bool shortMode = false, + }) => + WonderIllustrationConfig( + zoom: zoom, + isShowing: isShowing, + enableHero: enableHero, + enableAnims: enableAnims, + enableBg: false, + enableFg: false, + shortMode: shortMode, + ); +} diff --git a/lib/ui/wonder_illustrations/common/wonder_title_text.dart b/lib/ui/wonder_illustrations/common/wonder_title_text.dart new file mode 100644 index 00000000..e07e796f --- /dev/null +++ b/lib/ui/wonder_illustrations/common/wonder_title_text.dart @@ -0,0 +1,52 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/logic/common/string_utils.dart'; +import 'package:wonders/logic/data/wonder_data.dart'; + +/// To match designs: +/// - need a line-break after the first line +/// - of/the should be down-sized +/// Accomplished using a set of TextSpans, and a white list of 'small words' +class WonderTitleText extends StatelessWidget { + const WonderTitleText(this.data, {Key? key, this.enableShadows = false}) : super(key: key); + final WonderData data; + final bool enableShadows; + @override + Widget build(BuildContext context) { + var textStyle = $styles.text.wonderTitle.copyWith( + color: $styles.colors.offWhite, + ); + bool smallText = [WonderType.christRedeemer, WonderType.colosseum].contains(data.type); + if (smallText) { + textStyle = textStyle.copyWith(fontSize: 48); + } + + // First, get a list like: ['the\n', 'great wall'] + final title = data.title.toLowerCase(); + // Split on spaces, later, add either a linebreak or a space back in. + List pieces = title.split(' '); + // TextSpan builder, figures out whether to use small text, and adds linebreak or space (or nothing). + TextSpan buildTextSpan(String text) { + final smallWords = ['of', 'the']; + bool useSmallText = smallWords.contains(text.trim()); + int i = pieces.indexOf(text); + bool addLinebreak = i == 0 && pieces.length > 1; + bool addSpace = !addLinebreak && i < pieces.length - 1; + if (useSmallText == false) { + text = StringUtils.capitalize(text); + } + return TextSpan( + text: '$text${addLinebreak ? '\n' : addSpace ? ' ' : ''}', + style: useSmallText ? textStyle.copyWith(fontSize: 20) : textStyle, + ); + } + + List shadows = enableShadows ? $styles.shadows.text : []; + return RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: textStyle.copyWith(shadows: shadows), + children: pieces.map(buildTextSpan).toList(), + ), + ); + } +} diff --git a/lib/ui/wonder_illustrations/great_wall_illustration.dart b/lib/ui/wonder_illustrations/great_wall_illustration.dart new file mode 100644 index 00000000..da866034 --- /dev/null +++ b/lib/ui/wonder_illustrations/great_wall_illustration.dart @@ -0,0 +1,124 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class GreatWallIllustration extends StatelessWidget { + GreatWallIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.greatWall.assetPath; + final fgColor = WonderType.greatWall.fgColor; + final bgColor = WonderType.greatWall.bgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: $styles.colors.shift(fgColor, .15)), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller2, + flipX: true, + color: Colors.white, + opacity: anim.drive(Tween(begin: 0, end: .5)), + ), + ), + Align( + alignment: config.shortMode ? Alignment(-.5, -.5) : Alignment(-.45, -.63), + child: FractionalTranslation( + translation: Offset(0, -.5 * anim.value), + child: WonderHero( + config, + 'great-wall-sun', + child: Image.asset( + '$assetPath/sun.png', + cacheWidth: context.widthPx.round() * 2, + width: config.shortMode ? 100 : 150, + opacity: anim, + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) => [ + Center( + child: FractionalTranslation( + translation: Offset(0, config.shortMode ? .1 * anim.value : 0), + child: FractionallySizedBox( + widthFactor: config.shortMode ? null : 1.3, + child: WonderHero( + config, + 'great-wall-mg', + child: Image.asset( + '$assetPath/great-wall.png', + opacity: anim, + width: config.shortMode ? 300 : 500, + ), + ), + ), + ), + + // child: FractionalTranslation( + // translation: Offset(0, 0), + // child: Transform.scale( + // scale: 1, //config.shortMode ? .95 : 1.4 + config.zoom * .2, + // child: WonderHero( + // config, + // 'great-wall-mg', + // child: Image.asset( + // '$assetPath/great-wall.png', + // opacity: anim, + // width: 700, + // ), + // ), + // ), + // ), + ) + ]; + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Stack(children: [ + BottomRight( + child: FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1.5 + config.zoom * .1, + child: FractionalTranslation( + translation: Offset(.46, -.22), + child: Image.asset('$assetPath/foreground-right.png', + opacity: anim, cacheWidth: context.widthPx.round() * 3), + ), + ), + ), + ), + BottomLeft( + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1 + config.zoom * .3, + child: FractionalTranslation( + translation: Offset(-.3, -.01), + child: Image.asset('$assetPath/foreground-left.png', + opacity: anim, cacheWidth: context.widthPx.round() * 3), + ), + ), + ), + ), + ]) + ]; + } +} diff --git a/lib/ui/wonder_illustrations/machu_picchu_illustration.dart b/lib/ui/wonder_illustrations/machu_picchu_illustration.dart new file mode 100644 index 00000000..63276740 --- /dev/null +++ b/lib/ui/wonder_illustrations/machu_picchu_illustration.dart @@ -0,0 +1,112 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class MachuPicchuIllustration extends StatelessWidget { + MachuPicchuIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.machuPicchu.assetPath; + final fgColor = WonderType.machuPicchu.fgColor; + final bgColor = WonderType.machuPicchu.bgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: fgColor), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller1, + flipX: true, + color: Colors.white, + opacity: anim.drive(Tween(begin: 0, end: .7)), + ), + ), + Align( + alignment: config.shortMode ? Alignment.center : Alignment(.75, -.6), + child: FractionalTranslation( + translation: Offset(0, -.5 * anim.value), + child: Transform.scale( + scale: config.shortMode ? .75 : 1, + child: WonderHero( + config, + 'machu-sun', + child: Image.asset( + '$assetPath/sun.png', + cacheWidth: context.widthPx.round() * 2, + opacity: anim, + ), + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) => [ + Center( + child: Transform.scale( + scale: config.shortMode ? 1.2 : 2.5 + config.zoom * .2, + alignment: Alignment(config.shortMode ? 0 : .15, config.shortMode ? -0.6 : .3), + child: WonderHero( + config, + 'machu-mg', + child: Image.asset( + '$assetPath/machu-picchu.png', + fit: BoxFit.contain, + opacity: anim, + ), + ), + ), + ) + ]; + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Transform.translate( + offset: Offset(0, 20 * (1 - curvedAnim)), + child: Stack(children: [ + BottomRight( + child: Transform.scale( + scale: 1 + config.zoom * .05, + child: FractionallySizedBox( + widthFactor: 1.5, + child: FractionalTranslation( + translation: Offset(0, .1), + child: Image.asset('$assetPath/foreground-back.png', opacity: anim), + ), + ), + ), + ), + BottomLeft( + child: FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1 + config.zoom * .25, + child: FractionallySizedBox( + widthFactor: 1.5, + child: FractionalTranslation( + translation: Offset(-.3, .4), + child: Image.asset('$assetPath/foreground-front.png', opacity: anim), + ), + ), + ), + ), + ), + ]), + ) + ]; + } +} diff --git a/lib/ui/wonder_illustrations/petra_illustration.dart b/lib/ui/wonder_illustrations/petra_illustration.dart new file mode 100644 index 00000000..c885909f --- /dev/null +++ b/lib/ui/wonder_illustrations/petra_illustration.dart @@ -0,0 +1,106 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class PetraIllustration extends StatelessWidget { + PetraIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.petra.assetPath; + final fgColor = WonderType.petra.fgColor; + final bgColor = WonderType.petra.bgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: fgColor), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller1, + color: Colors.white, + flipX: true, + opacity: anim.drive(Tween(begin: 0, end: .25)), + ), + ), + Align( + alignment: Alignment(-.3, config.shortMode ? -1.5 : -1.23), + child: FractionalTranslation( + translation: Offset(0, .5 * anim.value), + child: WonderHero( + config, + 'petra-moon', + child: Image.asset( + '$assetPath/moon.png', + opacity: anim, + ), + ), + ), + ), + ]; + } + + List _buildMg(BuildContext context, Animation anim) => [ + Center( + child: FractionalTranslation( + translation: Offset(0, config.shortMode ? 0.05 : -.1), + child: FractionallySizedBox( + widthFactor: config.shortMode ? 1 : 2, + child: WonderHero( + config, + 'petra-mg', + child: Image.asset('$assetPath/petra.png', fit: BoxFit.contain, opacity: anim), + ), + ), + ), + ), + ]; + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Stack(children: [ + CenterLeft( + child: FractionallySizedBox( + widthFactor: .63, + child: FractionalTranslation( + translation: Offset(-.3 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1.1 + config.zoom * .2, + child: FractionalTranslation( + translation: Offset(-.35, -.07), + child: Image.asset('$assetPath/foreground-left.png', opacity: anim, fit: BoxFit.contain), + ), + ), + ), + ), + ), + CenterRight( + child: FractionallySizedBox( + widthFactor: .72, + child: FractionalTranslation( + translation: Offset(.3 * (1 - curvedAnim), 0), + child: Transform.scale( + scale: 1 + config.zoom * .4, + child: FractionalTranslation( + translation: Offset(.4, -.03), + child: Image.asset('$assetPath/foreground-right.png', opacity: anim, fit: BoxFit.contain), + ), + ), + ), + ), + ), + ]) + ]; + } +} diff --git a/lib/ui/wonder_illustrations/pyramids_giza_illustration.dart b/lib/ui/wonder_illustrations/pyramids_giza_illustration.dart new file mode 100644 index 00000000..fd94556f --- /dev/null +++ b/lib/ui/wonder_illustrations/pyramids_giza_illustration.dart @@ -0,0 +1,102 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class PyramidsGizaIllustration extends StatelessWidget { + PyramidsGizaIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + final String assetPath = WonderType.pyramidsGiza.assetPath; + final fgColor = WonderType.pyramidsGiza.fgColor; + final bgColor = WonderType.pyramidsGiza.bgColor; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + return [ + FadeColorTransition(animation: anim, color: fgColor), + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller2, + color: Colors.white, + opacity: anim.drive(Tween(begin: 0, end: .3)), + flipY: true, + ), + ), + Align( + alignment: Alignment(.75, config.shortMode ? -.2 : -.5), + child: FractionalTranslation( + translation: Offset(0, -.5 * anim.value), + child: WonderHero( + config, + 'pyramids-moon', + child: Transform.scale( + scale: config.shortMode ? 0.8 : 1.2, + child: Image.asset('$assetPath/moon.png', opacity: anim), + ), + ), + )), + ]; + } + + List _buildMg(BuildContext context, Animation anim) { + return [ + Align( + alignment: Alignment(0, config.shortMode ? 0.9 : 0), + child: WonderHero(config, 'pyramids-mg', + child: Transform.scale( + scale: 1 + config.zoom * .1, + child: FractionallySizedBox( + widthFactor: config.shortMode ? 1 : 1.94, + child: Image.asset('$assetPath/pyramids.png', fit: BoxFit.contain, opacity: anim), + ), + )), + ), + ]; + } + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Transform.scale( + scale: 1 + config.zoom * .2, + child: Transform.translate( + offset: Offset(0, 10 * (1 - curvedAnim)), + child: BottomCenter( + child: FractionallySizedBox( + widthFactor: 1.2, + child: FractionalTranslation( + translation: Offset(0, -1.2), + child: Image.asset('$assetPath/foreground-back.png', opacity: anim, fit: BoxFit.cover)), + ), + ), + ), + ), + Transform.scale( + scale: 1 + config.zoom * .4, + child: Transform.translate( + offset: Offset(0, 30 * (1 - curvedAnim)), + child: BottomCenter( + child: FractionallySizedBox( + widthFactor: 1.52, + child: FractionalTranslation( + translation: Offset(0, 0.1), + child: Image.asset('$assetPath/foreground-front.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ), + ]; + } +} diff --git a/lib/ui/wonder_illustrations/taj_mahal_illustration.dart b/lib/ui/wonder_illustrations/taj_mahal_illustration.dart new file mode 100644 index 00000000..93f52a2e --- /dev/null +++ b/lib/ui/wonder_illustrations/taj_mahal_illustration.dart @@ -0,0 +1,112 @@ +import 'package:wonders/common_libs.dart'; +import 'package:wonders/ui/common/fade_color_transition.dart'; +import 'package:wonders/ui/wonder_illustrations/common/paint_textures.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_hero.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_builder.dart'; +import 'package:wonders/ui/wonder_illustrations/common/wonder_illustration_config.dart'; + +class TajMahalIllustration extends StatelessWidget { + TajMahalIllustration({Key? key, required this.config}) : super(key: key); + final WonderIllustrationConfig config; + + final fgColor = WonderType.tajMahal.fgColor; + final bgColor = WonderType.tajMahal.bgColor; + final assetPath = WonderType.tajMahal.assetPath; + + @override + Widget build(BuildContext context) { + return WonderIllustrationBuilder( + config: config, + bgBuilder: _buildBg, + mgBuilder: _buildMg, + fgBuilder: _buildFg, + ); + } + + List _buildBg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + // Bg color + FadeColorTransition(color: fgColor, animation: anim), + // Noise texture + Positioned.fill( + child: IllustrationTexture( + ImagePaths.roller1, + flipY: true, + opacity: anim.drive(Tween(begin: 0, end: 1)), + color: bgColor, + ), + ), + // Sun + Align( + alignment: config.shortMode ? Alignment(-1.25, -2.8) : Alignment(-1.25, -1.15), + child: FractionalTranslation( + translation: Offset(-.2 + curvedAnim * .2, .4 - curvedAnim * .2), + child: WonderHero(config, 'taj-sun', child: Image.asset('$assetPath/sun.png', opacity: anim)), + ), + ) + ]; + } + + List _buildMg(BuildContext context, Animation anim) { + return [ + Transform.scale( + scale: 1 + config.zoom * .1, + child: Align( + alignment: Alignment(0, config.shortMode ? 1 : -.15), + child: FractionallySizedBox( + widthFactor: config.shortMode ? 1 : 1.7, + child: WonderHero(config, 'taj-mg', + child: Stack( + children: [ + Image.asset('$assetPath/taj-mahal.png', opacity: anim, fit: BoxFit.cover), + if (!config.shortMode) + FractionalTranslation( + translation: Offset(0, 1.33), + child: Image.asset('$assetPath/pool.png', opacity: anim, fit: BoxFit.cover), + ), + ], + )), + ), + ), + ) + ]; + } + + List _buildFg(BuildContext context, Animation anim) { + final curvedAnim = Curves.easeOut.transform(anim.value); + return [ + Transform.scale( + scale: 1 + config.zoom * .2, + child: Stack( + children: [ + FractionalTranslation( + translation: Offset(-.2 * (1 - curvedAnim), 0), + child: BottomLeft( + child: FractionallySizedBox( + heightFactor: .6, + child: FractionalTranslation( + translation: Offset(-.4, -.04), + child: Image.asset('$assetPath/foreground-left.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + FractionalTranslation( + translation: Offset(.2 * (1 - curvedAnim), 0), + child: BottomRight( + child: FractionallySizedBox( + heightFactor: .6, + child: FractionalTranslation( + translation: Offset(.4, -.04), + child: Image.asset('$assetPath/foreground-right.png', opacity: anim, fit: BoxFit.cover), + ), + ), + ), + ), + ], + ), + ) + ]; + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 00000000..87955950 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1187 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "40.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.0" + ansicolor: + dependency: transitive + description: + name: ansicolor + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.3" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.3.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + collection: + dependency: "direct main" + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + copy_with_extension: + dependency: "direct main" + description: + name: copy_with_extension + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.3" + copy_with_extension_gen: + dependency: "direct dev" + description: + name: copy_with_extension_gen + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.3" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.1" + dart_code_metrics: + dependency: "direct dev" + description: + name: dart_code_metrics + url: "https://pub.dartlang.org" + source: hosted + version: "4.17.1" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + drop_cap_text: + dependency: "direct main" + description: + name: drop_cap_text + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.3" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + extra_alignments: + dependency: "direct main" + description: + name: extra_alignments + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+1" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flextras: + dependency: "direct main" + description: + name: flextras + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + flutter_circular_text: + dependency: "direct main" + description: + name: flutter_circular_text + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.1" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview: + dependency: "direct main" + description: + name: flutter_inappwebview + url: "https://pub.dartlang.org" + source: hosted + version: "5.4.3+7" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_native_splash: + dependency: "direct main" + description: + name: flutter_native_splash + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.7" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + flutter_staggered_grid_view: + dependency: "direct main" + description: + name: flutter_staggered_grid_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + functional_listener: + dependency: transitive + description: + name: functional_listener + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + gap: + dependency: "direct main" + description: + name: gap + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.0" + get_it_mixin: + dependency: "direct main" + description: + name: get_it_mixin + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + go_router: + dependency: "direct main" + description: + name: go_router + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.8" + google_maps: + dependency: transitive + description: + name: google_maps + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + google_maps_flutter: + dependency: "direct main" + description: + name: google_maps_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.12" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.10" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.11" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" + google_maps_flutter_web: + dependency: "direct main" + description: + name: google_maps_flutter_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0+1" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + icons_launcher: + dependency: "direct dev" + description: + name: icons_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + image_fade: + dependency: "direct main" + description: + name: image_fade + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.1" + image_gallery_saver: + dependency: "direct main" + description: + name: image_gallery_saver + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.1" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + internet_connection_checker: + dependency: "direct main" + description: + name: internet_connection_checker + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+4" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + js_wrapping: + dependency: transitive + description: + name: js_wrapping + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.4" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.3.1" + lint: + dependency: transitive + description: + name: lint + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + modal_bottom_sheet: + dependency: "direct main" + description: + name: modal_bottom_sheet + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.3+1" + package_info_plus_linux: + dependency: transitive + description: + name: package_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + package_info_plus_macos: + dependency: transitive + description: + name: package_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + package_info_plus_web: + dependency: transitive + description: + name: package_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + package_info_plus_windows: + dependency: transitive + description: + name: package_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + particle_field: + dependency: "direct main" + description: + name: particle_field + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+1" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.14" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + rnd: + dependency: "direct main" + description: + name: rnd + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + screenshot: + dependency: "direct main" + description: + name: screenshot + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.3" + share_plus: + dependency: "direct main" + description: + name: share_plus + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.10" + share_plus_linux: + dependency: transitive + description: + name: share_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_macos: + dependency: transitive + description: + name: share_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + share_plus_web: + dependency: transitive + description: + name: share_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_windows: + dependency: transitive + description: + name: share_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + simple_rich_text: + dependency: "direct main" + description: + name: simple_rich_text + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.49" + sized_context: + dependency: "direct main" + description: + name: sized_context + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + smooth_page_indicator: + dependency: "direct main" + description: + name: smooth_page_indicator + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+2" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + sync_http: + dependency: transitive + description: + name: sync_http + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + universal_platform: + dependency: transitive + description: + name: universal_platform + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+1" + unsplash_client: + dependency: "direct main" + description: + name: unsplash_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0+3" + url_launcher: + dependency: transitive + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.5" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + webdriver: + dependency: transitive + description: + name: webdriver + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + youtube_player_iframe: + dependency: "direct main" + description: + name: youtube_player_iframe + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" +sdks: + dart: ">=2.17.1 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..56732f4e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,119 @@ +name: wonders +description: Explore the famous wonders of the world. +publish_to: "none" +version: 1.7.0 + +environment: + sdk: ">=2.16.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + flutter_localizations: + sdk: flutter + + collection: ^1.15.0 + copy_with_extension: ^4.0.3 + drop_cap_text: ^1.1.3 + equatable: ^2.0.5 + extra_alignments: ^1.0.0+1 + flextras: ^0.0.2 + flutter_animate: ^1.0.0 + flutter_circular_text: ^0.3.1 + flutter_inappwebview: ^5.4.3 + flutter_native_splash: ^2.2.7 + flutter_staggered_grid_view: ^0.6.2 + flutter_svg: ^1.1.4 + gap: ^2.0.0 + get_it: ^7.2.0 + get_it_mixin: ^3.1.4 + google_maps_flutter: ^2.1.12 + google_maps_flutter_web: ^0.4.0+1 + go_router: ^4.2.8 + http: ^0.13.5 + image_fade: ^0.6.1 + image_gallery_saver: ^1.7.1 + internet_connection_checker: ^0.0.1+3 + intl: ^0.17.0 + json_annotation: ^4.6.0 + modal_bottom_sheet: ^2.1.0 + package_info_plus: ^1.4.3+1 + particle_field: ^0.0.1 + path_provider: ^2.0.11 + provider: ^6.0.2 + rnd: ^0.2.0 + screenshot: ^1.2.3 + share_plus: ^4.0.10 + shared_preferences: ^2.0.15 + simple_rich_text: ^2.0.49 + sized_context: ^1.0.0+1 + smooth_page_indicator: ^1.0.0+2 + unsplash_client: ^2.1.0+3 + youtube_player_iframe: 2.3.0 + +dev_dependencies: + build_runner: ^2.2.0 + copy_with_extension_gen: ^4.0.3 + icons_launcher: ^2.0.1 + flutter_lints: ^2.0.1 + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + json_serializable: ^6.3.1 + dart_code_metrics: ^4.17.1 + +flutter_icons: + ios: true + android: true + image_path: "assets/marketing/app-icon-1024.png" + +flutter: + uses-material-design: true + + generate: true + + assets: + - assets/images/_common/ + - assets/images/_common/icons/ + - assets/images/_common/texture/ + - assets/images/chichen_itza/ + - assets/images/christ_the_redeemer/ + - assets/images/colosseum/ + - assets/images/great_wall_of_china/ + - assets/images/machu_picchu/ + - assets/images/petra/ + - assets/images/pyramids/ + - assets/images/taj_mahal/ + - assets/images/collectibles/ + + fonts: + - family: Cinzel + fonts: + - asset: assets/fonts/CinzelDecorative-Regular.ttf + - asset: assets/fonts/CinzelDecorative-Black.ttf + - asset: assets/fonts/CinzelDecorative-Bold.ttf + - family: Yeseva + fonts: + - asset: assets/fonts/YesevaOne-Regular.ttf + - family: Tenor + fonts: + - asset: assets/fonts/TenorSans-Regular.ttf + - family: Raleway + fonts: + - asset: assets/fonts/Raleway-Regular.ttf + - asset: assets/fonts/Raleway-Italic.ttf + style: italic + - asset: assets/fonts/Raleway-Medium.ttf + weight: 500 + - asset: assets/fonts/Raleway-Bold.ttf + weight: 700 + - asset: assets/fonts/Raleway-ExtraBold.ttf + weight: 800 + - family: MaShanZheng + fonts: + - asset: assets/fonts/MaShanZheng-Regular.ttf + - family: B612Mono + fonts: + - asset: assets/fonts/B612Mono-Regular.ttf diff --git a/py/builder.py b/py/builder.py new file mode 100755 index 00000000..e1255a1e --- /dev/null +++ b/py/builder.py @@ -0,0 +1,4 @@ +#!/usr/bin/python +import os +cmd = "flutter pub run build_runner build --delete-conflicting-outputs"; +os.system(cmd); \ No newline at end of file diff --git a/py/icon-builder.py b/py/icon-builder.py new file mode 100644 index 00000000..4f611dbe --- /dev/null +++ b/py/icon-builder.py @@ -0,0 +1,4 @@ +#!/usr/bin/python +import os +cmd = "flutter pub get && flutter pub run icons_launcher:create"; +os.system(cmd); \ No newline at end of file diff --git a/py/splash-builder.py b/py/splash-builder.py new file mode 100644 index 00000000..ced6e3f1 --- /dev/null +++ b/py/splash-builder.py @@ -0,0 +1,4 @@ +#!/usr/bin/python +import os +cmd = "flutter pub run flutter_native_splash:create"; +os.system(cmd); \ No newline at end of file diff --git a/release_notes.txt b/release_notes.txt new file mode 100644 index 00000000..5f47bf32 --- /dev/null +++ b/release_notes.txt @@ -0,0 +1,2 @@ +# 1.7.0 +- Initial release \ No newline at end of file diff --git a/tools/CollectibleData_helper.html b/tools/CollectibleData_helper.html new file mode 100644 index 00000000..f74245e3 --- /dev/null +++ b/tools/CollectibleData_helper.html @@ -0,0 +1,143 @@ + + + Met API Helper Tools + + + + This tool builds the CollectibleData list. Wonders should have a blank line between them. Icon names: camera, jewelry, scroll, vase +
+ +
+ +
+ + + + \ No newline at end of file diff --git a/tools/HighlightsData_helper.html b/tools/HighlightsData_helper.html new file mode 100644 index 00000000..efb58c7a --- /dev/null +++ b/tools/HighlightsData_helper.html @@ -0,0 +1,165 @@ + + + Met API Helper Tools + + + + This tool builds the HighlightsData list. Wonders should have a blank line between them. Icon names: camera, jewelry, scroll, vase +
+ +
+ +
+ + + + \ No newline at end of file diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000..1c9f1249 --- /dev/null +++ b/tools/README.md @@ -0,0 +1 @@ +This directory contains helper tools that are not compiled into the app. \ No newline at end of file