157
README.md
@ -1,11 +1,28 @@
|
|||||||
# PiPer
|
<p align="center">
|
||||||
Adds Picture in Picture functionality to Safari for YouTube, Netflix, Amazon Video, Twitch, and more!
|
<img src="/promo/Icon-512.png" alt="PiPer logo" width="200" />
|
||||||
|
</p>
|
||||||
|
|
||||||
<img src="/promo/Promo-shot.png" alt="Screenshot of PiPer in action" width="512" />
|
<h1 align="center">
|
||||||
|
PiPer
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
PiPer is the browser extension to watch video Picture in Picture.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="#installation">Install</a> ·
|
||||||
|
<a href="https://paypal.me/adampmarcus">Donate</a> ·
|
||||||
|
<a href="https://github.com/amarcu5/PiPer/issues">Report an issue</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
|
* [Safari](#safari)
|
||||||
|
* [Chrome](#chrome)
|
||||||
- [Supported sites](#supported-sites)
|
- [Supported sites](#supported-sites)
|
||||||
- [Changelog](#changelog)
|
- [Changelog](#changelog)
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
@ -19,13 +36,18 @@ Adds Picture in Picture functionality to Safari for YouTube, Netflix, Amazon Vid
|
|||||||
## Features
|
## Features
|
||||||
* Adds a dedicated Picture in Picture button to the video player of [supported sites](#supported-sites)
|
* Adds a dedicated Picture in Picture button to the video player of [supported sites](#supported-sites)
|
||||||
* Button integrates seamlessly with the player including hover effects and tooltips
|
* Button integrates seamlessly with the player including hover effects and tooltips
|
||||||
* Supports closed captions in Picture and Picture mode
|
* Supports closed captions in Picture and Picture mode (Safari only)
|
||||||
|
* Supports Safari and Chrome
|
||||||
* Free and open source
|
* Free and open source
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Get the latest release from the [Safari Extension Gallery](https://safari-extensions.apple.com/details/?id=com.amarcus.safari.piper-BQ6Q24MF9X)
|
### Safari
|
||||||
|
Install from the [Mac App Store](https://itunes.apple.com/app/id1421915518?mt=12&ls=1) by clicking "Get"
|
||||||
<sub>...or live life on the edge with the latest [development build](https://rawgit.com/amarcu5/PiPer/develop/out/PiPer.safariextz) (IMPORTANT: these builds do not update automatically!)</sub>
|
<sub>(The [Safari Extension Gallery](https://safari-extensions.apple.com/details/?id=com.amarcus.safari.piper-BQ6Q24MF9X) is now [deprecated](https://developer.apple.com/documentation/safariextensions))</sub>
|
||||||
|
### Chrome
|
||||||
|
Install from the [Chrome Web Store](https://chrome.google.com/webstore/detail/piper/jbjleapidaddpbncgofepljddfeoghkc) by clicking "Add to Chrome"
|
||||||
|
|
||||||
|
<sub>...or live life on the edge with the latest [development build](https://github.com/amarcu5/PiPer/tree/develop/out) (IMPORTANT: these builds do not update automatically!)</sub>
|
||||||
|
|
||||||
## Supported sites
|
## Supported sites
|
||||||
* [Amazon Video](http://www.amazon.com/PrimeVideo)
|
* [Amazon Video](http://www.amazon.com/PrimeVideo)
|
||||||
@ -71,30 +93,38 @@ You can find information about releases [here](https://github.com/amarcu5/PiPer/
|
|||||||
### Building
|
### Building
|
||||||
|
|
||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
* Mac running macOS 10.12 Sierra or later
|
* Operating system
|
||||||
|
* macOS: 10.12 Sierra or newer (required to build Safari extension)
|
||||||
|
* Windows: Vista or newer using [Cygwin](https://cygwin.com/install.html)
|
||||||
|
* Linux: 64-bit Ubuntu 14.04+, Debian 8+, openSUSE 13.3+, or Fedora Linux 24+
|
||||||
|
* Software
|
||||||
|
* [Node.js](https://nodejs.org)
|
||||||
|
* [Java](https://www.java.com/en/download/) (Windows only)
|
||||||
|
|
||||||
|
|
||||||
#### Build tools
|
#### Build tools
|
||||||
For convenience the following Node.js tools have been packaged with [nexe](https://github.com/nexe/nexe) and included in this repository:
|
The following build tools are used to build the extension:
|
||||||
* [csso](https://github.com/css/csso) (3.1.1) for compressing CSS
|
* [csso](https://github.com/css/csso) for compressing CSS
|
||||||
* [svgo](https://github.com/svg/svgo) (0.7.2) for compressing SVG images
|
* [svgo](https://github.com/svg/svgo) for compressing SVG images
|
||||||
* [xarjs](https://github.com/robertknight/xar-js) (0.2.0) for packaging Safari extension
|
* [xarjs](https://github.com/robertknight/xar-js) for packaging Safari legacy extension
|
||||||
* [google-closure-compiler-js](https://github.com/google/closure-compiler-js) (20170806.0.0) for compiling JavaScript
|
* [google-closure-compiler](https://github.com/google/closure-compiler) for compiling JavaScript
|
||||||
|
|
||||||
However it is recommended to install the latest versions with [Node.js](https://nodejs.org):
|
These can be installed by executing the following command:
|
||||||
```Shell
|
```Shell
|
||||||
npm install -g csso-cli
|
npm install -g csso-cli
|
||||||
npm install -g svgo
|
npm install -g svgo
|
||||||
npm install -g xar-js
|
npm install -g xar-js
|
||||||
npm install -g google-closure-compiler-js
|
npm install -g google-closure-compiler
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally a reimplementation of the utility PlistBuddy used for automated build numbering is [provided](https://github.com/amarcu5/PiPer/tree/master/build-tools/) but it is advisable to download the original as part of [Xcode](https://itunes.apple.com/gb/app/xcode/id497799835?mt=12) or from [Apple's Command Line Tools](https://developer.apple.com/download/)
|
|
||||||
|
|
||||||
#### Steps
|
#### Steps
|
||||||
1. Clone the repository
|
1. Clone the repository
|
||||||
2. Run `make.sh`
|
2. Run `make.sh`
|
||||||
1. By default this builds the unoptimized and unpackaged development version into the `./out/` directory that can then be installed using Safari's Extension Builder
|
1. By default this builds the unoptimized and unpackaged development version for all targets into the `./out/` directory
|
||||||
2. Alternatively run `./make.sh -p release` to build the optimized and packaged release version (note that packaging requires a private key)
|
2. Alternatively:
|
||||||
|
* `./make.sh -p release` to build the optimized release versions for all targets
|
||||||
|
* `./make.sh -p release -t chrome` to build the optimized release version for the Chrome browser
|
||||||
|
* `./make.sh -h` to see the full list of options
|
||||||
|
|
||||||
### Supporting a new site
|
### Supporting a new site
|
||||||
If we wanted to support `example.com` with the source:
|
If we wanted to support `example.com` with the source:
|
||||||
@ -110,73 +140,46 @@ If we wanted to support `example.com` with the source:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
We would start by adding a new entry in the `resources` object in `main.js`:
|
We would start by adding a new file `example.js` in the [resources directory](https://github.com/amarcu5/PiPer/tree/master/src/common/scripts/resources):
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const resources = {
|
export const domain = 'example';
|
||||||
...
|
|
||||||
'example' : {
|
export const resource = {
|
||||||
buttonParent: function() {
|
buttonParent: function() {
|
||||||
// Returns the element that will contain the button
|
// Returns the element that will contain the button
|
||||||
return document.querySelector('.video-controls');
|
return document.querySelector('.video-controls');
|
||||||
},
|
},
|
||||||
videoElement: function() {
|
videoElement: function() {
|
||||||
// Returns the video element
|
// Returns the video element
|
||||||
return document.querySelector('.video-container video');
|
return document.querySelector('.video-container video');
|
||||||
},
|
},
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
captionElement: function() {
|
captionElement: function() {
|
||||||
// Returns the element that contains the video captions
|
// Returns the element that contains the video captions
|
||||||
return document.querySelector('.video-captions');
|
return document.querySelector('.video-captions');
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
We also need to update the extension permissions to support the new site by editing `./src/Info.plist`:
|
|
||||||
```XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>Permissions</key>
|
|
||||||
<dict>
|
|
||||||
<key>Website Access</key>
|
|
||||||
<dict>
|
|
||||||
...
|
|
||||||
<key>Allowed Domains</key>
|
|
||||||
<array>
|
|
||||||
...
|
|
||||||
<string>example.com</string>
|
|
||||||
<string>*.example.com</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
```
|
|
||||||
We might want to style the button so that it integrates with the page better:
|
We might want to style the button so that it integrates with the page better:
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const resources = {
|
export const resource = {
|
||||||
...
|
...
|
||||||
'example' : {
|
// Assign a CSS class
|
||||||
...
|
buttonClassName: 'control',
|
||||||
// Assign a CSS class
|
// Scale the button
|
||||||
buttonClassName: 'control',
|
buttonScale: 0.5,
|
||||||
// Scale the button
|
// Apply custom CSS styles
|
||||||
buttonScale: 0.5,
|
buttonStyle: /** CSS */ (`
|
||||||
// Apply custom CSS styles
|
/* Declaring CSS this way ensures it gets optimized when the extension is built */
|
||||||
buttonStyle: /** CSS */ (`
|
cursor: pointer;
|
||||||
/* Declaring CSS this way ensures it gets optimized when the extension is built */
|
opacity: 0.5;
|
||||||
cursor: pointer;
|
`),
|
||||||
opacity: 0.5;
|
// Apply a custom CSS hover style
|
||||||
`),
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
// Apply a custom CSS hover style
|
|
||||||
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
For more examples, please see the [source](https://github.com/amarcu5/PiPer/tree/master/src/scripts)
|
For more examples, please see the [source](https://github.com/amarcu5/PiPer/tree/master/src/)
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
* [Pied PíPer](https://github.com/JoeKuhns/PiedPiPer.safariextension) for the original inspiration and the Netflix icon
|
* [Pied PíPer](https://github.com/JoeKuhns/PiedPiPer.safariextension) for the original inspiration and the Netflix icon
|
||||||
* [Google](https://github.com/google/material-design-icons) for the Picture in Picture icon
|
|
||||||
|
|||||||
BIN
build-tools/csso
BIN
build-tools/svgo
645
make.sh
@ -4,12 +4,7 @@
|
|||||||
|
|
||||||
EXTENSION_NAME="PiPer"
|
EXTENSION_NAME="PiPer"
|
||||||
|
|
||||||
# Build tool paths; fallback to local build tools
|
SOURCE_FILES=("main.js" "fix.js" "background.js" "install.js" "localization-bridge.js")
|
||||||
CSSO_PATH=$(type "csso" >/dev/null 2>&1 && echo "csso" || echo "./build-tools/csso")
|
|
||||||
CCJS_PATH=$(type "google-closure-compiler-js" >/dev/null 2>&1 && echo "google-closure-compiler-js" || echo "./build-tools/google-closure-compiler-js")
|
|
||||||
XARJS_PATH=$(type "xarjs" >/dev/null 2>&1 && echo "xarjs" || echo "./build-tools/xarjs")
|
|
||||||
SVGO_PATH=$(type "svgo" >/dev/null 2>&1 && echo "svgo" || echo "./build-tools/svgo")
|
|
||||||
PLISTBUDDY_PATH=$(type "/usr/libexec/PlistBuddy" >/dev/null 2>&1 && echo "/usr/libexec/PlistBuddy" || echo "./build-tools/plistbuddy")
|
|
||||||
|
|
||||||
# Certifcate paths
|
# Certifcate paths
|
||||||
LEAF_CERT_PATH="../certs/cert.pem"
|
LEAF_CERT_PATH="../certs/cert.pem"
|
||||||
@ -24,13 +19,20 @@ show_help() {
|
|||||||
Usage: make.sh [options]
|
Usage: make.sh [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h -? --help Show this screen
|
-h -? --help Show this screen
|
||||||
-p --profile <release|debug> Set settings according to profile [default: debug]
|
-t --target (all|safari|safari-legacy|chrome) Make extension for target browser [default: all]
|
||||||
-c --compress-css Compress CSS
|
-p --profile (release|debug|distribute) Set settings according to profile [default: debug]
|
||||||
-j --compress-js Compress JavaScript
|
-c --compress-css Compress CSS
|
||||||
-s --compress-svg Compress SVG
|
-j --compress-js Compress JavaScript
|
||||||
-e --package-extension Package Safari extension (requires private key)
|
-s --compress-svg Compress SVG
|
||||||
-v --no-version-increment Disable automatic version incrementing
|
-l --logging-level <number> Set logging level (0=all 10=trace 20=info 30=warning 40=error)
|
||||||
|
-o --optimize-strings Remove unused localized strings by static program analysis
|
||||||
|
-i --development-team <id> Set development team ID
|
||||||
|
-a --archive-to-xcode Archive Safari extension to Xcode for Mac App Store distribution
|
||||||
|
-e --package-extension Package extension for distribution (safari-legacy requires private key)
|
||||||
|
-d --no-debug-js Remove JavaScript source maps to prevent debugging
|
||||||
|
-v --no-version-increment Disable automatic version incrementing
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
@ -43,6 +45,7 @@ while :; do
|
|||||||
-h|-\?|--help) show_help ;;
|
-h|-\?|--help) show_help ;;
|
||||||
-p|--profile) [[ "$2" ]] && profile=$2 ;;
|
-p|--profile) [[ "$2" ]] && profile=$2 ;;
|
||||||
--profile=?*) profile=${1#*=} ;;
|
--profile=?*) profile=${1#*=} ;;
|
||||||
|
-l|-t|-i|--logging-level|--target|--development-team) shift ;;
|
||||||
-?*) ;;
|
-?*) ;;
|
||||||
*) break
|
*) break
|
||||||
esac
|
esac
|
||||||
@ -51,19 +54,39 @@ done
|
|||||||
|
|
||||||
# Set default settings as per profile
|
# Set default settings as per profile
|
||||||
case $profile in
|
case $profile in
|
||||||
|
distribute)
|
||||||
|
compress_svg=1
|
||||||
|
compress_css=1
|
||||||
|
compress_js=1
|
||||||
|
debug_js=0
|
||||||
|
package_ext=1
|
||||||
|
logging_level=100
|
||||||
|
optimize_strings=1
|
||||||
|
;;
|
||||||
release)
|
release)
|
||||||
compress_svg=1
|
compress_svg=1
|
||||||
compress_css=1
|
compress_css=1
|
||||||
compress_js=1
|
compress_js=1
|
||||||
package_ext=1
|
debug_js=1
|
||||||
|
package_ext=0
|
||||||
|
logging_level=40
|
||||||
|
optimize_strings=1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
compress_svg=0
|
compress_svg=0
|
||||||
compress_css=0
|
compress_css=0
|
||||||
compress_js=0
|
compress_js=0
|
||||||
|
debug_js=1
|
||||||
package_ext=0
|
package_ext=0
|
||||||
|
logging_level=0
|
||||||
|
optimize_strings=0
|
||||||
|
profile="debug"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
update_version=1
|
update_version=1
|
||||||
|
archive_xcode=0
|
||||||
|
development_team=""
|
||||||
|
targets="all"
|
||||||
|
|
||||||
set -- "${arguments[@]}"
|
set -- "${arguments[@]}"
|
||||||
|
|
||||||
@ -74,90 +97,588 @@ while :; do
|
|||||||
-j|--compress-js) compress_js=1 ;;
|
-j|--compress-js) compress_js=1 ;;
|
||||||
-s|--compress-svg) compress_svg=1 ;;
|
-s|--compress-svg) compress_svg=1 ;;
|
||||||
-e|--package-extension) package_ext=1 ;;
|
-e|--package-extension) package_ext=1 ;;
|
||||||
|
-o|--optimize-localizations) optimize_strings=1 ;;
|
||||||
|
-d|--no-debug-js) debug_js=0 ;;
|
||||||
-v|--no-version-increment) update_version=0 ;;
|
-v|--no-version-increment) update_version=0 ;;
|
||||||
|
-t|--target) [[ "$2" ]] && targets=$2 && shift ;;
|
||||||
|
--target=?*) targets=${1#*=} ;;
|
||||||
|
-l|--logging-level) [[ "$2" ]] && logging_level=$2 && shift ;;
|
||||||
|
--logging-level=?*) logging_level=${1#*=} ;;
|
||||||
|
-i|--development-team) [[ "$2" ]] && development_team=$2 && shift ;;
|
||||||
|
--development-team=?*) development_team=${1#*=} ;;
|
||||||
|
-a|--archive-to-xcode) archive_xcode=1 ;;
|
||||||
-p|--profile) shift ;;
|
-p|--profile) shift ;;
|
||||||
-?*) ;;
|
-?*) ;;
|
||||||
*) break
|
*) break ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Highlight selected build profile
|
||||||
|
echo "Setting '${profile}' profile"
|
||||||
|
|
||||||
|
# Validate targets
|
||||||
|
case $targets in
|
||||||
|
safari) targets=("safari") ;;
|
||||||
|
safari-legacy) targets=("safari-legacy") ;;
|
||||||
|
chrome) targets=("chrome") ;;
|
||||||
|
*) targets=("safari" "safari-legacy" "chrome")
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Validate logging level
|
||||||
|
logging_level="${logging_level//[!0-9]/}"
|
||||||
|
[[ -z "$logging_level" ]] && logging_level=0
|
||||||
|
|
||||||
|
# Helper checks for build tool dependency and falls back to 'npx' if possible
|
||||||
|
function get_node_command() {
|
||||||
|
if type "$1" &>/dev/null; then
|
||||||
|
echo "$1"
|
||||||
|
elif type "npx" &>/dev/null; then
|
||||||
|
npx_package=$([[ -z "$2" ]] && echo "$1" || echo "$2")
|
||||||
|
echo "npx --quiet --package ${npx_package} $1"
|
||||||
|
echo "Info: '$1' command not found therefore falling back to 'npx'; performance may suffer (avoid this by installing package with 'npm install ${npx_package} -g')" >&2
|
||||||
|
else
|
||||||
|
echo "Error: '$1' command not found and neither fallback 'npx'" >&2
|
||||||
|
echo "Please install the latest version of Node.js (see https://nodejs.org/en/download/package-manager/)" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Target specific build checks
|
||||||
|
for i in "${!targets[@]}"; do
|
||||||
|
|
||||||
|
if [[ "${targets[$i]}" = "safari" ]]; then
|
||||||
|
|
||||||
|
# Only build 'safari' extension target when running under macOS
|
||||||
|
if [[ "$(uname)" != "Darwin" ]]; then
|
||||||
|
echo "Warning: Building 'safari' extension skipped as requires macOS" >&2
|
||||||
|
unset "targets[$i]"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure with have Xcode command line tools installed
|
||||||
|
if [[ -z $(xcode-select --print-path) ]]; then
|
||||||
|
echo "Installing Xcode Command Line Tools (expect a GUI popup)"
|
||||||
|
xcode-select --install &>/dev/null
|
||||||
|
echo "Press any key after installation has completed"
|
||||||
|
read -rsn1
|
||||||
|
if [[ -z $(xcode-select --print-path) ]]; then
|
||||||
|
echo "Unable to find Xcode Command Line Tools"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${targets[$i]}" = "safari-legacy" ]]; then
|
||||||
|
|
||||||
|
# Get 'safari-legacy' specific build tool path and exit if not found
|
||||||
|
[[ "${package_ext}" -eq 1 ]] && { XARJS_PATH=$(get_node_command "xarjs" "xar-js") || exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for google closure compiler requirements and exit if not found
|
||||||
|
CCJS_PATH=$(get_node_command "google-closure-compiler") || exit 1;
|
||||||
|
if ${CCJS_PATH} --platform native --version &>/dev/null; then
|
||||||
|
CCJS_PATH="${CCJS_PATH} --platform native";
|
||||||
|
elif ${CCJS_PATH} --platform java --version &>/dev/null; then
|
||||||
|
CCJS_PATH="${CCJS_PATH} --platform java";
|
||||||
|
else
|
||||||
|
echo "Error: Java runtime required by 'google-closure-compiler' not found" >&2
|
||||||
|
echo "Please install the latest version of Java (see https://www.java.com/en/download/)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for csso and exit if not found
|
||||||
|
[[ "${compress_css}" -eq 1 ]] && { CSSO_PATH=$(get_node_command "csso" "csso-cli") || exit 1; }
|
||||||
|
|
||||||
|
# Check for svgo and exit if not found
|
||||||
|
[[ "${compress_svg}" -eq 1 ]] && { SVGO_PATH=$(get_node_command "svgo") || exit 1; }
|
||||||
|
|
||||||
|
# Check for git and exit if not found
|
||||||
|
if [[ "${update_version}" -eq 1 ]] && { ! type "git" &>/dev/null; }; then
|
||||||
|
echo "Error: 'git' command not found" >&2
|
||||||
|
echo "Please install the latest version of git (see https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)" >&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
GIT_PATH=$(sh /etc/profile; which git)
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Build script
|
# Build script
|
||||||
#
|
#
|
||||||
|
|
||||||
# Set working directory to project root
|
# Set working directory to project root
|
||||||
project_root=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
|
cd "${BASH_SOURCE[0]%/*}"
|
||||||
cd "$project_root" || exit
|
|
||||||
|
|
||||||
# Remove output folder
|
# Remove output folder
|
||||||
rm -rf out
|
rm -rf out
|
||||||
|
|
||||||
# Make output folder
|
# Make common output folder
|
||||||
mkdir -p "out/${EXTENSION_NAME}.safariextension"
|
mkdir -p "out/${EXTENSION_NAME}"
|
||||||
|
|
||||||
# Copy items into output folder
|
# Copy common items into common output folder
|
||||||
cp -r src/* "out/${EXTENSION_NAME}.safariextension/"
|
cp -r "src/common"/* "out/${EXTENSION_NAME}/"
|
||||||
|
|
||||||
# Compress all supported images with SVGO
|
# Compress all supported images with SVGO
|
||||||
if [[ "$compress_svg" -eq 1 ]]; then
|
if [[ "${compress_svg}" -eq 1 ]]; then
|
||||||
${SVGO_PATH} -q -f "out/${EXTENSION_NAME}.safariextension/images"
|
${SVGO_PATH} -q -f "out/${EXTENSION_NAME}/images"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Compress all inline CSS with CSSO
|
# Compress all inline CSS with CSSO
|
||||||
if [[ "$compress_css" -eq 1 ]]; then
|
if [[ "${compress_css}" -eq 1 ]]; then
|
||||||
function minify_css() {
|
function minify_css() {
|
||||||
echo "$@" | sed -e 's/\\"/"/g' -e 's/\\\$/$/g' | ${CSSO_PATH} --declaration-list
|
echo "$@" | sed -e 's/\\"/"/g' -e 's/\\\$/$/g' | ${CSSO_PATH} --declaration-list
|
||||||
}
|
}
|
||||||
export -f minify_css
|
export -f minify_css
|
||||||
export CSSO_PATH
|
export CSSO_PATH
|
||||||
for path in "out/${EXTENSION_NAME}.safariextension/scripts"/*.js; do
|
for path in "out/${EXTENSION_NAME}/scripts"/{*,**/*}.js; do
|
||||||
|
[[ ! -f "${path}" ]] && continue
|
||||||
source=$(cat "${path}")
|
source=$(cat "${path}")
|
||||||
echo "echo \"$(sed -e 's/\\/\\\\/g' -e 's/\$/\\$/g' -e 's/\`/\\`/g' -e 's/\"/\\\"/g' -e 's/\\n/\\\\n/g' <<< "$source" \
|
echo "echo \"$(sed -e 's/\\/\\\\/g' -e 's/\$/\\$/g' -e 's/`/\\`/g' -e 's/\"/\\\"/g' -e 's/\\n/\\\\n/g' <<< "$source" \
|
||||||
| perl -0pe 's/\/\*\*\s+CSS\s+\*\/\s*\(\s*\\`(.*?)\\`\s*\)/\\`\$(minify_css '\''$1'\'')\\`/gms')\"" \
|
| tr '\n' '\f' \
|
||||||
|
| sed -E 's/\/\*\*[[:space:]]+CSS[[:space:]]+\*\/[[:space:]]*\([[:space:]]*\\`([^`]*)\\`[[:space:]]*\)/\\`\$(minify_css '\''\1'\'')\\`/g' \
|
||||||
|
| tr '\f' '\n')\"" \
|
||||||
| sh > "${path}"
|
| sh > "${path}"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use closure compiler to compress javascript
|
# Get current version from git if automatic versioning enabled
|
||||||
if [[ "$compress_js" -eq 1 ]]; then
|
if [[ "${update_version}" -eq 1 ]]; then
|
||||||
for path in "out/${EXTENSION_NAME}.safariextension/scripts"/*.js; do
|
|
||||||
[[ $(basename $path) == "externs.js" ]] && continue
|
# Check we're inside a git work tree
|
||||||
path=${path%.*}
|
inside_git_repo="$(git rev-parse --is-inside-work-tree 2>/dev/null)"
|
||||||
|
if [[ "${inside_git_repo}" ]]; then
|
||||||
|
|
||||||
|
# Get number of commits and release version from most recent tag
|
||||||
|
number_of_commits=$(($("${GIT_PATH}" rev-list HEAD --count) + 1))
|
||||||
|
git_release_version=$("${GIT_PATH}" describe --tags --always --abbrev=0)
|
||||||
|
git_release_version=${git_release_version%%-*};
|
||||||
|
git_release_version=${git_release_version#*v};
|
||||||
|
|
||||||
|
# Otherwise issue warning and set blank version
|
||||||
|
else
|
||||||
|
echo "Warning: Unable to set version automatically as cannot find 'git' repository (ensure repository has been cloned to fix this)" >&2
|
||||||
|
number_of_commits="0"
|
||||||
|
git_release_version="0.0.0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Helper performs multiline sed regular expression
|
||||||
|
function multiline_sed_regex() {
|
||||||
|
mv "$1" "$1.bak"
|
||||||
|
echo -n "$(cat "$1.bak")" | tr "\n" "\f" | sed -E "$2" | tr "\f" "\n" > "$1"
|
||||||
|
rm -rf "$1.bak"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make resources index file
|
||||||
|
import_list=""
|
||||||
|
resource_list=""
|
||||||
|
alias_list=""
|
||||||
|
resource_count=0
|
||||||
|
for path in "out/${EXTENSION_NAME}/scripts/resources"/*.js; do
|
||||||
|
path="${path##*/}"
|
||||||
|
[[ $path == "index.js" ]] && continue
|
||||||
|
resource_count=$((resource_count+1))
|
||||||
|
import_list="${import_list}"$'\n'"import * as r${resource_count} from \"./${path}\";"
|
||||||
|
resource=$(<"out/${EXTENSION_NAME}/scripts/resources/${path}")
|
||||||
|
regex_arr="(^|[ "$'\n'"])(const|let|var)[ "$'\n'"]+domain[ "$'\n'"]*=[ "$'\n'"]*\[([^]]+)\]"
|
||||||
|
regex_val="(^|[ "$'\n'"])(const|let|var)[ "$'\n'"]+domain[ "$'\n'"]*="
|
||||||
|
if [[ "$resource" =~ $regex_arr ]]; then
|
||||||
|
IFS=", " read -a arr <<< "${BASH_REMATCH[3]}"
|
||||||
|
resource_list="${resource_list}"$'\n'"resources[${arr[0]}] = r${resource_count}.resource;"
|
||||||
|
for ((i=1;i<${#arr[@]};i++)); do
|
||||||
|
alias_list="${alias_list}"$'\n'"resources[${arr[$i]}] = resources[${arr[0]}];"
|
||||||
|
done
|
||||||
|
elif [[ "$resource" =~ $regex_val ]]; then
|
||||||
|
resource_list="${resource_list}"$'\n'"resources[r${resource_count}.domain] = r${resource_count}.resource;"
|
||||||
|
else
|
||||||
|
echo "Warning: No domain's listed for resource '${path}'" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
/** Auto-generated file **/
|
||||||
|
${import_list}
|
||||||
|
|
||||||
|
export const resources = {};
|
||||||
|
${resource_list}
|
||||||
|
${alias_list}
|
||||||
|
EOF
|
||||||
|
} >"out/${EXTENSION_NAME}/scripts/resources/index.js"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for target in "${targets[@]}"; do
|
||||||
|
|
||||||
|
echo "Building '${target}' extension"
|
||||||
|
|
||||||
|
# Set target specific flags
|
||||||
|
case $target in
|
||||||
|
safari)
|
||||||
|
browser=1
|
||||||
|
target_extension=""
|
||||||
|
common_file_path="/Extension/Resources"
|
||||||
|
;;
|
||||||
|
safari-legacy)
|
||||||
|
browser=1
|
||||||
|
target_extension=".safariextension"
|
||||||
|
common_file_path=""
|
||||||
|
;;
|
||||||
|
chrome)
|
||||||
|
browser=2
|
||||||
|
target_extension=""
|
||||||
|
common_file_path=""
|
||||||
|
;;
|
||||||
|
*) exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Make target folder
|
||||||
|
mkdir -p "out/${EXTENSION_NAME}-${target}${target_extension}${common_file_path}"
|
||||||
|
|
||||||
|
# Copy items from common output folder to target folder
|
||||||
|
cp -r "out/${EXTENSION_NAME}"/* "out/${EXTENSION_NAME}-${target}${target_extension}${common_file_path}/"
|
||||||
|
|
||||||
|
# Copy target specific items to target output folder
|
||||||
|
cp -r "src/${target}"/* "out/${EXTENSION_NAME}-${target}${target_extension}/" 2>/dev/null
|
||||||
|
|
||||||
|
|
||||||
|
# Use closure compiler to compress javascript
|
||||||
|
function remove_element() {
|
||||||
|
for i in "${!files[@]}"; do
|
||||||
|
if [[ ${files[$i]} = "$1" ]]; then
|
||||||
|
unset "files[$i]"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_element() {
|
||||||
|
remove_element "$1"
|
||||||
|
files+=("$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_absolute_path() {
|
||||||
|
local dirname="${1%/*}"
|
||||||
|
local basename="${1##*/}"
|
||||||
|
|
||||||
|
echo "$(cd "$dirname" 2>/dev/null; pwd)/$basename"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert absolute paths to platform native path on Windows
|
||||||
|
function fix_absolute_path() {
|
||||||
|
case "$(uname -s)" in
|
||||||
|
CYGWIN*|MINGW32*|MSYS*)
|
||||||
|
echo "$(cygpath -wa ${1})"
|
||||||
|
;;
|
||||||
|
*) echo "$1"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_file() {
|
||||||
|
local dirname="${1%/*}"
|
||||||
|
local imports=()
|
||||||
|
|
||||||
|
if [[ ! -f "$1" ]]; then
|
||||||
|
remove_element "$1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local source=$(<"$1")
|
||||||
|
regex="(^| |"$'\n'")(import|export)["$'\n'" ]+(([*a-zA-Z0-9_,{}"$'\n'" $]+)from["$'\n'" ]+)?['\"]([^'\"]+)['\"][ "$'\n'";]"
|
||||||
|
while true; do
|
||||||
|
if [[ "$source" =~ $regex ]]; then
|
||||||
|
source="${source##*${BASH_REMATCH[0]}}"
|
||||||
|
imports+=("${BASH_REMATCH[5]}")
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in "${!imports[@]}"; do
|
||||||
|
imports[$i]=$(cd "$dirname"; get_absolute_path "${imports[$i]}")
|
||||||
|
add_element "${imports[$i]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in "${!imports[@]}"; do
|
||||||
|
process_file "${imports[$i]}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
scripts_path=$(get_absolute_path "out/${EXTENSION_NAME}-${target}${target_extension}${common_file_path}/scripts")
|
||||||
|
defines_path="${scripts_path}/defines.js"
|
||||||
|
extern_path=$(fix_absolute_path "${scripts_path}/externs.js")
|
||||||
|
|
||||||
|
defines_processed_path=$(echo "${defines_path%.*}" | sed -E 's|[/@\]|$|g' | sed -E 's/[-. ]/_/g' | sed -e 's/\[/%5B/g' -e 's/]/%5D/g' -e 's/>/%3E/g' -e 's/</%3C/g')
|
||||||
|
browser_flag="BROWSER$\$module${defines_processed_path}=${browser}"
|
||||||
|
logging_flag="LOGGING_LEVEL$\$module${defines_processed_path}=${logging_level}"
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$optimize_strings" -eq 1 ]]; then
|
||||||
|
localization_path="${scripts_path}/localization.js"
|
||||||
|
localization_source=$(<"$localization_path")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for entry in "${SOURCE_FILES[@]}"; do
|
||||||
|
files=()
|
||||||
|
|
||||||
|
absolute_entry="${scripts_path}/${entry}"
|
||||||
|
[[ ! -f "$absolute_entry" ]] && continue
|
||||||
|
|
||||||
|
add_element "$absolute_entry"
|
||||||
|
process_file "$absolute_entry"
|
||||||
|
|
||||||
|
|
||||||
|
# Statically analyze javascript and remove unused localized strings
|
||||||
|
if [[ "$optimize_strings" -eq 1 ]]; then
|
||||||
|
locale_keys=()
|
||||||
|
regex="(=[ \t"$'\n'"]*localizedString(WithReplacements)?[ \t"$'\n'";,])|(localizedString(WithReplacements)?\([ \t"$'\n'"]*(\"([^\"]+)\"|'([^']+)'|([^'\",)]+))[ \t"$'\n'"]*[,)])"
|
||||||
|
dynamic_access=0
|
||||||
|
for path in "${files[@]}"; do
|
||||||
|
[[ "$path" == "$localization_path" ]] && continue
|
||||||
|
source=$(<"$path")
|
||||||
|
while true; do
|
||||||
|
if [[ "$source" =~ $regex ]]; then
|
||||||
|
source="${source##*${BASH_REMATCH[0]}}"
|
||||||
|
locale_key="${BASH_REMATCH[6]:-${BASH_REMATCH[7]}}"
|
||||||
|
if [[ ! -z "$locale_key" ]]; then
|
||||||
|
locale_keys+=("$locale_key")
|
||||||
|
else
|
||||||
|
dynamic_access=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
source="$localization_source"
|
||||||
|
if [[ "$dynamic_access" -eq 0 ]]; then
|
||||||
|
processing="$localization_source"
|
||||||
|
regex="localizations\[[ \t"$'\n'"]*('([^']+)'|\"([^\"]+)\")[ \t"$'\n'"]*\][^=]+=[ "$'\n'"]*{([^}'\"]*('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")?)*}[ \t"$'\n'"]*;?"
|
||||||
|
while true; do
|
||||||
|
if [[ "$processing" =~ $regex ]]; then
|
||||||
|
processing=${processing##*"${BASH_REMATCH[0]}"}
|
||||||
|
locale_key="${BASH_REMATCH[2]:-${BASH_REMATCH[3]}}"
|
||||||
|
found=0
|
||||||
|
for key in "${locale_keys[@]}"; do
|
||||||
|
if [[ "$key" == "$locale_key" ]]; then
|
||||||
|
found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$found" -eq 0 ]]; then
|
||||||
|
source=${source/"${BASH_REMATCH[0]}"/}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo "$source" > "${localization_path}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
absolute_entry=$(fix_absolute_path "$absolute_entry")
|
||||||
|
|
||||||
|
defines=()
|
||||||
|
js_code=()
|
||||||
|
for path in "${files[@]}"; do
|
||||||
|
path=$(fix_absolute_path "$path")
|
||||||
|
js_code=("--js" "$path" "${js_code[@]}")
|
||||||
|
if [[ "$path" = "$defines_path" ]]; then
|
||||||
|
defines=(
|
||||||
|
"--define" "$logging_flag"
|
||||||
|
"--define" "$browser_flag"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$debug_js" -eq 0 ]]; then
|
||||||
|
source_map_options=()
|
||||||
|
else
|
||||||
|
source_map_options=(
|
||||||
|
--create_source_map "${absolute_entry}.map"
|
||||||
|
--source_map_location_mapping "$scripts_path|."
|
||||||
|
--source_map_include_content
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$compress_js" -eq 0 ]]; then
|
||||||
|
compression_options=(
|
||||||
|
--compilation_level WHITESPACE_ONLY \
|
||||||
|
--js_module_root "$scripts_path" \
|
||||||
|
--formatting PRETTY_PRINT \
|
||||||
|
--formatting PRINT_INPUT_DELIMITER \
|
||||||
|
)
|
||||||
|
else
|
||||||
|
compression_options=(
|
||||||
|
--compilation_level ADVANCED \
|
||||||
|
--use_types_for_optimization \
|
||||||
|
--assume_function_wrapper \
|
||||||
|
--jscomp_error strictCheckTypes \
|
||||||
|
--jscomp_error strictMissingProperties \
|
||||||
|
--jscomp_error checkTypes \
|
||||||
|
--jscomp_error checkVars \
|
||||||
|
--jscomp_error reportUnknownTypes \
|
||||||
|
--externs "$extern_path" \
|
||||||
|
"${defines[@]}" \
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
${CCJS_PATH} \
|
${CCJS_PATH} \
|
||||||
--compilationLevel ADVANCED \
|
"${compression_options[@]}" \
|
||||||
--warningLevel VERBOSE \
|
--warning_level VERBOSE \
|
||||||
--newTypeInf \
|
"${source_map_options[@]}" \
|
||||||
--useTypesForOptimization \
|
"${js_code[@]}" \
|
||||||
--externs "out/${EXTENSION_NAME}.safariextension/scripts/externs.js" \
|
> "${absolute_entry%.*}.cjs"
|
||||||
"${path}.js" > "${path}.min.js"
|
|
||||||
mv "${path}.min.js" "${path}.js"
|
|
||||||
done
|
done
|
||||||
fi
|
|
||||||
rm "out/${EXTENSION_NAME}.safariextension/scripts/externs.js"
|
|
||||||
|
|
||||||
# Update version info from git
|
# Remove uncompiled JavaScript
|
||||||
if [[ "$update_version" -eq 1 ]]; then
|
rm -f "${scripts_path}/"{*,**/*}.js
|
||||||
git=$(sh /etc/profile; which git)
|
|
||||||
number_of_commits=$(($("$git" rev-list HEAD --count) + 1))
|
# Remove any empty folders
|
||||||
git_release_version=$("$git" describe --tags --always --abbrev=0)
|
for path in "${scripts_path}/"{*,**/*}; do
|
||||||
git_release_version=${git_release_version%%-*};
|
if [[ -d "$path" ]] && [[ ! -f "$path"/* ]]; then
|
||||||
git_release_version=${git_release_version#*v};
|
rm -rf "$path"
|
||||||
info_plist="out/${EXTENSION_NAME}.safariextension/Info.plist"
|
fi
|
||||||
update_plist="update.plist"
|
done
|
||||||
${PLISTBUDDY_PATH} -c "Set :CFBundleVersion $number_of_commits" "$info_plist"
|
|
||||||
${PLISTBUDDY_PATH} -c "Set :CFBundleShortVersionString ${git_release_version}" "$info_plist"
|
# Restore '.js' extension for compiled JavaScript
|
||||||
${PLISTBUDDY_PATH} -c "Set \":Extension Updates:0:CFBundleVersion\" $number_of_commits" "$update_plist"
|
for entry in "${SOURCE_FILES[@]}"; do
|
||||||
${PLISTBUDDY_PATH} -c "Set \":Extension Updates:0:CFBundleShortVersionString\" ${git_release_version#*v}" "$update_plist"
|
entry="${scripts_path}/"${entry%.*}
|
||||||
fi
|
[ ! -f "${entry}.cjs" ] && continue
|
||||||
|
mv "${entry}.cjs" "${entry}.js"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Embed source maps and remove map files
|
||||||
|
if [[ "$debug_js" -eq 1 ]]; then
|
||||||
|
for entry in "${SOURCE_FILES[@]}"; do
|
||||||
|
entry="${scripts_path}/${entry}"
|
||||||
|
[[ ! -f "${entry}" ]] && continue
|
||||||
|
source_map=$(base64 "${entry}.map" | tr -d \\n)
|
||||||
|
echo "//# sourceMappingURL=data:application/json;base64,${source_map}" >> "${entry}"
|
||||||
|
rm -f "${entry}.map"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Package safari extension
|
# Safari specific build steps
|
||||||
cd out || exit
|
if [[ "${target}" == "safari" ]]; then
|
||||||
if [[ "$package_ext" -eq 1 ]] && [[ -f "${PRIVATE_KEY_PATH}" ]]; then
|
|
||||||
[[ ${XARJS_PATH} != /* ]] && ! type "${XARJS_PATH}" >/dev/null 2>&1 && XARJS_PATH="../${XARJS_PATH}"
|
# Update version info from git
|
||||||
${XARJS_PATH} create "${EXTENSION_NAME}.safariextz" --cert "${LEAF_CERT_PATH}" --cert "${INTERMEDIATE_CERT_PATH}" --cert "${ROOT_CERT_PATH}" --private-key "${PRIVATE_KEY_PATH}" "${EXTENSION_NAME}.safariextension"
|
if [[ "${update_version}" -eq 1 ]]; then
|
||||||
rm -rf "${EXTENSION_NAME}.safariextension"
|
multiline_sed_regex "out/${EXTENSION_NAME}-${target}/Extension/Info.plist" "s|(> *CFBundleShortVersionString *</key>[^>]+>)[^<]+|\1${git_release_version}|g"
|
||||||
fi
|
multiline_sed_regex "out/${EXTENSION_NAME}-${target}/Extension/Info.plist" "s|(> *CFBundleVersion *</key>[^>]+>)[^<]+|\1${number_of_commits}|g"
|
||||||
|
multiline_sed_regex "out/${EXTENSION_NAME}-${target}/App/Info.plist" "s|(> *CFBundleShortVersionString *</key>[^>]+>)[^<]+|\1${git_release_version}|g"
|
||||||
|
multiline_sed_regex "out/${EXTENSION_NAME}-${target}/App/Info.plist" "s|(> *CFBundleVersion *</key>[^>]+>)[^<]+|\1${number_of_commits}|g"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get development team id automatically if needed
|
||||||
|
if [[ -z "${development_team}" ]]; then
|
||||||
|
|
||||||
|
# Helper maintains unique list of ids
|
||||||
|
team_ids=()
|
||||||
|
function add_unique_team_ids() {
|
||||||
|
for i in "${!team_ids[@]}"; do
|
||||||
|
[[ ${team_ids[$i]} = "$1" ]] && return
|
||||||
|
done
|
||||||
|
team_ids+=("$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search mobileprovision files for team identifiers
|
||||||
|
regex="TeamIdentifier<\/key>[^\/]+>([A-Z0-9]{10})<\/"
|
||||||
|
for path in "${HOME}/Library/MobileDevice/Provisioning Profiles"/*.mobileprovision; do
|
||||||
|
source=$(cat "${path}" | iconv -f "ISO-8859-1" -t "UTF-8")
|
||||||
|
if [[ "${source}" =~ $regex ]]; then
|
||||||
|
add_unique_team_ids "${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If multiple or no identifiers found then prompt the user
|
||||||
|
development_team_hint="(avoid this message in future by specifying --development-team)"
|
||||||
|
if (( ${#team_ids[@]} == 0 )); then
|
||||||
|
echo "Unable to find development team automatically, please enter below: ${development_team_hint}"
|
||||||
|
read development_team </dev/tty
|
||||||
|
elif (( ${#team_ids[@]} == 1 )); then
|
||||||
|
development_team="${team_ids[0]}"
|
||||||
|
else
|
||||||
|
echo "Multiple development team's found"
|
||||||
|
for (( i=0; i < ${#team_ids[@]}; i++ )); do
|
||||||
|
echo " [${i}]: ${team_ids[$i]}"
|
||||||
|
done
|
||||||
|
echo "Please select development team to use: ${development_team_hint}"
|
||||||
|
read selection </dev/tty
|
||||||
|
development_team="${team_ids[$selection]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the xcode project
|
||||||
|
config_profile=$([[ "${profile}" == "debug" ]] && echo "Debug" || echo "Release")
|
||||||
|
xcodebuild -allowProvisioningUpdates -allowProvisioningDeviceRegistration -quiet -project "./out/${EXTENSION_NAME}-${target}/PiPer.xcodeproj" -scheme "PiPer" archive -archivePath "./out/${EXTENSION_NAME}-${target}.xcarchive" -configuration "${config_profile}" CODE_SIGN_STYLE="Automatic" CODE_SIGN_IDENTITY="Mac Developer" DEVELOPMENT_TEAM="${development_team}"
|
||||||
|
xcodebuild -allowProvisioningUpdates -allowProvisioningDeviceRegistration -quiet -exportArchive -archivePath "./out/${EXTENSION_NAME}-${target}.xcarchive" -exportPath "./out/" -exportOptionsPlist "./out/${EXTENSION_NAME}-${target}/exportOptions.plist" CODE_SIGN_STYLE="Automatic" CODE_SIGN_IDENTITY="Mac Developer" DEVELOPMENT_TEAM="${development_team}" &>/dev/null
|
||||||
|
|
||||||
|
# Copy archive to Xcode if needed
|
||||||
|
if [[ "${archive_xcode}" -eq 1 ]]; then
|
||||||
|
archive_time=$(date '+%d-%m-%Y, %H.%M')
|
||||||
|
mv "./out/${EXTENSION_NAME}-${target}.xcarchive" "${HOME}/Library/Developer/Xcode/Archives/${EXTENSION_NAME} ${archive_time}.xcarchive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Package extension
|
||||||
|
if [[ "${package_ext}" -eq 1 ]]; then
|
||||||
|
productbuild --quiet --component "./out/PiPer.app" "/Applications" "./out/${EXTENSION_NAME}-${target}.pkg"
|
||||||
|
rm -rf "./out/PiPer.app"
|
||||||
|
else
|
||||||
|
mv "out/PiPer.app" "out/${EXTENSION_NAME}-${target}.app"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove everything else
|
||||||
|
rm -rf "out/${EXTENSION_NAME}-${target}.xcarchive"
|
||||||
|
rm -rf "out/${EXTENSION_NAME}-${target}"
|
||||||
|
|
||||||
|
elif [[ "${target}" == "safari-legacy" ]]; then
|
||||||
|
|
||||||
|
# Remove irrelevant target file
|
||||||
|
rm -f "out/${EXTENSION_NAME}-${target}${target_extension}/update.plist"
|
||||||
|
|
||||||
|
# Update version info from git
|
||||||
|
if [[ "${update_version}" -eq 1 ]]; then
|
||||||
|
info_plist="out/${EXTENSION_NAME}-${target}${target_extension}/Info.plist"
|
||||||
|
update_plist="src/${target}/update.plist"
|
||||||
|
multiline_sed_regex "${info_plist}" "s|(> *CFBundleShortVersionString *</key>[^>]+>)[^<]+|\1${git_release_version}|g"
|
||||||
|
multiline_sed_regex "${info_plist}" "s|(> *CFBundleVersion *</key>[^>]+>)[^<]+|\1${number_of_commits}|g"
|
||||||
|
multiline_sed_regex "${update_plist}" "s|(> *CFBundleShortVersionString *</key>[^>]+>)[^<]+|\1${git_release_version}|g"
|
||||||
|
multiline_sed_regex "${update_plist}" "s|(> *CFBundleVersion *</key>[^>]+>)[^<]+|\1${number_of_commits}|g"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Package safari extension
|
||||||
|
if [[ "${package_ext}" -eq 1 ]] && [[ -f "out/${PRIVATE_KEY_PATH}" ]]; then
|
||||||
|
(cd out && ${XARJS_PATH} create "${EXTENSION_NAME}-${target}.safariextz" --cert "${LEAF_CERT_PATH}" --cert "${INTERMEDIATE_CERT_PATH}" --cert "${ROOT_CERT_PATH}" --private-key "${PRIVATE_KEY_PATH}" "${EXTENSION_NAME}-${target}${target_extension}")
|
||||||
|
rm -rf "out/${EXTENSION_NAME}-${target}${target_extension}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${target}" == "chrome" ]]; then
|
||||||
|
|
||||||
|
# Update manifest version information
|
||||||
|
if [[ "${update_version}" -eq 1 ]]; then
|
||||||
|
sed -i.bak -E "s|\"version\": *\"[^\"]+\"|\"version\": \"${git_release_version}.${number_of_commits}\"|g" "out/${EXTENSION_NAME}-${target}/manifest.json"
|
||||||
|
sed -i.bak -E "s|\"version_name\": *\"[^\"]+\"|\"version_name\": \"${git_release_version}\"|g" "out/${EXTENSION_NAME}-${target}/manifest.json"
|
||||||
|
rm -rf "out/${EXTENSION_NAME}-${target}/manifest.json.bak"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Package chrome extension
|
||||||
|
if [[ "${package_ext}" -eq 1 ]]; then
|
||||||
|
(cd "out/${EXTENSION_NAME}-${target}${target_extension}" && zip -rq "${EXTENSION_NAME}-${target}${target_extension}.zip" *)
|
||||||
|
mv "out/${EXTENSION_NAME}-${target}${target_extension}/${EXTENSION_NAME}-${target}${target_extension}.zip" "out/"
|
||||||
|
rm -rf "out/${EXTENSION_NAME}-${target}${target_extension}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clean common output folder
|
||||||
|
rm -rf "out/${EXTENSION_NAME}"
|
||||||
|
|
||||||
echo "Done."
|
echo "Done."
|
||||||
|
|||||||
BIN
out/PiPer-chrome.zip
Normal file
BIN
out/PiPer-safari-legacy.safariextz
Normal file
BIN
out/PiPer-safari.pkg
Normal file
BIN
promo/Chrome-small-tile.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 8.9 KiB |
BIN
promo/Icon-512.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 169 KiB |
BIN
promo/Screenshot-1.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
promo/Screenshot-2.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
promo/Screenshot-3.png
Normal file
|
After Width: | Height: | Size: 332 KiB |
BIN
promo/Screenshot-4a.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
promo/Screenshot-4b.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
promo/Screenshot-5a.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
src/Icon-128.png
|
Before Width: | Height: | Size: 354 B |
141
src/Info.plist
@ -1,141 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>Author</key>
|
|
||||||
<string>Adam Marcus</string>
|
|
||||||
<key>Builder Version</key>
|
|
||||||
<string>12602.4.8</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>PiPer</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.amarcus.safari.piper</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>0</string>
|
|
||||||
<key>Content</key>
|
|
||||||
<dict>
|
|
||||||
<key>Scripts</key>
|
|
||||||
<dict>
|
|
||||||
<key>End</key>
|
|
||||||
<array>
|
|
||||||
<string>scripts/main.js</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>Description</key>
|
|
||||||
<string>Adds Picture in Picture functionality to Youtube, Netflix, Amazon Video, Twitch, and more!</string>
|
|
||||||
<key>DeveloperIdentifier</key>
|
|
||||||
<string>BQ6Q24MF9X</string>
|
|
||||||
<key>ExtensionInfoDictionaryVersion</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>Permissions</key>
|
|
||||||
<dict>
|
|
||||||
<key>Website Access</key>
|
|
||||||
<dict>
|
|
||||||
<key>Allowed Domains</key>
|
|
||||||
<array>
|
|
||||||
<string>aktualne.cz</string>
|
|
||||||
<string>*.aktualne.cz</string>
|
|
||||||
<string>amazon.co.uk</string>
|
|
||||||
<string>*.amazon.co.uk</string>
|
|
||||||
<string>amazon.com</string>
|
|
||||||
<string>*.amazon.com</string>
|
|
||||||
<string>amazon.fr</string>
|
|
||||||
<string>*.amazon.fr</string>
|
|
||||||
<string>amazon.de</string>
|
|
||||||
<string>*.amazon.de</string>
|
|
||||||
<string>amazon.ca</string>
|
|
||||||
<string>*.amazon.ca</string>
|
|
||||||
<string>collegehumor.com</string>
|
|
||||||
<string>*.collegehumor.com</string>
|
|
||||||
<string>curiositystream.com</string>
|
|
||||||
<string>*.curiositystream.com</string>
|
|
||||||
<string>eurosportplayer.com</string>
|
|
||||||
<string>*.eurosportplayer.com</string>
|
|
||||||
<string>giantbomb.com</string>
|
|
||||||
<string>*.giantbomb.com</string>
|
|
||||||
<string>hulu.com</string>
|
|
||||||
<string>*.hulu.com</string>
|
|
||||||
<string>littlethings.com</string>
|
|
||||||
<string>*.littlethings.com</string>
|
|
||||||
<string>mashable.com</string>
|
|
||||||
<string>*.mashable.com</string>
|
|
||||||
<string>metacafe.com</string>
|
|
||||||
<string>*.metacafe.com</string>
|
|
||||||
<string>mixer.com</string>
|
|
||||||
<string>*.mixer.com</string>
|
|
||||||
<string>mlb.com</string>
|
|
||||||
<string>*.mlb.com</string>
|
|
||||||
<string>mlb.tv</string>
|
|
||||||
<string>*.mlb.tv</string>
|
|
||||||
<string>netflix.com</string>
|
|
||||||
<string>*.netflix.com</string>
|
|
||||||
<string>ocs.fr</string>
|
|
||||||
<string>*.ocs.fr</string>
|
|
||||||
<string>openload.co</string>
|
|
||||||
<string>*.openload.co</string>
|
|
||||||
<string>oload.stream</string>
|
|
||||||
<string>*.oload.stream</string>
|
|
||||||
<string>oload.tv</string>
|
|
||||||
<string>*.oload.tv</string>
|
|
||||||
<string>periscope.tv</string>
|
|
||||||
<string>*.periscope.tv</string>
|
|
||||||
<string>plex.tv</string>
|
|
||||||
<string>*.plex.tv</string>
|
|
||||||
<string>primevideo.com</string>
|
|
||||||
<string>*.primevideo.com</string>
|
|
||||||
<string>pscp.tv</string>
|
|
||||||
<string>*.pscp.tv</string>
|
|
||||||
<string>seznam.cz</string>
|
|
||||||
<string>*.seznam.cz</string>
|
|
||||||
<string>stream.cz</string>
|
|
||||||
<string>*.stream.cz</string>
|
|
||||||
<string>streamable.com</string>
|
|
||||||
<string>*.streamable.com</string>
|
|
||||||
<string>ted.com</string>
|
|
||||||
<string>*.ted.com</string>
|
|
||||||
<string>theonion.com</string>
|
|
||||||
<string>*.theonion.com</string>
|
|
||||||
<string>twitch.tv</string>
|
|
||||||
<string>*.twitch.tv</string>
|
|
||||||
<string>udemy.com</string>
|
|
||||||
<string>*.udemy.com</string>
|
|
||||||
<string>vevo.com</string>
|
|
||||||
<string>*.vevo.com</string>
|
|
||||||
<string>vice.com</string>
|
|
||||||
<string>*.vice.com</string>
|
|
||||||
<string>vid.me</string>
|
|
||||||
<string>*.vid.me</string>
|
|
||||||
<string>vier.be</string>
|
|
||||||
<string>*.vier.be</string>
|
|
||||||
<string>vijf.be</string>
|
|
||||||
<string>*.vijf.be</string>
|
|
||||||
<string>vrt.be</string>
|
|
||||||
<string>*.vrt.be</string>
|
|
||||||
<string>vrv.co</string>
|
|
||||||
<string>*.vrv.co</string>
|
|
||||||
<string>yeloplay.be</string>
|
|
||||||
<string>*.yeloplay.be</string>
|
|
||||||
<string>youtu.be</string>
|
|
||||||
<string>*.youtu.be</string>
|
|
||||||
<string>youtube.com</string>
|
|
||||||
<string>*.youtube.com</string>
|
|
||||||
<string>zes.be</string>
|
|
||||||
<string>*.zes.be</string>
|
|
||||||
</array>
|
|
||||||
<key>Include Secure Pages</key>
|
|
||||||
<true/>
|
|
||||||
<key>Level</key>
|
|
||||||
<string>Some</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>Update Manifest URL</key>
|
|
||||||
<string>https://s3.amazonaws.com/piper-extension/update.plist</string>
|
|
||||||
<key>Website</key>
|
|
||||||
<string>https://github.com/amarcu5/PiPer/</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
115
src/chrome/install.html
Normal file
31
src/chrome/manifest.json
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "PiPer",
|
||||||
|
"description": "Adds Picture in Picture functionality to YouTube, Netflix, Amazon Video, Twitch, and more!",
|
||||||
|
"version": "0.0.0.0",
|
||||||
|
"version_name": "0.0.0",
|
||||||
|
"icons": {
|
||||||
|
"128": "Icon-128.png"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"scripts": ["scripts/background.js"],
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"all_frames": true,
|
||||||
|
"matches": ["http://*/*", "https://*/*"],
|
||||||
|
"run_at": "document_idle",
|
||||||
|
"js": ["scripts/main.js"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"activeTab",
|
||||||
|
"storage"
|
||||||
|
],
|
||||||
|
"web_accessible_resources": [
|
||||||
|
"images/*.svg",
|
||||||
|
"scripts/*.js"
|
||||||
|
],
|
||||||
|
"minimum_chrome_version": "69.0.3483.0",
|
||||||
|
"manifest_version": 2
|
||||||
|
}
|
||||||
5
src/chrome/scripts/background.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
chrome.runtime.onInstalled.addListener(function(/** {reason: string} */ details) {
|
||||||
|
if (details.reason == "install") {
|
||||||
|
chrome.tabs.create({url: chrome.extension.getURL("install.html")});
|
||||||
|
}
|
||||||
|
});
|
||||||
48
src/chrome/scripts/install.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { info } from './logger.js'
|
||||||
|
import { localizedString, localizedStringWithReplacements } from './localization.js'
|
||||||
|
|
||||||
|
// Hide page during loading
|
||||||
|
const htmlTag = /** @type {HTMLElement} */ (document.getElementsByTagName("html")[0]);
|
||||||
|
htmlTag.style.display = 'none';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
// Localize text elements
|
||||||
|
const localizedElements = document.getElementsByClassName('localized-string');
|
||||||
|
for (let index = 0, element; element = localizedElements[index]; index++) {
|
||||||
|
const key = element.textContent.trim();
|
||||||
|
|
||||||
|
let string;
|
||||||
|
if (key == 'chrome-flags-warning') {
|
||||||
|
string = localizedStringWithReplacements(key, [
|
||||||
|
['emphasis', '<span class="warning-emphasis">'],
|
||||||
|
['/emphasis', '</span>'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
string = localizedString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
element.innerHTML = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make page visible
|
||||||
|
htmlTag.style.removeProperty('display');
|
||||||
|
|
||||||
|
// Open required Chrome flag if warning button clicked
|
||||||
|
document.getElementById('warning-button').addEventListener('click', function(event) {
|
||||||
|
chrome.tabs.create({url: 'chrome://flags/#enable-surfaces-for-videos'});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test for Picture in Picture support and display warning to activate Chrome flags if needed
|
||||||
|
const video = /** @type {HTMLVideoElement} */ (document.getElementById('test-video'));
|
||||||
|
video.addEventListener('loadeddata', function() {
|
||||||
|
video.requestPictureInPicture().catch(function(/** Error */ error) {
|
||||||
|
if (~error.message.indexOf('Picture-in-Picture is not available')) {
|
||||||
|
info('Picture-in-Picture NOT supported');
|
||||||
|
document.getElementById('warning').style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
info('Picture-in-Picture IS supported');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
BIN
src/common/Icon-128.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
20
src/common/images/default-exit.svg
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Symbol" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M3.5,25.5c-0.4,0-1-0.1-1.5-0.5C1.7,24.6,1.2,24,1.2,23V9.8c0-0.3,0-1,0.4-1.6c0.4-0.5,0.9-0.7,1.6-0.7h24.9
|
||||||
|
c1.3,0.1,2.5,0.8,2.5,2.2v8h-2.5V10c-0.1,0-0.1,0-0.1,0L3.8,10v13h10v2.5h-10C3.7,25.5,3.6,25.5,3.5,25.5z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M13.5,18.5l-5.6-5.6h2.7c0.6,0,1-0.4,1-1s-0.4-1-1-1H5.5c-0.1,0-0.3,0-0.4,0.1c-0.2,0.1-0.4,0.3-0.5,0.5
|
||||||
|
c-0.1,0.1-0.1,0.3-0.1,0.4v5.4c0,0.6,0.4,1,1,1s1-0.4,1-1v-3l5.6,5.6c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3
|
||||||
|
C13.9,19.5,13.9,18.9,13.5,18.5z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M31.9,31.2H17.6c-1.6,0-2.9-1.3-2.9-2.8v-6.3c0-1.6,1.3-2.8,2.9-2.8h14.3c1.6,0,2.8,1.3,2.8,2.8v6.3
|
||||||
|
C34.8,29.9,33.4,31.2,31.9,31.2z M17.6,21.8c-0.2,0-0.4,0.1-0.4,0.3v6.3c0,0.2,0.1,0.3,0.4,0.3h14.3c0.2,0,0.3-0.2,0.3-0.3v-6.3
|
||||||
|
c0-0.2-0.1-0.3-0.3-0.3H17.6z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
19
src/common/images/default.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Symbol" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M12.8,25.8H3.7c-1.2,0-2.5-0.9-2.5-2.5V9.6c0-2.1,1.7-2.2,2-2.2h24.9c2.4,0,2.5,1.7,2.5,2.2v8.5h-2.5V9.9l0,0
|
||||||
|
H3.8v13.4h9V25.8z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M13.8,14.2c0-0.6-0.4-1-1-1s-1,0.4-1,1v2.6l-5.6-5.6c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.6,5.6H7.8
|
||||||
|
c-0.6,0-1,0.4-1,1s0.4,1,1,1h5c0.1,0,0.3,0,0.4-0.1c0.2-0.1,0.4-0.3,0.5-0.5c0.1-0.1,0.1-0.3,0.1-0.4V14.2z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M31.9,31.8H17.1c-1.6,0-2.9-1.3-2.9-2.8v-6.8c0-1.6,1.3-2.8,2.9-2.8h14.8c1.6,0,2.8,1.3,2.8,2.8v6.7
|
||||||
|
C34.8,30.4,33.5,31.8,31.9,31.8z M17.1,21.7c-0.2,0-0.4,0.2-0.4,0.4v6.8c0,0.2,0.1,0.3,0.4,0.3h14.8c0.2,0,0.3-0.2,0.3-0.5V22
|
||||||
|
c0-0.2-0.1-0.3-0.3-0.3H17.1L17.1,21.7z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
848
src/common/images/logo.svg
Normal file
@ -0,0 +1,848 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:url(#Backround_1_);}
|
||||||
|
.st1{opacity:0.25;}
|
||||||
|
.st2{opacity:0.2;}
|
||||||
|
.st3{fill:none;stroke:#231F20;}
|
||||||
|
.st4{fill:none;stroke:#231F20;stroke-width:2;}
|
||||||
|
.st5{opacity:0.18;}
|
||||||
|
.st6{fill:none;stroke:#231F20;stroke-width:3;}
|
||||||
|
.st7{fill:none;stroke:#231F20;stroke-width:4;}
|
||||||
|
.st8{opacity:0.16;}
|
||||||
|
.st9{fill:none;stroke:#231F20;stroke-width:5;}
|
||||||
|
.st10{fill:none;stroke:#231F20;stroke-width:6;}
|
||||||
|
.st11{opacity:0.14;}
|
||||||
|
.st12{fill:none;stroke:#231F20;stroke-width:7;}
|
||||||
|
.st13{fill:none;stroke:#231F20;stroke-width:8;}
|
||||||
|
.st14{opacity:0.12;}
|
||||||
|
.st15{fill:none;stroke:#231F20;stroke-width:9;}
|
||||||
|
.st16{fill:none;stroke:#231F20;stroke-width:10;}
|
||||||
|
.st17{opacity:0.1;}
|
||||||
|
.st18{fill:none;stroke:#231F20;stroke-width:11;}
|
||||||
|
.st19{fill:none;stroke:#231F20;stroke-width:12;}
|
||||||
|
.st20{opacity:9.000000e-02;}
|
||||||
|
.st21{fill:none;stroke:#231F20;stroke-width:13;}
|
||||||
|
.st22{fill:none;stroke:#231F20;stroke-width:14;}
|
||||||
|
.st23{opacity:8.000000e-02;}
|
||||||
|
.st24{fill:none;stroke:#231F20;stroke-width:15;}
|
||||||
|
.st25{fill:none;stroke:#231F20;stroke-width:16;}
|
||||||
|
.st26{opacity:7.000000e-02;}
|
||||||
|
.st27{fill:none;stroke:#231F20;stroke-width:17;}
|
||||||
|
.st28{fill:none;stroke:#231F20;stroke-width:18;}
|
||||||
|
.st29{opacity:6.000000e-02;}
|
||||||
|
.st30{fill:none;stroke:#231F20;stroke-width:19;}
|
||||||
|
.st31{fill:none;stroke:#231F20;stroke-width:20;}
|
||||||
|
.st32{opacity:5.000000e-02;}
|
||||||
|
.st33{fill:none;stroke:#231F20;stroke-width:21;}
|
||||||
|
.st34{fill:none;stroke:#231F20;stroke-width:22;}
|
||||||
|
.st35{opacity:4.000000e-02;}
|
||||||
|
.st36{fill:none;stroke:#231F20;stroke-width:23;}
|
||||||
|
.st37{fill:none;stroke:#231F20;stroke-width:24;}
|
||||||
|
.st38{opacity:3.000000e-02;}
|
||||||
|
.st39{fill:none;stroke:#231F20;stroke-width:25;}
|
||||||
|
.st40{fill:none;stroke:#231F20;stroke-width:26;}
|
||||||
|
.st41{opacity:2.000000e-02;}
|
||||||
|
.st42{fill:none;stroke:#231F20;stroke-width:27;}
|
||||||
|
.st43{fill:none;stroke:#231F20;stroke-width:28;}
|
||||||
|
.st44{opacity:1.000000e-02;}
|
||||||
|
.st45{fill:none;stroke:#231F20;stroke-width:29;}
|
||||||
|
.st46{fill:none;stroke:#231F20;stroke-width:30;}
|
||||||
|
.st47{fill:none;stroke:#FFFFFF;stroke-width:32;stroke-miterlimit:10;}
|
||||||
|
.st48{fill:none;stroke:#FFFFFF;stroke-width:32;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<linearGradient id="Backround_1_" gradientUnits="userSpaceOnUse" x1="193.802" y1="830.1981" x2="830.1981" y2="193.802" gradientTransform="matrix(1 0 0 -1 0 1024)">
|
||||||
|
<stop offset="1.179610e-03" style="stop-color:#0671D9"/>
|
||||||
|
<stop offset="1" style="stop-color:#1B557C"/>
|
||||||
|
</linearGradient>
|
||||||
|
<circle id="Backround" class="st0" cx="512" cy="512" r="450"/>
|
||||||
|
<g id="Shadow" class="st1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2l133.2,135.3
|
||||||
|
c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2c24.4,0,44.2,19.8,44.2,44.2
|
||||||
|
v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5c0,8.5,6.9,15.4,15.4,15.4h263.2
|
||||||
|
c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st2">
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st2">
|
||||||
|
<g>
|
||||||
|
<path class="st4" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st4" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st4" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st4" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st4" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st5">
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st5">
|
||||||
|
<g>
|
||||||
|
<path class="st7" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st7" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st7" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st7" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st7" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st8">
|
||||||
|
<g>
|
||||||
|
<path class="st9" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st9" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st9" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st9" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st9" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st8">
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st11">
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st11">
|
||||||
|
<g>
|
||||||
|
<path class="st13" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st13" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st13" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st13" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st13" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st14">
|
||||||
|
<g>
|
||||||
|
<path class="st15" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st15" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st15" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st15" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st15" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st14">
|
||||||
|
<g>
|
||||||
|
<path class="st16" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st16" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st16" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st16" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st16" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st17">
|
||||||
|
<g>
|
||||||
|
<path class="st18" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st18" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st18" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st18" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st18" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st17">
|
||||||
|
<g>
|
||||||
|
<path class="st19" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st19" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st19" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st19" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st19" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st20">
|
||||||
|
<g>
|
||||||
|
<path class="st21" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st21" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st21" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st21" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st21" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st20">
|
||||||
|
<g>
|
||||||
|
<path class="st22" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st22" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st22" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st22" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st22" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st23">
|
||||||
|
<g>
|
||||||
|
<path class="st24" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st24" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st24" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st24" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st24" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st23">
|
||||||
|
<g>
|
||||||
|
<path class="st25" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st25" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st25" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st25" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st25" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st26">
|
||||||
|
<g>
|
||||||
|
<path class="st27" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st27" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st27" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st27" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st27" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st26">
|
||||||
|
<g>
|
||||||
|
<path class="st28" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st28" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st28" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st28" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st28" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st29">
|
||||||
|
<g>
|
||||||
|
<path class="st30" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st30" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st30" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st30" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st30" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st29">
|
||||||
|
<g>
|
||||||
|
<path class="st31" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st31" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st31" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st31" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st31" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st32">
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st32">
|
||||||
|
<g>
|
||||||
|
<path class="st34" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st34" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st34" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st34" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st34" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st35">
|
||||||
|
<g>
|
||||||
|
<path class="st36" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st36" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st36" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st36" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st36" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st35">
|
||||||
|
<g>
|
||||||
|
<path class="st37" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st37" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st37" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st37" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st37" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st38">
|
||||||
|
<g>
|
||||||
|
<path class="st39" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st39" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st39" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st39" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st39" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st38">
|
||||||
|
<g>
|
||||||
|
<path class="st40" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st40" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st40" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st40" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st40" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st41">
|
||||||
|
<g>
|
||||||
|
<path class="st42" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st42" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st42" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st42" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st42" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st41">
|
||||||
|
<g>
|
||||||
|
<path class="st43" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st43" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st43" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st43" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st43" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st44">
|
||||||
|
<g>
|
||||||
|
<path class="st45" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st45" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st45" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st45" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st45" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st44">
|
||||||
|
<g>
|
||||||
|
<path class="st46" d="M253,619.9c-5.6,0-14.3-1.2-21.8-7.5c-5.5-4.6-12.1-13.5-12.1-29.7V339.9c-0.4-4.3-0.8-15.5,6.6-24.3
|
||||||
|
c3.7-4.3,10.5-9.5,22.1-9.5h458.3h0.3c1.4,0.1,8.8,0.5,16.6,3.8c13.1,5.4,20.7,16,20.7,29.1v146.5H715V338.9c0-0.5,0-1.3-2.9-2.4
|
||||||
|
c-2.9-1.2-6-1.5-6.8-1.6H247.9h-0.1c-0.1,0.7-0.1,1.5,0,1.7l0.2,1.2v245c0,4.5,0.9,6.8,1.7,7.6c0.8,0.7,2.9,0.9,4,0.8l1.4-0.4
|
||||||
|
l1,0.1h185.4v28.8H256.9C255.9,619.8,254.6,619.9,253,619.9z M253.4,591.1L253.4,591.1L253.4,591.1z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st46" d="M422.5,528c-3.7,0-7.5-1.4-10.3-4.3L279,388.4c-5.6-5.7-5.5-14.8,0.2-20.3c5.7-5.6,14.8-5.5,20.3,0.2
|
||||||
|
l133.2,135.3c5.6,5.7,5.5,14.8-0.2,20.3C429.8,526.7,426.1,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st46" d="M422.5,528h-94.2c-7.9,0-14.4-6.5-14.4-14.4s6.5-14.4,14.4-14.4h94.2c7.9,0,14.4,6.5,14.4,14.4
|
||||||
|
S430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st46" d="M422.5,528c-7.9,0-14.4-6.5-14.4-14.4V413.3c0-7.9,6.5-14.4,14.4-14.4c7.9,0,14.4,6.5,14.4,14.4v100.4
|
||||||
|
C436.9,521.5,430.5,528,422.5,528z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st46" d="M773.8,725.6H510.6c-24.4,0-44.2-19.8-44.2-44.2V565.9c0-24.4,19.8-44.2,44.2-44.2h263.2
|
||||||
|
c24.4,0,44.2,19.8,44.2,44.2v115.5C818,705.7,798.2,725.6,773.8,725.6z M510.6,550.5c-8.5,0-15.4,6.9-15.4,15.4v115.5
|
||||||
|
c0,8.5,6.9,15.4,15.4,15.4h263.2c8.5,0,15.4-6.9,15.4-15.4V565.9c0-8.5-6.9-15.4-15.4-15.4H510.6L510.6,550.5z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Symbol">
|
||||||
|
<g>
|
||||||
|
<path class="st47" d="M435.1,599H249.7c0,0-22.5,4.1-22.5-22.5c0-26.6,0-243.8,0-243.8s-3.1-18.5,14.3-18.5s457.9,0,457.9,0
|
||||||
|
s23.6,1,23.6,18.5c0,17.5,0,146.5,0,146.5"/>
|
||||||
|
<line class="st48" x1="416.1" y1="507.3" x2="283" y2="372"/>
|
||||||
|
<line class="st48" x1="321.9" y1="507.3" x2="416.1" y2="507.3"/>
|
||||||
|
<line class="st48" x1="416.1" y1="407" x2="416.1" y2="507.3"/>
|
||||||
|
<path class="st47" d="M767.5,704.9H504.4c-16.4,0-29.8-13.4-29.8-29.8V559.6c0-16.4,13.4-29.8,29.8-29.8h263.2
|
||||||
|
c16.4,0,29.8,13.4,29.8,29.8v115.5C797.3,691.5,783.9,704.9,767.5,704.9z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 46 KiB |
380
src/common/images/warning.svg
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||||
|
<g id="Triangle_Shadow" opacity="0.25">
|
||||||
|
<path fill="#323232" d="M512.1,116.8c-28.9,0-54.7,14.9-69.1,39.9L71.6,799.9c-14.4,25-14.4,54.8,0,79.8s40.3,39.9,70.7,36.9h742.6
|
||||||
|
c27.4,3,53.2-11.9,67.6-36.9c14.4-25,14.4-54.8,0-79.8L581.2,156.7C566.8,131.7,541,116.8,512.1,116.8L512.1,116.8z"/>
|
||||||
|
<g opacity="0.16">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,115.8c-29.2,0-55.4,15.1-70,40.4L70.8,799.4c-14.6,25.3-14.6,55.5,0,80.8
|
||||||
|
c13.9,24,37.1,37.8,63.8,37.8c2.6,0,5.2-0.1,7.8-0.4h742.5c2.3,0.3,4.7,0.4,7.1,0.4c12.2,0,24.4-3.5,35.2-10.2
|
||||||
|
c10.6-6.6,19.7-16.1,26.3-27.6c14.6-25.3,14.6-55.5,0-80.8L582.1,156.2C567.5,130.9,541.3,115.8,512.1,115.8L512.1,115.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.16">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,114.8c-29.6,0-56.1,15.3-70.9,40.9L69.9,798.9c-14.8,25.6-14.8,56.2,0,81.8
|
||||||
|
c6.7,11.7,16.1,21.3,27,27.9c11.4,6.9,24.1,10.4,37.7,10.4c2.6,0,5.2-0.1,7.8-0.4h742.4c2.4,0.3,4.7,0.4,7.1,0.4
|
||||||
|
c12.4,0,24.7-3.6,35.7-10.4c10.8-6.7,20-16.3,26.7-27.9c14.8-25.6,14.8-56.2,0-81.8L583,155.7
|
||||||
|
C568.2,130.1,541.7,114.8,512.1,114.8L512.1,114.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,113.8c-29.9,0-56.8,15.5-71.7,41.4L69,798.4c-15,25.9-15,56.9,0,82.8
|
||||||
|
c6.8,11.8,16.3,21.6,27.3,28.3c11.5,7,24.4,10.5,38.2,10.5c2.6,0,5.2-0.1,7.9-0.4h742.3c2.4,0.3,4.8,0.4,7.2,0.4
|
||||||
|
c12.6,0,25.1-3.6,36.2-10.5c10.9-6.8,20.2-16.5,27-28.3c15-25.9,15-56.9,0-82.8L583.8,155.2C568.9,129.3,542,113.8,512.1,113.8
|
||||||
|
L512.1,113.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,112.8c-30.3,0-57.5,15.7-72.6,41.9L68.2,797.9c-15.2,26.2-15.2,57.6,0,83.8
|
||||||
|
c6.9,12,16.5,21.9,27.7,28.6c11.7,7.1,24.7,10.6,38.7,10.6c2.6,0,5.3-0.1,7.9-0.4h742.2c2.4,0.3,4.8,0.4,7.2,0.4
|
||||||
|
c12.8,0,25.5-3.7,36.7-10.7c11-6.9,20.5-16.7,27.4-28.6c15.2-26.2,15.2-57.6,0-83.8L584.7,154.7
|
||||||
|
C569.5,128.5,542.4,112.8,512.1,112.8L512.1,112.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,111.8c-30.7,0-58.1,15.9-73.5,42.4L67.3,797.4C52,824,52,855.7,67.3,882.2
|
||||||
|
c7,12.1,16.7,22.1,28,29c11.9,7.2,25.1,10.8,39.2,10.8c2.6,0,5.3-0.1,8-0.4h742.1c2.4,0.3,4.9,0.4,7.3,0.4
|
||||||
|
c13,0,25.9-3.7,37.3-10.8c11.2-6.9,20.8-17,27.7-29c15.3-26.6,15.3-58.3,0-84.8L585.6,154.2C570.2,127.7,542.8,111.8,512.1,111.8
|
||||||
|
L512.1,111.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,110.8c-31,0-58.8,16-74.3,42.9L66.4,796.9c-15.5,26.9-15.5,59,0,85.8
|
||||||
|
c7.1,12.3,16.9,22.4,28.4,29.3c11.9,7.2,25.6,10.9,39.8,10.9c2.6,0,5.3-0.1,8-0.4h742c2.4,0.3,4.9,0.4,7.3,0.4
|
||||||
|
c13.2,0,26.2-3.8,37.8-11c11.3-7,21-17.2,28-29.3c15.5-26.9,15.5-59,0-85.8L586.4,153.7C570.9,126.9,543.1,110.8,512.1,110.8
|
||||||
|
L512.1,110.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,109.8c-31.4,0-59.5,16.2-75.2,43.4L65.6,796.4c-15.7,27.2-15.7,59.7,0,86.8
|
||||||
|
c7.2,12.4,17.1,22.7,28.7,29.7c12,7.2,25.9,11.1,40.3,11.1c2.7,0,5.4-0.1,8.1-0.4h741.9c2.4,0.3,4.9,0.4,7.4,0.4
|
||||||
|
c13.3,0,26.6-3.8,38.3-11.1c11.5-7.1,21.3-17.4,28.4-29.7c15.7-27.2,15.7-59.7,0-86.8L587.3,153.2
|
||||||
|
C571.6,126,543.5,109.8,512.1,109.8L512.1,109.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,108.8c-31.8,0-60.2,16.4-76.1,43.9L64.7,795.9c-15.9,27.5-15.9,60.3,0,87.8
|
||||||
|
c7.2,12.6,17.3,23,29.1,30.1c12.2,7.3,26.3,11.2,40.8,11.2c2.7,0,5.4-0.1,8.1-0.4h741.8c2.5,0.3,5,0.4,7.4,0.4
|
||||||
|
c13.5,0,27-3.9,38.9-11.3c11.6-7.2,21.5-17.6,28.7-30c15.9-27.5,15.9-60.3,0-87.8L588.2,152.7
|
||||||
|
C572.3,125.2,543.8,108.8,512.1,108.8L512.1,108.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,107.8c-32.1,0-60.9,16.6-76.9,44.4L63.8,795.4c-16.1,27.8-16.1,61,0,88.8
|
||||||
|
c7.3,12.7,17.5,23.2,29.4,30.4c12.3,7.4,26.6,11.4,41.3,11.4c2.7,0,5.4-0.1,8.1-0.4h741.7c2.5,0.3,5,0.4,7.5,0.4
|
||||||
|
c13.7,0,27.3-4,39.4-11.4c11.7-7.3,21.8-17.8,29-30.4c16.1-27.8,16.1-61,0-88.8L589,152.2C573,124.4,544.2,107.8,512.1,107.8
|
||||||
|
L512.1,107.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,106.8c-32.5,0-61.6,16.8-77.8,44.9L63,794.9c-16.2,28.1-16.2,61.7,0,89.8
|
||||||
|
c7.4,12.8,17.7,23.5,29.8,30.8c12.5,7.5,26.9,11.5,41.8,11.5c2.7,0,5.5-0.1,8.2-0.4h741.6c2.5,0.3,5,0.4,7.5,0.4
|
||||||
|
c13.9,0,27.7-4,39.9-11.6c11.9-7.4,22-18,29.4-30.7c16.2-28.1,16.2-61.7,0-89.8L589.9,151.7C573.7,123.6,544.6,106.8,512.1,106.8
|
||||||
|
L512.1,106.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,105.8c-32.8,0-62.2,17-78.7,45.4L62.1,794.4c-16.4,28.4-16.4,62.4,0,90.8
|
||||||
|
c7.5,13,17.9,23.8,30.1,31.1c12.6,7.6,27.3,11.7,42.3,11.7c2.7,0,5.5-0.1,8.2-0.4h741.5c2.5,0.3,5.1,0.4,7.6,0.4
|
||||||
|
c14.1,0,28.1-4.1,40.4-11.7c12-7.5,22.3-18.2,29.7-31.1c16.4-28.4,16.4-62.4,0-90.8L590.8,151.2
|
||||||
|
C574.3,122.8,544.9,105.8,512.1,105.8L512.1,105.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,104.8c-33.2,0-62.9,17.2-79.5,45.9L61.2,793.9c-16.6,28.8-16.6,63.1,0,91.8
|
||||||
|
c7.6,13.1,18.1,24,30.5,31.5c12.8,7.7,27.6,11.8,42.9,11.8c2.7,0,5.5-0.1,8.3-0.4h741.4c2.5,0.3,5.1,0.4,7.6,0.4
|
||||||
|
c14.3,0,28.4-4.1,41-11.9c12.2-7.5,22.6-18.4,30.1-31.4c16.6-28.8,16.6-63.1,0-91.8L591.6,150.7C575,122,545.3,104.8,512.1,104.8
|
||||||
|
L512.1,104.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,103.8c-33.6,0-63.6,17.4-80.4,46.4L60.4,793.4c-16.8,29.1-16.8,63.8,0,92.8
|
||||||
|
c7.7,13.3,18.3,24.3,30.8,31.8c12.9,7.8,27.9,11.9,43.4,11.9c2.7,0,5.5-0.1,8.3-0.4h741.3c2.6,0.3,5.1,0.4,7.7,0.4
|
||||||
|
c14.5,0,28.8-4.2,41.5-12c12.3-7.6,22.8-18.6,30.4-31.8c16.8-29.1,16.8-63.8,0-92.8L592.5,150.2
|
||||||
|
C575.7,121.2,545.7,103.8,512.1,103.8L512.1,103.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,102.8c-33.9,0-64.3,17.5-81.3,46.9L59.5,792.9c-17,29.4-17,64.5,0,93.8
|
||||||
|
c7.8,13.4,18.5,24.6,31.2,32.2c13.1,7.9,28.3,12.1,43.9,12.1c2.8,0,5.6-0.1,8.4-0.4h741.2c2.6,0.3,5.2,0.4,7.7,0.4
|
||||||
|
c14.6,0,29.2-4.2,42-12.2c12.4-7.7,23.1-18.8,30.7-32.1c17-29.4,17-64.5,0-93.8L593.4,149.7C576.4,120.4,546,102.8,512.1,102.8
|
||||||
|
L512.1,102.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,101.8c-34.3,0-65,17.7-82.1,47.4L58.6,792.4c-17.1,29.7-17.1,65.1,0,94.8
|
||||||
|
c7.8,13.6,18.7,24.8,31.5,32.6c13.3,8,28.6,12.2,44.4,12.2c2.8,0,5.6-0.1,8.4-0.4H884c2.6,0.3,5.2,0.4,7.8,0.4
|
||||||
|
c14.8,0,29.5-4.3,42.5-12.3c12.6-7.8,23.3-19,31.1-32.5c17.1-29.7,17.1-65.1,0-94.8L594.2,149.2
|
||||||
|
C577.1,119.5,546.4,101.8,512.1,101.8L512.1,101.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,100.8c-34.6,0-65.7,17.9-83,47.9L57.8,791.9c-17.3,30-17.3,65.8,0,95.8
|
||||||
|
c7.9,13.7,18.9,25.1,31.9,32.9c13.4,8.1,28.9,12.4,44.9,12.4c2.8,0,5.6-0.1,8.5-0.4h741c2.6,0.3,5.2,0.4,7.8,0.4
|
||||||
|
c15,0,29.9-4.3,43.1-12.5c12.7-7.9,23.6-19.2,31.4-32.8c17.3-30,17.3-65.8,0-95.8L595.1,148.7
|
||||||
|
C577.8,118.7,546.7,100.8,512.1,100.8L512.1,100.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="1.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,99.8c-35,0-66.4,18.1-83.9,48.4L56.9,791.4c-17.5,30.3-17.5,66.5,0,96.8
|
||||||
|
c8,13.9,19.2,25.4,32.2,33.3c13.6,8.2,29.3,12.5,45.4,12.5c2.8,0,5.7-0.1,8.5-0.4h741c2.6,0.3,5.3,0.4,7.9,0.4
|
||||||
|
c15.2,0,30.3-4.4,43.6-12.6c12.9-8,23.8-19.4,31.8-33.2c17.5-30.3,17.5-66.5,0-96.8L596,148.2C578.4,117.9,547.1,99.8,512.1,99.8
|
||||||
|
L512.1,99.8z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="1.000000e-02">
|
||||||
|
<path d="M512.1,116.8c28.9,0,54.7,14.9,69.1,39.9l371.3,643.1c14.4,25,14.4,54.8,0,79.8c-13.2,22.9-35.9,37.3-60.6,37.3
|
||||||
|
c-2.3,0-4.7-0.1-7-0.4H142.3c-2.6,0.3-5.2,0.4-7.7,0.4c-27,0-49.7-14.4-62.9-37.3c-14.4-25-14.4-54.8,0-79.8L443,156.7
|
||||||
|
C457.4,131.7,483.2,116.8,512.1,116.8 M512.1,98.8c-35.4,0-67,18.3-84.7,48.9L56,790.9c-17.7,30.6-17.7,67.2,0,97.8
|
||||||
|
c8.1,14,19.4,25.6,32.6,33.6c13.7,8.3,29.6,12.7,46,12.7c2.8,0,5.7-0.1,8.6-0.4H884c2.6,0.3,5.3,0.4,7.9,0.4
|
||||||
|
c15.4,0,30.6-4.4,44.1-12.8c13-8.1,24.1-19.7,32.1-33.5c17.7-30.6,17.7-67.2,0-97.8L596.8,147.7
|
||||||
|
C579.1,117.1,547.5,98.8,512.1,98.8L512.1,98.8z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Triangle">
|
||||||
|
|
||||||
|
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="512.1" y1="886.825" x2="512.1" y2="144.3" gradientTransform="matrix(1 0 0 -1 0 1024)">
|
||||||
|
<stop offset="0" style="stop-color:#FAE462"/>
|
||||||
|
<stop offset="0.9989" style="stop-color:#DBAC3A"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path fill="url(#SVGID_1_)" d="M469.1,162L97.8,805.2c-19.1,33.1,4.8,74.5,43,74.5h742.6c38.2,0,62.1-41.4,43-74.5L555.1,162
|
||||||
|
C536,128.9,488.2,128.9,469.1,162z"/>
|
||||||
|
|
||||||
|
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="512" y1="917" x2="512" y2="114.2" gradientTransform="matrix(1 0 0 -1 0 1024)">
|
||||||
|
<stop offset="0" style="stop-color:#FCFBF5"/>
|
||||||
|
<stop offset="1" style="stop-color:#E3E1DB"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path fill="url(#SVGID_2_)" d="M512.1,137.2c16.7,0,33.4,8.3,43,24.8l371.3,643.1c19.1,33.1-4.8,74.5-43,74.5H140.8
|
||||||
|
c-38.2,0-62.1-41.4-43-74.5L469.1,162C478.7,145.5,495.4,137.2,512.1,137.2 M512.1,107c-28.9,0-54.7,14.9-69.1,39.9L71.6,790.1
|
||||||
|
c-14.4,25-14.4,54.8,0,79.8s40.3,39.9,69.1,39.9h742.6c28.9,0,54.7-14.9,69.1-39.9c14.4-25,14.4-54.8,0-79.8L581.2,146.9
|
||||||
|
C566.8,121.9,541,107,512.1,107L512.1,107z"/>
|
||||||
|
</g>
|
||||||
|
<g id="Point_Shadow" opacity="0.22">
|
||||||
|
<path fill="#323232" d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5z"/>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,713.5c-12.7,0-23.5,4.5-32.3,13.5c-8.8,8.9-13.3,19.9-13.3,32.5
|
||||||
|
c0,14.3,5,25.7,14.8,33.8c9.7,8.1,20.1,12.2,30.9,12.2c8.8,0,16.7-2.1,23.4-6.2c6.7-4.1,12.2-9.6,16.4-16.3
|
||||||
|
c4.1-6.8,6.2-14.6,6.2-23.4c0-8.8-2-16.7-6-23.4s-9.5-12.2-16.4-16.4C528.5,715.6,520.5,713.5,511.8,713.5L511.8,713.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,712.5c-12.9,0-24.1,4.6-33.1,13.8c-9,9.1-13.6,20.3-13.6,33.2
|
||||||
|
c0,14.6,5.1,26.3,15.1,34.6c9.9,8.2,20.5,12.4,31.5,12.4c9,0,17-2.1,24-6.4c6.9-4.2,12.5-9.8,16.7-16.7s6.4-15,6.4-24
|
||||||
|
s-2.1-17-6.2-23.9c-4.1-6.9-9.7-12.5-16.7-16.7C528.9,714.6,520.7,712.5,511.8,712.5L511.8,712.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,711.5c-13.2,0-24.6,4.7-33.8,14.1c-9.2,9.3-13.8,20.7-13.8,33.9
|
||||||
|
c0,14.9,5.2,26.8,15.5,35.4c10.1,8.4,20.9,12.7,32.1,12.7c9.2,0,17.4-2.2,24.5-6.5c7-4.3,12.7-10,17-17c4.3-7.1,6.5-15.3,6.5-24.5
|
||||||
|
c0-9.1-2.1-17.4-6.3-24.4s-9.9-12.8-17.1-17.1C529.2,713.7,520.9,711.5,511.8,711.5L511.8,711.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,710.5c-13.5,0-25.1,4.8-34.5,14.4c-9.4,9.5-14.1,21.2-14.1,34.6
|
||||||
|
c0,15.2,5.3,27.4,15.8,36.1c10.3,8.6,21.3,12.9,32.8,12.9c9.4,0,17.8-2.2,25-6.7c7.1-4.4,13-10.2,17.3-17.3
|
||||||
|
c4.4-7.2,6.7-15.6,6.7-25c0-9.3-2.2-17.7-6.4-24.9c-4.3-7.2-10.1-13-17.4-17.4C529.6,712.7,521.1,710.5,511.8,710.5L511.8,710.5z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,709.5c-13.8,0-25.6,4.9-35.2,14.7c-9.6,9.7-14.4,21.6-14.4,35.3
|
||||||
|
c0,15.6,5.5,28,16.2,36.9c10.5,8.7,21.7,13.1,33.4,13.1c9.5,0,18.1-2.3,25.5-6.8c7.3-4.5,13.2-10.4,17.7-17.7
|
||||||
|
c4.5-7.4,6.8-16,6.8-25.5s-2.2-18.1-6.6-25.5c-4.3-7.3-10.3-13.3-17.8-17.8C529.9,711.8,521.3,709.5,511.8,709.5L511.8,709.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,708.5c-14,0-26.1,5-35.9,15c-9.8,9.9-14.7,22-14.7,36c0,15.9,5.6,28.5,16.6,37.7
|
||||||
|
c10.7,8.9,22.1,13.3,34,13.3c9.7,0,18.5-2.3,26-7c7.4-4.5,13.5-10.6,18-18c4.6-7.5,7-16.3,7-26s-2.3-18.4-6.7-26
|
||||||
|
c-4.4-7.5-10.5-13.6-18.1-18.1C530.3,710.8,521.5,708.5,511.8,708.5L511.8,708.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,707.5c-14.3,0-26.6,5.1-36.6,15.3c-9.9,10.1-15,22.5-15,36.7
|
||||||
|
c0,16.2,5.7,29.1,16.9,38.4c10.8,9,22.5,13.6,34.7,13.6c9.9,0,18.9-2.4,26.6-7.1c7.6-4.6,13.7-10.8,18.3-18.3
|
||||||
|
c4.7-7.7,7.1-16.6,7.1-26.6c0-9.9-2.3-18.8-6.9-26.5c-4.5-7.6-10.7-13.8-18.4-18.5C530.6,709.9,521.6,707.5,511.8,707.5
|
||||||
|
L511.8,707.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,706.5c-14.6,0-27.2,5.2-37.3,15.6c-10.1,10.3-15.3,22.9-15.3,37.4
|
||||||
|
c0,16.5,5.8,29.7,17.3,39.2c11,9.2,22.9,13.8,35.3,13.8c10.1,0,19.2-2.4,27.1-7.3c7.7-4.7,14-11,18.7-18.7
|
||||||
|
c4.8-7.9,7.3-17,7.3-27.1s-2.4-19.1-7-27c-4.6-7.8-10.9-14.1-18.8-18.8C531,708.9,521.8,706.5,511.8,706.5L511.8,706.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,705.5c-14.9,0-27.7,5.3-38,15.9c-10.3,10.5-15.6,23.3-15.6,38.1
|
||||||
|
c0,16.8,5.9,30.3,17.6,40c11.2,9.3,23.3,14,36,14c10.3,0,19.6-2.5,27.6-7.4c7.8-4.8,14.2-11.2,19-19c4.9-8,7.4-17.3,7.4-27.6
|
||||||
|
c0-10.2-2.4-19.5-7.1-27.5c-4.7-7.9-11.1-14.3-19.1-19.2C531.3,708,522,705.5,511.8,705.5L511.8,705.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,703.5c-15.4,0-28.7,5.5-39.5,16.5c-10.7,10.9-16.1,24.2-16.1,39.6
|
||||||
|
c0,17.4,6.2,31.4,18.4,41.5c11.6,9.6,24.1,14.5,37.2,14.5c10.7,0,20.3-2.6,28.7-7.7c8.1-5,14.7-11.6,19.7-19.7
|
||||||
|
c5.1-8.3,7.7-18,7.7-28.7c0-10.6-2.5-20.2-7.4-28.5c-4.9-8.2-11.5-14.9-19.8-19.9C532.1,706.1,522.4,703.5,511.8,703.5
|
||||||
|
L511.8,703.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,702.5c-15.7,0-29.2,5.6-40.2,16.8c-10.9,11.1-16.4,24.6-16.4,40.3
|
||||||
|
c0,17.7,6.3,32,18.7,42.3c11.8,9.8,24.5,14.7,37.9,14.7c10.8,0,20.7-2.6,29.2-7.8c8.2-5,15-11.8,20-20c5.2-8.5,7.8-18.3,7.8-29.2
|
||||||
|
c0-10.8-2.5-20.5-7.5-29c-4.9-8.3-11.7-15.1-20.2-20.2C532.4,705.1,522.6,702.5,511.8,702.5L511.8,702.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,701.5c-16,0-29.7,5.7-40.9,17.1c-11.1,11.3-16.7,25-16.7,41c0,18,6.4,32.5,19.1,43.1
|
||||||
|
c11.9,9.9,24.9,15,38.5,15c11,0,21-2.7,29.7-8c8.4-5.1,15.2-12,20.3-20.3c5.3-8.7,8-18.7,8-29.7s-2.6-20.9-7.7-29.5
|
||||||
|
c-5-8.5-11.9-15.4-20.5-20.6C532.8,704.2,522.7,701.5,511.8,701.5L511.8,701.5z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,700.5c-16.3,0-30.3,5.8-41.6,17.4c-11.3,11.5-17,25.5-17,41.7
|
||||||
|
c0,18.4,6.5,33.1,19.5,43.8c12.1,10.1,25.3,15.2,39.2,15.2c11.2,0,21.4-2.7,30.2-8.1c8.5-5.2,15.5-12.2,20.7-20.7
|
||||||
|
c5.4-8.8,8.1-19,8.1-30.2c0-11.1-2.6-21.2-7.8-30c-5.1-8.6-12.1-15.7-20.9-20.9C533.1,703.2,522.9,700.5,511.8,700.5L511.8,700.5z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M511.8,714.5c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9c0,8.7-2,16.3-6.1,22.9
|
||||||
|
c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1c0-12.4,4.3-23,13-31.8
|
||||||
|
C488.8,718.9,499.3,714.5,511.8,714.5 M511.8,699.5c-16.5,0-30.8,5.9-42.3,17.7c-11.5,11.6-17.3,25.9-17.3,42.4
|
||||||
|
c0,18.4,6.9,33.8,19.8,44.6c12.3,10.2,25.7,15.4,39.8,15.4c11.4,0,21.8-2.8,30.7-8.3c8.6-5.3,15.7-12.4,21-21
|
||||||
|
c5.5-9,8.3-19.3,8.3-30.7c0-11.3-2.7-21.6-8-30.5c-5.2-8.8-12.3-15.9-21.2-21.2C533.5,702.3,523.1,699.5,511.8,699.5L511.8,699.5z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<radialGradient id="Point_1_" cx="512" cy="272.1" r="44.9001" gradientTransform="matrix(1 0 0 -1 0 1024)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" style="stop-color:#F8F8F8"/>
|
||||||
|
<stop offset="1" style="stop-color:#E9E9E8"/>
|
||||||
|
</radialGradient>
|
||||||
|
<path id="Point" fill="url(#Point_1_)" d="M511.8,706.9c8.6,0,16.4,2,23.1,6.1c6.8,4.1,12.1,9.4,16,16s5.9,14.3,5.9,22.9
|
||||||
|
c0,8.7-2,16.3-6.1,22.9c-4.1,6.6-9.4,12-16,16c-6.6,4.1-14.3,6.1-22.9,6.1c-10.5,0-20.6-4-30.2-12s-14.4-19-14.4-33.1
|
||||||
|
c0-12.4,4.3-23,13-31.8C488.8,711.3,499.3,706.9,511.8,706.9z"/>
|
||||||
|
<g id="Exclamation_Shadow" opacity="0.22">
|
||||||
|
<path fill="#323232" d="M520.3,667.4h-15.8l-22.7-171.5c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9
|
||||||
|
c8.2-10,19.4-15,33.5-15c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3L520.3,667.4z"/>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,325c-14.3,0-25.8,5.2-34.2,15.4
|
||||||
|
c-8.4,10.1-12.6,23.5-12.6,39.6c0,10.8,4.6,49.9,13.8,116.1l22.7,171.5l0.1,0.9h0.9h15.8h0.9l0.1-0.9l24.3-193
|
||||||
|
c7.1-56,10.6-87.2,10.6-95.4c0-14.7-3.6-27.5-10.7-38.1C538.2,330.4,527.6,325,513.8,325L513.8,325z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.14">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,324c-14.6,0-26.4,5.3-35,15.7
|
||||||
|
c-8.5,10.3-12.8,23.8-12.8,40.2c0,10.9,4.6,50,13.8,116.3l22.7,171.5l0.2,1.7h1.8h15.8h1.8l0.2-1.7l24.3-193
|
||||||
|
c7.1-56,10.6-87.3,10.6-95.6c0-14.9-3.7-27.9-10.9-38.6C538.9,329.5,527.9,324,513.8,324L513.8,324z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,323c-14.9,0-27,5.4-35.8,16.1
|
||||||
|
c-8.7,10.5-13.1,24.2-13.1,40.8c0,11.1,4.5,49.1,13.8,116.4l22.7,171.5l0.3,2.6h2.6h15.8h2.6l0.3-2.6l24.3-193
|
||||||
|
c7.1-56.1,10.6-87.4,10.6-95.7c0-15.1-3.7-28.3-11.1-39.2C539.5,328.7,528.3,323,513.8,323L513.8,323z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.12">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,322c-15.2,0-27.5,5.5-36.5,16.5
|
||||||
|
c-8.8,10.7-13.3,24.6-13.3,41.5c0,11.1,4.5,49.2,13.8,116.5L500.5,668l0.5,3.5h3.5h15.8h3.5l0.4-3.5l24.3-193
|
||||||
|
c7.1-56.1,10.6-87.5,10.6-95.8c0-15.3-3.8-28.7-11.2-39.8C540.1,327.8,528.6,322,513.8,322L513.8,322z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,321c-15.6,0-28.1,5.7-37.3,16.8
|
||||||
|
c-9,10.9-13.5,25-13.5,42.1c0,11.2,4.5,49.3,13.8,116.7l22.7,171.5l0.6,4.3h4.4h15.8h4.4l0.6-4.4l24.3-193
|
||||||
|
c7.1-56.2,10.6-87.5,10.6-95.9c0-15.5-3.8-29.1-11.4-40.3C540.8,327,529,321,513.8,321L513.8,321z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,320c-15.9,0-28.7,5.8-38.1,17.2
|
||||||
|
c-9.1,11.1-13.7,25.4-13.7,42.7c0,11.2,4.5,49.4,13.8,116.8l22.7,171.5l0.7,5.2h5.3h15.8h5.3l0.7-5.2l24.3-193
|
||||||
|
c7.1-56.2,10.6-87.6,10.6-96.1c0-15.7-3.9-29.5-11.6-40.9C541.4,326.1,529.3,320,513.8,320L513.8,320z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,319c-16.2,0-29.3,5.9-38.9,17.6
|
||||||
|
c-9.3,11.2-14,25.8-14,43.4c0,11.3,4.5,49.5,13.9,116.9l22.7,171.5l0.8,6.1h6.1h15.8h6.2l0.8-6.1l24.3-193
|
||||||
|
c7.1-56.2,10.6-87.7,10.6-96.2c0-15.9-4-29.9-11.7-41.4C542,325.3,529.7,319,513.8,319L513.8,319z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="8.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,318c-16.5,0-29.8,6-39.6,17.9
|
||||||
|
c-9.4,11.4-14.2,26.2-14.2,44c0,11.3,4.5,49.6,13.9,117.1l22.7,171.5l0.9,7h7h15.8h7.1l0.9-7l24.3-193
|
||||||
|
c7.1-56.3,10.6-87.8,10.6-96.3c0-16.2-4-30.3-11.9-42C542.6,324.4,530,318,513.8,318L513.8,318z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,317c-16.8,0-30.4,6.2-40.4,18.3
|
||||||
|
c-9.6,11.6-14.4,26.6-14.4,44.7c0,11.3,4.5,49.7,13.9,117.2l22.7,171.5l1,7.8h7.9h15.8h7.9l1-7.9l24.3-193
|
||||||
|
c7.1-56.3,10.6-87.9,10.6-96.4c0-16.4-4.1-30.7-12.1-42.5C543.4,323.7,530.2,317,513.8,317L513.8,317z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="6.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,315c-17.4,0-31.5,6.4-41.9,19
|
||||||
|
c-9.9,12-14.9,27.4-14.9,45.9c0,11.4,4.5,49.9,13.9,117.5l22.7,171.5l1.3,9.6h9.6h15.8h9.7l1.2-9.6l24.3-193
|
||||||
|
c9.5-75.1,10.6-91.2,10.6-96.7c0-16.8-4.2-31.5-12.4-43.7C544.7,322,530.9,315,513.8,315L513.8,315z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,314c-17.7,0-32.1,6.5-42.7,19.4
|
||||||
|
c-10,12.2-15.1,27.8-15.1,46.6c0,11.5,4.5,50,13.9,117.6L492.6,669l1.4,10.4h10.5h15.8h10.6l1.3-10.5l24.3-193
|
||||||
|
c9.5-75.2,10.6-91.3,10.6-96.8c0-17-4.2-31.8-12.6-44.2C545.3,321.2,531.2,314,513.8,314L513.8,314z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="4.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,313c-18.1,0-32.7,6.6-43.5,19.7
|
||||||
|
C460.1,345,455,360.9,455,379.9c0,11.5,4.5,50,13.9,117.7l22.7,171.5l1.5,11.3h11.4h15.8h11.5l1.4-11.4l24.3-193
|
||||||
|
c9.5-75.3,10.6-91.4,10.6-96.9c0-17.2-4.3-32.2-12.8-44.8C545.9,320.4,531.6,313,513.8,313L513.8,313z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,312c-18.1,0-33.4,7-44.3,20.1
|
||||||
|
c-10.3,12.5-15.6,28.6-15.6,47.8c0,7.4,1.4,27.6,13.9,117.9l22.7,171.5l1.6,12.2h12.3h15.8h12.3l1.5-12.2l24.3-193
|
||||||
|
c9.5-75.3,10.7-91.5,10.7-97.1c0-17.4-4.4-32.6-12.9-45.3C546.6,319.5,531.9,312,513.8,312L513.8,312z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="2.000000e-02">
|
||||||
|
<path d="M513.8,326c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3l-24.3,193h-15.8l-22.7-171.5
|
||||||
|
c-9.2-66.5-13.8-105.2-13.8-116c0-15.9,4.1-28.9,12.4-38.9C488.6,331,499.7,326,513.8,326 M513.8,311c-18.4,0-34,7.1-45,20.5
|
||||||
|
c-10.5,12.7-15.8,29-15.8,48.5c0,7.4,1.4,27.7,13.9,118l22.7,171.5l1.7,13h13.1h15.8h13.2l1.7-13.1l24.3-193
|
||||||
|
c9.6-75.4,10.7-91.6,10.7-97.2c0-17.6-4.4-33-13.1-45.9C547.2,318.7,532.2,311,513.8,311L513.8,311z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<radialGradient id="Exclamation_1_" cx="511.6" cy="534.8233" r="124.5798" gradientTransform="matrix(1 0 0 -2.5 0 1826.2583)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" style="stop-color:#F8F8F8"/>
|
||||||
|
<stop offset="1" style="stop-color:#E9E9E8"/>
|
||||||
|
</radialGradient>
|
||||||
|
<path id="Exclamation" fill="url(#Exclamation_1_)" d="M520.3,659.9h-15.8l-22.7-171.5c-9.2-66.5-13.8-105.2-13.8-116
|
||||||
|
c0-15.9,4.1-28.9,12.4-38.9c8.2-10,19.4-15,33.5-15c13.5,0,23.8,5.2,30.8,15.6s10.5,22.9,10.5,37.5c0,8.1-3.5,39.9-10.5,95.3
|
||||||
|
L520.3,659.9z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 30 KiB |
100
src/common/scripts/button.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { info, error } from './logger.js'
|
||||||
|
import { getResource, getExtensionURL } from './common.js'
|
||||||
|
import { togglePictureInPicture, addPictureInPictureEventListener } from './video.js'
|
||||||
|
import { localizedString } from './localization.js'
|
||||||
|
|
||||||
|
const BUTTON_ID = 'PiPer_button';
|
||||||
|
|
||||||
|
let /** ?HTMLElement */ button = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects Picture in Picture button into webpage
|
||||||
|
*
|
||||||
|
* @param {Element} parent - Element button will be inserted into
|
||||||
|
*/
|
||||||
|
export const addButton = function(parent) {
|
||||||
|
|
||||||
|
// Create button if needed
|
||||||
|
if (!button) {
|
||||||
|
const buttonElementType = getResource().buttonElementType || 'button';
|
||||||
|
button = /** @type {HTMLElement} */ (document.createElement(buttonElementType));
|
||||||
|
|
||||||
|
// Set button properties
|
||||||
|
button.id = BUTTON_ID;
|
||||||
|
button.title = localizedString('button-title');
|
||||||
|
const buttonStyle = getResource().buttonStyle;
|
||||||
|
if (buttonStyle) button.style.cssText = buttonStyle;
|
||||||
|
const buttonClassName = getResource().buttonClassName;
|
||||||
|
if (buttonClassName) button.className = buttonClassName;
|
||||||
|
|
||||||
|
// Add scaled image to button
|
||||||
|
const image = /** @type {HTMLImageElement} */ (document.createElement('img'));
|
||||||
|
image.style.width = image.style.height = '100%';
|
||||||
|
const buttonScale = getResource().buttonScale;
|
||||||
|
if (buttonScale) image.style.transform = `scale(${buttonScale})`;
|
||||||
|
button.appendChild(image);
|
||||||
|
|
||||||
|
// Set image paths
|
||||||
|
let buttonImage = getResource().buttonImage;
|
||||||
|
let buttonExitImage = getResource().buttonExitImage;
|
||||||
|
if (!buttonImage) {
|
||||||
|
buttonImage = 'default';
|
||||||
|
buttonExitImage = 'default-exit';
|
||||||
|
}
|
||||||
|
const buttonImageURL = getExtensionURL(`images/${buttonImage}.svg`);
|
||||||
|
image.src = buttonImageURL;
|
||||||
|
if (buttonExitImage) {
|
||||||
|
const buttonExitImageURL = getExtensionURL(`images/${buttonExitImage}.svg`);
|
||||||
|
addPictureInPictureEventListener(function(video, isPlayingPictureInPicture) {
|
||||||
|
image.src = (isPlayingPictureInPicture) ? buttonExitImageURL : buttonImageURL;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add hover style to button (a nested stylesheet is used to avoid tracking another element)
|
||||||
|
const buttonHoverStyle = getResource().buttonHoverStyle;
|
||||||
|
if (buttonHoverStyle) {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
const css = `#${BUTTON_ID}:hover{${buttonHoverStyle}}`;
|
||||||
|
style.appendChild(document.createTextNode(css));
|
||||||
|
button.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle Picture in Picture mode when button is clicked
|
||||||
|
button.addEventListener('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Get the video element and bypass caching to accomodate for the underlying video changing (e.g. pre-roll adverts)
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement(true));
|
||||||
|
if (!video) {
|
||||||
|
error('Unable to find video');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePictureInPicture(video);
|
||||||
|
});
|
||||||
|
|
||||||
|
info('Picture in Picture button created');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject button into correct place
|
||||||
|
const referenceNode = getResource().buttonInsertBefore ? getResource().buttonInsertBefore(parent) : null;
|
||||||
|
parent.insertBefore(button, referenceNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Picture in Picture button element
|
||||||
|
*
|
||||||
|
* @return {?HTMLElement}
|
||||||
|
*/
|
||||||
|
export const getButton = function() {
|
||||||
|
return button;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if Picture in Picture button is injected into page
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const checkButton = function() {
|
||||||
|
return !!document.getElementById(BUTTON_ID);
|
||||||
|
};
|
||||||
49
src/common/scripts/cache.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { getResource } from './common.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises caching for button, video, and caption elements
|
||||||
|
*/
|
||||||
|
export const initialiseCaches = function() {
|
||||||
|
|
||||||
|
// Return a unique id
|
||||||
|
let uniqueIdCounter = 0;
|
||||||
|
const /** function():string */ uniqueId = function() {
|
||||||
|
return 'PiPer_' + uniqueIdCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a function that returns an element to provide faster lookups by id
|
||||||
|
*
|
||||||
|
* @param {function(boolean=):?Element} elementFunction
|
||||||
|
* @return {function(boolean=):?Element}
|
||||||
|
*/
|
||||||
|
const cacheElementWrapper = function(elementFunction) {
|
||||||
|
let /** ?string */ cachedElementId = null;
|
||||||
|
|
||||||
|
return /** function():?Element */ function(/** boolean= */ bypassCache) {
|
||||||
|
|
||||||
|
// Return element by id if possible
|
||||||
|
const cachedElement = cachedElementId ?
|
||||||
|
document.getElementById(cachedElementId) : null;
|
||||||
|
if (cachedElement && !bypassCache) return cachedElement;
|
||||||
|
|
||||||
|
// Call the underlying function to get the element
|
||||||
|
const uncachedElement = elementFunction();
|
||||||
|
if (uncachedElement) {
|
||||||
|
|
||||||
|
// Save the native id otherwise assign a unique id
|
||||||
|
if (!uncachedElement.id) uncachedElement.id = uniqueId();
|
||||||
|
cachedElementId = uncachedElement.id;
|
||||||
|
}
|
||||||
|
return uncachedElement;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap the button, video, and caption elements
|
||||||
|
const currentResource = getResource();
|
||||||
|
currentResource.buttonParent = cacheElementWrapper(currentResource.buttonParent);
|
||||||
|
currentResource.videoElement = cacheElementWrapper(currentResource.videoElement);
|
||||||
|
if (currentResource.captionElement) {
|
||||||
|
currentResource.captionElement = cacheElementWrapper(currentResource.captionElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
185
src/common/scripts/captions.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { info } from './logger.js'
|
||||||
|
import { Browser, getBrowser, getResource } from './common.js'
|
||||||
|
import { videoPlayingPictureInPicture, addPictureInPictureEventListener, removePictureInPictureEventListener } from './video.js'
|
||||||
|
|
||||||
|
const TRACK_ID = 'PiPer_track';
|
||||||
|
|
||||||
|
let /** ?TextTrack */ track = null;
|
||||||
|
let /** boolean */ captionsEnabled = false;
|
||||||
|
let /** boolean */ showingCaptions = false;
|
||||||
|
let /** boolean */ showingEmptyCaption = false;
|
||||||
|
let /** string */ lastUnprocessedCaption = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable closed caption support in Picture in Picture mode
|
||||||
|
*/
|
||||||
|
export const disableCaptions = function() {
|
||||||
|
captionsEnabled = false;
|
||||||
|
showingCaptions = false;
|
||||||
|
processCaptions();
|
||||||
|
removePictureInPictureEventListener(pictureInPictureEventListener);
|
||||||
|
|
||||||
|
info('Closed caption support disabled');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable closed caption support in Picture in Picture mode
|
||||||
|
*
|
||||||
|
* @param {boolean=} ignoreNowPlayingCheck - assumes video isn't already playing Picture in Picture
|
||||||
|
*/
|
||||||
|
export const enableCaptions = function(ignoreNowPlayingCheck) {
|
||||||
|
|
||||||
|
if (!getResource().captionElement) return;
|
||||||
|
|
||||||
|
captionsEnabled = true;
|
||||||
|
addPictureInPictureEventListener(pictureInPictureEventListener);
|
||||||
|
|
||||||
|
info('Closed caption support enabled');
|
||||||
|
|
||||||
|
if (ignoreNowPlayingCheck) return;
|
||||||
|
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement(true));
|
||||||
|
if (!video) return;
|
||||||
|
showingCaptions = videoPlayingPictureInPicture(video);
|
||||||
|
prepareCaptions(video);
|
||||||
|
processCaptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether processing closed captions is required
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const shouldProcessCaptions = function() {
|
||||||
|
return captionsEnabled && showingCaptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares video for captions
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - video element that will display captions
|
||||||
|
*/
|
||||||
|
const prepareCaptions = function(video) {
|
||||||
|
|
||||||
|
// Find existing caption track
|
||||||
|
track = null;
|
||||||
|
const allTracks = video.textTracks;
|
||||||
|
for (let trackId = allTracks.length; trackId--;) {
|
||||||
|
if (allTracks[trackId].label === TRACK_ID) {
|
||||||
|
track = allTracks[trackId];
|
||||||
|
info('Existing caption track found');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (track) return;
|
||||||
|
|
||||||
|
// Otherwise create new caption track
|
||||||
|
info('Caption track created');
|
||||||
|
track = video.addTextTrack('captions', TRACK_ID, 'en');
|
||||||
|
track.mode = 'showing';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles captions when video enters or exits Picture in Picture mode
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - target video element
|
||||||
|
* @param {boolean} isPlayingPictureInPicture - true if video playing Picture in Picture
|
||||||
|
*/
|
||||||
|
const pictureInPictureEventListener = function(video, isPlayingPictureInPicture) {
|
||||||
|
|
||||||
|
// Toggle display of the captions and prepare video if needed
|
||||||
|
showingCaptions = isPlayingPictureInPicture;
|
||||||
|
if (showingCaptions) prepareCaptions(video);
|
||||||
|
lastUnprocessedCaption = '';
|
||||||
|
processCaptions();
|
||||||
|
|
||||||
|
info(`Video presentation mode changed (showingCaptions: ${showingCaptions})`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes visible Picture in Picture mode captions
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - video element showing captions
|
||||||
|
* @param {boolean=} workaround - apply Safari bug workaround
|
||||||
|
*/
|
||||||
|
const removeCaptions = function(video, workaround = true) {
|
||||||
|
|
||||||
|
while (track.activeCues.length) {
|
||||||
|
track.removeCue(track.activeCues[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround Safari bug; 'removeCue' doesn't immediately remove captions shown in Picture in Picture mode
|
||||||
|
if (getBrowser() == Browser.SAFARI && workaround && video && !showingEmptyCaption) {
|
||||||
|
track.addCue(new VTTCue(video.currentTime, video.currentTime + 60, ''));
|
||||||
|
showingEmptyCaption = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays Picture in Picture mode caption
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - video element showing captions
|
||||||
|
* @param {string} caption - a caption to display
|
||||||
|
*/
|
||||||
|
const addCaption = function(video, caption) {
|
||||||
|
|
||||||
|
info(`Showing caption '${caption}'`);
|
||||||
|
track.addCue(new VTTCue(video.currentTime, video.currentTime + 60, caption));
|
||||||
|
|
||||||
|
if (getBrowser() == Browser.SAFARI) {
|
||||||
|
showingEmptyCaption = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates visible captions
|
||||||
|
*/
|
||||||
|
export const processCaptions = function() {
|
||||||
|
|
||||||
|
// Get handles to caption and video elements
|
||||||
|
const captionElement = getResource().captionElement();
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
|
||||||
|
// Remove Picture in Picture mode captions and show native captions if no longer showing captions or encountered an error
|
||||||
|
if (!showingCaptions || !captionElement) {
|
||||||
|
removeCaptions(video);
|
||||||
|
if (captionElement) captionElement.style.visibility = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise ensure native captions remain hidden
|
||||||
|
captionElement.style.visibility = 'hidden';
|
||||||
|
|
||||||
|
// Check if a new native caption needs to be processed
|
||||||
|
const unprocessedCaption = captionElement.textContent;
|
||||||
|
if (unprocessedCaption == lastUnprocessedCaption) return;
|
||||||
|
lastUnprocessedCaption = unprocessedCaption;
|
||||||
|
|
||||||
|
// Remove old captions and apply Safari bug fix if caption has no content as otherwise causes flicker
|
||||||
|
removeCaptions(video, !unprocessedCaption);
|
||||||
|
|
||||||
|
// Performance optimisation - early exit if caption has no content
|
||||||
|
if (!unprocessedCaption) return;
|
||||||
|
|
||||||
|
// Show correctly spaced and formatted Picture in Picture mode caption
|
||||||
|
let caption = '';
|
||||||
|
const walk = document.createTreeWalker(captionElement, NodeFilter.SHOW_TEXT, null, false);
|
||||||
|
while (walk.nextNode()) {
|
||||||
|
const segment = walk.currentNode.nodeValue.trim();
|
||||||
|
if (segment) {
|
||||||
|
const style = window.getComputedStyle(walk.currentNode.parentElement);
|
||||||
|
if (style.fontStyle == 'italic') {
|
||||||
|
caption += `<i>${segment}</i>`;
|
||||||
|
} else if (style.textDecoration == 'underline') {
|
||||||
|
caption += `<u>${segment}</u>`;
|
||||||
|
} else {
|
||||||
|
caption += segment;
|
||||||
|
}
|
||||||
|
caption += ' ';
|
||||||
|
} else if (caption.charAt(caption.length - 1) != '\n') {
|
||||||
|
caption += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
caption = caption.trim();
|
||||||
|
addCaption(video, caption);
|
||||||
|
};
|
||||||
104
src/common/scripts/common.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { BROWSER } from './defines.js'
|
||||||
|
import { warn } from './logger.js'
|
||||||
|
|
||||||
|
/** @enum {number} - Enum for browser */
|
||||||
|
export const Browser = {
|
||||||
|
UNKNOWN: 0,
|
||||||
|
SAFARI: 1,
|
||||||
|
CHROME: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current web browser
|
||||||
|
*
|
||||||
|
* @return {Browser}
|
||||||
|
*/
|
||||||
|
export const getBrowser = function() {
|
||||||
|
if (BROWSER != Browser.UNKNOWN) {
|
||||||
|
return /** @type {Browser} */ (BROWSER);
|
||||||
|
}
|
||||||
|
if (/Safari/.test(navigator.userAgent) && /Apple/.test(navigator.vendor)) {
|
||||||
|
return Browser.SAFARI;
|
||||||
|
}
|
||||||
|
if (/Chrome/.test(navigator.userAgent) && /Google/.test(navigator.vendor)) {
|
||||||
|
return Browser.CHROME;
|
||||||
|
}
|
||||||
|
return Browser.UNKNOWN;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* buttonClassName: (string|undefined),
|
||||||
|
* buttonDidAppear: (function():undefined|undefined),
|
||||||
|
* buttonElementType: (string|undefined),
|
||||||
|
* buttonExitImage: (string|undefined),
|
||||||
|
* buttonHoverStyle: (string|undefined),
|
||||||
|
* buttonImage: (string|undefined),
|
||||||
|
* buttonInsertBefore: (function(Element):?Node|undefined),
|
||||||
|
* buttonParent: function(boolean=):?Element,
|
||||||
|
* buttonScale: (number|undefined),
|
||||||
|
* buttonStyle: (string|undefined),
|
||||||
|
* captionElement: (function(boolean=):?Element|undefined),
|
||||||
|
* videoElement: function(boolean=):?Element,
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
let PiperResource;
|
||||||
|
|
||||||
|
let /** ?PiperResource */ currentResource = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current resource
|
||||||
|
*
|
||||||
|
* @return {?PiperResource}
|
||||||
|
*/
|
||||||
|
export const getResource = function() {
|
||||||
|
return currentResource;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current resource
|
||||||
|
*
|
||||||
|
* @param {?PiperResource} resource - a resource to set as current resource
|
||||||
|
*/
|
||||||
|
export const setResource = function(resource) {
|
||||||
|
currentResource = resource;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a relative path within an extension to a fully-qualified URL
|
||||||
|
*
|
||||||
|
* @param {string} path - a path to a resource
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const getExtensionURL = function(path) {
|
||||||
|
switch (getBrowser()) {
|
||||||
|
case Browser.SAFARI:
|
||||||
|
return safari.extension.baseURI + path;
|
||||||
|
case Browser.CHROME:
|
||||||
|
return chrome.runtime.getURL(path);
|
||||||
|
case Browser.UNKNOWN:
|
||||||
|
default:
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies fix to bypass background DOM timer throttling
|
||||||
|
*/
|
||||||
|
export const bypassBackgroundTimerThrottling = function() {
|
||||||
|
|
||||||
|
// Issue warning for unnecessary use of background timer throttling
|
||||||
|
if (!currentResource.captionElement) {
|
||||||
|
warn('Unnecessary bypassing of background timer throttling on page without caption support');
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = new XMLHttpRequest();
|
||||||
|
request.open('GET', getExtensionURL('scripts/fix.js'));
|
||||||
|
request.onload = function() {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.setAttribute('type', 'module');
|
||||||
|
script.appendChild(document.createTextNode(request.responseText));
|
||||||
|
document.head.appendChild(script);
|
||||||
|
};
|
||||||
|
request.send();
|
||||||
|
};
|
||||||
5
src/common/scripts/defines.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/** @define {number} - Flag used by closure compiler to set logging level */
|
||||||
|
export const LOGGING_LEVEL = 0;
|
||||||
|
|
||||||
|
/** @define {number} - Flag used by closure compiler to target specific browser */
|
||||||
|
export const BROWSER = 0;
|
||||||
109
src/common/scripts/externs.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Externs file for PiPer used by Closure Compiler
|
||||||
|
* @externs
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Safari Extension */
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
const safari = {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
safari.extension = {};
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
safari.extension.baseURI;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
HTMLVideoElement.prototype.webkitPresentationMode;
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
HTMLVideoElement.prototype.webkitSetPresentationMode = function(mode) {};
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
TextTrack.prototype.label;
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
safari.extension.dispatchMessage = function(message, userInfo) {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
safari.self = {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
safari.self.addEventListener = function(type, listener) {};
|
||||||
|
|
||||||
|
/** @constructor */
|
||||||
|
const SafariExtensionMessageEvent = function() {};
|
||||||
|
|
||||||
|
/** @type {*} */
|
||||||
|
SafariExtensionMessageEvent.prototype.message;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
SafariExtensionMessageEvent.prototype.name;
|
||||||
|
|
||||||
|
|
||||||
|
/* Legacy Safari Extension */
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
safari.extension.settings = {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
safari.extension.settings.clear = function() {};
|
||||||
|
|
||||||
|
|
||||||
|
/* Chrome Extension */
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
const chrome = {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.runtime = {};
|
||||||
|
|
||||||
|
/** @return {string} */
|
||||||
|
chrome.runtime.getURL = function(path) {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.runtime.onInstalled = {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
chrome.runtime.onInstalled.addListener = function(callback) {};
|
||||||
|
|
||||||
|
/** @constructor */
|
||||||
|
chrome.runtime.Manifest = function() {};
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
chrome.runtime.Manifest.prototype.version;
|
||||||
|
|
||||||
|
/** @return {!chrome.runtime.Manifest} */
|
||||||
|
chrome.runtime.getManifest = function() {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.tabs = {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
chrome.tabs.create = function(properties) {};
|
||||||
|
|
||||||
|
/** @return {Promise<*>} */
|
||||||
|
HTMLVideoElement.prototype.requestPictureInPicture = function() {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.extension = {};
|
||||||
|
|
||||||
|
/** @return {string} */
|
||||||
|
chrome.extension.getURL = function(path) {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.storage = {};
|
||||||
|
|
||||||
|
/** @const */
|
||||||
|
chrome.storage.sync = {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
chrome.storage.sync.get = function(keys, callback) {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
chrome.storage.sync.set = function(items, callback) {};
|
||||||
|
|
||||||
|
/** @return {undefined} */
|
||||||
|
chrome.storage.sync.clear = function(callback) {};
|
||||||
152
src/common/scripts/fix.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { info } from './logger.js'
|
||||||
|
|
||||||
|
let activeVideo = null;
|
||||||
|
let timeoutId = 0;
|
||||||
|
let /** !Object<string, Array> */ timeouts = {};
|
||||||
|
|
||||||
|
const /** !Array<number> */ requests = [];
|
||||||
|
const /** !Array<function(number): undefined> */ callbacks = [];
|
||||||
|
|
||||||
|
const originalSetTimeout = window.setTimeout;
|
||||||
|
const originalClearTimeout = window.clearTimeout;
|
||||||
|
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks animation frame requests and forwards requests when page visible
|
||||||
|
*
|
||||||
|
* @param {function(number): undefined} callback - a requestAnimationFrame callback
|
||||||
|
*/
|
||||||
|
const trackAnimationFrameRequest = function(callback) {
|
||||||
|
let request = 0;
|
||||||
|
|
||||||
|
if (!activeVideo) {
|
||||||
|
request = originalRequestAnimationFrame(callback);
|
||||||
|
requests.push(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.push(callback);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
};
|
||||||
|
window.requestAnimationFrame = trackAnimationFrameRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears tracked animation frame requests on new frame
|
||||||
|
*/
|
||||||
|
const clearAnimationFrameRequests = function() {
|
||||||
|
requests.length = 0;
|
||||||
|
callbacks.length = 0;
|
||||||
|
|
||||||
|
originalRequestAnimationFrame(clearAnimationFrameRequests);
|
||||||
|
};
|
||||||
|
clearAnimationFrameRequests();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls tracked animation frame requests and timeouts
|
||||||
|
*/
|
||||||
|
const callAnimationFrameRequestsAndTimeouts = function() {
|
||||||
|
|
||||||
|
// Copy animation frame callbacks before calling to prevent endless looping
|
||||||
|
const callbacksCopy = callbacks.slice();
|
||||||
|
callbacks.length = 0;
|
||||||
|
|
||||||
|
// Call animation frame requests
|
||||||
|
const timestamp = window.performance.now();
|
||||||
|
for (let callback; callback = callbacksCopy.pop();) {
|
||||||
|
callback(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy timeouts to prevent endless looping
|
||||||
|
const timeoutsCopy = timeouts;
|
||||||
|
timeouts = {};
|
||||||
|
|
||||||
|
// Call elapsed timeouts
|
||||||
|
for (let id in timeoutsCopy) {
|
||||||
|
let timeout = timeoutsCopy[id];
|
||||||
|
if (timeout[0] <= timestamp) {
|
||||||
|
if (typeof timeout[1] == "function") {
|
||||||
|
timeout[1]();
|
||||||
|
} else {
|
||||||
|
eval(/** @type {string} */ (timeout[1]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timeouts[id] = timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avoids background throttling by invoking timeouts with media 'timeupdate' events
|
||||||
|
*
|
||||||
|
* @param {Function|string} callback - a setTimeout callback
|
||||||
|
* @param {number=} timeout - a delay in ms
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
const unthrottledSetTimeout = function(callback, timeout) {
|
||||||
|
const id = timeoutId++;
|
||||||
|
timeouts[id.toString()] = [window.performance.now() + (timeout || 0), callback];
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears queued timeouts to be invoked with media 'timeupdate' events
|
||||||
|
*
|
||||||
|
* @param {?number|undefined} id - an id returned by unthrottledSetTimeout
|
||||||
|
*/
|
||||||
|
const unthrottledClearTimeout = function(id) {
|
||||||
|
if (id) delete timeouts[id.toString()];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bypasses background timer throttling when video playing picture in picture
|
||||||
|
*/
|
||||||
|
const bypassBackgroundTimerThrottling = function() {
|
||||||
|
|
||||||
|
if (document.hidden) {
|
||||||
|
|
||||||
|
const allVideos = document.querySelectorAll('video');
|
||||||
|
for (let videoId = allVideos.length; videoId--;) {
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (allVideos[videoId]);
|
||||||
|
if (video.webkitPresentationMode == 'picture-in-picture') {
|
||||||
|
activeVideo = video;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!activeVideo) return;
|
||||||
|
|
||||||
|
for (let request; request = requests.pop();) {
|
||||||
|
window.cancelAnimationFrame(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout = unthrottledSetTimeout;
|
||||||
|
window.clearTimeout = unthrottledClearTimeout;
|
||||||
|
|
||||||
|
activeVideo.addEventListener('timeupdate', callAnimationFrameRequestsAndTimeouts);
|
||||||
|
|
||||||
|
info('Bypassing background timer throttling');
|
||||||
|
|
||||||
|
} else if (activeVideo) {
|
||||||
|
|
||||||
|
info('Finished bypassing background timer throttling');
|
||||||
|
|
||||||
|
window.setTimeout = originalSetTimeout;
|
||||||
|
window.clearTimeout = originalClearTimeout;
|
||||||
|
|
||||||
|
activeVideo.removeEventListener('timeupdate', callAnimationFrameRequestsAndTimeouts);
|
||||||
|
|
||||||
|
activeVideo = null;
|
||||||
|
|
||||||
|
for (let callbackId = callbacks.length; callbackId--;) {
|
||||||
|
let request = originalRequestAnimationFrame(callbacks[callbackId]);
|
||||||
|
requests.push(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = window.performance.now();
|
||||||
|
for (let id in timeouts) {
|
||||||
|
let timeout = timeouts[id];
|
||||||
|
originalSetTimeout(timeout[1], timeout[0] - timestamp);
|
||||||
|
}
|
||||||
|
timeouts = {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('visibilitychange', bypassBackgroundTimerThrottling);
|
||||||
117
src/common/scripts/localization.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { error } from './logger.js'
|
||||||
|
|
||||||
|
const localizations = {};
|
||||||
|
|
||||||
|
localizations['button-title'] = {
|
||||||
|
'en': 'Open Picture in Picture mode',
|
||||||
|
'de': 'Bild-in-Bild starten',
|
||||||
|
'nl': 'Beeld in beeld starten',
|
||||||
|
'fr': 'Démarrer Image dans l’image',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['donate'] = {
|
||||||
|
'en': 'Donate',
|
||||||
|
'de': 'Spenden',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['donate-small'] = {
|
||||||
|
'en': 'Small donation',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['donate-medium'] = {
|
||||||
|
'en': 'Medium donation',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['donate-large'] = {
|
||||||
|
'en': 'Grand donation',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['total-donations'] = {
|
||||||
|
'en': 'Total donations:',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['donate-error'] = {
|
||||||
|
'en': 'In-app purchase unavailable',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['report-bug'] = {
|
||||||
|
'en': 'Report a bug',
|
||||||
|
'de': 'Einen Fehler melden',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['options'] = {
|
||||||
|
'en': 'Options',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['install-thanks'] = {
|
||||||
|
'en': 'Thanks for adding PiPer!',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['enable'] = {
|
||||||
|
'en': 'Enable',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['safari-disabled-warning'] = {
|
||||||
|
'en': 'Extension is currently disabled, enable in Safari preferences',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['chrome-flags-open'] = {
|
||||||
|
'en': 'Open Chrome Flags',
|
||||||
|
};
|
||||||
|
|
||||||
|
localizations['chrome-flags-warning'] = {
|
||||||
|
'en': 'Before you get started you need to enable the chrome flag [emphasis]"SurfaceLayer objects for videos"[/emphasis]',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set English as the default fallback language
|
||||||
|
const defaultLanguage = 'en';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a localized version of the string designated by the specified key
|
||||||
|
*
|
||||||
|
* @param {string} key - the key for a string
|
||||||
|
* @param {string=} language - two-letter ISO 639-1 language code
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const localizedString = function(key, language = navigator.language.substring(0, 2)) {
|
||||||
|
|
||||||
|
// Get all localizations for key
|
||||||
|
const /** Object<string,string> */ localizationsForKey = localizations[key];
|
||||||
|
if (localizationsForKey) {
|
||||||
|
|
||||||
|
// Get the users specific localization or fallback to default language
|
||||||
|
let string = localizationsForKey[language] || localizationsForKey[defaultLanguage];
|
||||||
|
if (string) return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
error(`No localized string found for key '${key}'`);
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a localized version of the string designated by the specified key with tags replaced
|
||||||
|
*
|
||||||
|
* @param {string} key - the key for a string
|
||||||
|
* @param {Array<Array<string>>} replacements - an array of arrays containing pairs of tags and their replacement
|
||||||
|
* @param {string=} language - two-letter ISO 639-1 language code
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const localizedStringWithReplacements = function(key, replacements, language) {
|
||||||
|
|
||||||
|
let string = localizedString(key, language);
|
||||||
|
|
||||||
|
// Replace tags of the form [XXX] with directed replacements if needed
|
||||||
|
for (let index = replacements.length; index--; ) {
|
||||||
|
let replacement = replacements[index];
|
||||||
|
|
||||||
|
// Ensure tags do not contain special characters (this gets optimised away as opposed to escaping the tags with the associated performance cost)
|
||||||
|
if (/[^-_0-9a-zA-Z\/]/.test(replacement[0])) {
|
||||||
|
error(`Invalid characters used in localized string tag '${replacement[0]}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const regex = new RegExp(`\\\[${replacement[0]}\\\]`, 'g');
|
||||||
|
string = string.replace(regex, replacement[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
};
|
||||||
36
src/common/scripts/logger.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { LOGGING_LEVEL } from './defines.js'
|
||||||
|
|
||||||
|
const loggingPrefix = '[PiPer] ';
|
||||||
|
|
||||||
|
/** @enum {number} - Enum for logging level */
|
||||||
|
export const LoggingLevel = {
|
||||||
|
ALL: 0,
|
||||||
|
TRACE: 10,
|
||||||
|
INFO: 20,
|
||||||
|
WARNING: 30,
|
||||||
|
ERROR: 40,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs stack trace to console
|
||||||
|
*/
|
||||||
|
export const trace = (LoggingLevel.TRACE >= LOGGING_LEVEL) ?
|
||||||
|
console.trace.bind(console) : function(){};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs informational message to console
|
||||||
|
*/
|
||||||
|
export const info = (LoggingLevel.INFO >= LOGGING_LEVEL) ?
|
||||||
|
console.info.bind(console, loggingPrefix) : function(){};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs warning message to console
|
||||||
|
*/
|
||||||
|
export const warn = (LoggingLevel.WARNING >= LOGGING_LEVEL) ?
|
||||||
|
console.warn.bind(console, loggingPrefix) : function(){};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs error message to console
|
||||||
|
*/
|
||||||
|
export const error = (LoggingLevel.ERROR >= LOGGING_LEVEL) ?
|
||||||
|
console.error.bind(console, loggingPrefix) : function(){};
|
||||||
50
src/common/scripts/main.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { info } from './logger.js'
|
||||||
|
import { Browser, getBrowser, getResource, setResource } from './common.js'
|
||||||
|
import { addVideoElementListeners } from './video.js'
|
||||||
|
import { resources } from './resources/index.js';
|
||||||
|
import { checkButton, addButton } from './button.js'
|
||||||
|
import { shouldProcessCaptions, enableCaptions, processCaptions } from './captions.js'
|
||||||
|
import { initialiseCaches } from './cache.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks injected button and captions
|
||||||
|
*/
|
||||||
|
const mutationObserver = function() {
|
||||||
|
|
||||||
|
// Process video captions if needed
|
||||||
|
if (shouldProcessCaptions()) processCaptions();
|
||||||
|
|
||||||
|
// Workaround Chrome's lack of an entering Picture in Picture mode event by monitoring all video elements
|
||||||
|
if (getBrowser() == Browser.CHROME) addVideoElementListeners();
|
||||||
|
|
||||||
|
// Try adding the button to the page if needed
|
||||||
|
if (checkButton()) return;
|
||||||
|
const currentResource = getResource();
|
||||||
|
const buttonParent = currentResource.buttonParent();
|
||||||
|
if (buttonParent) {
|
||||||
|
addButton(buttonParent);
|
||||||
|
if (currentResource.buttonDidAppear) currentResource.buttonDidAppear();
|
||||||
|
info('Picture in Picture button added to webpage');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove subdomain and public suffix (far from comprehensive as only removes .X and .co.Y)
|
||||||
|
const domainName = location.hostname && location.hostname.match(/([^.]+)\.(?:co\.)?[^.]+$/)[1];
|
||||||
|
|
||||||
|
if (domainName in resources) {
|
||||||
|
info(`Matched site ${domainName} (${location})`);
|
||||||
|
setResource(resources[domainName]);
|
||||||
|
|
||||||
|
initialiseCaches();
|
||||||
|
|
||||||
|
if (getBrowser() == Browser.SAFARI) {
|
||||||
|
enableCaptions(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(mutationObserver);
|
||||||
|
observer.observe(document, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
mutationObserver();
|
||||||
|
}
|
||||||
22
src/common/scripts/resources/aktualne.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export const domain = 'aktualne';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'jw-icon jw-icon-inline jw-button-color jw-reset jw-icon-logo',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`
|
||||||
|
filter: brightness(50%) sepia(1) hue-rotate(311deg) saturate(550%) brightness(49%) !important;
|
||||||
|
`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.jw-controlbar-right-group');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
width: 38px;
|
||||||
|
filter: brightness(80%);
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.jw-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
32
src/common/scripts/resources/amazon.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
export const domain = ['amazon', 'primevideo'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.querySelector('.fullscreenButtonWrapper');
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
const e = document.getElementById('dv-web-player');
|
||||||
|
return e && e.querySelector('.hideableTopButtons');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
position: relative;
|
||||||
|
left: 8px;
|
||||||
|
width: 3vw;
|
||||||
|
height: 2vw;
|
||||||
|
min-width: 35px;
|
||||||
|
min-height: 24px;
|
||||||
|
border: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 0.8;
|
||||||
|
`),
|
||||||
|
captionElement: function() {
|
||||||
|
const e = document.getElementById('dv-web-player');
|
||||||
|
return e && e.querySelector('.captions');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
const e = document.querySelector('.rendererContainer');
|
||||||
|
return e && e.querySelector('video[width="100%"]');
|
||||||
|
},
|
||||||
|
};
|
||||||
13
src/common/scripts/resources/bbc.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const domain = 'bbc';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonParent: function() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.p_subtitlesContainer');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('#mediaContainer video[src]');
|
||||||
|
},
|
||||||
|
};
|
||||||
16
src/common/scripts/resources/collegehumor.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const domain = 'collegehumor';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`cursor: pointer`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('vjs_video_3_html5_api');
|
||||||
|
},
|
||||||
|
};
|
||||||
38
src/common/scripts/resources/curiositystream.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Browser, getBrowser, getResource } from './../common.js'
|
||||||
|
|
||||||
|
export const domain = 'curiositystream';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
if (getBrowser() != Browser.SAFARI) return;
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
const videoContainer = video.parentElement;
|
||||||
|
video.addEventListener('webkitbeginfullscreen', function() {
|
||||||
|
const height = Math.floor(100 * video.videoHeight / video.videoWidth) + 'vw';
|
||||||
|
const maxHeight = video.videoHeight + 'px';
|
||||||
|
videoContainer.style.setProperty('height', height, 'important');
|
||||||
|
videoContainer.style.setProperty('max-height', maxHeight);
|
||||||
|
});
|
||||||
|
video.addEventListener('webkitendfullscreen', function() {
|
||||||
|
videoContainer.style.removeProperty('height');
|
||||||
|
videoContainer.style.removeProperty('max-height');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
const e = document.getElementById('main-player');
|
||||||
|
return e && e.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
opacity: 0.8;
|
||||||
|
cursor: pointer;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('main-player_html5_api');
|
||||||
|
},
|
||||||
|
};
|
||||||
19
src/common/scripts/resources/eurosportplayer.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export const domain = 'eurosportplayer';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.controls-bar-right-section');
|
||||||
|
},
|
||||||
|
buttonScale: 0.9,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 15px;
|
||||||
|
opacity: 0.8;
|
||||||
|
cursor: pointer;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.video-player__screen');
|
||||||
|
},
|
||||||
|
};
|
||||||
21
src/common/scripts/resources/giantbomb.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export const domain = 'giantbomb';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.querySelector('.js-vid-pin-wrap').nextSibling;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.av-controls--right');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
margin-left: 16px;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 1.0;
|
||||||
|
cursor: pointer;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[id^="video_js-vid-player"]');
|
||||||
|
}
|
||||||
|
};
|
||||||
31
src/common/scripts/resources/hulu.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { getResource } from './../common.js'
|
||||||
|
|
||||||
|
export const domain = 'hulu';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'simple-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const buttonParent = getResource().buttonParent();
|
||||||
|
buttonParent.querySelector('.progress-bar-tracker').style.width = 'calc(100% - 380px)';
|
||||||
|
buttonParent.querySelector('.progress-time-container').style.marginRight = '45px';
|
||||||
|
},
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`
|
||||||
|
filter: brightness(50%) sepia(1) hue-rotate(58deg) saturate(160%) brightness(110%) !important;
|
||||||
|
`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('#site-player .main-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
top: -45px;
|
||||||
|
left: -50px;
|
||||||
|
filter: brightness(80%);
|
||||||
|
`),
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.closed-caption-container');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('content-video-player');
|
||||||
|
},
|
||||||
|
};
|
||||||
16
src/common/scripts/resources/littlethings.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const domain = 'littlethings';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'jw-icon jw-icon-inline jw-button-color jw-reset jw-icon-logo',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.jw-controlbar-right-group');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`width: 38px`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.jw-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
19
src/common/scripts/resources/mashable.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export const domain = 'mashable';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'jw-icon jw-icon-inline jw-button-color jw-reset jw-icon-logo',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.jw-controlbar-right-group');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
top: -2px;
|
||||||
|
width: 38px;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.jw-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
15
src/common/scripts/resources/metacafe.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const domain = 'metacafe';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('#player_place .tray');
|
||||||
|
},
|
||||||
|
buttonScale: 0.85,
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('#player_place video');
|
||||||
|
},
|
||||||
|
};
|
||||||
23
src/common/scripts/resources/mixer.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export const domain = 'mixer';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'control',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`background: rgba(255, 255, 255, 0.08)`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild.previousSibling;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.control-container .toolbar .right');
|
||||||
|
},
|
||||||
|
buttonScale: 0.65,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
margin-top: 3px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.control-container + video');
|
||||||
|
},
|
||||||
|
};
|
||||||
20
src/common/scripts/resources/mlb.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const domain = 'mlb';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
border: 0px;
|
||||||
|
background: transparent;
|
||||||
|
filter: brightness(80%);
|
||||||
|
`),
|
||||||
|
buttonHoverStyle: /** CSS */ (`filter: brightness(120%) !important`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.bottom-controls-right');
|
||||||
|
},
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.mlbtv-media-player video');
|
||||||
|
},
|
||||||
|
};
|
||||||
22
src/common/scripts/resources/netflix.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { getResource } from './../common.js'
|
||||||
|
|
||||||
|
export const domain = 'netflix';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'touchable PlayerControls--control-element nfp-button-control default-control-button',
|
||||||
|
buttonHoverStyle: /** CSS */ (`transform: scale(1.2);`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.PlayerControlsNeo__button-control-row');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
captionElement: function() {
|
||||||
|
const e = getResource().videoElement();
|
||||||
|
return e && e.parentElement.querySelector('.player-timedtext');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.VideoContainer video');
|
||||||
|
},
|
||||||
|
};
|
||||||
25
src/common/scripts/resources/ocs.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export const domain = 'ocs';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'footer-elt fltr',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.querySelector('#togglePlay');
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.footer-block:last-child');
|
||||||
|
},
|
||||||
|
buttonScale: 1.2,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
display: block;
|
||||||
|
width: 25px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
padding: 0px;
|
||||||
|
border: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('LgyVideoPlayer');
|
||||||
|
},
|
||||||
|
};
|
||||||
19
src/common/scripts/resources/openload.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export const domain = ['openload', 'oload'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('olvideo_html5_api');
|
||||||
|
},
|
||||||
|
};
|
||||||
24
src/common/scripts/resources/periscope.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export const domain = ['periscope', 'pscp'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'Pill Pill--withIcon',
|
||||||
|
buttonElementType: 'span',
|
||||||
|
buttonHoverStyle: /** CSS */ (`
|
||||||
|
opacity: 0.8 !important;
|
||||||
|
filter: brightness(125%) !important;
|
||||||
|
`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.querySelector('.ShareBroadcast').nextSibling;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.VideoOverlayRedesign-BottomBar-Right');
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: brightness(200%);
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.vjs-tech video[src]');
|
||||||
|
},
|
||||||
|
};
|
||||||
33
src/common/scripts/resources/plex.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { bypassBackgroundTimerThrottling } from './../common.js'
|
||||||
|
|
||||||
|
export const domain = 'plex';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
bypassBackgroundTimerThrottling();
|
||||||
|
},
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
const e = document.querySelector('div[class^="FullPlayerTopControls-topControls"]');
|
||||||
|
return /** @type {?Element} */ (e && e.lastChild);
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 0px;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 0.7;
|
||||||
|
text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.45);
|
||||||
|
`),
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.libjass-subs');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[class^="HTMLMedia-mediaElement"]');
|
||||||
|
},
|
||||||
|
};
|
||||||
18
src/common/scripts/resources/seznam.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const domain = ['seznam', 'stream'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'sznp-ui-widget-box',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`transform: scale(1.05)`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.sznp-ui-ctrl-panel-layout-wrapper');
|
||||||
|
},
|
||||||
|
buttonScale: 0.75,
|
||||||
|
buttonStyle: /** CSS */ (`cursor: pointer`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.sznp-ui-tech-video-wrapper video');
|
||||||
|
},
|
||||||
|
};
|
||||||
29
src/common/scripts/resources/streamable.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { getButton } from './../button.js'
|
||||||
|
|
||||||
|
export const domain = 'streamable';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const progressBar = document.getElementById('player-progress');
|
||||||
|
const progressBarStyle = window.getComputedStyle(progressBar);
|
||||||
|
getButton().style.right = progressBarStyle.right;
|
||||||
|
progressBar.style.right = (parseInt(progressBarStyle.right, 10) + 40) + 'px';
|
||||||
|
},
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.player-controls-right');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.9;
|
||||||
|
filter: drop-shadow(rgba(0, 0, 0, 0.5) 0px 0px 2px);
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('video-player-tag');
|
||||||
|
},
|
||||||
|
};
|
||||||
23
src/common/scripts/resources/ted.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { getButton } from './../button.js'
|
||||||
|
|
||||||
|
export const domain = 'ted';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'z-i:0 pos:r bottom:0 hover/bg:white.7 b-r:.1 p:1 cur:p',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
const playButton = document.querySelector('[aria-controls="video1"]');
|
||||||
|
return playButton.parentElement.parentElement;
|
||||||
|
},
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const img = getButton().querySelector('img');
|
||||||
|
img.classList.add('w:2');
|
||||||
|
img.classList.add('h:2');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[id^="ted-player-"]');
|
||||||
|
}
|
||||||
|
};
|
||||||
20
src/common/scripts/resources/theonion.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const domain = 'theonion';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'jw-icon jw-icon-inline jw-button-color jw-reset jw-icon-logo',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.jw-controlbar-right-group');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
top: -2px;
|
||||||
|
left: 10px;
|
||||||
|
width: 38px;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.jw-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
44
src/common/scripts/resources/twitch.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { getResource } from './../common.js'
|
||||||
|
import { getButton } from './../button.js'
|
||||||
|
import { videoPlayingPictureInPicture, togglePictureInPicture } from './../video.js'
|
||||||
|
|
||||||
|
export const domain = 'twitch';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'player-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const neighbourButton = document.querySelector('.qa-fullscreen-button');
|
||||||
|
const neighbourTooltip = /** @type {HTMLElement} */ (neighbourButton.querySelector('.player-tip'));
|
||||||
|
const button = getButton();
|
||||||
|
const title = button.title;
|
||||||
|
const /** string */ neighbourTitle = neighbourTooltip.dataset['tip'];
|
||||||
|
button.title = '';
|
||||||
|
button.addEventListener('mouseover', function() {
|
||||||
|
neighbourTooltip.dataset['tip'] = title;
|
||||||
|
neighbourTooltip.style.display = 'block';
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseout', function() {
|
||||||
|
neighbourTooltip.style.display = '';
|
||||||
|
neighbourTooltip.dataset['tip'] = neighbourTitle;
|
||||||
|
});
|
||||||
|
neighbourButton.addEventListener('click', function() {
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
if (videoPlayingPictureInPicture(video)) togglePictureInPicture(video);
|
||||||
|
});
|
||||||
|
neighbourButton.style.order = 2;
|
||||||
|
},
|
||||||
|
buttonHoverStyle: /** CSS */ (`
|
||||||
|
filter: brightness(50%) sepia(1) hue-rotate(219deg) saturate(117%) brightness(112%);
|
||||||
|
`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.player-buttons-right');
|
||||||
|
},
|
||||||
|
buttonScale: 0.8,
|
||||||
|
buttonStyle: /** CSS */ (`order: 1`),
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.player-captions-container');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('.player-video video');
|
||||||
|
},
|
||||||
|
};
|
||||||
26
src/common/scripts/resources/udemy.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { getResource } from './../common.js'
|
||||||
|
import { videoPlayingPictureInPicture, togglePictureInPicture } from './../video.js'
|
||||||
|
|
||||||
|
export const domain = 'udemy';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
document.querySelector('.vjs-fullscreen-control').addEventListener('click', function() {
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
if (videoPlayingPictureInPicture(video)) togglePictureInPicture(video);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`order: 7`),
|
||||||
|
captionElement: function() {
|
||||||
|
const e = getResource().videoElement();
|
||||||
|
return e && e.parentElement.querySelector('.vjs-text-track-display');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.vjs-tech');
|
||||||
|
},
|
||||||
|
};
|
||||||
23
src/common/scripts/resources/ustream.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export const domain = 'ustream';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'component shown',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonHoverStyle: /** CSS */ (`
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: drop-shadow(0px 0px 5px rgba(255, 255, 255, 0.5));
|
||||||
|
`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonScale: 0.8,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
opacity: 0.7;
|
||||||
|
`),
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.getElementById('controlPanelRight');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('#ViewerContainer video');
|
||||||
|
},
|
||||||
|
};
|
||||||
19
src/common/scripts/resources/vevo.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export const domain = 'vevo';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'player-control',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('#control-bar .right-controls');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
border: 0px;
|
||||||
|
background: transparent;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('html5-player');
|
||||||
|
},
|
||||||
|
};
|
||||||
17
src/common/scripts/resources/vice.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export const domain = 'vice';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vp__controls__icon__popup__container',
|
||||||
|
buttonElementType: 'div',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vp__controls__icons');
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`top: -11px`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.jw-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
21
src/common/scripts/resources/vid.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export const domain = 'vid';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.7,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
left: 9px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('video_player_html5_api');
|
||||||
|
},
|
||||||
|
};
|
||||||
23
src/common/scripts/resources/viervijfzes.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { getButton } from './../button.js'
|
||||||
|
|
||||||
|
export const domain = ['vijf', 'vier', 'zes'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
// Move fullscreen button to the right so the pip button appears left of it
|
||||||
|
const fullScreenButton = document.getElementsByClassName('vjs-fullscreen-control')[0];
|
||||||
|
fullScreenButton.style.order = 10;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.getElementsByClassName('vjs-control-bar')[0];
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
text-indent: 0! important;
|
||||||
|
margin-left: 10px;
|
||||||
|
order: 9;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[preload="metadata"]');
|
||||||
|
},
|
||||||
|
};
|
||||||
25
src/common/scripts/resources/vrt.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export const domain = 'vrt';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vuplay-control',
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.getElementsByClassName('vuplay-control-right')[0];
|
||||||
|
},
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.theoplayer-texttracks');
|
||||||
|
},
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
width: 30px;
|
||||||
|
height: 47px;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
top: -9px;
|
||||||
|
right: 8px;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[preload="metadata"]');
|
||||||
|
},
|
||||||
|
};
|
||||||
38
src/common/scripts/resources/vrv.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { getResource, bypassBackgroundTimerThrottling } from './../common.js'
|
||||||
|
import { getButton } from './../button.js'
|
||||||
|
import { videoPlayingPictureInPicture, togglePictureInPicture } from './../video.js'
|
||||||
|
|
||||||
|
export const domain = 'vrv';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'vjs-control vjs-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const neighbourButton = getButton().nextSibling;
|
||||||
|
neighbourButton.addEventListener('click', function() {
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
if (videoPlayingPictureInPicture(video)) togglePictureInPicture(video);
|
||||||
|
});
|
||||||
|
bypassBackgroundTimerThrottling();
|
||||||
|
},
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.vjs-control-bar');
|
||||||
|
},
|
||||||
|
buttonScale: 0.6,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
position: absolute;
|
||||||
|
right: calc(50px + 2.5rem);
|
||||||
|
width: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
`),
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.libjass-subs');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.getElementById('player_html5_api');
|
||||||
|
},
|
||||||
|
};
|
||||||
31
src/common/scripts/resources/yeloplay.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { getResource } from './../common.js'
|
||||||
|
|
||||||
|
export const domain = 'yeloplay';
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const parent = getResource().buttonParent();
|
||||||
|
parent.style.width = '210px';
|
||||||
|
},
|
||||||
|
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return document.getElementsByTagName('player-fullscreen-button')[0];
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.getElementsByClassName('buttons')[0];
|
||||||
|
},
|
||||||
|
buttonScale: 0.8,
|
||||||
|
buttonStyle: /** CSS */ (`
|
||||||
|
margin-bottom: -10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.8;
|
||||||
|
height: 40px !important;
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
`),
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video[src]');
|
||||||
|
},
|
||||||
|
};
|
||||||
55
src/common/scripts/resources/youtube.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Browser, getBrowser, getResource, bypassBackgroundTimerThrottling } from './../common.js'
|
||||||
|
import { getButton } from './../button.js'
|
||||||
|
import { enableCaptions, disableCaptions, shouldProcessCaptions } from './../captions.js'
|
||||||
|
|
||||||
|
export const domain = ['youtube', 'youtu'];
|
||||||
|
|
||||||
|
export const resource = {
|
||||||
|
buttonClassName: 'ytp-button',
|
||||||
|
buttonDidAppear: function() {
|
||||||
|
const button = getButton();
|
||||||
|
const neighbourButton = /** @type {?HTMLElement} */ (button.nextSibling);
|
||||||
|
const /** string */ title = button.title;
|
||||||
|
const /** string */ neighbourTitle = neighbourButton.title;
|
||||||
|
button.title = '';
|
||||||
|
button.addEventListener('mouseover', function() {
|
||||||
|
neighbourButton.title = title;
|
||||||
|
neighbourButton.dispatchEvent(new Event('mouseover'));
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseout', function() {
|
||||||
|
neighbourButton.dispatchEvent(new Event('mouseout'));
|
||||||
|
neighbourButton.title = neighbourTitle;
|
||||||
|
});
|
||||||
|
bypassBackgroundTimerThrottling();
|
||||||
|
|
||||||
|
// Workaround Safari bug; old captions persist in Picture in Picture mode when MediaSource buffers change
|
||||||
|
if (getBrowser() == Browser.SAFARI) {
|
||||||
|
const video = /** @type {?HTMLVideoElement} */ (getResource().videoElement());
|
||||||
|
let captionsVisible = false;
|
||||||
|
const navigateStart = function() {
|
||||||
|
captionsVisible = shouldProcessCaptions();
|
||||||
|
if (captionsVisible) disableCaptions();
|
||||||
|
};
|
||||||
|
const navigateFinish = function() {
|
||||||
|
if (captionsVisible) enableCaptions();
|
||||||
|
};
|
||||||
|
window.addEventListener('spfrequest', navigateStart);
|
||||||
|
window.addEventListener('spfdone', navigateFinish);
|
||||||
|
window.addEventListener('yt-navigate-start', navigateStart);
|
||||||
|
window.addEventListener('yt-navigate-finish', navigateFinish);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttonInsertBefore: function(/** Element */ parent) {
|
||||||
|
return parent.lastChild;
|
||||||
|
},
|
||||||
|
buttonParent: function() {
|
||||||
|
return document.querySelector('.ytp-right-controls');
|
||||||
|
},
|
||||||
|
buttonScale: 0.68,
|
||||||
|
captionElement: function() {
|
||||||
|
return document.querySelector('.caption-window');
|
||||||
|
},
|
||||||
|
videoElement: function() {
|
||||||
|
return document.querySelector('video.html5-main-video');
|
||||||
|
},
|
||||||
|
};
|
||||||
153
src/common/scripts/video.js
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { info } from './logger.js'
|
||||||
|
import { Browser, getBrowser, getResource } from './common.js'
|
||||||
|
|
||||||
|
const CHROME_PLAYING_PIP_ATTRIBUTE = 'data-playing-picture-in-picture';
|
||||||
|
|
||||||
|
const /** !Array<function(HTMLVideoElement, boolean)> */ eventListeners = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles video Picture in Picture
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - video element to toggle Picture in Picture mode
|
||||||
|
*/
|
||||||
|
export const togglePictureInPicture = function(video) {
|
||||||
|
const playingPictureInPicture = videoPlayingPictureInPicture(video);
|
||||||
|
switch (getBrowser()) {
|
||||||
|
case Browser.SAFARI:
|
||||||
|
if (playingPictureInPicture) {
|
||||||
|
video.webkitSetPresentationMode('inline');
|
||||||
|
} else {
|
||||||
|
video.webkitSetPresentationMode('picture-in-picture');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Browser.CHROME:
|
||||||
|
if (playingPictureInPicture) {
|
||||||
|
// Workaround Chrome content scripts being unable to call 'exitPictureInPicture' directly
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.textContent = 'document.exitPictureInPicture()';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
script.remove();
|
||||||
|
} else {
|
||||||
|
video.requestPictureInPicture();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Browser.UNKNOWN:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Picture in Picture event listener
|
||||||
|
*
|
||||||
|
* @param {function(HTMLVideoElement, boolean)} listener - an event listener to add
|
||||||
|
*/
|
||||||
|
export const addPictureInPictureEventListener = function(listener) {
|
||||||
|
if (!eventListeners.includes(listener)) {
|
||||||
|
eventListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getBrowser() == Browser.SAFARI) {
|
||||||
|
document.addEventListener('webkitpresentationmodechanged', videoPresentationModeChanged, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a Picture in Picture event listener
|
||||||
|
*
|
||||||
|
* @param {function(HTMLVideoElement,boolean)} listener - an event listener to remove
|
||||||
|
*/
|
||||||
|
export const removePictureInPictureEventListener = function(listener) {
|
||||||
|
const index = eventListeners.indexOf(listener);
|
||||||
|
if (index > -1) {
|
||||||
|
eventListeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getBrowser() == Browser.SAFARI && eventListeners.length == 0) {
|
||||||
|
document.removeEventListener('webkitpresentationmodechanged', videoPresentationModeChanged);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a Picture in Picture event
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - target video element
|
||||||
|
*/
|
||||||
|
const dispatchPictureInPictureEvent = function(video) {
|
||||||
|
|
||||||
|
// Ignore events from other video elements e.g. adverts
|
||||||
|
const expectedVideo = getResource().videoElement(true);
|
||||||
|
if (video != expectedVideo) return;
|
||||||
|
|
||||||
|
const isPlayingPictureInPicture = videoPlayingPictureInPicture(video);
|
||||||
|
if (isPlayingPictureInPicture) {
|
||||||
|
info('Video entering Picture in Picture mode');
|
||||||
|
} else {
|
||||||
|
info('Video leaving Picture in Picture mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call event listeners using a copy to prevent possiblity of endless looping
|
||||||
|
const eventListenersCopy = eventListeners.slice();
|
||||||
|
for (let listener; listener = eventListenersCopy.pop();) {
|
||||||
|
listener(video, isPlayingPictureInPicture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a Picture in Picture event for every 'webkitpresentationmodechanged' event
|
||||||
|
*
|
||||||
|
* @param {!Event} event - a webkitpresentationmodechanged event
|
||||||
|
*/
|
||||||
|
const videoPresentationModeChanged = function(event) {
|
||||||
|
const video = /** @type {HTMLVideoElement} */ (event.target);
|
||||||
|
dispatchPictureInPictureEvent(video);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if video is playing Picture in Picture
|
||||||
|
*
|
||||||
|
* @param {HTMLVideoElement} video - video element to test
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const videoPlayingPictureInPicture = function(video) {
|
||||||
|
switch (getBrowser()) {
|
||||||
|
case Browser.SAFARI:
|
||||||
|
return video.webkitPresentationMode == 'picture-in-picture';
|
||||||
|
case Browser.CHROME:
|
||||||
|
return video.hasAttribute(CHROME_PLAYING_PIP_ATTRIBUTE);
|
||||||
|
case Browser.UNKNOWN:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets Picture in Picture attribute and toggles captions on entering Picture in Picture mode
|
||||||
|
*
|
||||||
|
* @param {!Event} event - an enterpictureinpicture event
|
||||||
|
*/
|
||||||
|
const videoDidEnterPictureInPicture = function(event) {
|
||||||
|
const video = /** @type {HTMLVideoElement} */ (event.target);
|
||||||
|
|
||||||
|
// Set playing in Picture in Picture mode attribute and dispatch event
|
||||||
|
video.setAttribute(CHROME_PLAYING_PIP_ATTRIBUTE, true);
|
||||||
|
dispatchPictureInPictureEvent(video);
|
||||||
|
|
||||||
|
// Remove Picture in Picture attribute and dispatch event on leaving Picture in Picture mode
|
||||||
|
video.addEventListener('leavepictureinpicture', function(event) {
|
||||||
|
video.removeAttribute(CHROME_PLAYING_PIP_ATTRIBUTE);
|
||||||
|
dispatchPictureInPictureEvent(video);
|
||||||
|
}, { once: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds Picture in Picture event listeners to all video elements
|
||||||
|
*/
|
||||||
|
export const addVideoElementListeners = function() {
|
||||||
|
const elements = document.getElementsByTagName('video');
|
||||||
|
for (let index = 0, element; element = elements[index]; index++) {
|
||||||
|
element.addEventListener('enterpictureinpicture', videoDidEnterPictureInPicture);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<svg fill="#FFFFFF" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>
|
|
||||||
<path d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 289 B |
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:url(#SVGID_1_);}
|
|
||||||
.st1{fill:url(#SVGID_2_);}
|
|
||||||
</style>
|
|
||||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="18" y1="0" x2="18" y2="36">
|
|
||||||
<stop offset="0" style="stop-color:#C9C9C9"/>
|
|
||||||
<stop offset="1" style="stop-color:#6D6D6D"/>
|
|
||||||
</linearGradient>
|
|
||||||
<path class="st0" d="M36,18c0,9.9-8.1,18-18,18S0,27.9,0,18S8.1,0,18,0S36,8.1,36,18z M18,3.1C9.8,3.1,3.1,9.8,3.1,18
|
|
||||||
S9.8,32.9,18,32.9S32.9,26.2,32.9,18S26.2,3.1,18,3.1z"/>
|
|
||||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="18" y1="9.8" x2="18" y2="26.3">
|
|
||||||
<stop offset="0" style="stop-color:#C9C9C9"/>
|
|
||||||
<stop offset="1" style="stop-color:#6D6D6D"/>
|
|
||||||
</linearGradient>
|
|
||||||
<path class="st1" d="M27,26.3H9c-1.1,0-2-0.9-2-2V11.8c0-1.1,0.9-2,2-2h18c1.1,0,2,0.9,2,2v12.5C29,25.4,28.1,26.3,27,26.3z
|
|
||||||
M18.4,15v-1.9c0-1.1-0.9-2-2-2h-6.3c-1.1,0-2,0.9-2,2V15c0,1.1,0.9,2,2,2h6.3C17.5,17,18.4,16.1,18.4,15z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
50
src/safari-legacy/Info.plist
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Author</key>
|
||||||
|
<string>Adam Marcus</string>
|
||||||
|
<key>Builder Version</key>
|
||||||
|
<string>12602.4.8</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>PiPer</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.amarcus.safari.piper</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>Content</key>
|
||||||
|
<dict>
|
||||||
|
<key>Scripts</key>
|
||||||
|
<dict>
|
||||||
|
<key>End</key>
|
||||||
|
<array>
|
||||||
|
<string>scripts/main.js</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>Description</key>
|
||||||
|
<string>Adds Picture in Picture functionality to Youtube, Netflix, Amazon Video, Twitch, and more!</string>
|
||||||
|
<key>DeveloperIdentifier</key>
|
||||||
|
<string>BQ6Q24MF9X</string>
|
||||||
|
<key>ExtensionInfoDictionaryVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>Permissions</key>
|
||||||
|
<dict>
|
||||||
|
<key>Website Access</key>
|
||||||
|
<dict>
|
||||||
|
<key>Include Secure Pages</key>
|
||||||
|
<true/>
|
||||||
|
<key>Level</key>
|
||||||
|
<string>All</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>Update Manifest URL</key>
|
||||||
|
<string>https://s3.amazonaws.com/piper-extension/update.plist</string>
|
||||||
|
<key>Website</key>
|
||||||
|
<string>https://github.com/amarcu5/PiPer/</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -8,9 +8,9 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.amarcus.safari.piper</string>
|
<string>com.amarcus.safari.piper</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2.5</string>
|
<string></string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>151</string>
|
<string>186</string>
|
||||||
<key>Developer Identifier</key>
|
<key>Developer Identifier</key>
|
||||||
<string>BQ6Q24MF9X</string>
|
<string>BQ6Q24MF9X</string>
|
||||||
<key>URL</key>
|
<key>URL</key>
|
||||||
27
src/safari/App/AppDelegate.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// AppDelegate.swift
|
||||||
|
// PiPer App
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 19/07/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
@NSApplicationMain
|
||||||
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
|
// Preload donation in-app purchases
|
||||||
|
DonationManager.shared.getDonationProducts()
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
117
src/safari/App/ConfettiView.swift
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
//
|
||||||
|
// ConfettiView.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 22/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class ConfettiView: NSView {
|
||||||
|
|
||||||
|
private static var colors = [
|
||||||
|
NSColor(red:0.95, green:0.40, blue:0.27, alpha:1.0),
|
||||||
|
NSColor(red:1.00, green:0.78, blue:0.36, alpha:1.0),
|
||||||
|
NSColor(red:0.48, green:0.78, blue:0.64, alpha:1.0),
|
||||||
|
NSColor(red:0.30, green:0.76, blue:0.85, alpha:1.0),
|
||||||
|
NSColor(red:0.58, green:0.39, blue:0.55, alpha:1.0)
|
||||||
|
]
|
||||||
|
|
||||||
|
private var emitter: CAEmitterLayer!
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setup() {
|
||||||
|
|
||||||
|
// Set up confetti emitter layer
|
||||||
|
emitter = CAEmitterLayer()
|
||||||
|
emitter.emitterShape = CAEmitterLayerEmitterShape.line
|
||||||
|
emitter.birthRate = 0
|
||||||
|
|
||||||
|
// Set confetti emitter layer position/size and respond to view frame changes
|
||||||
|
self.postsFrameChangedNotifications = true
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(setEmitterFrame),
|
||||||
|
name: NSView.frameDidChangeNotification,
|
||||||
|
object: self)
|
||||||
|
setEmitterFrame()
|
||||||
|
|
||||||
|
// Add emitter cells for each confetti color
|
||||||
|
var cells = [CAEmitterCell]()
|
||||||
|
for color in ConfettiView.colors {
|
||||||
|
cells.append(confettiWithColor(color: color))
|
||||||
|
}
|
||||||
|
emitter.emitterCells = cells
|
||||||
|
|
||||||
|
// Add confetti emitter layer
|
||||||
|
self.wantsLayer = true
|
||||||
|
self.layer!.addSublayer(emitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func setEmitterFrame() {
|
||||||
|
|
||||||
|
// Position confetti emitter offscreen and size to fit view
|
||||||
|
emitter.emitterPosition = CGPoint(x: frame.size.width * 0.5,
|
||||||
|
y: frame.size.height + 32)
|
||||||
|
emitter.emitterSize = CGSize(width: frame.size.width,
|
||||||
|
height: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dropConfetti() {
|
||||||
|
|
||||||
|
// Set confetti emitter to show particles start spawning from emitter position
|
||||||
|
emitter.beginTime = CACurrentMediaTime()
|
||||||
|
|
||||||
|
// Animate confetti emitter birth rate to spawn a burst of confetti
|
||||||
|
let birthRateDecayAnimation = CABasicAnimation()
|
||||||
|
birthRateDecayAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||||
|
birthRateDecayAnimation.duration = CFTimeInterval(1.0)
|
||||||
|
birthRateDecayAnimation.fromValue = NSNumber(value: 1.0)
|
||||||
|
birthRateDecayAnimation.toValue = NSNumber(value: 0.0)
|
||||||
|
emitter.add(birthRateDecayAnimation, forKey: "birthRate")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a diamond CGImage representing confetti
|
||||||
|
private func getDiamondImage() -> CGImage? {
|
||||||
|
let image = NSImage(size: CGSize(width: 24, height: 32), flipped: false) { _ in
|
||||||
|
let path = NSBezierPath()
|
||||||
|
path.move(to: NSPoint(x: 12, y: 0))
|
||||||
|
path.line(to: NSPoint(x: 24, y: 16))
|
||||||
|
path.line(to: NSPoint(x: 12, y: 32))
|
||||||
|
path.line(to: NSPoint(x: 0, y: 16))
|
||||||
|
path.line(to: NSPoint(x: 12, y: 0))
|
||||||
|
NSColor.white.setFill()
|
||||||
|
path.fill()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return image.cgImage(forProposedRect: nil, context: nil, hints: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup an confetti emitter cell with a specific color
|
||||||
|
private func confettiWithColor(color: NSColor) -> CAEmitterCell {
|
||||||
|
let confetti = CAEmitterCell()
|
||||||
|
confetti.birthRate = 400.0
|
||||||
|
confetti.lifetime = 10.0
|
||||||
|
confetti.alphaSpeed = -1.0 / confetti.lifetime
|
||||||
|
confetti.color = color.cgColor
|
||||||
|
confetti.velocity = 200.0
|
||||||
|
confetti.velocityRange = 200.0
|
||||||
|
confetti.emissionRange = CGFloat(Double.pi * 0.5)
|
||||||
|
confetti.spin = 3.0
|
||||||
|
confetti.spinRange = 3.0
|
||||||
|
confetti.scale = 0.15
|
||||||
|
confetti.scaleRange = 0.15
|
||||||
|
confetti.contents = getDiamondImage()
|
||||||
|
return confetti
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/safari/App/DonateContainerViewController.swift
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// DonateContainerViewController.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 19/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class DonateContainerViewController: NSTabViewController {
|
||||||
|
|
||||||
|
@IBOutlet var donateProgressViewTabItem: NSTabViewItem!
|
||||||
|
@IBOutlet var donateViewTabItem: NSTabViewItem!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
let donateViewController = donateViewTabItem.viewController as! DonateViewController
|
||||||
|
let donateProgressViewController = donateProgressViewTabItem.viewController as! DonateProgressViewController
|
||||||
|
|
||||||
|
let productsAvaliable = DonationManager.shared.donationProductsAvaliable()
|
||||||
|
|
||||||
|
donateViewController.loadProducts(completionHandler: {
|
||||||
|
success in
|
||||||
|
if !success {
|
||||||
|
donateProgressViewController.showError()
|
||||||
|
} else if !productsAvaliable {
|
||||||
|
self.tabView.selectTabViewItem(self.donateViewTabItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if productsAvaliable {
|
||||||
|
self.tabView.selectTabViewItem(self.donateViewTabItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
|
||||||
|
super.tabView(tabView, didSelect: tabViewItem)
|
||||||
|
|
||||||
|
if let window = self.view.window, let contentSize = tabViewItem?.view?.fittingSize {
|
||||||
|
let newWindowSize = window.frameRect(forContentRect: NSRect(origin: CGPoint.zero, size: contentSize)).size
|
||||||
|
|
||||||
|
var frame = window.frame
|
||||||
|
frame.origin.x = frame.origin.x + (frame.size.width - newWindowSize.width) * 0.5
|
||||||
|
frame.origin.y = frame.origin.y + (frame.size.height - newWindowSize.height)
|
||||||
|
frame.size = newWindowSize
|
||||||
|
|
||||||
|
window.animator().setFrame(frame, display: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
29
src/safari/App/DonateProgressViewController.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// DonateProgressViewController.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 19/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class DonateProgressViewController: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet var progressIndicator: NSProgressIndicator!
|
||||||
|
@IBOutlet var errorMessage: LocalizedTextField!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
progressIndicator.startAnimation(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showError() {
|
||||||
|
progressIndicator.stopAnimation(self)
|
||||||
|
errorMessage.isHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func dismissClicked(sender: NSButton) {
|
||||||
|
self.parent?.dismiss(self.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/safari/App/DonateViewController.swift
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// DonateViewController.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 17/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class DonateViewController: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet var totalDonations: NSTextField!
|
||||||
|
|
||||||
|
@objc dynamic var donationProducts = [DonationProduct]()
|
||||||
|
|
||||||
|
@IBOutlet var donationTableView: NSTableView!
|
||||||
|
@IBOutlet var tableViewHeightConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet var tableViewWidthConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
@IBOutlet var confettiView: ConfettiView!
|
||||||
|
|
||||||
|
func loadProducts(completionHandler: @escaping (_ success: Bool) -> ()) {
|
||||||
|
DonationManager.shared.getDonationProducts(completionHandler: {
|
||||||
|
productsResponse, error in
|
||||||
|
if let products = productsResponse {
|
||||||
|
self.donationProducts = products
|
||||||
|
completionHandler(true)
|
||||||
|
} else {
|
||||||
|
completionHandler(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.donationTableView.postsFrameChangedNotifications = true
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(sizeDonationTableViewToFitContents),
|
||||||
|
name: NSView.frameDidChangeNotification,
|
||||||
|
object: self.donationTableView)
|
||||||
|
sizeDonationTableViewToFitContents()
|
||||||
|
|
||||||
|
updateTotalDonations()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func sizeDonationTableViewToFitContents() {
|
||||||
|
var computedWidth: CGFloat = 0
|
||||||
|
for row in 0..<self.donationTableView.numberOfRows {
|
||||||
|
if let tableCellView = self.donationTableView.view(atColumn: 0, row:row, makeIfNecessary: true) {
|
||||||
|
computedWidth = max(computedWidth, tableCellView.fittingSize.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.tableViewHeightConstraint.constant = self.donationTableView.frame.size.height
|
||||||
|
self.tableViewWidthConstraint.constant = computedWidth
|
||||||
|
self.donationTableView.tableColumns.first?.width = computedWidth
|
||||||
|
self.donationTableView.needsUpdateConstraints = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func updateTotalDonations() {
|
||||||
|
let totalDonations = DonationManager.shared.totalDonations
|
||||||
|
if let priceString = DonationManager.shared.localizedStringForPrice(totalDonations),
|
||||||
|
let emoticon = DonationManager.shared.emoticonForPrice(totalDonations) {
|
||||||
|
self.totalDonations.stringValue = priceString + " " + emoticon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func buyClicked(sender: NSButton) {
|
||||||
|
let row = self.donationTableView.row(for: sender)
|
||||||
|
let donationProduct = self.donationProducts[row]
|
||||||
|
DonationManager.shared.buyDonationProduct(donationProduct, completionHandler: {
|
||||||
|
transaction in
|
||||||
|
|
||||||
|
let state = transaction.transactionState
|
||||||
|
if state == .purchased || state == .restored {
|
||||||
|
self.confettiView.dropConfetti()
|
||||||
|
self.updateTotalDonations()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func dismissClicked(sender: NSButton) {
|
||||||
|
self.parent?.dismiss(self.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/safari/App/DonationManager.swift
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
//
|
||||||
|
// DonationManager.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 21/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
|
||||||
|
class DonationProduct : NSObject {
|
||||||
|
@objc let name: String
|
||||||
|
@objc let price: String
|
||||||
|
@objc let emoticon: String
|
||||||
|
|
||||||
|
fileprivate let product: SKProduct
|
||||||
|
|
||||||
|
fileprivate init(name: String, price: String, emoticon: String, product: SKProduct) {
|
||||||
|
self.name = name
|
||||||
|
self.price = price
|
||||||
|
self.emoticon = emoticon
|
||||||
|
self.product = product
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DonationManager {
|
||||||
|
|
||||||
|
public typealias GetDonationProductsCompletionHandler = (_ products: [DonationProduct]?, _ error: Error?) -> ()
|
||||||
|
public typealias BuyDonationProductCompletionHandler = (_ transaction: SKPaymentTransaction) -> ()
|
||||||
|
|
||||||
|
static let shared = DonationManager()
|
||||||
|
|
||||||
|
static private let identifiers = Set([
|
||||||
|
"com.amarcus.PiPer.donation.1",
|
||||||
|
"com.amarcus.PiPer.donation.3",
|
||||||
|
"com.amarcus.PiPer.donation.10"
|
||||||
|
])
|
||||||
|
|
||||||
|
static private let totalDonationsKey = "com.amarcus.PiPer.totalDonations"
|
||||||
|
|
||||||
|
private var donationProducts:[DonationProduct]?
|
||||||
|
|
||||||
|
private var smallestDonation: NSDecimalNumber?
|
||||||
|
private var priceFormatter: NumberFormatter?
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
func getDonationProducts(completionHandler: @escaping GetDonationProductsCompletionHandler = {_,_ in}) {
|
||||||
|
|
||||||
|
if let products = self.donationProducts {
|
||||||
|
completionHandler(products, .none)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard InAppPurchaseHelper.shared.canMakePayments() else {
|
||||||
|
let error = NSError(domain: "com.amarcus.PiPer.DonationManager",
|
||||||
|
code: 0,
|
||||||
|
userInfo: [NSLocalizedDescriptionKey: "Payments are unavailable"])
|
||||||
|
completionHandler(nil, error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
InAppPurchaseHelper.shared.requestProducts(identifiers:DonationManager.identifiers, completionHandler: {
|
||||||
|
productResponse, error in
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
guard error == nil else {
|
||||||
|
completionHandler(nil, error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let response = productResponse {
|
||||||
|
let sortedProducts = response.products.sorted { (product1, product2) -> Bool in
|
||||||
|
return product1.price.compare(product2.price) == .orderedAscending
|
||||||
|
}
|
||||||
|
|
||||||
|
if let cheapestProduct = sortedProducts.first {
|
||||||
|
let priceFormatter = NumberFormatter()
|
||||||
|
priceFormatter.numberStyle = .currency
|
||||||
|
priceFormatter.locale = cheapestProduct.priceLocale
|
||||||
|
self.priceFormatter = priceFormatter
|
||||||
|
self.smallestDonation = cheapestProduct.price
|
||||||
|
}
|
||||||
|
|
||||||
|
self.donationProducts = sortedProducts.map {
|
||||||
|
DonationProduct(name: $0.localizedTitle,
|
||||||
|
price: self.localizedStringForPrice($0.price)!,
|
||||||
|
emoticon: self.emoticonForPrice($0.price)!,
|
||||||
|
product: $0)
|
||||||
|
}
|
||||||
|
completionHandler(self.donationProducts, .none)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalDonations: NSDecimalNumber {
|
||||||
|
set {
|
||||||
|
guard let priceFormatter = self.priceFormatter else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var totalDonationsDictionary = NSUbiquitousKeyValueStore.default.dictionary(forKey: DonationManager.totalDonationsKey) ?? [String:String]()
|
||||||
|
let numberString = newValue.description(withLocale: [NSLocale.Key.decimalSeparator: "."])
|
||||||
|
totalDonationsDictionary[priceFormatter.currencyCode] = numberString
|
||||||
|
NSUbiquitousKeyValueStore.default.set(totalDonationsDictionary, forKey: DonationManager.totalDonationsKey)
|
||||||
|
NSUbiquitousKeyValueStore.default.synchronize()
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
guard let priceFormatter = self.priceFormatter,
|
||||||
|
let totalDonationsDictionary = NSUbiquitousKeyValueStore.default.dictionary(forKey: DonationManager.totalDonationsKey),
|
||||||
|
let numberString = totalDonationsDictionary[priceFormatter.currencyCode] as? String else {
|
||||||
|
return NSDecimalNumber.zero
|
||||||
|
}
|
||||||
|
return NSDecimalNumber(string: numberString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buyDonationProduct(_ donationProduct: DonationProduct, completionHandler: @escaping BuyDonationProductCompletionHandler = {_ in}) {
|
||||||
|
|
||||||
|
InAppPurchaseHelper.shared.buyProduct(donationProduct.product, completionHandler: {
|
||||||
|
transaction in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.totalDonations = self.totalDonations.adding(donationProduct.product.price)
|
||||||
|
completionHandler(transaction)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func donationProductsAvaliable() -> Bool {
|
||||||
|
return self.donationProducts != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func localizedStringForPrice(_ price: NSDecimalNumber) -> String? {
|
||||||
|
guard let priceFormatter = self.priceFormatter else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return priceFormatter.string(from:price) ?? "\(price)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func emoticonForPrice(_ decimalPrice: NSDecimalNumber) -> String? {
|
||||||
|
guard let baseUnit = self.smallestDonation?.doubleValue else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get seasonal emoticons
|
||||||
|
let emoticons = Array({
|
||||||
|
() -> String in
|
||||||
|
switch Calendar.current.dateComponents([.month, .weekdayOrdinal, .weekday, .day], from: Date()) {
|
||||||
|
case let date where date.month == 10 && date.day == 31: // Halloween
|
||||||
|
return "💀👻🧙♀️🎃"
|
||||||
|
case let date where date.month == 11 && date.weekdayOrdinal == 4 && date.weekday == 5: // Thanksgiving
|
||||||
|
return "🍂🍴🍗🇺🇸"
|
||||||
|
case let date where date.month == 12 && date.day == 25: // Christmas
|
||||||
|
return "🥶🎄🎁🎅"
|
||||||
|
default:
|
||||||
|
return "😢🙂😃😍"
|
||||||
|
}
|
||||||
|
}()).map { String($0) }
|
||||||
|
|
||||||
|
// Select emoticon based on price
|
||||||
|
let price = decimalPrice.doubleValue
|
||||||
|
if price < baseUnit {
|
||||||
|
return emoticons[0]
|
||||||
|
} else if price < 3 * baseUnit {
|
||||||
|
return emoticons[1]
|
||||||
|
} else if price < 10 * baseUnit {
|
||||||
|
return emoticons[2]
|
||||||
|
} else {
|
||||||
|
return emoticons[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
src/safari/App/Icon.icns
Normal file
87
src/safari/App/InAppPurchaseHelper.swift
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// InAppPurchaseHelper.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 18/11/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
class InAppPurchaseHelper: NSObject {
|
||||||
|
|
||||||
|
static let shared = InAppPurchaseHelper()
|
||||||
|
|
||||||
|
public typealias RequestProductsCompletionHandler = (_ response: SKProductsResponse?, _ error: Error?) -> ()
|
||||||
|
public typealias BuyProductCompletionHandler = (_ transaction: SKPaymentTransaction) -> ()
|
||||||
|
|
||||||
|
private var productsRequestsInProgress = [SKRequest:RequestProductsCompletionHandler]()
|
||||||
|
private var purchasesInProgress = [SKPayment:BuyProductCompletionHandler]()
|
||||||
|
private let paymentQueue = SKPaymentQueue.default()
|
||||||
|
|
||||||
|
private override init() {
|
||||||
|
super.init()
|
||||||
|
self.paymentQueue.add(self)
|
||||||
|
}
|
||||||
|
deinit {
|
||||||
|
self.paymentQueue.remove(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestProducts(identifiers: Set<String>, completionHandler: @escaping RequestProductsCompletionHandler) {
|
||||||
|
let request = SKProductsRequest(productIdentifiers: identifiers)
|
||||||
|
self.productsRequestsInProgress[request] = completionHandler
|
||||||
|
request.delegate = self
|
||||||
|
request.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func buyProduct(_ product: SKProduct, completionHandler: @escaping BuyProductCompletionHandler) {
|
||||||
|
let payment = SKPayment(product: product)
|
||||||
|
self.purchasesInProgress[payment] = completionHandler
|
||||||
|
self.paymentQueue.add(payment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canMakePayments() -> Bool {
|
||||||
|
return SKPaymentQueue.canMakePayments()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InAppPurchaseHelper: SKProductsRequestDelegate {
|
||||||
|
|
||||||
|
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||||
|
if let completionHandler = self.productsRequestsInProgress[request] {
|
||||||
|
completionHandler(response, .none)
|
||||||
|
}
|
||||||
|
self.productsRequestsInProgress.removeValue(forKey: request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func request(_ request: SKRequest, didFailWithError error: Error) {
|
||||||
|
if let completionHandler = self.productsRequestsInProgress[request] {
|
||||||
|
completionHandler(.none, error)
|
||||||
|
}
|
||||||
|
self.productsRequestsInProgress.removeValue(forKey: request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension InAppPurchaseHelper: SKPaymentTransactionObserver {
|
||||||
|
|
||||||
|
func paymentQueue(_ queue: SKPaymentQueue,
|
||||||
|
updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||||
|
for transaction in transactions {
|
||||||
|
switch transaction.transactionState {
|
||||||
|
case .purchased, .failed, .restored:
|
||||||
|
if let completionHandler = self.purchasesInProgress[transaction.payment] {
|
||||||
|
completionHandler(transaction)
|
||||||
|
self.purchasesInProgress.removeValue(forKey: transaction.payment)
|
||||||
|
}
|
||||||
|
queue.finishTransaction(transaction)
|
||||||
|
break
|
||||||
|
case .purchasing, .deferred:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
src/safari/App/Info.plist
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>PiPer</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>Icon.icns</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.0.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.utilities</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2018 Adam Marcus. All rights reserved.</string>
|
||||||
|
<key>NSMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
49
src/safari/App/LocalizationManager.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// LocalizationManager.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 12/10/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import JavaScriptCore
|
||||||
|
|
||||||
|
class LocalizationManager {
|
||||||
|
|
||||||
|
let context: JSContext = JSContext()
|
||||||
|
let languageCode: String
|
||||||
|
|
||||||
|
static let `default` = LocalizationManager()
|
||||||
|
|
||||||
|
init(withLanguageCode languageCode: String? = Locale.current.languageCode) {
|
||||||
|
|
||||||
|
self.languageCode = languageCode ?? ""
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
context.exceptionHandler = { _, value in
|
||||||
|
print("Localization JavaScriptCore error: \(value!)")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
context.evaluateScript("const window = {};")
|
||||||
|
|
||||||
|
if let extensionBundleURL = ResourceHelper.extensionBundleURL {
|
||||||
|
let localizationFile = extensionBundleURL.appendingPathComponent("scripts/localization-bridge.js").path
|
||||||
|
|
||||||
|
let localizationFileContents = try? String(contentsOfFile: localizationFile,
|
||||||
|
encoding: String.Encoding.utf8)
|
||||||
|
|
||||||
|
if let localizationScript = localizationFileContents {
|
||||||
|
context.evaluateScript(localizationScript)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func localizedString(forKey key: String) -> String {
|
||||||
|
let string = context.evaluateScript("window.localizedString('\(key)', '\(languageCode)');").toString()
|
||||||
|
return string ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
33
src/safari/App/LocalizedButton.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// LocalizedButton.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 13/10/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
@IBDesignable
|
||||||
|
class LocalizedButton: NSButton {
|
||||||
|
|
||||||
|
override init(frame frameRect: NSRect) {
|
||||||
|
super.init(frame: frameRect)
|
||||||
|
localizeTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
localizeTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForInterfaceBuilder() {
|
||||||
|
super.prepareForInterfaceBuilder()
|
||||||
|
localizeTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func localizeTitle() {
|
||||||
|
self.title = LocalizationManager.default.localizedString(forKey:self.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
src/safari/App/LocalizedTextField.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// LocalizedTextField.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 13/10/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
@IBDesignable
|
||||||
|
class LocalizedTextField: NSTextField {
|
||||||
|
|
||||||
|
override init(frame frameRect: NSRect) {
|
||||||
|
super.init(frame: frameRect)
|
||||||
|
localizeValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
localizeValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForInterfaceBuilder() {
|
||||||
|
super.prepareForInterfaceBuilder()
|
||||||
|
localizeValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func localizeValue() {
|
||||||
|
self.stringValue = LocalizationManager.default.localizedString(forKey:self.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
494
src/safari/App/Main.storyboard
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Application-->
|
||||||
|
<scene sceneID="JPo-4y-FX3">
|
||||||
|
<objects>
|
||||||
|
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||||
|
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||||
|
<items>
|
||||||
|
<menuItem title="PiPer" id="1Xt-HY-uBw">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="PiPer" systemMenu="apple" id="uQy-DD-JDr">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Quit PiPer" keyEquivalent="q" id="4sb-4s-VLi">
|
||||||
|
<connections>
|
||||||
|
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||||
|
</connections>
|
||||||
|
</application>
|
||||||
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="PiPer" customModuleProvider="target"/>
|
||||||
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
|
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="360" y="-44"/>
|
||||||
|
</scene>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="R2V-B0-nI4">
|
||||||
|
<objects>
|
||||||
|
<windowController id="B8D-0N-5wS" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="PiPer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="IQv-IB-iLA">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" fullSizeContentView="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="196" y="240" width="356" height="220"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
||||||
|
<value key="minSize" type="size" width="356" height="220"/>
|
||||||
|
<value key="maxSize" type="size" width="356" height="220"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="75" y="55"/>
|
||||||
|
</scene>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="hIz-AP-VOD">
|
||||||
|
<objects>
|
||||||
|
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="PiPer" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="m2S-Jp-Qdl">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="356" height="220"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<visualEffectView appearanceType="inheritedVibrantLight" blendingMode="behindWindow" material="underWindowBackground" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="fhh-nv-kqK">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="356" height="220"/>
|
||||||
|
</visualEffectView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="fhh-nv-kqK" secondAttribute="trailing" id="R1a-1n-kYy"/>
|
||||||
|
<constraint firstItem="fhh-nv-kqK" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="Vgc-sd-EOP"/>
|
||||||
|
<constraint firstItem="fhh-nv-kqK" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="ZHb-JW-3Uc"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="fhh-nv-kqK" secondAttribute="bottom" id="f4x-N6-X6o"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="extensionDisabledView" destination="Wtu-CL-ZAO" id="yuV-8q-YtZ"/>
|
||||||
|
<outlet property="mainView" destination="3Hb-XS-UyM" id="yUU-7Q-ncx"/>
|
||||||
|
<outlet property="viewContainer" destination="fhh-nv-kqK" id="c0U-Yq-xxu"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<customView id="Wtu-CL-ZAO">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="356" height="220"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="SlA-kL-hiD">
|
||||||
|
<rect key="frame" x="16" y="94" width="32" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="ZWA-Q4-Gnv"/>
|
||||||
|
<constraint firstAttribute="width" constant="32" id="sOE-7r-348"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSCaution" id="dJM-ez-eQ2"/>
|
||||||
|
</imageView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5oh-0M-Zn6" customClass="LocalizedButton" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="265" y="93" width="81" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="enable" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="HcB-QW-aQq">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedEnableExtensionWithSender:" target="XfG-lQ-9wD" id="jgv-vd-Hd3"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="200" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="500" translatesAutoresizingMaskIntoConstraints="NO" id="Gzc-Eb-L1r" customClass="LocalizedTextField" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="56" y="102" width="207" height="17"/>
|
||||||
|
<textFieldCell key="cell" alignment="left" title="safari-disabled-warning" drawsBackground="YES" id="LEL-vU-9sz">
|
||||||
|
<font key="font" metaFont="systemMedium" size="13"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="5oh-0M-Zn6" firstAttribute="leading" secondItem="Gzc-Eb-L1r" secondAttribute="trailing" constant="8" id="Hcd-kf-9uY"/>
|
||||||
|
<constraint firstItem="Gzc-Eb-L1r" firstAttribute="leading" secondItem="SlA-kL-hiD" secondAttribute="trailing" constant="8" id="IAC-H1-5u2"/>
|
||||||
|
<constraint firstItem="SlA-kL-hiD" firstAttribute="centerY" secondItem="Wtu-CL-ZAO" secondAttribute="centerY" id="P98-CO-DBI"/>
|
||||||
|
<constraint firstItem="5oh-0M-Zn6" firstAttribute="centerY" secondItem="Wtu-CL-ZAO" secondAttribute="centerY" id="Ttv-0y-TAb"/>
|
||||||
|
<constraint firstItem="Gzc-Eb-L1r" firstAttribute="centerY" secondItem="Wtu-CL-ZAO" secondAttribute="centerY" id="cWx-Ul-bxK"/>
|
||||||
|
<constraint firstItem="SlA-kL-hiD" firstAttribute="leading" secondItem="Wtu-CL-ZAO" secondAttribute="leading" constant="16" id="gUm-TC-3DN"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="5oh-0M-Zn6" secondAttribute="trailing" constant="16" id="rAw-aH-ZXO"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
<customView id="3Hb-XS-UyM">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="356" height="220"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="l9e-rg-eUq">
|
||||||
|
<rect key="frame" x="58" y="88" width="80" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="52v-J9-pFo"/>
|
||||||
|
<constraint firstAttribute="height" constant="80" id="9ic-ED-CML"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Icon" id="655-Gm-l0p"/>
|
||||||
|
</imageView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aXl-MI-TCo">
|
||||||
|
<rect key="frame" x="61" y="45" width="73" height="39"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="PiPer" drawsBackground="YES" id="pyu-MO-9NT">
|
||||||
|
<font key="font" metaFont="system" size="32"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="h9M-Bg-2Qi" customClass="LocalizedButton" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="196" y="104" width="107" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="report-bug" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="HPq-wV-jsz">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedReportBugWithSender:" target="XfG-lQ-9wD" id="GA1-uc-3Zx"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="l7Q-NM-lYD" customClass="LocalizedButton" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="196" y="70" width="107" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="donate" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jbL-wE-6Bb">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<segue destination="1sh-YF-bwE" kind="sheet" id="qCv-wd-VJy"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="l9e-rg-eUq" firstAttribute="centerX" secondItem="3Hb-XS-UyM" secondAttribute="centerX" multiplier="0.55" id="0rd-47-5ke"/>
|
||||||
|
<constraint firstItem="l7Q-NM-lYD" firstAttribute="centerY" secondItem="3Hb-XS-UyM" secondAttribute="centerY" multiplier="1.2" id="1yu-Ya-ffe"/>
|
||||||
|
<constraint firstItem="aXl-MI-TCo" firstAttribute="top" secondItem="l9e-rg-eUq" secondAttribute="bottom" constant="4" id="9A4-i3-Pj0"/>
|
||||||
|
<constraint firstItem="l9e-rg-eUq" firstAttribute="centerY" secondItem="3Hb-XS-UyM" secondAttribute="centerY" constant="-18" id="NqP-Fs-Cka"/>
|
||||||
|
<constraint firstItem="aXl-MI-TCo" firstAttribute="centerX" secondItem="l9e-rg-eUq" secondAttribute="centerX" id="SqJ-aZ-Yox"/>
|
||||||
|
<constraint firstItem="h9M-Bg-2Qi" firstAttribute="centerY" secondItem="3Hb-XS-UyM" secondAttribute="centerY" multiplier="0.9" id="XJe-gR-gCd"/>
|
||||||
|
<constraint firstItem="l7Q-NM-lYD" firstAttribute="centerX" secondItem="3Hb-XS-UyM" secondAttribute="centerX" multiplier="1.4" id="b0H-tS-xdf"/>
|
||||||
|
<constraint firstItem="h9M-Bg-2Qi" firstAttribute="centerX" secondItem="3Hb-XS-UyM" secondAttribute="centerX" multiplier="1.4" id="bh5-aa-dpw"/>
|
||||||
|
<constraint firstItem="l7Q-NM-lYD" firstAttribute="width" secondItem="h9M-Bg-2Qi" secondAttribute="width" id="orO-L8-WbF"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="75" y="688"/>
|
||||||
|
</scene>
|
||||||
|
<!--Donate Progress View Controller-->
|
||||||
|
<scene sceneID="exg-Cy-V2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="xq6-KE-CKE" customClass="DonateProgressViewController" customModule="PiPer" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<customView key="view" id="Z3j-yD-7xa" userLabel="Progress View">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="232" height="164"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="E2A-bR-Me7">
|
||||||
|
<rect key="frame" x="100" y="50" width="32" height="32"/>
|
||||||
|
</progressIndicator>
|
||||||
|
<textField horizontalHuggingPriority="700" verticalHuggingPriority="700" translatesAutoresizingMaskIntoConstraints="NO" id="bXO-nx-K3f" customClass="LocalizedTextField" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="14" y="130" width="61" height="22"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="donate" id="rKt-S9-lad">
|
||||||
|
<font key="font" metaFont="system" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="bch-TQ-lJc">
|
||||||
|
<rect key="frame" x="200" y="132" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="5Dk-nE-VK5"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="FKA-tU-4uc"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSStopProgressTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="fAn-Nq-nO9">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="dismissClickedWithSender:" target="xq6-KE-CKE" id="G6E-PK-2VE"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField hidden="YES" horizontalHuggingPriority="200" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="500" translatesAutoresizingMaskIntoConstraints="NO" id="3uK-eO-TcT" customClass="LocalizedTextField" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="74" y="58" width="84" height="17"/>
|
||||||
|
<textFieldCell key="cell" alignment="left" title="donate-error" drawsBackground="YES" id="Hxk-5m-6NQ">
|
||||||
|
<font key="font" metaFont="systemMedium" size="13"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="E2A-bR-Me7" firstAttribute="centerX" secondItem="3uK-eO-TcT" secondAttribute="centerX" id="2vw-3v-xBM"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="E2A-bR-Me7" secondAttribute="trailing" constant="100" id="BlZ-i8-jaF"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="E2A-bR-Me7" secondAttribute="bottom" constant="50" id="M9l-9k-0VE"/>
|
||||||
|
<constraint firstItem="E2A-bR-Me7" firstAttribute="centerY" secondItem="3uK-eO-TcT" secondAttribute="centerY" id="SJ6-ae-jQf"/>
|
||||||
|
<constraint firstItem="bXO-nx-K3f" firstAttribute="leading" secondItem="Z3j-yD-7xa" secondAttribute="leading" constant="16" id="VcV-Nc-ter"/>
|
||||||
|
<constraint firstItem="E2A-bR-Me7" firstAttribute="leading" secondItem="Z3j-yD-7xa" secondAttribute="leading" constant="100" id="fGX-Sq-opK"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="3uK-eO-TcT" secondAttribute="trailing" constant="16" id="hSv-y4-EXk"/>
|
||||||
|
<constraint firstItem="3uK-eO-TcT" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Z3j-yD-7xa" secondAttribute="leading" constant="16" id="hTs-8t-V5s"/>
|
||||||
|
<constraint firstItem="bch-TQ-lJc" firstAttribute="top" secondItem="Z3j-yD-7xa" secondAttribute="top" constant="16" id="hdL-dp-n0p"/>
|
||||||
|
<constraint firstItem="bXO-nx-K3f" firstAttribute="top" secondItem="Z3j-yD-7xa" secondAttribute="top" constant="12" id="j5I-CN-UJD"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="bch-TQ-lJc" secondAttribute="trailing" constant="16" id="rRf-qq-P7G"/>
|
||||||
|
<constraint firstItem="bch-TQ-lJc" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="bXO-nx-K3f" secondAttribute="trailing" constant="32" id="uO5-sb-fQB"/>
|
||||||
|
<constraint firstItem="E2A-bR-Me7" firstAttribute="top" secondItem="Z3j-yD-7xa" secondAttribute="top" constant="82" id="xHG-uf-eaf"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="errorMessage" destination="3uK-eO-TcT" id="8PG-6n-cfA"/>
|
||||||
|
<outlet property="progressIndicator" destination="E2A-bR-Me7" id="6tU-D7-Ded"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="vHi-dq-1X0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="883" y="1114"/>
|
||||||
|
</scene>
|
||||||
|
<!--Donate Container View Controller-->
|
||||||
|
<scene sceneID="4f0-FV-KFt">
|
||||||
|
<objects>
|
||||||
|
<tabViewController selectedTabViewItemIndex="0" tabStyle="unspecified" canPropagateSelectedChildViewControllerTitle="NO" id="1sh-YF-bwE" customClass="DonateContainerViewController" customModule="PiPer" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<tabViewItems>
|
||||||
|
<tabViewItem id="FNZ-vA-ksg">
|
||||||
|
<nil key="label"/>
|
||||||
|
</tabViewItem>
|
||||||
|
<tabViewItem id="yts-dh-y6U">
|
||||||
|
<nil key="label"/>
|
||||||
|
</tabViewItem>
|
||||||
|
</tabViewItems>
|
||||||
|
<viewControllerTransitionOptions key="transitionOptions"/>
|
||||||
|
<tabView key="tabView" type="noTabsNoBorder" id="wNE-Ok-s1w">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="440" height="300"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<font key="font" metaFont="message"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="1sh-YF-bwE" id="AYb-DG-aSi"/>
|
||||||
|
</connections>
|
||||||
|
</tabView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="donateProgressViewTabItem" destination="FNZ-vA-ksg" id="6Rv-TM-Xbs"/>
|
||||||
|
<outlet property="donateViewTabItem" destination="yts-dh-y6U" id="ssm-6E-EOf"/>
|
||||||
|
<outlet property="tabView" destination="wNE-Ok-s1w" id="4Hw-fF-nhC"/>
|
||||||
|
<segue destination="xq6-KE-CKE" kind="relationship" relationship="tabItems" id="ows-ED-wFf"/>
|
||||||
|
<segue destination="GR1-9j-nxR" kind="relationship" relationship="tabItems" id="W0l-7m-cHu"/>
|
||||||
|
</connections>
|
||||||
|
</tabViewController>
|
||||||
|
<customObject id="4GA-r3-EqO" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="708" y="688"/>
|
||||||
|
</scene>
|
||||||
|
<!--Donate View Controller-->
|
||||||
|
<scene sceneID="SK0-Zh-jDO">
|
||||||
|
<objects>
|
||||||
|
<userDefaultsController representsSharedInstance="YES" id="w8N-nO-ksB"/>
|
||||||
|
<customObject id="x5G-oc-Cl6" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<viewController id="GR1-9j-nxR" customClass="DonateViewController" customModule="PiPer" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<customView key="view" id="jce-U3-f7Q" userLabel="Donate View">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="290" height="211"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="700" verticalHuggingPriority="700" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Qla-Ap-0TG" customClass="LocalizedTextField" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="14" y="177" width="214" height="22"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="donate" id="5EO-9z-Dxr">
|
||||||
|
<font key="font" metaFont="system" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="fkf-qb-CLL">
|
||||||
|
<rect key="frame" x="258" y="179" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="Ib1-Px-Ar5"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="ZLd-K6-ZMG"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSStopProgressTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="gLf-Cj-g0U">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="dismissClickedWithSender:" target="GR1-9j-nxR" id="Old-11-Q0X"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<scrollView borderType="none" horizontalLineScroll="41" horizontalPageScroll="10" verticalLineScroll="41" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="3xY-5n-Vbh">
|
||||||
|
<rect key="frame" x="20" y="57" width="250" height="100"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="gfE-sr-B6p">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="250" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="none" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="41" rowSizeStyle="automatic" usesAutomaticRowHeights="YES" viewBased="YES" floatsGroupRows="NO" id="olo-kf-cLh">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="250" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="gridColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="DonationView" editable="NO" width="250" minWidth="40" maxWidth="1000" id="JyQ-k0-fgZ" userLabel="Table Column">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Mzk-zH-z2o">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="DonationView" id="ZU1-3P-6SN">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="250" height="41"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button horizontalHuggingPriority="600" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sog-KQ-GEk">
|
||||||
|
<rect key="frame" x="155" y="3" width="81" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="[price]" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8UV-Mq-9Dr">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="buyClickedWithSender:" target="GR1-9j-nxR" id="IIx-8J-Ntq"/>
|
||||||
|
<binding destination="ZU1-3P-6SN" name="title" keyPath="objectValue.price" id="iFQ-vj-MH6"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="600" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Hr1-xK-qgu">
|
||||||
|
<rect key="frame" x="41" y="12" width="83" height="17"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[donate title]" id="udR-9h-GKY">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<binding destination="ZU1-3P-6SN" name="value" keyPath="objectValue.name" id="47R-DM-6Jh"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="600" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="b0V-JY-XrR">
|
||||||
|
<rect key="frame" x="18" y="12" width="23" height="17"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="17" id="QdU-dF-gV5"/>
|
||||||
|
<constraint firstAttribute="width" constant="19" id="mmi-Q8-YXX"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="🙂" id="43s-1k-v0h">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<binding destination="ZU1-3P-6SN" name="value" keyPath="objectValue.emoticon" id="hk1-FH-AEy"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="sog-KQ-GEk" secondAttribute="trailing" constant="20" id="3dr-fi-PSr"/>
|
||||||
|
<constraint firstItem="Hr1-xK-qgu" firstAttribute="leading" secondItem="b0V-JY-XrR" secondAttribute="trailing" constant="4" id="NwT-BL-sll"/>
|
||||||
|
<constraint firstItem="sog-KQ-GEk" firstAttribute="top" secondItem="ZU1-3P-6SN" secondAttribute="top" constant="10" id="Ugx-5j-kJ5"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="sog-KQ-GEk" secondAttribute="bottom" constant="10" id="X4t-gu-0b6"/>
|
||||||
|
<constraint firstItem="b0V-JY-XrR" firstAttribute="leading" secondItem="ZU1-3P-6SN" secondAttribute="leading" constant="20" id="bch-fC-0rz"/>
|
||||||
|
<constraint firstItem="Hr1-xK-qgu" firstAttribute="centerY" secondItem="sog-KQ-GEk" secondAttribute="centerY" id="ozX-67-q6l"/>
|
||||||
|
<constraint firstItem="b0V-JY-XrR" firstAttribute="centerY" secondItem="sog-KQ-GEk" secondAttribute="centerY" id="xyO-Jl-lyi"/>
|
||||||
|
<constraint firstItem="sog-KQ-GEk" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Hr1-xK-qgu" secondAttribute="trailing" constant="20" id="yI5-rB-nOq"/>
|
||||||
|
</constraints>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<binding destination="GR1-9j-nxR" name="content" keyPath="self.donationProducts" id="tGM-Nk-kgc"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="100" id="0zv-4R-5jc"/>
|
||||||
|
<constraint firstAttribute="width" constant="250" id="cl8-we-9Se"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="fcd-Ce-3EI">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="228" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="gac-6B-vof">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="h03-80-d3t">
|
||||||
|
<rect key="frame" x="59" y="20" width="172" height="17"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pBV-ld-iOi" customClass="LocalizedTextField" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="-2" y="0.0" width="97" height="17"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="justified" title="total-donations" id="x3s-bb-ehg">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iQm-Zi-kbg">
|
||||||
|
<rect key="frame" x="99" y="0.0" width="75" height="17"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[total price]" id="rsy-PF-Hcd">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="iQm-Zi-kbg" secondAttribute="bottom" id="1EG-C8-Id8"/>
|
||||||
|
<constraint firstItem="pBV-ld-iOi" firstAttribute="top" secondItem="h03-80-d3t" secondAttribute="top" id="DiX-q8-tQ0"/>
|
||||||
|
<constraint firstItem="iQm-Zi-kbg" firstAttribute="top" secondItem="h03-80-d3t" secondAttribute="top" id="Grc-VA-qu3"/>
|
||||||
|
<constraint firstItem="iQm-Zi-kbg" firstAttribute="leading" secondItem="pBV-ld-iOi" secondAttribute="trailing" constant="8" id="Jjf-dV-WJ0"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="pBV-ld-iOi" secondAttribute="bottom" id="OyK-Oz-Bdw"/>
|
||||||
|
<constraint firstItem="pBV-ld-iOi" firstAttribute="leading" secondItem="h03-80-d3t" secondAttribute="leading" id="SI9-EH-ZqN"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="iQm-Zi-kbg" secondAttribute="trailing" id="xYO-fk-ic9"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="LkE-AY-8Be" customClass="ConfettiView" customModule="PiPer" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="211" width="0.0" height="0.0"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" placeholder="YES" id="odk-ek-PXI"/>
|
||||||
|
<constraint firstAttribute="height" placeholder="YES" id="rAx-rk-ghc"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="fkf-qb-CLL" firstAttribute="top" secondItem="jce-U3-f7Q" secondAttribute="top" constant="16" id="1vq-gO-4zV"/>
|
||||||
|
<constraint firstItem="fkf-qb-CLL" firstAttribute="leading" secondItem="Qla-Ap-0TG" secondAttribute="trailing" constant="32" id="5xK-PD-qqj"/>
|
||||||
|
<constraint firstItem="3xY-5n-Vbh" firstAttribute="centerX" secondItem="jce-U3-f7Q" secondAttribute="centerX" id="6IE-e6-52M"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="fkf-qb-CLL" secondAttribute="trailing" constant="16" id="6ef-vV-JKx"/>
|
||||||
|
<constraint firstItem="h03-80-d3t" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="jce-U3-f7Q" secondAttribute="leading" constant="20" id="8Jn-IB-ZqE"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="3xY-5n-Vbh" secondAttribute="trailing" constant="20" id="BUz-au-06b"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="h03-80-d3t" secondAttribute="bottom" constant="20" id="EKh-7R-Bb6"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="LkE-AY-8Be" secondAttribute="trailing" priority="10" id="GR9-El-pSz"/>
|
||||||
|
<constraint firstItem="Qla-Ap-0TG" firstAttribute="top" secondItem="jce-U3-f7Q" secondAttribute="top" constant="12" id="Mxa-mZ-LYM"/>
|
||||||
|
<constraint firstItem="h03-80-d3t" firstAttribute="top" secondItem="3xY-5n-Vbh" secondAttribute="bottom" constant="20" id="Nu0-Dh-Q4Z"/>
|
||||||
|
<constraint firstItem="3xY-5n-Vbh" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="jce-U3-f7Q" secondAttribute="leading" constant="20" id="RRH-PB-gLG"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="LkE-AY-8Be" secondAttribute="bottom" priority="10" id="dec-YN-wsX"/>
|
||||||
|
<constraint firstItem="LkE-AY-8Be" firstAttribute="top" secondItem="jce-U3-f7Q" secondAttribute="top" priority="100" id="gVA-aT-I3V"/>
|
||||||
|
<constraint firstItem="3xY-5n-Vbh" firstAttribute="top" secondItem="Qla-Ap-0TG" secondAttribute="bottom" constant="20" id="h9U-lb-nmw"/>
|
||||||
|
<constraint firstItem="Qla-Ap-0TG" firstAttribute="leading" secondItem="jce-U3-f7Q" secondAttribute="leading" constant="16" id="oSs-lE-Sua"/>
|
||||||
|
<constraint firstItem="LkE-AY-8Be" firstAttribute="leading" secondItem="jce-U3-f7Q" secondAttribute="leading" priority="20" id="sLZ-FG-Ydl"/>
|
||||||
|
<constraint firstItem="h03-80-d3t" firstAttribute="centerX" secondItem="jce-U3-f7Q" secondAttribute="centerX" id="tc6-rX-HfD"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="h03-80-d3t" secondAttribute="trailing" constant="20" id="wzf-a8-HBd"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="confettiView" destination="LkE-AY-8Be" id="z2W-cQ-VLV"/>
|
||||||
|
<outlet property="donationTableView" destination="olo-kf-cLh" id="fLx-Vx-APx"/>
|
||||||
|
<outlet property="tableViewHeightConstraint" destination="0zv-4R-5jc" id="m53-Ot-Emw"/>
|
||||||
|
<outlet property="tableViewWidthConstraint" destination="cl8-we-9Se" id="Y1e-W1-pqX"/>
|
||||||
|
<outlet property="totalDonations" destination="iQm-Zi-kbg" id="cQ3-32-YCb"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="514" y="1138"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="Icon" width="512" height="512"/>
|
||||||
|
<image name="NSCaution" width="128" height="128"/>
|
||||||
|
<image name="NSStopProgressTemplate" width="11" height="11"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
20
src/safari/App/PiPer_App.entitlements
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||||
|
<array/>
|
||||||
|
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||||
|
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(APP_GROUP_ID)</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
23
src/safari/App/ResourceHelper.swift
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// ResourceHelper.swift
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 13/10/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ResourceHelper {
|
||||||
|
|
||||||
|
static let extensionBundleURL: URL? = {
|
||||||
|
guard let pluginURL = Bundle.main.builtInPlugInsURL else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let extensionBundle = Bundle(url: pluginURL.appendingPathComponent("PiPerExt.appex")) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return extensionBundle.resourceURL
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
63
src/safari/App/ViewController.swift
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// ViewController.swift
|
||||||
|
// PiPer App
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 19/07/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import SafariServices
|
||||||
|
|
||||||
|
class ViewController: NSViewController {
|
||||||
|
|
||||||
|
let extensionId = String(cString:EXTENSION_BUNDLE_ID)
|
||||||
|
|
||||||
|
@IBOutlet var viewContainer: NSVisualEffectView!
|
||||||
|
@IBOutlet var mainView: NSView!
|
||||||
|
@IBOutlet var extensionDisabledView: NSView!
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// Display the main view by default
|
||||||
|
viewContainer.addSubview(mainView)
|
||||||
|
|
||||||
|
// Poll every second for Safari extension state changes
|
||||||
|
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.checkExtensionState), userInfo: nil, repeats: true).fire()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the requested view
|
||||||
|
func showView(_ newView: NSView) {
|
||||||
|
let oldView = viewContainer.subviews.first
|
||||||
|
if oldView == newView {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Avoid animating the transition due to NSButton vibrancy rendering glitch in dark mode
|
||||||
|
// viewContainer.animator().replaceSubview(oldView!, with:newView)
|
||||||
|
viewContainer.replaceSubview(oldView!, with:newView)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check extension state and show extension disabled view if necessary
|
||||||
|
@objc func checkExtensionState() {
|
||||||
|
SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: extensionId) { state, error in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if let status = state?.isEnabled {
|
||||||
|
self.showView(status ? self.mainView : self.extensionDisabledView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedEnableExtension(sender: NSButton) {
|
||||||
|
SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedReportBug(sender: NSButton) {
|
||||||
|
if let url = URL(string: "https://github.com/amarcu5/PiPer/issues") {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/safari/Common/Defines.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Defines.c
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 10/08/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#define QUOTE(str) #str
|
||||||
|
#define EXPAND_AND_QUOTE(str) QUOTE(str)
|
||||||
|
|
||||||
|
const char * APP_GROUP_ID = EXPAND_AND_QUOTE(APP_GROUP_ID_CONST);
|
||||||
|
|
||||||
|
const char * APP_BUNDLE_ID = EXPAND_AND_QUOTE(APP_BUNDLE_ID_CONST);
|
||||||
|
const char * EXTENSION_BUNDLE_ID = EXPAND_AND_QUOTE(EXTENSION_BUNDLE_ID_CONST);
|
||||||
12
src/safari/Common/Defines.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Defines.h
|
||||||
|
// PiPer
|
||||||
|
//
|
||||||
|
// Created by Adam Marcus on 10/08/2018.
|
||||||
|
// Copyright © 2018 Adam Marcus. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extern const char * APP_GROUP_ID;
|
||||||
|
|
||||||
|
extern const char * APP_BUNDLE_ID;
|
||||||
|
extern const char * EXTENSION_BUNDLE_ID;
|
||||||
53
src/safari/Extension/Info.plist
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>PiPer</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.0.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.Safari.extension</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).SafariExtensionHandler</string>
|
||||||
|
<key>SFSafariContentScript</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>Script</key>
|
||||||
|
<string>scripts/main.js</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>SFSafariExtensionBundleIdentifiersToUninstall</key>
|
||||||
|
<array>
|
||||||
|
<string>com.amarcus.safari.piper</string>
|
||||||
|
</array>
|
||||||
|
<key>SFSafariWebsiteAccess</key>
|
||||||
|
<dict>
|
||||||
|
<key>Level</key>
|
||||||
|
<string>All</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2018 Adam Marcus. All rights reserved.</string>
|
||||||
|
<key>NSHumanReadableDescription</key>
|
||||||
|
<string>Adds Picture in Picture functionality to Youtube, Netflix, Amazon Video, Twitch, and more!</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
12
src/safari/Extension/PiPer_Extension.entitlements
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(APP_GROUP_ID)</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||